Merge branch 'master' into docs/readme_darkmode_logo
diff --git a/.cm/change_categories.cm b/.cm/change_categories.cm
new file mode 100644
index 0000000..2c74a91
--- /dev/null
+++ b/.cm/change_categories.cm
@@ -0,0 +1,253 @@
+# -*- mode: yaml -*-
+manifest:
+  version: 1.0
+
+# The `automations` section lists automations to run on PRs in this repository.
+# Each automation has an `if` key that defines conditions to run the automation,
+# as well as a `run` key with the actions to do. All the conditions need to be true
+# for the actions to run (there is an implicit AND relation between
+# the conditions on each line).  Each condition is specified as a Jinja expression.  You
+# can also use the constant `true` if you want to always run the actions.
+
+# Each automation is independent of the others.  Every time a PR is opened or changed,
+# the conditions are evaluated and the actions are executed one by one in order
+# if all the conditions pass.  You can define multiple automations in a .cm file, but
+# each automation name should be unique within the file.
+
+# Every action only runs if a JVM team member is the author of the change, for testing purposes.
+automations:
+
+  # Add the a:documentation label to docs-only changes
+  docs_only:
+    if:
+      - {{ author.using_gitstream }}
+      - {{ is.docs }}
+    run:
+      - action: add-label@v1
+        args:
+          label: 'a:documentation'
+  # Add the a:chore label and auto-approve formatting-only changes
+  formatting_only:
+    if:
+      - {{ author.using_gitstream }}
+      - {{ is.formatting }}
+    run:
+      - action: add-label@v1
+        args:
+          label: 'a:chore'
+      - action: approve@v1
+
+  # Label changes by affected platforms (in blue)
+  {% for platform in platforms %}
+  label_platform_{{ platform.name }}:
+    if:
+      - {{ author.using_gitstream }}
+      - {{ files | match(list=platform.subprojects) | some }}
+    run:
+      - action: add-label@v1
+        args:
+          label: 'platform:{{ platform.name }}'
+          color: '0000FF'
+  {% endfor %}
+
+  # Call out PRs that change the build script (and will be longer to checkout and review)
+  label_build_script_change:
+    if:
+      - {{ author.using_gitstream }}
+      - {{ is.build_script_change }}
+    run:
+      - action: add-label@v1
+        args:
+          label: 'build-script-change'
+          color: 'E94637'
+
+# To simplify the automations section, some calculations are placed under unique YAML keys defined here.
+# Read the "|" not as "or", but as a "pipe", taking the output of the previous command and passing it to the next command.
+
+# The results of these calculations are assigned to `is.formatting`, `is.docs` and `is.tests`.
+is:
+  formatting: {{ source.diff.files | isFormattingChange }}
+  docs: {{ (files | allDocs) or (files | match(regex=r/\.adoc$/) | every) }} # This won't catch a mix of .adoc and non .adoc changes, see: https://github.com/linear-b/gitstream/issues/93
+  tests: {{ files | allTests }}
+
+  # Also just an approximation
+  build_script_change: {{ files | reject(regex=r/\/src\/(samples|snippets)\//) | match(list=build_logic_files) | some }}
+
+build_logic_files:
+  - 'build.gradle.kts'
+  - 'build.gradle'
+  - 'settings.gradle.kts'
+  - 'settings.gradle'
+
+# Perhaps a task could generate this list from the CODEOWNERS, or both this and CODEOWNERS could be generated from different single source of truth?
+platforms:
+  - build_infrastructure:
+    name: 'build-infrastructure'
+    subprojects:
+      - 'subprojects/distributions-dependencies/'
+      - 'subprojects/distributions-full/'
+      - 'subprojects/internal-architecture-testing/'
+      - 'subprojects/internal-build-reports/'
+      - 'subprojects/internal-integ-testing/'
+      - 'subprojects/internal-performance-testing/'
+      - 'subprojects/internal-testing/'
+      - 'subprojects/build-logic/'
+      - 'subprojects/build-logic-commons/'
+      - 'subprojects/build-logic-settings/'
+  - cc:
+    name: 'configuration-cache'
+    subprojects:
+      - 'subprojects/api-metadata/'
+      - 'subprojects/base-annotations/'
+      - 'subprojects/configuration-cache/'
+      - 'subprojects/instrumentation-agent'
+      - 'subprojects/internal-instrumentation-api/'
+      - 'subprojects/internal-instrumentation-processor/'
+      - 'subprojects/kotlin-dsl/'
+      - 'subprojects/kotlin-dsl-integ-tests/'
+      - 'subprojects/kotlin-dsl-plugins/'
+      - 'subprojects/kotlin-dsl-provider-plugins/'
+  - core:
+    name: 'core'
+    subprojects:
+      - 'subprojects/base-services/'
+      - 'subprojects/base-services-groovy/'
+      - 'subprojects/bootstrap/'
+      - 'subprojects/build-option/'
+      - 'subprojects/cli/'
+      - 'subprojects/core-platform/'
+      - 'subprojects/distributions-basics/'
+      - 'subprojects/distributions-core/'
+      - 'subprojects/file-collections/'
+      - 'subprojects/file-temp/'
+      - 'subprojects/file-watching/'
+      - 'subprojects/files/'
+      - 'subprojects/hashing/'
+      - 'subprojects/installation-beacon/'
+      - 'subprojects/instrumentation-agent/'
+      - 'subprojects/launcher/'
+      - 'subprojects/logging/'
+      - 'subprojects/logging-api/'
+      - 'subprojects/messaging/'
+      - 'subprojects/model-core/'
+      - 'subprojects/model-groovy/'
+      - 'subprojects/persistent-cache/'
+      - 'subprojects/problems/'
+      - 'subprojects/process-services/'
+      - 'subprojects/snapshots/'
+      - 'subprojects/worker-processes/'
+      - 'subprojects/worker-services/'
+      - 'subprojects/workers/'
+      - 'subprojects/wrapper/'
+      - 'subprojects/wrapper-shared/'
+  - documentation:
+    name: 'documentation'
+    subprojects:
+      - 'subprojects/docs/'
+      - 'subprojects/docs-asciidoctor-extensions/'
+      - 'subprojects/docs-asciidoctor-extensions-base/'
+      - 'subprojects/samples/'
+  - execution:
+    name: 'execution'
+    subprojects:
+      - 'subprojects/build-cache/'
+      - 'subprojects/build-cache-base/'
+      - 'subprojects/build-cache-http/'
+      - 'subprojects/build-cache-packaging/'
+      - 'subprojects/build-profile/'
+      - 'subprojects/execution/'
+  - extensibility:
+    name: 'extensibility'
+    subprojects:
+      - 'subprojects/plugin-development/'
+      - 'subprojects/plugin-use/'
+      - 'subprojects/test-kit/'
+  - ge_integration:
+    name: 'ge-integration'
+    subprojects:
+      - 'subprojects/build-scan-performance/'
+      - 'subprojects/enterprise/'
+      - 'subprojects/enterprise-logging/'
+      - 'subprojects/enterprise-operations/'
+      - 'subprojects/enterprise-workers/'
+  - ide:
+    name: 'ide'
+    subprojects:
+      - 'subprojects/ide/'
+      - 'subprojects/tooling-api/'
+      - 'subprojects/tooling-api-builders/'
+  - jvm:
+    name: 'jvm'
+    subprojects:
+      - 'subprojects/code-quality/'
+      - 'subprojects/distributions-jvm/'
+      - 'subprojects/distributions-publishing/'
+      - 'subprojects/ear/'
+      - 'subprojects/jacoco/'
+      - 'subprojects/java-compiler-plugin/'
+      - 'subprojects/jvm-services/'
+      - 'subprojects/language-groovy/'
+      - 'subprojects/language-java/'
+      - 'subprojects/language-jvm/'
+      - 'subprojects/normalization-java/'
+      - 'subprojects/platform-jvm/'
+      - 'subprojects/plugins/'
+      - 'subprojects/scala/'
+      - 'subprojects/testing-junit-platform/'
+      - 'subprojects/testing-jvm/'
+      - 'subprojects/testing-jvm-infrastructure/'
+  - release_coordination:
+    name: 'release-coordination'
+    subprojects:
+      - 'subprojects/distributions-dependencies/'
+      - 'subprojects/distributions-full/'
+      - 'subprojects/performance/'
+      - 'subprojects/smoke-test/'
+      - 'subprojects/soak/'
+  - software:
+    name: 'software'
+    subprojects:
+      - 'subprojects/antlr/'
+      - 'subprojects/build-init/'
+      - 'subprojects/dependency-management/'
+      - 'subprojects/diagnostics/'
+      - 'subprojects/ivy/'
+      - 'subprojects/maven/'
+      - 'subprojects/platform-base/'
+      - 'subprojects/platform-native/'
+      - 'subprojects/publish/'
+      - 'subprojects/reporting/'
+      - 'subprojects/resources/'
+      - 'subprojects/resources-gcs/'
+      - 'subprojects/resources-http/'
+      - 'subprojects/resources-s3/'
+      - 'subprojects/resources-sftp/'
+      - 'subprojects/security/'
+      - 'subprojects/signing/'
+      - 'subprojects/testing-base/'
+      - 'subprojects/version-control/'
+
+author:
+  using_gitstream: {{ (pr.author | match(list=teams.jvm.members)) or (pr.author | match(list=teams.execution.members)) or (pr.author | match(list=teams.ide.members)) or (pr.author | match(list=teams.build_scan.members)) }}
+
+teams:
+  build_scan:
+    members:
+      - 'alllex'
+      - 'wolfs'
+  execution:
+    members:
+      - 'asodja'
+      - 'lptr'
+      - 'FrauBoes'
+  ide:
+    members:
+      - 'donat'
+      - 'reinsch82'
+  jvm:
+    members:
+      - 'big-guy'
+      - 'ghale'
+      - 'jvandort'
+      - 'octylFractal'
+      - 'tresat'
diff --git a/.cm/code_experts.cm b/.cm/code_experts.cm
new file mode 100644
index 0000000..7c56043
--- /dev/null
+++ b/.cm/code_experts.cm
@@ -0,0 +1,69 @@
+# -*- mode: yaml -*-
+manifest:
+  version: 1.0
+
+# The `automations` section lists automations to run on PRs in this repository.
+# Each automation has an `if` key that defines conditions to run the automation,
+# as well as a `run` key with the actions to do. All the conditions need to be true
+# for the actions to run (there is an implicit AND relation between
+# the conditions on each line).  Each condition is specified as a Jinja expression.  You
+# can also use the constant `true` if you want to always run the actions.
+
+# Each automation is independent of the others.  Every time a PR is opened or changed,
+# the conditions are evaluated and the actions are executed one by one in order
+# if all the conditions pass.  You can define multiple automations in a .cm file, but
+# each automation name should be unique within the file.
+
+# Every action only runs if a JVM team member is the author of the change, for testing purposes.
+automations:
+
+  # If someone is a primary author of the files in a change, but NOT the pr author, automatically add them as a reviewer.
+  add_primary_author_as_reviewer:
+    if:
+      - {{ author.using_gitstream }}
+      - {{ repo | codeExperts(gt=50) | match(branch.author) | nope }}
+    run:
+      - action: add-reviewers@v1
+        args:
+          reviewers: {{ repo | codeExperts(gt=50) }}
+
+  # Also post a comment that lists the best experts for the files that were modified.
+  comment_experts:
+    if:
+      - {{ author.using_gitstream }}
+    run:
+      - action: add-comment@v1
+        args:
+          # Note the comment starts with | and a new-line as explainCodeExperts generates a multiline comment.
+          comment: |
+            {{ repo | explainCodeExperts(gt=10) }}
+
+
+# To simplify the automations section, some calculations are placed under unique YAML keys defined here.
+# Read the "|" not as "or", but as a "pipe", taking the output of the previous command and passing it to the next command.
+# This section could also appear ahead of the automations section.
+
+author:
+  using_gitstream: {{ (pr.author | match(list=teams.jvm.members)) or (pr.author | match(list=teams.execution.members)) or (pr.author | match(list=teams.ide.members)) or (pr.author | match(list=teams.build_scan.members)) }}
+
+teams:
+  build_scan:
+    members:
+      - 'alllex'
+      - 'wolfs'
+  execution:
+    members:
+      - 'asodja'
+      - 'lptr'
+      - 'FrauBoes'
+  ide:
+    members:
+      - 'donat'
+      - 'reinsch82'
+  jvm:
+    members:
+      - 'big-guy'
+      - 'ghale'
+      - 'jvandort'
+      - 'octylFractal'
+      - 'tresat'
diff --git a/.cm/complex_changes.cm b/.cm/complex_changes.cm
new file mode 100644
index 0000000..0e6ab6e
--- /dev/null
+++ b/.cm/complex_changes.cm
@@ -0,0 +1,61 @@
+# -*- mode: yaml -*-
+manifest:
+  version: 1.0
+
+# The `automations` section lists automations to run on PRs in this repository.
+# Each automation has an `if` key that defines conditions to run the automation,
+# as well as a `run` key with the actions to do. All the conditions need to be true
+# for the actions to run (there is an implicit AND relation between
+# the conditions on each line).  Each condition is specified as a Jinja expression.  You
+# can also use the constant `true` if you want to always run the actions.
+
+# Each automation is independent of the others.  Every time a PR is opened or changed,
+# the conditions are evaluated and the actions are executed one by one in order
+# if all the conditions pass.  You can define multiple automations in a .cm file, but
+# each automation name should be unique within the file.
+
+# Every action only runs if a JVM team member is the author of the change, for testing purposes.
+automations:
+
+  # If a PR is very complex, require 2 approvals
+  complex_change:
+    if:
+      - {{ author.using_gitstream }}
+      - {{ branch | estimatedReviewTime >= 45 }}
+      - {{ files | length >= 20 }}
+      - {{ includes_src_changes }}
+    run:
+      - action: set-required-approvals@v1
+        args:
+          approvals: 2
+
+# To simplify the automations section, some calculations are placed under unique YAML keys defined here.
+# Read the "|" not as "or", but as a "pipe", taking the output of the previous command and passing it to the next command.
+# This section could also appear ahead of the automations section.
+
+includes_src_changes: {{ files | match(regex=r/.*\/src\//) | some }}
+
+author:
+  using_gitstream: {{ (pr.author | match(list=teams.jvm.members)) or (pr.author | match(list=teams.execution.members)) or (pr.author | match(list=teams.ide.members)) or (pr.author | match(list=teams.build_scan.members)) }}
+
+teams:
+  build_scan:
+    members:
+      - 'alllex'
+      - 'wolfs'
+  execution:
+    members:
+      - 'asodja'
+      - 'lptr'
+      - 'FrauBoes'
+  ide:
+    members:
+      - 'donat'
+      - 'reinsch82'
+  jvm:
+    members:
+      - 'big-guy'
+      - 'ghale'
+      - 'jvandort'
+      - 'octylFractal'
+      - 'tresat'
diff --git a/.cm/estimated_time_to_review.cm b/.cm/estimated_time_to_review.cm
new file mode 100644
index 0000000..8af5555
--- /dev/null
+++ b/.cm/estimated_time_to_review.cm
@@ -0,0 +1,63 @@
+# -*- mode: yaml -*-
+manifest:
+  version: 1.0
+
+# The `automations` section lists automations to run on PRs in this repository.
+# Each automation has an `if` key that defines conditions to run the automation,
+# as well as a `run` key with the actions to do. All the conditions need to be true
+# for the actions to run (there is an implicit AND relation between
+# the conditions on each line).  Each condition is specified as a Jinja expression.  You
+# can also use the constant `true` if you want to always run the actions.
+
+# Each automation is independent of the others.  Every time a PR is opened or changed,
+# the conditions are evaluated and the actions are executed one by one in order
+# if all the conditions pass.  You can define multiple automations in a .cm file, but
+# each automation name should be unique within the file.
+
+# Every action only runs if a JVM team member is the author of the change, for testing purposes.
+automations:
+
+  # Add a label that indicates how many minutes it will take to review the PR and categorizes it
+  estimated_time_to_review:
+    if:
+      - {{ author.using_gitstream }}
+    run:
+      - action: add-label@v1
+        # etr is defined in the last section of this example
+        args:
+          label: "{{ calc.etr }} min review"
+          color: {{ 'E94637' if (calc.etr >= 20) else ('FBBD10' if (calc.etr >= 5) else '36A853') }}
+
+# To simplify the automations section, some calculations are placed under unique YAML keys defined here.
+# Read the "|" not as "or", but as a "pipe", taking the output of the previous command and passing it to the next command.
+# This section could also appear ahead of the automations section.
+
+# This function calculates the estimated time to review and makes it available in the automation above.
+# The name `calc`, and the nesting is arbitrary.
+calc:
+  etr: {{ branch | estimatedReviewTime }}
+
+author:
+  using_gitstream: {{ (pr.author | match(list=teams.jvm.members)) or (pr.author | match(list=teams.execution.members)) or (pr.author | match(list=teams.ide.members)) or (pr.author | match(list=teams.build_scan.members)) }}
+
+teams:
+  build_scan:
+    members:
+      - 'alllex'
+      - 'wolfs'
+  execution:
+    members:
+      - 'asodja'
+      - 'lptr'
+      - 'FrauBoes'
+  ide:
+    members:
+      - 'donat'
+      - 'reinsch82'
+  jvm:
+    members:
+      - 'big-guy'
+      - 'ghale'
+      - 'jvandort'
+      - 'octylFractal'
+      - 'tresat'
diff --git a/.cm/includes_todos.cm b/.cm/includes_todos.cm
new file mode 100644
index 0000000..2e95966
--- /dev/null
+++ b/.cm/includes_todos.cm
@@ -0,0 +1,57 @@
+# -*- mode: yaml -*-
+manifest:
+  version: 1.0
+
+# The `automations` section lists automations to run on PRs in this repository.
+# Each automation has an `if` key that defines conditions to run the automation,
+# as well as a `run` key with the actions to do. All the conditions need to be true
+# for the actions to run (there is an implicit AND relation between
+# the conditions on each line).  Each condition is specified as a Jinja expression.  You
+# can also use the constant `true` if you want to always run the actions.
+
+# Each automation is independent of the others.  Every time a PR is opened or changed,
+# the conditions are evaluated and the actions are executed one by one in order
+# if all the conditions pass.  You can define multiple automations in a .cm file, but
+# each automation name should be unique within the file.
+
+# Every action only runs if a JVM team member is the author of the change, for testing purposes.
+automations:
+
+  # If someone adds a TODO, remind them about it by requesting removal
+  point_out_todos:
+    if:
+      - {{ author.using_gitstream }}
+      - {{ source.diff.files | matchDiffLines(regex=r/(TODO)|(todo)/) | some }}
+    run:
+      - action: request-changes@v1
+        args:
+          comment: 'There are new TODOs present in this change.  Should any be removed?'
+
+# To simplify the automations section, some calculations are placed under unique YAML keys defined here.
+# Read the "|" not as "or", but as a "pipe", taking the output of the previous command and passing it to the next command.
+# This section could also appear ahead of the automations section.
+
+author:
+  using_gitstream: {{ (pr.author | match(list=teams.jvm.members)) or (pr.author | match(list=teams.execution.members)) or (pr.author | match(list=teams.ide.members)) or (pr.author | match(list=teams.build_scan.members)) }}
+
+teams:
+  build_scan:
+    members:
+      - 'alllex'
+      - 'wolfs'
+  execution:
+    members:
+      - 'asodja'
+      - 'lptr'
+      - 'FrauBoes'
+  ide:
+    members:
+      - 'donat'
+      - 'reinsch82'
+  jvm:
+    members:
+      - 'big-guy'
+      - 'ghale'
+      - 'jvandort'
+      - 'octylFractal'
+      - 'tresat'
diff --git a/.cm/lacks_tests.cm b/.cm/lacks_tests.cm
new file mode 100644
index 0000000..28d4819
--- /dev/null
+++ b/.cm/lacks_tests.cm
@@ -0,0 +1,66 @@
+# -*- mode: yaml -*-
+manifest:
+  version: 1.0
+
+# The `automations` section lists automations to run on PRs in this repository.
+# Each automation has an `if` key that defines conditions to run the automation,
+# as well as a `run` key with the actions to do. All the conditions need to be true
+# for the actions to run (there is an implicit AND relation between
+# the conditions on each line).  Each condition is specified as a Jinja expression.  You
+# can also use the constant `true` if you want to always run the actions.
+
+# Each automation is independent of the others.  Every time a PR is opened or changed,
+# the conditions are evaluated and the actions are executed one by one in order
+# if all the conditions pass.  You can define multiple automations in a .cm file, but
+# each automation name should be unique within the file.
+
+# Every action only runs if a JVM team member is the author of the change, for testing purposes.
+automations:
+
+  # If a PR contains non-trivial source changes and no tests, add a comment to the PR
+  no_tests:
+    if:
+      - {{ author.using_gitstream }}
+      - {{ includes_src_changes }}
+      - {{ not (includes_test_changes) }}
+      - {{ not (is_docs_only_change) }}
+      - {{ not (is_formatting_only_change) }}
+    run:
+      - action: add-comment@v1
+        args:
+          comment: |
+            This PR appears to be lacking tests.  Consider adding tests to cover the new functionality.
+
+# To simplify the automations section, some calculations are placed under unique YAML keys defined here.
+# Read the "|" not as "or", but as a "pipe", taking the output of the previous command and passing it to the next command.
+# This section could also appear ahead of the automations section.
+
+includes_src_changes: {{ files | match(regex=r/.*\/src\//) | some }}
+includes_test_changes: {{ files | match(regex=r/.*\/(test|integTest|crossVersionTest|docsTest|smokeTest)\//) | some }}
+is_docs_only_change: {{ (files | allDocs) or (files | match(regex=r/\.adoc$/) | every) }} # This won't catch a mix of .adoc and non .adoc changes, see: https://github.com/linear-b/gitstream/issues/93
+is_formatting_only_change: {{ source.diff.files | isFormattingChange }}
+
+author:
+  using_gitstream: {{ (pr.author | match(list=teams.jvm.members)) or (pr.author | match(list=teams.execution.members)) or (pr.author | match(list=teams.ide.members)) or (pr.author | match(list=teams.build_scan.members)) }}
+
+teams:
+  build_scan:
+    members:
+      - 'alllex'
+      - 'wolfs'
+  execution:
+    members:
+      - 'asodja'
+      - 'lptr'
+      - 'FrauBoes'
+  ide:
+    members:
+      - 'donat'
+      - 'reinsch82'
+  jvm:
+    members:
+      - 'big-guy'
+      - 'ghale'
+      - 'jvandort'
+      - 'octylFractal'
+      - 'tresat'
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 9922465..557e5c3 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -6,30 +6,39 @@
 subprojects/build-cache-http/               @gradle/bt-execution
 subprojects/build-cache-packaging/          @gradle/bt-execution
 subprojects/build-operations/               @gradle/bt-execution
+subprojects/build-profile/                  @gradle/bt-execution
 subprojects/execution/                      @gradle/bt-execution
 subprojects/file-watching/                  @gradle/bt-execution
 subprojects/files/                          @gradle/bt-execution
 subprojects/hashing/                        @gradle/bt-execution
-subprojects/normalization-java/             @gradle/bt-execution
 subprojects/snapshots/                      @gradle/bt-execution
 
-# Configuration cache
-subprojects/instrumentation-agent           @gradle/bt-configuration-cache
+# Configuration cache team (soon to be renamed Configuration team)
+subprojects/instrumentation-agent/              @gradle/bt-configuration-cache
+subprojects/internal-instrumentation-api/       @gradle/bt-configuration-cache
+subprojects/internal-instrumentation-processor/ @gradle/bt-configuration-cache
+subprojects/api-metadata/                       @gradle/bt-configuration-cache
+subprojects/base-annotations/                   @gradle/bt-configuration-cache
+subprojects/configuration-cache/                @gradle/bt-configuration-cache
 
 # Gradle Enterprise
 subprojects/enterprise/                     @ldaley @gradle/ge-testing @gradle/ge-build-insights @gradle/bt-build-scan
 subprojects/enterprise-logging/             @gradle/ge-build-insights @gradle/bt-build-scan @gradle/ge-testing
 subprojects/enterprise-operations/          @gradle/ge-build-insights @gradle/bt-build-scan
 subprojects/enterprise-workers/             @gradle/ge-build-insights @gradle/bt-build-scan
+subprojects/build-scan-performance/         @gradle/bt-build-scan
 
 # JVM platform
 subprojects/code-quality/                   @gradle/bt-jvm
+subprojects/distributions-jvm/              @gradle/bt-jvm
+subprojects/distributions-publishing/       @gradle/bt-jvm
 subprojects/ear/                            @gradle/bt-jvm
 subprojects/jacoco/                         @gradle/bt-jvm
 subprojects/java-compiler-plugin/           @gradle/bt-jvm
 subprojects/jvm-services/                   @gradle/bt-jvm
 subprojects/language-groovy/                @gradle/bt-jvm
 subprojects/language-java/                  @gradle/bt-jvm
+subprojects/language-jvm/                   @gradle/bt-jvm
 subprojects/normalization-java/             @gradle/bt-jvm
 subprojects/platform-jvm/                   @gradle/bt-jvm
 subprojects/plugins/                        @gradle/bt-jvm
@@ -39,9 +48,9 @@
 subprojects/testing-jvm-infrastructure/     @gradle/bt-jvm @gradle/ge-testing
 
 # Software platform
+subprojects/antlr/                          @gradle/bt-jvm
 subprojects/build-init/                     @gradle/bt-jvm
 subprojects/dependency-management/          @gradle/bt-jvm
-subprojects/diagnostics/                    @gradle/bt-jvm
 subprojects/ivy/                            @gradle/bt-jvm
 subprojects/maven/                          @gradle/bt-jvm
 subprojects/platform-base/                  @gradle/bt-jvm
@@ -49,6 +58,7 @@
 subprojects/reporting/                      @gradle/bt-jvm
 subprojects/resources/                      @gradle/bt-jvm
 subprojects/resources-gcs/                  @gradle/bt-jvm
+subprojects/resources-http/                 @gradle/bt-jvm
 subprojects/resources-s3/                   @gradle/bt-jvm
 subprojects/resources-sftp/                 @gradle/bt-jvm
 subprojects/security/                       @gradle/bt-jvm
@@ -56,32 +66,68 @@
 subprojects/testing-base/                   @gradle/bt-jvm
 subprojects/version-control/                @gradle/bt-jvm
 
-# Documentation
-subprojects/docs/src/docs/release/notes.md  @hythloda
-subprojects/docs/                           @hythloda
+# Mixed
+subprojects/diagnostics/                    @gradle/bt-jvm @gradle/bt-configuration-cache @gradle/bt-execution
 
-subprojects/docs/src/docs/userguide/core-plugins/base_plugin.adoc                 @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/build_dashboard_plugin.adoc      @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/build_init_plugin.adoc           @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/checkstyle_plugin.adoc           @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/codenarc_plugin.adoc             @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/distribution_plugin.adoc         @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/ear_plugin.adoc                  @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/jacoco_plugin.adoc               @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/java_gradle_plugin.adoc          @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/pmd_plugin.adoc                  @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/core-plugins/war_plugin.adoc                  @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/dep-man/                                      @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/jvm/                                          @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/reference/directory_layout.adoc               @gradle/bt-jvm
-subprojects/docs/src/docs/userguide/troubleshooting/version_catalog_problems.adoc @gradle/bt-jvm
+# Kotlin DSL
+build-logic/kotlin-dsl/                                                                 @eskatos @jbartok
+subprojects/kotlin-dsl/                                                                 @eskatos @jbartok
+subprojects/kotlin-dsl-integ-tests/                                                     @eskatos @jbartok
+subprojects/kotlin-dsl-plugins/                                                         @eskatos @jbartok
+subprojects/kotlin-dsl-provider-plugins/                                                @eskatos @jbartok
+subprojects/kotlin-dsl-tooling-builders/                                                @eskatos @jbartok
+subprojects/kotlin-dsl-tooling-models/                                                  @eskatos @jbartok
 
 # Dev-prod team
 .teamcity/                                  @gradle/bt-developer-productivity
 .github/                                    @gradle/bt-developer-productivity
 gradle/shared-with-buildSrc/                @gradle/bt-developer-productivity
-build-logic/                                @gradle/bt-developer-productivity
-build-logic-commons/                        @gradle/bt-developer-productivity
-build-logic-settings/                       @gradle/bt-developer-productivity
+/build-logic/                               @gradle/bt-developer-productivity
+/build-logic-commons/                       @gradle/bt-developer-productivity
+/build-logic-settings/                      @gradle/bt-developer-productivity
 subprojects/*/build.gradle*                 @gradle/bt-developer-productivity
-*.gradle.kts                                @gradle/bt-developer-productivity
+/build.gradle*                              @gradle/bt-developer-productivity
+/settings.gradle*                           @gradle/bt-developer-productivity
+subprojects/internal-architecture-testing/  @gradle/bt-developer-productivity
+subprojects/internal-build-reports/         @gradle/bt-developer-productivity
+subprojects/internal-integ-testing/         @gradle/bt-developer-productivity
+subprojects/internal-performance-testing/   @gradle/bt-developer-productivity
+subprojects/internal-testing/               @gradle/bt-developer-productivity
+
+# gitStream files
+.cm/                                @tresat
+.github/workflows/gitstream.yml     @tresat
+
+# Architecture council
+.github/CODEOWNERS @gradle/bt-architecture-council
+
+# IDE Experience team
+subprojects/ide/                  @gradle/bt-ide-experience
+subprojects/tooling-api/          @gradle/bt-ide-experience
+subprojects/tooling-api-builders/ @gradle/bt-ide-experience
+
+# Documentation
+subprojects/docs/src/docs/                                                        @gradle/bt-devrel-education
+subprojects/docs/src/samples/                                                     @gradle/bt-devrel-education
+subprojects/docs/src/docs-asciidoctor-extensions-base/                            @gradle/bt-devrel-education
+subprojects/docs/src/docs-asciidoctor-extensions/                                 @gradle/bt-devrel-education
+
+subprojects/docs/src/snippets/kotlinDsl/                                                @eskatos @jbartok @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/api/kotlin_dsl.adoc                                 @eskatos @jbartok @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/migration/migrating_from_groovy_to_kotlin_dsl.adoc  @eskatos @jbartok @gradle/bt-devrel-education
+
+subprojects/docs/src/docs/userguide/core-plugins/base_plugin.adoc                 @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/build_dashboard_plugin.adoc      @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/build_init_plugin.adoc           @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/checkstyle_plugin.adoc           @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/codenarc_plugin.adoc             @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/distribution_plugin.adoc         @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/ear_plugin.adoc                  @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/jacoco_plugin.adoc               @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/java_gradle_plugin.adoc          @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/pmd_plugin.adoc                  @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/core-plugins/war_plugin.adoc                  @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/dep-man/                                      @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/jvm/                                          @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/reference/directory_layout.adoc               @gradle/bt-jvm @gradle/bt-devrel-education
+subprojects/docs/src/docs/userguide/troubleshooting/version_catalog_problems.adoc @gradle/bt-jvm @gradle/bt-devrel-education
diff --git a/.github/ISSUE_TEMPLATE/10_contributor_bug_report.yml b/.github/ISSUE_TEMPLATE/10_contributor_bug_report.yml
new file mode 100644
index 0000000..7737a7d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/10_contributor_bug_report.yml
@@ -0,0 +1,73 @@
+name: Bug Report
+description: Create a report to help us improve
+labels: [ "a:bug", "to-triage" ]
+assignees: [ ]
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Please follow the instructions below.
+        We receive dozens of issues every week, so to stay productive, we will close issues that don't provide enough information.
+
+        If you need help with Gradle or have a usage question, please reach [our community](http://help.gradle.org/) instead of creating an issue.
+
+        Please open Android-related issues on [the Android Issue Tracker](https://source.android.com/source/report-bugs)
+        Please open IntelliJ-related issues on [the JetBrains Issue Tracker](https://youtrack.jetbrains.com/newIssue?project=IDEA)
+        Please open Gradle Native-related issues on [the Gradle Native repository](https://github.com/gradle/gradle-native/issues)
+
+        Provide a brief summary of the issue in the title above
+  - type: textarea
+    id: expected-behavior
+    attributes:
+      label: Expected Behavior
+      description: Tell us what should happen
+    validations:
+      required: true
+  - type: textarea
+    id: current-behavior
+    attributes:
+      label: Current Behavior
+      description: Tell us what happens instead of the expected behavior
+    validations:
+      required: true
+  - type: textarea
+    id: context
+    attributes:
+      label: Context (optional)
+      description: |
+        How has this issue affected you? What are you trying to accomplish?
+        Providing context helps us come up with a solution that is most useful in the real world
+    validations:
+      required: false
+  - type: textarea
+    id: steps-to-reproduce
+    attributes:
+      label: Steps to Reproduce
+      description: |
+        Provide a self-contained example project as an attached archive or a Github project.
+        You can use [the template](https://github.com/gradle/gradle-issue-reproducer) with a Gradle GitHub action set up to showcase your problem.
+        In the rare cases where this is infeasible, we will also accept a detailed set of instructions.
+        You can also use [Gradle Project Replicator](https://github.com/android/project-replicator) to reproduce the structure of your project.
+    validations:
+      required: true
+  - type: input
+    id: gradle-version
+    attributes:
+      label: Gradle version
+      description: What version of Gradle are you running?
+    validations:
+      required: true
+  - type: input
+    id: build-scan-url
+    attributes:
+      label: Build scan URL (optional)
+    validations:
+      required: false
+  - type: textarea
+    id: environment
+    attributes:
+      label: Your Environment (optional)
+      description: |
+        Include as many relevant details about the environment you experienced the bug in
+    validations:
+      required: false
diff --git a/.github/ISSUE_TEMPLATE/20_contributor_feature_request.yml b/.github/ISSUE_TEMPLATE/20_contributor_feature_request.yml
new file mode 100644
index 0000000..4c685ee
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/20_contributor_feature_request.yml
@@ -0,0 +1,39 @@
+name: Feature request
+description: Suggest an idea for this project
+labels: [ "a:feature", "to-triage" ]
+assignees: [ ]
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Please follow the instructions below.
+        We receive dozens of issues every week, so to stay productive, we will close issues that don't provide enough information.
+
+        Please open Android-related issues on [the Android Issue Tracker](https://source.android.com/source/report-bugs)
+        Please open IntelliJ-related issues on [the JetBrains Issue Tracker](https://youtrack.jetbrains.com/newIssue?project=IDEA)
+        Please open Gradle Native-related issues on [the Gradle Native repository](https://github.com/gradle/gradle-native/issues)
+
+        Provide a brief summary of the issue in the title above
+  - type: textarea
+    id: expected-behavior
+    attributes:
+      label: Expected Behavior
+      description: Tell us how it should work
+    validations:
+      required: true
+  - type: textarea
+    id: current-behavior
+    attributes:
+      label: Current Behavior (optional)
+      description: Explain the difference from current behavior
+    validations:
+      required: false
+  - type: textarea
+    id: context
+    attributes:
+      label: Context
+      description: |
+        How has this issue affected you? What are you trying to accomplish? What other alternatives have you considered?
+        Providing context helps us come up with a solution that is most useful in the real world
+    validations:
+      required: true
diff --git a/.github/ISSUE_TEMPLATE/30_contributor_regression.yml b/.github/ISSUE_TEMPLATE/30_contributor_regression.yml
new file mode 100644
index 0000000..560d8b2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/30_contributor_regression.yml
@@ -0,0 +1,72 @@
+name: Regression
+description: Report a problem about something that used to work
+labels: [ "a:regression", "to-triage" ]
+assignees: [ ]
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Please use our bug report template to report problems with something that has never worked.
+        Regressions reports are greatly appreciated during our RC phase and before a final release.
+  - type: textarea
+    id: expected-behavior
+    attributes:
+      label: Expected Behavior
+      description: Tell us what should happen
+    validations:
+      required: true
+  - type: textarea
+    id: current-behavior
+    attributes:
+      label: Current Behavior
+      description: Tell us what happens instead of the expected behavior
+    validations:
+      required: true
+  - type: textarea
+    id: context
+    attributes:
+      label: Context (optional)
+      description: |
+        How has this issue affected you? What are you trying to accomplish?
+        Providing context helps us come up with a solution that is most useful in the real world
+    validations:
+      required: false
+  - type: textarea
+    id: steps-to-reproduce
+    attributes:
+      label: Steps to Reproduce
+      description: |
+        Provide a self-contained example project as an attached archive or a Github project.
+        You can use [the template](https://github.com/gradle/gradle-issue-reproducer) with a Gradle GitHub action set up to showcase your problem.
+        In the rare cases where this is infeasible, we will also accept a detailed set of instructions.
+        You can also use [Gradle Project Replicator](https://github.com/android/project-replicator) to reproduce the structure of your project.
+    validations:
+      required: true
+  - type: input
+    id: gradle-version
+    attributes:
+      label: Gradle version
+      description: What version of Gradle are you running?
+    validations:
+      required: true
+  - type: input
+    id: gradle-old-version
+    attributes:
+      label: Gradle version that used to work
+      description: What version of Gradle gives proper result for your case?
+    validations:
+      required: true
+  - type: input
+    id: build-scan-url
+    attributes:
+      label: Build scan URL (optional)
+    validations:
+      required: false
+  - type: textarea
+    id: environment
+    attributes:
+      label: Your Environment (optional)
+      description: |
+        Include as many relevant details about the environment you experienced the bug in
+    validations:
+      required: false
diff --git a/.github/ISSUE_TEMPLATE/40_contributor_documentation.yml b/.github/ISSUE_TEMPLATE/40_contributor_documentation.yml
new file mode 100644
index 0000000..fa77936
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/40_contributor_documentation.yml
@@ -0,0 +1,45 @@
+name: Documentation
+description: Report a problem with our documentation
+labels: [ "a:documentation", "to-triage" ]
+assignees: [ ]
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Please search related information in our [latest documentation](https://docs.gradle.org/current/userguide/userguide.html) before opening a documentation issue.
+        If you need help with Gradle or have a usage question, please reach [our community](http://help.gradle.org/) instead of creating an issue.
+
+  - type: dropdown
+    id: issue-type
+    attributes:
+      label: Issue type
+      options:
+        - Typo
+        - Wrong or misleading information
+        - Missing information
+    validations:
+      required: true
+  - type: textarea
+    id: description
+    attributes:
+      label: Problem description
+      description: |
+        Please describe the problem as concisely as possible.
+    validations:
+      required: true
+  - type: textarea
+    id: context
+    attributes:
+      label: Context (optional)
+      description: |
+        How has this issue affected you? What are you trying to accomplish?
+        Providing context helps us come up with a solution that is most useful in the real world
+    validations:
+      required: false
+  - type: input
+    id: page
+    attributes:
+      label: Page with the problem
+      description: Please, provide a link to the relevant documentation page
+    validations:
+      required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 126e135..168591b 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,8 @@
-blank_issues_enabled: true
+blank_issues_enabled: false
 contact_links:
-  - name: Gradle Discussion Forums
-    url: https://discuss.gradle.org
-    about: Please ask and answer questions here.
+  - name: Ask for help
+    url: http://help.gradle.org/
+    about: If you need help with Gradle or have a usage question, please reach our community instead of creating an issue.
+  - name: Create an issue without template
+    url: https://github.com/gradle/gradle/issues/new
+    about: This option is only available for repository maintainers.
diff --git a/.github/ISSUE_TEMPLATE/contributor_bug_report.md b/.github/ISSUE_TEMPLATE/contributor_bug_report.md
deleted file mode 100644
index 65c8015..0000000
--- a/.github/ISSUE_TEMPLATE/contributor_bug_report.md
+++ /dev/null
@@ -1,40 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: a:bug, to-triage
-assignees: ''
-
----
-
-<!--- 
-Please follow the instructions below. 
-We receive dozens of issues every week, so to stay productive, we will close issues that don't provide enough information. 
-
-Please open Android-related issues on the Android Issue Tracker at https://source.android.com/source/report-bugs
-Please open Gradle Native-related issues at https://github.com/gradle/gradle-native/issues
--->
-
-<!--- Provide a brief summary of the issue in the title above -->
-
-### Expected Behavior
-<!--- Tell us what should happen -->
-
-### Current Behavior
-<!--- Tell us what happens instead of the expected behavior -->
-
-### Context
-<!--- How has this issue affected you? What are you trying to accomplish? -->
-<!--- Providing context helps us come up with a solution that is most useful in the real world -->
-
-### Steps to Reproduce 
-<!---
-Provide a self-contained example project as an attached archive or a Github project.
-You can use the following template with a Gradle GitHub action set up to showcase your problem: https://github.com/gradle/gradle-issue-reproducer
-In the rare cases where this is infeasible, we will also accept a detailed set of instructions.
--->
-
-### Your Environment
-<!--- Include as many relevant details about the environment you experienced the bug in -->
-<!--- A build scan https://scans.gradle.com/get-started is ideal -->
-Build scan URL:
diff --git a/.github/ISSUE_TEMPLATE/contributor_feature_request.md b/.github/ISSUE_TEMPLATE/contributor_feature_request.md
deleted file mode 100644
index 2a5f3b3..0000000
--- a/.github/ISSUE_TEMPLATE/contributor_feature_request.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: a:feature, to-triage
-assignees: ''
-
----
-
-<!--- 
-Please follow the instructions below. 
-We receive dozens of issues every week, so to stay productive, we will close issues that don't provide enough information. 
-
-Please open Android-related issues on the Android Issue Tracker at https://source.android.com/source/report-bugs
-Please open Gradle Native-related issues at https://github.com/gradle/gradle-native/issues
--->
-
-<!--- Provide a brief summary of the issue in the title above -->
-
-### Expected Behavior
-<!--- Tell us how it should work -->
-
-### Current Behavior
-<!--- Explain the difference from current behavior -->
-
-### Context
-<!--- How has this issue affected you? What are you trying to accomplish? What other alternatives have you considered? -->
-<!--- Providing context helps us come up with a solution that is most useful in the real world -->
diff --git a/.github/ISSUE_TEMPLATE/contributor_regression.md b/.github/ISSUE_TEMPLATE/contributor_regression.md
deleted file mode 100644
index 9ff08fe..0000000
--- a/.github/ISSUE_TEMPLATE/contributor_regression.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-name: Regression
-about: Report a problem about something that used to work
-title: ''
-labels: a:regression, to-triage
-assignees: ''
-
----
-
-<!--- 
-Please use our bug report template to report problems with something that has never worked.  
-Regressions reports are greatly appreciated during our RC phase and before a final release.
--->
-
-<!--- Provide a brief summary of the issue in the title above -->
-
-### Expected Behavior
-<!--- Tell us what should happen -->
-
-### Current Behavior
-<!--- Tell us what happens instead of the expected behavior -->
-
-### Context
-<!--- Which version did this stop working with? Which version do you know it works? -->
-<!--- How has this issue affected you? What are you trying to accomplish? -->
-<!--- Providing context helps us come up with a solution that is most useful in the real world -->
-
-### Steps to Reproduce 
-<!--- Provide a self-contained example project (as an attached archive or a Github project). -->
-<!--- In the rare cases where this is infeasible, we will also accept a detailed set of instructions. -->
-
-### Your Environment
-<!--- Include as many relevant details about the environment you experienced the bug in -->
-<!--- A build scan https://scans.gradle.com/get-started is ideal -->
-Build scan URL:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index d9228c0..aed703b 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -23,7 +23,7 @@
       matrix:
         # Override automatic language detection by changing the below list
         # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
-        language: ['javascript']
+        language: ['java', 'javascript']
         # Learn more...
         # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
 
@@ -59,6 +59,7 @@
       uses: github/codeql-action/init@v2
       with:
         languages: ${{ matrix.language }}
+        tools: latest
         # If you wish to specify custom queries, you can do so here or in a config file.
         # By default, queries listed here will override any specified in a config file.
         # Prefix the list here with "+" to use these queries and those in the config file.
diff --git a/.github/workflows/gitstream.yml b/.github/workflows/gitstream.yml
new file mode 100644
index 0000000..c7942eb
--- /dev/null
+++ b/.github/workflows/gitstream.yml
@@ -0,0 +1,48 @@
+# Code generated by gitStream GitHub app - DO NOT EDIT
+
+name: gitStream workflow automation
+run-name: |
+  /:\ gitStream: PR #${{ fromJSON(fromJSON(github.event.inputs.client_payload)).pullRequestNumber }} from ${{ github.event.inputs.full_repository }}
+
+on:
+  workflow_dispatch:
+    inputs:
+      client_payload:
+        description: The Client payload
+        required: true
+      full_repository:
+        description: the repository name include the owner in `owner/repo_name` format
+        required: true
+      head_ref:
+        description: the head sha
+        required: true
+      base_ref:
+        description: the base ref
+        required: true
+      installation_id:
+        description: the installation id
+        required: false
+      resolver_url:
+        description: the resolver url to pass results to
+        required: true
+      resolver_token:
+        description: Optional resolver token for resolver service
+        required: false
+        default: ''
+
+jobs:
+  gitStream:
+    timeout-minutes: 15
+    runs-on: ubuntu-latest
+    name: gitStream workflow automation
+    steps:
+      - name: Evaluate Rules
+        uses: linear-b/gitstream-github-action@v1
+        id: rules-engine
+        with:
+          full_repository: ${{ github.event.inputs.full_repository }}
+          head_ref: ${{ github.event.inputs.head_ref }}
+          base_ref: ${{ github.event.inputs.base_ref }}
+          client_payload: ${{ github.event.inputs.client_payload }}
+          installation_id: ${{ github.event.inputs.installation_id }}
+          resolver_url: ${{ github.event.inputs.resolver_url }}
diff --git a/.github/workflows/notify-on-rc-for-manual-test.yml b/.github/workflows/notify-on-rc-for-manual-test.yml
new file mode 100644
index 0000000..76125c9
--- /dev/null
+++ b/.github/workflows/notify-on-rc-for-manual-test.yml
@@ -0,0 +1,30 @@
+name: IDE Experience team notifier
+run-name: Notify the IDE Experience team about new RCs for manual testing
+on:
+  push:
+    tags:
+      - 'v*.*.*-RC1'
+
+jobs:
+  send-slack-notification:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Send Slack notification about new RCs for manual testing
+        id: slack
+        uses: slackapi/slack-github-action@v1.23.0
+        with:
+          payload: |
+            {
+              "text": "<https://github.com/gradle/gradle/${{ github.event.ref }}|[gradle/gradle#${{ github.event.ref }}]> has been pushed",
+              "blocks": [
+                {
+                  "type": "section",
+                  "text": {
+                    "type": "mrkdwn",
+                    "text": "<https://github.com/gradle/gradle/${{ github.event.ref }}|[gradle/gradle#${{ github.event.ref }}]> has been pushed"
+                  }
+                }
+              ]
+            }
+        env:
+          SLACK_WEBHOOK_URL: ${{ secrets.IDE_EXPERIENCE_TEAM_SLACK_WEBHOOK_URL }}
diff --git a/.github/workflows/slack-notifier.yml b/.github/workflows/slack-notifier.yml
new file mode 100644
index 0000000..53269298
--- /dev/null
+++ b/.github/workflows/slack-notifier.yml
@@ -0,0 +1,31 @@
+name: IDE Experience team notifier
+run-name: Notify the IDE Experience team about relevant issues
+on:
+  issues:
+    types:
+      - labeled
+
+jobs:
+  send-slack-notification:
+    if: ${{ github.event.label.name == 'in:ide' || github.event.label.name == 'in:eclipse-plugin' || github.event.label.name == 'in:idea-plugin' || github.event.label.name == 'in:tooling-api' }}
+    runs-on: ubuntu-latest
+    steps:
+      - name: Send Slack notification about issues with specific labels
+        id: slack
+        uses: slackapi/slack-github-action@v1.23.0
+        with:
+          payload: |
+            {
+              "text": "<https://github.com/gradle/gradle/issues/${{ github.event.issue.number }}|[gradle/gradle#${{ github.event.issue.number }}]> `${{ github.event.issue.title }}` has label `${{ github.event.label.name }}`",
+              "blocks": [
+                {
+                  "type": "section",
+                  "text": {
+                    "type": "mrkdwn",
+                    "text": "<https://github.com/gradle/gradle/issues/${{ github.event.issue.number }}|[gradle/gradle#${{ github.event.issue.number }}]> `${{ github.event.issue.title }}` has label `${{ github.event.label.name }}`"
+                  }
+                }
+              ]
+            }
+        env:
+          SLACK_WEBHOOK_URL: ${{ secrets.IDE_EXPERIENCE_TEAM_SLACK_WEBHOOK_URL }}
diff --git a/.gitignore b/.gitignore
index 3cc3b1e..bafc0ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -102,3 +102,6 @@
 # Added GE support maven support to the maven build in .teamcity, per the GE docs, this dir is NOT to be committed
 .teamcity/.mvn/.gradle-enterprise/
 /discoclient.properties
+
+# Ignore local configuration files for asdf, allowing the JDK to be configured for project (https://asdf-vm.com)
+.tool-versions
diff --git a/.idea/inspectionProfiles/Gradle.xml b/.idea/inspectionProfiles/Gradle.xml
index 34612b8..ef7ae6f 100644
--- a/.idea/inspectionProfiles/Gradle.xml
+++ b/.idea/inspectionProfiles/Gradle.xml
@@ -52,4 +52,4 @@
       </option>
     </inspection_tool>
   </profile>
-</component>
\ No newline at end of file
+</component>
diff --git a/.teamcity/src/main/kotlin/common/Os.kt b/.teamcity/src/main/kotlin/common/Os.kt
index 106d76e..26fdd8d 100644
--- a/.teamcity/src/main/kotlin/common/Os.kt
+++ b/.teamcity/src/main/kotlin/common/Os.kt
@@ -68,7 +68,7 @@
     val perfTestWorkingDir: String = "%teamcity.build.checkoutDir%",
     val perfTestJavaVendor: JvmVendor = JvmVendor.openjdk,
     val buildJavaVersion: JvmVersion = JvmVersion.java11,
-    val perfTestJavaVersion: JvmVersion = JvmVersion.java11,
+    val perfTestJavaVersion: JvmVersion = JvmVersion.java17,
     val defaultArch: Arch = Arch.AMD64
 ) {
     LINUX(
diff --git a/.teamcity/src/main/kotlin/common/extensions.kt b/.teamcity/src/main/kotlin/common/extensions.kt
index 4a91bcb..178c3d8 100644
--- a/.teamcity/src/main/kotlin/common/extensions.kt
+++ b/.teamcity/src/main/kotlin/common/extensions.kt
@@ -31,6 +31,7 @@
 import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
 import jetbrains.buildServer.configs.kotlin.v2019_2.Dependencies
 import jetbrains.buildServer.configs.kotlin.v2019_2.FailureAction
+import jetbrains.buildServer.configs.kotlin.v2019_2.Project
 import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId
 import jetbrains.buildServer.configs.kotlin.v2019_2.Requirements
 import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
@@ -273,3 +274,27 @@
 }
 
 fun String.toCapitalized() = this.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
+
+/**
+ * Define clean up rules for the project.
+ * See https://www.jetbrains.com/help/teamcity/teamcity-data-clean-up.html#Clean-up+Rules
+ *
+ * @param historyDays days number of days to store build history .
+ * @param artifactsDays number of days to store artifacts. In the stored history, artifacts older than this number will be cleaned up.
+ * @param artifactPatterns patterns for artifacts clean-up. If not specified, all artifacts will be removed.
+ */
+fun Project.cleanupRule(historyDays: Int, artifactsDays: Int, artifactsPatterns: String? = null) {
+    features {
+        this@cleanupRule.cleanup {
+            baseRule {
+                history(days = historyDays)
+            }
+            baseRule {
+                artifacts(
+                    days = artifactsDays,
+                    artifactPatterns = artifactsPatterns
+                )
+            }
+        }
+    }
+}
diff --git a/.teamcity/src/main/kotlin/configurations/CompileAllBuildCacheNG.kt b/.teamcity/src/main/kotlin/configurations/CompileAllBuildCacheNG.kt
new file mode 100644
index 0000000..2a2a3ce
--- /dev/null
+++ b/.teamcity/src/main/kotlin/configurations/CompileAllBuildCacheNG.kt
@@ -0,0 +1,25 @@
+package configurations
+
+import model.CIBuildModel
+import model.Stage
+
+class CompileAllBuildCacheNG(model: CIBuildModel, stage: Stage) : BaseGradleBuildType(stage = stage, init = {
+    id("${model.projectId}_CompileAllBuild_BuildCacheNG")
+    name = "Compile All BuildCacheNG"
+    description = "Compiles all production/test source code and warms up the build cache NG"
+
+    applyDefaults(
+        model,
+        this,
+        "compileAllBuild -PignoreIncomingBuildReceipt=true -DdisableLocalCache=false -Dorg.gradle.unsafe.cache.ng=true",
+        extraParameters = "-Porg.gradle.java.installations.auto-download=false"
+    )
+
+    params {
+        param("env.GRADLE_CACHE_REMOTE_URL", "%gradle.experimental.cache.ng.remote.url%")
+    }
+
+    artifactRules = """$artifactRules
+        subprojects/base-services/build/generated-resources/build-receipt/org/gradle/build-receipt.properties
+    """.trimIndent()
+})
diff --git a/.teamcity/src/main/kotlin/configurations/GradleBuildConfigurationDefaults.kt b/.teamcity/src/main/kotlin/configurations/GradleBuildConfigurationDefaults.kt
index ef9e8de..5385a15 100644
--- a/.teamcity/src/main/kotlin/configurations/GradleBuildConfigurationDefaults.kt
+++ b/.teamcity/src/main/kotlin/configurations/GradleBuildConfigurationDefaults.kt
@@ -179,7 +179,7 @@
             dependsOn(RelativeId(stageTriggerId(model, StageName.QUICK_FEEDBACK_LINUX_ONLY)))
         }
     }
-    if (buildType !is CompileAll) {
+    if (buildType !is CompileAll && buildType !is CompileAllBuildCacheNG) {
         buildType.dependencies {
             compileAllDependency(CompileAll.buildTypeId(model))
         }
diff --git a/.teamcity/src/main/kotlin/configurations/Gradleception.kt b/.teamcity/src/main/kotlin/configurations/Gradleception.kt
index f71b2cc..69eef93 100644
--- a/.teamcity/src/main/kotlin/configurations/Gradleception.kt
+++ b/.teamcity/src/main/kotlin/configurations/Gradleception.kt
@@ -91,7 +91,7 @@
 
             localGradle {
                 name = "QUICKCHECK_WITH_GRADLE_BUILT_BY_GRADLE"
-                tasks = "clean sanityCheck test --dry-run"
+                tasks = "clean sanityCheck test " + if (bundleGroovy4) "--dry-run" else ""
                 gradleHome = "%teamcity.build.checkoutDir%/dogfood-second"
                 gradleParams = defaultParameters
             }
diff --git a/.teamcity/src/main/kotlin/model/CIBuildModel.kt b/.teamcity/src/main/kotlin/model/CIBuildModel.kt
index 80ff038..2009079 100644
--- a/.teamcity/src/main/kotlin/model/CIBuildModel.kt
+++ b/.teamcity/src/main/kotlin/model/CIBuildModel.kt
@@ -13,6 +13,7 @@
 import configurations.BuildDistributions
 import configurations.CheckLinks
 import configurations.CompileAll
+import configurations.CompileAllBuildCacheNG
 import configurations.DocsTestType
 import configurations.DocsTestType.CONFIG_CACHE_DISABLED
 import configurations.DocsTestType.CONFIG_CACHE_ENABLED
@@ -33,7 +34,8 @@
     READY_FOR_RELEASE("Ready for Release", "Once a day: Rerun tests in more environments", "ReadyforRelease"),
     HISTORICAL_PERFORMANCE("Historical Performance", "Once a week: Run performance tests for multiple Gradle versions", "HistoricalPerformance"),
     EXPERIMENTAL_VFS_RETENTION("Experimental FS Watching", "On demand checks to run tests with file system watching enabled", "ExperimentalVfsRetention"),
-    EXPERIMENTAL_PERFORMANCE("Experimental Performance", "Try out new performance test running", "ExperimentalPerformance");
+    EXPERIMENTAL_PERFORMANCE("Experimental Performance", "Try out new performance test running", "ExperimentalPerformance"),
+    EXPERIMENTAL_BUILD_CACHE_NG("Experimental BuildCacheNG", "Try out new build cache", "ExperimentalBuildCacheNG");
 
     val id: String
         get() = stageName.replace(" ", "").replace("-", "")
@@ -180,6 +182,12 @@
                 PerformanceTestCoverage(12, PerformanceTestType.per_commit, Os.MACOS, numberOfBuckets = 5, withoutDependencies = true),
                 PerformanceTestCoverage(13, PerformanceTestType.per_day, Os.LINUX, numberOfBuckets = 30, withoutDependencies = true)
             )
+        ),
+        Stage(
+            StageName.EXPERIMENTAL_BUILD_CACHE_NG,
+            trigger = Trigger.eachCommit,
+            runsIndependent = true,
+            specificBuilds = listOf(SpecificBuild.CompileAllBuildCacheNG),
         )
     ),
     val subprojects: GradleSubprojectProvider
@@ -349,6 +357,11 @@
             return CompileAll(model, stage)
         }
     },
+    CompileAllBuildCacheNG {
+        override fun create(model: CIBuildModel, stage: Stage): BaseGradleBuildType {
+            return CompileAllBuildCacheNG(model, stage)
+        }
+    },
     SanityCheck {
         override fun create(model: CIBuildModel, stage: Stage): BaseGradleBuildType {
             return SanityCheck(model, stage)
diff --git a/.teamcity/src/main/kotlin/projects/CheckProject.kt b/.teamcity/src/main/kotlin/projects/CheckProject.kt
index e34feb0..99c5b3a 100644
--- a/.teamcity/src/main/kotlin/projects/CheckProject.kt
+++ b/.teamcity/src/main/kotlin/projects/CheckProject.kt
@@ -1,5 +1,6 @@
 package projects
 
+import common.cleanupRule
 import common.hiddenArtifactDestination
 import configurations.PerformanceTestsPass
 import configurations.StagePasses
@@ -65,18 +66,12 @@
     buildTypesOrder = buildTypes
     subProjectsOrder = subProjects
 
-    cleanup {
-        baseRule {
-            history(days = 14)
-        }
-        baseRule {
-            artifacts(
-                days = 14,
-                artifactPatterns = """
+    cleanupRule(
+        historyDays = 14,
+        artifactsDays = 7,
+        artifactsPatterns = """
                 +:**/*
                 +:$hiddenArtifactDestination/**/*"
-                """.trimIndent()
-            )
-        }
-    }
+        """.trimIndent()
+    )
 })
diff --git a/.teamcity/src/main/kotlin/promotion/BasePublishGradleDistribution.kt b/.teamcity/src/main/kotlin/promotion/BasePublishGradleDistribution.kt
index 6578dbc..52385c6 100644
--- a/.teamcity/src/main/kotlin/promotion/BasePublishGradleDistribution.kt
+++ b/.teamcity/src/main/kotlin/promotion/BasePublishGradleDistribution.kt
@@ -19,6 +19,7 @@
 import common.gradleWrapper
 import common.promotionBuildParameters
 import jetbrains.buildServer.configs.kotlin.v2019_2.BuildSteps
+import jetbrains.buildServer.configs.kotlin.v2019_2.FailureAction
 import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId
 import vcsroots.gradlePromotionMaster
 
@@ -47,6 +48,8 @@
         dependencies {
             snapshot(RelativeId("Check_Stage_${this@BasePublishGradleDistribution.triggerName}_Trigger")) {
                 synchronizeRevisions = false
+                onDependencyFailure = FailureAction.FAIL_TO_START
+                onDependencyCancel = FailureAction.FAIL_TO_START
             }
         }
 
diff --git a/.teamcity/src/main/kotlin/promotion/PromotionProject.kt b/.teamcity/src/main/kotlin/promotion/PromotionProject.kt
index 1d26bd6..fdcc0ab 100644
--- a/.teamcity/src/main/kotlin/promotion/PromotionProject.kt
+++ b/.teamcity/src/main/kotlin/promotion/PromotionProject.kt
@@ -3,6 +3,7 @@
 import common.BuildToolBuildJvm
 import common.Os
 import common.VersionedSettingsBranch
+import common.cleanupRule
 import common.javaHome
 import jetbrains.buildServer.configs.kotlin.v2019_2.Project
 
@@ -10,6 +11,8 @@
     id("Promotion")
     name = "Promotion"
 
+    cleanupRule(historyDays = 14, artifactsDays = 7)
+
     buildType(SanityCheck)
     buildType(PublishNightlySnapshot(branch))
     buildType(PublishNightlySnapshotFromQuickFeedback(branch))
diff --git a/.teamcity/src/main/kotlin/util/AdHocPerformanceScenario.kt b/.teamcity/src/main/kotlin/util/AdHocPerformanceScenario.kt
index 3d3503d..aa8cdd3 100644
--- a/.teamcity/src/main/kotlin/util/AdHocPerformanceScenario.kt
+++ b/.teamcity/src/main/kotlin/util/AdHocPerformanceScenario.kt
@@ -50,7 +50,7 @@
             allowEmpty = false,
             description = "Which performance test to run. Should be the fully qualified class name dot (unrolled) method name. E.g. org.gradle.performance.regression.java.JavaUpToDatePerformanceTest.up-to-date assemble (parallel true)"
         )
-        text("testJavaVersion", "8", display = ParameterDisplay.PROMPT, allowEmpty = false, description = "The java version to run the performance tests, e.g. 8/11/17")
+        text("testJavaVersion", "17", display = ParameterDisplay.PROMPT, allowEmpty = false, description = "The java version to run the performance tests, e.g. 8/11/17")
         select(
             "testJavaVendor",
             JvmVendor.openjdk.name,
diff --git a/.teamcity/src/test/kotlin/PerformanceTestBuildTypeTest.kt b/.teamcity/src/test/kotlin/PerformanceTestBuildTypeTest.kt
index 2bb72ae..434ad06 100644
--- a/.teamcity/src/test/kotlin/PerformanceTestBuildTypeTest.kt
+++ b/.teamcity/src/test/kotlin/PerformanceTestBuildTypeTest.kt
@@ -89,7 +89,7 @@
 
         val expectedRunnerParams = listOf(
             "-PperformanceBaselines=%performance.baselines%",
-            "-PtestJavaVersion=11",
+            "-PtestJavaVersion=17",
             "-PtestJavaVendor=openjdk",
             "-PautoDownloadAndroidStudio=true",
             "-PrunAndroidStudioInHeadlessMode=true",
diff --git a/.teamcity/subprojects.json b/.teamcity/subprojects.json
index 4b0bf4e..8ec02a3 100644
--- a/.teamcity/subprojects.json
+++ b/.teamcity/subprojects.json
@@ -367,7 +367,14 @@
     "dirName": "instrumentation-agent",
     "name": "instrumentation-agent",
     "unitTests": false,
-    "functionalTests": true,
+    "functionalTests": false,
+    "crossVersionTests": false
+  },
+  {
+    "dirName": "instrumentation-declarations",
+    "name": "instrumentation-declarations",
+    "unitTests": false,
+    "functionalTests": false,
     "crossVersionTests": false
   },
   {
@@ -392,6 +399,20 @@
     "crossVersionTests": false
   },
   {
+    "dirName": "internal-instrumentation-api",
+    "name": "internal-instrumentation-api",
+    "unitTests": false,
+    "functionalTests": false,
+    "crossVersionTests": false
+  },
+  {
+    "dirName": "internal-instrumentation-processor",
+    "name": "internal-instrumentation-processor",
+    "unitTests": true,
+    "functionalTests": false,
+    "crossVersionTests": false
+  },
+  {
     "dirName": "internal-integ-testing",
     "name": "internal-integ-testing",
     "unitTests": true,
@@ -472,7 +493,7 @@
     "dirName": "kotlin-dsl-tooling-builders",
     "name": "kotlin-dsl-tooling-builders",
     "unitTests": true,
-    "functionalTests": false,
+    "functionalTests": true,
     "crossVersionTests": true
   },
   {
diff --git a/.teamcity/test-buckets.json b/.teamcity/test-buckets.json
index 83a807a..6b81b83 100644
--- a/.teamcity/test-buckets.json
+++ b/.teamcity/test-buckets.json
@@ -38,7 +38,17 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"plugins"
+					"plugins",
+					"java-compiler-plugin",
+					"logging-api",
+					"base-services-groovy",
+					"docs-asciidoctor-extensions-base",
+					"kotlin-dsl-provider-plugins",
+					"internal-instrumentation-processor",
+					"native",
+					"security",
+					"file-temp",
+					"functional"
 				]
 			},
 			{
@@ -47,24 +57,7 @@
 				},
 				"subprojects":[
 					"launcher",
-					"java-compiler-plugin",
-					"logging-api",
-					"base-services-groovy",
-					"docs-asciidoctor-extensions-base",
-					"kotlin-dsl-provider-plugins",
-					"native",
-					"security",
-					"file-temp",
-					"functional",
-					"worker-processes"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"scala",
+					"worker-processes",
 					"problems",
 					"files",
 					"internal-testing",
@@ -73,26 +66,7 @@
 					"cli",
 					"build-operations",
 					"normalization-java",
-					"docs",
-					"language-jvm"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"plugin-use",
-					"tooling-api-builders",
-					"build-option",
-					"hashing",
-					"execution",
-					"resources-http",
-					"instrumentation-agent",
-					"internal-performance-testing",
-					"wrapper-shared",
-					"snapshots",
-					"publish"
+					"docs"
 				]
 			},
 			{
@@ -101,16 +75,34 @@
 				},
 				"subprojects":[
 					"testing-jvm",
-					"resources",
-					"internal-integ-testing",
-					"process-services",
+					"language-jvm",
+					"tooling-api-builders",
+					"build-option",
+					"hashing",
+					"execution",
+					"resources-http",
+					"internal-performance-testing",
 					"jvm-services",
+					"publish",
+					"snapshots"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"scala",
+					"resources",
+					"wrapper-shared",
+					"internal-integ-testing",
 					"base-services",
+					"process-services",
 					"messaging",
 					"build-profile",
+					"resources-gcs",
 					"ear",
-					"core-api",
-					"resources-gcs"
+					"core-api"
 				]
 			},
 			{
@@ -119,15 +111,15 @@
 				},
 				"subprojects":[
 					"language-java",
+					"resources-sftp",
+					"testing-base",
 					"build-cache",
 					"build-events",
 					"tooling-native",
-					"platform-jvm",
-					"testing-base",
-					"resources-sftp",
 					"model-groovy",
-					"reporting",
 					"wrapper",
+					"reporting",
+					"platform-jvm",
 					"build-cache-http"
 				]
 			},
@@ -136,13 +128,13 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"language-native",
-					"ide-native",
+					"plugin-use",
 					"platform-base",
 					"diagnostics",
-					"antlr",
+					"ide-native",
 					"resources-s3",
 					"signing",
+					"antlr",
 					"language-groovy",
 					"test-kit",
 					"platform-native",
@@ -154,16 +146,27 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"model-core",
+					"kotlin-dsl-tooling-builders",
 					"file-watching",
 					"file-collections",
+					"version-control",
 					"jacoco",
 					"testing-native",
-					"version-control",
-					"kotlin-dsl-plugins",
-					"ivy",
 					"enterprise",
-					"kotlin-dsl",
+					"ivy",
+					"plugin-development"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"kotlin-dsl-integ-tests",
+					"ide",
+					"kotlin-dsl-plugins",
+					"samples",
+					"workers",
 					"maven"
 				]
 			},
@@ -172,13 +175,11 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"kotlin-dsl-tooling-builders",
-					"samples",
-					"plugin-development",
-					"code-quality",
-					"composite-builds",
+					"language-native",
 					"logging",
-					"ide"
+					"kotlin-dsl",
+					"model-core",
+					"code-quality"
 				]
 			},
 			{
@@ -186,10 +187,9 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"workers",
+					"composite-builds",
 					"integ-test",
-					"build-init",
-					"kotlin-dsl-integ-tests"
+					"build-init"
 				]
 			}
 		],
@@ -200,7 +200,7 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":7
+					"numberOfBatches":15
 				},
 				"subprojects":[
 					"dependency-management"
@@ -218,7 +218,7 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":4
+					"numberOfBatches":3
 				},
 				"subprojects":[
 					"core"
@@ -227,7 +227,7 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":4
+					"numberOfBatches":3
 				},
 				"subprojects":[
 					"tooling-api"
@@ -236,16 +236,7 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":3
-				},
-				"subprojects":[
-					"launcher"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":3
+					"numberOfBatches":2
 				},
 				"subprojects":[
 					"plugins"
@@ -257,16 +248,7 @@
 					"numberOfBatches":2
 				},
 				"subprojects":[
-					"plugin-use"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":2
-				},
-				"subprojects":[
-					"scala"
+					"launcher"
 				]
 			},
 			{
@@ -284,15 +266,6 @@
 					"numberOfBatches":2
 				},
 				"subprojects":[
-					"language-native"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":2
-				},
-				"subprojects":[
 					"language-java"
 				]
 			},
@@ -302,26 +275,7 @@
 					"numberOfBatches":2
 				},
 				"subprojects":[
-					"kotlin-dsl-tooling-builders"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"integ-test",
-					"java-compiler-plugin",
-					"logging-api",
-					"base-services-groovy",
-					"docs-asciidoctor-extensions-base",
-					"kotlin-dsl-provider-plugins",
-					"native",
-					"security",
-					"file-temp",
-					"functional",
-					"worker-processes"
+					"plugin-use"
 				]
 			},
 			{
@@ -331,16 +285,16 @@
 				},
 				"subprojects":[
 					"composite-builds",
-					"problems",
-					"files",
-					"internal-testing",
-					"testing-jvm-infrastructure",
-					"build-cache-packaging",
-					"cli",
-					"build-operations",
-					"normalization-java",
-					"docs",
-					"language-jvm"
+					"java-compiler-plugin",
+					"logging-api",
+					"base-services-groovy",
+					"docs-asciidoctor-extensions-base",
+					"kotlin-dsl-provider-plugins",
+					"internal-instrumentation-processor",
+					"native",
+					"security",
+					"file-temp",
+					"functional"
 				]
 			},
 			{
@@ -349,16 +303,35 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"model-core",
+					"scala",
+					"worker-processes",
+					"problems",
+					"files",
+					"internal-testing",
+					"testing-jvm-infrastructure",
+					"build-cache-packaging",
+					"cli",
+					"build-operations",
+					"normalization-java",
+					"docs"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"language-native",
+					"language-jvm",
 					"tooling-api-builders",
 					"build-option",
 					"hashing",
 					"execution",
 					"resources-http",
 					"internal-performance-testing",
-					"instrumentation-agent",
-					"snapshots",
 					"wrapper-shared",
+					"snapshots",
 					"internal-integ-testing"
 				]
 			},
@@ -368,36 +341,17 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"ide",
+					"model-core",
 					"resources",
 					"process-services",
 					"publish",
 					"jvm-services",
 					"base-services",
-					"messaging",
 					"build-profile",
+					"messaging",
 					"ear",
-					"core-api",
-					"build-cache"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"build-init",
 					"resources-gcs",
-					"resources-s3",
-					"model-groovy",
-					"resources-sftp",
-					"build-events",
-					"testing-base",
-					"build-cache-http",
-					"tooling-native",
-					"platform-jvm",
-					"reporting"
+					"core-api"
 				]
 			},
 			{
@@ -406,11 +360,43 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"kotlin-dsl-integ-tests",
+					"kotlin-dsl-tooling-builders",
+					"resources-s3",
+					"build-cache",
+					"build-events",
+					"resources-sftp",
+					"model-groovy",
+					"testing-base",
 					"wrapper",
-					"testing-native",
+					"tooling-native",
+					"build-cache-http"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"integ-test",
+					"platform-jvm",
 					"ide-native",
-					"platform-base"
+					"signing",
+					"reporting",
+					"diagnostics"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"ide",
+					"testing-native",
+					"platform-base",
+					"antlr",
+					"language-groovy"
 				]
 			},
 			{
@@ -420,34 +406,9 @@
 				},
 				"subprojects":[
 					"code-quality",
-					"language-groovy",
-					"signing",
-					"diagnostics",
-					"antlr"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"workers",
 					"persistent-cache",
-					"file-collections",
-					"file-watching"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"logging",
-					"version-control",
 					"test-kit",
-					"platform-native"
+					"version-control"
 				]
 			},
 			{
@@ -456,8 +417,20 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"maven",
-					"ivy",
+					"kotlin-dsl",
+					"file-watching",
+					"platform-native",
+					"file-collections"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"kotlin-dsl-plugins",
+					"enterprise",
 					"jacoco"
 				]
 			},
@@ -467,9 +440,8 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"plugin-development",
-					"kotlin-dsl-plugins",
-					"enterprise"
+					"build-init",
+					"ivy"
 				]
 			},
 			{
@@ -478,8 +450,28 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"samples",
-					"kotlin-dsl"
+					"maven",
+					"samples"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"kotlin-dsl-integ-tests",
+					"workers",
+					"logging"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"plugin-development"
 				]
 			}
 		],
@@ -500,14 +492,6 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"core"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
 					"configuration-cache"
 				]
 			},
@@ -516,7 +500,25 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"language-native"
+					"core"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"language-native",
+					"java-compiler-plugin",
+					"logging-api",
+					"base-services-groovy",
+					"docs-asciidoctor-extensions-base",
+					"kotlin-dsl-provider-plugins",
+					"internal-instrumentation-processor",
+					"native",
+					"security",
+					"file-temp",
+					"functional"
 				]
 			},
 			{
@@ -525,24 +527,7 @@
 				},
 				"subprojects":[
 					"plugins",
-					"java-compiler-plugin",
-					"logging-api",
-					"base-services-groovy",
-					"docs-asciidoctor-extensions-base",
-					"kotlin-dsl-provider-plugins",
-					"native",
-					"security",
-					"file-temp",
-					"functional",
-					"worker-processes"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"testing-jvm",
+					"worker-processes",
 					"problems",
 					"files",
 					"internal-testing",
@@ -551,8 +536,7 @@
 					"cli",
 					"build-operations",
 					"normalization-java",
-					"docs",
-					"language-jvm"
+					"docs"
 				]
 			},
 			{
@@ -560,7 +544,8 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"launcher",
+					"testing-jvm",
+					"language-jvm",
 					"tooling-api-builders",
 					"build-option",
 					"hashing",
@@ -569,77 +554,7 @@
 					"internal-performance-testing",
 					"internal-integ-testing",
 					"wrapper-shared",
-					"resources",
-					"process-services"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"tooling-api",
-					"snapshots",
-					"base-services",
-					"publish",
-					"jvm-services",
-					"messaging",
-					"build-profile",
-					"tooling-native",
-					"testing-base",
-					"instrumentation-agent",
-					"ear"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"build-init",
-					"resources-gcs",
-					"resources-sftp",
-					"platform-jvm",
-					"core-api",
-					"build-events",
-					"build-cache-http",
-					"build-cache",
-					"reporting",
-					"model-groovy",
-					"resources-s3"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"scala",
-					"persistent-cache",
-					"antlr",
-					"signing",
-					"platform-base",
-					"language-groovy",
-					"file-collections",
-					"diagnostics",
-					"file-watching",
-					"wrapper",
-					"platform-native"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"plugin-use",
-					"version-control",
-					"jacoco",
-					"ivy",
-					"ide",
-					"ide-native",
-					"enterprise",
-					"testing-native"
+					"snapshots"
 				]
 			},
 			{
@@ -648,11 +563,34 @@
 				},
 				"subprojects":[
 					"language-java",
-					"samples",
-					"kotlin-dsl",
-					"kotlin-dsl-tooling-builders",
-					"workers",
-					"kotlin-dsl-integ-tests"
+					"resources",
+					"process-services",
+					"base-services",
+					"publish",
+					"jvm-services",
+					"messaging",
+					"build-profile",
+					"tooling-native",
+					"testing-base",
+					"resources-gcs"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"tooling-api",
+					"resources-sftp",
+					"ear",
+					"build-events",
+					"platform-jvm",
+					"core-api",
+					"reporting",
+					"build-cache-http",
+					"resources-s3",
+					"model-groovy",
+					"build-cache"
 				]
 			},
 			{
@@ -661,10 +599,16 @@
 				},
 				"subprojects":[
 					"integ-test",
-					"kotlin-dsl-plugins",
-					"test-kit",
-					"code-quality",
-					"logging"
+					"antlr",
+					"persistent-cache",
+					"signing",
+					"platform-base",
+					"language-groovy",
+					"diagnostics",
+					"ide-native",
+					"file-watching",
+					"file-collections",
+					"version-control"
 				]
 			},
 			{
@@ -672,11 +616,59 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"composite-builds",
-					"model-core",
+					"plugin-use",
+					"wrapper",
+					"jacoco",
+					"enterprise",
+					"platform-native",
+					"samples",
+					"kotlin-dsl-tooling-builders",
+					"ivy"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"build-init",
+					"workers",
 					"plugin-development",
+					"ide",
+					"test-kit"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"scala",
+					"logging",
+					"kotlin-dsl",
+					"testing-native",
 					"maven"
 				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"kotlin-dsl-integ-tests",
+					"kotlin-dsl-plugins",
+					"composite-builds",
+					"code-quality"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"launcher",
+					"model-core"
+				]
 			}
 		],
 		"testCoverageUuid":3
@@ -695,10 +687,10 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":5
+					"numberOfBatches":6
 				},
 				"subprojects":[
-					"core"
+					"configuration-cache"
 				]
 			},
 			{
@@ -707,7 +699,7 @@
 					"numberOfBatches":5
 				},
 				"subprojects":[
-					"configuration-cache"
+					"core"
 				]
 			},
 			{
@@ -725,7 +717,7 @@
 					"numberOfBatches":3
 				},
 				"subprojects":[
-					"language-native"
+					"testing-jvm"
 				]
 			},
 			{
@@ -734,7 +726,7 @@
 					"numberOfBatches":2
 				},
 				"subprojects":[
-					"testing-jvm"
+					"language-native"
 				]
 			},
 			{
@@ -761,30 +753,21 @@
 					"numberOfBatches":2
 				},
 				"subprojects":[
-					"scala"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":2
-				},
-				"subprojects":[
-					"plugin-use"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":2
-				},
-				"subprojects":[
 					"composite-builds"
 				]
 			},
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
+					"numberOfBatches":2
+				},
+				"subprojects":[
+					"scala"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
 					"numberOfBatches":1
 				},
 				"subprojects":[
@@ -794,11 +777,11 @@
 					"base-services-groovy",
 					"docs-asciidoctor-extensions-base",
 					"kotlin-dsl-provider-plugins",
+					"internal-instrumentation-processor",
 					"native",
 					"security",
 					"file-temp",
-					"functional",
-					"worker-processes"
+					"functional"
 				]
 			},
 			{
@@ -808,6 +791,7 @@
 				},
 				"subprojects":[
 					"integ-test",
+					"worker-processes",
 					"problems",
 					"files",
 					"internal-testing",
@@ -816,8 +800,7 @@
 					"cli",
 					"build-operations",
 					"normalization-java",
-					"docs",
-					"language-jvm"
+					"docs"
 				]
 			},
 			{
@@ -826,7 +809,8 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"build-init",
+					"plugin-use",
+					"language-jvm",
 					"tooling-api-builders",
 					"build-option",
 					"hashing",
@@ -835,58 +819,7 @@
 					"internal-performance-testing",
 					"internal-integ-testing",
 					"wrapper-shared",
-					"process-services",
-					"resources"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"ide",
-					"snapshots",
-					"base-services",
-					"publish",
-					"jvm-services",
-					"messaging",
-					"build-profile",
-					"instrumentation-agent",
-					"testing-base",
-					"resources-gcs",
-					"ear"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"logging",
-					"resources-sftp",
-					"tooling-native",
-					"platform-jvm",
-					"build-events",
-					"reporting",
-					"core-api",
-					"build-cache-http"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"model-core",
-					"resources-s3",
-					"build-cache",
-					"persistent-cache",
-					"model-groovy",
-					"antlr",
-					"signing"
+					"process-services"
 				]
 			},
 			{
@@ -896,9 +829,69 @@
 				},
 				"subprojects":[
 					"code-quality",
+					"resources",
+					"snapshots",
+					"base-services",
+					"build-profile",
+					"publish",
+					"jvm-services",
+					"messaging",
+					"resources-gcs",
+					"tooling-native",
+					"testing-base"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"build-init",
+					"resources-sftp",
+					"ear",
+					"build-events",
+					"platform-jvm",
+					"core-api"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"ide",
+					"build-cache-http",
+					"reporting",
+					"persistent-cache",
+					"resources-s3",
+					"signing"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"model-core",
+					"model-groovy",
+					"build-cache",
+					"antlr",
+					"language-groovy"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"logging",
 					"platform-base",
-					"language-groovy",
-					"file-collections"
+					"file-collections",
+					"diagnostics"
 				]
 			},
 			{
@@ -908,8 +901,18 @@
 				},
 				"subprojects":[
 					"maven",
-					"diagnostics",
-					"jacoco",
+					"version-control",
+					"jacoco"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"kotlin-dsl-plugins",
+					"file-watching",
 					"wrapper"
 				]
 			},
@@ -919,31 +922,9 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"kotlin-dsl-tooling-builders",
-					"testing-native",
-					"version-control"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
 					"plugin-development",
-					"file-watching",
-					"platform-native"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"test-kit",
-					"enterprise",
-					"kotlin-dsl"
+					"testing-native",
+					"enterprise"
 				]
 			},
 			{
@@ -953,8 +934,28 @@
 				},
 				"subprojects":[
 					"samples",
-					"ivy",
-					"kotlin-dsl-plugins"
+					"platform-native",
+					"kotlin-dsl-tooling-builders"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"test-kit",
+					"ide-native"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"kotlin-dsl-integ-tests",
+					"kotlin-dsl"
 				]
 			},
 			{
@@ -964,16 +965,7 @@
 				},
 				"subprojects":[
 					"workers",
-					"kotlin-dsl-integ-tests"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"ide-native"
+					"ivy"
 				]
 			}
 		],
@@ -1002,15 +994,7 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"plugins"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"build-init",
+					"testing-jvm",
 					"configuration-cache",
 					"model-groovy",
 					"execution",
@@ -1019,8 +1003,8 @@
 					"internal-integ-testing",
 					"wrapper-shared",
 					"resources",
-					"process-services",
-					"snapshots"
+					"snapshots",
+					"process-services"
 				]
 			},
 			{
@@ -1028,16 +1012,16 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"testing-jvm",
+					"plugins",
 					"jvm-services",
 					"base-services",
-					"platform-base",
 					"publish",
+					"platform-base",
 					"messaging",
 					"build-profile",
-					"testing-base",
-					"instrumentation-agent",
 					"resources-gcs",
+					"kotlin-dsl-tooling-builders",
+					"testing-base",
 					"resources-sftp"
 				]
 			},
@@ -1047,32 +1031,16 @@
 				},
 				"subprojects":[
 					"launcher",
+					"resources-s3",
 					"ear",
 					"reporting",
-					"core-api",
-					"build-cache-http",
 					"build-events",
 					"platform-jvm",
-					"build-cache",
+					"core-api",
+					"build-cache-http",
 					"language-groovy",
-					"signing",
-					"resources-s3"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"plugin-use",
 					"persistent-cache",
-					"antlr",
-					"ide-native",
-					"diagnostics",
-					"platform-native",
-					"tooling-api",
-					"jacoco",
-					"version-control"
+					"antlr"
 				]
 			},
 			{
@@ -1081,32 +1049,35 @@
 				},
 				"subprojects":[
 					"language-java",
+					"build-cache",
+					"signing",
+					"ide-native",
+					"version-control",
+					"diagnostics",
+					"tooling-api"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"build-init",
+					"platform-native",
 					"file-collections",
 					"wrapper",
-					"file-watching",
+					"file-watching"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"plugin-use",
 					"enterprise",
-					"testing-native"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"language-native",
-					"code-quality",
-					"ide",
-					"ivy"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"scala",
-					"samples",
-					"kotlin-dsl",
+					"jacoco",
+					"testing-native",
 					"workers"
 				]
 			},
@@ -1115,9 +1086,18 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"composite-builds",
-					"kotlin-dsl-plugins",
+					"scala",
 					"plugin-development",
+					"samples"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"kotlin-dsl-integ-tests",
+					"test-kit",
 					"logging"
 				]
 			},
@@ -1126,9 +1106,9 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"integ-test",
-					"kotlin-dsl-integ-tests",
-					"test-kit"
+					"language-native",
+					"ivy",
+					"code-quality"
 				]
 			},
 			{
@@ -1136,9 +1116,28 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
+					"kotlin-dsl-plugins",
+					"kotlin-dsl",
+					"ide"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"integ-test",
 					"maven",
 					"model-core"
 				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"composite-builds"
+				]
 			}
 		],
 		"testCoverageUuid":20
@@ -1158,14 +1157,6 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"core"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
 					"configuration-cache"
 				]
 			},
@@ -1174,7 +1165,7 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"plugins"
+					"core"
 				]
 			},
 			{
@@ -1182,15 +1173,15 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"language-native",
+					"plugins",
 					"execution",
 					"resources-http",
 					"internal-performance-testing",
-					"wrapper-shared",
 					"internal-integ-testing",
-					"process-services",
+					"wrapper-shared",
 					"resources",
 					"snapshots",
+					"process-services",
 					"base-services",
 					"publish"
 				]
@@ -1200,35 +1191,17 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"language-java",
-					"jvm-services",
+					"language-native",
 					"messaging",
+					"jvm-services",
 					"build-profile",
-					"instrumentation-agent",
+					"kotlin-dsl-tooling-builders",
 					"testing-base",
 					"resources-gcs",
 					"resources-sftp",
-					"ear",
-					"platform-jvm",
-					"build-events"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"launcher",
-					"reporting",
 					"resources-s3",
-					"core-api",
-					"build-cache",
-					"model-groovy",
-					"build-cache-http",
-					"antlr",
-					"persistent-cache",
-					"language-groovy",
-					"platform-base"
+					"ear",
+					"reporting"
 				]
 			},
 			{
@@ -1237,14 +1210,16 @@
 				},
 				"subprojects":[
 					"testing-jvm",
+					"build-events",
+					"platform-jvm",
+					"core-api",
+					"build-cache-http",
 					"signing",
-					"ide-native",
-					"diagnostics",
-					"jacoco",
-					"file-collections",
-					"wrapper",
-					"platform-native",
-					"version-control"
+					"model-groovy",
+					"antlr",
+					"persistent-cache",
+					"build-cache",
+					"language-groovy"
 				]
 			},
 			{
@@ -1252,12 +1227,14 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"plugin-use",
-					"tooling-api",
-					"file-watching",
-					"enterprise",
-					"kotlin-dsl-plugins",
-					"testing-native"
+					"language-java",
+					"ide-native",
+					"platform-base",
+					"jacoco",
+					"version-control",
+					"diagnostics",
+					"wrapper",
+					"file-collections"
 				]
 			},
 			{
@@ -1266,10 +1243,11 @@
 				},
 				"subprojects":[
 					"build-init",
-					"ide",
-					"ivy",
-					"kotlin-dsl",
-					"code-quality"
+					"file-watching",
+					"platform-native",
+					"tooling-api",
+					"enterprise",
+					"ivy"
 				]
 			},
 			{
@@ -1277,9 +1255,31 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"scala",
-					"samples",
+					"launcher",
 					"workers",
+					"ide",
+					"testing-native"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"maven",
+					"samples",
+					"kotlin-dsl-plugins",
+					"plugin-development"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"plugin-use",
+					"kotlin-dsl",
+					"logging",
 					"test-kit"
 				]
 			},
@@ -1289,9 +1289,9 @@
 				},
 				"subprojects":[
 					"integ-test",
-					"logging",
-					"model-core",
-					"plugin-development"
+					"code-quality",
+					"kotlin-dsl-integ-tests",
+					"composite-builds"
 				]
 			},
 			{
@@ -1299,9 +1299,8 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"composite-builds",
-					"maven",
-					"kotlin-dsl-integ-tests"
+					"model-core",
+					"scala"
 				]
 			}
 		],
@@ -1322,14 +1321,6 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"core"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
 					"configuration-cache"
 				]
 			},
@@ -1338,7 +1329,7 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"language-native"
+					"core"
 				]
 			},
 			{
@@ -1346,16 +1337,16 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"plugins",
+					"language-native",
 					"execution",
 					"resources-http",
 					"internal-performance-testing",
 					"internal-integ-testing",
 					"file-watching",
-					"instrumentation-agent",
 					"process-services",
 					"wrapper-shared",
 					"resources",
+					"base-services",
 					"jvm-services"
 				]
 			},
@@ -1364,16 +1355,16 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"testing-jvm",
-					"base-services",
+					"plugins",
 					"snapshots",
 					"build-profile",
 					"publish",
 					"messaging",
+					"kotlin-dsl-tooling-builders",
 					"testing-base",
 					"persistent-cache",
 					"build-events",
-					"resources-gcs",
+					"resources-sftp",
 					"platform-jvm"
 				]
 			},
@@ -1382,17 +1373,16 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"language-java",
-					"language-groovy",
-					"resources-sftp",
+					"testing-jvm",
 					"ear",
-					"resources-s3",
+					"resources-gcs",
+					"language-groovy",
 					"reporting",
 					"antlr",
-					"build-cache",
-					"core-api",
+					"resources-s3",
 					"wrapper",
-					"build-cache-http"
+					"build-cache",
+					"core-api"
 				]
 			},
 			{
@@ -1400,43 +1390,14 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"maven",
-					"model-groovy",
+					"language-java",
+					"build-cache-http",
 					"signing",
-					"tooling-api",
+					"model-groovy",
 					"jacoco",
-					"ide-native",
+					"tooling-api",
 					"file-collections",
-					"platform-base",
-					"version-control",
-					"platform-native",
-					"launcher"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"build-init",
-					"enterprise",
-					"test-kit",
-					"code-quality",
-					"diagnostics",
-					"kotlin-dsl",
-					"samples"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"plugin-use",
-					"kotlin-dsl-plugins",
-					"plugin-development",
-					"ivy",
-					"ide"
+					"platform-base"
 				]
 			},
 			{
@@ -1445,10 +1406,56 @@
 				},
 				"subprojects":[
 					"integ-test",
-					"workers",
-					"kotlin-dsl-integ-tests",
-					"scala",
-					"model-core"
+					"ide-native",
+					"version-control",
+					"enterprise",
+					"launcher",
+					"platform-native"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"maven",
+					"diagnostics",
+					"test-kit",
+					"samples",
+					"workers"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"model-core",
+					"code-quality",
+					"ivy",
+					"plugin-development"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"plugin-use",
+					"kotlin-dsl",
+					"kotlin-dsl-plugins",
+					"scala"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"build-init",
+					"testing-native",
+					"ide",
+					"kotlin-dsl-integ-tests"
 				]
 			},
 			{
@@ -1457,8 +1464,7 @@
 				},
 				"subprojects":[
 					"composite-builds",
-					"logging",
-					"testing-native"
+					"logging"
 				]
 			}
 		],
@@ -1472,16 +1478,25 @@
 					"numberOfBatches":8
 				},
 				"subprojects":[
+					"configuration-cache"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":7
+				},
+				"subprojects":[
 					"core"
 				]
 			},
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":6
+					"numberOfBatches":5
 				},
 				"subprojects":[
-					"configuration-cache"
+					"dependency-management"
 				]
 			},
 			{
@@ -1496,19 +1511,10 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":4
-				},
-				"subprojects":[
-					"dependency-management"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
 					"numberOfBatches":3
 				},
 				"subprojects":[
-					"language-native"
+					"testing-jvm"
 				]
 			},
 			{
@@ -1526,7 +1532,7 @@
 					"numberOfBatches":2
 				},
 				"subprojects":[
-					"testing-jvm"
+					"language-native"
 				]
 			},
 			{
@@ -1544,7 +1550,7 @@
 					"numberOfBatches":2
 				},
 				"subprojects":[
-					"integ-test"
+					"model-core"
 				]
 			},
 			{
@@ -1553,16 +1559,16 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"plugin-use",
+					"integ-test",
 					"architecture-test",
 					"execution",
 					"resources-http",
 					"internal-performance-testing",
-					"file-watching",
 					"internal-integ-testing",
+					"file-watching",
 					"process-services",
-					"instrumentation-agent",
 					"wrapper-shared",
+					"resources",
 					"base-services"
 				]
 			},
@@ -1572,23 +1578,13 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"logging",
-					"resources",
+					"composite-builds",
 					"jvm-services",
 					"build-profile",
 					"snapshots",
-					"publish"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"build-init",
+					"publish",
 					"messaging",
-					"persistent-cache",
+					"kotlin-dsl-tooling-builders",
 					"testing-base"
 				]
 			},
@@ -1598,8 +1594,18 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"composite-builds",
-					"build-events",
+					"plugin-use",
+					"persistent-cache",
+					"build-events"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"build-init",
 					"resources-gcs",
 					"resources-sftp"
 				]
@@ -1610,21 +1616,9 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"model-core",
+					"logging",
 					"platform-jvm",
-					"language-groovy"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"ide",
-					"ear",
-					"build-cache",
-					"reporting"
+					"antlr"
 				]
 			},
 			{
@@ -1634,18 +1628,9 @@
 				},
 				"subprojects":[
 					"kotlin-dsl-integ-tests",
-					"antlr",
-					"build-cache-http",
-					"wrapper"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"workers",
+					"reporting",
+					"language-groovy",
+					"ear",
 					"resources-s3",
 					"core-api"
 				]
@@ -1656,30 +1641,11 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"plugin-development",
-					"signing",
-					"model-groovy"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"ivy",
-					"tooling-api",
-					"file-collections"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"samples",
-					"test-kit"
+					"ide",
+					"build-cache",
+					"build-cache-http",
+					"wrapper",
+					"signing"
 				]
 			},
 			{
@@ -1689,6 +1655,8 @@
 				},
 				"subprojects":[
 					"scala",
+					"model-groovy",
+					"tooling-api",
 					"jacoco"
 				]
 			},
@@ -1698,38 +1666,8 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"kotlin-dsl-plugins",
-					"platform-native",
-					"platform-base"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"ide-native",
-					"version-control"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"code-quality",
-					"kotlin-dsl"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"diagnostics",
+					"ivy",
+					"file-collections",
 					"testing-native"
 				]
 			},
@@ -1739,9 +1677,61 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"launcher",
+					"plugin-development",
+					"platform-base",
+					"version-control"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"workers",
+					"test-kit",
+					"launcher"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"kotlin-dsl-plugins",
+					"ide-native",
+					"platform-native"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"code-quality",
+					"diagnostics"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"samples",
 					"enterprise"
 				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"kotlin-dsl"
+				]
 			}
 		],
 		"testCoverageUuid":13
@@ -1760,7 +1750,7 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":2
+					"numberOfBatches":3
 				},
 				"subprojects":[
 					"dependency-management"
@@ -1778,175 +1768,19 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
+					"numberOfBatches":2
 				},
 				"subprojects":[
-					"tooling-api",
-					"java-compiler-plugin",
-					"logging-api",
-					"base-services-groovy",
-					"docs-asciidoctor-extensions-base",
-					"kotlin-dsl-provider-plugins",
-					"native",
-					"architecture-test",
-					"security",
-					"file-temp",
-					"functional"
+					"plugins"
 				]
 			},
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
+					"numberOfBatches":2
 				},
 				"subprojects":[
-					"plugins",
-					"worker-processes",
-					"problems",
-					"files",
-					"internal-testing",
-					"testing-jvm-infrastructure",
-					"build-cache-packaging",
-					"cli",
-					"build-operations",
-					"normalization-java",
-					"docs"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"testing-jvm",
-					"language-jvm",
-					"tooling-api-builders",
-					"build-option",
-					"hashing",
-					"execution",
-					"resources-http",
-					"internal-performance-testing",
-					"internal-integ-testing",
-					"process-services",
-					"snapshots"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"plugin-use",
-					"wrapper-shared",
-					"resources",
-					"base-services",
-					"publish",
-					"messaging",
-					"jvm-services",
-					"build-profile",
-					"testing-base",
-					"ear",
-					"instrumentation-agent"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"launcher",
-					"resources-gcs",
-					"tooling-native",
-					"resources-sftp",
-					"build-events",
-					"resources-s3",
-					"platform-native",
-					"core-api",
-					"testing-native",
-					"platform-jvm",
-					"build-cache-http"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"scala",
-					"reporting",
-					"build-cache",
-					"persistent-cache",
-					"model-groovy",
-					"antlr",
-					"signing",
-					"language-groovy",
-					"version-control"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"language-java",
-					"file-collections",
-					"platform-base",
-					"jacoco",
-					"wrapper",
-					"diagnostics"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"build-init",
-					"file-watching",
-					"kotlin-dsl",
-					"kotlin-dsl-plugins",
-					"ivy"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"integ-test",
-					"enterprise",
-					"model-core",
-					"ide-native"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"kotlin-dsl-tooling-builders",
-					"ide",
-					"samples",
-					"plugin-development"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"logging",
-					"kotlin-dsl-integ-tests",
-					"code-quality"
+					"testing-jvm"
 				]
 			},
 			{
@@ -1956,8 +1790,16 @@
 				},
 				"subprojects":[
 					"composite-builds",
-					"test-kit",
-					"workers"
+					"java-compiler-plugin",
+					"logging-api",
+					"base-services-groovy",
+					"docs-asciidoctor-extensions-base",
+					"kotlin-dsl-provider-plugins",
+					"internal-instrumentation-processor",
+					"native",
+					"architecture-test",
+					"security",
+					"file-temp"
 				]
 			},
 			{
@@ -1966,8 +1808,132 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
+					"tooling-api",
+					"functional",
+					"worker-processes",
+					"problems",
+					"files",
+					"internal-testing",
+					"testing-jvm-infrastructure",
+					"build-cache-packaging",
+					"cli",
+					"build-operations",
+					"normalization-java"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"scala",
+					"docs",
+					"language-jvm",
+					"tooling-api-builders",
+					"build-option",
+					"hashing",
+					"execution",
+					"resources-http",
+					"internal-performance-testing",
+					"internal-integ-testing",
+					"wrapper-shared"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"plugin-use",
+					"process-services",
+					"resources",
+					"snapshots",
+					"base-services",
+					"publish",
+					"messaging",
+					"jvm-services",
+					"build-profile",
+					"tooling-native",
+					"testing-base"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"build-init",
+					"ear",
+					"resources-gcs",
+					"build-events",
+					"platform-native",
+					"resources-s3",
+					"resources-sftp",
+					"core-api",
+					"build-cache-http",
+					"platform-jvm",
+					"reporting"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"launcher",
+					"persistent-cache",
+					"testing-native",
+					"build-cache",
+					"model-groovy",
+					"version-control",
+					"signing",
+					"file-watching",
+					"antlr",
+					"jacoco",
+					"wrapper"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"kotlin-dsl-tooling-builders",
+					"enterprise",
+					"ivy",
+					"language-groovy",
+					"workers",
+					"samples",
+					"platform-base",
+					"kotlin-dsl",
+					"ide-native",
+					"diagnostics",
+					"file-collections"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"language-java",
+					"integ-test",
+					"code-quality",
+					"model-core",
+					"logging",
 					"maven",
-					"language-native"
+					"kotlin-dsl-plugins",
+					"test-kit",
+					"plugin-development",
+					"ide",
+					"language-native",
+					"kotlin-dsl-integ-tests"
 				]
 			}
 		],
@@ -2008,62 +1974,13 @@
 					"execution",
 					"resources-http",
 					"internal-performance-testing",
-					"instrumentation-agent",
 					"wrapper-shared",
-					"resources",
-					"internal-integ-testing",
 					"snapshots",
+					"internal-integ-testing",
+					"resources",
 					"publish",
-					"jvm-services"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"language-native",
-					"process-services",
-					"wrapper",
-					"base-services",
-					"messaging",
-					"ear",
-					"build-profile",
-					"resources-gcs",
-					"build-cache",
-					"core-api",
-					"resources-s3"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"launcher",
-					"build-events",
-					"resources-sftp",
-					"testing-base",
-					"platform-jvm",
-					"build-cache-http",
-					"ide-native",
-					"reporting",
-					"model-groovy",
-					"diagnostics"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"composite-builds",
-					"platform-base",
-					"platform-native",
-					"test-kit",
-					"file-collections",
-					"language-groovy",
-					"antlr"
+					"jvm-services",
+					"process-services"
 				]
 			},
 			{
@@ -2072,31 +1989,51 @@
 				},
 				"subprojects":[
 					"testing-jvm",
-					"persistent-cache",
+					"base-services",
+					"messaging",
+					"wrapper",
+					"build-profile",
+					"ear",
+					"resources-gcs",
+					"resources-s3",
+					"build-events",
+					"core-api",
+					"build-cache"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"language-native",
+					"kotlin-dsl-tooling-builders",
+					"resources-sftp",
+					"testing-base",
+					"model-groovy",
+					"platform-jvm",
+					"ide-native",
+					"reporting",
+					"build-cache-http",
+					"platform-base",
+					"antlr"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"composite-builds",
+					"test-kit",
+					"language-groovy",
+					"diagnostics",
 					"signing",
-					"tooling-api",
-					"version-control"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"plugin-use",
-					"file-watching",
-					"kotlin-dsl-plugins",
-					"jacoco"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"language-java",
+					"persistent-cache",
+					"version-control",
 					"testing-native",
-					"enterprise"
+					"file-watching",
+					"platform-native"
 				]
 			},
 			{
@@ -2104,9 +2041,12 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"scala",
-					"ivy",
-					"samples"
+					"launcher",
+					"tooling-api",
+					"file-collections",
+					"jacoco",
+					"enterprise",
+					"plugin-development"
 				]
 			},
 			{
@@ -2114,19 +2054,10 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"integ-test",
-					"plugin-development",
-					"maven"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TestDistribution"
-				},
-				"subprojects":[
-					"build-init",
-					"code-quality",
-					"kotlin-dsl"
+					"kotlin-dsl-integ-tests",
+					"workers",
+					"ide",
+					"ivy"
 				]
 			},
 			{
@@ -2135,8 +2066,9 @@
 				},
 				"subprojects":[
 					"model-core",
-					"ide",
-					"workers"
+					"maven",
+					"integ-test",
+					"logging"
 				]
 			},
 			{
@@ -2144,8 +2076,28 @@
 					"name":"TestDistribution"
 				},
 				"subprojects":[
-					"logging",
-					"kotlin-dsl-integ-tests"
+					"kotlin-dsl",
+					"samples",
+					"code-quality"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"scala",
+					"build-init",
+					"kotlin-dsl-plugins"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TestDistribution"
+				},
+				"subprojects":[
+					"plugin-use",
+					"language-java"
 				]
 			}
 		],
@@ -2228,8 +2180,8 @@
 				"subprojects":[
 					"test-kit",
 					"platform-native",
-					"ide-native",
 					"language-groovy",
+					"ide-native",
 					"jacoco",
 					"launcher",
 					"antlr",
@@ -2251,7 +2203,6 @@
 					"file-collections",
 					"file-watching",
 					"ide",
-					"instrumentation-agent",
 					"integ-test",
 					"internal-integ-testing",
 					"internal-performance-testing",
@@ -2260,6 +2211,7 @@
 					"kotlin-dsl",
 					"kotlin-dsl-integ-tests",
 					"kotlin-dsl-plugins",
+					"kotlin-dsl-tooling-builders",
 					"language-java",
 					"logging",
 					"maven",
@@ -2281,7 +2233,7 @@
 					"numberOfBatches":3
 				},
 				"subprojects":[
-					"language-native"
+					"plugins"
 				]
 			},
 			{
@@ -2290,7 +2242,16 @@
 					"numberOfBatches":3
 				},
 				"subprojects":[
-					"plugins"
+					"language-native"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":2
+				},
+				"subprojects":[
+					"dependency-management"
 				]
 			},
 			{
@@ -2336,7 +2297,7 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"dependency-management",
+					"test-kit",
 					"resources-s3",
 					"resources-http",
 					"resources-gcs",
@@ -2355,51 +2316,12 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"test-kit",
-					"platform-base",
-					"persistent-cache",
-					"model-groovy",
-					"model-core",
-					"messaging",
-					"maven",
-					"logging",
-					"language-java",
-					"kotlin-dsl-plugins",
-					"kotlin-dsl-integ-tests"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"language-groovy",
-					"kotlin-dsl",
-					"jvm-services",
-					"ivy",
-					"internal-performance-testing",
-					"internal-integ-testing",
-					"integ-test",
-					"instrumentation-agent",
-					"ide",
-					"file-watching",
-					"file-collections"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
 					"ide-native",
-					"testing-native",
+					"language-groovy",
 					"platform-native",
-					"configuration-cache",
+					"testing-native",
 					"jacoco",
 					"launcher",
-					"core",
 					"architecture-test",
 					"antlr",
 					"base-services",
@@ -2409,11 +2331,33 @@
 					"build-init",
 					"build-profile",
 					"composite-builds",
+					"configuration-cache",
+					"core",
 					"core-api",
 					"diagnostics",
 					"ear",
 					"enterprise",
-					"execution"
+					"execution",
+					"file-collections",
+					"file-watching",
+					"ide",
+					"integ-test",
+					"internal-integ-testing",
+					"internal-performance-testing",
+					"ivy",
+					"jvm-services",
+					"kotlin-dsl",
+					"kotlin-dsl-integ-tests",
+					"kotlin-dsl-plugins",
+					"kotlin-dsl-tooling-builders",
+					"language-java",
+					"logging",
+					"maven",
+					"messaging",
+					"model-core",
+					"model-groovy",
+					"persistent-cache",
+					"platform-base"
 				]
 			}
 		],
@@ -2427,13 +2371,13 @@
 					"numberOfBatches":3
 				},
 				"subprojects":[
-					"core"
+					"dependency-management"
 				]
 			},
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":2
+					"numberOfBatches":3
 				},
 				"subprojects":[
 					"configuration-cache"
@@ -2442,10 +2386,10 @@
 			{
 				"parallelizationMethod":{
 					"name":"TeamCityParallelTests",
-					"numberOfBatches":2
+					"numberOfBatches":3
 				},
 				"subprojects":[
-					"dependency-management"
+					"core"
 				]
 			},
 			{
@@ -2464,12 +2408,12 @@
 				},
 				"subprojects":[
 					"composite-builds",
-					"tooling-native",
 					"java-compiler-plugin",
 					"logging-api",
 					"base-services-groovy",
 					"docs-asciidoctor-extensions-base",
 					"kotlin-dsl-provider-plugins",
+					"internal-instrumentation-processor",
 					"native",
 					"architecture-test",
 					"security",
@@ -2507,49 +2451,11 @@
 					"tooling-api-builders",
 					"build-option",
 					"hashing",
+					"tooling-native",
 					"execution",
 					"resources-http",
 					"internal-performance-testing",
-					"internal-integ-testing",
-					"wrapper-shared"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"launcher",
-					"resources",
-					"snapshots",
-					"process-services",
-					"publish",
-					"base-services",
-					"build-profile",
-					"jvm-services",
-					"messaging",
-					"ide-native",
-					"instrumentation-agent"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"scala",
-					"testing-base",
-					"testing-native",
-					"ear",
-					"platform-native",
-					"resources-gcs",
-					"resources-sftp",
-					"build-events",
-					"resources-s3",
-					"core-api",
-					"platform-jvm"
+					"internal-integ-testing"
 				]
 			},
 			{
@@ -2559,16 +2465,16 @@
 				},
 				"subprojects":[
 					"language-java",
-					"reporting",
-					"build-cache",
-					"language-native",
-					"build-cache-http",
-					"model-groovy",
-					"signing",
-					"antlr",
-					"persistent-cache",
-					"platform-base",
-					"file-collections"
+					"wrapper-shared",
+					"resources",
+					"process-services",
+					"snapshots",
+					"publish",
+					"base-services",
+					"jvm-services",
+					"messaging",
+					"build-profile",
+					"ide-native"
 				]
 			},
 			{
@@ -2577,28 +2483,17 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"integ-test",
-					"language-groovy",
-					"diagnostics",
-					"file-watching",
-					"jacoco",
-					"wrapper",
-					"enterprise",
-					"kotlin-dsl"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
-					"build-init",
-					"ivy",
-					"kotlin-dsl-plugins",
-					"version-control",
-					"kotlin-dsl-integ-tests",
-					"test-kit"
+					"launcher",
+					"resources-gcs",
+					"resources-sftp",
+					"testing-base",
+					"platform-native",
+					"ear",
+					"testing-native",
+					"resources-s3",
+					"build-events",
+					"platform-jvm",
+					"core-api"
 				]
 			},
 			{
@@ -2608,9 +2503,51 @@
 				},
 				"subprojects":[
 					"plugin-use",
+					"reporting",
+					"language-native",
+					"build-cache-http",
+					"model-groovy",
+					"build-cache",
+					"signing",
+					"antlr",
+					"persistent-cache",
+					"language-groovy",
+					"platform-base"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"build-init",
+					"file-collections",
+					"jacoco",
+					"diagnostics",
+					"file-watching",
+					"wrapper",
+					"version-control",
+					"enterprise",
+					"samples",
+					"ivy",
+					"kotlin-dsl-integ-tests"
+				]
+			},
+			{
+				"parallelizationMethod":{
+					"name":"TeamCityParallelTests",
+					"numberOfBatches":1
+				},
+				"subprojects":[
+					"scala",
 					"workers",
+					"kotlin-dsl-plugins",
+					"kotlin-dsl",
+					"test-kit",
+					"ide",
 					"plugin-development",
-					"samples"
+					"maven"
 				]
 			},
 			{
@@ -2619,20 +2556,11 @@
 					"numberOfBatches":1
 				},
 				"subprojects":[
-					"maven",
-					"model-core",
 					"code-quality",
-					"kotlin-dsl-tooling-builders"
-				]
-			},
-			{
-				"parallelizationMethod":{
-					"name":"TeamCityParallelTests",
-					"numberOfBatches":1
-				},
-				"subprojects":[
+					"integ-test",
 					"logging",
-					"ide"
+					"kotlin-dsl-tooling-builders",
+					"model-core"
 				]
 			}
 		],
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0611026..b5c27d1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -37,7 +37,7 @@
 
 In order to make changes to Gradle, you'll need:
 
-* A [Java Development Kit](http://jdk.java.net/) (JDK) **version 11**.
+* A [Java Development Kit](http://jdk.java.net/) (JDK) **version 11**. Fixed version is required to use [remote cache](#remote-build-cache). 
 * A text editor or IDE. We use and recommend [IntelliJ IDEA CE](http://www.jetbrains.com/idea/).  IntelliJ Ultimate will also work. You'll need IntelliJ 2021.2.2 or newer.
 * [git](https://git-scm.com/) and a [GitHub account](https://github.com/join).
 
@@ -110,6 +110,11 @@
 
 ## Useful tips
 
+### How Gradle Works
+
+We have [a series of blog](https://blog.gradle.org/how-gradle-works-1) that explains how Gradle works.
+This may help you better understand and contribute to Gradle.
+
 ### Debugging Gradle
 
 You can debug Gradle by adding `-Dorg.gradle.debug=true` to the command-line. Gradle will wait for you to attach a debugger at `localhost:5005` by default.
@@ -139,6 +144,7 @@
 The Gradle build uses [Java Toolchain](https://docs.gradle.org/current/userguide/toolchains.html) support to compile and execute tests across multiple versions of Java.
 
 Available JDKs on your machine are automatically detected and wired for the various compile and test tasks.
+Some tests require multiple JDKs to be installed on your computer, be aware of this if you make changes related to anything toolchains related.
 
 If you want to explicitly run tests with a different Java version, you need to specify `-PtestJavaVersion=#` with the major version of the JDK you want the tests to run with (e.g. `-PtestJavaVersion=14`).
 
@@ -152,12 +158,20 @@
 
 Tasks known to have problems are listed in the build logic. You can find this list at:
 
-    build-logic-settings/build-logic-settings-plugin/src/main/kotlin/gradlebuild.internal.cc-experiment.settings.gradle.kts
+    build-logic/root-build/src/main/kotlin/gradlebuild.internal.cc-experiment.gradle.kts
 
 If you discover a task that doesn't work with the configuration but it not in this list, please add it.
 
 For more information on the configuration cache, see the [user manual](https://docs.gradle.org/current/userguide/configuration_cache.html).
 
+### Remote build cache
+
+Gradle, Inc runs a set of remote build cache nodes to speed up local builds when developing Gradle. By default, the build is [configured](https://github.com/gradle/gradle-org-conventions-plugin#what-it-does) to use the build cache node in the EU region.
+
+The build cache has anonymous read access, so you don't need to authenticate in order to use it. You can use a different build cache node by specifying `-DcacheNode=us` for a build cache node in the US or `-DcacheNode=au` for a build cache node in Australia.
+
+If you are not getting cache hits from the build cache, you may be using the wrong version of Java. A fixed version (Java 11) is required to get remote cache hits.
+
 ### Building a distribution from source
 
 To create a Gradle distribution from the source tree you can run either of the following:
diff --git a/build-logic-commons/gradle-plugin/build.gradle.kts b/build-logic-commons/gradle-plugin/build.gradle.kts
index 3ec576e..97c05ec 100644
--- a/build-logic-commons/gradle-plugin/build.gradle.kts
+++ b/build-logic-commons/gradle-plugin/build.gradle.kts
@@ -14,10 +14,11 @@
 }
 
 dependencies {
-    compileOnly("com.gradle:gradle-enterprise-gradle-plugin:3.12.3")
+    compileOnly("com.gradle:gradle-enterprise-gradle-plugin:3.13.2")
 
     implementation(project(":commons"))
-    implementation("org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.0.7")
-    implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10")
+    implementation("org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.0.14")
+    implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20")
     implementation("org.gradle.kotlin:gradle-kotlin-dsl-conventions:0.8.0")
+    implementation("org.gradle:test-retry-gradle-plugin:1.5.2")
 }
diff --git a/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.build-logic.groovy-dsl-gradle-plugin.gradle.kts b/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.build-logic.groovy-dsl-gradle-plugin.gradle.kts
index 911ad68..a8acdf2 100644
--- a/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.build-logic.groovy-dsl-gradle-plugin.gradle.kts
+++ b/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.build-logic.groovy-dsl-gradle-plugin.gradle.kts
@@ -19,6 +19,7 @@
     id("groovy-gradle-plugin")
     id("gradlebuild.code-quality")
     id("gradlebuild.ci-reporting")
+    id("gradlebuild.test-retry")
 }
 
 java.configureJavaToolChain()
diff --git a/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.build-logic.kotlin-dsl-gradle-plugin.gradle.kts b/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.build-logic.kotlin-dsl-gradle-plugin.gradle.kts
index e65c7f7..2611e18 100644
--- a/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.build-logic.kotlin-dsl-gradle-plugin.gradle.kts
+++ b/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.build-logic.kotlin-dsl-gradle-plugin.gradle.kts
@@ -21,6 +21,7 @@
     id("gradlebuild.code-quality")
     id("gradlebuild.ktlint")
     id("gradlebuild.ci-reporting")
+    id("gradlebuild.test-retry")
 }
 
 java.configureJavaToolChain()
@@ -34,8 +35,7 @@
 
 tasks.withType<KotlinCompile>().configureEach {
     compilerOptions {
-        // Can be set to true when assignment plugin becomes stable
-        allWarningsAsErrors = false
+        allWarningsAsErrors = true
     }
 }
 
diff --git a/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.cache-miss-monitor.gradle.kts b/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.cache-miss-monitor.gradle.kts
index 1e9c7ff..1562f20 100644
--- a/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.cache-miss-monitor.gradle.kts
+++ b/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.cache-miss-monitor.gradle.kts
@@ -61,7 +61,7 @@
 
 fun Project.isAsciidoctorCacheMissTask() = isMonitoredAsciidoctorTask() && !isExpectedAsciidoctorCacheMiss()
 
-fun Task.isMonitoredCompileTask() = (this is AbstractCompile || this.isClasspathManifest()) && !isKotlinJsIrLink() 
+fun Task.isMonitoredCompileTask() = (this is AbstractCompile || this.isClasspathManifest()) && !isKotlinJsIrLink()
 
 fun Task.isClasspathManifest() = this.javaClass.simpleName.startsWith("ClasspathManifest")
 
@@ -89,13 +89,15 @@
 // 2. Gradleception which re-builds Gradle with a new Gradle version
 // 3. buildScanPerformance test, which doesn't depend on compileAll
 // 4. buildScanPerformance test, which doesn't depend on compileAll
-// 5. BuildCommitDistribution may build a commit which is not built before
+// 5. Compile All for the experimental build cache NG
+// 6. BuildCommitDistribution may build a commit which is not built before
     isInBuild(
         "Check_CompileAllBuild",
         "Component_GradlePlugin_Performance_PerformanceLatestMaster",
         "Component_GradlePlugin_Performance_PerformanceLatestReleased",
         "Check_Gradleception",
-        "Check_GradleceptionWithGroovy4"
+        "Check_GradleceptionWithGroovy4",
+        "CompileAllBuild_BuildCacheNG"
     ) || isBuildCommitDistribution
 
 val Project.isBuildCommitDistribution: Boolean
diff --git a/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.test-retry.gradle.kts b/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.test-retry.gradle.kts
new file mode 100644
index 0000000..d312d3f
--- /dev/null
+++ b/build-logic-commons/gradle-plugin/src/main/kotlin/gradlebuild.test-retry.gradle.kts
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.
+ */
+
+plugins {
+    id("org.gradle.test-retry")
+}
+
+if (System.getenv().containsKey("CI")) {
+    tasks.withType<Test>().configureEach {
+        retry {
+            maxRetries.set(2)
+        }
+    }
+}
diff --git a/build-logic-commons/gradle.properties b/build-logic-commons/gradle.properties
deleted file mode 100644
index 6acaf50..0000000
--- a/build-logic-commons/gradle.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-# Enables Kotlin assignment support, should be removed once we enable assignment by default
-systemProp.org.gradle.unsafe.kotlin.assignment=true
diff --git a/build-logic-settings/build-logic-settings-plugin/build.gradle.kts b/build-logic-settings/build-logic-settings-plugin/build.gradle.kts
deleted file mode 100644
index 5ffc3a4..0000000
--- a/build-logic-settings/build-logic-settings-plugin/build.gradle.kts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-plugins {
-   `kotlin-dsl`
-    id("gradlebuild.commons")
-}
-
-description = "Provides settings plugins for main build"
-
-java.configureJavaToolChain()
diff --git a/build-logic-settings/build-logic-settings-plugin/src/main/kotlin/gradlebuild.internal.cc-experiment.settings.gradle.kts b/build-logic-settings/build-logic-settings-plugin/src/main/kotlin/gradlebuild.internal.cc-experiment.settings.gradle.kts
deleted file mode 100644
index 8e22502..0000000
--- a/build-logic-settings/build-logic-settings-plugin/src/main/kotlin/gradlebuild.internal.cc-experiment.settings.gradle.kts
+++ /dev/null
@@ -1,123 +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.
- */
-import org.gradle.api.internal.plugins.DslObject
-
-val unsupportedTasksPredicate: (Task) -> Boolean = { task: Task ->
-    when {
-
-        // Working tasks that would otherwise be matched by filters below
-        task.name in listOf(
-            "publishLocalPublicationToLocalRepository",
-            "publishEmbeddedKotlinPluginMarkerMavenPublicationToTestRepository",
-            "publishKotlinDslBasePluginMarkerMavenPublicationToTestRepository",
-            "publishKotlinDslCompilerSettingsPluginMarkerMavenPublicationToTestRepository",
-            "publishKotlinDslPluginMarkerMavenPublicationToTestRepository",
-            "publishKotlinDslPrecompiledScriptPluginsPluginMarkerMavenPublicationToTestRepository",
-            "publishPluginMavenPublicationToTestRepository",
-            "publishPluginsToTestRepository",
-        ) -> false
-
-        // Core tasks
-        task.name in listOf(
-            "components",
-            "dependantComponents",
-            "model",
-        ) -> true
-        task.name.startsWithAnyOf(
-            "publish",
-            "idea",
-        ) -> true
-        task is GradleBuild -> true
-
-        // gradle/gradle build tasks
-        task.name in listOf(
-            "updateInitPluginTemplateVersionFile",
-            "resolveAllDependencies",
-            "quickCheck",
-        ) -> true
-        task.name.endsWith("Wrapper") -> true
-        task.name in listOf("docs", "stageDocs", "serveDocs") -> true
-        task.name.startsWith("userguide") -> true
-        task.name == "samplesMultiPage" -> true
-        task.typeSimpleName in listOf(
-            "KtsProjectGeneratorTask",
-            "JavaExecProjectGeneratorTask",
-            "JvmProjectGeneratorTask",
-            "NativeProjectGeneratorTask",
-            "MonolithicNativeProjectGeneratorTask",
-            "NativeProjectWithDepsGeneratorTask",
-            "CppMultiProjectGeneratorTask",
-            "BuildBuilderGenerator",
-        ) -> true
-
-        // Third parties tasks
-
-        // Publish plugin
-        task.name == "login" -> true
-
-        // Kotlin/JS
-        // https://youtrack.jetbrains.com/issue/KT-50881
-        task.name in listOf("generateExternals") -> true
-
-        // JMH plugin
-        task.name in listOf("jmh", "jmhJar", "jmhReport") -> true
-
-        // Gradle Doctor plugin
-        task.name in listOf(
-            "buildHealth",
-            "projectHealth",
-            "graph", "graphMain",
-            "projectGraphReport",
-            "ripples",
-            "aggregateAdvice",
-        ) -> true
-        task.name.startsWithAnyOf(
-            "abiAnalysis",
-            "advice",
-            "analyzeClassUsage",
-            "analyzeJar",
-            "artifactsReport",
-            "constantUsageDetector",
-            "createVariantFiles",
-            "findDeclaredProcs",
-            "findUnusedProcs",
-            "generalsUsageDetector",
-            "importFinder",
-            "inlineMemberExtractor",
-            "locateDependencies",
-            "misusedDependencies",
-            "reason",
-            "redundantKaptCheck",
-            "redundantPluginAlert",
-            "serviceLoader",
-        ) -> true
-
-        else -> false
-    }
-}
-
-gradle.taskGraph.whenReady {
-    allTasks.filter(unsupportedTasksPredicate).forEach { task ->
-        task.notCompatibleWithConfigurationCache("Task is not compatible with the configuration cache")
-    }
-}
-
-
-fun String.startsWithAnyOf(vararg prefixes: String): Boolean =
-    prefixes.any { prefix -> startsWith(prefix) }
-
-val Task.typeSimpleName: String
-    get() = DslObject(this).declaredType.simpleName
diff --git a/build-logic-settings/build.gradle.kts b/build-logic-settings/build.gradle.kts
deleted file mode 100644
index ee04814..0000000
--- a/build-logic-settings/build.gradle.kts
+++ /dev/null
@@ -1,17 +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.
- */
-
-description = "This module contains settings plugins applied to our build logic."
diff --git a/build-logic-settings/gradle.properties b/build-logic-settings/gradle.properties
deleted file mode 100644
index 6acaf50..0000000
--- a/build-logic-settings/gradle.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-# Enables Kotlin assignment support, should be removed once we enable assignment by default
-systemProp.org.gradle.unsafe.kotlin.assignment=true
diff --git a/build-logic-settings/settings.gradle.kts b/build-logic-settings/settings.gradle.kts
deleted file mode 100644
index b46ffb8..0000000
--- a/build-logic-settings/settings.gradle.kts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-rootProject.name = "build-logic-settings"
-
-pluginManagement {
-    includeBuild("../build-logic-commons")
-}
-
-plugins {
-    id("org.gradle.toolchains.foojay-resolver-convention") version("0.4.0")
-}
-
-dependencyResolutionManagement {
-    repositories {
-        gradlePluginPortal()
-    }
-}
-
-include("build-logic-settings-plugin")
diff --git a/build-logic/basics/src/main/kotlin/gradlebuild.minify.gradle.kts b/build-logic/basics/src/main/kotlin/gradlebuild.minify.gradle.kts
index 0408976..945a0c5 100644
--- a/build-logic/basics/src/main/kotlin/gradlebuild.minify.gradle.kts
+++ b/build-logic/basics/src/main/kotlin/gradlebuild.minify.gradle.kts
@@ -41,6 +41,10 @@
         "it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet",
         "it.unimi.dsi.fastutil.objects.ObjectOpenHashSet",
         "it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap",
+        // For dependency management
+        "it.unimi.dsi.fastutil.longs.Long2ObjectMap",
+        "it.unimi.dsi.fastutil.longs.Long2ObjectMaps",
+        "it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap",
         // For the configuration cache module
         "it.unimi.dsi.fastutil.objects.ReferenceArrayList",
         "it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet",
@@ -55,7 +59,7 @@
             attributes.attribute(minified, java.lang.Boolean.FALSE)
         }
         /*
-         * This transform exists solely to shrink the size of the fastutil jar from 25MB to 1.5MB.
+         * This transform exists solely to shrink the size of the fastutil jar from 25MB to 1.7MB.
          * The keys to the map parameter are used as the names of the files to which to apply the transform - there is only one entry.
          * It would perhaps be better to do this more selectively instead of applying this transform so broadly and having
          * it just no-op in most cases.
diff --git a/build-logic/basics/src/main/kotlin/gradlebuild/basics/PublicKotlinDslApi.kt b/build-logic/basics/src/main/kotlin/gradlebuild/basics/PublicKotlinDslApi.kt
index 0597157..3a861f5 100644
--- a/build-logic/basics/src/main/kotlin/gradlebuild/basics/PublicKotlinDslApi.kt
+++ b/build-logic/basics/src/main/kotlin/gradlebuild/basics/PublicKotlinDslApi.kt
@@ -20,7 +20,7 @@
 
     val includes = listOf(
         "org/gradle/kotlin/dsl/*",
-        "org/gradle/kotlin/dsl/precompile/*"
+        "org/gradle/kotlin/dsl/precompile/*",
     )
 
     val excludes = listOf(
diff --git a/build-logic/binary-compatibility/src/main/groovy/gradlebuild.binary-compatibility.gradle b/build-logic/binary-compatibility/src/main/groovy/gradlebuild.binary-compatibility.gradle
index fbb35de..1787675 100644
--- a/build-logic/binary-compatibility/src/main/groovy/gradlebuild.binary-compatibility.gradle
+++ b/build-logic/binary-compatibility/src/main/groovy/gradlebuild.binary-compatibility.gradle
@@ -180,4 +180,4 @@
 private String writeFilterPreset(Project project) {
     def preset = project.hasProperty('bin.cmp.report.severity.filter') ? project.getProperty('bin.cmp.report.severity.filter') : 'All levels'
     return """<input id="filter-preset" type="hidden" value="$preset" />"""
-}
\ No newline at end of file
+}
diff --git a/build-logic/binary-compatibility/src/main/groovy/gradlebuild/binarycompatibility/BinaryCompatibilityHelper.groovy b/build-logic/binary-compatibility/src/main/groovy/gradlebuild/binarycompatibility/BinaryCompatibilityHelper.groovy
index 6ed6066..5c94bb7 100644
--- a/build-logic/binary-compatibility/src/main/groovy/gradlebuild/binarycompatibility/BinaryCompatibilityHelper.groovy
+++ b/build-logic/binary-compatibility/src/main/groovy/gradlebuild/binarycompatibility/BinaryCompatibilityHelper.groovy
@@ -44,8 +44,6 @@
         Directory projectRootDir
     ) {
         japicmpTask.tap {
-            doNotTrackState("classloading issues with rules")
-
             addExcludeFilter(AnonymousClassesFilter)
             addExcludeFilter(KotlinInternalFilter)
 
diff --git a/build-logic/binary-compatibility/src/main/groovy/gradlebuild/binarycompatibility/JapicmpTask.java b/build-logic/binary-compatibility/src/main/groovy/gradlebuild/binarycompatibility/JapicmpTask.java
index 5488203..f8fab16 100644
--- a/build-logic/binary-compatibility/src/main/groovy/gradlebuild/binarycompatibility/JapicmpTask.java
+++ b/build-logic/binary-compatibility/src/main/groovy/gradlebuild/binarycompatibility/JapicmpTask.java
@@ -355,4 +355,4 @@ public void addExcludeFilter(Class<? extends Filter> excludeFilterClass) {
     @Nested
     public abstract Property<RichReport> getRichReport();
 
-}
\ No newline at end of file
+}
diff --git a/build-logic/binary-compatibility/src/main/kotlin/gradlebuild/binarycompatibility/sources/SourcesRepository.kt b/build-logic/binary-compatibility/src/main/kotlin/gradlebuild/binarycompatibility/sources/SourcesRepository.kt
index d2f0dce..1393e8a 100644
--- a/build-logic/binary-compatibility/src/main/kotlin/gradlebuild/binarycompatibility/sources/SourcesRepository.kt
+++ b/build-logic/binary-compatibility/src/main/kotlin/gradlebuild/binarycompatibility/sources/SourcesRepository.kt
@@ -122,7 +122,7 @@
         sourceRoots.asSequence()
             .map { it.resolve(sourceFilePath) to it }
             .firstOrNull { it.first.isFile }
-            ?: throw IllegalStateException("Source file '$sourceFilePath' not found, searched in source roots:\n${sourceRoots.joinToString("\n  - ")}")
+            ?: throw IllegalStateException("Source file '$sourceFilePath' not found, searched in source roots:\n  - ${sourceRoots.joinToString("\n  - ")}")
 
     private
     val KtFile.normalizedPath: String?
diff --git a/build-logic/build-init-samples/src/main/kotlin/gradlebuild/samples/SamplesGenerator.kt b/build-logic/build-init-samples/src/main/kotlin/gradlebuild/samples/SamplesGenerator.kt
index aa5d4bd..1cbf85e 100644
--- a/build-logic/build-init-samples/src/main/kotlin/gradlebuild/samples/SamplesGenerator.kt
+++ b/build-logic/build-init-samples/src/main/kotlin/gradlebuild/samples/SamplesGenerator.kt
@@ -198,10 +198,6 @@
         """.trimIndent() else ""
         val nativeTestTaskPrefix = if (descriptor.language === Language.SWIFT) "xc" else "run"
         val classesUpToDate = if (descriptor.language === Language.KOTLIN) " UP-TO-DATE" else ""
-        val inspectClassesForKotlinICTask = if (descriptor.language === Language.KOTLIN) """
-     > Task :$subprojectName:inspectClassesForKotlinIC
-
-        """.trimIndent() else ""
         projectLayoutSetupRegistry.templateOperationFactory.newTemplateOperation()
             .withTemplate(templateFolder.template("$templateFragment-build.out"))
             .withTarget(settings.target.file("../tests/build.out").asFile)
@@ -212,7 +208,6 @@
             .withBinding("nativeTestTaskPrefix", nativeTestTaskPrefix)
             .withBinding("tasksExecuted", "" + tasksExecuted(descriptor))
             .withBinding("classesUpToDate", "" + classesUpToDate)
-            .withBinding("inspectClassesForKotlinICTask", "" + inspectClassesForKotlinICTask)
             .create().generate()
         projectLayoutSetupRegistry.templateOperationFactory.newTemplateOperation()
             .withTemplate(templateFolder.template("build.sample.conf"))
@@ -223,9 +218,6 @@
     private
     fun tasksExecuted(descriptor: CompositeProjectInitDescriptor): Int {
         var tasksExecuted = if (descriptor.componentType === ComponentType.LIBRARY) 4 else 7
-        if (descriptor.language === Language.KOTLIN) {
-            tasksExecuted++
-        }
         return tasksExecuted
     }
 
diff --git a/build-logic/build-platform/build.gradle.kts b/build-logic/build-platform/build.gradle.kts
index 42bc0e3..d8562ed 100644
--- a/build-logic/build-platform/build.gradle.kts
+++ b/build-logic/build-platform/build.gradle.kts
@@ -13,7 +13,7 @@
 val isGroovy4 = VersionNumber.parse(groovyVersion).major >= 4
 val codenarcVersion = if (isGroovy4) "3.1.0-groovy-4.0" else "3.1.0"
 val spockVersion = if (isGroovy4) "2.2-groovy-4.0" else "2.2-groovy-3.0"
-val asmVersion = "9.2"
+val asmVersion = "9.4"
 // To try out better kotlin compilation avoidance and incremental compilation
 // with -Pkotlin.incremental.useClasspathSnapshot=true
 val kotlinVersion = providers.gradleProperty("buildKotlinVersion")
@@ -22,7 +22,7 @@
 dependencies {
     constraints {
         api("org.gradle.guides:gradle-guides-plugin:0.21")
-        api("com.gradle:gradle-enterprise-gradle-plugin:3.12.3") // Sync with `settings.gradle.kts`
+        api("com.gradle:gradle-enterprise-gradle-plugin:3.13.2") // Sync with `settings.gradle.kts`
         api("com.gradle.publish:plugin-publish-plugin:1.1.0")
         api("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.0.1")
         api("me.champeau.gradle:japicmp-gradle-plugin:0.4.1")
@@ -58,6 +58,8 @@
         api("org.spockframework:spock-junit4:$spockVersion")
         api("org.asciidoctor:asciidoctorj:2.4.3")
         api("org.asciidoctor:asciidoctorj-pdf:1.5.4")
+        api("dev.adamko.dokkatoo:dokkatoo-plugin:1.3.0")
+        api("org.jetbrains.dokka:dokka-core:1.8.10")
         api("com.beust:jcommander:1.78")
         api("org.codehaus.groovy:$groovyVersion")
         api("org.codehaus.groovy.modules.http-builder:http-builder:0.7.2") // TODO maybe change group name when upgrading to Groovy 4
diff --git a/build-logic/build-update-utils/src/main/kotlin/gradlebuild.update-versions.gradle.kts b/build-logic/build-update-utils/src/main/kotlin/gradlebuild.update-versions.gradle.kts
index 58203b9..ee1fad3 100644
--- a/build-logic/build-update-utils/src/main/kotlin/gradlebuild.update-versions.gradle.kts
+++ b/build-logic/build-update-utils/src/main/kotlin/gradlebuild.update-versions.gradle.kts
@@ -3,6 +3,7 @@
 import gradlebuild.basics.releasedVersionsFile
 import gradlebuild.buildutils.model.ReleasedVersion
 import gradlebuild.buildutils.tasks.UpdateAgpVersions
+import gradlebuild.buildutils.tasks.UpdateKotlinVersions
 import gradlebuild.buildutils.tasks.UpdateReleasedVersions
 import java.net.URL
 
@@ -33,6 +34,14 @@
     minimumSupportedMinor = "7.3"
     fetchNightly = false
     propertiesFile = layout.projectDirectory.file("gradle/dependency-management/agp-versions.properties")
+    compatibilityDocFile = layout.projectDirectory.file("subprojects/docs/src/docs/userguide/compatibility.adoc")
+}
+
+tasks.register<UpdateKotlinVersions>("updateKotlinVersions") {
+    comment = " Generated - Update by running `./gradlew updateKotlinVersions`"
+    minimumSupported = "1.6.10"
+    propertiesFile = layout.projectDirectory.file("gradle/dependency-management/kotlin-versions.properties")
+    compatibilityDocFile = layout.projectDirectory.file("subprojects/docs/src/docs/userguide/compatibility.adoc")
 }
 
 data class VersionBuildTimeInfo(val version: String, val buildTime: String)
diff --git a/build-logic/build-update-utils/src/main/kotlin/gradlebuild/buildutils/tasks/UpdateAgpVersions.kt b/build-logic/build-update-utils/src/main/kotlin/gradlebuild/buildutils/tasks/UpdateAgpVersions.kt
index 424789ea..483c742 100644
--- a/build-logic/build-update-utils/src/main/kotlin/gradlebuild/buildutils/tasks/UpdateAgpVersions.kt
+++ b/build-logic/build-update-utils/src/main/kotlin/gradlebuild/buildutils/tasks/UpdateAgpVersions.kt
@@ -21,8 +21,9 @@
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.UntrackedTask
 import org.gradle.internal.util.PropertiesUtils
-import org.gradle.work.DisableCachingByDefault
+import org.gradle.util.internal.VersionNumber
 import org.w3c.dom.Element
 import java.util.Properties
 import javax.xml.parsers.DocumentBuilderFactory
@@ -32,7 +33,7 @@
  * Fetch the latest AGP versions and write a properties file.
  * Never up-to-date, non-cacheable.
  */
-@DisableCachingByDefault(because = "Not worth caching")
+@UntrackedTask(because = "Not worth tracking")
 abstract class UpdateAgpVersions : DefaultTask() {
 
     @get:Internal
@@ -47,29 +48,79 @@
     @get:Internal
     abstract val propertiesFile: RegularFileProperty
 
+    @get:Internal
+    abstract val compatibilityDocFile: RegularFileProperty
+
     @TaskAction
-    fun fetch() {
-
-        val dbf = DocumentBuilderFactory.newInstance()
-        val properties = Properties().apply {
-
-            val latests = dbf.fetchLatests(minimumSupportedMinor.get())
-            setProperty("latests", latests.joinToString(","))
-
-            if (fetchNightly.get()) {
-                val nightly = dbf.fetchNightly()
-                setProperty("nightly", nightly)
-            }
+    fun fetch() =
+        fetchLatestAgpVersions().let { (latests, nightly) ->
+            updateProperties(latests, nightly)
+            updateCompatibilityDoc(latests)
         }
-        properties.store(
-            propertiesFile.get().asFile,
-            comment.get()
+
+    private
+    fun fetchLatestAgpVersions(): Pair<List<String>, String?> {
+        val dbf = DocumentBuilderFactory.newInstance()
+        val latests = dbf.fetchLatests(
+            minimumSupportedMinor.get(),
+            "https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/maven-metadata.xml"
         )
+        val nightly =
+            if (fetchNightly.get()) dbf.fetchNightly()
+            else null
+        return latests to nightly
     }
 
     private
-    fun DocumentBuilderFactory.fetchLatests(minimumSupported: String): List<String> {
-        var latests = fetchVersionsFromMavenMetadata("https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/maven-metadata.xml")
+    fun updateProperties(latests: List<String>, nightly: String?) =
+        Properties().run {
+            setProperty("latests", latests.joinToString(","))
+            if (nightly != null) {
+                setProperty("nightly", nightly)
+            }
+            store(
+                propertiesFile.get().asFile,
+                comment.get()
+            )
+        }
+
+    private
+    fun updateCompatibilityDoc(latestAgpVersions: List<String>) {
+        val docFile = compatibilityDocFile.get().asFile
+        val linePrefix = "Gradle is tested with Android Gradle Plugin"
+        var lineFound = false
+        docFile.writeText(
+            docFile.readLines().joinToString(separator = "\n", postfix = "\n") { line ->
+                if (line.startsWith(linePrefix)) {
+                    lineFound = true
+                    "$linePrefix ${latestAgpVersions.firstBaseVersion} through ${latestAgpVersions.lastBaseVersion}."
+                } else {
+                    line
+                }
+            }
+        )
+        require(lineFound) {
+            "File '$docFile' does not contain the expected Kotlin compatibility line"
+        }
+    }
+
+    private
+    val List<String>.firstBaseVersion: String
+        get() = VersionNumber.parse(first()).minorBaseVersion
+
+    private
+    val List<String>.lastBaseVersion: String
+        get() = map { VersionNumber.parse(it) }
+            .last { it.qualifier == null || it.qualifier?.startsWith("rc") == true }
+            .minorBaseVersion
+
+    private
+    val VersionNumber.minorBaseVersion: String
+        get() = "$major.$minor"
+
+    private
+    fun DocumentBuilderFactory.fetchLatests(minimumSupported: String, mavenMetadataUrl: String): List<String> {
+        var latests = fetchVersionsFromMavenMetadata(mavenMetadataUrl)
             .groupBy { it.take(3) }
             .map { (_, versions) -> versions.first() }
         latests = (latests + minimumSupported).sorted()
@@ -81,19 +132,21 @@
     fun DocumentBuilderFactory.fetchNightly(): String =
         fetchVersionsFromMavenMetadata("https://repo.gradle.org/gradle/ext-snapshots-local/com/android/tools/build/gradle/maven-metadata.xml")
             .first()
+}
 
-    private
-    fun DocumentBuilderFactory.fetchVersionsFromMavenMetadata(url: String): List<String> =
-        newDocumentBuilder()
-            .parse(url)
-            .getElementsByTagName("version").let { versions ->
-                (0 until versions.length)
-                    .map { idx -> (versions.item(idx) as Element).textContent }
-                    .reversed()
-            }
 
-    private
-    fun Properties.store(file: java.io.File, comment: String? = null) {
-        PropertiesUtils.store(this, file, comment, Charsets.ISO_8859_1, "\n")
-    }
+internal
+fun DocumentBuilderFactory.fetchVersionsFromMavenMetadata(url: String): List<String> =
+    newDocumentBuilder()
+        .parse(url)
+        .getElementsByTagName("version").let { versions ->
+            (0 until versions.length)
+                .map { idx -> (versions.item(idx) as Element).textContent }
+                .reversed()
+        }
+
+
+internal
+fun Properties.store(file: java.io.File, comment: String? = null) {
+    PropertiesUtils.store(this, file, comment, Charsets.ISO_8859_1, "\n")
 }
diff --git a/build-logic/build-update-utils/src/main/kotlin/gradlebuild/buildutils/tasks/UpdateKotlinVersions.kt b/build-logic/build-update-utils/src/main/kotlin/gradlebuild/buildutils/tasks/UpdateKotlinVersions.kt
new file mode 100644
index 0000000..3ec2ef0
--- /dev/null
+++ b/build-logic/build-update-utils/src/main/kotlin/gradlebuild/buildutils/tasks/UpdateKotlinVersions.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2023 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 gradlebuild.buildutils.tasks
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.UntrackedTask
+import java.util.Properties
+import javax.xml.parsers.DocumentBuilderFactory
+
+
+/**
+ * Fetch the latest Kotlin versions and write a properties file.
+ * Never up-to-date, non-cacheable.
+ */
+@UntrackedTask(because = "Not worth tracking")
+abstract class UpdateKotlinVersions : DefaultTask() {
+
+    @get:Internal
+    abstract val comment: Property<String>
+
+    @get:Internal
+    abstract val minimumSupported: Property<String>
+
+    @get:Internal
+    abstract val propertiesFile: RegularFileProperty
+
+    @get:Internal
+    abstract val compatibilityDocFile: RegularFileProperty
+
+    @TaskAction
+    fun action() =
+        fetchLatestKotlinVersions().let { latestKotlinVersions ->
+            updateProperties(latestKotlinVersions)
+            updateCompatibilityDoc(latestKotlinVersions)
+        }
+
+    private
+    fun fetchLatestKotlinVersions() =
+        DocumentBuilderFactory.newInstance().fetchFirstAndLatestsOfEachMinor(
+            minimumSupported.get(),
+            "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/maven-metadata.xml"
+        )
+
+    private
+    fun updateProperties(latestKotlinVersions: List<String>) =
+        Properties().run {
+            setProperty("latests", latestKotlinVersions.joinToString(","))
+            store(
+                propertiesFile.get().asFile,
+                comment.get()
+            )
+        }
+
+    private
+    fun updateCompatibilityDoc(latestKotlinVersions: List<String>) {
+        val docFile = compatibilityDocFile.get().asFile
+        val linePrefix = "Gradle is tested with Kotlin"
+        var lineFound = false
+        docFile.writeText(
+            docFile.readLines().joinToString(separator = "\n", postfix = "\n") { line ->
+                if (line.startsWith(linePrefix)) {
+                    lineFound = true
+                    "$linePrefix ${latestKotlinVersions.first()} through ${latestKotlinVersions.last()}."
+                } else {
+                    line
+                }
+            }
+        )
+        require(lineFound) {
+            "File '$docFile' does not contain the expected Kotlin compatibility line"
+        }
+    }
+
+    private
+    fun DocumentBuilderFactory.fetchFirstAndLatestsOfEachMinor(minimumSupported: String, mavenMetadataUrl: String): List<String> {
+        val versionsByMinor = fetchVersionsFromMavenMetadata(mavenMetadataUrl)
+            .groupBy { it.take(3) }
+            .toSortedMap()
+        val latests = buildList {
+            versionsByMinor.entries.forEachIndexed { idx, entry ->
+                add(entry.value.lastOrNull { !it.contains("-") })
+                if (idx < versionsByMinor.size - 1) {
+                    add(entry.value.first())
+                } else {
+                    add(entry.value.firstOrNull { !it.contains("-") })
+                    add(entry.value.first())
+                }
+            }
+            add(minimumSupported)
+        }.filterNotNull().distinct().sorted()
+        return latests.subList(latests.indexOf(minimumSupported), latests.size)
+    }
+}
diff --git a/build-logic/dependency-modules/src/main/kotlin/gradlebuild/modules/extension/ExternalModulesExtension.kt b/build-logic/dependency-modules/src/main/kotlin/gradlebuild/modules/extension/ExternalModulesExtension.kt
index 77780ab..e0db5b0 100644
--- a/build-logic/dependency-modules/src/main/kotlin/gradlebuild/modules/extension/ExternalModulesExtension.kt
+++ b/build-logic/dependency-modules/src/main/kotlin/gradlebuild/modules/extension/ExternalModulesExtension.kt
@@ -20,9 +20,9 @@
 
 abstract class ExternalModulesExtension(isBundleGroovy4: Boolean) {
 
-    val groovyVersion = if (isBundleGroovy4) "4.0.7" else "3.0.13"
+    val groovyVersion = if (isBundleGroovy4) "4.0.7" else "3.0.17"
     val configurationCacheReportVersion = "1.2"
-    val kotlinVersion = "1.8.10"
+    val kotlinVersion = "1.8.21"
 
     fun futureKotlin(module: String) = "org.jetbrains.kotlin:kotlin-$module:$kotlinVersion"
 
@@ -77,6 +77,7 @@
     val groovyXml = "$groovyGroup:groovy-xml"
     val gson = "com.google.code.gson:gson"
     val guava = "com.google.guava:guava"
+    val h2Database = "com.h2database:h2"
     val hamcrest = "org.hamcrest:hamcrest-core"
     val httpcore = "org.apache.httpcomponents:httpcore"
     val inject = "javax.inject:javax.inject"
@@ -88,6 +89,7 @@
     val jakartaXmlBind = "jakarta.xml.bind:jakarta.xml.bind-api"
     val jansi = "org.fusesource.jansi:jansi"
     val jatl = "com.googlecode.jatl:jatl"
+    val javaPoet = "com.squareup:javapoet"
     val jaxbCore = "com.sun.xml.bind:jaxb-core"
     val jaxbImpl = "com.sun.xml.bind:jaxb-impl"
     val jcifs = "jcifs:jcifs"
@@ -144,6 +146,7 @@
     val bytebuddy = "net.bytebuddy:byte-buddy"
     val bytebuddyAgent = "net.bytebuddy:byte-buddy-agent"
     val cglib = "cglib:cglib"
+    val compileTesting = "com.google.testing.compile:compile-testing"
     val equalsverifier = "nl.jqno.equalsverifier:equalsverifier"
     val hikariCP = "com.zaxxer:HikariCP"
     val guice = "com.google.inject:guice"
@@ -203,6 +206,7 @@
         commonsLang to License.Apache2,
         commonsLang3 to License.Apache2,
         commonsMath to License.Apache2,
+        compileTesting to License.Apache2,
         configurationCacheReport to License.Apache2,
         fastutil to License.Apache2,
         gcs to License.Apache2,
@@ -215,6 +219,7 @@
         groovy to License.Apache2,
         gson to License.Apache2,
         guava to License.Apache2,
+        h2Database to License.EPL,
         hamcrest to License.BSD3,
         httpcore to License.Apache2,
         hikariCP to License.Apache2,
@@ -227,6 +232,7 @@
         jakartaXmlBind to License.EDL,
         jansi to License.Apache2,
         jatl to License.Apache2,
+        javaPoet to License.Apache2,
         jaxbCore to License.EDL,
         jaxbImpl to License.EDL,
         jcifs to License.LGPL21,
diff --git a/build-logic/documentation/build.gradle.kts b/build-logic/documentation/build.gradle.kts
index 17c9c84..0f44d4f 100644
--- a/build-logic/documentation/build.gradle.kts
+++ b/build-logic/documentation/build.gradle.kts
@@ -20,6 +20,8 @@
     implementation("org.asciidoctor:asciidoctor-gradle-jvm")
     implementation("org.asciidoctor:asciidoctorj")
     implementation("org.asciidoctor:asciidoctorj-pdf")
+    implementation("dev.adamko.dokkatoo:dokkatoo-plugin")
+    implementation("org.jetbrains.dokka:dokka-core")
 
     testImplementation(gradleTestKit())
 }
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/FindBrokenInternalLinks.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/FindBrokenInternalLinks.java
index 8d272ba..e59812f 100644
--- a/build-logic/documentation/src/main/groovy/gradlebuild/docs/FindBrokenInternalLinks.java
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/FindBrokenInternalLinks.java
@@ -48,6 +48,7 @@
  */
 @CacheableTask
 public abstract class FindBrokenInternalLinks extends DefaultTask {
+
     private final Pattern linkPattern = Pattern.compile("<<([^,>]+)[^>]*>>");
     private final Pattern linkWithHashPattern = Pattern.compile("([a-zA-Z_0-9-.]*)#(.*)");
     private final Pattern javadocLinkPattern = Pattern.compile("link:\\{javadocPath\\}/(.*?\\.html)");
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleBuildDocumentationPlugin.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleBuildDocumentationPlugin.java
index 8a1e1c7..f3fa370 100644
--- a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleBuildDocumentationPlugin.java
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleBuildDocumentationPlugin.java
@@ -17,6 +17,7 @@
 package gradlebuild.docs;
 
 import gradlebuild.basics.PublicApi;
+import gradlebuild.basics.PublicKotlinDslApi;
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
@@ -53,6 +54,7 @@ public void apply(Project project) {
 
         project.apply(target -> target.plugin(GradleReleaseNotesPlugin.class));
         project.apply(target -> target.plugin(GradleJavadocsPlugin.class));
+        project.apply(target -> target.plugin(GradleKotlinDslReferencePlugin.class));
         project.apply(target -> target.plugin(GradleDslReferencePlugin.class));
         project.apply(target -> target.plugin(GradleUserManualPlugin.class));
 
@@ -73,6 +75,9 @@ private void applyConventions(Project project, TaskContainer tasks, ObjectFactor
             // Javadocs reference goes into javadoc/
             task.from(extension.getJavadocs().getRenderedDocumentation(), sub -> sub.into("javadoc"));
 
+            // Dokka Kotlin DSL reference goes into kotlin-dsl/
+            task.from(extension.getKotlinDslReference().getRenderedDocumentation(), sub -> sub.into("kotlin-dsl"));
+
             // User manual goes into userguide/ (for historical reasons)
             task.from(extension.getUserManual().getRenderedDocumentation(), sub -> sub.into("userguide"));
 
@@ -104,10 +109,15 @@ private void applyConventions(Project project, TaskContainer tasks, ObjectFactor
 
         extension.getClasspath().from(runtimeClasspath);
         extension.getDocumentedSource().from(sourcesPath.getIncoming().artifactView(v -> v.lenient(true)).getFiles().getAsFileTree().matching(f -> {
-            // Filter out any non-public APIs
             f.include(PublicApi.INSTANCE.getIncludes());
+            // Filter out any non-public APIs
             f.exclude(PublicApi.INSTANCE.getExcludes());
         }));
+        extension.getKotlinDslSource().from(sourcesPath.getIncoming().artifactView(v -> v.lenient(true)).getFiles().getAsFileTree().matching(f -> {
+            f.include(PublicKotlinDslApi.INSTANCE.getIncludes());
+            // Filter out any non-public APIs
+            f.exclude(PublicKotlinDslApi.INSTANCE.getExcludes());
+        }));
     }
 
     private void addUtilityTasks(TaskContainer tasks, GradleDocumentationExtension extension) {
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleDocumentationExtension.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleDocumentationExtension.java
index da71760..266c35b 100644
--- a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleDocumentationExtension.java
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleDocumentationExtension.java
@@ -32,6 +32,7 @@ public abstract class GradleDocumentationExtension {
     private final UserManual userManual;
     private final DslReference dslReference;
     private final Javadocs javadocs;
+    private final KotlinDslReference kotlinDslReference;
 
     @Inject
     public GradleDocumentationExtension(ObjectFactory objects) {
@@ -39,6 +40,7 @@ public GradleDocumentationExtension(ObjectFactory objects) {
         userManual = objects.newInstance(UserManual.class);
         dslReference = objects.newInstance(DslReference.class);
         javadocs = objects.newInstance(Javadocs.class);
+        kotlinDslReference = objects.newInstance(KotlinDslReference.class);
     }
 
     /**
@@ -57,6 +59,12 @@ public GradleDocumentationExtension(ObjectFactory objects) {
     public abstract ConfigurableFileCollection getDocumentedSource();
 
     /**
+     * Kotlin DSL source code to be documented. This should be the "public" Kotlin APIs,
+     * including generated code.
+     */
+    public abstract ConfigurableFileCollection getKotlinDslSource();
+
+    /**
      * The runtime classpath of the source code to be documented.
      */
     public abstract ConfigurableFileCollection getClasspath();
@@ -110,6 +118,14 @@ public void javadocs(Action<? super Javadocs> action) {
         action.execute(javadocs);
     }
 
+    public KotlinDslReference getKotlinDslReference() {
+        return kotlinDslReference;
+    }
+
+    public void kotlinDslReference(Action<? super KotlinDslReference> action) {
+        action.execute(kotlinDslReference);
+    }
+
     /**
      * This property is wired into very slow documentation generation tasks.
      *
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleJavadocsPlugin.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleJavadocsPlugin.java
index 676df3e..c114be3 100644
--- a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleJavadocsPlugin.java
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleJavadocsPlugin.java
@@ -104,6 +104,7 @@ private void generateJavadocs(Project project, ProjectLayout layout, TaskContain
             task.setDestinationDir(generatedJavadocDirectory.get().getAsFile());
 
             if (BuildEnvironment.INSTANCE.getJavaVersion().isJava11Compatible()) {
+                // TODO html4 output was removed in Java 13, see https://bugs.openjdk.org/browse/JDK-8215578
                 options.addBooleanOption("html4", true);
                 options.addBooleanOption("-no-module-directories", true);
 
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleKotlinDslReferencePlugin.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleKotlinDslReferencePlugin.java
new file mode 100644
index 0000000..0b543df
--- /dev/null
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleKotlinDslReferencePlugin.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2023 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 gradlebuild.docs;
+
+import dev.adamko.dokkatoo.DokkatooExtension;
+import dev.adamko.dokkatoo.dokka.parameters.DokkaSourceSetSpec;
+import dev.adamko.dokkatoo.dokka.plugins.DokkaHtmlPluginParameters;
+import dev.adamko.dokkatoo.formats.DokkatooHtmlPlugin;
+import dev.adamko.dokkatoo.tasks.DokkatooGenerateTask;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.file.Directory;
+import org.gradle.api.provider.Provider;
+import org.gradle.api.tasks.TaskProvider;
+
+public class GradleKotlinDslReferencePlugin implements Plugin<Project> {
+
+    private static final String TASK_NAME = "dokkatooGeneratePublicationHtml";
+
+    @Override
+    public void apply(Project project) {
+        GradleDocumentationExtension documentationExtension = project.getExtensions().getByType(GradleDocumentationExtension.class);
+        applyPlugin(project);
+        updateDocumentationExtension(project, documentationExtension);
+        configurePlugin(project, documentationExtension);
+    }
+
+    private static void applyPlugin(Project project) {
+        project.getPlugins().apply(DokkatooHtmlPlugin.class);
+    }
+
+    private static void updateDocumentationExtension(Project project, GradleDocumentationExtension extension) {
+        TaskProvider<Task> generateTask = project.getTasks().named(TASK_NAME);
+        Provider<? extends Directory> outputDirectory = generateTask.flatMap(t -> ((DokkatooGenerateTask) t).getOutputDirectory());
+        extension.getKotlinDslReference().getRenderedDocumentation().set(outputDirectory);
+    }
+
+    private static void configurePlugin(Project project, GradleDocumentationExtension extension) {
+        renameModule(project);
+        wireInArtificialSourceSet(project, extension);
+        setStyling(project, extension);
+    }
+
+    /**
+     * The name of the module is part of the URI for deep links, changing it will break existing links.
+     * The name of the module must match the first header of {@code kotlin/Module.md} file.
+     */
+    private static void renameModule(Project project) {
+        getDokkatooExtension(project).getModuleName().set("gradle");
+    }
+
+    private static void wireInArtificialSourceSet(Project project, GradleDocumentationExtension extension) {
+        TaskProvider<GradleKotlinDslRuntimeGeneratedSources> runtimeExtensions = project.getTasks()
+            .register("gradleKotlinDslRuntimeGeneratedSources", GradleKotlinDslRuntimeGeneratedSources.class, task -> {
+                task.getGeneratedSources().set(project.getLayout().getBuildDirectory().dir("gradle-kotlin-dsl-extensions/sources"));
+                task.getGeneratedClasses().set(project.getLayout().getBuildDirectory().dir("gradle-kotlin-dsl-extensions/classes"));
+            });
+
+        NamedDomainObjectContainer<DokkaSourceSetSpec> kotlinSourceSet = getDokkatooExtension(project).getDokkatooSourceSets();
+        kotlinSourceSet.register("kotlin_dsl", spec -> {
+            spec.getDisplayName().set("Kotlin DSL");
+            spec.getSourceRoots().from(extension.getKotlinDslSource());
+            spec.getSourceRoots().from(runtimeExtensions.flatMap(GradleKotlinDslRuntimeGeneratedSources::getGeneratedSources));
+            spec.getClasspath().from(extension.getClasspath());
+            spec.getClasspath().from(runtimeExtensions.flatMap(GradleKotlinDslRuntimeGeneratedSources::getGeneratedClasses));
+            spec.getIncludes().from(extension.getSourceRoot().file("kotlin/Module.md"));
+        });
+
+        NamedDomainObjectContainer<DokkaSourceSetSpec> javaSourceSet = getDokkatooExtension(project).getDokkatooSourceSets();
+        javaSourceSet.register("java_api", spec -> {
+            spec.getDisplayName().set("Java API");
+            spec.getSourceRoots().from(extension.getDocumentedSource());
+            spec.getClasspath().from(extension.getClasspath());
+            spec.getIncludes().from(extension.getSourceRoot().file("kotlin/Module.md"));
+        });
+    }
+
+    private static void setStyling(Project project, GradleDocumentationExtension extension) {
+        getDokkatooExtension(project).getPluginsConfiguration().named("html", DokkaHtmlPluginParameters.class, config -> {
+            config.getCustomStyleSheets().from(extension.getSourceRoot().file("kotlin/styles/gradle.css"));
+            config.getCustomAssets().from(extension.getSourceRoot().file("kotlin/images/gradle-logo.svg"));
+            config.getFooterMessage().set("Gradle Kotlin DSL Reference");
+        });
+    }
+
+    private static DokkatooExtension getDokkatooExtension(Project project) {
+        return project.getExtensions().getByType(DokkatooExtension.class);
+    }
+
+}
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleKotlinDslRuntimeGeneratedSources.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleKotlinDslRuntimeGeneratedSources.java
new file mode 100644
index 0000000..7e908e0
--- /dev/null
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleKotlinDslRuntimeGeneratedSources.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2023 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 gradlebuild.docs;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.file.ArchiveOperations;
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.file.FileSystemOperations;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.initialization.ClassLoaderScope;
+import org.gradle.api.tasks.CacheableTask;
+import org.gradle.api.tasks.Classpath;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.initialization.ClassLoaderScopeRegistry;
+import org.gradle.internal.classloader.ClasspathUtil;
+import org.gradle.kotlin.dsl.provider.KotlinScriptClassPathProvider;
+
+import javax.inject.Inject;
+import java.io.File;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static java.util.stream.Collectors.toList;
+
+/**
+ * Extracts Kotlin DSL runtime generated sources.
+ *
+ * Current implementation extracts these from the wrapper's API jars.
+ * This is not correct as it should do this with the built distribution instead.
+ *
+ * Doing it correctly would require running a Gradle build with the full
+ * distribution and extracting the generated api jar from its Gradle user home,
+ * slowing down building documentation.
+ *
+ * All this would be so much simpler if the Kotlin extensions to the Gradle API
+ * were generated at build time instead.
+ *
+ * This is a first step to get the doc to be complete and will be revisited.
+ */
+@CacheableTask
+public abstract class GradleKotlinDslRuntimeGeneratedSources extends DefaultTask {
+
+    @Classpath
+    public abstract ConfigurableFileCollection getInputClasspath();
+
+    @OutputDirectory
+    public abstract DirectoryProperty getGeneratedSources();
+
+    @OutputDirectory
+    public abstract DirectoryProperty getGeneratedClasses();
+
+    @Inject
+    protected abstract KotlinScriptClassPathProvider getKotlinScriptClassPathProvider();
+
+    @Inject
+    protected abstract ClassLoaderScopeRegistry getClassLoaderScopeRegistry();
+
+    @Inject
+    protected abstract ArchiveOperations getArchives();
+
+    @Inject
+    protected abstract FileSystemOperations getFs();
+
+    public GradleKotlinDslRuntimeGeneratedSources() {
+        getInputClasspath().from(
+            ClasspathUtil.getClasspath(getInputClassLoaderScope().getExportClassLoader()).getAsFiles()
+        );
+    }
+
+    private ClassLoaderScope getInputClassLoaderScope() {
+        return getClassLoaderScopeRegistry().getCoreAndPluginsScope();
+    }
+
+    @TaskAction
+    public void action() {
+        FileTree kotlinDslExtensionsJar = getArchives().zipTree(getKotlinDslExtensionsJar());
+        getFs().sync(spec -> {
+            spec.from(kotlinDslExtensionsJar, zip -> zip.include("**/*.kt"));
+            spec.into(getGeneratedSources());
+        });
+        getFs().sync(spec -> {
+            spec.from(kotlinDslExtensionsJar, zip -> zip.include("**/*.class"));
+            spec.into(getGeneratedClasses());
+        });
+    }
+
+    private File getKotlinDslExtensionsJar() {
+        return getOnlyElement(
+            getKotlinScriptClassPathProvider()
+                .compilationClassPathOf(getInputClassLoaderScope())
+                .getAsFiles()
+                .stream()
+                .filter(file -> file.getName().startsWith("gradle-kotlin-dsl-extensions"))
+                .collect(toList())
+        );
+    }
+}
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleReleaseNotesPlugin.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleReleaseNotesPlugin.java
index 9aa0b8a..eb2fc8c 100644
--- a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleReleaseNotesPlugin.java
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleReleaseNotesPlugin.java
@@ -82,6 +82,7 @@ private void generateReleaseNotes(Project project, ProjectLayout layout, TaskCon
         tasks.withType(AbstractCheckOrUpdateContributorsInReleaseNotes.class).configureEach(task -> {
             task.getGithubToken().set(project.getProviders().environmentVariable("GITHUB_TOKEN"));
             task.getReleaseNotes().set(extension.getReleaseNotes().getMarkdownFile());
+            task.getMilestone().convention(project.getProviders().fileContents(project.getRootProject().getLayout().getProjectDirectory().file("version.txt")).getAsText().map(String::trim));
         });
 
         Configuration jquery = project.getConfigurations().create("jquery", conf -> {
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleUserManualPlugin.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleUserManualPlugin.java
index d8a38e1..c86e854 100644
--- a/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleUserManualPlugin.java
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/GradleUserManualPlugin.java
@@ -45,6 +45,9 @@
 import static java.util.Collections.singletonMap;
 
 public class GradleUserManualPlugin implements Plugin<Project> {
+
+    public static final String DOCS_GRADLE_ORG = "https://docs.gradle.org/";
+
     @Override
     public void apply(Project project) {
         ProjectLayout layout = project.getLayout();
@@ -260,7 +263,7 @@ private void generateUserManual(Project project, TaskContainer tasks, ProjectLay
             attributes.put("toc-title", "Contents");
             attributes.put("groovyDslPath", "../dsl");
             attributes.put("javadocPath", "../javadoc");
-            attributes.put("kotlinDslPath", "https://gradle.github.io/kotlin-dsl-docs/api");
+            attributes.put("kotlinDslPath", "../kotlin-dsl");
             // Used by SampleIncludeProcessor from `gradle/dotorg-docs`
             // TODO: This breaks the provider
             attributes.put("samples-dir", extension.getUserManual().getStagedDocumentation().get().getAsFile()); // TODO:
@@ -312,9 +315,10 @@ private void configureForUserGuideSinglePage(AsciidoctorTask task, GradleDocumen
         attributes.put("toclevels", 2);
 
         // TODO: This breaks if version is changed later
-        attributes.put("groovyDslPath", "https://docs.gradle.org/" + project.getVersion() + "/dsl");
-        attributes.put("javadocPath", "https://docs.gradle.org/" + project.getVersion() + "/javadoc");
-        attributes.put("samplesPath", "https://docs.gradle.org/" + project.getVersion() + "/samples");
+        String versionUrl = DOCS_GRADLE_ORG + project.getVersion();
+        attributes.put("groovyDslPath", versionUrl + "/dsl");
+        attributes.put("javadocPath", versionUrl + "/javadoc");
+        attributes.put("samplesPath", versionUrl + "/samples");
         attributes.put("kotlinDslPath", "https://gradle.github.io/kotlin-dsl-docs/api");
         // Used by SampleIncludeProcessor from `gradle/dotorg-docs`
         // TODO: This breaks the provider
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/Javadocs.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/Javadocs.java
index 695c581..ea649d2 100644
--- a/build-logic/documentation/src/main/groovy/gradlebuild/docs/Javadocs.java
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/Javadocs.java
@@ -30,10 +30,12 @@ public abstract class Javadocs {
      * Link to Java API to use when generating Javadoc
      */
     public abstract Property<URI> getJavaApi();
+
     /**
      * Link to Groovy API to use when generating Javadoc
      */
     public abstract Property<URI> getGroovyApi();
+
     /**
      * The CSS file to style Javadocs with
      */
diff --git a/build-logic/documentation/src/main/groovy/gradlebuild/docs/KotlinDslReference.java b/build-logic/documentation/src/main/groovy/gradlebuild/docs/KotlinDslReference.java
new file mode 100644
index 0000000..0d0d275f
--- /dev/null
+++ b/build-logic/documentation/src/main/groovy/gradlebuild/docs/KotlinDslReference.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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 gradlebuild.docs;
+
+import org.gradle.api.file.DirectoryProperty;
+
+/**
+ * Configuration for generating Dokka based Kotlin DSL docs.
+ */
+public abstract class KotlinDslReference {
+
+    /**
+     * The location of the final rendered Dokka content.
+     */
+    public abstract DirectoryProperty getRenderedDocumentation();
+
+}
diff --git a/build-logic/documentation/src/test/resources/org/gradle/test/GroovyAnnotation.groovy b/build-logic/documentation/src/test/resources/org/gradle/test/GroovyAnnotation.groovy
index 6db3075..b35d8f1 100644
--- a/build-logic/documentation/src/test/resources/org/gradle/test/GroovyAnnotation.groovy
+++ b/build-logic/documentation/src/test/resources/org/gradle/test/GroovyAnnotation.groovy
@@ -2,4 +2,4 @@
 
 public @interface GroovyAnnotation {
 
-}
\ No newline at end of file
+}
diff --git a/build-logic/documentation/src/test/resources/org/gradle/test/GroovyEnum.groovy b/build-logic/documentation/src/test/resources/org/gradle/test/GroovyEnum.groovy
index b0eb5be..c53882f 100644
--- a/build-logic/documentation/src/test/resources/org/gradle/test/GroovyEnum.groovy
+++ b/build-logic/documentation/src/test/resources/org/gradle/test/GroovyEnum.groovy
@@ -2,4 +2,4 @@
 
 public enum GroovyEnum {
     A, B
-}
\ No newline at end of file
+}
diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties
index a4dc07f..a56e714 100644
--- a/build-logic/gradle.properties
+++ b/build-logic/gradle.properties
@@ -1,4 +1,2 @@
 org.gradle.jvmargs=-Xmx2500m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
 org.gradle.parallel=true
-# Enables Kotlin assignment support, should be removed once we enable assignment by default
-systemProp.org.gradle.unsafe.kotlin.assignment=true
diff --git a/build-logic/integration-testing/src/main/kotlin/gradlebuild.distribution-testing.gradle.kts b/build-logic/integration-testing/src/main/kotlin/gradlebuild.distribution-testing.gradle.kts
index b1f3056..9b0f1ce 100644
--- a/build-logic/integration-testing/src/main/kotlin/gradlebuild.distribution-testing.gradle.kts
+++ b/build-logic/integration-testing/src/main/kotlin/gradlebuild.distribution-testing.gradle.kts
@@ -16,8 +16,8 @@
 
 import gradlebuild.basics.repoRoot
 import gradlebuild.cleanup.services.CachesCleaner
-import gradlebuild.integrationtests.tasks.DistributionTest
 import gradlebuild.integrationtests.setSystemPropertiesOfTestJVM
+import gradlebuild.integrationtests.tasks.DistributionTest
 
 plugins {
     java
@@ -62,13 +62,11 @@
 
     gradleInstallationForTest.apply {
         if (executerRequiresDistribution(taskName)) {
-            gradleHomeDir.setFrom(
-                if (executerRequiresFullDistribution(taskName)) {
-                    configurations["${prefix}TestFullDistributionRuntimeClasspath"]
-                } else {
-                    configurations["${prefix}TestDistributionRuntimeClasspath"]
-                }
-            )
+            gradleHomeDir = if (executerRequiresFullDistribution(taskName)) {
+                configurations["${prefix}TestFullDistributionRuntimeClasspath"]
+            } else {
+                configurations["${prefix}TestDistributionRuntimeClasspath"]
+            }
         }
         // Set the base user home dir to be share by integration tests.
         // The actual user home dir will be a subfolder using the name of the distribution.
@@ -80,12 +78,12 @@
     }
 
     // Wire the different inputs for local distributions and repos that are declared by dependencies in the build scripts
-    normalizedDistributionZip.distributionZip.setFrom(configurations["${prefix}TestNormalizedDistributionPath"])
-    binDistributionZip.distributionZip.setFrom(configurations["${prefix}TestBinDistributionPath"])
-    allDistributionZip.distributionZip.setFrom(configurations["${prefix}TestAllDistributionPath"])
-    docsDistributionZip.distributionZip.setFrom(configurations["${prefix}TestDocsDistributionPath"])
-    srcDistributionZip.distributionZip.setFrom(configurations["${prefix}TestSrcDistributionPath"])
-    localRepository.localRepo.setFrom(configurations["${prefix}TestLocalRepositoryPath"])
+    normalizedDistributionZip.distributionZip = configurations["${prefix}TestNormalizedDistributionPath"]
+    binDistributionZip.distributionZip = configurations["${prefix}TestBinDistributionPath"]
+    allDistributionZip.distributionZip = configurations["${prefix}TestAllDistributionPath"]
+    docsDistributionZip.distributionZip = configurations["${prefix}TestDocsDistributionPath"]
+    srcDistributionZip.distributionZip = configurations["${prefix}TestSrcDistributionPath"]
+    localRepository.localRepo = configurations["${prefix}TestLocalRepositoryPath"]
 }
 
 fun DistributionTest.setJvmArgsOfTestJvm() {
diff --git a/build-logic/integration-testing/src/main/kotlin/gradlebuild/integrationtests/shared-configuration.kt b/build-logic/integration-testing/src/main/kotlin/gradlebuild/integrationtests/shared-configuration.kt
index 07d0102..4ad54a1 100644
--- a/build-logic/integration-testing/src/main/kotlin/gradlebuild/integrationtests/shared-configuration.kt
+++ b/build-logic/integration-testing/src/main/kotlin/gradlebuild/integrationtests/shared-configuration.kt
@@ -34,8 +34,10 @@
 import org.gradle.api.attributes.Category
 import org.gradle.api.attributes.LibraryElements
 import org.gradle.api.attributes.Usage
+import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.Directory
 import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.SourceSet
@@ -79,6 +81,7 @@
         resolver("${prefix}TestAllDistributionPath", "gradle-all-distribution-zip", allDistribution)
         resolver("${prefix}TestDocsDistributionPath", "gradle-docs-distribution-zip", docsDistribution)
         resolver("${prefix}TestSrcDistributionPath", "gradle-src-distribution-zip", srcDistribution)
+        resolver("${prefix}TestAgentsClasspath", LibraryElements.JAR)
     }
 
     // do not attempt to find projects when the plugin is applied just to generate accessors
@@ -87,6 +90,10 @@
             "${prefix}TestRuntimeOnly"(project.the<ExternalModulesExtension>().junit5Vintage)
             "${prefix}TestImplementation"(project(":internal-integ-testing"))
             "${prefix}TestFullDistributionRuntimeClasspath"(project(":distributions-full"))
+            // Add the agent JAR to the test runtime classpath so the InProcessGradleExecuter can find the module and spawn daemons.
+            // This doesn't apply the agent to the test process.
+            "${prefix}TestRuntimeOnly"(project(":instrumentation-agent"))
+            "${prefix}TestAgentsClasspath"(project(":instrumentation-agent"))
         }
     }
 }
@@ -136,6 +143,16 @@
 
 
 internal
+abstract class AgentsClasspathProvider : CommandLineArgumentProvider {
+    @get:InputFiles
+    @get:PathSensitive(PathSensitivity.NONE)
+    abstract val agentsClasspath: ConfigurableFileCollection
+
+    override fun asArguments() = agentsClasspath.files.map { "-javaagent:$it" }
+}
+
+
+internal
 class SamplesBaseDirPropertyProvider(@InputDirectory @PathSensitive(PathSensitivity.RELATIVE) val autoTestedSamplesDir: Directory) : CommandLineArgumentProvider {
     override fun asArguments() = listOf("-DdeclaredSampleInputs=${autoTestedSamplesDir.asFile.absolutePath}")
 }
@@ -156,10 +173,28 @@
             val samplesDir = layout.projectDirectory.dir("src/main")
             jvmArgumentProviders.add(SamplesBaseDirPropertyProvider(samplesDir))
         }
+        setUpAgentIfNeeded(testType, executer)
     }
 
 
 private
+fun IntegrationTest.setUpAgentIfNeeded(testType: TestType, executer: String) {
+    if (executer == "embedded") {
+        // Apply the instrumentation agent to the test process when running integration tests with embedded Gradle executer.
+        jvmArgumentProviders.add(project.objects.newInstance<AgentsClasspathProvider>().apply {
+            agentsClasspath.from(project.configurations["${testType.prefix}TestAgentsClasspath"])
+        })
+    }
+
+    val integTestUseAgentSysPropName = "org.gradle.integtest.agent.allowed"
+    if (project.hasProperty(integTestUseAgentSysPropName)) {
+        val shouldUseAgent = (project.property(integTestUseAgentSysPropName) as? String).toBoolean()
+        systemProperties[integTestUseAgentSysPropName] = shouldUseAgent.toString()
+    }
+}
+
+
+private
 fun IntegrationTest.addDebugProperties() {
     // TODO Move magic property out
     if (project.hasProperty("org.gradle.integtest.debug")) {
diff --git a/build-logic/integration-testing/src/main/kotlin/gradlebuild/integrationtests/tasks/GenerateLanguageAnnotations.kt b/build-logic/integration-testing/src/main/kotlin/gradlebuild/integrationtests/tasks/GenerateLanguageAnnotations.kt
index 009c3fe..704c850 100644
--- a/build-logic/integration-testing/src/main/kotlin/gradlebuild/integrationtests/tasks/GenerateLanguageAnnotations.kt
+++ b/build-logic/integration-testing/src/main/kotlin/gradlebuild/integrationtests/tasks/GenerateLanguageAnnotations.kt
@@ -49,7 +49,7 @@
     @TaskAction
     fun generateAnnotations() {
         val queue = workerExecutor.classLoaderIsolation {
-            classpath.setFrom(classpath)
+            classpath = this@GenerateLanguageAnnotations.classpath
         }
         queue.submit(AnnotationGeneratorWorkAction::class) {
             packageName = this@GenerateLanguageAnnotations.packageName
diff --git a/build-logic/jvm/src/main/kotlin/gradlebuild.launchable-jar.gradle.kts b/build-logic/jvm/src/main/kotlin/gradlebuild.launchable-jar.gradle.kts
index b4e6a54..f1802a1 100644
--- a/build-logic/jvm/src/main/kotlin/gradlebuild.launchable-jar.gradle.kts
+++ b/build-logic/jvm/src/main/kotlin/gradlebuild.launchable-jar.gradle.kts
@@ -21,9 +21,18 @@
 }
 
 val manifestClasspath by configurations.creating {
+    isTransitive = false
+
+    configureAsJarClasspath()
+}
+
+val agentsClasspath by configurations.creating {
+    configureAsJarClasspath()
+}
+
+fun Configuration.configureAsJarClasspath() {
     isCanBeResolved = true
     isCanBeConsumed = false
-    isTransitive = false
 
     attributes {
         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
@@ -46,6 +55,7 @@
 val startScripts = tasks.register<GradleStartScriptGenerator>("startScripts") {
     startScriptsDir = layout.buildDirectory.dir("startScripts")
     launcherJar.from(tasks.jar)
+    agentJars.from(agentsClasspath)
     // The trick below is to use the templates from the current code instead of the wrapper. It does not cover the case where the generation logic is updated though.
     unixScriptTemplate.from(layout.projectDirectory.file("../plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt"))
     windowsScriptTemplate.from(layout.projectDirectory.file("../plugins/src/main/resources/org/gradle/api/internal/plugins/windowsStartScript.txt"))
diff --git a/build-logic/jvm/src/main/kotlin/gradlebuild.unittest-and-compile.gradle.kts b/build-logic/jvm/src/main/kotlin/gradlebuild.unittest-and-compile.gradle.kts
index 1270fd9..308c998 100644
--- a/build-logic/jvm/src/main/kotlin/gradlebuild.unittest-and-compile.gradle.kts
+++ b/build-logic/jvm/src/main/kotlin/gradlebuild.unittest-and-compile.gradle.kts
@@ -21,6 +21,7 @@
 import com.gradle.enterprise.gradleplugin.testselection.internal.PredictiveTestSelectionExtensionInternal
 import gradlebuild.basics.BuildEnvironment
 import gradlebuild.basics.FlakyTestStrategy
+import gradlebuild.basics.accessors.kotlin
 import gradlebuild.basics.flakyTestStrategy
 import gradlebuild.basics.maxParallelForks
 import gradlebuild.basics.maxTestDistributionPartitionSecond
@@ -99,6 +100,11 @@
         main.groovy.srcDirs.forEach {
             outgoing.artifact(it)
         }
+        pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
+            main.kotlin.srcDirs.forEach {
+                outgoing.artifact(it)
+            }
+        }
     }
 }
 
@@ -218,6 +224,11 @@
             jvmArgs(listOf("--add-opens", "java.base/java.lang=ALL-UNNAMED")) // Used in tests by ClassLoaderUtils
         }
     }
+    if (usesEmbeddedExecuter() && OperatingSystem.current().isWindows) {
+        // Disable incremental snapshotting for Windows, since it runs OOM,
+        // root cause: https://youtrack.jetbrains.com/issue/KT-57757
+        jvmArgs("-Dkotlin.incremental.useClasspathSnapshot=false")
+    }
 }
 
 fun Test.addOsAsInputs() {
diff --git a/build-logic/jvm/src/main/kotlin/gradlebuild/jvm/extension/StrictCompileExtension.kt b/build-logic/jvm/src/main/kotlin/gradlebuild/jvm/extension/StrictCompileExtension.kt
index b8badf7..5cb695d 100644
--- a/build-logic/jvm/src/main/kotlin/gradlebuild/jvm/extension/StrictCompileExtension.kt
+++ b/build-logic/jvm/src/main/kotlin/gradlebuild/jvm/extension/StrictCompileExtension.kt
@@ -37,6 +37,12 @@
         }
     }
 
+    fun ignoreAnnotationProcessing() {
+        tasks.withType<JavaCompile>().configureEach {
+            options.compilerArgs.add("-Xlint:-processing")
+        }
+    }
+
     fun ignoreParameterizedVarargType() {
         tasks.withType<JavaCompile>().configureEach {
             // There is no way to ignore this warning, so we need to turn off "-Werror" completely
diff --git a/build-logic/jvm/src/main/kotlin/gradlebuild/propagated-env-variables.kt b/build-logic/jvm/src/main/kotlin/gradlebuild/propagated-env-variables.kt
index d90161b..44842ac 100644
--- a/build-logic/jvm/src/main/kotlin/gradlebuild/propagated-env-variables.kt
+++ b/build-logic/jvm/src/main/kotlin/gradlebuild/propagated-env-variables.kt
@@ -63,6 +63,8 @@
 
     // Used by performance test to recognize TeamCity buildId
     "BUILD_ID",
+    // Used by some tests to be ignored in specific build
+    "BUILD_TYPE_ID",
     "JPROFILER_HOME",
 
     "LANG",
diff --git a/build-logic/jvm/src/main/kotlin/gradlebuild/startscript/tasks/GradleStartScriptGenerator.kt b/build-logic/jvm/src/main/kotlin/gradlebuild/startscript/tasks/GradleStartScriptGenerator.kt
index bce81a1..d54a311 100644
--- a/build-logic/jvm/src/main/kotlin/gradlebuild/startscript/tasks/GradleStartScriptGenerator.kt
+++ b/build-logic/jvm/src/main/kotlin/gradlebuild/startscript/tasks/GradleStartScriptGenerator.kt
@@ -34,6 +34,7 @@
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskAction
 import org.gradle.util.internal.TextUtil
+import java.io.File
 import java.nio.charset.StandardCharsets
 import javax.inject.Inject
 
@@ -48,6 +49,13 @@
     val launcherJarName: String
         get() = launcherJar.singleFile.name
 
+    @get:Internal
+    abstract val agentJars: ConfigurableFileCollection
+
+    @get:Input
+    val agentJarNames: List<String>
+        get() = agentJars.files.map { it.name }
+
     @get:InputFiles
     @get:PathSensitive(PathSensitivity.NONE)
     abstract val unixScriptTemplate: ConfigurableFileCollection
@@ -75,8 +83,14 @@
         generator.setClasspath(listOf("lib/$launcherJarName"))
         generator.setAppNameSystemProperty("org.gradle.appname")
         generator.setDefaultJvmOpts(listOf("-Xmx64m", "-Xms64m"))
-        generator.generateUnixScript(startScriptsDir.file("gradle").get().asFile)
-        generator.generateWindowsScript(startScriptsDir.file("gradle.bat").get().asFile)
+
+        val unixScriptFile = startScriptsDir.file("gradle").get().asFile
+        generator.generateUnixScript(unixScriptFile)
+        unixScriptFile.injectAgentOptions(TextUtil.getUnixLineSeparator())
+
+        val windowsScriptFile = startScriptsDir.file("gradle.bat").get().asFile
+        generator.generateWindowsScript(windowsScriptFile)
+        windowsScriptFile.injectAgentOptions(TextUtil.getWindowsLineSeparator())
     }
 
     private
@@ -92,4 +106,45 @@
         StartScriptTemplateBindingFactory.windows(),
         FileCollectionBackedTextResource(temporaryFileProvider, windowsScriptTemplate, StandardCharsets.UTF_8)
     )
+
+    /**
+     * Modifies the start script injecting -javaagent flags. The start script file is updated in-place by appending Java agent switches to 'DEFAULT_JVM_OPTS' variable declaration.
+     */
+    private
+    fun File.injectAgentOptions(separator: String) {
+        if (agentJarNames.isEmpty()) {
+            return
+        }
+        var replacementsCount = 0
+        // readLines eats EOLs, so we need to use postfix to make sure the last line ends with EOL too.
+        writeBytes(readLines().joinToString(separator = separator, postfix = separator) { line ->
+            when {
+                // We assume that the declaration is not empty.
+                line.startsWith("DEFAULT_JVM_OPTS='") && line.endsWith('\'') -> {
+                    ++replacementsCount
+                    // Use shell's string concatenation: '...'"..." glues contents of quoted and double-quoted strings together.
+                    // The result would be something like DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'" \"-javaagent:$APP_HOME/lib/agents/foobar.jar\""
+                    line + getAgentOptions("\$APP_HOME").joinToString(separator = " ", prefix = "\" ", postfix = "\"") {
+                        // Wrap the agent switch in double quotes, as the expanded APP_HOME may contain spaces.
+                        // The joined line is enclosed in double quotes too, so double quotes here must be escaped.
+                        "\\\"$it\\\""
+                    }
+                }
+
+                line.startsWith("set DEFAULT_JVM_OPTS=") -> {
+                    ++replacementsCount
+                    // The result would be something like set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" "-javaagent:%APP_HOME%/lib/agents/foobar.jar"
+                    line + getAgentOptions("%APP_HOME%").joinToString(separator = " ", prefix = " \"", postfix = "\"")
+                }
+
+                else -> line
+            }
+        }.toByteArray(StandardCharsets.UTF_8))
+        if (replacementsCount != 1) {
+            throw IllegalArgumentException("The script file produced by the default start script doesn't match expected layout")
+        }
+    }
+
+    private
+    fun getAgentOptions(appHomeVar: String) = agentJarNames.map { "-javaagent:$appHomeVar/lib/agents/$it" }
 }
diff --git a/build-logic/kotlin-dsl/src/main/kotlin/gradlebuild.kotlin-dsl-dependencies-embedded.gradle.kts b/build-logic/kotlin-dsl/src/main/kotlin/gradlebuild.kotlin-dsl-dependencies-embedded.gradle.kts
index 5113aa2..be58c57 100644
--- a/build-logic/kotlin-dsl/src/main/kotlin/gradlebuild.kotlin-dsl-dependencies-embedded.gradle.kts
+++ b/build-logic/kotlin-dsl/src/main/kotlin/gradlebuild.kotlin-dsl-dependencies-embedded.gradle.kts
@@ -26,7 +26,7 @@
 // --- Enable automatic generation of API extensions -------------------
 val apiExtensionsOutputDir = layout.buildDirectory.dir("generated-sources/kotlin")
 
-val publishedKotlinDslPluginVersion = "4.0.7" // TODO:kotlin-dsl
+val publishedKotlinDslPluginVersion = "4.0.14" // TODO:kotlin-dsl
 
 tasks {
     val generateKotlinDependencyExtensions by registering(GenerateKotlinDependencyExtensions::class) {
@@ -41,6 +41,12 @@
         kotlin.srcDir(apiExtensionsFileCollection)
     }
 
+    // Workaround for https://github.com/gradle/gradle/issues/24131
+    // See gradlebuild.unittest-and-compile.gradle.kts
+    configurations["transitiveSourcesElements"].outgoing.artifact(apiExtensionsOutputDir) {
+        builtBy(generateKotlinDependencyExtensions)
+    }
+
     processResources {
         // Add generated sources to the main jar because `src` or any other Gradle distribution does not include them.
         // A more general solution is probably required: https://github.com/gradle/gradle/issues/21114
diff --git a/build-logic/packaging/src/main/kotlin/gradlebuild.install.gradle.kts b/build-logic/packaging/src/main/kotlin/gradlebuild.install.gradle.kts
index 7a7cca6..9911967 100644
--- a/build-logic/packaging/src/main/kotlin/gradlebuild.install.gradle.kts
+++ b/build-logic/packaging/src/main/kotlin/gradlebuild.install.gradle.kts
@@ -36,13 +36,20 @@
 }
 
 fun validateInstallDir(installDir: Directory) = installDir.also { dir ->
-    if (dir.asFile.isFile) {
-        throw RuntimeException("Install directory $dir does not look like a Gradle installation. Cannot delete it to install.")
+    val dirFile = dir.asFile
+    if (dirFile.isFile) {
+        throw RuntimeException("Install directory $dir is not valid: it is actually a file")
     }
-    if (dir.asFile.isDirectory) {
-        val libDir = dir.asFile.resolve("lib")
-        if (libDir.list()?.none { it.matches(Regex("gradle.*\\.jar")) } == true) {
-            throw RuntimeException("Install directory $dir does not look like a Gradle installation. Cannot delete it to install.")
+    if (dirFile.list()?.isEmpty() != false) {
+        return@also
+    }
+    val binDirFiles = dirFile.resolve("bin").list()
+    if (binDirFiles != null && binDirFiles.isNotEmpty() && binDirFiles.all { it.matches(Regex("^gradle.*")) }) {
+        val libDir = dirFile.resolve("lib")
+        if (libDir.list()?.any { it.matches(Regex("^gradle.*\\.jar")) } == true) {
+            return@also
         }
     }
+
+    throw RuntimeException("Install directory $dir does not look like a Gradle installation. Cannot delete it to install.")
 }
diff --git a/build-logic/packaging/src/test/kotlin/gradlebuild/packaging/GradleDistributionInstallTest.kt b/build-logic/packaging/src/test/kotlin/gradlebuild/packaging/GradleDistributionInstallTest.kt
new file mode 100644
index 0000000..fd3b8f0
--- /dev/null
+++ b/build-logic/packaging/src/test/kotlin/gradlebuild/packaging/GradleDistributionInstallTest.kt
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2023 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 gradlebuild.packaging
+
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.io.TempDir
+import java.io.File
+
+
+class GradleDistributionInstallTest {
+    @TempDir
+    private
+    lateinit var temporaryFolder: File
+
+    private
+    lateinit var target: File
+
+    private
+    lateinit var projectRoot: File
+
+    @BeforeEach
+    fun setup() {
+        target = File(temporaryFolder, "target")
+        target.mkdir()
+
+        projectRoot = File(temporaryFolder, "gradle")
+
+        createMinimalDistribution()
+    }
+
+    @Test
+    fun `installs into empty dir`() {
+        assertSucceeds()
+    }
+
+    @Test
+    fun `installs to non-existing dir`() {
+        target = File(target, "non-existing")
+
+        assertSucceeds()
+    }
+
+    @Test
+    fun `installs into previous distribution`() {
+        assertSucceeds()
+        assertSucceeds()
+    }
+
+    @Test
+    fun `installs into something that looks like previous distribution`() {
+        target.resolve("bin").apply {
+            mkdir()
+            File(this, "gradle").writeText("stub")
+            File(this, "gradle.exe").writeText("stub")
+        }
+
+        target.resolve("lib").apply {
+            mkdir()
+            File(this, "gradle-8.0.2.jar").writeText("stub")
+            File(this, "all-deps-in-the-world-1.2.2.jar").writeText("stub")
+        }
+
+        assertSucceeds()
+    }
+
+    @Test
+    fun `does not install to a file`() {
+        val file = File(target, "some_file.txt").also {
+            it.writeText("some content")
+        }
+        target = file
+
+        assertFails("Install directory $file is not valid: it is actually a file")
+        assertTrue(file.exists())
+    }
+
+    @Test
+    fun `does not install to non-empty dir without lib`() {
+        target.resolve("bin").apply {
+            mkdir()
+            File(this, "gradle").writeText("stub")
+            File(this, "gradle.exe").writeText("stub")
+        }
+
+        assertFails("Install directory $target does not look like a Gradle installation. Cannot delete it to install.")
+        assertTargetIsPreserved()
+    }
+
+    @Test
+    fun `does not install to non-empty dir with empty lib`() {
+        target.resolve("bin").apply {
+            mkdir()
+            File(this, "gradle").writeText("stub")
+            File(this, "gradle.exe").writeText("stub")
+        }
+
+        target.resolve("lib").apply {
+            mkdir()
+        }
+
+        assertFails("Install directory $target does not look like a Gradle installation. Cannot delete it to install.")
+        assertTargetIsPreserved()
+    }
+
+    @Test
+    fun `does not install to non-empty dir without gradle executables`() {
+        target.resolve("lib").apply {
+            mkdir()
+            File(this, "gradle-8.0.2.jar").writeText("stub")
+            File(this, "all-deps-in-the-world-1.2.2.jar").writeText("stub")
+        }
+
+        assertFails("Install directory $target does not look like a Gradle installation. Cannot delete it to install.")
+        assertTargetIsPreserved()
+    }
+
+    @Test
+    fun `does not install to non-empty dir without gradle executables and empty bin`() {
+        target.resolve("bin").apply {
+            mkdir()
+        }
+
+        target.resolve("lib").apply {
+            mkdir()
+            File(this, "gradle-8.0.2.jar").writeText("stub")
+            File(this, "all-deps-in-the-world-1.2.2.jar").writeText("stub")
+        }
+        assertFails("Install directory $target does not look like a Gradle installation. Cannot delete it to install.")
+        assertTargetIsPreserved()
+    }
+
+    @Test
+    fun `does not install to dir with other executables`() {
+        target.resolve("bin").apply {
+            mkdir()
+            File(this, "gradle").writeText("stub")
+            File(this, "gradle.exe").writeText("stub")
+            File(this, "python").writeText("stub")
+        }
+
+        target.resolve("lib").apply {
+            mkdir()
+            File(this, "gradle-8.0.2.jar").writeText("stub")
+            File(this, "all-deps-in-the-world-1.2.2.jar").writeText("stub")
+        }
+
+        assertFails("Install directory $target does not look like a Gradle installation. Cannot delete it to install.")
+        assertTargetIsPreserved()
+    }
+
+    @Test
+    fun `does not install to dir without jars`() {
+        target.resolve("bin").apply {
+            mkdir()
+            File(this, "gradle").writeText("stub")
+            File(this, "gradle.exe").writeText("stub")
+            File(this, "python").writeText("stub")
+        }
+
+        target.resolve("lib").apply {
+            mkdir()
+            File(this, "all-deps-in-the-world-1.2.2.jar").writeText("stub")
+        }
+
+        assertFails("Install directory $target does not look like a Gradle installation. Cannot delete it to install.")
+        assertTargetIsPreserved()
+    }
+
+    private
+    fun runner() = GradleRunner.create()
+        .withProjectDir(projectRoot)
+        .withPluginClasspath()
+        .forwardOutput()
+        .withArguments("install", "-Pgradle_installPath=$target")
+
+    private
+    fun assertSucceeds() {
+        runner().build()
+        assertEquals(
+            marker,
+            target.resolve("bin/gradlew.bat").readText(),
+        )
+    }
+
+    private
+    fun assertFails(error: String) {
+        val result = runner().buildAndFail()
+        assertTrue(
+            result.output.contains(error)
+        )
+    }
+
+    private
+    fun assertTargetIsPreserved() {
+        assertTrue((target.list()?.size ?: 0) > 0)
+        assertTrue(target.walk().filter { it.isFile }.all { it.readText() == "stub" })
+    }
+
+    private
+    fun createMinimalDistribution() {
+        projectRoot.mkdir()
+        File(projectRoot, "build.gradle.kts").writeText("""
+        plugins {
+            id("gradlebuild.install")
+        }
+
+        val gradleScriptPath by configurations.creating
+        val coreRuntimeClasspath by configurations.creating
+        val runtimeClasspath by configurations.creating
+        val agentsRuntimeClasspath by configurations.creating
+
+        val runtimeApiInfoJar by tasks.registering(Jar::class){
+            archiveVersion.set("8.2")
+            archiveBaseName.set("gradle-runtime-api")
+            destinationDirectory.set(layout.buildDirectory.dir("jars"))
+        }
+
+        dependencies {
+            gradleScriptPath(files("gradlew.bat"))
+        }
+        """)
+        File(projectRoot, "version.txt").writeText("8.2")
+        File(projectRoot, "gradlew.bat").writeText(marker)
+    }
+
+    private
+    val marker = "I'm a marker text"
+}
diff --git a/build-logic/performance-testing/src/main/groovy/gradlebuild.performance-templates.gradle b/build-logic/performance-testing/src/main/groovy/gradlebuild.performance-templates.gradle
index a127866..b927b21 100644
--- a/build-logic/performance-testing/src/main/groovy/gradlebuild.performance-templates.gradle
+++ b/build-logic/performance-testing/src/main/groovy/gradlebuild.performance-templates.gradle
@@ -409,7 +409,7 @@
 performanceTest.registerAndroidTestProject("santaTrackerAndroidBuild", RemoteProject) {
     remoteUri = 'https://github.com/gradle/santa-tracker-android.git'
     // Pinned from main branch
-    ref = '180b7a91054d5fe5b617543bb2f74a3819537b7b'
+    ref = '70b2fec935b82d8783579290512690fe2eca8884'
     doLast {
         addGoogleServicesJson(outputDirectory)
     }
diff --git a/build-logic/performance-testing/src/main/kotlin/gradlebuild/performance/PerformanceTestPlugin.kt b/build-logic/performance-testing/src/main/kotlin/gradlebuild/performance/PerformanceTestPlugin.kt
index 1c354a4..b39d94a 100644
--- a/build-logic/performance-testing/src/main/kotlin/gradlebuild/performance/PerformanceTestPlugin.kt
+++ b/build-logic/performance-testing/src/main/kotlin/gradlebuild/performance/PerformanceTestPlugin.kt
@@ -56,6 +56,7 @@
 import org.gradle.api.Project
 import org.gradle.api.Task
 import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RelativePath
 import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter
 import org.gradle.api.provider.Property
 import org.gradle.api.provider.Provider
@@ -97,8 +98,8 @@
     const val performanceTestResultsJsonName = "perf-results.json"
     const val performanceTestResultsJson = "performance-tests/$performanceTestResultsJsonName"
 
-    // Android Studio Electric Eel (2022.1.1) Beta 2
-    const val androidStudioVersion = "2022.1.1.12"
+    // Android Studio Giraffe (2022.3.1) Canary 8
+    const val androidStudioVersion = "2022.3.1.8"
     val defaultAndroidStudioJvmArgs = listOf("-Xms256m", "-Xmx4096m")
 }
 
@@ -312,7 +313,13 @@
                         else -> zipTree(singleFile)
                     }
                 }
-            )
+            ) {
+                eachFile {
+                    // Remove top folder when unzipping, that way we get rid of Android Studio.app folder that can cause issues on Mac
+                    // where MacOS would kill the Android Studio process right after start, issue: https://github.com/gradle/gradle-profiler/issues/469
+                    relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray())
+                }
+            }
             into("$buildDir/android-studio")
         }
     }
@@ -576,15 +583,7 @@
         val systemProperties = mutableListOf<String>()
         if (autoDownloadAndroidStudio) {
             val androidStudioPath = studioInstallation.studioInstallLocation.asFile.get().absolutePath
-            val macOsAndroidStudioPath = "$androidStudioPath/Android Studio.app"
-            val macOsAndroidStudioPathPreview = "$androidStudioPath/Android Studio Preview.app"
-            val windowsAndLinuxPath = "$androidStudioPath/android-studio"
-            val studioHome = when {
-                isMacOsX && File(macOsAndroidStudioPath).exists() -> macOsAndroidStudioPath
-                isMacOsX -> macOsAndroidStudioPathPreview
-                else -> windowsAndLinuxPath
-            }
-            systemProperties.add("-Dstudio.home=$studioHome")
+            systemProperties.add("-Dstudio.home=$androidStudioPath")
         } else {
             if (androidStudioHome.isPresent) {
                 systemProperties.add("-Dstudio.home=${androidStudioHome.get()}")
diff --git a/build-logic/publishing/src/main/kotlin/gradlebuild.publish-public-libraries.gradle.kts b/build-logic/publishing/src/main/kotlin/gradlebuild.publish-public-libraries.gradle.kts
index b5de3f1..2dabea5 100644
--- a/build-logic/publishing/src/main/kotlin/gradlebuild.publish-public-libraries.gradle.kts
+++ b/build-logic/publishing/src/main/kotlin/gradlebuild.publish-public-libraries.gradle.kts
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import org.gradle.external.javadoc.StandardJavadocDocletOptions
 import java.time.Year
 
 plugins {
@@ -74,6 +75,10 @@
     java {
         withJavadocJar()
     }
+    tasks.named<Javadoc>("javadoc") {
+        // See GradleJavadocsPlugin.generateJavadocs() and the javadocsAll task
+        (options as StandardJavadocDocletOptions).tags("apiNote:a:API Note:", "implSpec:a:Implementation Requirements:", "implNote:a:Implementation Note:")
+    }
 }
 
 fun MavenPublication.configureGradleModulePublication() {
diff --git a/build-logic/root-build/src/main/kotlin/gradlebuild.internal.cc-experiment.gradle.kts b/build-logic/root-build/src/main/kotlin/gradlebuild.internal.cc-experiment.gradle.kts
new file mode 100644
index 0000000..ce60269
--- /dev/null
+++ b/build-logic/root-build/src/main/kotlin/gradlebuild.internal.cc-experiment.gradle.kts
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+import org.gradle.api.internal.plugins.DslObject
+
+val unsupportedTasksPredicate: (Task) -> Boolean = { task: Task ->
+    when {
+
+        // Working tasks that would otherwise be matched by filters below
+        task.name in listOf(
+            "publishLocalPublicationToLocalRepository",
+            "publishEmbeddedKotlinPluginMarkerMavenPublicationToTestRepository",
+            "publishKotlinDslBasePluginMarkerMavenPublicationToTestRepository",
+            "publishKotlinDslCompilerSettingsPluginMarkerMavenPublicationToTestRepository",
+            "publishKotlinDslPluginMarkerMavenPublicationToTestRepository",
+            "publishKotlinDslPrecompiledScriptPluginsPluginMarkerMavenPublicationToTestRepository",
+            "publishPluginMavenPublicationToTestRepository",
+            "publishPluginsToTestRepository",
+        ) -> false
+
+        // Core tasks
+        task.name in listOf(
+            "components",
+            "dependantComponents",
+            "model",
+        ) -> true
+        task.name.startsWithAnyOf(
+            "publish",
+            "idea",
+        ) -> true
+        task is GradleBuild -> true
+
+        // gradle/gradle build tasks
+        task.name in listOf(
+            "updateInitPluginTemplateVersionFile",
+            "resolveAllDependencies",
+            "quickCheck",
+        ) -> true
+        task.name.endsWith("Wrapper") -> true
+        task.name in listOf("docs", "stageDocs", "serveDocs") -> true
+        task.name.startsWith("userguide") -> true
+        task.name == "samplesMultiPage" -> true
+        task.typeSimpleName in listOf(
+            "KtsProjectGeneratorTask",
+            "JavaExecProjectGeneratorTask",
+            "JvmProjectGeneratorTask",
+            "NativeProjectGeneratorTask",
+            "MonolithicNativeProjectGeneratorTask",
+            "NativeProjectWithDepsGeneratorTask",
+            "CppMultiProjectGeneratorTask",
+            "BuildBuilderGenerator",
+            "GenerateSamplePageAsciidoc",
+        ) -> true
+
+        // Third parties tasks
+
+        // Publish plugin
+        task.name == "login" -> true
+
+        // Kotlin/JS
+        // https://youtrack.jetbrains.com/issue/KT-50881
+        task.name in listOf("generateExternals") -> true
+
+        // JMH plugin
+        task.name in listOf("jmh", "jmhJar", "jmhReport") -> true
+
+        // Gradle Doctor plugin
+        task.name in listOf(
+            "buildHealth",
+            "projectHealth",
+            "graph", "graphMain",
+            "projectGraphReport",
+            "ripples",
+            "aggregateAdvice",
+        ) -> true
+        task.name.startsWithAnyOf(
+            "abiAnalysis",
+            "advice",
+            "analyzeClassUsage",
+            "analyzeJar",
+            "artifactsReport",
+            "constantUsageDetector",
+            "createVariantFiles",
+            "findDeclaredProcs",
+            "findUnusedProcs",
+            "generalsUsageDetector",
+            "importFinder",
+            "inlineMemberExtractor",
+            "locateDependencies",
+            "misusedDependencies",
+            "reason",
+            "redundantKaptCheck",
+            "redundantPluginAlert",
+            "serviceLoader",
+        ) -> true
+
+        else -> false
+    }
+}
+
+gradle.taskGraph.whenReady {
+    allTasks.filter(unsupportedTasksPredicate).forEach { task ->
+        task.notCompatibleWithConfigurationCache("Task is not compatible with the configuration cache")
+    }
+}
+
+
+fun String.startsWithAnyOf(vararg prefixes: String): Boolean =
+    prefixes.any { prefix -> startsWith(prefix) }
+
+val Task.typeSimpleName: String
+    get() = DslObject(this).declaredType.simpleName
diff --git a/build-logic/root-build/src/main/kotlin/gradlebuild.root-build.gradle.kts b/build-logic/root-build/src/main/kotlin/gradlebuild.root-build.gradle.kts
index 434ac2c1..28b8b2a 100644
--- a/build-logic/root-build/src/main/kotlin/gradlebuild.root-build.gradle.kts
+++ b/build-logic/root-build/src/main/kotlin/gradlebuild.root-build.gradle.kts
@@ -15,6 +15,7 @@
  */
 
 plugins {
+    id("gradlebuild.internal.cc-experiment")
     id("gradlebuild.buildscan") // Reporting: Add more data through custom tags to build scans
     id("gradlebuild.ide") // Local development: Tweak IDEA import
     id("gradlebuild.dependency-analysis") // Auditing dependencies to find unused libraries
diff --git a/gradle.properties b/gradle.properties
index 76f1290..fd2acde 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,11 +1,9 @@
-org.gradle.jvmargs=-Xmx2500m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+org.gradle.jvmargs=-Xmx2500m -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
 org.gradle.parallel=true
 org.gradle.caching=true
 
 org.gradle.unsafe.configuration-cache=true
 
-# Enables Kotlin assignment support, should be removed once we enable assignment by default
-systemProp.org.gradle.unsafe.kotlin.assignment=true
 systemProp.gradle.publish.skip.namespace.check=true
 
 # Kotlin settings
@@ -16,6 +14,9 @@
 # Temporarily force IDEs to produce build scans
 systemProp.org.gradle.internal.ide.scan=true
 
+# Enable persistent `javac` daemons
+systemProp.org.gradle.internal.java.compile.daemon.keepAlive=DAEMON
+
 # If you're experimenting with changes and don't want to update the verification file right away, please change the mode to "lenient" (not "off")
 org.gradle.dependency.verification=strict
 
@@ -25,4 +26,4 @@
 systemProp.gradle.internal.testdistribution.queryResponseTimeout=PT20S
 
 # Default performance baseline
-defaultPerformanceBaselines=8.1-commit-4b3536b593b
+defaultPerformanceBaselines=8.2-commit-c7ab13564eb
diff --git a/gradle/dependency-management/agp-versions.properties b/gradle/dependency-management/agp-versions.properties
index 392587c..e2733fa 100644
--- a/gradle/dependency-management/agp-versions.properties
+++ b/gradle/dependency-management/agp-versions.properties
@@ -1,2 +1,2 @@
 # Generated - Update by running `./gradlew updateAgpVersions`
-latests=7.3.1,7.4.1,8.0.0-beta03,8.1.0-alpha05
+latests=7.3.1,7.4.2,8.0.0,8.1.0-beta01
diff --git a/gradle/dependency-management/kotlin-versions.properties b/gradle/dependency-management/kotlin-versions.properties
new file mode 100644
index 0000000..3e82262
--- /dev/null
+++ b/gradle/dependency-management/kotlin-versions.properties
@@ -0,0 +1,2 @@
+# Generated - Update by running `./gradlew updateKotlinVersions`
+latests=1.6.10,1.6.21,1.7.0,1.7.22,1.8.0,1.8.21
diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys
index 9d8436c..b652edb 100644
--- a/gradle/verification-keyring.keys
+++ b/gradle/verification-keyring.keys
@@ -11,37 +11,31 @@
 7E3Tkm7y6Rbz0DHrSqPYd28Od/3fKogFHHIE/srnAR7MEBf3HfEDJk4xTvH3sQ4+
 4QOn+eyE9PnoNMvB9H4BfFMEsjNX8ZyoRv7zMLsB1aY3DmzzrcMiudx8qGntDB/G
 ufAsZ5MRKH3gY7hW3SvXKOKp52pXfbJ/CSvPABEBAAG0IkFkYW0gR2VudCA8YWRh
-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
-=SgzX
+bS5nZW50QGV2b2NhdHVzLmNvbT65Ag0EWSB/xQEQAM/knv0YAn90VzGohsA+IaKc
+r88kC0jDfQGEfOv8xLEXweMKbe/glFpaeVp16GODmbAQcJFcwWTGq1VcLUhdAUsX
+FGmVtgwnfrZrAT7YZgi0AJa2L2JGhXGH0+x7f940sAcDBsU20McE8i+auZRT7+rp
+c89NuiNG18uwpbq4py8XzaZmNEka2RSSY7y4wtYnMkOINi99e1exyWix0Wf6qAa0
+NTspUwUIopSu6NMFexcgpnTaEsKQj+1WzmIB0S4uOTwiDW7/fffheLIUQ5Ry10EZ
+WCOAuDQBQG7vk5+0biolMxp1Yt4tTxO+cXslBpPbJ1ULJnGYjkHzTGG2ZNZY+Lux
+BYAzfnehZ9rLeVfdwZFu1oA7Vts2ADzIAh45Tgc7n7MGg/xRndoFUCsXR5ItR0R0
+UBoqI/str/m/ZgLmi4Tk1GKC9v/p9WRpGVN3T/yh8EorV4wtN/klZDPwxqFIdI/M
+b3xD5F+r78YK/Jq2b0fDt8ZLun2JomOHiZ01qQnvZ2Gd1gni8jJ8nKf8Qja2QpcH
+crpsKbo5SRzePJNLJErYiQJUQY6plie0+2WZl3fiqiiyi7tQLPYxOK63KkdYx7OX
+63hAnlUlKox53+ojhUulCqDxxNY7h/rLotdy2j2LfUqmzZOOfxseS5knOQxoEVJt
+fQ861UAkpbb8+a2ZWmsJABEBAAGJAjYEGAEIACAWIQQWCnqc9GIhpWsGrWRGGoBP
+Jgn9iQUCWSB/xQIbIAAKCRBGGoBPJgn9idyGD/9dO+obTPJQAD3safexN+7dcC7Z
+shZmMtNi/Qie2OeWGX3MwKylqyIzM73F/zmxIDJOyD4ya6k2DUhB9SDwjAkBUDri
+XHfdCM4SBwTiq4OolASPQs0szST9HEHsNMLv7CHD3c7f0AAih1TOguysIPx0AHJW
+sXJ0+CROWmicLavmNQX0vQgNEMCXHfhYYzfuiS1TzqEcWAPaONjZsupOGXz+9W53
+F4XMNhBVxnN82RyHsyC2+7cHMxwAdnj+D9AxeW/Nx6XQerQFORNVeO6gTbJeR9hS
+iWtcz27T6SNMv2GIeX7QEGdYOeNvRt1ilFB0ILZkOd3h/vaXBriN5K+O9mabzpg0
+rp8xQ9rxev9NPSd8MNslr8W+hewp8d8ulc/wzY8LtmbpApvOrdCqoNBDiKxCHLwa
+YStaFDVEF38xWbSBh9pdYwkKmZ0+biXOVGeHQFXKAn53TY/NL3XgsWNHCynIYPzf
+FNBDFzEYDPBLy1bp7PfvgFILDM0iad21dXwnyTXtZBEgLVeGyhW9j93Rel/UtPBh
+KmIedbje5/rdfg5gujQC7bSUfbqcfj+p8MymGsUg0mttvWVVDhCSOe2GAA4PA7y/
+Ply5JTS3XLsfwVyzilyFyy4hrXsAUjvWhOa6w1PFjLC/wVXj+apPxVugz+gQ5puP
+1iMkdAn6D90Np+vwUA==
+=OBtF
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    806E7D417A281864
@@ -67,11 +61,8 @@
 18mJUCFO156cT7v6EEtps+jhbvMh+cucsxuSi2qPSlOa8ym269WL905KMiuDufyA
 iJb/o/kKVm25NxP/gfvgL97BFXb0oaRIFUMQk4vhWKQ3LB5i3hSwErdfCZjFQkxa
 ccDFcYWnIxCt22TJZpXR3RxirN5gdgiUW81V7MZn7XKRQizc56m2lDrGUNM4e/Ri
-xLQkS3VydCBBbGZyZWQgS2x1ZXZlciA8a2FrQGdvb2dsZS5jb20+iHoEExEIACIF
-Ak/qFp4CGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEIBufUF6KBhkaJgB
-AMGpjgFm3HRGaxD/UWK/HLyw5amng3g3l3KuygquYGKCAP9A2DKiPNe/ArAQhrtW
-sEc6jwtXjkhFkZ+IhbJI5cd4Ew==
-=WgHm
+xLQkS3VydCBBbGZyZWQgS2x1ZXZlciA8a2FrQGdvb2dsZS5jb20+
+=CI7m
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    80C08B1C29100955
@@ -150,10 +141,7 @@
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    825C06C827AF6B66
-uid    C?dric Champeau <cchampeau@apache.org>
 uid    C?dric Champeau <cedric@gradle.com>
-uid    C?dric Champeau <cedric.champeau@gmail.com>
-uid    keybase.io/melix <melix@keybase.io>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -169,140 +157,8 @@
 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
-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
-Sl3dhA3DcWU=
-=Wcl5
+tCRDw6lkcmljIENoYW1wZWF1IDxjZWRyaWNAZ3JhZGxlLmNvbT4=
+=l4/M
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    82D8373A8E0F8D06
@@ -324,63 +180,47 @@
 upcpofIbapypZYJmCs+/U95q+A75UK6XERzAWo31yOwkeXG2pIWWHgVoGI/Bsctq
 ISwuQi0k7LErvsogQq0GxHWqpZl6YMlatbwSBmKkHEniQvvduBeS6um4eP+eIzTK
 j+tYbfaSy8pafDR3RtiQiS+lvGcgu+LyEF87cl0LLf/5O+6IvlweaY1VkQARAQAB
-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
-=vbjz
+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
+BvMJygVjRd/Y0wvspXMrK18=
+=40nT
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    858FC4C4F43856A3
 uid    J. Daniel Kulp <dan@kulp.com>
-uid    J. Daniel Kulp <dkulp@apache.org>
-uid    J. Daniel Kulp <dkulp@talend.com>
-uid    J. Daniel Kulp <dkulp@progress.com>
-uid    J. Daniel Kulp <dan.kulp@sopera.com>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -396,487 +236,8 @@
 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
-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
-iSDk0j7Iz6iUhN7c512aKapUtWq++1aZbTWZNYYqJ4KYzzQqX5PsTM2KDUbI/SLj
-9AeeCI3iB7ocwpe/c2HpIBChqmrfSTc82UOPiniBF93/wMFi+l5Ad/BeevbbeF22
-VfV57mvfdQ396JTHGdFBSPQt050jRjLAW8a6C/xvUiPcDSv3fRud2GsMPvITJeYV
-azEl0iiaSm7RS7T3r0ll8/SRaFTgW5zZSpgi18qdCIHNo7xv4+GqOzPaZzXtmg9W
-eFf5CFztoOeabXW9rWcnHwrhRjNNGtINEaXKWxP+udvToyNPBgH6VQ+EbDQ/WDtr
-M8ejkG+tBrfOkdVritnGXmSy7zdZZcodbNrv0pCsPe6NfZ0D1UPNdCwSLn5QYaKc
-VgQkf9om1NIJsba1oblVgFBhIAPEunTxx7vLlLt9mFDlwi3PXSmDU1xfTm4U0n0d
-bQqgGA9wmXahYmvdvNnPyyk8PoVYK9HUIf9DQwki2se/mQQGXen70YkslEChzH4Y
-ly/w+gvdDZJWWbHfFORVlcaYJb5gqNyabc1dAxkTQ/RL8wQ2lBQ1rleC/frUJBeH
-LmlTqVYPBvUjF62ef2nL0ode24ZEAZr9myfJNNOfW5mL2dKCa5nLc05F7I69gIkC
-fgQQAQIAaAUCSwzeZyAcU2FuZGVyIFRlbW1lIDxzYW5kZXJAdGVtbWUubmV0PiIc
-U2FuZGVyIFRlbW1lIDxzY3RlbW1lQGFwYWNoZS5vcmc+HRxTYW5kZXIgVGVtbWUg
-PHNhbmRlckBtZS5jb20+AAoJEJu4Y7D1G7iK9z4P/03ghV81yqcpz2r11KC22bEO
-/6T/j/IWqcP/Mj1OoHys6bK8x33i4TX6YihtLzmBjW9bhib7ATNCgy47Psar0ngM
-OJpeHVvFyTgaakclDOsKSLfGZi7tPSWSISAZTI1ARly8LhVJDgpsgu+2+1QTp/LQ
-CZgFG5Xo7Md5L89yyOZVtsHDtMOJOyRJ633ijjiYxPD9Uax5HUau2MnVMuEtMhCx
-78XWgk7wMckemXTGK7r3XjLZGOJGaaT6hCXbh3ujQjJlblr1hHJnRrQri/zQGhr3
-N+F5u65e41VoPEcLWx3UyLO+g50oifuaiVlRWycCvkA/RSxTCM6Rru+bMXfJAn+C
-wHisgnEIeMYM/3+HhW6n/KqlRuhtgXXPMhvGr17oNcj/to/UoyKoOVzK8O89arYn
-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
-/bn49jBTov5o1l991RjhFhIb
-=OE1z
+tB1KLiBEYW5pZWwgS3VscCA8ZGFuQGt1bHAuY29tPg==
+=eQIu
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    85911F425EC61B51
@@ -897,33 +258,20 @@
 MQhFlGSIODiVcW8folGIjzkyNZbNMWkwnl2QnWp/h2TAwYQJOMqcv2MG9o5pyzpx
 97Iz1ngq1FlM/gJnGnNUydP2tAjT2L2U3MP1uX/EdRChdgPqdolqYhdFfwCr0Fpf
 W527bUZpReHCEiQ29ABSnQ711mO+d9+qM6edRyHUoBWz89IHt8sCunuvNwARAQAB
-tC1PcGVuIFNvdXJjZSBEZXZlbG9wbWVudCA8bWFpbEBtYXJjcGhpbGlwcC5kZT6J
-Ak4EEwEIADgWIQT/biwAGUjF8vOLDMOFkR9CXsYbUQUCWspb0gIbAwULCQgHAgYV
-CAkKCwIEFgIDAQIeAQIXgAAKCRCFkR9CXsYbUQyRD/9xm3BqdpWcRCE5UyB6nbwV
-8TgzMmbOhpFhhcjzobly/pKAbcofKsjhreENJkfBVUo+zAFx21ToC5tbH20wRtIE
-vQVCP6sAIzhYWU1ohafqVFP4+PztNBuYTnS6vGvSwzp0IXLIIoxSxo0IOED9uUS9
-DTxh1n9NnDLDe2pfjrXBblQtLSW3W5ISDoUvcoyO7Hk1OByW6MNsSoLvXIUNeVhB
-ju9TfYxFACJSWBhUxJfgip9Y2GrNBJaYGLZrTAoW1Lh1H1DfLV3wHDClQ1+H+oyx
-IOZULEGYY3MgZTd6Ner2yNAUCB7gVa50NiCZXCS74m+XzMrTEsdWjSMUaOe+dL0I
-9MCrgi4ycUHWIfTKx9gGlIOo3hSDMN+8Nj33XPjLT8kcfoFeUX8jTOvC1HFfTuQJ
-x2t/dKHizdrS3F6A/JQa7v8GNTrZFnEXkwgRTf3ccLoo3gPwzNJeCm2xNjvne1VH
-fvxzwNmq8M05oicEigvEed2VMStMhvT7dSiMAf66rEJHjjaHAoNqbLDEATYrWUP2
-I52txHSSxSJohxVP6Ec6dERnqqYi0mVyzBPo7mmFFBisq74w8RvZXyzvXE3BTiDL
-we1E/Z/AXbtJye9DickQ/G6RFtVLbUHQfzyRS/65JPtlH8rqJr58YWlylGImVLwE
-OsKNQrwLZ0UkfaWV7wqr3rkBDQRK6SpOAQgAw1NaOeHNB7JSMa+9y5zOGIpaPyU+
-UsKk3wAXXrw81AMWgMJTF2LtLk4qLDAPGbVxTL+BuKPqotrMgCBIFGtbkLh00r5I
-gByC018ttPln/iaU5+AStgf6UDEI07eT4DN9VT+fRlKGES42FWAdzykOIj8xcJlk
-qwAnE8VW7unGIQzst1OxK6fMz+NkOt8hTsoGNl2XPQUrwvrbj60AkPtaiZDwN3Hc
-yk6O75KGk/dip11TQfGOM/V4aRxmJsnMqYHJDDSvlZf148UnQrzqi9Y0pNycMltQ
-TCGraz+Umwiy9txs1ogto5MSJAjYQHLQ4HvbOu9RzDRfBqKl+3QELBQg3QARAQAB
-iQEfBBgBAgAJBQJK6SpOAhsMAAoJEA62n3b9FxU4FxgH/0pcL4JXcYEVk1WIEl08
-DMYUPkUhyvzeuLDLZEpxt6F6QGMRN8uNJ83MHrQecB5T+X68g5Owsu7V2TRd6Ebo
-tqrjfbmQhkaZEgiojNmow0b8MuaozCJxKdvD7iEktToZB0s1e7R8YOMrg25nTJG6
-vR7YkduAo2AUOe20nH8RHpDVJ9ogplIpJ3joQVhfZ/rdS7vD7IiC+lAJpFQbsurP
-s0lXHEKIX6pF1URjYTeGHIbUMYAwxcRNXYentKgGZCMCqrADLVovDLI2CfUW3fxP
-7r8gEwZAcycFxZ853jUugpHteuhUAM0h2CAlrBjNk3JTpyXD87WJ/m9JRG6Yt66X
-XdM=
-=fOHi
+tC1PcGVuIFNvdXJjZSBEZXZlbG9wbWVudCA8bWFpbEBtYXJjcGhpbGlwcC5kZT65
+AQ0ESukqTgEIAMNTWjnhzQeyUjGvvcuczhiKWj8lPlLCpN8AF168PNQDFoDCUxdi
+7S5OKiwwDxm1cUy/gbij6qLazIAgSBRrW5C4dNK+SIAcgtNfLbT5Z/4mlOfgErYH
++lAxCNO3k+AzfVU/n0ZShhEuNhVgHc8pDiI/MXCZZKsAJxPFVu7pxiEM7LdTsSun
+zM/jZDrfIU7KBjZdlz0FK8L624+tAJD7WomQ8Ddx3MpOju+ShpP3YqddU0HxjjP1
+eGkcZibJzKmByQw0r5WX9ePFJ0K86ovWNKTcnDJbUEwhq2s/lJsIsvbcbNaILaOT
+EiQI2EBy0OB72zrvUcw0Xwaipft0BCwUIN0AEQEAAYkBHwQYAQIACQUCSukqTgIb
+DAAKCRAOtp92/RcVOBcYB/9KXC+CV3GBFZNViBJdPAzGFD5FIcr83riwy2RKcbeh
+ekBjETfLjSfNzB60HnAeU/l+vIOTsLLu1dk0XehG6Laq4325kIZGmRIIqIzZqMNG
+/DLmqMwicSnbw+4hJLU6GQdLNXu0fGDjK4NuZ0yRur0e2JHbgKNgFDnttJx/ER6Q
+1SfaIKZSKSd46EFYX2f63Uu7w+yIgvpQCaRUG7Lqz7NJVxxCiF+qRdVEY2E3hhyG
+1DGAMMXETV2Hp7SoBmQjAqqwAy1aLwyyNgn1Ft38T+6/IBMGQHMnBcWfOd41LoKR
+7XroVADNIdggJawYzZNyU6clw/O1if5vSURumLeul13T
+=flZu
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    8671A8DF71296252
@@ -943,20 +291,8 @@
 zys+8QoSoLtVeo4UDJa8qUuTUuu5R+d73i9iChWdDsYgTCXlxuDV0eAmVQqjBKbN
 Zpmk401Efz9QORJI0C5kaEnT9mPFltuiYhOjg8I08AbfPoijB1kgzYnKgNxXyUT3
 8vGvziOgS1A3qTGvMwNpkd1vg/n/B3wPBZC124wx/yHl4YM19b+xsvp3SQARAQAB
-tB1KZXNzZSBXaWxzb24gPGplc3NlQHN3YW5rLmNhPokCTgQTAQgAOBYhBKbWyXEI
-uFhfkbFYdIZxqN9xKWJSBQJaEIeeAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheA
-AAoJEIZxqN9xKWJS/JcQAK9cSOTF4IspBb9Bd/Xil9qBwVXdNr3MnKFlKc3qy1Cx
-NapqjITYuYnGbSNIjCicyVrZDtD/ydeNNWKT8gOn0UX0U+Lpcoz7sKoSS2XnoBu7
-3Eh5hpwv44ivW7OuQVpe8D4cJ9Csr4WgeNNxpQzorTx6AMNby46NOAAERKFcI3r5
-9B+RzV3hdtjDcj8VnQ9J07JrFwA+5aARayKlXdlYHAQ7K3cd8NESvVhSvRUSxURB
-pn+wcKmG6bu4af1HJya4WrTdNZb9D7pV1d2zBMq29UyHm0XTv8UHcX2Us0A6HqYD
-8gLA/lubpoNCSffopPD/RANnAXhvsEqKUTL/qzIcdO5FgJyfWp0JGu12TWTj+jtT
-7qByrQ9ZUQwHYl7X3q0jFsfU+munCgIvMza3NrtSKs22vrENqaK+XcHpJXflBnrY
-l/RSa0oB0+picci0Pv5ZjrSNFe5gtu1Zz/K9DbujjodQS+EgZmkOt/ym9y9t4n7f
-Z3CJ79xaAeqUZx/fJrd90B2y+rQp5Wxemd36lTXPE8VY81kt1rkKs4dKR5vQPKME
-Gl7fWdNpVg3rNf0klmhGAx+da2Ilptwhj0T9yoImtixyF3WEebILKng+MbzECtSm
-oC169EdJm3MXKgBJ2C/BStyWp42N67QyoQ/cK+cFI7iX21Vu8NPRnUqVa8AtXKiB
-=AiHX
+tB1KZXNzZSBXaWxzb24gPGplc3NlQHN3YW5rLmNhPg==
+=owuM
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    86FDC7E2A11262CB
@@ -971,27 +307,8 @@
 WHCMAdSa34wsg0ZpffwQmRxeA+ebrf2ydKupGkeZsKjkLlaXNkTVp1ghn5ts/lvg
 KeHv1SJivWKCRmFlbPhBK4+mxSUSOPdoBNAfxA51QzZoPizSk0VbRz3YufYRVLFy
 9vqPSorDmYJhCvn3f6+A38FS/j8VE+8obQ2rABEBAAG0O0dhcnkgRGF2aWQgR3Jl
-Z29yeSAoQ29kZSBzaWduaW5nIGtleSkgPGdncmVnb3J5QGFwYWNoZS5vcmc+iQE4
-BBMBAgAiBQJNpM7sAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCG/cfi
-oRJiy2vXCACU6jW2+XIWuWBKSmeowBMxlUMJFSq7QjLt+o6B1ZYClbzBLDKcxlnS
-dF6v0jLhp71LozTde+HFI4/qIv6OO4UaGHin8Z1rNU/i5L07HkhLxcrbDoWhaSGD
-buRsWCv9ljm5GCXyK9s6Uj7d4af9mDWaGhNFJJFIs+yqOvTwffoaB7y+2a1YWStZ
-ZXDJ0KiHdY7xQ7bbYR/61MgvJslF1uX+u4jLTNmbU0tCXLtUrLd2VbJiAMFezqy6
-hV/ZxQLZoBPpr3FYhR2SIzNhQ30nj9AVcPgCCuLD49nx6Ad5CpcP8sTwb4aWk9GK
-+Uu3Bfz4jTAvd9Q7TsRMt3NV+7sOGD4viQIzBBABCAAdFiEERb6+7JUKvQXPDvXD
-UKBNDDtlF/IFAls3k2UACgkQUKBNDDtlF/IfnxAAi+bGKL1HHLUZiIzQrIJbEx0M
-lLuVFrBtIePwtztnAEfE+SdWC0p3CKnHxHMMf6LHcXD4LmJLjzzhV5zoh+Wr+b4f
-sOLRejHRisxloo4teEAJdK0XEAFgqQhhTKQ2LRfVY1lOP8334La49GU3dduqKy8T
-1lVkcWrBMriSSKgdx0xC/PzM6dPyHcrOmoRgmcy6YtdLO5VYzZ6XfIOtTa5gY9Qh
-bHloSkwPgp7KGYk+HBl6MR/8c3tA9tDyYSHrir9KcLPLhF8/ifF0I1sC3h3MbPoC
-zm0LitqJqxdSeNrEkRbfsA0ul3WM6F+/4Q1xQ6lIzSisWee5UpaNUHbbxHDdYyef
-HycHM4zBHL/YVBHuJ6/HV/oHqmhzN6nQ8rrDihaCHkHilfd4lC+SPRGBu81EoLjq
-YD8/QpBLY0oQv004D+/AIAYfhkmGCmc3QEr1N7BWqRdfAScTUUzngZQAwtAL0QTr
-xf5Y9bPVzVKDdy3x4NW9UzKceUnufAt8LP4YTjIHE0FKCxB3kw10i3stuJUuQ7pp
-jTMitnPOWJUlsJ/2Co5WcZPDNNQMHH1BzaOpPrhCUtXYiLQ0gGkSEB9H/uwQmLkt
-qkqYABUlaEsB1rFUlrXbvconF7o4/JRZzMx5myT34CWITBMjiTNXqUIfCiiyLDJ+
-XfldDxtq5hIxa64jHvQ=
-=JB44
+Z29yeSAoQ29kZSBzaWduaW5nIGtleSkgPGdncmVnb3J5QGFwYWNoZS5vcmc+
+=XyIL
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    873A8E86B4372146
@@ -1010,39 +327,26 @@
 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
-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
-CsJSQbSV3sUgilcuk2yCRac2AA78eA==
-=L1Eh
+YW15IDxvbGFteUBhcGFjaGUub3JnPrkBjQRbR5CvAQwAwAhp6vUjPlCrTPYB2zMi
+/YlTV4CB25eKLGfUBHBpV/aWvfs8s64jE16/9zJlv/vnKsYmvknNiaRuDlT9JgSY
+n7SlJfEnUms6phc7hwI1om4vPCv3EWeaI4k8l4Mv8l0zh/AtpgsPX2aCFvoXev5I
+rkY8IEf2Woa1hWoLl3a/uWB55KjLNvTXi+Ej3h9Dhj52TnCgRo6G0/1BpEyJeZ7Z
+fHNARccnKoPlCOpNuRxuYi8uV7mAXpRIr7B6gSPObJpUI1sz9Qo8UM6CIASSSjw9
+sS8Vh1UwfqO/YHtQpyPpqiIYwg04sh786G7YhfQ5iQT/X8KJH6FSLb1UV/5uilxb
+W7c71a+DcDV/Bdl7C24/VLMZi3pWzQTVVhn2dvFh9LlUsMogwh8OoCnkFRgB7w5N
+I4f9Ey9bUdMDEznhf2WMJsItEcB3iLccu7QI/seljpomK3H08WggDF3AWtHtHprH
+nAnmOZMBrAPXcW+V3uHysCnBPZ6bqJQ18tZwJneKaGorABEBAAGJAbwEGAEKACYW
+IQQh8JQideoVmlAdN1MohSAIpHu5lQUCW0eQrwIbDAUJA8JnAAAKCRAohSAIpHu5
+lWY+DACn1Xm5rzANFVpITBWlex3JwmvUuDnHIwpEYhlZPYMgYkhGhHSMUhr0ymcR
+dFi/Yq7+9j7tX9t6tkwS5easOVt0PXH9CycpECXBgOvnaKGJkNv3qNl6QNJNu5dZ
+sSKoTEo71ZGb++KetuVd5IQdxWJjq6iUV1HizvBxIaCenfTRCNniY66Vthh2u0yG
+XjJFjqYlAncWWM3UjUPq5G966r+wGvw31lmT5FYdMBWdhx2DZoV0eX0pKVtPiwLm
+cTeq4608wxc6M4nsJ7RsMR2ZGxzDzKrNiDHzj5kIpued1xzBrUgFN/qfasPjWjxL
+tjavP9KGRfEl8yMjSyBy+onTIkMqMK+jTQtq1R/nAlnLz/LYKThKnHkT1/u7fEtI
+xUeu7BpGhHSA6X9syJNaOcw4s7YEZ55ADiKAHxrGfHk4F+P/rGnYqmoe+lZFB3ia
+NBCXhrDTsketrZtF79qzp5GnevGlJ6MeWv4KxT8I5grCUkG0ld7FIIpXLpNsgkWn
+NgAO/Hg=
+=tBx0
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    889B89A539677894
@@ -1066,101 +370,89 @@
 96EfHU5up75lKhKErNbN+09tpiede3ETv423RxnlvIBR2mFONw8i6/osoE+9rPcU
 vE7qFORR6B02Q0l7mpUF7Qs5KJaclqqafr3xgv5eXrnSUPauprfmrXn5MbKZ7+cf
 i2xdrzFQx2srKVW8L2Tco83ROEZwdEl8MD9ABQ3fWhHdSryVOfxyqcuJTQARAQAB
-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
-T81JFrizhvS9t0HoPbDcDhG5iVE=
-=GFav
+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
+uYlR
+=BiJx
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    8C718D3B5072E1F5
@@ -1178,409 +470,8 @@
 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
-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
-=b3L+
+ZWFzZSBFbmdpbmVlcmluZyA8bXlzcWwtYnVpbGRAb3NzLm9yYWNsZS5jb20+
+=BZek
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    8D7F1BEC1E2ECAE7
@@ -1621,15 +512,8 @@
 4vK21DF/BRhyFGaIGwN1aPYRofy3ERsUfwe9WiPXXuYb3gvG++QuiS4V7UJwcAkx
 5TGukoatI4T1PVmZPk2zmeM2pHQRisHAScRt5YJ9bswgBphk1xHoENVQ3BYhzrsu
 a3hFDY5hO+UQiT+eIE38noOuKuSbRalSPelvABEBAAG0I0plc3NlIFdpbHNvbiA8
-andpbHNvbkBzcXVhcmV1cC5jb20+iQE+BBMBAgAoBQJTJ6XgAhsDBQkSzAMABgsJ
-CAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCOPw3nrjVGUbOIB/9Wk4gJMgAszRHR
-TsR8RnObOrihHFcmSOQHzB2re4XITlYBzWsKt/sQUYhLDAHfV1HHhtJQIL9ovnh4
-BMLxGTk7jxZlVJoIu3o2NKHeDLq/IaQfaAIM61WOxApTyLMEeC/LEa0aivpdkdiH
-herPnEmuoZwOQAzejV/FV/X9KTRxJDhpXEQBqoEYUnXn7Q/h1vXHjC1IiKvRzvuR
-vv6hq3B46f8o3V+qJOw7ni1XuRfpvsQoKEukrh0fLYyutL408z95VYQ4VfIX0vax
-R+KERnlqGkFHUB47Nln9yOc5axkOJCJ3vwheZjuEfzzjvEIvTQ+o2P4GnmjojVOr
-klv520DO
-=mI78
+andpbHNvbkBzcXVhcmV1cC5jb20+
+=tkHY
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    8EDF2667D0ECFFAF
@@ -1663,38 +547,26 @@
 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
-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
-=GFyA
+tCxBbmF0b2xpaSBLbWV0aXVrIDxhbmF0b2xpeWttZXR5dWtAZ21haWwuY29tPrkB
+jQRa41EFAQwAquSR3AR/8vClCEfG5TjqL9+eHEIF+MfnheM58ONv+ZW96S6WBfYA
+LTzLNM0BYcIChRwq/EsWWPp2IvYdaVC6miyV3jS2YqWqFLn8lwC8lyaI7wX+ee1J
+Y5b2TOCep9MRzFTOTbh8RpJDaGcjK6g6rGq27C2N+bLIs3Pn8F6uHT5shp+YQksM
+HNWfw16hWnSvI0naO5JVccLZwZThZUqkN4k9KPaaQeYdd+coJBmN730rKq8VxNgo
+NWO70AXa4Qt7ddnSAmQM6iP7H2C+RI8NQ2GNh+HOb21jyI+ZkUDQrFJKLReAbfyA
+DhzedJT6aAyzA60wJwab05lP77CzVn2dOpzCY/hZuM2faz33YEZ03RapYeYhRIUY
+pXT0nR03dc8NbRVtUqYFSKOPab0CI5UmnML82v6Go8N6eRTiSXHGwGEbr3pCLwTF
+UJTYdku0xGA9TCnXTjUsNW7+Cco6bTUmtEZOGomRGBhI0qG9Uj73jWXNZLgLvfFY
+j27Sjb/spVARABEBAAGJAbYEGAEKACAWIQSwIzWqVMzyHlK7+avZxWWqcrov3QUC
+WuNRBQIbDAAKCRDZxWWqcrov3fslDACSodXt2F9phaTM9aArlcNszxMLSb0ixO6u
+fdm9PS8kzlziot3lZrxVcEDEkqM9CUHyDaAf4ZB3geb81NGrYDdrFEwNYVaxK19a
+y3aYxKyfup+r1V32XKxTF348JsboZ72wKig0OQptZnspDq14BkSIJR1SQbOCkJwn
+HUv29Lsro7NxN4QUddYwLoOHlFwO51jlSO1ngsYy5QOfoAszn4kV9zY5eJAUF/np
+CHMTvPACZn/zZUo0ycxAt7f6NE3OnTfmWMaTk9HiAgucaxI9lmk6BN3FOOeUqt2V
+slIMb4k1pX7FRRsvzNuWTJO9S8DPlpg4WjzWtu/cHYCSq6h3mwojlt+h7SA6Ctl1
+uA+umidvPp7ozubbxsGdCCdxGSVg4zX/L02i+5fEzU7kkPUpKBx6hHg/zJnwTVAY
+/g4+Iw6CHwBhw+2/KoMpjQ63VqjwQZ6+VIwdsSCh4wzAJkf+DhvDmH/G9sIawu3J
+cVj1Pza0Iw3gLf/9xZrbnl+HUEcs+F4=
+=R3Qu
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    912D2C0ECCDA55C0
@@ -1718,128 +590,119 @@
 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
-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
-=FDCV
+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
+fjX0Qj6YJdQ=
+=unQB
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    95962C5E716C39AA
@@ -1859,21 +722,8 @@
 qH1PBfwbyIkW9xufqXXIwc6LFBLEbLgXvAeInuArcX0LtT8/pzNPztOQaTNsHB4B
 8w+8ZnQierSbrFUVGdNqCXNZIWOIjuAuW0MvH/0mM+uls/2cBTZ8+Z+qkO9G3AHj
 1RjRtUyOpSfQm0bWTHX8iApCdF0ZKhJe8dy5Iaa7/lMsNO9Geh09MWXwLwARAQAB
-tCVUb255IFJvYmFsaWsgPHRvbnkucm9iYWxpa0BnbWFpbC5jb20+iQJOBBMBCAA4
-FiEEz0s6P1O++aLOLL+4lZYsXnFsOaoFAl5ZgzYCGwMFCwkIBwIGFQoJCAsCBBYC
-AwECHgECF4AACgkQlZYsXnFsOarvJg//RQi2K578yt+aT0BWKH+RnD3lEpxwgVaE
-BQsTdVZ0LzY49ydO698rKWKB6x3DhJzZIgtgaWga5r5X8XJvGsA92iapTr3mXo7d
-xwrKR8AhJw2gaBgRvUxTE1+bcoTndn5laPw8wyO3RhlX89Jw8za764psDQY0uka5
-A5IbiMJZQh+7erJSyo5rlhcBSoyZBolWkDrulbYOXzg8qXEf5vi9dutt53rtSZme
-6sJh5TE+MDLUimcjbHFrK5PNcDcvIpgzpprebGZgQxOWLH+WLdhLcaOa7ZGU5gOz
-ZaeemZyCS23nwtJLY+0GquOcte3U4Us0/K3sAgBKjWXGfs8Exn4pGLNVnSyZVgfI
-WXb/N9RtTQFq49cPOHKP+RPVJ5ZA99310JbwpU32S4rpFBc/9CcSrVTQChJAczZT
-oTa99wBmo9ahwf3SfnU0fF0v8+b+NIy8GNja5inwIPvdHEsVWhckOZvypJLtjsV0
-61P2SxF4OMTOnlbU8iwPLUX1VZrAsFeW/FKBXvUlcd56K8GgR69WVhvxDyA1IFVn
-uUlfrjkUTLB72kKFv1eNwOVakUU5fP1O+8PNG0sb4u1xkyMON9vyrtk7nwtVPXyE
-lqUFRFegV43OSdjTv7NBRShwqVJUCD0iYtcdFR8tjRaN3EkosGUAeZmZgV2oyyca
-BEuiaxOPFU4=
-=WAjy
+tCVUb255IFJvYmFsaWsgPHRvbnkucm9iYWxpa0BnbWFpbC5jb20+
+=ZXI4
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    96B88EACD463B90B
@@ -1894,53 +744,44 @@
 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
-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
-=THLC
+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
+3sBENw==
+=3MkP
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    98FE03A974CE0A0B
@@ -1955,15 +796,8 @@
 9+c100+KaAldVL/6uLeGoQsAIxtMH8GiOPiSjrw+XCQ5mbP6e+oYYBKxEyAgu1XW
 8jP4bF0rz2+1lkIGfWfYHZmMbmSutDxXqOXA9cZomhOayOSe+iczoxXkVXkQzMxq
 bG4ru5CHxPh+RSfpwA+9StLvzLeoFrBUlqW3ABEBAAG0KktvdGxpbiBSZWxlYXNl
-IEtleSA8c3VwcG9ydEBqZXRicmFpbnMuY29tPokBOQQTAQgAIwUCWGVdBQIbAwcL
-CQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEJj+A6l0zgoLPTgH/AkH+uBwgQhS
-xIfYNA/pEt+ni0eGtjClsFtFF89jjQQAAOMsflebvBmGB/ptpfAEp2QYqRiGX8Mz
-3cRYwa8JcwEVWbN8BrXSgtS3P5zlzBJNPSJJFqpXENxs0VOLZUd63ZbDgQ3h1DDt
-lP7VwsIlF/ogq/wql6gC4qdP9c/kA57eqvZeMrflSUCIUlN2XkeMZhGugWd/Juee
-kz8qMRXY/kEKGbojJkmrehLKOsFz/L8vOW9TLskFuYBIb9PHf2hqLFOOQ3z4o/NN
-fgrOGQVZxM4LWIsUMkO1hRiIdk6R0jLEdFJIOS0z7mGqKzODWNOVegPNhjUX1E7E
-viyo727zBJs=
-=txfB
+IEtleSA8c3VwcG9ydEBqZXRicmFpbnMuY29tPg==
+=cgfT
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    995EFBF4A3D20BEB
@@ -1984,20 +818,8 @@
 6lcbQK1lrXflWSSRmtfNKpysVOfaIgT5p9F5zJJFEFGm5J25z8beCD8Pics+OHF4
 xfYB2SlM4xmbow2kr2htAE2RyT5EuUNuokkdtrZONmBGHBqzBPvj1vzncwARAQAB
 tDhLdGxpbnQgKGt0bGludCBzaWduaW5nIGtleSkgPGt0bGludC1hZG1pbkBwaW50
-ZXJlc3QuY29tPokCTgQTAQgAOBYhBK28mH0ae5HbawqqgZle+/Sj0gvrBQJfWpjZ
-AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEJle+/Sj0gvrSf0P/1UHPPd1
-1/gohW7EDBz1S0R7Nvu73PIx6ajhRfPmS5rwQes4EoHSVx5DjLMJb97FbRRuXJCM
-TJd3lbyBXbU/k6cfD1KbyhrqZ5NgWL54O+9c2WtaKcatnbn+YdHVMhF89FwDURCo
-gF8CpH03sVgZzS9X+WHPtdQ90u2SV/5AWgsAFR5kxRRF8OjLd38LORQQ7nDfxB0/
-pmppQym1NzeNfWa4JBhbKbQoucMuum7tL1l8tFfqpzfLi6TBXDPM7lt8zvZYwRs/
-Yup80iPd/3W3vNjmPmWfqkv4p8VuC6dP/Rv/YRer5lz943JmEutPFx0aLopdwhOF
-5ARwHFkoJAgwgQn/S+TrKlwAZcFv0NWiXSUDbBhZRE24/MlpogrxTOCPzbw9oeEP
-0BZK9fSsYaxZo4mQJl+jUzHBOzR+F/36OEip4V9bJqyEKXI36peO3s5SAFriD3iA
-1yx21o5J5MopSzTNu6YFXQoy7qXM9ePhuaBCyYcLWpwB8Knhc+XvqEfv4a7RWXhR
-eNXeVRGlTO2Ktc5jyrvbEwLoBa8NXt2TSGhWZvyyjEhKgG6FlS7m9j4io2PooCiJ
-fFfMM9yUKeCFWW/OAKi5O+IRIHwn2qtjFRlIo/OG1aKHwBnsOpnH3n7GYJRtRIma
-nY2bTfu4DB9ziayHEOXlyabVxqu1AZ0oMvTI
-=JiNs
+ZXJlc3QuY29tPg==
+=k8F+
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    9A259C7EE636C5ED
@@ -2034,9 +856,6 @@
 
 pub    9AE296FD02E9F65B
 uid    Luc Maisonobe <luc@orekit.org>
-uid    Luc Maisonobe <Luc.Maisonobe@c-s.fr>
-uid    Luc Maisonobe <Luc.Maisonobe@free.fr>
-uid    Luc Maisonobe (CODE SIGNING KEY) <luc@apache.org>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -2052,1581 +871,8 @@
 YN9R59qJ27AUV6J3dAAumzXECXBDcvyLdb7pEIBs5/QtdgE2ivCH8BwFlmcdqe0+
 uKL0jgylMDsfiADVhzagv899MqrmQh8po6Sj78G0gwdfCF9neZgX2czolSFYFSy0
 rmSwUetem6IPwaXpV5r3852P+MqEvI2s86c3ZIyGFO0ltK5KSZq62DANqwARAQAB
-tB5MdWMgTWFpc29ub2JlIDxsdWNAb3Jla2l0Lm9yZz6IRgQQEQIABgUCTP4LUQAK
-CRCFHQqgDMyrSAg6AJ4owIbNEwcyS8NpkJQ43ZqgeYnuQgCfaSegqmhnZRBojF/q
-Nvk6ZjLaw3iIRgQQEQIABgUCTP4LlAAKCRDKfrr0Ov4oztBJAJ9WqxwWwG2PANtg
-w8UP/7c4lqKxMQCgj82S/81CkqLrlY6lJ/SmuuQJeL6IRgQQEQIABgUCUJ7IHAAK
-CRDmFmcpZUIqPUnxAKCaNzPmmnR/bk0AdY8PG3TD7lgdPQCfZP5zZUb2gnwxtMlG
-SC0n8wnk1rqIRgQQEQIABgUCUKFCkgAKCRCL2C5vMLlLXE+tAJ46/5O1Jsx2DnNJ
-k9297WkpvBXjmQCfYgp8ylUK3Hi6MfGxMRwLmv86kbCIRgQQEQIABgUCUMzjtwAK
-CRCRk5iP5w0jV8f3AJ48efTPGJQKEU5qBvXcID9BHSvVFgCgh/mGFpGvTozNcwHm
-uUmTyKyDPKiIRgQQEQIABgUCUNsH/gAKCRD9b4jGIdCnG16DAKDL9UVZdBjQ2exW
-9hds3RLIbGxuSACfeZEpRaB2+SLEcRE6aM92p7HrOXOIRgQQEQoABgUCTMgjfAAK
-CRBz3YwWAVMCNdaQAJ0TzjpW/TlgVmVu+9XPet674rqjTwCZAeKl6o+oA66vAdhy
-pEEzBPuCbkGIRgQTEQIABgUCUJv1cgAKCRAuuUaCiIF0AnnjAJwMD/SQlaUDOwGY
-lTYbqmKu8djGDgCgq4e5OJ7IZ5s03+h65fBAG6NzHN6IdQQQEQIANQUCTOJSoi4c
-SmVhbi1TZWJhc3RpZW4gRGVsZmlubyA8anNkZWxmaW5vQGFwYWNoZS5vcmc+AAoJ
-EKR9Hi3QHg4YMIgAoJbLQLsCq24V/nSxZf3ENLlNpBwqAJ9vmXrld8+75T+5eKld
-jdP2kX1Am4icBBABAgAGBQJQ2wfBAAoJEDGmPZbsFAuBAd4D/2+RodaejTNfYOmE
-h0WtGPNU5vDizEhD6Lc6+BqfBp2bYSo89ShSfRqOTT0a9j6UcwURI4x2GIG3TqUQ
-POVh4IHPTQR37nNNYqCAYPLV9Jnk4hlNt7pMZCo9ImVWGMG3/TQS2MADdsGRxT7+
-P6WQB69SDhVjV/3nmXPPfS5QkgQyiMoEEBECAIoFAkzStEMfHEppbSBKYWdpZWxz
-a2kgPGppbUBhcGFjaGUub3JnPiAcSmltIEphZ2llbHNraSA8amltQGphZ3VORVQu
-Y29tPh8cSmltIEphZ2llbHNraSA8amltQGppbWphZy5jb20+IhxKaW0gSmFnaWVs
-c2tpIDxqaW1qQGNvdmFsZW50Lm5ldD4ACgkQizpgHwjJdeXL7QCgkrVbQo54WUHn
-HKoBrcCapxl22vgAnRNaLFpJyp7xQr7qzm8qejj0aMroiQEcBBABAgAGBQJQmioN
-AAoJEO0JbZPu7Ufa9/sIAMv8M62L5LhDB6ZSt5JE4kSwfL5Bd6pOjh3eCZPVAa9q
-J1yCCn+am8iQfVvJsBNQDq3d34bRiHm5gCj8MY7Jd7fh8Y8Ht0IqVa4xvi0Beqc7
-WriCgNTMNJkHQ1zdRssHOLjITIxlZEjuUol86yRQ0U9x6hQJcAQyPNuhl7KEJQce
-UlePB16iGxbOlrWJEd0hLpdIMuZ8FTZJcVGEhv83pYL/wltulvCDMpXuRXMg3g+O
-QMlAJfa1Mw+3Hd4u1TYLKFDijFX8tQLmcYzX9tyvJ4dwb01m6G08fF5Rr5rSiDOR
-lKD/ZQVXUq2fgzDH+TBLwK/fuJTiRDM3Pp/KsHbPuviJARwEEAECAAYFAlCegfQA
-CgkQRxXcAmQovbpwJQf/cyzxW0DVrpVK7Nq9ARP9tiaIujXSyuYiEz1BXK5rMRdI
-cDTju9WWq0OP7A2bLzxh4uNzxkb1sEeh6H79IXuAG5ea/hKjBKkVAnmCshoczDts
-7vY9fZExcWWtRSgHfEZS5Y9GEbUun55/zQrItMRcCWr2rM5RP6lD8WjuiG/XduaF
-2/5VkZx/yOwYiL/b8MYsCxMRFMSmSbQtOhv/dFeMu8lvnKlb/jAc0UoqDHpxmhbr
-i1zSJI7W/IrQHX/yo9rb8fmDBR1axWrwP87s7i8UQhkA0QotAaAVwnn4dYemsWu8
-p9yP74FqgEwE6ztIpLa/4DN68nLOQvBP79vr4+vlZYkBIgQTAQIADAUCUKlt+QWD
-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
-fkFl7IKv2DiEI/qCFSOUfGTFx87HnOLzxcaJNbmozFf313qnQ2LrW2a0II+eqYkC
-fQQQAQIAZwUCTNK+FSEcRGFuIFBvaXJpZXIgPHBvaXJpZXJAYXBhY2hlLm9yZz4g
-HERhbiBQb2lyaWVyIDxwb2lyaWVyQHBvYm94LmNvbT4dHERhbiBQb2lyaWVyIDxk
-YW5AcG9pcmllci51cz4ACgkQnPorAWKylj829g//SPOoBL9zJNx1P9PFD8wBm6lC
-aGJWCs8XirlSCSZ968/LBC1iEnuhH0BPTkddnIkLhJhmWWmski7fSdvDZGvIsW4I
-QvboeBDsVwNegk3+fviUcuz/SF+50um4JHCuo8vNXg4PCs3kSH5asBy00D0Bgyhe
-3BaeaIQ6FEsZfAxgAvrxM0DgZ7PQx9kQF6gqg59H88/brj2OqY1npHVLcVuBtzEV
-bEYuQas0LeAKgPQ/92tAC5ahTyqQr8XdPRfRGgAZmvbHwkMXkhLFSPiLAgZjA1us
-rCB+ul7LweQmeS9K1irVDy+693E9iC+pY2NbRCxiBZOSPYks/xKnbh9967Cr0Hyx
-XvVDxNRfq1YTjNzApm8d99zXhOzDqd13/SevCsEm1LpFMwV09Qcke+13PaVz1ei5
-xmmmz/JZ02QRqsnjPCpCnqme59fH1FXsprF+jq6ziMu03F8tInrKVWA0NK1KwrvI
-xR4dt+o911oLt1p4q0/3iC+Pv0f4eDBoe2NV+AqoQefu+2cnujyP7A8bNwMfmy/7
-oVhiO3f7RgYVAo73DoqEaA03HQny7JyOSqSvYiTJHM4P4PxcYsoP+W/sbwUwg1E5
-6GsXm9dihFeMLzmW8iIjrIEzELg0zExT1kqYte867kb1Wiwp9rBqaIL104QRiuvQ
-A11eBaeOcVZBX9CKROyJApMEEAECAH0FAkzStCg1HEppbSBKYWdpZWxza2kgKFJl
-bGVhc2UgU2lnbmluZyBLZXkpIDxqaW1AYXBhY2hlLm9yZz4gHEppbSBKYWdpZWxz
-a2kgPGppbUBqYWd1TkVULmNvbT4fHEppbSBKYWdpZWxza2kgPGppbUBqaW1qYWcu
-Y29tPgAKCRA06nbmeRSFqDN8D/98M5/avhHg2ZHEwFiSyKVAZ2C2t6cQsaHRZew+
-/2iC9q77ue4n6chUb1vtLj6tpzsuKb0v7XlKpafABJ3D7JU5QjuvpUcdpBjqZiHc
-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
-tCg1HEppbSBKYWdpZWxza2kgKFJlbGVhc2UgU2lnbmluZyBLZXkpIDxqaW1AYXBh
-Y2hlLm9yZz4gHEppbSBKYWdpZWxza2kgPGppbUBqYWd1TkVULmNvbT4fHEppbSBK
-YWdpZWxza2kgPGppbUBqaW1qYWcuY29tPgAKCRA06nbmeRSFqDN8D/98M5/avhHg
-2ZHEwFiSyKVAZ2C2t6cQsaHRZew+/2iC9q77ue4n6chUb1vtLj6tpzsuKb0v7XlK
-pafABJ3D7JU5QjuvpUcdpBjqZiHc+2N2S5eRPTiMIEqcQFcstRquIZM8hgQkRYfg
-VTM5GDqe3/29AHNdsbQMbgnsPMOYUYpL2dAMCrEpFutFW2H0i6aEiHiS7AMls3U8
-d0kBgcdHIP3x/m5PHMy9TuvlWYbTT2gAbgkJUExb16ePT+q72YuY2bgjHK+Zu4Ld
-mIlfBBSfaoMmhu/0G0Oe8nBgI20Ulge27yWklnKTbDE/62ly+fLkPf2JBEMbRanK
-a0Nlxm3wjlLzo+uHFVcR6bfsg0eJdWwwMsZ182l917MCq8iRhWZV8riBiEMuPWiR
-riu6J6uDBbcfwFqRK2l4ap22na00w0ZBe6gpvKOorVYU1VQo3ezt5+OuCiHZdH7I
-WMGinik0uS1uOj4tcZzR7i5Pt7FGulKUctS4Rratl54q2e2dPgMq/fLh5snl/fhV
-6AEBvh7hex3peC0M9hf8dQax5KOPvY483ukBS1sFM7arU0Ubq9Q9kFIuJX1nM67W
-U9hYRXSD5wgo+YuYixGcRM/fjq9bHbCrkQhaHtPd9N9iRiewPe/ICk+Qb0yoyCi1
-H4UgqVYWCU80S/RXZxmByBV7G/DlEPxMy4kCqQQQAQIAkwUCTNLjsSAcU2FuZGVy
-IFRlbW1lIDxzYW5kZXJAdGVtbWUubmV0PiIcU2FuZGVyIFRlbW1lIDxzY3RlbW1l
-QGFwYWNoZS5vcmc+HRxTYW5kZXIgVGVtbWUgPHNhbmRlckBtZS5jb20+KhxTYW5k
-ZXIgVGVtbWUgPHNjdGVtbWVAa2V5c2ludGhlY2xvdWQuY29tPgAKCRCbuGOw9Ru4
-ipqVEACvRCha4TNsYuRnkxb/54c8AKytjKZ3L0EwRqLrJ8iWHqlt3abUpf7tIAdg
-qTmXbTML3TnIgd/Qf7NPrr1zlf2QofCaM0qrK+7tnSdrYxcNb9fGcCg5O5nmfzP1
-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
-j+cNI1ez1ACdGe3PnexvDQzsp1f2uHwIvJb9TPkAn20YQ1xVa65YEGNFTkzh+ZA+
-7aN4iEYEEBECAAYFAlDbB/4ACgkQ/W+IxiHQpxs8dQCg4TV/biipyyvspLyQKNUD
-kgsRK6kAn3t8jS8pNv7jZY3MDhLYnAPk83kgiEYEEBEKAAYFAkzII3wACgkQc92M
-FgFTAjVEogCdEh9nqoy9qoy29WvxrAH2OAsUHV4Aniqb7mAePtJWgLDShY5SkGGw
-5D4+iEYEExECAAYFAlCb9XIACgkQLrlGgoiBdAIGvQCfeiLgmBB07DtW90SWOdv5
-hMNKybEAmwbEB5h1pC8+j0CKOFANHXQZ13Q2iHUEEBECADUFAkziUqIuHEplYW4t
-U2ViYXN0aWVuIERlbGZpbm8gPGpzZGVsZmlub0BhcGFjaGUub3JnPgAKCRCkfR4t
-0B4OGDCIAKCWy0C7AqtuFf50sWX9xDS5TaQcKgCfb5l65XfPu+U/uXipXY3T9pF9
-QJuInAQQAQIABgUCUNsHwQAKCRAxpj2W7BQLgcasA/sFco0T+I5Za1lTk6xH1qJZ
-cvxX7Reaacm+yvw0dWfxS81qhxbb4tx4lvo9lh5TLyb6oq/Mmc0GPdgUy6OWgEOg
-RgYAb5U9LMSpXVquIVoopl8f6EUMu89HvD7LBqg7vEpSKGyRMTshzCE3mgX5qIx4
-QL148DuuDxsFhquScTwB74jKBBARAgCKBQJM0rRDHxxKaW0gSmFnaWVsc2tpIDxq
-aW1AYXBhY2hlLm9yZz4gHEppbSBKYWdpZWxza2kgPGppbUBqYWd1TkVULmNvbT4f
-HEppbSBKYWdpZWxza2kgPGppbUBqaW1qYWcuY29tPiIcSmltIEphZ2llbHNraSA8
-amltakBjb3ZhbGVudC5uZXQ+AAoJEIs6YB8IyXXly+0AoJK1W0KOeFlB5xyqAa3A
-mqcZdtr4AJ0TWixaScqe8UK+6s5vKno49GjK6IkBHAQQAQIABgUCUJoqDQAKCRDt
-CW2T7u1H2rgZB/9TOzFefX3rDiNGyzju2pTWo5cIV7EDJJQshuWvxAdBU33tVfoG
-Hz3UkdbD2L/koV2VKZ91+YerfgU5uLSXz8BEmjHiDux3TN70hzGb7KGC2zkR5rq7
-B7dqVMdKqisw/oBJtKC95HaKC499FEDwish01Hi8G1ae5+ow9YMDq1peq0om//G3
-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
-3xeyFIDKGYX3ocyFh+4av+QJ1vC5Wo5uHyCTdVSOVn8QViazjpe9eBI44VMv967n
-tRP3OCDDxl6IGuDESHzgG2kBXjtqTpZWqAjoo5pSjo0wMSSXgDqyhKDBG2Ic0ASg
-0+/Dg+/TucL1NDniR3ruUL41fjLQ/crN9l5q8m2vzRwJc8mhyajphnCXOx1aGn5V
-AxyleUbk0MeHg4kCoidDDd0G9p8WkpJl94kCXQQQAQIARwUCTNLRrkAcSm9zZXBo
-IEVkd2FyZCBCZXJnbWFyayAoQ09ERSBTSUdOSU5HIEtFWSkgPGJlcmdtYXJrQGFw
-YWNoZS5vcmc+AAoJELDZkbR8QIc3I4oP/ApdAv1QGsCOfRrJIzwlctFRZU7IAwZ8
-5stV3bPFwqv7fjOJbsqZybM0Soji9MI1RGJBbR8yeBGgLhgxCdvrVzWPjyUCmxFK
-U6wkADzY8k1QUjmyrDereuBPLk+gkr/BU2U/TRvdRonvOS4jGe21G9sXB4XKUG9u
-VwmOZJ2WEeRBWBY0j6l/rlYxaiRSzwlfbgUPHkuYpEELT2HcW3nHjQfpIYuS9w+1
-O2DT2ptYPuBPrWlfjlPtmxqcPlMqK8nf4brI/b6utu2MZOEF/Z8NwZ9/76RhhIdV
-Sy3P/5e3LBw+ocUmeeQvPqOBkOTXf7rKFFNz/pjN/E1UoogqPcqxxeRx+7CkaEXu
-iHeq7iiQ1BGzISQt23F+A0wJUk5X2VZOLu60cBrD1nLuTTCYcflatiBz/54lh01+
-ovsBYO1QkPUF/zqFhp6oOWPE0ErBPf+4uyk+HwJNHsOQtTS3ACv486Ny3pB3M6XC
-/hsKHcQgjh727mWouWaW++n9fvPwbN4PDEragPJRIig34Uw+5LwRJWExX5JPjT2K
-jun06f8ymBDgXjvVSxi7Oktd8cGXiXfZFreFo0h9bjvteBEfPe18tS8VBZD08fs4
-UlXitcMXKIs9CwmBbtNODPTMm3g3bH/ZrB6cw6ndPchecik7zQQ07hVw+ZVKMv1p
-eQyJLpiJxkbViQJdBBABAgBHBQJM0tesQBxKb3NlcGggRWR3YXJkIEJlcmdtYXJr
-IChDT0RFIFNJR05JTkcgS0VZKSA8YmVyZ21hcmtAYXBhY2hlLm9yZz4ACgkQsNmR
-tHxAhzeNcg//VHjvrlwovn1icWlC4k3CsmZyGDqiHgT4LrRxZ6z3nlxgKK1Q/NGQ
-d4cJKYLnlNwVbCEa3NAfxCXheV5QmTsBeHzIy/cpLEbshzn/DgxNcBpjnevWr8iZ
-MeeVKYXT6IyrgsXYF8sZbWVZfP7kPUWbZ+qXJgnsF8NHCFtBmJADF511/5h3Hx3x
-mTUJWpA6SFMVak3076jGITLF+jlI05nNwIqL5xHQTsAH2p6385ON5ycrLi8Wobhp
-1zpE09Tt37bQY6IkBkw4KF2CXqSetfdvXWKW7jWuLVjY+ofKMEmebjNJXDzHwfFu
-ke3lMH9MjLtjBWdmzXeaK3FGhrkLC4x7IMLP/w5vkrd9MXv/RQCwbtz92FkEDoQ3
-OMdhLGGVgMMvSZNQb5vwLpnKksd5LTSuTLyDyY1X3TW+wbGgmG7/DZ2IVF/+jhAB
-uP72e23HG1i8EpO+4dIQYUSs4tXfkKGLGGwx2v5KXh4FYXcfFElSzIhP+RFnMvjW
-6xc/YofwJuR6tBVozM5vcz1F87rtUs99v6rYlb4F0cyVtWxB19aEsmgxMscaDGFc
-IDsWl3qyUjcetS+UXF39/0MeZm4Trea+2OAj0ZF1sM4d2SEnnJN3W5B7en5BZeyC
-r9g4hCP6ghUjlHxkxcfOx5zi88XGiTW5qMxX99d6p0Ni61tmtCCPnqmJAn0EEAEC
-AGcFAkzSvhUhHERhbiBQb2lyaWVyIDxwb2lyaWVyQGFwYWNoZS5vcmc+IBxEYW4g
-UG9pcmllciA8cG9pcmllckBwb2JveC5jb20+HRxEYW4gUG9pcmllciA8ZGFuQHBv
-aXJpZXIudXM+AAoJEJz6KwFispY/NvYP/0jzqAS/cyTcdT/TxQ/MAZupQmhiVgrP
-F4q5UgkmfevPywQtYhJ7oR9AT05HXZyJC4SYZllprJIu30nbw2RryLFuCEL26HgQ
-7FcDXoJN/n74lHLs/0hfudLpuCRwrqPLzV4ODwrN5Eh+WrActNA9AYMoXtwWnmiE
-OhRLGXwMYAL68TNA4Gez0MfZEBeoKoOfR/PP2649jqmNZ6R1S3FbgbcxFWxGLkGr
-NC3gCoD0P/drQAuWoU8qkK/F3T0X0RoAGZr2x8JDF5ISxUj4iwIGYwNbrKwgfrpe
-y8HkJnkvStYq1Q8vuvdxPYgvqWNjW0QsYgWTkj2JLP8Sp24ffeuwq9B8sV71Q8TU
-X6tWE4zcwKZvHffc14Tsw6ndd/0nrwrBJtS6RTMFdPUHJHvtdz2lc9XoucZpps/y
-WdNkEarJ4zwqQp6pnufXx9RV7Kaxfo6us4jLtNxfLSJ6ylVgNDStSsK7yMUeHbfq
-PddaC7daeKtP94gvj79H+HgwaHtjVfgKqEHn7vtnJ7o8j+wPGzcDH5sv+6FYYjt3
-+0YGFQKO9w6KhGgNNx0J8uycjkqkr2IkyRzOD+D8XGLKD/lv7G8FMINROehrF5vX
-YoRXjC85lvIiI6yBMxC4NMxMU9ZKmLXvOu5G9VosKfawamiC9dOEEYrr0ANdXgWn
-jnFWQV/QikTsiQKTBBABAgB9BQJM0rQoNRxKaW0gSmFnaWVsc2tpIChSZWxlYXNl
-IFNpZ25pbmcgS2V5KSA8amltQGFwYWNoZS5vcmc+IBxKaW0gSmFnaWVsc2tpIDxq
-aW1AamFndU5FVC5jb20+HxxKaW0gSmFnaWVsc2tpIDxqaW1AamltamFnLmNvbT4A
-CgkQNOp25nkUhagzfA//fDOf2r4R4NmRxMBYksilQGdgtrenELGh0WXsPv9ogvau
-+7nuJ+nIVG9b7S4+rac7Lim9L+15SqWnwASdw+yVOUI7r6VHHaQY6mYh3PtjdkuX
-kT04jCBKnEBXLLUariGTPIYEJEWH4FUzORg6nt/9vQBzXbG0DG4J7DzDmFGKS9nQ
-DAqxKRbrRVth9IumhIh4kuwDJbN1PHdJAYHHRyD98f5uTxzMvU7r5VmG009oAG4J
-CVBMW9enj0/qu9mLmNm4IxyvmbuC3ZiJXwQUn2qDJobv9BtDnvJwYCNtFJYHtu8l
-pJZyk2wxP+tpcvny5D39iQRDG0WpymtDZcZt8I5S86PrhxVXEem37INHiXVsMDLG
-dfNpfdezAqvIkYVmVfK4gYhDLj1oka4ruiergwW3H8BakStpeGqdtp2tNMNGQXuo
-KbyjqK1WFNVUKN3s7efjrgoh2XR+yFjBop4pNLktbjo+LXGc0e4uT7exRrpSlHLU
-uEa2rZeeKtntnT4DKv3y4ebJ5f34VegBAb4e4Xsd6XgtDPYX/HUGseSjj72OPN7p
-AUtbBTO2q1NFG6vUPZBSLiV9ZzOu1lPYWEV0g+cIKPmLmIsRnETP346vWx2wq5EI
-Wh7T3fTfYkYnsD3vyApPkG9MqMgotR+FIKlWFglPNEv0V2cZgcgVexvw5RD8TMuJ
-AqkEEAECAJMFAkzS47EgHFNhbmRlciBUZW1tZSA8c2FuZGVyQHRlbW1lLm5ldD4i
-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
-sz3DClkE/Uh8nAHieaVaQjwGeWW/bz2VBYB3DXYX/iCMnjozFDC0etY6lzFYBRNw
-X9All/tRRPwSTNbpq95M/c/siQCQC+9p0RbNlwzBUTLvRx5rKu58OUxhnuJrbhYo
-XLObzMJApZXlf3i5CE9DV7qixpd/XBOmyFueaqFFKpkLUW8BWxY1LjffRPvynVtA
-Axh6yZhBCRJrnd5xbZgzP56LVeIL2fIO7ET1itjZY2g3/B/Vi6rYRiJ7G9nG8CeM
-gw34ztixPNLNEmnjA1wUTSeAauQ6X7q/s+VKCrjiS+8hjxoqSKhXAUDqdjON7jil
-oPsQR6meYfAVQDqj5KWdB8R6Ez7im76JAhwEEAECAAYFAk9NL+cACgkQcDQTAR4i
-1bgO+g/+JvvosdohVbgP+6UPQpAEuseTAMHQpUlT+7fobW7FDaSJ1d+QiSp3SqKu
-FuFHjgQ1GWVzht1NJgf8gD6E8j/zVfjM3pG1WiUIuukK2FJlAX4AojUQTdbqGhlj
-kfSUF6DOy8JV9vBF/dn/9nPfcqpnl5+e61dCwozQXsHO4XmZnzkK+zB1ATfW6YYt
-aUeGAmrkMHasxj5si+h4gw8BqfdmA88AHWsMo1TM1BSl47rvyBompLzVMgdBtxcr
-HN8J6blq4fPuFV8Q1kqQo+HXVc/MgDUclOmRhCV45AqxzwHmpTBB3Z5wBN+t8hjU
-pMCZPupxOcGl079NPCfDsz3DClkE/Uh8nAHieaVaQjwGeWW/bz2VBYB3DXYX/iCM
-njozFDC0etY6lzFYBRNwX9All/tRRPwSTNbpq95M/c/siQCQC+9p0RbNlwzBUTLv
-Rx5rKu58OUxhnuJrbhYoXLObzMJApZXlf3i5CE9DV7qixpd/XBOmyFueaqFFKpkL
-UW8BWxY1LjffRPvynVtAAxh6yZhBCRJrnd5xbf//////////////////////////
-////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////+JAhwEEAECAAYF
-AlCaQ9wACgkQNiWQp/XP2RUr5hAAhpLsiBfJJN0wWDh0dH3MbXiZ69vWibHd/CNG
-3WZhxxoxnfT5M1P7Xdu+k1vUjBGCTT8mYuHzMUyZRa2qvGIZK1cn/eZzjS1AuV9y
-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
-XQQQAQIARwUCTNLXrEAcSm9zZXBoIEVkd2FyZCBCZXJnbWFyayAoQ09ERSBTSUdO
-SU5HIEtFWSkgPGJlcmdtYXJrQGFwYWNoZS5vcmc+AAoJELDZkbR8QIc3jXIP/1R4
-765cKL59YnFpQuJNwrJmchg6oh4E+C60cWes955cYCitUPzRkHeHCSmC55TcFWwh
-GtzQH8Ql4XleUJk7AXh8yMv3KSxG7Ic5/w4MTXAaY53r1q/ImTHnlSmF0+iMq4LF
-2BfLGW1lWXz+5D1Fm2fqlyYJ7BfDRwhbQZiQAxeddf+Ydx8d8Zk1CVqQOkhTFWpN
-9O+oxiEyxfo5SNOZzcCKi+cR0E7AB9qet/OTjecnKy4vFqG4adc6RNPU7d+20GOi
-JAZMOChdgl6knrX3b11ilu41ri1Y2PqHyjBJnm4zSVw8x8HxbpHt5TB/TIy7YwVn
-Zs13mitxRoa5CwuMeyDCz/8Ob5K3fTF7/0UAsG7c/dhZBA6ENzjHYSxhlYDDL0mT
-UG+b8C6ZypLHeS00rky8g8mNV901vsGxoJhu/w2diFRf/o4QAbj+9nttxxtYvBKT
-vuHSEGFErOLV35ChixhsMdr+Sl4eBWF3HxRJUsyIT/kRZzL41usXP2KH8CbkerQV
-aMzOb3M9RfO67VLPfb+q2JW+BdHMlbVsQdfWhLJoMTLHGgxhXCA7Fpd6slI3HrUv
-lFxd/f9DHmZuE63mvtjgI9GRdbDOHdkhJ5yTd1uQe3p+QWXsgq/YOIQj+oIVI5R8
-ZMXHzsec4vPFxok1uajMV/fXeqdDYutbZrQgj56piQJ9BBABAgBnBQJM0r4VIRxE
-YW4gUG9pcmllciA8cG9pcmllckBhcGFjaGUub3JnPiAcRGFuIFBvaXJpZXIgPHBv
-aXJpZXJAcG9ib3guY29tPh0cRGFuIFBvaXJpZXIgPGRhbkBwb2lyaWVyLnVzPgAK
-CRCc+isBYrKWPzb2D/9I86gEv3Mk3HU/08UPzAGbqUJoYlYKzxeKuVIJJn3rz8sE
-LWISe6EfQE9OR12ciQuEmGZZaaySLt9J28Nka8ixbghC9uh4EOxXA16CTf5++JRy
-7P9IX7nS6bgkcK6jy81eDg8KzeRIflqwHLTQPQGDKF7cFp5ohDoUSxl8DGAC+vEz
-QOBns9DH2RAXqCqDn0fzz9uuPY6pjWekdUtxW4G3MRVsRi5BqzQt4AqA9D/3a0AL
-lqFPKpCvxd09F9EaABma9sfCQxeSEsVI+IsCBmMDW6ysIH66XsvB5CZ5L0rWKtUP
-L7r3cT2IL6ljY1tELGIFk5I9iSz/EqduH33rsKvQfLFe9UPE1F+rVhOM3MCmbx33
-3NeE7MOp3Xf9J68KwSbUukUzBXT1ByR77Xc9pXPV6LnGaabP8lnTZBGqyeM8KkKe
-qZ7n18fUVeymsX6OrrOIy7TcXy0iespVYDQ0rUrCu8jFHh236j3XWgu3WnirT/eI
-L4+/R/h4MGh7Y1X4CqhB5+77Zye6PI/sDxs3Ax+bL/uhWGI7d/tGBhUCjvcOioRo
-DTcdCfLsnI5KpK9iJMkczg/g/Fxiyg/5b+xvBTCDUTnoaxeb12KEV4wvOZbyIiOs
-gTMQuDTMTFPWSpi17zruRvVaLCn2sGpogvXThBGK69ADXV4Fp45xVkFf0IpE7IkC
-kwQQAQIAfQUCTNK0KDUcSmltIEphZ2llbHNraSAoUmVsZWFzZSBTaWduaW5nIEtl
-eSkgPGppbUBhcGFjaGUub3JnPiAcSmltIEphZ2llbHNraSA8amltQGphZ3VORVQu
-Y29tPh8cSmltIEphZ2llbHNraSA8amltQGppbWphZy5jb20+AAoJEDTqduZ5FIWo
-M3wP/3wzn9q+EeDZkcTAWJLIpUBnYLa3pxCxodFl7D7/aIL2rvu57ifpyFRvW+0u
-Pq2nOy4pvS/teUqlp8AEncPslTlCO6+lRx2kGOpmIdz7Y3ZLl5E9OIwgSpxAVyy1
-Gq4hkzyGBCRFh+BVMzkYOp7f/b0Ac12xtAxuCew8w5hRikvZ0AwKsSkW60VbYfSL
-poSIeJLsAyWzdTx3SQGBx0cg/fH+bk8czL1O6+VZhtNPaABuCQlQTFvXp49P6rvZ
-i5jZuCMcr5m7gt2YiV8EFJ9qgyaG7/QbQ57ycGAjbRSWB7bvJaSWcpNsMT/raXL5
-8uQ9/YkEQxtFqcprQ2XGbfCOUvOj64cVVxHpt+yDR4l1bDAyxnXzaX3XswKryJGF
-ZlXyuIGIQy49aJGuK7onq4MFtx/AWpEraXhqnbadrTTDRkF7qCm8o6itVhTVVCjd
-7O3n464KIdl0fshYwaKeKTS5LW46Pi1xnNHuLk+3sUa6UpRy1LhGtq2XnirZ7Z0+
-Ayr98uHmyeX9+FXoAQG+HuF7Hel4LQz2F/x1BrHko4+9jjze6QFLWwUztqtTRRur
-1D2QUi4lfWczrtZT2FhFdIPnCCj5i5iLEZxEz9+Or1sdsKuRCFoe093032JGJ7A9
-78gKT5BvTKjIKLUfhSCpVhYJTzRL9FdnGYHIFXsb8OUQ/EzLiQKpBBABAgCTBQJM
-0uOxIBxTYW5kZXIgVGVtbWUgPHNhbmRlckB0ZW1tZS5uZXQ+IhxTYW5kZXIgVGVt
-bWUgPHNjdGVtbWVAYXBhY2hlLm9yZz4dHFNhbmRlciBUZW1tZSA8c2FuZGVyQG1l
-LmNvbT4qHFNhbmRlciBUZW1tZSA8c2N0ZW1tZUBrZXlzaW50aGVjbG91ZC5jb20+
-AAoJEJu4Y7D1G7iKmpUQAK9EKFrhM2xi5GeTFv/nhzwArK2MpncvQTBGousnyJYe
-qW3dptSl/u0gB2CpOZdtMwvdOciB39B/s0+uvXOV/ZCh8JozSqsr7u2dJ2tjFw1v
-18ZwKDk7meZ/M/VJqUkEOC49FjU36Kx3EhPdN58u/V6kR4b6G08FSTjozwpCNK70
-GFNZz5pY4D3Pe70BFZhShVmHmdk3qA0ECbFs0bSk5cF5utQriqvWiAWsO/6dU9Ni
-aB9aZoQsEc+nKwl0gCgzvNhmYGKz6S7bjfdF/pXIgI1UAI2odtHuMCU/E6OCFZIX
-9zROMJSNADmNSQhzfSlC72NJm2XBgvIKWigDIbHfIxm7To9yxB5WiBo9qG8mFH2k
-fe3Mo896wy0p2Ag369GuAg62c89B5uuV6nO1GVXEyOIEaklrZC5NYb0r0Fh9w+Vn
-dq1Hnd5GEMDDe/s4yJ+5JCTBgNpEv2L8ngabzKFvG7LRag8QHmXm8yR6+CmKM7Eb
-hgweqdb3zk1wqIwloOEOy7dlJCcs/g9EzDSBZ8nNXQWkZXQP8QFhdQwVJGQS/UeF
-zDJdsD5Uo4VqOYhPgBiRKx5JXtNXxFYpns87l5NtS/ycSIXm6hUfXncSIvZ87RyF
-U0OsuHK/1QU7ev7l8FMAIBOA/9jkLV1HmAHSyL3nQK2W5mf3EZxEw1C6R407vyGz
-iQQcBBABCAAGBQJM0vTaAAoJEIqviNbYTkGu67ogAKKfpTQSXR0gA0rzePTtLWnJ
-CL9SUeRtwpDM2q0kHhctxMKGeqTARIOj00ZreW2YIQYpqbKSUZBn9srozWA2gUcJ
-xzViTpKCPiyN5okE+LCgTwFFHJgNVzvz8t0EYYbk5o/6VBMzefgP2i+ErIUfvqOE
-nqk5Tg1q8vpGOXH3r+30T9Md7Jh9JNPQPzV1kfl5B1zgk/U5VLs3VLpjyVaSTs2n
-SO4CCTn4RgH1ABidAdVyvThC2cyZHEIEn+WQRIr2vNADWOwkU0WeBOW5wQkoKDjf
-1GNZD9HY/r0s+l970/ZDDRgx83Dnnvij9D7cykpTNw2OG+AgdCrfC5b8vX36eR9m
-1NOuXx66Q8QHY764yMcq2j24dveNDK/sVEcgEdjM8Kp3Xgj/4uqRkA2KPpg2GD4E
-PWeAP1L5SO84gc8+JfXZaJCO6Z5hvP/j2zASwFYpKso10eRpvVXPX7ssD3PfSZtN
-11QwZOwWRzjvyFwuI5iHQt2eeXEDKBFRyJq2T72ZrBPItqFzRAw7lh6wXYUGiWcS
-4iJrkR+dNZ7K9bNhn1cg5f+NxqUXUTw0oFiLgi/OKoNM9Fe07bbUaNuTFtiSjxbF
-uFhaza74HMZ6gt6qhoyG98WZwz+UB25G4pa1R1RV1drWjAo/U3xKQHvhGvLtrSQp
-aM5TprlorP0ULPGoDe3AzfKrOFBajkH+Ahtttkd79Hh3sce082r2SChICTPOlM5G
-/Zn9Xbo81+1/vMa/kivncBgfZyLcaP5cSFDumx2vEts6Qr7f26g11/fXAiGLNvki
-EuYoLTITo64QNph5LSkzoao/fzYH7IbBv0VMcNxwyChVJtP2H6Qsr044NP+FDaOe
-yflVbhAh6d2ql52mf4kS7RpFcoBd8rHcXBOUxSnjr7qlXaL98pgTzQtsHS1zUBck
-R1KrMH3PHLAtX4QI9yjthEr8OgIqU6GDsgBMEtAOyNEnpeCcZvd1A9jIb90/Ji7b
-shM1+avD67zfnd9dLLS1Eg5QPYCI0Vrp49OcTMHa3ucl6z7gF7grmxBvcDS8mirG
-bYKuFUuwQcHwum4YaD3qFxofNg5/wbK4GI86n70MyK1kainxQ/cfn1+6VWjHyCe9
-tfC4IG69QdidHLdNDQ82MupL2e+Rm98ETwYDcS/za4wv9xOsTOWGO9hmucxj358i
-xXSSyhg9nmANvVUYz8sQjH6xTiDLVEAhX92BczNSdM7lblWZ0n691BOTvHF+TSwM
-rm0QDuxjffMMARfi/Q71eVogh0wAm/U27Ix4gOihR+CCokM/fDRFSdGFb1ENlwV5
-j8Cu4OwcubYoKullw6rJ2dXbd1xYOzBzvtabsVCT4O96QgyBE5DeCwloyGCvrD8=
-=wxsP
+tB5MdWMgTWFpc29ub2JlIDxsdWNAb3Jla2l0Lm9yZz4=
+=aoua
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    9B7D32F2D50582E6
@@ -3726,228 +972,70 @@
 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
-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
-Ss8TRqh/8nnuEA==
-=dvTW
+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
+jYZ42ZKaTNFKSs8TRqh/8nnuEA==
+=+3+Y
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A1B4460D8BA7B9AF
@@ -3962,15 +1050,8 @@
 9MeVKyoYfj+2Szaaz98W097Pqf9+7i3WjB3TKR1pnWlzF6rLWIGBJqcJuTGLWUAB
 hwOMoNiIBAqyGu8E4rT8pP9OueprOeIjyKgOBWV5yctIxd7uu51sGR47TM2wmMmm
 r/Kz8FkMr1efof7Bx4mJuup6wQXonRMVBqpLABEBAAG0M01vY2tpdG8gKGh0dHA6
-Ly9tb2NraXRvLm9yZykgPG1vY2tpdG8ucGxAZ21haWwuY29tPokBOAQTAQIAIgUC
-TjBo4QIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQobRGDYunua/0Ewf/
-QRv2EXl6rlgV9LgXifdgD4ctYsMvhmJV0uaAaTvTcNpbYL0lG8FpCs0KHvBLWkoq
-esOdJYT4q/9msnBAMXS/9ckteDism8BwZR0qi9qzASzzMJEH7cdpr1te17JR0fl7
-8cgbGyw6UGfRSay9c3HyV0rCuQSFPMwQkG5YVpRiN6NSnYqDczS+aSjd6JCKu/4I
-LVgu4jH8oaUcGJEaWLmExSUzrUv9HFZXQJxLzrFT4k6AVVzBQCuEYW9XtgosKwfo
-0GweBXkC7iuO+bfNi84vyRLTkK50h4+rVNkyvNlgqknbeGrpvOvGErYJy24API/5
-TNP+ZLCTl7Y+qQejTFVJ4Q==
-=DDxv
+Ly9tb2NraXRvLm9yZykgPG1vY2tpdG8ucGxAZ21haWwuY29tPg==
+=k8S2
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A2115AE15F6B8B72
@@ -4002,53 +1083,7 @@
 +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
-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
-AVM4hR7ZHvQIvAYrqT0=
-=m5DR
+=2Vle
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A3264AA953A97466
@@ -4063,19 +1098,12 @@
 PiWaVbDaR6UsiHSkj/Pl9vOKds/lpBvO1Ld/dQesFN7oQy0iGRjspD6ZsUcCO+kS
 gZVSd5fbvY3MJ5k0/eLjXzj8RDSWVPru6bKfK1MZ+YO9g8g73fJqUdi3fq+P5weC
 aXXZmp6n1eJk558pST2Xx19bPrfH+h5B95P5ABEBAAG0G1NldGggVGlzdWUgPHNl
-dGhAdGlzdWUubmV0PokBOAQTAQIAIgUCVYnHNwIbAwYLCQgHAwIGFQgCCQoLBBYC
-AwECHgECF4AACgkQoyZKqVOpdGb/jQf/UrTC6SP0qpx4cUGjbXLMycZmGz3jGIsw
-LHf0mgKvUVlfXkE1p65f3jFMi5BbNTCbZsGAf3tmCPcGH8YDHNsyGBW1syn/OzA3
-x2ATac0Rx/N/uqYY4K+ctrBPWbFEPVUMUGXFx1tKAOcwdPAMbp7KqAxDnQV5a0sr
-jYYOFnRLV2vcj91yNpOzahGYmrOD1aCAdgV8AVseGWab9N+wufBUgBEMRhJccGXJ
-xh8BanvuwuOQr7bM3U2V3F/qbXBT47anXRa1wKmDv3QWUjMr1GbqZO6AZe/4yMaF
-AK9e0UHSgkF+OeHVg55s1pMtbwlzXq7KEvvtisfFEUVMhWU92iiafQ==
-=O7gb
+dGhAdGlzdWUubmV0Pg==
+=1udU
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A41F13C999945293
 uid    Thomas Neidhart <thomas.neidhart@gmail.com>
-uid    Thomas Neidhart (CODE SIGNING KEY) <tn@apache.org>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -4091,80 +1119,8 @@
 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
-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
-oxxS
-=m78U
+tCtUaG9tYXMgTmVpZGhhcnQgPHRob21hcy5uZWlkaGFydEBnbWFpbC5jb20+
+=VFcQ
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A50569C7CA7FA1F0
@@ -4182,10 +1138,8 @@
 GBWBA/9rMCXgJelu+gRx2DkqGxI/EiiG8o4NZ+NpF0Xka818nyVFGcf8LjuVotDb
 AYeKnzduIHVSIxjLI114oASI4Pc4X9zqYPt1rnbGtVKGjUvA00xsgigfVgFy8ZzF
 9hxLnTaAdDuASrLtnhkzrCkFHgD4nq+Qsn9yn3+DMZj1p/WnnrQqQXRzdWhpa28g
-WWFtYW5ha2EgKHltbmspIDx5bW5rQGpjcmFmdC5jb20+iGAEExECACAFAk01exkC
-GwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRClBWnHyn+h8PIuAJ94zigSUtPO
-a3/CT4pdA2P9BZKQTACglxJOpOepQJJ7uh2DmQByl+SjDwE=
-=YYAv
+WWFtYW5ha2EgKHltbmspIDx5bW5rQGpjcmFmdC5jb20+
+=cKiy
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A5DEF5A76F94A471
@@ -4220,49 +1174,43 @@
 d433t5O+tGXWL2TjAz+Scsk2Hf4zcuDeQcxELAMnVaVgKuGuEZvibrjsdIvJDGI+
 0BzWIu8ZP8ldBl4SVtzGpEVzLvDUo3mOqBeTkj3rP7xLtFDN/3AFtowbLfL7L2Pg
 SMcTnKK+jfFHRfbHP1Ih3rQ4ilLzhCnY/QIZABEBAAG0K1RvbSBEZW5sZXkgKHNj
-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
-=eh1R
+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
+xDkAGSHVhApHgnFrFys=
+=FTiz
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A7764F502A938C99
@@ -4279,44 +1227,38 @@
 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
-aXNpQGdvb2dsZS5jb20+iQE4BBMBAgAiBQJXO6DEAhsDBgsJCAcDAgYVCAIJCgsE
-FgIDAQIeAQIXgAAKCRCndk9QKpOMmaF/B/oCSNkCzFaGhvWPLdEgudqmuuq6DOJg
-dZtgWQtQv6tqPrnJpd8vReag0V4IzCd1uIEov55HLhLzbBQ4h5j7lZRXdBSQ1mnx
-FQ2ILsVX6ZersmtCJnegUcrgrh6sstLrkk4jRFsqnemlp7BSbk2DN9hEgPPsawWO
-Rnnz5fWJ8hfMujSy8KSYmWCkUnMwbqYqP9jzG75MXz+J8p1bqcaoq6Z+CqHMu9/L
-eKOl138kwpjab/kA3C2EHIAYqdLz+x9GtHFQTnvUQox1YYlq0PvJOgtNKV5sRmOP
-xb+BC6knhZluEOh4p8OTOwNpERtIjdAHseVPot8j6xivMRHQ0dFq36tGuQINBF3E
-p5QBEADHuJVhV5X/6fEu46eqLNQ/XenyvFMpdEYiEDTq4RlmJcgtzzG0mi8bfzGd
-AT6oFH+prcTU4sC9Y+R8UrLM1/JIj2rxkt7rJEXAd1aKDSBEQZWojWlVw2eXnWFD
-aPh8J5c6rOvTpeaguMchnhwnEBkfOak2QzKkUcZ/179/pYZUJN1/mJHNN7p69v8u
-jVwC4LnD3YQfwTbO0UKhL92x9Ww+f8361+g6Y5NcfvcEmu8a2J3zOsKpI2TFZGkP
-vNOTJ7e0XUgIn2UAW9WO8Ud4jIuF4/tUGFCKU+b2NlnWHiIBkkrWlQV3QIrl9+1d
-ixj+m05/4tv19Q0LFCPi0dtUgMGRy0M8IJ8+U0zac/RfYNpg71LTAVdWt3uJyavU
-ft4dnJJnN1eNVnKeHUWipWusFvUDqTUNxNoHso2AqScm9dbCuTJrgqAbxhjPd6cE
-PAIyGYoQP4S6Et25T66RCgioTCwMeQJXQui7LeL85QnQDszrVYfUFZsfwT5hmpv5
-7ry0FWmC2KlJ+uoSd0rJsQ9bppu+s216XSKEsRiCT/pyy/suLPb1sDofk05rwtZs
-0grOB/hzwI+5JZDS9RCI4v0RUOGwdUbyNRx9p5tPnay8rvrvAmZjHnA4B8fLmK2t
-GGm4+laTrcqfwU35Jn7IStmFPfWQVu0cXYU0UkxLBq/5fbRv/wARAQABiQI2BBgB
-CgAgFiEEtKyM3BQa8K5GjRaSHaeEzLXEbdUFAl3Ep5QCGwwACgkQHaeEzLXEbdV/
-rRAArOZ/LVor94GlTgcjArHIRsCToP49rqJIwoterNfNGzVXqzmWj4U2mlAtkvvM
-NLoL9YIeEZqgjyK8Q5GJQ0YGM6heNQedpgb9UknwZc5eo4gGut23/qVVeTdEAIk6
-PzBF952ohQSEn/TeSI18oyqcvxpxyR2eea15kDzErSrmhlB0yr/SAZGSygEcIRJ4
-Pf1iAuBvskh1JSAhpXS+GpK27u1Ph0MrAhvScfaOzAMA9rN+U8yq0Ccr+RmRtZBx
-c9Iz/IvAl8/f922XwN5hQAdnDSiSs9JYB2NwAiIVcnSSBqYiaMh4ZNWggpqab93u
-6epwre1gKGCWkBmOQGGgOFdJJWO1NEQhFg3bmyHTLDiK2FWmgEJGT7D2RECZTX/4
-YFTBY795h2mRr0AGHnHcNYgKKOQ3mOjZrCgADWEw5qzOxa/1PJryQGtE/h/+zFHV
-dZliW+ijbM9QOpZUghbb3yNea14ZF68/0jNX1VxKiFpE6xJ8blI9ZhUNIq3tRlOP
-ujFfYRcuOAwVqrYFs8k8t6f1bGISAG8iGwdO7Ru4tvnk4zAsBG7dTLIELn617oqA
-yp0POZ+D9FF+DobGuI99l8Ybd8PNzWHa0gtihCtfxk861ROxM8SPN+mxx76irhXV
-xUOXiTc2dabd3EWc/qSF4z5UNIupFdIfCmqRNVKNNOSWIHq5AQ0EQpOhARAEAKF+
-H5mBfml5QBcj1ckeyX8ZGrUxWnB+4l8MIJwkIsy8WW/mUCGI2fW366/7A7D/Peoy
-7MKnQ61SR+8n2dyI+LEgdP8iyASAJwvnjuTM+YnwiVkwePt9pIY/JxQFdouLidWs
-e5WVOPuXRa1ozlFqEy8ky7Gw8RD6Bx9Jyhxf/7GbAAMFA/0V/TgI5ATUmveiuJRl
-ytVpQjJh4B7rWSuM05vMXvBwPB2eMW06w07MLoW1+p5Pwm6OzE+3ISzzJOeqbFLL
-OoQ06T5nfX5Cf6C6rhrQr9Bn5Pqh6ovlbrGcHqJ3kzWvSGOTkWERBdAwWwUkdlG9
-a+uabAK8aeWT26SNwD71B0iT14hJBBgRAgAJBQJCk6EBAhsMAAoJEMe/JtC7YXhm
-y98AnRHs7OqylRv6FAvdrO1lHHwN3gznAKCB8rz92M8BU9vWD9WXUWwcNBZl6A==
-=D6xy
+aXNpQGdvb2dsZS5jb20+uQINBF3Ep5QBEADHuJVhV5X/6fEu46eqLNQ/XenyvFMp
+dEYiEDTq4RlmJcgtzzG0mi8bfzGdAT6oFH+prcTU4sC9Y+R8UrLM1/JIj2rxkt7r
+JEXAd1aKDSBEQZWojWlVw2eXnWFDaPh8J5c6rOvTpeaguMchnhwnEBkfOak2QzKk
+UcZ/179/pYZUJN1/mJHNN7p69v8ujVwC4LnD3YQfwTbO0UKhL92x9Ww+f8361+g6
+Y5NcfvcEmu8a2J3zOsKpI2TFZGkPvNOTJ7e0XUgIn2UAW9WO8Ud4jIuF4/tUGFCK
+U+b2NlnWHiIBkkrWlQV3QIrl9+1dixj+m05/4tv19Q0LFCPi0dtUgMGRy0M8IJ8+
+U0zac/RfYNpg71LTAVdWt3uJyavUft4dnJJnN1eNVnKeHUWipWusFvUDqTUNxNoH
+so2AqScm9dbCuTJrgqAbxhjPd6cEPAIyGYoQP4S6Et25T66RCgioTCwMeQJXQui7
+LeL85QnQDszrVYfUFZsfwT5hmpv57ry0FWmC2KlJ+uoSd0rJsQ9bppu+s216XSKE
+sRiCT/pyy/suLPb1sDofk05rwtZs0grOB/hzwI+5JZDS9RCI4v0RUOGwdUbyNRx9
+p5tPnay8rvrvAmZjHnA4B8fLmK2tGGm4+laTrcqfwU35Jn7IStmFPfWQVu0cXYU0
+UkxLBq/5fbRv/wARAQABiQI2BBgBCgAgFiEEtKyM3BQa8K5GjRaSHaeEzLXEbdUF
+Al3Ep5QCGwwACgkQHaeEzLXEbdV/rRAArOZ/LVor94GlTgcjArHIRsCToP49rqJI
+woterNfNGzVXqzmWj4U2mlAtkvvMNLoL9YIeEZqgjyK8Q5GJQ0YGM6heNQedpgb9
+UknwZc5eo4gGut23/qVVeTdEAIk6PzBF952ohQSEn/TeSI18oyqcvxpxyR2eea15
+kDzErSrmhlB0yr/SAZGSygEcIRJ4Pf1iAuBvskh1JSAhpXS+GpK27u1Ph0MrAhvS
+cfaOzAMA9rN+U8yq0Ccr+RmRtZBxc9Iz/IvAl8/f922XwN5hQAdnDSiSs9JYB2Nw
+AiIVcnSSBqYiaMh4ZNWggpqab93u6epwre1gKGCWkBmOQGGgOFdJJWO1NEQhFg3b
+myHTLDiK2FWmgEJGT7D2RECZTX/4YFTBY795h2mRr0AGHnHcNYgKKOQ3mOjZrCgA
+DWEw5qzOxa/1PJryQGtE/h/+zFHVdZliW+ijbM9QOpZUghbb3yNea14ZF68/0jNX
+1VxKiFpE6xJ8blI9ZhUNIq3tRlOPujFfYRcuOAwVqrYFs8k8t6f1bGISAG8iGwdO
+7Ru4tvnk4zAsBG7dTLIELn617oqAyp0POZ+D9FF+DobGuI99l8Ybd8PNzWHa0gti
+hCtfxk861ROxM8SPN+mxx76irhXVxUOXiTc2dabd3EWc/qSF4z5UNIupFdIfCmqR
+NVKNNOSWIHq5AQ0EQpOhARAEAKF+H5mBfml5QBcj1ckeyX8ZGrUxWnB+4l8MIJwk
+Isy8WW/mUCGI2fW366/7A7D/Peoy7MKnQ61SR+8n2dyI+LEgdP8iyASAJwvnjuTM
++YnwiVkwePt9pIY/JxQFdouLidWse5WVOPuXRa1ozlFqEy8ky7Gw8RD6Bx9Jyhxf
+/7GbAAMFA/0V/TgI5ATUmveiuJRlytVpQjJh4B7rWSuM05vMXvBwPB2eMW06w07M
+LoW1+p5Pwm6OzE+3ISzzJOeqbFLLOoQ06T5nfX5Cf6C6rhrQr9Bn5Pqh6ovlbrGc
+HqJ3kzWvSGOTkWERBdAwWwUkdlG9a+uabAK8aeWT26SNwD71B0iT14hJBBgRAgAJ
+BQJCk6EBAhsMAAoJEMe/JtC7YXhmy98AnRHs7OqylRv6FAvdrO1lHHwN3gznAKCB
+8rz92M8BU9vWD9WXUWwcNBZl6A==
+=XgGN
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A8538F26D55EA9A3
@@ -4331,14 +1273,8 @@
 2KQ0YnZKnC8kgb35+yMHOT78mQy9tNzRAyz8f09yjVc0LjfRF3TrkEA4AATFrz1O
 OAL4pXzxhiR07ng5UdIxynAk17qFzKfDFOpqB3hKdJhjCoDZyzx4gTv6ytOj+NsZ
 rWMLOma4hp+E+Uu1eAj6nEf4Hl7X2r09zLvrABEBAAG0H0NlZHJpYyBCZXVzdCA8
-Y2VkcmljQGJldXN0LmNvbT6JATgEEwECACIFAk5oy/QCGwMGCwkIBwMCBhUIAgkK
-CwQWAgMBAh4BAheAAAoJEKhTjybVXqmjCxoH+wSbVe7/+/mba+Bvjm3cIT7uhJX8
-u4yLEsmnuTaNvzFQimw+iCWRgqmx/LQmLhzr7zVtojHmBbP42sYD84LrM3+OxRId
-RlVwxo78t4HieAcauNGc38axrDckPBJfN2B38+2R8JBbgexiTZagW1EhAcT8QI6R
-3efp0HmLvtN70LTCfX4S14gmymTOeTjo/mr3P96azrUUDoMCi5+pgP4C6EW1GABg
-9E2Sy+0n6nLSFVwtBzDDoiz61v9zns9VwbFCEc0zK1o8nXOZorQYN43EM2b1myEs
-Z30PI9vl5DDGgCm24knUKho1y0QjQTl9tYlEuUnHc49bDNCxZsU7iHGi9Q4=
-=vwDW
+Y2VkcmljQGJldXN0LmNvbT4=
+=6V4u
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    A9052B1B6D92E560
@@ -4448,20 +1384,8 @@
 rAezDrFmT4iQVrIQBhQBhh051FiJmF+5M3VHa5eiFiCu64R+UniX+4O7uFtzXsR7
 G61wq3hmA+PqlsM5xp5PIKrPVT+l2SADk6AF5UV9ifEAOjpEsnV3sBaXtQARAQAB
 tDdTaGEgU2hhIENodSAoa3RsaW50IHNpZ25pbmcga2V5KSA8c2hhc2hhQHBpbnRl
-cmVzdC5jb20+iQJOBBMBCAA4FiEEolcCiOEJMiY+gybLqknGM7RzSDIFAly6CfQC
-GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQqknGM7RzSDIxGg//S9L/FZhE
-pceYb8+yyFbfxWHHqlzH4NDNQjHplMyTgImRiV8RfXp3w8iX6KtAh7bcZbvxUV1c
-grosG3gjxaWeZ9lJnP3w5XsDOqQtzZT68dQ8Cpq0BA7f/zKCi26pDxyajXvvXoJI
-IYI/YYBM+ryJoGUBEL1q5BTtbkXDwegPwApde4YtZn9Uwaf4S++vqTlDuMiUggWI
-wU3BU8UaenRQskU6yn9TY+4K1a838JXiNUvTRi7jOK+EScKTk/q/v3fn4WGcP+Nd
-+MBWiWGKkfN8g8GpoL1bWwvUCa4wqLsBBgWBfJbi2/Wc66sDbipeoFbjd0b5rl68
-B42LMb0arjAztC+/FmEHMWXuDMgdkkHaPL/g9XZLueMOR1YTRxB+9fKOK5C7gmk5
-PNapQM3A9tOvmcKbQbqP5TzW12jpGwFAh19mGrwLmdoIIN8n772n210ofRSew3q9
-ZK1mltsDXmAzzA22iJDld4z+qgmo8e2DajDSxt59aiFf0HvUTOlhQvQLIGI/fhzV
-FCbYF6JH6DYe+2o2v497wUPMUO3WT9S8r20j5yyZs/jnmQ6umUUFoEIZDMJ3xN0B
-r1SXk5dE4ZSN7DSf7C8x9/t+w/AIbKkDOu1umZXkqXOkUv6Tw7FkpSxfKjKBU35z
-KATwEBSZvrlG1EaQIl4wytmplQkSj6oombQ=
-=sGen
+cmVzdC5jb20+
+=rJmh
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    AA6917FA45E67F20
@@ -4554,20 +1478,8 @@
 gxGUqujNoKtQb9R8dBX1X9wQMHQmxSbqdJ4pWD621OTYSp3O4bN5CTYj3lZ5V3xL
 wD/79tzASc9Hzz3yhXhcN4RIzWX+ZMKg+wmG7+QGTtuQMwdeofbNzrTDmwARAQAB
 tDNUZXN0Y29udGFpbmVycyBSZWxlYXNlIDxyZWxlYXNlQHRlc3Rjb250YWluZXJz
-Lm9yZz6JAj0EEwEKACcFAlky2NUCGwMFCRLMAwAFCwkIBwMFFQoJCAsFFgIDAQAC
-HgECF4AACgkQrAHZZceRtjch/A//RFys7tlXPlf2tWUA7wgDZkAuO8SqsLMGZZhn
-QB9ZSXR26YLvKNwTsE+sWGwoOt0Yl0gIJZrYoxPk81tn4JiyNRnBf+YFX84h1I/Q
-87Z46b+rrJ9f9O4lJV/AHJ6Dp4aVLePdLE7to2nU02Do2B/+US5IxwR5LmLf4Lsk
-TiHF7Ll2sC0jBr3cEwEcK8yzRKCong0nWi0FkMcd494xQigT9zcakKAxJ08BSVnc
-LqQInmecVbUtx60+O81AtoXORtZnmmk6mZ/zReZs2ZzHM1lntpfa4jbaVFMtewET
-HxfqaLAUbO3hfxhQfmbIFPuxE4CgBRWyX1wrTERXeoumAhV+XcQ9Snv8lHyvw2+V
-qLiz1O8Fx56ojVEBp34BmRW991M/N1WdDTB/+mpjMuBx9JB97bCjfyf5P4Th4XIf
-MWZkM8KjVy6MbJ9GVszfiQnAZQjQuQnvxPXNVDnJAB3jIiM/iJh1sW65x5xC+wKu
-3Z/sl6pGzbF0v3t6DaT0wcInui4z15hq8jrX+lVwDeBBy0YMmkPUcKYdiFZxfMsx
-8aheBCBDUVz1YuDnt1ax+M5twTO+9wEKXsyidL/1JmVqbVVxG2zC0wEgn7RzCTvS
-f6m4qBqPpj1uBUIj1rwqi1t4ebYrBayfYmVrrudSMIU+1Uj4rmNDvCjJmSLmZbDJ
-gvMaPDw=
-=thl/
+Lm9yZz4=
+=+QjI
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    AC107B386692DADD
@@ -4600,15 +1512,8 @@
 IXw6PXFmkg4wRwlqW5FiC90I8gd3ItFCzqx0TR7DuhOFhtvOheWMlApPoMOUY+6u
 C5Ek6kVW5w5xshKafS7p2s0WOsXkOnH76avFtkBtcsPbHkwWTal4xivSMMsCpdPM
 cF6Fr2p5hF4khQ1r5pdmHeYNoXcC+hSnPM9tABEBAAG0H0NlZHJpYyBCZXVzdCA8
-Y2VkcmljQGJldXN0LmNvbT6JAVQEEwEIAD4WIQTcugM4HvbIkJas2YWsXsdJgfnN
-pgUCXRjNxgIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCsXsdJ
-gfnNpjMGCAC9ojPgU3nJjPdbr57iB1ulJuDaMq+5AsFycAvpbBlbHQYVj3ngldzX
-eehl5H2KYYQzUfr7lDoTwk2dz1i64opMBbsNJZFamivwOaAEk1hX4CwZDGbzk0Cf
-T0oE4dR4TsNEbsW8AiLxXa8ZVbK9cGYdcWHYyNKDJeb/afNeCCpZpkx6+qpXJG2B
-QbIO+8J2E03WcaO0ualRdyw7GM/APJrU0o9sA3wwv4dReuJSrV4JlaoNvHvOok43
-Z6IxuWYwPe5Gf+zMXrHDUFOIiNbYLnXMxi1QDIyXsnJ+KNXRRo0icfB8P81AeG1g
-qFuw0Y15YoFvN+sFWtks0wdzZqzxLLp1
-=FOAZ
+Y2VkcmljQGJldXN0LmNvbT4=
+=uaoB
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    AC7A514BC9F9BB70
@@ -4628,21 +1533,8 @@
 Z0sKkSSUocTWRPUp+9Ny8Vy+BfEreqrKdiu2PTqim66OzGU6kcqYDE9Zs67LVV/H
 asqo8vPqnvcXh5N79bbKOlxfcK6hYe1sTudn9wld7JP06SVv9ERrXuTVGx2pcoX9
 vR0nZbnlM5wAWl//eBYDKJ4l78wppwBbvIc0iHLUWtniWDvLYS3hyGROvwARAQAB
-tCVQdW55YXNobG9rYSBCaXN3YWwgPHB1bnlhQGdvb2dsZS5jb20+iQJOBBMBCgA4
-FiEEYA6iArHsaC9KeI5arHpRS8n5u3AFAmHu5IUCGwMFCwkIBwIGFQoJCAsCBBYC
-AwECHgECF4AACgkQrHpRS8n5u3BRbw/+NKcH+7htXmr05wLohrbMDTzXgt//7LbS
-yAIqG1S+wwyq+x7Trqi+YYqKB0EFi5tI0s/iUTnHpQsXUrsT5g6uXN7xyPaJcDfW
-oSA64UT8+3KSJmMJh/DLRiocdjNIuNThRRjUe/pFaDmR+bQD3KNenbfbpoOlO4gg
-HPyxPzS/pABlqGoGiIGYyD6iwWAwfjcG8PlZsEGGycQ9u8c4lN0oxUOpl9Y8Khqt
-nyd9Yvf/H4Hn49nn/Fd7FkB6Pd0KitRPQmhQ1fqfc3CrgVIwPXAmwIHWzPZ2v0W4
-d9aGeiu1lijzdlrp5dof9RLZnZIfHvtvdJzzQ9eY8mH1JMtMxyXNjrSgCYZnoH5n
-07UKZBKF5d9ON0hMSF1E+CrSnzeoR0KsY378RnSL5MqgqiqBfgDzeEQGeyIFy+AW
-BGo2tYBmZT9AbNmVoF4YtKtyKu5drlyrSI8v1SpVpw9ocAC5Dh+Ws7o9SWEakhnN
-yGc+pmp6KGtGDJkNXQfbH5X/Pyikw3bxjjl9prCCqqRvlLtNq0ldtMoM0ea+uGrP
-dyTTFNoRH//3aoc/9bpO1PZtIRWLrchhwh8cvEt/M8LvmH14Fy3pwUPz/yFVUjj5
-rvGtZa8TercsR/F69jGQC984CyXPg7z4H8Ya0Aejqjb+8w+NzZOTEmV3IeisNbX8
-hwxWQ5N63Fg=
-=9udH
+tCVQdW55YXNobG9rYSBCaXN3YWwgPHB1bnlhQGdvb2dsZS5jb20+
+=HVXg
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    AD21629F6AE723D2
@@ -4660,17 +1552,8 @@
 7bwWswQZAK55x22SuSEcLdUfOPr7NOpSLAqDNVydLgbvW0ZF6Gm1tlof830xakMa
 EnF6oQWHDGosqKfIlJHoZOpulGax6Op+/lpfE1oCQzy5eoBQGBWD1dVKL6qDS7Sz
 s9mGhGKOELzMDh0AEQEAAbQlTmllayBIYWFybWFuIDxoYWFybWFuLm5pZWtAZ21h
-aWwuY29tPokB1AQTAQoAPhYhBFKJpvGDZiTL1wi5rK0hYp9q5yPSBQJbK3KiAhsD
-BQkDwmcABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEK0hYp9q5yPS8KQL/04G
-Z/n0nahXKpHUYFsefl5Da2r9T9PNE/kmR4vAsMHD3RfUv4mFv0FATmqGUsxRJ360
-pbLPGacTl9BbxvO0hZxFJg9rRjRmryeUo/AcMr8W6dZYAOfo8zOyo0Wh7FMmuKSf
-4oYCdb0V4utq5RpAwkNNDuR4Sh+m37EwC6/uuZBxvuLVH3lZSxcU2PxrHHdjwZWn
-PFsK/aMb+ukheqGRjs06c1mvrcN7c0ueba7nRyHvODkqkNmG9CVAtlmecwdHtQmP
-dcmvTmF2Mop0kq7vSQlW6+CKf8x6Fcm/yXRWxcmW4EGStR6LRjwu4o8kJbHcQxW2
-yjPwv91czlA4+xuzRiNZlGReQglI9X7nVZJL/MyviqJuCuDSCSMRuBOstIprz3Xy
-dKW48NYZZD+u5BlP/YWQ93akb/7c0A7AsrNRFpb6tZ8nEkFFM7dHa6k/eJZdD8eQ
-fBEeWr9CI0AjhX5wLKZrgOPmoaPkkUHLggXkvHmnjRxZToJR/K8blp1tSl+zwQ==
-=mdvo
+aWwuY29tPg==
+=EHAj
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    AE5A7FB608A0221C
@@ -4697,23 +1580,20 @@
 JKLDKLZ3Lv0t4MTiaSmVsZEVRwYD3x70J7l3mUUwVHAK0QeKg9RczJQRd/i0lKzt
 OAA/d4gZYscWHbZi0dH/KxnqHzSUDkrLuWrYSdvgaln5tS9hG1ge0LFDxf82f1U9
 +ckdxzYsu5FNjgu8GFZLbLshRri0kKPWqTBX/YPubApadVU94i0eXnqjmZMajXTm
-LbQlUm9iZXJ0IFNjaG9sdGUgPHJmc2Nob2x0ZUBhcGFjaGUub3JnPoh6BBMRCAAi
-BQJSk89cAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCuWn+2CKAiHOT5
-AP0bECw/IDKybdLYSd9icYWeMzkBNAsQD00Dvd9bOZOXFQD/TpURVm08mUI6Jexa
-u7eNLxfRfsOlCmbzrzMiBAB8dvS5AQ0EV5a8TAEIAN9uOpE3Ua9J/1WSMMNYGpfe
-EguI/HcMo+JIWZKwCiItISQ/yBEMEPLqmj857P2r5uBv1KT6IaJ8m9tU1mvv7zwt
-LFAQKytUv5mBMBnYuSoAFAnxdiH91M7oEwnmtIsf9g3ps71Xg2Nih3rtbm5ijH5o
-KnqR4TuJrt4EdyTbDKrGKQKq9XOYB248KSQ1JG47AuQ6C525d/BvsKDVGdpwwwR8
-N3235rrK1j/wkW7TUb75VXEUc7e+z/9Eg2ubQ7jEo+RPX45x3j6HcOWGFG9Fe8j4
-wp4zS53Q6lRUIEoJmpsUpNWChGmwoL3bllFRKpubIFwiSrJiPMPVp1pl2Srg8sUA
-EQEAAYkBJQQYAQIADwUCV5a8TAIbDAUJB4TOAAAKCRDJ+6qDqHU5lDqzB/9WbSd0
-Fy90rSoKi5tH9Y6zKdLXB1DasYokLmhWxQ3hhUJmGD04yHAxI7cQ4E93G2rgDxtJ
-mn/xlaYcuiETxAcPwCBpAG1FnI2S8xGI3XntcaQ8QFFZ0fDpcC/H+UTfVb7X2oVG
-cATwFsRETeCJ4lwbgIpbLkLLNeeNIz7Odm8NDwnsx8b4vEpuvvNQdAf1layTxX0P
-4ZcOCm7Jlzhy3W00MtyfrvzgW5tx2sPdEsyI4BhJKApKCgQtHMHzNl2z5YoW6IBh
-dlJ3MJuMknZWqcmdC1gc3tnmvfJKpsbvDetF6f4S23o9f0eu9zUxPL9RR1vezTwL
-TAPTH5nMXOpRcpNm
-=j0XE
+LbQlUm9iZXJ0IFNjaG9sdGUgPHJmc2Nob2x0ZUBhcGFjaGUub3JnPrkBDQRXlrxM
+AQgA3246kTdRr0n/VZIww1gal94SC4j8dwyj4khZkrAKIi0hJD/IEQwQ8uqaPzns
+/avm4G/UpPohonyb21TWa+/vPC0sUBArK1S/mYEwGdi5KgAUCfF2If3UzugTCea0
+ix/2DemzvVeDY2KHeu1ubmKMfmgqepHhO4mu3gR3JNsMqsYpAqr1c5gHbjwpJDUk
+bjsC5DoLnbl38G+woNUZ2nDDBHw3fbfmusrWP/CRbtNRvvlVcRRzt77P/0SDa5tD
+uMSj5E9fjnHePodw5YYUb0V7yPjCnjNLndDqVFQgSgmamxSk1YKEabCgvduWUVEq
+m5sgXCJKsmI8w9WnWmXZKuDyxQARAQABiQElBBgBAgAPBQJXlrxMAhsMBQkHhM4A
+AAoJEMn7qoOodTmUOrMH/1ZtJ3QXL3StKgqLm0f1jrMp0tcHUNqxiiQuaFbFDeGF
+QmYYPTjIcDEjtxDgT3cbauAPG0maf/GVphy6IRPEBw/AIGkAbUWcjZLzEYjdee1x
+pDxAUVnR8OlwL8f5RN9VvtfahUZwBPAWxERN4IniXBuAilsuQss1540jPs52bw0P
+CezHxvi8Sm6+81B0B/WVrJPFfQ/hlw4KbsmXOHLdbTQy3J+u/OBbm3Haw90SzIjg
+GEkoCkoKBC0cwfM2XbPlihbogGF2Uncwm4ySdlapyZ0LWBze2ea98kqmxu8N60Xp
+/hLbej1/R673NTE8v1FHW97NPAtMA9Mfmcxc6lFyk2Y=
+=yw6d
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    B0F3710FA64900E7
@@ -4728,38 +1608,32 @@
 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
-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
-=D4PY
+w4Ax1nYpRkCxYdHOX3YBdayUiP9ZaYH/YHtLABEBAAG0Je+/vWFtb25uIE1jTWFu
+dXMgPGVhbW9ubkBtY21hbnVzLm5ldD65Ag0EXT0hQAEQAN4ZnvGo82lnu/1UtA6Z
+m/O6Pj3z9Nnupovv2j2k3iy8SJI/UUg7uiXdrvMefB8ce8nJ3vp+jmVWW9FG2CoR
+8l6rMkBJRtNDA9TlT/twwRCEuG817Qq6Etw0icik91KZfTPQJLS+4JQJzn3Bc9qe
+DJcRHWmWSq1pAOr5H6CJjlpdGpya61MMvgtb6UlhQh/SXJux5Uc2aCpFErMi8Php
+ijB6AcgpUSIEHGHzbqusuYHvzrTz/r5qUAEsSul9+U9PDImcjnoPvvrvZ+7qtcTm
+bbU2JewRxLlriyc/xCvFykWQlsG/GY7CAa4Pz/qO44zbre4JabO31CueP+6MDtG/
+A+UVdlJNOqZFyEQ8VMJ4L+dEw0gibBEOZusdmSCqwYmE29Ztrt/zoOK0Kfn7md74
+9aLuAArrD6aNYO1Qc44imjpTSwR6BRK7p+sdoThTR0HbL4XEkKvaOG26o1iOWygW
+QEw0YIYpSPnOLNtfkCzZBHs8+0F4DYhkSFrvgLN03HW2YEcCogCYBEN/LqJqYCiH
+/Iy4sJw7FXRk3iq3B67gOY248UkFRmCd4NJ8Ri/Z72gh1WSNoxRmCB/F47nFRSgZ
+pZkpMB6ZMABLH/A15/9PXUPSPDHKlNqkYyCY+2Iaiua4ek3z7IkxIJKeezwO9VMd
+pTR3uRW/4QNwx75FDO/w2cI9ABEBAAGJAjYEGAEIACAWIQSYV8OI19HZ0DEnTNCl
+3vWnb5SkcQUCXT0hQAIbDAAKCRCl3vWnb5SkcTKQD/9C/a0O6YKBq2/plT6DOUqO
+/cAXeaCq03ptehBguVWom2DNNYx0yp+jmdADyIaX8wCNqBm/5LXEqZR4mw21EK8Z
++0oaPfJCPeGPBZGLzfYQAttJJNxBxb3wxjtBEs0bnLfuzgjEFH85HeQbXwyO0M4j
+WxnGP5UU2tCeTgcUUEDRduRJec3rXgPq8BbCAodKVXLP9e1jPQq/aPmpL14x1DSR
+g/KJ9J38qt0vwRWc0MhGGRKnA7HRIriUQz9QDNhLOwvyHxhS//28hHVUafxx8tgC
+D9Ov4oSJRBfFzybhh1K3QQv9xLNuOmW+NqrUfgPcYbOR6iUMm9lgVTt1CHGzhF9L
+kAmRV7x8GdgnyUdPnRtEjAkQZLMwxU4QiqPhf18Ad2X5oQhj7fALz43kRI7rB/X3
+5Rnxkfdg22BE9+XaUApQ4p0vDtWe8NP6Z8gZBxwPZKRJm7hGQgkekb4TmAy+ucFC
+Zp08sTOAL+STvnKxq2ULJVMsXTAklfnPMtfZPTvBBP2SifApsS4p9SyiJQYOX7Uh
+czLD2i6k1T7uPM8FLmkyTUwA3Ie5gq5kjqt3QB6d6MsTXzCaNwzehRdlHE1g+CQi
+z+U91Ih1C8NkzL0tPyo6LcXG+yAgI5gR+8qVztkczfanpRDW8JEhrZOjdu7N3s6l
+ifXu8ZujOwn2A4SYw+02dw==
+=8Jrt
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    B16698A4ADF4D638
@@ -4775,14 +1649,8 @@
 3vENjH7enAjWh0iGRu+GTP/fayZnX0uhmausCCwMMhsr489e63ZOaJrqeC//wWrX
 dtEJjcmvRmJ2hwLmgwMP4zSNKsnLGzP0sh69ABEBAAG0TUNoZWNrZXIgRnJhbWV3
 b3JrIChPZmZpY2lhbCBSZWxlYXNlKSA8Y2hlY2tlci1mcmFtZXdvcmstZGV2QGdv
-b2dsZWdyb3Vwcy5jb20+iQE4BBMBAgAiBQJTNb/QAhsDBgsJCAcDAgYVCAIJCgsE
-FgIDAQIeAQIXgAAKCRCxZpikrfTWOJ4nB/sGNeq4d18M8Cbrfaj+YwQCkmO2ZELb
-VegqcJELSnvvs9EKO4X1c4Snnbk5r9apoCBSXhODYMfkZ6SUI4Op/8y4S4Km35PL
-8revBwkuen1xMr5cRwRVkdZemKtLaKenUyY+ERpZPdRCSEfW7AVsRqRmsHIIz7UJ
-1p2tAiWHMnyhjFOM3Iap/w7liGPsLmqbHReKUkeriRo40H92ASlPcZxmnbKCMtkB
-wP2FuE1waOvjKfihtU1ZGjsP4JxriIcGz7h80kdGfWyFbZMIeZvnTFfB3odp5qNT
-Zf5enUcYf3NO3IsUqege0gy1PWEF6c3RSPX97YzuW90OPKAHKt/QLON6
-=quSF
+b2dsZWdyb3Vwcy5jb20+
+=WPzE
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    B263D126C8A7E813
@@ -4800,87 +1668,80 @@
 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
-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
-TyNLKHc6zhODNA==
-=LLyN
+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
+=GGv6
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    B341DDB020FCB6AB
@@ -4900,22 +1761,20 @@
 Om/Yg0Bi+CiONLgUjiwYe1wNls8zkk3LwYFeKIJ1AjAY3auBRWOI0/IFFzwTkV8J
 YPHa3Dl/kmYp8NMMwA5bgrblggM0Qhnp+k//xpb0FYbmwHMwUrRhVGhlIExlZ2lv
 biBvZiB0aGUgQm91bmN5IENhc3RsZSAoTWF2ZW4gUmVwb3NpdG9yeSBBcnRpZmFj
-dCBTaWduZXIpIDxiY21hdmVuc3luY0Bib3VuY3ljYXN0bGUub3JnPohgBBMRAgAg
-BQJKMGw7AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQs0HdsCD8tqsVVQCf
-fP6hDSk3fnQ9Hls+Bc0syNyo/aAAn1WKdS/Sf2OQiVG6iPUE3F/o5gu5uQENBE5F
-dQoBCACmJUyQy7YfyYw0WhbuoQNMcThLV2St378qz/n007WgspdthNawIHhE676K
-oit6r8Y4BMTvdl5ypaTUqty6z4qiCGUe74zK9B1kx5hTrRBJsITMbIA7f31beLA9
-NRbzK99BxmVdS/mPcEptFSOMiRz8v6/YHAlm01kxyRr6Un++TXnrWcDJpzWfMp3g
-lLUja6FhwZfw55X7I9PHrzIRwPehwNajvw9zURCy2I4aRdJnCIo2P+2YhKr10MRP
-Nyl3XxEjqE2nj6obnqRwpXaugteW7HjkNyjquftt0xXjQz4p2OvALLRHYOgiG1s5
-TgBhkAD0drv1jHurD7KHhAYnA6N1ABEBAAGJAR8EGAECAAkFAk5FdQoCGwwACgkQ
-Shusfx47ibOWSgf/ctafMLfHTM2131ty2l5wq7lyBl5pmpAtu6vtsCafm7DPNuSs
-BqDYn3iE2hmZ0xAzAJeOQsFdRQQuwvchQ1UWg5BiqrZ+XzdjQDyfFqYN7Iu5nqmc
-paiYGQ8Nb3pWQOMLLZCYJWK6Ekteq1bN30jxU9uqPu81xP/sZc+OMPmNzONLExgH
-L8HnnraRFJXLLOE4F3Soy7sulocM3Ji8ovftWk9b72uYPbY2ewWpSphUAPbxctF/
-kyfX/MkNXSHTcNilm8KYQYQXCBMOm3+O23ikrmjfeC7hNDrD3ZCdfXK05zMjaRyX
-KeaEq+yvVCrjjouRL1WlfQnrZ1qfCaOv5f16hg==
-=XpYf
+dCBTaWduZXIpIDxiY21hdmVuc3luY0Bib3VuY3ljYXN0bGUub3JnPrkBDQRORXUK
+AQgApiVMkMu2H8mMNFoW7qEDTHE4S1dkrd+/Ks/59NO1oLKXbYTWsCB4ROu+iqIr
+eq/GOATE73ZecqWk1Krcus+KoghlHu+MyvQdZMeYU60QSbCEzGyAO399W3iwPTUW
+8yvfQcZlXUv5j3BKbRUjjIkc/L+v2BwJZtNZMcka+lJ/vk1561nAyac1nzKd4JS1
+I2uhYcGX8OeV+yPTx68yEcD3ocDWo78Pc1EQstiOGkXSZwiKNj/tmISq9dDETzcp
+d18RI6hNp4+qG56kcKV2roLXlux45Dco6rn7bdMV40M+KdjrwCy0R2DoIhtbOU4A
+YZAA9Ha79Yx7qw+yh4QGJwOjdQARAQABiQEfBBgBAgAJBQJORXUKAhsMAAoJEEob
+rH8eO4mzlkoH/3LWnzC3x0zNtd9bctpecKu5cgZeaZqQLbur7bAmn5uwzzbkrAag
+2J94hNoZmdMQMwCXjkLBXUUELsL3IUNVFoOQYqq2fl83Y0A8nxamDeyLuZ6pnKWo
+mBkPDW96VkDjCy2QmCViuhJLXqtWzd9I8VPbqj7vNcT/7GXPjjD5jczjSxMYBy/B
+5562kRSVyyzhOBd0qMu7LpaHDNyYvKL37VpPW+9rmD22NnsFqUqYVAD28XLRf5Mn
+1/zJDV0h03DYpZvCmEGEFwgTDpt/jtt4pK5o33gu4TQ6w92QnX1ytOczI2kclynm
+hKvsr1Qq446LkS9VpX0J62danwmjr+X9eoY=
+=OU6p
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    B57BD58EF6D0A713
@@ -4933,17 +1792,8 @@
 IozAd33hLvlCnH8miXRnGPyNT5T481qbi09wOnQEUYrXBe1zOvBAEVxwSLcNcxe5
 /2gRDbSNU9NJst994hzofSyxA/3X/CDiOSHDKdAcOglgqfpistQIS68FqDGo4eIS
 WeR3S583Two98+UAEQEAAbQkQWRhbSBDb3p6ZXR0ZSA8YWNvenpldHRlQGdvb2ds
-ZS5jb20+iQHUBBMBCgA+FiEEIrefRWsG9Odbi1edtXvVjvbQpxMFAlsZf3oCGwMF
-CQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQtXvVjvbQpxOekgv/ReHq
-GA+AGdZdQuuBDDVaWqM2YDpYmjlPdm/A5yGKu/3ZaNUyjUOOsx79QHvgrSiavXWg
-FENNbDnmMjzgKk6hS0wfHcWf2f8wPgYyWlKZMhR4y/ksmBEL3mAOPOoUt8Q976hL
-vly/kWtuNJp/DnIDUm+9uMAnnlBQvlpl4a6GBhVOwdHqOXjykwM08zwAHeJqdegz
-34zeYU35SFaXEM717n47rsHUkoF86r9MBhc0mLAoa3AhG0WDMzjtip2SvQ2L/GTA
-t5DVoElciGzNr8rHLndCaGu8q0RXteWqNG6Bes+AT4xWcLWoDJOYku2fV7BYrzup
-mPtzO2W1S7lQOKrehuLKawBEdlEv3cRG/RXIcL1IbufrTa2V6u76RMDca5pdyr8G
-wjv/zz3KWbCCvpDghbwnZm1knFx8f5vCddC9yZjZ6qp3EQdibK8rRUseL3ZK1D7r
-0Ya5qhaukNWjwZkf7WpXzALJtYiGbTHtD5UXExB546ivt9T9bVplWdBAXqh4
-=Vqg1
+ZS5jb20+
+=2BM+
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    B5A9E81B565E89E0
@@ -5016,187 +1866,203 @@
 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
-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
-nhkFO5QMtCsCmQ==
-=yNhA
+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
+=+AJ0
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    BAC30622339994C4
+uid    Chris Povirk <cpovirk@google.com>
+
+sub    FC9BDC25FB378008
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQENBFlMExYBCACmdTDSXPwSJeYbfYvHoDl5C7vx/0+LOTunDGJN38pNQHYQAZnv
+Gyoc9ZmChrhLoim7z4ILqmNo8eegknepQ3dGdUij4NVIhR+m+8irayTbsNHvo3UG
+9y7eM5tTSjyNYkyk5fAVuT7OhzIzMA+qtc3GRVxNYRKnaHajt+pOSqr+uoDtMG3n
+6eAMHCAnhgh5Nd+dCFcNT+syl3zCwolA1wrzGxxOaif+xi5wwXjmF/lAt4PDIuDT
+etA2/AqPM4zAC0BtC0iqVgVypjFV3EAexm/g0LNMiG/M/krzwjPq5gf1DY/57jU0
+02FpKd79HmR7bHdc4e2olEf9NlHxfbPXDDsHABEBAAG0IUNocmlzIFBvdmlyayA8
+Y3Bvdmlya0Bnb29nbGUuY29tPrkBDQRZTBMWAQgA2YylXc0rxousuBBPKHn75163
+wLcPdPm2QR2WMc643fX+p0o2B7wfq4ofzNdFmyPml3xp2laQPhPMgy/MjDOLOwbD
+fg72F/UTZc+gOq9Cvf2nWyi9fc94O9Elj/skU230OymFVzfFZB82QLsl0w9rolGI
+Ud3fKd9tD7CmV/i19oNWfUlcBanQ1IaAy1d+fmLX2EdBaOZE0Ao1At1WfxRzMjxd
+1UzttLiCIOlAy6RLKOyOACjjrqJErb7x/wHAhF7UlG8XfsF2du9OD93OiLX8Ti6T
+BUkPpgTYuUPDN0aej0rE+VGy5jeeeF5WCNusYnZcpEmQiTztwwaHjVUrDovi6wAR
+AQABiQEfBBgBAgAJBQJZTBMWAhsMAAoJELrDBiIzmZTESIcIAJBWIY4eLQGhnvxk
+nPovjxg0zHx9J1K/dgnzciHWfkNYImCsNXlLf951iKeShUjQu6zLcx1+1GDo5Zpg
+645N/oxhx107+ZjfreOIwHJ/1SHcOqriwqpUtGdlsCqAPWh1GiPY2QRB+AQf8fgl
+FWvGq7MAqhbAmHcyFj6RqNglEnJZ9zX8GbcGPW1wwaUZ4hB+BaF54yDeTGvEn1Nc
++pWLpVU9o8ohTmLyXjpL5aDe68yzy8LdfM4i+FjpzCC4m/1SSNnAYNIj3nbAuqdE
+QzLSAVn44AHSomlgzecMR6RZk/fwbzSSiOc3rW1mNFOG70iLz9v4/BJb6dbcSlBl
+0upm05Q=
+=Gf3Y
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    BB2914C1FA0811C3
@@ -5227,15 +2093,8 @@
 6/9BHM0PwelXwrDndQzgncBf/bWmGpkV+qtW0MSqClFJNswXrCaR5+X1Ps+2Eycv
 mNCF/hbuVb6yi+Fm4UAR03sMPgFWJUJ45XRiyICtjyMMGS3yQHvrLtT9ePdQVQy7
 8JcrqyeMYFJ8HaInDnB+kLBHewYiYoYIGrb3ABEBAAG0K0x1a2UgSW5tYW4tU2Vt
-ZXJhdSA8bHVrZS5zZW1lcmF1QGdtYWlsLmNvbT6JAT0EEwEKACcFAlLykGoCGwMF
-CQeGH4AFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQu2/VrZHQGFcW5AgAoAcp
-WxSe7aKffjIARaTv45VD5H/JC/XghDJVdPfNdXHZrSjukYh7/PqoOjpEpBLK4ecH
-akBslWCV18cIvarK6dTq7cy8Uxfx7fRICRK5OBtKYxz72OppUSaEG64/e0R3XdEv
-RRN3mny/6beB5swkmzwJn74pojEowGnerI1sKXSyLnPdIqBIA2x5I65rHP5apzha
-eZMOnkHIAJl4obUcHrGlQgInYyjIG8Vx6j1olk09wcfLOT0FzFBdoGGDaVK+InxQ
-pq1H0CP9sD9WNpsn/vKEBRzOvvDb7pHPWLGB0Zvltq5XxCGLrGuRm0nMfBX6b07B
-RyiWKuC4J92kjOFoPg==
-=T7Ed
+ZXJhdSA8bHVrZS5zZW1lcmF1QGdtYWlsLmNvbT4=
+=5u/2
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    BCF4173966770193
@@ -5264,14 +2123,8 @@
 U3oo0kKfAPFkW7rX4zeJEBgpncK0i4ertd5to/y2hauqPU3VMExqNS0/L2GqKNmb
 Q2Me02pBSBCrLLCUJkm8jZorPNxlQxHpFLHDABEBAAG0T0VyaWMgTGFmb3J0dW5l
 IChLZXkgZm9yIHNpZ25pbmcgUHJvR3VhcmQgYXJ0ZWZhY3RzKSA8ZXJpY0BncmFw
-aGljcy5jb3JuZWxsLmVkdT6JATgEEwECACIFAlFGZXQCGwMGCwkIBwMCBhUIAgkK
-CwQWAgMBAh4BAheAAAoJEL4JbintuNFBUQ0H/1ahtWOeS+EEZqUxxgLE3aiuw6XR
-mcFY/9kRlnBsKpWGGyV0DRkLThXSV5fWwiP0+moef04Miv81txVXR3U5f4MY3U5J
-SDgeB1GK+BvcYz1CXxGcErgaMIDXu/K03OyQNp1lD2kjmDDz8SHdVqpv/aFfxWpL
-uMaogbStXfbDiMjeMg2zRcAzXqu7JlMt3z33uiTUZk9jubAPOBnP5/Yhuc2wcnfu
-cLUUL9xNE6gpl0Noy8qJ2TaVoHLfDEdSgVbLDNpippNQCIEXn8VtlBCG9FBtTUR7
-z8fGU8f+ijNYHMa6jNsDlSo45/RV+alQSSZhx3hPCIgO2klEkawBs0ikEjQ=
-=jPI8
+aGljcy5jb3JuZWxsLmVkdT4=
+=jDk0
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    BE84D764623A3644
@@ -5286,22 +2139,8 @@
 11MO779KSUgcZL2Cqzn4U+0SkTE75w1Srp8/Le8XIIy+0yyS3AbKEARwBgpYJmO0
 MsaWSa4I8PuoO+NK5rp/u6zuXqfMWnoPcgk1Tl1k0KPqvEN+zWVJl1kHD9AClN13
 6oJ+lm25PtKb4aKtHayYs75fgeVCkiCOIuljABEBAAG0K0V2Z2VuaWkgS2xpdWNo
-bmlrb3YgPGV1c3RhcytncGdAZ29vZ2xlLmNvbT6JAT8EEwEIACkFAlim0TMCGwMF
-CQPCZwAHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRC+hNdkYjo2RHAqCACD
-zldj3+w75lypb8P+goTnzf55YSVYqEzxg8T32mRfbzGPGoRSXi5pmdYJwGwNc8uz
-0kBLpYYMrJ4gP3WUv873RcYQ+cjNIySPTVqBjPpVXX3ZPTkYnv60XEMOy83pL3FY
-ID+2RULomJAxNIG1V2O+VRVYlLf0V7UV5v8g2sNJj2UMbEaAeuLsNT/iI1pee6xt
-85zTqvi9Gz3fOdwqDE5r8Vm42jKaypZe1LgmExdC2fsdEBtwEAVJwFg2ecEaLHxM
-i4hgv8KxO/xgM7rv0nQLuiNAi2x8Pnq/PkHBkaGwy41+t/LVIE2UTkXKC1NUv/Qy
-X7iLbcjC7q2YpN7OHCXziQFWBBMBCABAAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwEC
-HgECF4AWIQQ6TYbFbKmTc7bYlT2+hNdkYjo2RAUCXGV3nwUJB4ENbAAKCRC+hNdk
-Yjo2RDBYB/9BNtJaNIbQrh60wU6S7tINpgfGRddC9r1fMChX/T9Tf8tDEdldl+rS
-YntGOoEzElLn2jwPfT1vznn12CfshS0ARUI//M8UDL7VsrF9nsVzXW9JVI90j3Xh
-3ibO0Lzum4M8y86ipxH5TdRXEWt0L/JF1hSGw0BFw/ZAFmx2i6xg/itv/xofNknt
-hxDsTK85qPDns8FeMq5gt50Z/MVUcbUpIwaQibHPMwyU1+5J6xFgn96GvGAHDUO0
-xspCRvckAZZnqjxCEB9LnRLdfr2enIluE4NTiFBFBXwRVQXFb1QgfSM4XWduHx/B
-SEj6hwouIY0l0vFRh6f91INX1fbV4lnA
-=OQ9p
+bmlrb3YgPGV1c3RhcytncGdAZ29vZ2xlLmNvbT4=
+=utgQ
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    BEABCFBEE059E4E5
@@ -5317,14 +2156,8 @@
 2Pauuj9QEyQIqnn07uWAUTzKrWF/WtcAXmLMxs59VuVRC3xnPVxekQTcJeCKas2L
 Aaic1HKp3AfTOT7+Ao2RRg4GpIiFhaJpul0dABEBAAG0RU1hcnRpbiBNb2lzIChL
 ZXkgZm9yIHNpZ25pbmcgbWF2ZW4gYXJ0aWZhY3RzKSA8bWFydGluLm1vaXNAZ21h
-aWwuY29tPokBOAQTAQIAIgUCU6/vxwIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgEC
-F4AACgkQvqvPvuBZ5OWj7wf/STz2Lr91Cjdf8SwJ6oC3wsAruEoQS0KGmZHYk/NE
-jTbK8vKylIyFgCQzmHwr580FkcIeXVX74U/Ek3Y2gLK/jRT1wDG4tijGzHMPnhY1
-3AAj0039KktRySuvOPG+vR3DUjc4Yi/MzZ2Y67IvOE+uvUJPokpG3eDYKK5BmGC9
-Kx06x2okSlwUyHofOoIwQAYvj276twijaC1IkBvxN8FEl6jCL6Zt8dHO4ZNmaahL
-pzykZrnYBsJz0shXH22i3gUXEdB0/eGyWvBoBs6BL8l0gxYbCirJC381cii18gGo
-C1rAnYsBJk5v9lCkhvZdsI+fauqfUPgucbei9ICaIMhzJA==
-=0Urp
+aWwuY29tPg==
+=YWU2
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    BEDE11EAF1164480
@@ -5344,53 +2177,43 @@
 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
-PokB1AQTAQoAPhYhBOOp+VB56EziAffPYL7eEerxFkSABQJb9RBMAhsDBQkDwmcA
-BQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEL7eEerxFkSAmfQL/A/61Wy/2XWE
-EsHGFhBFIGA3Z+MkEG+wtIZSs91Czz11n8288zt4o6As+7LzSnNj73VCw3wPTGFa
-+Tx/jj/459p0AeAxiOWBz6uQYRIOT+5j6za/3SvCrPf725UKpKVy6kPGQTfDg9ab
-Fyk16hj1nr1I/UDBeTjxnzWkGFw8vgSrFG+qVDJyZP4rNFeFNyG7VAl4kSukwRD0
-TAMyQP0HTo6Hxh1Zk6fYfVlqiSwsy1vzWAyLgHIImxQkE6KLJ54cd77Z6ie+WstE
-ZZKia6KHzwtGrYAq2Og5yEQnpMuWd5MCQa5xbLIB5MhcgV2EKVy4ad2ORIjyz+wy
-sFG1GIpRoqw96UcVfbZtfkyb4fmMrCtaCzteACcPmLIXHrNPvv3JLSJQ9j9Dn0+3
-jMA0whzbXcQWdqfF9Khb91U8PNUczuSfTTo8JUPIK7Wf52avk+iG9ArMYHGNn94g
-KcAzu0OCuZAxGCCFGHE+xR/KwtIRCojvWB13Q1aoxSfp28BpyXcSLLkCDQRHAOUe
-EAgApfbKEp2AQnbOBgXO/jHApqu7tdy6mgms5Cnf4Vhb86YrNnmDTa+ztXMNoMW2
-/0UuDPJQEiPZ2lHFL9K4x6hV+oT7yMotaX8AIREZRG7NMq/2DGVSJTnKqBGnundw
-zFVcmFBcJj7qlFk+X+dLjbq+F6ZN1wKKz78++MUE1mcKdULxZqZjYCjhrtMrYEhr
-D78OCokHQQ5g9hsCq5ttHubhthV2mn1rsB51SUZm0Wy6U+rvGLG2B0FoNh5ZSvYw
-zYzk2ybRvmshJTlW2YG2A3hFHhoXTd5/b03f0USnSbRhaZXAhH6fdanroTIX/ufj
-utA9M739JB1bzTD51foTR/cvdwADBQf+LktL6nmah+qqnSOTOB72BsnLSU3cW+tO
-oSycOQQ1LgzkGhZuoYhISsZBWsrk9zhzzeQUnZKyTh4W6RNHZrAI+UUBQ4Abz5pF
-47PUpqjFjNMiYdVATDGjo35KaSnO8JKmLjeW9hnROXJm2+TrNzaiuG7TC5oK0xDc
-8O0cqVbHqOFC5COMLNs3l81jHLcF+HX3YhUX7sG7CSDsg4waWPNfqCalEBrNHe7M
-Yf6B1+d+CfRPzoh5UZf2l5vf2e4nTvgSnzHn70ZwpUuAO2mt0zanVxqzW2rWPf5p
-d5GdXBJx4N67F/7CdSQpHk0r4wAZ2s4WMs4xs4wkyOICz0MuDQUvrIhJBBgRAgAJ
-BQJHAOUeAhsMAAoJECyC2+bHgOKvj3MAn3ENhSnaCnYwC8wKSWt3vdP959KbAJ46
-yLj/2ynv0bPeuQ9uiyo7XzvXVbkCDQRhEx3SARAArMLeru8sLwaYNmSVvrs6c7Wp
-HcGaihzT6Prb7hVx3HvtozMhyclpv1u73lJwD2bfOEjd/z99OWfOXR57qFTI/aQF
-TidREaYOVmBSf7yOv/a7/CoRCDa9ltfz4kGT4m6mPYIqRQu0ZmOqVUmfDujDj4m1
-RMGkXBJG/qiAcyxW/HazMJDqeDOtZeKSL0vUucBQ+YB02+xf9k2EDcvq9+K5WmtS
-ddpncaA8Qe8L7cuKqeT3upsIcLESI73gMxc26TL6k5i7hK/sMpcCkSo4okEwpn9I
-VRkiTPDGX2SAvtx1pUAG8Zx9vtZlLK1ZMtNVkuU5R15LhFQqarK4K/Yy44cN4HUB
-1oXBVw4mxbZdeCWQznDtK0TUtUSERQLN0aTYcHkyPViNHlf8sd4Kp3cpHC1pK+eV
-lFnR1UHxdVPMyxWKmjuBro20msRvfgCY3Hr/45Ch/VLQWnZDeK0qtUwtvw5r86Av
-7bkHakPazdnllCGFT1G3GuDBl9sDLu7UHIVrbkVgSnL/diS9ubEJ8tlNnlwbR5ZR
-A8CgRMqUSgW1R+2PDP6xOe1yr91iKl8F5iA9vTzG+oIRqMJ6pjgIe+5KbxSeFczQ
-dfKvAozxPj5zJW7qJ6Xc0bVc0Lf7DIY/sBSBhH7w9OhTE9nyso3JtHRl90GtPZ3u
-BJhHibry/WjOKHRP1GkAEQEAAYkCPAQYAQgAJhYhBFsTHoJlgs95UQ2qEc0+U58g
-iDLQBQJhEx3SAhsMBQkHhh8rAAoJEM0+U58giDLQpkAQAIdMqGZOn2QjSu9Kbj8N
-zvmUz3EURQHpwQqN4zHN9iSqlIbG12HR2sIc7xGr0BL02EZ79u5CoMkxaJAVNgUz
-aLCPzqwdr4eEWOAmun17rNGVHL5hG3SrNqS2iILmxsp84vzPlkiZZJSsixKRou0v
-ldi89Oj0Q5sWJ4OolD7/uKIANFqcc3z+UImTwGpzkh13a38yfZnqG2mUUQJOn0oa
-QRmfBEnxsXB7wfRQi7XeZNMB0komiUFRrxBJtiyL5xMzZTL5Zh3muKHHFkJzDQVu
-+HoT97myjaDQrBkZEO4Gd6hx0A1JRYsWYl5xebz/kGRO9sKLA/EYgvqGsWRpW4NJ
-9VsI0jYigWuAhiMsmFtYT2UYYqUGEPEwcW83MpwYBZxVOgr2EZOdKFBVWqp2LzWJ
-+sqrjMxPahMahK15b4rUFBGSlZEkey02jiBMdYXsiyxG7dhNDP4eZ7nFDF3LHlR7
-Ax4YgTRt6OpXF9xJRuGjsz2j47en2sGgCPJYUhChU6CfS82fxi9+uTn6Avg4d8lo
-Q8eoGDbqbWfmH3GYfLKZf2SZRNd6P4P+BvqduGqsqad24/MDfGEQueJPmSr4nrw1
-hP0cFVgxLMFgGzKaomvcxWus1YK9aJQfvVgVNEWjeUdw+TcabLoTAqAR9AxadX2f
-xtwU+IdfTOMbuHy9LBzs+Nzp
-=S2pU
+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
+AqAR9AxadX2fxtwU+IdfTOMbuHy9LBzs+Nzp
+=CCaU
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    BF935C771A8474F8
@@ -5411,32 +2234,46 @@
 5FWeX7b7GPytKoeN5UiDcWgLuzwjN/wlN/IntHWDWkvUCEJ/JpN6FG5AUY+RQkvS
 FrFQLTkGTLQaP8GHo+47Weve+CApUrvTr58NXHBYh8FxXG/cpgAOYt3xy+Z4niuh
 kpcriHwO2xwd4RL0pFhCuaxvgZlgtbJsCv3yLZ5czoUgy2i4u91ZaGflhwARAQAB
-tCNOaWNrIEdsb3Jpb3NvIDxnbG9yaW9zb0Bnb29nbGUuY29tPokCOAQTAQIAIgUC
-V8XLqAIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQv5NcdxqEdPjrXxAA
-kdkWrISfTKYotUiQn33WsyliFuxr7eZkYf8zyOUYdwkQVyvakwl6EKTRvu5EmrDc
-ISVSFHor0UtSUvXK/zX+5H8Bt8ph0hjM8ePc0q47l4M9oArHTtZ+S+M+wb5iF1Jn
-Carr5TtWnA/x67FqN6/1UeqOb8HvcMnDQMk5MYINHOcEWBiSK+8u6PxDZK+09BwY
-VaFEQdlm/QuAAccogz+eRdtDoiU/EcZ7XND978DJuPCiyhQ9KNbcbhUo4p9Q3veC
-S2c/KbMHt7Ppt0YtFeP9xbc5WTQAE0V096lT8rnw/yKh5j0q+qxnarGitYH9fhLu
-6/l9RbICvAFGm16wDGXOPKCs0cUR3U2Ew7naPK2shRvAFnIYTiL0N5QI3CfRIrg5
-AepRpmtOaasBSgd0WcvtmGWlgYv1juURS0rI8RdooA1d0ZK9NM7VDv0+Lfy/RQ30
-8bTGThFpKtzyFPL7IEEsWYq49A/X89xtCg66dKo0V/8Cj55cAuWk20NjYSWXfU2z
-ZhoAgz9+kwhBiudCx1iDuazkIv37axOsD7S/M75TUpByQpgPsM1h3JtobiGrpIbA
-lFMUQneEXSETedgEUsEsnmPUgCrsk9rJF1REZ/aC6GQqtuMDxcrIzhfejLWk2hbL
-/i9nN7CeegU9wgwReu3GNPLtI0P2QRYezpEOuk3A2ca5AQ0EWmo33gEIAMB9fJ0T
-VVhqKzqj/gmlVDCT0kvevaGSDB83rwHatG/D2h9dmipoEIWBvD42/PXkYuY42iIO
-8/itvVOxpPZOL+FNRvei/ZbVEno4VGaJKQ646NkeVWyVgXZ8+VkRdZ0n09a+goz/
-e1pogJfL8BVKbU0F6trWXYywnV4+vp2kwwMGNRTXmvNabdY6rAE0TfjCGE6O9T28
-OXy7iHXFX3oTkHjtltUHWlHrLe9JhCDCPoFiSndTPYyDcZD+cEWg27BX8XsuJRvl
-brzA57xD3w25ESHWJyj84z6K52M/Ys7S/PawZ7pflRkbAJ9smeWr9+qg+GLJboc9
-vX4kdTTyQp5jkEkAEQEAAYkBNgQYAQgAIBYhBMcLhE8ALyH20rnIdSLkSsBiK5HD
-BQJaajfeAhsMAAoJECLkSsBiK5HD5coH/ROhc7Z1pjr9mWR8rr07yNEHNuGf3T/Z
-148z+ovLkV7dRobayB64VhtMwZtE+8Kmf0Tltx5zN2GQkqoSPl08dWFRTr6N7If8
-N0OCtw/XZJBHa8eD2FF7SnNWi6IwNfhCdRtbT0LCmaHKaUineldvmM1riI3GNggG
-a4cMSYw/65blR757aWAgAVZEWi9n0LUMKDRhjIaS1zjtxqSrcQ0o1TYsW9FxuCjJ
-+MzQSmtJQKiVRQ03fLJQ1z4j5u8YvmzCEqp2dEdqBuDuR6Wyf/TmgMUY9AeLT9U6
-VZTvAGH69TDqVvdmLsKhtQidSFbes+7ku5tM61P6ggfv5FJQgQk7WEQ=
-=yjvX
+tCNOaWNrIEdsb3Jpb3NvIDxnbG9yaW9zb0Bnb29nbGUuY29tPrkBDQRaajfeAQgA
+wH18nRNVWGorOqP+CaVUMJPSS969oZIMHzevAdq0b8PaH12aKmgQhYG8Pjb89eRi
+5jjaIg7z+K29U7Gk9k4v4U1G96L9ltUSejhUZokpDrjo2R5VbJWBdnz5WRF1nSfT
+1r6CjP97WmiAl8vwFUptTQXq2tZdjLCdXj6+naTDAwY1FNea81pt1jqsATRN+MIY
+To71Pbw5fLuIdcVfehOQeO2W1QdaUest70mEIMI+gWJKd1M9jINxkP5wRaDbsFfx
+ey4lG+VuvMDnvEPfDbkRIdYnKPzjPornYz9iztL89rBnul+VGRsAn2yZ5av36qD4
+Ysluhz29fiR1NPJCnmOQSQARAQABiQE2BBgBCAAgFiEExwuETwAvIfbSuch1IuRK
+wGIrkcMFAlpqN94CGwwACgkQIuRKwGIrkcPlygf9E6FztnWmOv2ZZHyuvTvI0Qc2
+4Z/dP9nXjzP6i8uRXt1GhtrIHrhWG0zBm0T7wqZ/ROW3HnM3YZCSqhI+XTx1YVFO
+vo3sh/w3Q4K3D9dkkEdrx4PYUXtKc1aLojA1+EJ1G1tPQsKZocppSKd6V2+YzWuI
+jcY2CAZrhwxJjD/rluVHvntpYCABVkRaL2fQtQwoNGGMhpLXOO3GpKtxDSjVNixb
+0XG4KMn4zNBKa0lAqJVFDTd8slDXPiPm7xi+bMISqnZ0R2oG4O5HpbJ/9OaAxRj0
+B4tP1TpVlO8AYfr1MOpW92YuwqG1CJ1IVt6z7uS7m0zrU/qCB+/kUlCBCTtYRA==
+=6OQM
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    BF984B4145EA13F7
+sub    84761D363E7B0FC4
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQENBF7rgogBCADU9OwoEFdIgN5U0JU5pI7s3T1T1LeDMzAQ8l2Hq4jFrhnrjcEA
+ieDSut1YIv5NTBoZv4CrklaKvvQNUXPvKrFImA4OKhBodKV3wsq2efCATDGa1JAw
+VEJx6nJxxMsCLCJvmZsD+YE8/DIBI6jjnjh8jagZVkxkSRPvUIxlZCxytIyqXI0t
+O8pLh8+8p5e0PgGb9OoszxEQZdBavsixdpe+0feU9cz0l0jJYx3W4ErZeCGGwNat
+UUiW0ctb3iz7BkNhhoV9zepxkSLzCf5zBeyA+WfD34028pAfPpyAfDYXF4x55sVP
+/3MdWGB6eU6KzPG2/QV/6or5E+C1yCMrnMy1ABEBAAG5AQ0EXuuCiAEIAMExiS4A
+u6FPSlMyjCaT8EfxP05ey79rYSSZd/ixmqyVzJkXYbf/SJCBeX9+NtWU3LEuL0L9
+WXgAA9Ys18NaJzBMC4kiQJfyXK6Kak7sUfZWWvx3Ad3It35X4svJNuR9GoqgsOvC
+D1SPgK7MfTcAF8e4j2UUqgRnjM5S6dRa6AH2s3bCj8GE+YSH9Ag2osNcKBgosNiv
+w04tYh+sjx8W5ehKaOEMrBDO8OMYVeiIEOvBIjsCZpJgAnOBPh/7zIQ72tPa9Ou3
+nAWBwdiPBgqv5FTIVmHWMfAJKRRCW4ri2hN6toHGrjJMgobtwW56vTibb82EGLdY
+3BF41DQT2MEiM1UAEQEAAYkBPAQYAQgAJhYhBB0Ki153xninxyREWr+YS0FF6hP3
+BQJe64KIAhsMBQkDwmcAAAoJEL+YS0FF6hP3VWoH/1Uhih+Q/iJIddvBatWdzpgO
+e03ppK9pCWZ2KepukILbR8bpX3cqUiTbFD3W+ybrrY0k4Oe9hXcm3re0N2GAfEWf
+tRknxXH0TMeyWoBlldfSM5DjrljM88XAIkk/T2wcARv1PC59IIZGKOpixItF5Pps
+YII4YzlripU98sXBTSlJLU1/UZaT7XNOZ9O1/PVxADpVIeH6MVdWh4W7AV/dYZ5j
+d31NbXDTtyDJBUYoiq2hu10+RNoqq28WmJQGD8aqIuKOpeBA8EirLcRoDGELSqYT
+lQyC8nl8P5PgkEZ5CHcGymZlpzihR3ECrPJTk39Sb7D3SxCW4WrChV3kVfmLgvc=
+=WqT9
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    BFFA420097F49C8A
@@ -5453,61 +2290,91 @@
 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
-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
-FHbRy73IXeH4hW7tfdaW3iJPwUn9dA==
-=undb
+IDxtaWtlYjAxQGdtYWlsLmNvbT65Ag0EWoQ8HQEQAOgaoiCh7xsBdeEYIkrdjvcE
+ehTIloMfU3jmTXlQ7kx6W7OU4Tn79NguBQoZigVkekarfgKIEDwrpMV6XNWdSXm4
+9MxrvJA9k79eGL4UFB7Caf/qJzCT+7oKa8PBwE8iVb94pWeFUuoRxF+pdRt5TfQL
+tF9u6f1KV9kvCHbVyf3AlndD7MjrWGqI+THTRviBId3NjcFq6e5OHbAjJ9O0leFx
+6YJlHNSfL6/jUtFoMsyjBEkgFfAH4rLk0Ky28uCe/dj+BjazKZzVUhSAhq7ENxQW
+bWl707eYFVcdlUQZ1z359C87nzOS3UEulOqz7aQoJXIx9KTCVMF01/W4bP4CzpEB
+nOTkKsm0kTsnn1dvJz+kGUyZ6dkujT1Puubimkcpmbeuj55NvWPTqFLARXqvaoU5
+tXCmBArbEYRwsQI0HjRK3cs9zBjQREXtmu+KEpBEQe1V8E8I6RKaOsL2Y6dBVHGT
+69/Au4iU+UYTXwJeHcKs8MSZ+XCBZBH1MqeKT+WEbT7IsMoRnB7rCErnDYPJjbYg
+TOvmsd6T0cAWHbzrjzo8jNz35hLQgneY+mPPvx2LPt3Rx7xN4KpB4Dfur/Z1GVw0
+RxdeBrV4dq7hmixKE4kD3pIO5iiw5cDPtaShfD/sT6v3WfnEeBAGkm8dMu6AyXI0
+mR642vLiPTyXN1+9eNbFABEBAAGJBHIEGAEIACYWIQSG2kGl4WmcnOvpZKipBSsb
+bZLlYAUCWoQ8HQIbAgUJA8JnAAJACRCpBSsbbZLlYMF0IAQZAQgAHRYhBB+oaKNI
+cZ6IttDeJMA+8dfWkrz/BQJahDwdAAoJEMA+8dfWkrz/SWQP/iV2Uecst7xdqmRe
+WzMcEt5zCbccZUw5Yn7eJFs/QWy25Mvm1JtlH0POTO0ysOhc/t2PNWeBg27DEl1u
+p/mVXnodMswIdxFSuVJaQz1tHFbNP0r3dsw6ELlq7VjFyMoIJypqrHRx0dN22c92
+UlVvzYG1hPe1YmBGp63p6dJhmJr+N1jnPCUKUD9pOU/tvo+lPqLRmLjee5FWRXOj
+x+0++SzvSt6P8u424wmrqr3OjvFkofbuJ5Km5tG9luWrhCeQ6RIsVlAuXxQTQ7LH
+rNyRPhUW+6NS1hkjxlrxvHqQtG/9qAb7tbS/frFC39dcjpxj58LLq0ZlOoPYB1fo
+4gUqU84myXwYZKKeLnURudwuEBXfnnuTixzP7C5/8Q/NMIopk5GSsqAKnTBd3sX1
+BnVMfwMknugYJaUvEPtndO7mW1LapfVwIURIobJRG82ix51mxBF3Z4sJlUhjTr9x
+Y7QmMW3qoz/En25Xw8CjdkJWlRQ7/qK+aGSYVIneK8BNQxwFTy4RJwRkZ12c0j5M
+yiL0/kab94iWM0xywdxPv6FWSj86iAILSJRJFAE1F3gT9/26AECYwZX3mwD9Nliv
+c43/H4mzfvI/7awcjW42b4zC2sZkKTonI0HhauAed/mtqoxTkvCIKU7O2wBrIGB8
+457uQRBtUlgzVBLkMjNSEOC9VC2k+VUQAKh/NZh8DasEwOoF8moz2tzC5QWn0SRL
+ctO/JigWfBVTiYKpxlkWIdoDW5yHcgONqkEFZ9x0LBVXKFIRI3cRvh23AI3z6613
+3nWqBnPeV63ZgTODTwHGPYtQSNQnvFR2DQCv13+dK0HPi5UsZultd1hE+HjEY7iU
+DtHe7wHSY8YvX2OoR6wam604ZukIV5VU4E7hP8V9eg65PiPk6M/PPlAxnDumvsOl
+kx68ze9KB+SBqyF3/5A6Dz9Mg1OnxZo+7Z66BjUrtISDDEDJIaQgK3MvMN2NsrS1
+tRgSElNXT0A0ACXltNHE1qEadlVLSyskVG/zuKTgaDQVexlJpJQHCX5XBwtk193t
+12nwwCiOb2NeuJKxDHqhhOOW1JnOcawO/q0oEOf8nEmcULjQXuGaa44MDxv1cO1M
+C7/JpXUESeh0EbNiwhAxFClNEwfxIpbxh9ISX1X04AZXD7jRTE+pJcImv/DC6Amq
+qstzugjZxhMHOO3rnuBPQuucz2zKQ7SlNWoJjQhqcKE6lQ6mo6Z6Bxg6ZSjnb82J
+axiJlVug0m7oDT136TC8dZhb72V4p/Dw9MoorbBVvjyRmX3xkvx4P7jMAmlyHw8m
+ROJSFjlVW1kgg2r37eYsPEDp88oRquQ/lOWB4KUadVsGb7PPt+b72tgG0BN6kdG9
+ViAl4mNAKZC0uQENBE5zrtcBCACjn9YLBVHVMpS8j+oc3OEQIKBa9nhw6Gt8OPX3
+XAX5avOEEiIxpK3C4IC7wD+HqiZ2lISYfvGuAgUYOBa1tW7P54XQva28xUl5G696
+BtEWn03E7Rrj9ic6FV4n2OTnzZ4mtnxyKbXsdoSaH6IfN8golN+jGhYEDZaKyu/3
+Piu992hJOCgczHwFNyYiqyrEkxPdlXdcoeKUPYDADQD8z6jH3IPV4iCqLPHdVw0L
+Z3UElnHATbORGVjHLfvlz2DnWSOG8LJo/msriT2yTmBtU9BxAPcdLwcEgnxhKMqu
+vmapQq8XAIrhKqWDFJT892TjVHwvwtvWW/9EOALmUD7oNn75ABEBAAGJAR8EGAEC
+AAkFAk5zrtcCGwwACgkQQKPEQyvXMIzbHQgAtdi9YecogKQPL9ZydmBBrQh3Il/Q
+eCfYhpzh3lOl06rj5goMwDXeuci2ngPdBP/wopALZR1IIs+/nxsqxm11A80vcrve
+2D+i/Bn4WvFMpQa9B5tWyBbjYxtgvklFPQz33XqnfgYdMoGMJwegwrn62wfUdBJb
+6lA7bcoiavZtZPYAkhCyjC41f9RdRdVwPtpPD43GSg4AF6y1Kbv26AnK/vKM7kwU
+8sXC9tcNZ1u1YoJ2kUbfQ37x5bJYvuVv/i6UrquF3GgIHF/K4pE/WBLCaFDl51zz
++5hd9AoQGORe5ifQU3vUWWZp4wNnFHbRy73IXeH4hW7tfdaW3iJPwUn9dA==
+=Odlj
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    C1B12A5D99C0729D
+uid    Valentin Fondaratov <fondarat@gmail.com>
+
+sub    606CC6C4533E81A2
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQGNBGAic/4BDACtIv4a32pL+84jJNhJ1yb6GFgoWknJSJ6IELIL0Z7m+FYsymRs
+lTJ/QwBgjZlgS3HS7IBhEl5o+kEt2/U5lPkz/krP8By8EvRv18PpfBzmXNT8rGqc
+3Kq6CSye+aLfdtY2yP60yZCtES/E8s3gIQWV+yFbVm8K8nhMTUnHRxYEcWfK7YI9
+FwjRkSVuOQHhOYJKYeSxRvmARd8i355MN8unPhkuY3stBuWoZYNjSuXXE8fs4DBL
+0yx0lkzH5jI5E1lagp98ChOjbLQuACvVLxLP326ktgQjeZjO5xqe+Rm5h9iV2ETw
+UUJhigrsOMhzl6lk+9JqqNxKiEaoNcsW2NL5O3Jd6ta/WPSQtQGrElKBcZnltf95
+0SAJBKa/+B9our/SuNSe8kwKAK284ecwVo4AwavdPd+s2UR9ECcytDXFDs/QGQD4
+TjZ7sGgpFrLaoXXu4OqR7w1085I4RNELrfR/p5kRBhpU41Ey/UXpE9KGMztQ/tA8
+W0JEQdCUVgc6MQEAEQEAAbQoVmFsZW50aW4gRm9uZGFyYXRvdiA8Zm9uZGFyYXRA
+Z21haWwuY29tPrkBjQRgInP+AQwA3Ec9GNzLiMlTBn0x6EJL/sxRo83VrlAQcR2W
+ulDV7e8zFeCVB/jiy1yFIdJ5VyCXeVzsSs/ceEBqjEnz4MvWX1lnzX9zqcRArx7a
+SaPfB4Hva8Z91f7sTcNQAbvwNw1kUBVJZU8UOfDGMt+fycVidWO7CQpvuq1ZvL3n
+dApXLXHD2YMvOqgVg1jtaFPlaVSOoWkXyMg09ECof3p+JECB3ZJ7lht0JA3MHOk8
+gObcdsDxwwb3A+dS/Zw5Q/8zopHqGVmldiF4tG1SYqzc/i3Az58EYNZ2Ul1C2OI+
+tfh4FS2UqkwuRPspfPCfc89NXoyO00ArJOe/87xY5HvVm6BK8azL9RaogEyFmCxi
+EuZo9yC5NZhWD1CEEO0J45ZsTpxitUhKwoGgGO86yRJqiFuCfYHzRtkGqgDBQGC1
+PIE1/thSwdVYwt8ym5Bn9iNvSctoXoVYfsCw0gcTpQFTgib7S/kK1Gryq/vyQLg/
+KNV99TstqIeuT4w/BmT1f1yQH0fbABEBAAGJAbwEGAEIACYWIQTmIjEzG8p+Hyks
+m4jBsSpdmcBynQUCYCJz/gIbDAUJA8JnAAAKCRDBsSpdmcBynQaPC/wIP9hArjec
+DiSx6omRgFBaAILsQG7eKPwXCjob4GE2jtnWQi1jobE32GuXoRO/Hj2gz9+Ipsvf
+vWKmyMzJ8noPkCNsvVehuGwp1FQyyk+c6MHww4vLa3abr2e61EEaqVUEyXQ99m6K
+h7+FQq8apyCp6L41AN4mb1/g4hWzrCv/18evLzxZ3sC0sTZfrx8ECc7iGhsOgkI4
+Ls+ME48vYt5c+8Vmq+Gae/IZgQQKupRTxCqRWGTqwDsXOfXIwxcJ4eW8cNWCa+V/
+MIVSBri7/6jRXufu3lYEby3rYjV7JHaWE9ZFQrpwvxk2riyNd/6OJdJg8mfuGVF0
+78KBRtMCorx0t3tGqjqhZz2fftFJ94VXrvjm7dvPhP69u2bVVFeA83B7pCNu+lXu
+30d8b5D319qJCx6c31wQvj4SvQuB9uBDDNePl6Bkn8QeKcudTJJUPB+dS/lTVpQO
++b//JnTWDaGUkhM6IdLK+pJDxQwFRJBJfDHZj4y10zQANp5u2nyyg8Q=
+=T2sw
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    C1D3063467F1EBD1
@@ -5528,19 +2395,7 @@
 VrV2p7zb3zNieGT2P0STM8XvZVZ0JO2cSK0Leq5cgQQTY4UVj7gpV/aC05lFYAJt
 HUBiXJc3thesUHa1/UWUAaR+RCvJtVkn8fzu8iK9EkM8Vzhq7JKLsndbm9UauLXg
 iuCFjn8fnGs5MhQoKE2Th2cgDP+u9PPK3qHbiJhPckoYxU586BL4QyTDNwARAQAB
-tCRSb2JlcnQgUGFuemVyIDxyb2JlcnQucGFuemVyQG1lLmNvbT6JAj0EEwEKACcF
-AlVsvBQCGwMFCQeGH4AFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQwdMGNGfx
-69G2jRAA3dzZxHCbi/IGQfHWXerNNH0GR4joBGqtx5pTynedmdY8SmCkWp9uLTmk
-Js+f2aE89z2LHYNVCO0rGiNmhT7DA38s5UHRX1YMz2U0VQwDKWtSBAOa4iEF1FGN
-FPAICKuQM2mJzC/VpDsy3Ntjy0EPKux/C4lrXp1EyRmVRskmUW+e5EMH/aV2QJur
-REsIkAxYCfLP0VGxwtcSlnnLKhj6OIYBAbVVSz74jWHHznYroYgDmvgdlRm/JB68
-GsppdGqLSeMBuYzbC+GhlXzjDFv+lYlqhsjB1VglxmUm0O16Y5obFQm81lPOfhAa
-BOH8a0oC3gm4JNRigWFDZylMKKrB6CZSl3m1H9l+ZYygnbnW1JN/ckdl+z28XDPO
-k5EBu+f5nCeYRSzZTnXdmuOfifCqqlEBWRCWMUWm7WgZOE7axIjAj9bypFvQeLzj
-+APTop06woDc9KSAR3F703wFkKblql8qfwMoN/C/0QmkMQGIOcPBT2ISt4li8h3P
-sijGWS33OTggetQugvjn5SEGZUhKs67Ovon+RkvR4wJOmC6Ss6ZPpWd6kY+9cWzn
-uu6OLOIINzjyxRVbwRONEBpHbQxAZwYxMXt1uQMfNaPTlsp5R6Y2v3QqDi4zFmNu
-eh64EnSPweYZN3ZPo87cf8L3Ld6cAQ2mMLJ000bXk1er4dp4/DO5AY0EX7LmWwEM
+tCRSb2JlcnQgUGFuemVyIDxyb2JlcnQucGFuemVyQG1lLmNvbT65AY0EX7LmWwEM
 AK/6MDgsXm2TE46fXKFLA4xWmyaMGR9Bfvr7oe91GP79s/r6sLoPPQAY6z1CHHBP
 f7eXUchIwbdBA9ihTN5ouvdkIcmBPLkWlCls9JGGjD7sNFu0U25YYVxKwa6K9ZtN
 ImzQxkn93yK/RWe7uXqf0UxQwg2mYKrVkBlQ/ri56gORB3cFMonlg0oHk4UXXTN4
@@ -5559,7 +2414,7 @@
 6cHP8QJIYSNiIuKhlzGch0FhJ918Mb8u8Ul42g27SDcdl401y8pEHb+aUn8d4myx
 XjVy1zSwvcXdKE4r02XNjic2fzIq2/tKGZrmcYgHpHU3fro4GVS+NOzSCB9jKltU
 D7paqnM9qu7+vtLxWjNxTBw1m3gL
-=JhmY
+=0Z3u
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    C3BAB45F4AF71FAB
@@ -5577,101 +2432,95 @@
 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
-Z3lhQGdvb2dsZS5jb20+iQE4BBMBAgAiBQJZHjfPAhsDBgsJCAcDAgYVCAIJCgsE
-FgIDAQIeAQIXgAAKCRDDurRfSvcfq6YvB/9hKDyqK/il3R5CMadzZHBYS8rTvrhT
-Mfxr4GTMlXM0pX0AiJp/2j1ge3yZOrvNhmnhWibH/c03vcT6LncjJt71e6SGrkmw
-wTgMIuuSR+LYq4tbFmSFmQY21Oo1OS8QI85Ip/5Z3Dm2RqwFdmgw2GqC6hxI9J90
-uZhWJDqTO2YkUZblV1Drd8sddAXK4WbHBqpNSvZXXxlaAfdhtsPx1O0OxSLOs18F
-sGynKn51XV1RKK8W0ki3BsWQkl5MG+ACCYfk8ptctModOxIi+DU5oy4YKNmQhOM6
-b9x2X23L0yZqU34Oo1kekhHaARN7mciRrTCANZrB7c9w+IBhvYWcVz/7uQENBFZU
-siQBCAC9PvFKCblLAt4wxOtLZqwKbv4SvnQm3uVCZ0fM/d2kP9QHxF+kqOiWcwZc
-SA/xwjr0YyW2ri+i678NsYEp65zs0S39UrbwTwEnyZNWXaPS2aJ+l6H84N+Kgi1E
-C5A8p7YF0GmBd2MP8X4qqkwv8mr9vVU8yp39Da4vGlxrBzsnCfJ0zuyNKeMbkYqx
-buwZL3e2yvff7Q8bbWJL1JxtwVBpa/OVF+Ljg4dxHFXIFULXmsGG5rH+BdNCAaEw
-MnLrQjnWz9kQID+pIBcS3Bs+M1arvd0I0oxHU3oBdUlF3Bqc7w0fE/M0bEGwQmd9
-gP1CapsfhCDzxRzZC4ow6fn+ZpCXABEBAAGJAR8EGAECAAkFAlZUsiQCGwwACgkQ
-egGw8jblQw9sYwgAqi+u9MSP9unVrM+umch0NsiSOwzUMaIlNzmbNm360WcS6Rj/
-UOmFgnoW/ornL5hRby2QaBhls335lQYrA9mM95h7Rd8C8ftsyVx7dV8+x+lvPTOe
-E96lIUE01eqZeX+fyQlytQz/YgtMuOdzkWOQetG8qxiFLwQL/qllwT59xAmf96BJ
-BK2+WRqH/oCY336r1kQDdy4tFgUY9JXV6SaD/2BPJ2gye1HT383J4d7Zzk31A7xX
-q9TYdNIpTJn+hGM50cGjfcIXPVv9R8Lw3l64o63Ov9VOx6da+W9Q3XU7cemh8aVq
-3MGMNclxnJvNn4150XaJs+pwbdgxeNNGLmwGUbkCDQROKxu4ARAA3Ul8OzCRecxR
-De7Ik++JBscgTuNIH4Vth0yGxl8lByhqZJxzh1dymEtBhLby3PEBjh7+ai32JQno
-w+XGKc6A3FiFKNSmYmqhui7pXAMvLA7l96Oc9C6VIpLrV3iMRk4MXaXN0n2D4W+N
-4q+qTyONjIYBTjgAIRy6PchNuBuRWJ2uUuTmcXjlosHpYWFMQTEyMgPr8AlIovdW
-UjuQcj1PXdECQUtmNV8KdEgw2uc9cfkmEcdVzmvGg+/7W/itRvoIrnnWOSdnlSr8
-RQdAAanUzU9bY/W2Zl6nyhI6Ws4l2vVlUjTVNA3uiGGRbAyfcgv95mAQeKzXd4qk
-J5R+m0yGklhgNSNdyWUFJtHSXrfSMMT+6woDPJUrD1473U7x2SLSCGEGjIoVKd9+
-tBtehEolUVyF8EclM7ZvTDDYVc05BV5CfRuVl/tzoPIpzY8yVYiRn6oGeCjZV6pf
-fwbapbD3Jk3jKGX9iws/Ork31K4X9fAFSSathAf0fTPk1kO0fUhKUOPUaGVH1V2/
-xylZ1sPToychhYXOfDARsk9VTRqb8AxM3HiXxq4u9ZZ8ggHXTXNdMDcn3hdOvcFa
-Bk2clpJVoAu1AenVJ/vU2Ddv2aR7Q883QKBDqaRXaom9PsX3mNJrfZNP4z7ytsKl
-zGN1Ytp71Q0DVBBCcTC0hQYqgAPPBA8AEQEAAYkCJQQYAQIADwIbDAUCUxgp9wUJ
-CpCouwAKCRANOzKFYqEZp7tXD/0UW+7SBaEAs4G5TmZeRusV65Kw2lIBnLwf86Xc
-ver0+koeE5nYP5JeRFrsZCJPG/QgEi1Hm0Hvw2rXUb2lfiM8Uyeb8+7g3cO4nqq8
-+fmXhssNJfSlJT+2/ZI/A0XVQCVDugnff8ewfS4SaYoi5uNyb5h+oZpYKao/kKRP
-kCJ3mu54l+ydrDXfzNXL2CUwB8G2P5vCk6jYRz8J0cFkU3ONgayPHrgQncd0mexm
-bYCs4eiTIyKYPlxrTKiMGHT8ZajWd6QdaEgXSXZAilQr3dJlNm/uoLaLj31Wc/HY
-qPSHRngKKZPfZNV1eBL7TqknnwLdIjJI8JmYCHL8timi6CxmIVBp9TXqEPXjOMwh
-jFy+JexKhSogEZR2zrir/Hnjiy9eFClWKFqogTaNEslOA/ZdfDkO7Zz0uxdBkquU
-HmH2U6haG64xDFS15FZkpX1368hUGJXbI/URJja3TkyncRy+WnpLq51oj+OuUmN1
-4MyUdfoF55/o+8ApGGGTXtvzmg2F/uL9P3PwHVyZreA7JwOtu/TP9RJfNnnTHkV0
-IvXuHROATvzqpbh6WXLALnzOG0itzpJksY/ofe+pO155fOmVmp4TTyPDXEFKXBAy
-iH8nvtbUQYzHw4anSdUpcETFcS4iHHSOXHY9Ki1EvJ10Cw/GkmCuo2Ojv9g0bZrA
-JjRYRIkCJQQYAQIADwIbDAUCWH6fxgUJDjBJCQAKCRANOzKFYqEZp2dGEACsTqfS
-lkECcvfkfOtXheLVhgRfn6RgJCVIjkHRRAiF563IqlJhLGMPnJ/PkyFvP9TvW23L
-EX7sQit/9eY5tftONokoItVLTn13Lmx9Xb61gvV8l4QSk2KMJ1s4CtsA3uFUO0Ml
-5cAoyo/a7b8Ob9ewBx/7ojeSvsEfQStW72ctEktV1JR9aNpQ81mmkKxD+OGQnt9k
-zYmD3oZsp0jB1GRg0DYC+Z6rUPVd9syk9G7Kb3CeMBwOy/W/KX8AxzR4NEbeKtmm
-sAXxThAuPa5HUMQ85xDJNQhKqwhmg+SG7CrZ52qJ5mm90Ahw4ogzRo16ZsYLmorC
-QPv9iaRkPPXxU/YhvsJde+MoDv4GYQazzdXFoZrTHjL5RzLujZCSs2BdX2FUeJKV
-hBkXIM1ZfZXiwMv5+hYHX4r0Uas0rJo/2tQfeVQVhNG6WzpccQgrNjpE+A7aK/oC
-qZaEQn39k85atFWOJ1WBZpshtJw0jKwbKvrerkCRCLuIZEG2RIh9n4jkkJaHPEX4
-9mAxzMlmiQWMuy85upPbHE+/bd4hszee9/WILK2EbLUhuk2zUmxQdpaJ9quOf72X
-w5YDORcCowag+RjnBEvH/0NK6H9NRs+BnVZOOzn8L/lkpEGv/LOU+rDRTKydrDQ1
-UK8KLY4Z41KziEGiDJOc5hQr2JxrWNgc6i1T1okCJQQYAQIADwUCTisbuAIbDAUJ
-BSZcAAAKCRANOzKFYqEZp5GbEADhu4CchNhuzuoz/jtDtB6XM83MQLev8+ZZhHHZ
-WxZDL+9d/OSSSCoTkN9HZn6eFX0LhDAmovl4Emmby3LA7OgGKr/UbPOY1GliiCVF
-IkABZQqq1FWEj/C2rOY5ulxzF/gGejmyZhQlsIy5bsYwOUPJvqj0qhv6zIuMBLUw
-CV1XB+J2dFuE01H8xTxh155MoqlpmGFzHQGrd7Qsh2egxDfwhhKS0CSzP9EdmVk7
-iOtCqVIp5STobNxkL3Mk8LehHwfPinl0nP9oa+iluw5Ty0mATv+hhfFQM8muo44d
-V04522F+/DMTDccGcgo+ZakBFryVsFaj9N8ffqv4p741p9TUazh62xVBlMTR/VD1
-cMGffxKg9ANufk9DBU4q87MiosLyWJrhFoL6yYfi+S2VCNXhEz+wSxXqJp6nG2cY
-XxGaj2cVP7CnBwQuvNN9A28cckpYZpRR6kuKLsCkrhhE9RtI9oZitTQYJA7wbS58
-99eGkkSz7yyKjMYKVOztnRtFf5B4iu2rZEFPnhkl0KYK3DRf3Rr/SncHSTLyc+yu
-dQwg/1f9QJ66G22vHs7+iFeiRp8RwFHoP8/fI6Nm9hUEOG8Fil0JJTK8+JF5opMC
-B+0o59RY7kL9ixSvwDFKHWSQAuXLsXwA28tvjlj2EKpb6l+UHIJ7mV/BNbS7db99
-QMvI/okCPAQYAQIAJgIbDBYhBAGQgrwA4DJOKu9M8A07MoVioRmnBQJcXA/VBQkT
-1I6dAAoJEA07MoVioRmnd68QAMXf5d4koONiQWnJTq6weWLuko4zLGb1aQgQuzYH
-61rgZT7dzcR1vOydDHtRNCfjmJZxXWhpCgzELl0nxYaBh4Ayd6vlGWGXmiBW9gxl
-ZukOm8lS+i16N6Z23pXWbeKfurtfxzHlyrcEXo/OqOdQKvbZncchbPzB4qiwa5PW
-vrcNuFRXk+hl0SQKXbFjzxYH9cha/XIbt7gmR5GiJRn/Xy2mnrJS+PvrBhmo+jCm
-LPVasxk0DrlsKMFQQwxZyK4MKSub+RLbLPr6nL3U38hfAeZE+OUdHNcIRs7e8g04
-BTmuub46GW51/+DtLpvqPvBn1/rsqGViFd+RFqi7JN+Xh5LeoU7u2xxpZN8l4chb
-X2h2sXwaj0BStso0DEeEWkIbeJ6s5L7q8XN005H28ZrFRh11cbyhglBTOl3Smvqp
-t4wJd21rwzF1lPkisIN9v3XVEK+loGL0gokTFYp5Fwr3FdyxDK1AS60ucMsljhUv
-McEAfXaozIcWUp30QbNoaAitANwxhnr9ImwoGLz8kSp0Mblc1PxyU8KSEqhyi3uD
-/7CLP9r7/E9Q/J/qKK9oUbpp39uwiwtKRRitUEJPjVwSbV/S7KvKJY+ebguTD58a
-DGxwYm3fPr2hfh64y9N+vUgp8DYSxNm2ZrSe7lBsq4oEGbtIFLo5KIMxqPH+1DsB
-0QaAuQGNBGGiftwBDAC0+YpwzX/Pywwme3iwd7ed1ew51KpMltGQBx3IM7UXiqCP
-nP3CSuVVUoa5W2YlLeqZH3TVD6gf4mozpR4aqE2KDghC8wSJCON6W8pcxf089XOU
-/6BrljX/aadSaCZhcrjToJTtppDeGzv75cOiedBS3mdYX11dP7Er9IMtgyTmLVM2
-o9UVkE+bjgekiMoY0lcPtW//nPrb6EqzCkteBi3xHP3kHIadyNDUujYzVPVj8S7C
-VGhz1FN3IAFq9JBZUsojPqQozgt6NqONG8ufJsxS6DQImXmaeLhwdfH23SkyUbkM
-TY7eZkvBOBZwnxy7YK0/ED2It9W8UBOHGTdmK2QSEKEG0b39XwPgOJMiG3pt3j3G
-Qc/mnG0H9+6j2U1vRrFIFo4B5qe3coDoXq+SL5yGcaE4WpXUokdzFgbtWwbWFiHL
-khtmyDgZ1xd9PDAXX+aryS8d/JOQHLocwMbCmvQBM2evE7u0lOJWoO7F++IZBSOo
-khAOezp8z0Ejg5+lfKMAEQEAAYkBtgQYAQgAIBYhBEfraDYkXS1A6J37QTbU6WGP
-Otq1BQJhon7cAhsMAAoJEDbU6WGPOtq1EFwMAIJ+GxoIW8wlOWzmVP91xOpIJglh
-nIOP3kOVOJpE2RecAatPITjk+eYku/oUVnNJl2794sTyWzYxj8paqdlhhXYxy3+n
-AMMtKN0A381JF70d4CHY5LWQ143ZIhygvnmASh0oE1IyKxj03fKUszEdk9rks0Gj
-6P3B+0RpWLZ9NfwsMkVC9Q5nd/tzPd/q7jYV4dSpoubZqUdBKR9MHfIi7weajYRc
-eHhR/BOZLnk4EYtD3V3yd67s9yKaoJ5p14db6pjmDmGvk00vEwD6f6/A8ZxA3GDS
-UfZcF2UUFsAQsQbExwptbnVAvaH4R3AbNP+crciJr+qbc3nRnXaP+GHOiGV/tNCO
-HMHjdZvF5/3glsppy+eDy3+Ebf6fxQBJDOLMJKf+gyRdCiZd1B7kkWAkKuhTYJ+t
-0WZl9uSSr2YCLzQEtQQAY1NRCuD9bf1VfX+SUaJeJa2lTyCr+1IZFAddPAbnep6O
-VS0ojfXlmLM6EmKeJIPHh9lorbMH1GVmSud3Vg==
-=df4I
+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
+Vg==
+=a4IK
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    C4C8CB73B1435348
@@ -5691,20 +2540,8 @@
 HwwTUzQDbj16+Ynww+w+4GG9P9US6LFPOMhcfmBfl2IPQKfryZthPNywvwvjNvmw
 gbwVFpoZQVbhJZY6hiE2NOABkX3kmgLz+tZ8yGoeoQU6CrSKhsxOgPOMejI8rRXR
 OvTf9a0cNEWrTiRaeuCLBPsKwPkGCtN4SUBGz8GV1iGVaHWRjZJ2mUv/owARAQAB
-tB9KZXJvbWUgRG9jaGV6IDxqZWRvQGdvb2dsZS5jb20+iQI4BBMBAgAiBQJUsBm6
-AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDEyMtzsUNTSOr9EADloXGO
-pyiR2rsA+F1Jvdbzg5jvTYn+t3qPWqlYmPhDYGM21vEnshpFM+V4bV6t2nroMv50
-kTR5PXOQBVndjHGS3GE8fYoO/ByelUUb7CpG+UXa+q/+0myH3Xf3jaK86t4VD5RU
-PH8sjCQCVSgnX5aFVpRZCNkNG5O45jExt7do2srP4s6bRpoGLnTyyGXhH1ggfNnX
-jveldIy4bMMgqv811XdaSr+52zW7gSg7upQLZ8Pnx3rfe8R4vmy7NYJ6eMiTsMQn
-avwxln5YwqaBU2Fd3satYIHV2bJWx4Spn11cJmCwobhrnpBGRXHl2JudoNJusbxQ
-BvVEsY9SgbjTcKDnXvG89526QEDK5aLfRzxKyfz3IqyE3Xd9fUvJGwVf5xIT49uO
-kz1D3mw9qYlqOfjqgcMKAPLD9SGW9qjMCGtf2XCs70LjcOzZ4QiEBZQqr6G4fqbo
-mQdoENx8hVULS8x59mG8dzoVCBtMUbF6b50FVKv3PTPQl94FvqiXbWRgiae0MRSB
-WX9Pkp9ZzBJPngSuPr+9ErxDXzORXbD+37sBFepFA34DHoklZJuj2yT8ha1ddSK8
-2GtBV90ynM+79JrJmwIv/3rlaLW339ahg4gQjTn5BBK1A3XJ/zYlcVV1bw6Rv9fZ
-UtZ6v9CcTvYCJ6IEpN7qv2Ny0Y+yXhNwDaXflA==
-=Z5Zp
+tB9KZXJvbWUgRG9jaGV6IDxqZWRvQGdvb2dsZS5jb20+
+=AbrI
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    C550938A54246AD3
@@ -5724,111 +2561,150 @@
 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
-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
-=m2Ot
+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
+VD+d31gs8Aq9
+=it4S
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    C71FB765CD9DE313
+uid    Jaikiran Pai (CODE SIGNING KEY) <jaikiran@apache.org>
+
+sub    DFF2D25E2CD6139E
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+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
+=NiWz
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    C7BF26D0BB617866
@@ -5846,47 +2722,8 @@
 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
-b24gdmFuIFp5bCA8amFzb25AbWF2ZW4ub3JnPoheBBMRAgAeBQJCk6EAAhsDBgsJ
-CAcDAgMVAgMDFgIBAh4BAheAAAoJEMe/JtC7YXhmQBoAn3scsf+j7hXa2SCR9V5p
-ZHSc+A5SAKCOd9jUCIgKanSdYr9qBKSphfbcxIhGBBMRAgAGBQJCwzjdAAoJECt4
-dgmcDv+FjtAAnRRt46ghaklFDXHpVzfDP9WzjAUdAJ9D7M3tVOSaC8/kRFuuu403
-Ujb2bYhGBBMRAgAGBQJDnlugAAoJEP1viMYh0KcbrFEAn28c91qKDPQ2rnsfOVxJ
-mbkNaNx2AJ9w0jkzdoDICMP2Z+rFANYE8JJTpYicBBMBAgAGBQJDnl7TAAoJEDGm
-PZbsFAuBM+QEAI6RArWXAB3lK0LJdheGzcGmeTN+2OVhgdjAW3z6Ge5NMuBbMbfr
-idcBN+kKGz6PPWmO9o6iwNYfV4sZzAOGy9uh4JkeCg5iRAjNO0CosK8z0uQviBRL
-kPTSJYNJs5tC8zXWRVgiuMD5aF0Mv/EMSZU6ORpfAayHLvZvjXiWwrMuiEYEEBEC
-AAYFAkOfi3sACgkQFqlkleIiZ5X50ACfa81U9DYWb+4QM71ehHXtwbMGwjAAoPwf
-/sFPk5cQdDSI93w26VFpqX3iiEYEEBECAAYFAkOh6zcACgkQMJF5cimLx9AjAQCf
-ZEASVa7NT3HFipw9nwrsmACazf4An3wqvbI6W1XGOxwouEDDLKnI4XbtiEYEEBEC
-AAYFAkOkxG0ACgkQAQVmvOQTY5LpQQCgjI/jZE1Wn4HNx3ID/0RfI5RUMLkAni8v
-GQNpFA3JTlfg3+P2KkRH7kediEYEEBECAAYFAkOoM7MACgkQjON2uBzUhh8JRACe
-NVdygPIdctwPnZrfyXzs4cssKvMAniZFUwotsoCqpdc/2UfQ6bLHnSfNiEYEEBEC
-AAYFAkOpZfYACgkQBJE0Quobo40R4QCgo+5Gz9vSA4GzoaYBuEe3ATzKo+UAoLXq
-veOoxhIk7x8cphxoAXhzZtyTiEYEEBECAAYFAkOrwucACgkQjOGcXNDx0CAp2gCg
-ghDmD5s6BdrycufwCn6clYiLriMAn0ahC92yNDM3mId58MnsdCKHu15XiEYEEBEC
-AAYFAkm514YACgkQmx/anzwGIjHwqgCeL2/m+Ap2ac4QH7FQkNlAECJSwGIAoKlX
-loBnCJcyCJDQdotok5M9uFR7iQIcBBABCAAGBQJV/HCSAAoJEDwfaA2fJlhIkjIP
-/1X/lnWvwL1UP3xeRnotbHF+1TiYgkBUIv78CT2JvCepZRwaGvrEBSS2eV4DAnjw
-kKSGUHLBk/Yd0e35RBZOCJ/1IR4aECz6xKXx745EH3Tzl7J2LcKfD5ynDogyOEhv
-+BRCSaBf4CRqpmQ/6dLdF8t0+/Oz6QFUGzRz3nsLtfjS3dNW2c75y2/o8KckYWuB
-uVwq8kS5vov1V3MdMAbQDHcx8nX6ExRw1w701p1cj7djXNd3Y56q4MVi2uJ4o6RH
-uymPbI2iQxD8Cf3bMRGZ2y1irteFGMLlBDe/0XnXIH00H2eLAKRFRGnvDy06Ryqq
-IMu6NuJNGtHaveF6j+GXH1DpvP7v1z8a7b5jcrbuRBHopF7tgVWGftrQlNMXB2SN
-VUC3mNYII+bf5q+sCNQ0iRXFvhgRTZGOYoiJNspRpQaBOqgLLZocR5HVsak2Fq3x
-An6yU6R2Nl5Px49HcBNtvLZHLTu8vpckpjK9zFhC78fZ7N/HPDEEMVwTpqyEJz66
-vPbIDJOUJVsYRu2q6sMEM/5K9wY5BvRsV55JHW0uUx+GpewLzBLpl1VxL4QfIThH
-1HlgUO1kACNSXskQWiRFif9pSVi6aZFevoIhS2ESCsJa5GIF1buf7ZuFITB8VUdP
-hPE3bRYTZM4xhJLP67zNrni7i7kt0V0ftnGA89V+bkKviQGcBBABCgAGBQJX4d6W
-AAoJEAdksi5A87bbjXEL/0MArAJHj9UDEca3SgH1ZMY2FqrPYHPOSP1uxvnvxlFE
-B9CA3t+RsqPo5wgUsi4Ulbj7JiuZGdar4BbX6Xb7yax3UIWAG5MDG0YlbzZSa4zF
-eK5JqWt2sqMNduvtzYINyOIdo0v7laGVoc+H+5tSL9aXCHlNOctKkM+j9L6FxruZ
-Uafr/wai8ia6ub173SRapZ0kQ4prOXDL9d4DByXqsVhPTiyOz/67YPdvZMvPQmBZ
-52cP2OelPFVcWeuWwz+vBXgTMmLftae+vmpQ8nInexeIRqbFH591I8sFXfUYL7B+
-kVhVJc0m8dMTGxSoXw3lJjulV4WRomUlzT6DuS4J4dKa8wCj6NV/T2WbRH5Mkd4S
-l0v/E4hmXTH+GeScW0xNBlFcWVeVZeYdWPG5eNBQDlq8w5X6UfGoDpJO2mQRXD7C
-QIArp7XDbZIEfyRdUjGah4Xs2j01OvWTSZvCIHYDpcuOSS+EGHML/WVBxfTuQELB
-HFRXEvcUloGb8MDooxIT9g==
-=M7Pm
+b24gdmFuIFp5bCA8amFzb25AbWF2ZW4ub3JnPg==
+=9p1B
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    C84125C13BF6F2F2
@@ -5945,14 +2782,8 @@
 ZbFbn1hn0uuLYEbyYnQf1VWc0jhjMtVkfrx9zuXD4SOBt4aJWXDkl3Q4d1xqNnBP
 oGnBn4900Ab4EB9nE+siR4/MbJJGGP46WNehFKEbIyEq7vIJwfu+k6wepQQAewjT
 bHC3Do+HA2mbJNHFpbSSIJ/U4KLwJEo5D1BVABEBAAG0Iklnb3IgU3Bhc2ljIChK
-b2RkKSA8aW5mb0Bqb2RkLm9yZz6JATgEEwECACIFAkwbr10CGwMGCwkIBwMCBhUI
-AgkKCwQWAgMBAh4BAheAAAoJEMoZUrHmgtimv0YH/1p198xcGzIHVdS0M9pEwATj
-vFv03RwVsOk6AsYbEjdZwhr5kMWDskE7y7kUxkAfnq0Q+jOe2YrVtwFLBIbXKsCh
-vhtCXHOYoqTHXUlW31HRgJ5qkkxzmSGRkXSbJBMbfmIoKuJD5kmuI0RXRm+JNnpl
-nTUs9/LZx0P2+qtrY/L2wHKfbCgOqlyb++4Xv6PWs4mGiBKDsDwrGByOZ6pwE6o4
-/4+JGX1K4aIpA073F6j8b2YgTITcFYSvCJDs+myN/R2y1f6kT3KsevFpNFU1y6M1
-j7UCZPNDRoqqQH/IBgAi2cwvr0bQJDEtIzAAcDFoj0wUG1Zz+Myqvod0ixXRxHY=
-=HMGy
+b2RkKSA8aW5mb0Bqb2RkLm9yZz4=
+=B5Xk
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    CA85FFE638D4407A
@@ -5971,41 +2802,31 @@
 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
-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
-NH2JWMfWgwvQfmLUfg==
-=E1gP
+aWduYUBnbWFpbC5jb20+uQENBFgMcBMBCADbdP0cslUGpTkw26WgDwoq/1IuhLHR
+Zhnri4OkyKsuscgmdNomOpeHh0zzcYZwzz+Hpvt/4x4fOEGp9NTvLCQf6OAEm4ow
+Jbz8BYpGifBof6CnPhTuZFMAHIo+H/HbYHZJxipcqARahHX88wBIkpV9Bu24qq6g
+/pkemDN3hWwU0Sn2VcJ8ETIzSDfJ9hVFKbzCnBnt79+Bi/e3vaoPhDCAWekicXSm
+jLbwHymAelxt1CJpQUHWEKEvDoyiY6B+TOzUWPKm/r9PDOc/eM/nWdphees6VnCJ
+aHGCE+HlYv9ifIbcpIMSAQVojT38mC6yt69oexrUvtHtjG8IYVIpozhpABEBAAGJ
+A0QEGAEKAA8FAlgMcBMFCQ8JnAACGwwBKQkQamUXag+xzQvAXSAEGQEKAAYFAlgM
+cBMACgkQyokKX6Cc/YA5TAgAl5h5XUOfyQ1VAr3Dsrom0MUEO0JN/Cj8uH58yhkc
+UAigDhdnkAu2GtpMgz3/XkeXQh8vRZrFdoePy5/YYflksc2Pc5hRf0CGYKtr8RQk
+KmjBpdppJcVVTIK4Azkgx9KkdGmyWeFmUENn2CD3/xRsETOff0PE0QQHq4ArUEsi
+qgf3YgJHibuZUIPl0bmTvfDHtmLqZKTxnv97OLgOIXOegkE6wDFJ3RC9KUQoLLVO
+bTI+H8xwA1KXYFyaitU8TMQM+Hl05bxoD+h33Hud0dUqOTTWG8rNnid9Ne594nGa
+l+kqkNVcEXlu85iUxeU9AqnAlIXh31Ksl1rsUmdPJDtoR92hD/40ZpM9N+E0s9AZ
++tJKOZyxf+ZTx+jpvL3U7JoLmCGQg4XzlKX0A0n7ExvricAD8gfh76qpwTVg5e8U
+ZS9sPhgRl/d7BTigZS3L+IbyUlRL5kG0wIMzoM6woMhfdQ3qhoDIY5QJig9odPE7
+UuVn/WTSYK077kZz6Ct7RsNM/CR8D66lDKUB38nKhcXbyy/WfPmwNuHRShr3qw0Q
+qwIcoEA8sWmOlbgHBnI2ulCV4qpWwSBOvsFZASCAqlslqcfXLHz0t8wCyiqNCY73
+SC/aTXsnbbNWgR6bFM6Yn2/8m+ihGl187yCBgf88o5EzJXiFMQyG0x2/rJJvbvBZ
+pNqNBcKG116cFnFP8KOCj6FfuClaqZjj7KHnX4QlUW+Y0Iw8E5ph1Y7s1hbRoCzc
+LeyqRgzUKOX13rj8aNdGkiX84TM5xrc6H/QjfmkO3BcKG1Khto/pYMzMrKAREAxI
+Q2x8AhaXZVEmgmjys1IBb/mWTajhZhPDDlWaxdE8YsgryrdXIDVgsgiW1KorJoMy
+62HNL1bhc3S0ifOPqIxNuWXkFH2GK+fWtr/A7DsnWyWEGk+d5CkGoxjJp6kyI6ly
+fkUmZRfMgmSDxBx1nq25Or9reT4VOK7Kd1ckrLDP3fcmXBpPDlGOsOpznL6YLU4J
+iwHgbHheWdSmNH2JWMfWgwvQfmLUfg==
+=Ze1k
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    CB43338E060CF9FA
@@ -6028,54 +2849,31 @@
 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
-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
-=2NfF
+QGdtYWlsLmNvbT65AQ0EWCeUDwEIAMGWqQT5ccT/Q1OypoOQGEZn+oRkgEdnzt8m
+jo7aOXd6pkNTkt3+LCkmb8Pp3/a3iYEfvSvBZbb2JbY9xnmM8jBucWnow1iwEPxG
+hUuu3jlIpRsCwLk+utLkMALRkooXqanDoVRWxuVeFYN0as8nndgWiJT30innN4vf
+aR3x3E6/nS57zp5IggxZYsXTRHb25kaof9lglHyXeypW7quKOP4SeES70PVVUnYZ
+BlLpnX8a2msRtJiouWxCv/kHnYsjW62vc7nqvWAsSsfBT61TVx7yI9CckVFBnkpG
+1I8C9WpfcR+j9yauptgUMfrfDTFg3Aip7czMSoL4Jpu7jBcXy9UAEQEAAYkBHwQY
+AQoACQUCWCeUDwIbDAAKCRABJXlGTQHAapGTB/oCoCsuJ7617gpcuEAXxWTBfcXK
+o4z8ObBek2RUh0AY9aXjRYSzwwbtVFRC01Esr7R9b5ScY7W1TDQBKL0OSRZ3jwy7
+/hA7k8P7xAp3mC4+FdHaHMH8nGz2IbUjGWl3Yp01NRn4jc8gcnHnqcUff7PXsRMU
+tJ3dnbsIYOrBAbWKld07RVEQUyafgUfdF9cxe6P/slSZxATJrlIPveB5bgcVDMMw
+1UQNqJL9LWP2IM7xcljOBHY7jqwqnQ4pRER92VzhLdtsdwF0H1SuERpDxBAuibch
+MoCfQ3HER+K2mTUoJ04xog7MQs2aaMwSbem1LMvNBZ/mfF7QMYfMV2n7rbIkuQEN
+BFhlXQUBCACjbf1YZCOW1zMaDNl2DIfTD8+u7AGqnPRIsWp/zjjX7cnErTlRwdGI
+X0PZbDXiyG+qkL73rJLSNXsb9fMMUqTfgBMK6DBg0g34sYaYGnjhs4/KJlzkwWew
+b+9wPuHQlXqENRf7LODpESuyceYyzO07mOyBuuF0mMug+kLzDqCMSnagfNX+iWA8
+1cLMxb1FGw6lUCH4V3LgiwG2MbSVVcwJXRgVkf+9lKvJxN3q3nnmYx54gA2sFPl0
+dhIL9WjA7z/M1KVpdpkczigPtUicl4QOeveN+bOtOD38T3REpSpzGJYp1mTby+a6
+0Vdeb2GFNrufaJf4Nuwzhct91AqwoGeBABEBAAGJAR8EGAEIAAkFAlhlXQUCGwwA
+CgkQmP4DqXTOCgtjcAgAkwpYXFuhm7EWkR80nZ1wz1DZVn9FAz89GpjafJkMLMoC
+/o1tGsLa38cUqm7/tZsjEAe4OrSxRnEx/o6n5A7Ddq6UNv3J61FZrpzI5Ap6Esoa
+6qJUr/aMNC+yvhREZsauUBqKxPxsmvO6Ct/KL12ZhlLO/ThkrQHhJ115LWvnQ5V9
+58kxjOdhxGmmzyUb/3jKK8mT4L/OmoWozFMW3PS3Y5HVZ8I/sWuVMemVbjxh3qHa
+vjFk2bijP/6k/bl2zrIIilidRN2D8uCKe5HOJ3vedFhp3c7vzt2gZXarMVLLUNGm
+2V7QFY9Y9iKPyYL7fW7RPYtLLHxk/BwmrTBcLH4vfA==
+=07M1
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    CC54B93C018511A4
@@ -6129,21 +2927,8 @@
 NpbuqGYkBSO79V8Gk0OA1Qeq5HvUtElp27mgOgDHu8JO/8jhk6Kbc3KUN5pLhfAs
 mUNiK2ZCMXVcPlWCMdc7gnm31d7VfPQ7enWtSBqjkOAxi/nnhBZ1crJSEmnB9i49
 IpTtwh1MAfIqLbIa99rK5RcFouorO/JGK3Y0m4OG0S0yV9NGxjML8pIdzQARAQAB
-tBxOZXh1cyA8YUBydWJ5Z3JhcGVmcnVpdC5uZXQ+iQJUBBMBCAA+FiEEWxMegmWC
-z3lRDaoRzT5TnyCIMtAFAmETHdICGwMFCQeGHysFCwkIBwIGFQoJCAsCBBYCAwEC
-HgECF4AACgkQzT5TnyCIMtByCg/9HY42g0txw6KuhiSSpIK5U2YeNb53vDAjn/cr
-S44Y3I6JNMGHPxOdhKoxHdHnbcUJrnPlIOJSo4akaZ6P9LTR7Vv9WdrOh+5DfN+P
-6vCUA8BtP5rIlPF24H/sVlsJlYaA82bzt3h13Nj6z5hu35bwH0b2wiOM5EDzKv9J
-y/sIEKR+D3bs5kyxsG+5EXOZ/0JGd9jzELc0EgXn8xQvlVhcLaW0aoAHEP6zAhic
-DxojkQHgvQipqmVtV8/e94GNpCldgvcK+vNozsObaqzSy1fLyTTxBNpAN/HVTZlV
-xLtJMEigDhJNrVnFK+2IJN+Va3cDnkNWCcQuvr8NrVxxYgdmEq+IIpfVW587Zz0/
-pnPScKtwKg0TusscrY6H1U4rP4XAV2vnSh+kcNooHZdHlAniXkYR+ZhS9E5Ba2yv
-7JZq3yqk/vMHT3pOyfAtPZ++M4Hi6y3CF1UlmCCsQTxG5Syel3c3Ov+2S9BAvTzN
-rDebg2EJdKP5JLQyVcYDJ3W0MMrR3N0MTRzqpIcz5BURyW0VwIYLC9l6XbT99IWb
-xIEli1hOuB+HIjXU78Zv/TDjTDjMhy3W7Vmu1qL3w1a50DKj4iNFaocfjQVKFCmv
-eM8KXKWIj1M2tWaqYeVTTvzZuhCXYX/3FnGDsqhC8OSvD519Dpnyrrwp2UT1nITe
-du1sJWA=
-=E0VM
+tBxOZXh1cyA8YUBydWJ5Z3JhcGVmcnVpdC5uZXQ+
+=6TWz
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    CE8B1D1D2530EDC5
@@ -6206,41 +2991,34 @@
 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
-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
-gV/CnS8wUo5/I8CLoaE=
-=jj4c
+Zz65Ag0ETTV7GRAIALtD96cFf5SlQhlx14MCYK0EixUnaygYmnihGbgmBABFlxPY
+S8Z90+jFq4qB8AfN70V1lNjQvLaToPxsSyO3+1d8yrZXj7KZowiuUHGqJwJCvJ9Q
+VGl8J4grsWsopkeyJj3N+Kdfn++6QaWdp1dFLOsgIkQc6NiACjZ90dDdE0MukBrz
+Ap+02BV+b8r5vXhOJuBlIfwu/fk8s4F5p1d/Fo3eqyerMDOChn5xBMy9tNloOhIJ
+4NMuA253xiST9B+QEGUJ1XnCxwM0DJz7TyKIu8WzQfTstXdp81QlKsbl+mPtR8pT
+qrIyhineZ0snYzz0H4hfTkCHq8kuLidLI6lMGV8AAwcH/3yIfY77meXlYZ+UDbTo
+eJ/98xarLEsVq2B4JcHfoTxJN60cI+co21hvxrFCS/K4VO6bIDGOFL61JqEVczQQ
+Q0ast0d9e8OsJpWcuuaCFxKeBbVRVdYrkxEuL3yK1qq/y+BPRTdmz6neDDXjJx3J
+zWF9/1R9Yo2LnpkjgW/wzuGYi+6spq7n0IzkdkipJ46V4v28Ulaq/+3HgHyPboDB
+T83AM5e7uRFoEDK7kTg6jMOmDvfNwG8gJQfOrkfdg3pUf0pgTtTYEsULHEhPhaA8
+g3RIPfuW7prCUPsf7BbuhjMIiEhQtj3TMX64ret4x9duIqovxsY2AT9RAJjTHG/w
+L/mISQQYEQIACQUCTTV7GQIbDAAKCRClBWnHyn+h8N61AJ9Swdwiq9h+ULtis5Ee
+QTCeQiS9+QCgouImrGAGZkRnMjjIBCglklNz5g65Ag0EScu/VBAIAIYkT4qggB8t
+JeDsGWQ6Uioge+vyYieHtmclXo6bomem6Ijx7AfxCn8pxBxQSXg6ZtFNH9U9MpXw
+zOGf4PQqjcEzpH/uh5OPFxcvTK+LB+4q6fAIXe2QqKm331vnAkSNmpWILaDfaq0E
+e2RarH4EexnLrc5Dx0453TUhyaiseochk6OQE1Q3mwRhbC8N6TMRqg8fKSrrwIB1
+Aev1Jl8ER94Iwwx/FY2Tskd5mAGu0XIbo68BWL/0uZ6yuZ5iEEiYU4sM0iJcentI
+iH7I1vXESV32fwMQifnQbMGedp9maiETjOngdSrXGLFg8+JlqFHxBl+CaDZeb4f5
+SDd3p6tAy4MAAwYH/Aqdy9gBYdcAo9mF2GwXQGCyICbPPNLs99GtDEI9si4gKYZ9
+c7De58C7accH6o/l23c+vYaqvBt5THYytqjLqBhMufuaxggAhLzh7QxoiZ+OJh3A
+mH+fM9PgR44StpQ1FjgNGkU/5LfueRCYTB0bAtuffWVJ4ec/LiOyXTYqKbdaZ423
+mb8zLTcoc2kD99YJfRXEP9vuzpl6m7eiRbH9VLzZggFqAiharp7tUs0QrgHLXrYV
+5fkWs9K0wOrYl24As66K+euSPJHysQTazA7wTsQSk9I2oFxxk6FxPAmW6OuLTMbF
+F/3z8y8T110d4OrjQocwrK/w3poOkhGcZUS9yZGISQQYEQIACQUCScu/VAIbDAAK
+CRB6PJ/iHf2/RDyaAKClT3jgC4Ax/jvbeDzYLIAJyn9TsACfZ0j7AVahWBh6PGpv
+SGOW4Lo6rZaISQQYEQIACQUCScu/VAIbDAAKCRB6PJ/iHf2/RDyaAKDFucnpykMl
+cCn/IrEfBO354q3QsACeLrFMMFAhgV/CnS8wUo5/I8CLoaE=
+=nR1P
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    D0EB627D4885CED1
@@ -6640,38 +3418,32 @@
 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
-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
-=M/0D
+IDxldGhhbi5hdGtpbnNAZ21haWwuY29tPrkCDQRJoXr/EAgAoJmSrlu4uHo9RhCt
+UmSNOczGf922j95Ldou20NKmRKd6Mki9dGdCt/TJSCaWFULfoQ7rKDQy8IRrP18x
+aJ/KzhG6fjh9uuA0v9x9ORsEHB5Xih3s9gt/TqC6NWeOSd8RZeG6Y6SgtFcA+5a3
+UP9yFSXo0xS2It48f/ClB1/I1V+iQjVXC3pbs3LwGJlaclNEV8ROkXDhyYPwW6LQ
+kWEDa043dSKCOFe6FnytD/9vv92weOFe7pSltEJiHjz9qpZlAq3CANsjqsqqZJU3
+6+8rpyiJfSiHXcdjgCiDQyzdQm5HQl0cU1e1+CabMrL/8+9X6C1a4dSXu++NDXgb
+VB5hnwAEDQf/Z3kUqSvprtKZjh2orialskkQ6jQlGTSCoX/oUAsBvnsVSdP0CHUn
+I0mYfIh4WhuiJwHgmvTfjrAzU2Jfwww9glw6OlhXP4mgvM23rF/Rk/gpAaUfweSy
+6TYlynfzFHQ6HPIYEZqGmLPJ/n8a4rsIGSWcW2+TC+OEI7RgYvC+LJuoXToqn2tP
+Q3Av3JeEbO8nenk3SJtrR70cu5F76ANJwgCuwug/ylzvM774DuXQd4vPFd4jFxvj
+/AvKs5itQAGRaInfCxn4uHcUZc2WiGy7T28CpD1Q3ptUybs/ogyDCKa/xVHKXu9w
+RF4osf8U1DdDpjfabdKWG1+RMbx08zH7dYhJBBgRAgAJBQJJoXr/AhsMAAoJEGUl
+/XDMMDZVr0oAoLmZHTCR39fwJJTr3cABz/njjNQvAKDSCgB4qq6hm4Gb0pdRaUN4
+u4e7pLkBDQRP/UWKAQgAwCpp7Tc6P1bg0mXQst8KghkdlC3t0sjAvwO2P/8YSJR0
+4R0rn1JftXrfpcx3S+hjg1Xo8wml3jX2mnfZq2tqYxEJYsPu3DyaXtSp+SKEWJnn
+3GADyrLboyYFhtvbpaw8MOKAODQwZieDPWq+1uc6rYAUDgDjm4QkNdBnzuxvph1b
+ksJJawJHBP+SFEarp5PfhRyzL3AeiM178jxbkbFO5TMr2K0NLsiOBlCEojEPIuSc
+8MRcUk4HOzeJTMjwaQw3aNVOem2J1aBpZWAG510/MmiBczRJhH9cvMRPTUt4tZpj
+QFRV5VyzcD/Mr5spkxW3DU1eiolUEBoHOt0XkSfyzQARAQABiQEfBBgBAgAJBQJP
+/UWKAhsMAAoJEGp650aDqK4apYMH/1kfSnMBlw6IAKSYtv4YdjOTcrSdIKMgI5Un
+auDRhSJyPz0/4KbV//nEEcH2fILA5pzjODmJVXCOXCvBKpOgfXh+lP37GIwTfTUs
+ljr+DwOdgEsLp5moU0tvrn9TukKT+m+S0rMrN2Y1LEAgIT729BNN4gKx9ZW73W/K
+8gLZfvNQ0LFY1utu/6DgdZe9GqIJlLmHNvqolZwEU3cyPffzf9HDCL2tjSRqhvyE
+KrQwFP6wgHgxzdLEWKdJtED17YEl5ejkXIRQS5HCH+JRupBCPLxqdsjKXGLudm9F
+cZC3O8IsAMB/WdvXC4fZxtXuFYirvR/J8e1ECGzaSQn5KMFW2v8=
+=vyx3
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    DDBCC1270A29D081
@@ -6698,149 +3470,136 @@
 o20q+byMuXC8kKIEiDOa7UFQUh1ku8RDAmjXlWwqw3+RtrRt0TXoHRNWRoShiBee
 TjkQ3ZW7FlSk1hs80qeVCcSJ6lf1texmZzonAJCyibEWdzvceGvs2/PenetsnP/5
 nDlJuEs4jhoYuU57HjXfcSSOqmLAFAym7pL0Yd+KNhvxjBF/SoJ1wgoLBQARAQAB
-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
-rXnsBd8THP6JFQo=
-=rGKr
+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
+=WvO+
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    E126833F9CF64915
@@ -6861,55 +3620,8 @@
 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
-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
-=3ivX
+Y2ttY2d1aXJlQGFwYWNoZS5vcmc+
+=djRF
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    E16AB52D79FD224F
@@ -6925,14 +3637,8 @@
 Xb4BWO+z2PCmChz60i/Ko2ZKPJV2TqPqWO+aklgxTTwZZ0IvgFm/5n3Dtn5p5iGf
 qwKkHPJIDWc8cWYtxC608LFdqiAlYmp/oPi5ABEBAAG0T0dvb2dsZSBBcGkgQ2xp
 ZW50IExpYnJhcmllcyAoUmVsZWFzZXMpIDxnb29nbGUtYXBpLWNsaWVudC1saWJy
-YXJpZXNAZ29vZ2xlLmNvbT6JATgEEwECACIFAlF/4bYCGwMGCwkIBwMCBhUIAgkK
-CwQWAgMBAh4BAheAAAoJEOFqtS15/SJP6NYIAMbwdE5S9M/5tIhLPg2arbvnxfh2
-i1e2aKE6PcRlBGeNq8YzWQStIewRhoDbY4MY3dG0nX9wgXU6XFJNxjyjE5Gqpyrc
-xEhs6r+HfxlGwB/OCaDMGR89lcWn1xF7uju3Qsdkb6o2JuCSAqOh4M1zIqADSMMq
-OjuSJ13GddcUFQ/MEvouE7Leesls9AM724BNZfnyJjIfJqfk38rXCxKpMbxCJ2v2
-louDMu0LClA1efdvDBPOrc8+A74dsVh7cQqMoe5lCqrcoC5apMqlJ71UV7SIBNBr
-7+AD6p0ZdGY8C/pTCl+NGe/Skjdqk7uEbmyePpm4BaJTmbwIgiZdGxthUgM=
-=C7cg
+YXJpZXNAZ29vZ2xlLmNvbT4=
+=lsJ5
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    E2B3D84202B812D9
@@ -6948,27 +3654,20 @@
 MzRIsqt+1m2JYoLARF+MHjEGW6nyILm0AmgiSMjj8VvsaJNJkffO5C5Ijc4xG5Oy
 ilT+7ig2JzgHGuDb2AtYdDPRJcPwkOS8B4V7DdV9csnW5VGhXQP5ow8SE9+CyqTR
 oZ0csvglx6hx0Qc8eY31q8/TKOcebPhuUa4xABEBAAG0LUpvZWwgQ29zdGlnbGlv
-bGEgPGpvZWwuY29zdGlnbGlvbGFAZ21haWwuY29tPokBPgQTAQIAKAUCVuUOzgIb
-AwUJCWYBgAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ4rPYQgK4Etn8sQf6
-ApWUWTm9PrdGrEr4j++minXk2GwlhsMcmXpsgnZcvEg3+xHqZJxgkpAO+XDtAUO3
-nrkgRMrCiuWQtdfxIoGtWwqZxRLo7HyC5teTrGWuOt56kQroE8/PBlu4Ly55ktpa
-WNPovJd8zuPdTY6o2IZs/FZtnfFa3ZF43cP6GbKfwf1uaZjTdfw0leam5BemoHqg
-kYcRkQwCKc1qOH2yFi7DpzyE0JYO+I17wN5aFv3VzEtvVZb+vUHPejwbzYqolTTS
-MdSKROSDUgsMexD8SWG12pjgWu2jzm9a6wMBn1L2M+nZ3ycSUFdpkiGC9zEdEs54
-g9P1+DYt0FeQdYNjDV+K+rkBDQRS8pBqAQgA6xspAr7Lnk9/9N2AjQXa1m7bOo/H
-g//5p6/gJ4Cu+OiMbtk70faXrHTgiY6Sn74UYNMXlO4jrVhMNo6vzLwjbptmmLB0
-2Y1ysPW2XYFCIwFm6f/2FdjrrbxFg4Q13kNpxktyjIS0/mX7n0J0G5a+ptSLnr6M
-Nx9T18n8ru+eUde1Clyph5dsEBdLNpNF+9mA7sE8t3wCUgkFejwxfqgFR1OFLGuH
-hk8py1BFbQ1eWjfFeKg4zRCROuxnbhOwkd0RnT9LKcXXDq0MmxSaFkcAQ0+hurE6
-9OjZnDwthhX70mXnOUptT4kVrzmydspSvwDWOuo32Fd1k4gfvrG0ptfAtwARAQAB
-iQElBBgBCgAPBQJS8pBqAhsMBQkHhh+AAAoJELtv1a2R0BhXtOcH/2U/sJAIGtWh
-SWs8VxcgErrdYoq7m4j1Io/7OhVypwGPUWa34l81KmXlsMdnylNOW2Pu186HiKWj
-Dt3opRGWeyQyWH9k4kgQwLpSXPqrwTQh0P6XOV0ORi3tZRcQJuhFm+Fma5W6MAP9
-R83hrX4mgj9KxR7hxP5fK7CBn2ORXcFCXgm9vL8dPQnJaXPD//OAPqWMKFJbQKdQ
-agbtwgJ8K4gzqnOF/mLnmA2OLDM182mlwHLdeOAXUnYU/FqoLCCA2GyHmiPx2B8B
-xKgpu9dGh08P680pTp/z9XYym2rQ8BYghKItYgAsh4XmgwFZSeHM8cJMnnOneiEr
-6A6WLeTPktw=
-=V6Im
+bGEgPGpvZWwuY29zdGlnbGlvbGFAZ21haWwuY29tPrkBDQRS8pBqAQgA6xspAr7L
+nk9/9N2AjQXa1m7bOo/Hg//5p6/gJ4Cu+OiMbtk70faXrHTgiY6Sn74UYNMXlO4j
+rVhMNo6vzLwjbptmmLB02Y1ysPW2XYFCIwFm6f/2FdjrrbxFg4Q13kNpxktyjIS0
+/mX7n0J0G5a+ptSLnr6MNx9T18n8ru+eUde1Clyph5dsEBdLNpNF+9mA7sE8t3wC
+UgkFejwxfqgFR1OFLGuHhk8py1BFbQ1eWjfFeKg4zRCROuxnbhOwkd0RnT9LKcXX
+Dq0MmxSaFkcAQ0+hurE69OjZnDwthhX70mXnOUptT4kVrzmydspSvwDWOuo32Fd1
+k4gfvrG0ptfAtwARAQABiQElBBgBCgAPBQJS8pBqAhsMBQkHhh+AAAoJELtv1a2R
+0BhXtOcH/2U/sJAIGtWhSWs8VxcgErrdYoq7m4j1Io/7OhVypwGPUWa34l81KmXl
+sMdnylNOW2Pu186HiKWjDt3opRGWeyQyWH9k4kgQwLpSXPqrwTQh0P6XOV0ORi3t
+ZRcQJuhFm+Fma5W6MAP9R83hrX4mgj9KxR7hxP5fK7CBn2ORXcFCXgm9vL8dPQnJ
+aXPD//OAPqWMKFJbQKdQagbtwgJ8K4gzqnOF/mLnmA2OLDM182mlwHLdeOAXUnYU
+/FqoLCCA2GyHmiPx2B8BxKgpu9dGh08P680pTp/z9XYym2rQ8BYghKItYgAsh4Xm
+gwFZSeHM8cJMnnOneiEr6A6WLeTPktw=
+=aKDQ
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    E3822B59020A349D
@@ -7041,119 +3740,104 @@
 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
-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
-=zAqh
+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
+=QGxc
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    EB380DC13C39F675
@@ -7175,59 +3859,29 @@
 lZ9zV6hPxRg1K0a8mHa/KRcvqTVXB50hUx+QCp4IdEF4BuAq/WK69Nond3qT7300
 bIZo0s/FejBkUc5Oz0rgQB90YxRhB42l8uM0GXiPROrXjvXaqbbmjVhhoQARAQAB
 tC5NYXJrIFZlZGRlciA8bWFyay52ZWRkZXIua2V5QG15LWFjY291bnRzLmluZm8+
-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
-=lEfe
+uQINBD4+ox0QCADv4Yl/Fsx1jjCyU+eMf2sXg3ap9awQ3+XFpmglhzdrozTZYKce
+XpqFPb+0ErbDVAjhgW15HjuAK+2Bvo7Ukd986jYd8uZENGJGN3UNMIep7JfsIeFy
+CGP901GVbZnSXlAURyZX1TRWGndoV9YLhSN+zctT6GQBbMTvNoPlwf0nvK//rG5l
+XDjXXHSHhSqxNxYy7SIzUHMQupfUNjsvCg8Rv871GRt/h+Yt7XUTMhoJrg+oBFdB
+lzh2FKKcy3ordfgGtGwpN+jMG7vgXjsPwiVt/m9Jgdu4Tmn/WggPOeSD+nyRb7cX
+G5avJxyKoVNw3PbXnLJff0tcWeUvMpRv8XkbAAMFB/4vCqprwIatF+w4AnGKbrcI
+d+3LmZRzmtRKdOyUZgQg4JHUF5Bq7I9ls8OwMP0xnVlpJp9qcW/AUbouXH3GRTu3
+Or68ouhaSbi7nF/e+fnlWOdJ3VpD15CdRxeIvhycEahNs5Yjf0RzLOCyXMF0L74w
++NxBNwDunolRWw/qgAHcVBaDni25SjQRzxuwzxvcS/jYua5BPk10ocbAexdM+2XS
+SWThtCTg5qMeyLLUExqGlPbuNaMmUyIlz4hYnSaCGQoe33bqz/KZ91/keR1DVzK+
+zPm2vJUjcXHvxd5Jh9C+67CqnYfXf2lcYSSDSfop1Q5611laF7vRgY0/DXKNYlPU
+iFQEGBECAAwFAk53Pe0FCRP7AbgAEgdlR1BHAAEBCRCMcY07UHLh9RSbAJsFivb5
+sESf8vYE5yfD1n9AVa6FEwCgpWAIWbl9p1DcB+L5RCUBw6mGucmITAQYEQIADAUC
+R6yU/AUJDTBY3wAKCRCMcY07UHLh9aXOAJ9CPeuR9yWNhk64s6fnQns8W4XsWgCe
+PBYaHBWL34WbYHlFm+mj74EGR0+ITAQYEQIADAUCPj6jHQUJCWYBgAAKCRCMcY07
+UHLh9e6eAJ969UKVQvcoZ1tnEgI6vpK+9hl9HgCeNveZJejcWBLhQKZq9ftLlKv0
+pbGIVAQYEQIADAUCPj6jHQUJCWYBgAASCRCMcY07UHLh9QdlR1BHAAEB7p4An3r1
+QpVC9yhnW2cSAjq+kr72GX0eAJ4295kl6NxYEuFApmr1+0uUq/SlsQ==
+=lNXZ
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    EC5BCE97B4DEFA96
 uid    Martin Grotzke <mg@fsfe.org>
-uid    Martin Grotzke <martin.grotzke@ballrace.org>
-uid    Martin Grotzke <martin.grotzke@freiheit.com>
-uid    Martin Grotzke <martin.grotzke@googlemail.com>
-uid    Martin Grotzke (Person) <martin.grotzke@magrokosmos.de>
-uid    Martin Grotzke (Development) <martin.grotzke@javakaffee.de>
-uid    Martin Grotzke (GS-fabline GbR) <martin.grotzke@gs-fabline.de>
 
 sub    EC60DDA0EB128193
 -----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -7242,124 +3896,20 @@
 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
-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
-=eLkc
+b3R6a2UgPG1nQGZzZmUub3JnPrkBDQRaOAFqAQgAqqD4qJuGsK/ExVQ3/sYlTNWC
+Brk/+MlDZqTKnf9Uhc0+WfjluuzvQIYgvhP6Uy7Q7HFVC5zoRr5nBvYgKfS75dL3
+vNRkbPGmJMfILluQDn3Yfzz8j4BD+LYzIeyUxIp8ilUYpvHJ1e4BC1LdOL3M/LyX
+XUof39UTwr8h9AKMbv7/Z7szY9lmVJy2RY3T/cOFkyqScC/PRyWLe1bVe7LnEeko
+o+2OJAZHebzd1zr7stpQ5qONeLySul7xEN62PZw5RmRb8bX2as5edrd1xaHn42dX
+1NVgfZPOtYv6GdYAgQmj1te7aH3rq2LQiUeJPbH+YKPN9Y9pcNXGX6sFlerY9wAR
+AQABiQEfBBgBAgAJBQJaOAFqAhsMAAoJEI7fJmfQ7P+vlCcH/1AwMWy0oFerkHrh
+ZBT8XrMTj6iHIcVXeiopOmKJU0zDks6MAmTrwcYtCFuUeGX2GQXZ40iJZrca7d1X
+V2uIRL76yZcHWjLtMmSO4ar7MQQU8LUFNSzObdIZ/fYAIUUYALGZtiOe13MY7J4+
+eL3L5lPQ+wqoEI53kfPdfzEieAWx1zRUI/W1OcIh5kclFkHX0ddDYsazCwRPb4o1
+jfqSFEGC8iNVy65nD6hD7WrZ3RKqhJgKIRjoDmmGg6NL3j0ijIFeBFHRBVRM8LzY
+PdbMOWnE/dIU1OANZtOW3wPcI/7q3yCbMuFLzy+QnI07Rod0LjbkBCSg01SUuVjF
+6us8gSc=
+=VT2Q
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    ECA081A9790D4FC4
@@ -7390,15 +3940,8 @@
 O7G9A/wItw9LtJhVBqXVxL/pNVNA++NneLMdl5OpAAqJtI44oflJpo/FJlnDFuHP
 ueLYZVWWGoOlPW0odKxI3fvMO0ZfTtm4VO0mjEN2kZ/OR8L3c2Tdx9AHS5lVqIh/
 T4fRqXl/zjbdE+ZqYqhEYelZat3V4EhBAmj2jFQiHsSbfMiSerQjR3VpbGxhdW1l
-IE5vZGV0IDxnbm9kZXRAYXBhY2hlLm9yZz6IYAQTEQIAIAUCREV5UQIbAwYLCQgH
-AwIEFQIIAwQWAgMBAh4BAheAAAoJEOzf6jy0STuUE5kAoLn8tQysgB+HBQX+QJxG
-5pnWDb2aAKCbBR+UU/lCBV7JNCUPjOmkmAR9T4hGBBARAgAGBQJE7bXOAAoJEMuu
-vjmkbEyhNCQAoJ70ZMfvGy6ap/U3dSYy3dULIIy9AKDbo4C50KbCsMaoY7cWjl2c
-LKbW44hGBBARAgAGBQJHZsZaAAoJEAg1a1HjGvUza/4AoJyGi6F2l75ddalhc0vv
-0Z6AFeyYAKCSVoK9NGbUT3W41xnOel2/HOIMy4hhBBMRAgAhAhsDAh4BAheABQJL
-K4WZBQsJCAcDBRUKCQgLBRYCAwEAAAoJEOzf6jy0STuUwSIAnRe5ozDnz8rCgysq
-+rekaP5v71qWAJwI54rq5vKbYbM5Mvo8GGF/DdPCSw==
-=C8x2
+IE5vZGV0IDxnbm9kZXRAYXBhY2hlLm9yZz4=
+=1BqI
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    ED2378CD09A08CDE
@@ -7421,52 +3964,38 @@
 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
-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
-H0hD3qfqZsgZV9p6yscaETbJeSljYkM=
-=ZwYW
+aWwuY29tPrkBDQRceU7GAQgAwaQT351vLvTU5kq0/SJxN1S41o4MYShLlI60InjH
+OTybjE9CdVeEkCivhcwtJVxXmNrAIw8T1vK2UxbmEPaii5fe7VSBShndIfwC4KCt
+CxFg2T7VcV8dftpbHW3E22a0zLbhpkgLcDzOEOFSRNl01k4j/Pjda+wHztVHcJdS
+5I1HzeEa20Sv9Agnsf4UGvRheLtj9h0aWb/oXZEhaEYAUDwHSBMd8B9OoG/ZL7sd
+NbLRwNKZYgDZ+K2k/Hg59+s5UoM+EVo0/ppfK1e5NFhiI7qsEUk2mh74+R8cjtdT
+5mARb4nZZKf1/cS7z1oS3st7Qv1wVkzLMnhAQ+fpmo/TuQARAQABiQE8BBgBCAAm
+FiEEzqtrzoRv2mNdpZZkFNFpBtlIKpIFAlx5TsYCGwwFCQPCZwAACgkQFNFpBtlI
+KpIM5QgApZEapVv+nyYF5GP6h676X4zwQ5ud4mU0vhzf8/3i30Uyn7RlGRLPJ/8h
+M3y+5tHJrRVIVuceCMWN8KzlcnLzHVBpCW1hKAZqiKTneUKRfSoDEOdbigtC08/b
+modNin9swiXVrc5Zh2gyhAYeNJ6Ht4UjrF9RXm8FFromqscibaB7hsjx3JL+XY6B
+Fat6ANbIHmmB05fsezyZHLOd0sCd7CXePjae8mAI8PLniS6STjTkY8rqrBMePHur
+qCyC2sySB8xZYVg4l5ULo1lcWx65TLtomCVS6eYUtX/nfFaZqlfii+/KGxICpBCb
+v0X+WimPbr80NnQeJNPyEw2eZqE6lLkBDQRT1PEyAQgA0A5RG31MshHFziVkue97
+0s1KNN7eDzww5KgIj8SzI5Hi20yZzs2o2ALBhfK42VqT6NIa6ktuJQJoWr6KhhY+
+kMaZlkt+I0U+Llmby4l2jlG6lFPNZYIavLOFvU8eWvU934fgLutK4HimBNo8HTqn
++jsfoNTf8cpvcASQnjQVOElf5xTYQ6RWz331ZDpfJpuPyPWK9/GMHQrurhK4QYSl
+a9sYdYIZM27OmNxdi9haPLSkaAmeYF9png0CoPqanI3HfIN9W9Uu7kPczW3Zr1Dg
+Xq4pto7rlK69M01GewKMVuTS2MJqzWzaP9BCMj0jI+icpSRV0696A/1eZCR7Vab9
+NwARAQABiQI+BBgBAgAJBQJT1PEyAhsuASkJEBZL0iR7k2cRwF0gBBkBAgAGBQJT
+1PEyAAoJEO/oCG+ek3dOenoH/1vTfHTzf4Z1odIE7lC98k0zF7jtvPHndsUysKp3
+zMOtruEb8CE0cuw9Z0rYzIBzVTzNm305So7G1xv3HVOd6rVcMtpsZpo2dAyFEbBT
+yOqbDKfWrGx4T9e61m7syNCCjadGlTAEr7XLkJwLgjhHGwTN9/VEWz207JfL6u1X
+dfaGGSxPgZRqDw7c+kKax1fU10wV9wv5bs6t/fnPqJppZSR607BiHNUAt8Cpn7kE
+C3Rm8mS2ufqg5LX+quLlgl4K7L+EYecq5Ox/UG4lRCNGa6e1TM/LyoM8GpMC0OOK
+079GceNjR+r1Jx5fxD8Qi04rKo8U4LZd8Kig//NPGjBx2ytpcQf+J0linycedx3O
+yhGifM1j7SfrWpeQT/TJZG08qVw7mr7eLzYgtPfz7YzsxCAcGo+IvxSaxVF8c3BM
+e0liwwcSDL3SF5XWYt+3X04e9YTODKimD4bqhZto/DsJHR2Md8m8Q8Za+V1bS8cl
+ogD1f6Wbo3adzy2w+braL+KDyjm2NQsBSuLNBqcK/ztXGqxK3bhWH2wT541djd98
+y/jB52X+eVoeCEEPZi21wW6UDCgHqfC1Xxhsq70BbH5RCwIMx2iyVVoKEyxNhaOg
+ApSfq9mko0NAlpAuT9HbTm6GPVgoEzTAHBhD37R+PsUfSEPep+pmyBlX2nrKxxoR
+Nsl5KWNiQw==
+=jFkb
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    EFEC7EA866CC43AE
@@ -7481,14 +4010,8 @@
 a16nyVR2dPfqOkL7LLzMR4Tl8VQFb36WPrFBmJKzZWxt0r2pQhEDMwItuZeKrBhm
 K/RZWtNqiBO61JCBHfWZdpduUcTjlr5cW+jkRtw8La0qgglJcSN/sErQamAtU6vo
 sdTZ2aQQZnYyVBt00yrLV+9Dq/dBS6cfV9NHABEBAAG0LHNjYWxhLXhtbCA8c2Nh
-bGEtaW50ZXJuYWxzQGdvb2dsZWdyb3Vwcy5jb20+iQEcBBMBAgAGBQJVUKIcAAoJ
-EO/sfqhmzEOuHtkH/25VVvDzMo85E8KlCtsnkD5Alb83zV1XF6+mZaRHikzKkQRz
-phZEGaU6ee3V6CH5qXsmKTU2B1WaOYIdPkuBjwdpRPJbaX0zzrWUCCv1vLKDb+z2
-nlcg0AehMUM3UinbGR6QCh06p3O/tBokJvZM+Ng3pkXtLOS4HphRfindpy7+u1Y/
-szcIQS88AH1g5xPt8nwrh9VQbrYD04K20mLckGIWnjSzgFB9hntMF5arAP9Q1RkS
-52xiOZB8RTZZCkFeHIdMKjjmoM9Vn/3JZzsy8Om4FWYa/l2fEExxKWFupvQetjFk
-VTTOG+T7/WwVPQQ0xQLROgWL7z5UgxHly64WClA=
-=/6/b
+bGEtaW50ZXJuYWxzQGdvb2dsZWdyb3Vwcy5jb20+
+=r8jh
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    F0DF21D1D0A3C384
@@ -7505,39 +4028,32 @@
 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
-bWViQGdvb2dsZS5jb20+iQE4BBMBAgAiBQJWKoUpAhsDBgsJCAcDAgYVCAIJCgsE
-FgIDAQIeAQIXgAAKCRDw3yHR0KPDhLl0CACQiHc/BJJhPeFhtuBCm0vli0U2YNo8
-zKxo0MZpe8QUkGSsLmxmvX5tY1tH7SnrYk+g8XerXMTzikk/RSrYF7hMcUF4wYGj
-3OtzhbXGuz9lH8tPL5KhmGxFXXw4BEbw3wUItBcdlvGaGfQEO7BJKPZ3lgDHTRy9
-masVpMXXBk5QkjWfOc+G0j0e03emwoUOP+LhTIKWTbPU3u1IYIi9mP8LWnfuCMBG
-K8LV/d3RW5B/72VDLYS6s/CwEMKhELdYVrA4RL3OQ2kvaZuK26hQcOMNAHj8xKDn
-FKohPz1WtsOUUuG1RYw2DsyL1voVSPtRnsyL7PWJY92WeSDgf+ZJIpiPuQENBFc7
-oMQBCADMbSSz+BBh63/250biR5MjKnfyxCDEeCrM1JdQFOaHgsWoe2bCtjI8AFAw
-9lHiwCMgDZiqvr17ZmtsL0Hjs6NEAzJ8S6gA0kOMalFHIG5+9a4zKtjv09OgSPS6
-9gWl3rmGI4nwPpaFswmzh3IM+/KfEUZsZQoTuZllpZ685w21I1eD4ofDNS0f2tm1
-IlPxbYNeBj0q4XLfQ92+QozCGuLaljKbyPuozoyZv2Q91Me1m85Rwfgff8k8O596
-S0jF4/9OzySq6bVHiTjaa3Te1d8INX51UvArkJiyqCYVNZC57nIJwhUDGh04YY8X
-WgTK+jJat7q09gUsPe9AAdaleFoPABEBAAGJAR8EGAECAAkFAlc7oMQCGwwACgkQ
-p3ZPUCqTjJlIzgf/Qcjkt4V/cicrAPyA2xVqJ2JpaO6doUG0dbYkNN0KW12Vwj8Z
-EpJ1trh6hS8XeXhZEeOhMVyDahczDule/qPrKI+41Ditq8UFCL3X+Nt64Y3TlPkx
-l0g1SnzYQXPz/s9MUF+05DrjcK3M7YLJqEiP+StK2xevmMd04c9vWTRT005HRRoq
-uhnwzsATqdFTrzch8O5juMeP+YHTyie87DE09ToAWPlEPZrG/2nuOx7wBCIBYOuT
-gGplPnFnuO9T0LqPDBJnkSgrCbmZWIx3MKr6nFTnByfV847ogj5+De2jZEk76DnB
-rwVmgJgU8VlUTIb7Sa8nZZ1iZQiYj32b03j1EbkCDQRJqClPEAgA0QeHyW6OIAnK
-i2f9oxjnsGli1YfeJrnEAD0KxhwzAfO9eB4rk5gCj2DJ2IQ2vQhnFrjcCdnhagn3
-oActfc61cmGvyN298QeusekfuweASCuW/dVjDYdlJT1yZ+/7K+ILsFKtCprot87B
-JpaLODlk6sIbsnYUAqEKdF3Brxk6zY/T8+7pqwHgbTeadVpHrZlKGe0XHiJJaU7v
-xxopRBsHk6AryhgDWT1gDgRF5LBkyUpal8Y6qDAcbD7G5GRdQ5vOWFpNa99eA+vl
-GzFnMi+IofgRdJ92IinZDOpmMz92uZ8jH2voCLb5zlYo4jK3RZpfQdY4ayHW31sE
-+zYWus7UfwADBQf9HFVVZi47bQfyhHVunnOSOh/CBaTu3o1Jdm7uZkxnCppGDHuB
-cHz0OriMAvDjFewBZ5uBhp1F5Z5/VlJSXHwvPUwo6KQICV3XyW+p/+V++seL5kci
-c3OphwB1qZPYEqhceEghHmN/r/wWV/8WxkZ7Sw1AnDwqXTJiIZhaEjRVXUIjN5Wp
-INIssz+DjFnTu76S3v9VSOjTmUU7qPII3Eg7dJEgE0wv3E1d9lIPPbUa0pba9735
-uMLqoQNrT87kXKSjKhQUD0u5bu3TmLdPboHzUBWYH/00zEodwkjWK1TxZ7sv4gC8
-oLXTpyHDhLGFdjFr8bp/FM2WQ9Ip1w8ax0UAtohJBBgRAgAJBQJJqClPAhsMAAoJ
-EAN0zy6N0b39q5AAoIK0VSayWKgg9KNmRZUsmmjvsiq5AJ9yBhsAewK4wWQeRnWh
-lAD9/+S7pA==
-=4Rx0
+bWViQGdvb2dsZS5jb20+uQENBFc7oMQBCADMbSSz+BBh63/250biR5MjKnfyxCDE
+eCrM1JdQFOaHgsWoe2bCtjI8AFAw9lHiwCMgDZiqvr17ZmtsL0Hjs6NEAzJ8S6gA
+0kOMalFHIG5+9a4zKtjv09OgSPS69gWl3rmGI4nwPpaFswmzh3IM+/KfEUZsZQoT
+uZllpZ685w21I1eD4ofDNS0f2tm1IlPxbYNeBj0q4XLfQ92+QozCGuLaljKbyPuo
+zoyZv2Q91Me1m85Rwfgff8k8O596S0jF4/9OzySq6bVHiTjaa3Te1d8INX51UvAr
+kJiyqCYVNZC57nIJwhUDGh04YY8XWgTK+jJat7q09gUsPe9AAdaleFoPABEBAAGJ
+AR8EGAECAAkFAlc7oMQCGwwACgkQp3ZPUCqTjJlIzgf/Qcjkt4V/cicrAPyA2xVq
+J2JpaO6doUG0dbYkNN0KW12Vwj8ZEpJ1trh6hS8XeXhZEeOhMVyDahczDule/qPr
+KI+41Ditq8UFCL3X+Nt64Y3TlPkxl0g1SnzYQXPz/s9MUF+05DrjcK3M7YLJqEiP
++StK2xevmMd04c9vWTRT005HRRoquhnwzsATqdFTrzch8O5juMeP+YHTyie87DE0
+9ToAWPlEPZrG/2nuOx7wBCIBYOuTgGplPnFnuO9T0LqPDBJnkSgrCbmZWIx3MKr6
+nFTnByfV847ogj5+De2jZEk76DnBrwVmgJgU8VlUTIb7Sa8nZZ1iZQiYj32b03j1
+EbkCDQRJqClPEAgA0QeHyW6OIAnKi2f9oxjnsGli1YfeJrnEAD0KxhwzAfO9eB4r
+k5gCj2DJ2IQ2vQhnFrjcCdnhagn3oActfc61cmGvyN298QeusekfuweASCuW/dVj
+DYdlJT1yZ+/7K+ILsFKtCprot87BJpaLODlk6sIbsnYUAqEKdF3Brxk6zY/T8+7p
+qwHgbTeadVpHrZlKGe0XHiJJaU7vxxopRBsHk6AryhgDWT1gDgRF5LBkyUpal8Y6
+qDAcbD7G5GRdQ5vOWFpNa99eA+vlGzFnMi+IofgRdJ92IinZDOpmMz92uZ8jH2vo
+CLb5zlYo4jK3RZpfQdY4ayHW31sE+zYWus7UfwADBQf9HFVVZi47bQfyhHVunnOS
+Oh/CBaTu3o1Jdm7uZkxnCppGDHuBcHz0OriMAvDjFewBZ5uBhp1F5Z5/VlJSXHwv
+PUwo6KQICV3XyW+p/+V++seL5kcic3OphwB1qZPYEqhceEghHmN/r/wWV/8WxkZ7
+Sw1AnDwqXTJiIZhaEjRVXUIjN5WpINIssz+DjFnTu76S3v9VSOjTmUU7qPII3Eg7
+dJEgE0wv3E1d9lIPPbUa0pba9735uMLqoQNrT87kXKSjKhQUD0u5bu3TmLdPboHz
+UBWYH/00zEodwkjWK1TxZ7sv4gC8oLXTpyHDhLGFdjFr8bp/FM2WQ9Ip1w8ax0UA
+tohJBBgRAgAJBQJJqClPAhsMAAoJEAN0zy6N0b39q5AAoIK0VSayWKgg9KNmRZUs
+mmjvsiq5AJ9yBhsAewK4wWQeRnWhlAD9/+S7pA==
+=qbxE
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    F42E87F9665015C9
@@ -7556,22 +4072,20 @@
 ld5ZA/9awGTsigBdpBK2F6GOmbvBv+Xebu9rbaJvBvP+npNx01s/f5sHPCxmBTFk
 m1vtaMdZ29RovrWPSZRj8WWes0bcisw80250r1CBlYzGzqEVZ7b0Hh2RfkfaxbYh
 wikyfTfA2iX8TUGBgirsZbyegjUadElhwFNDASnvLTEuQKeVLLQlSm9uYXRoYW4g
-SGVkbGV5IDxqb25hdGhhbkBoZWRsZXkubmV0PohgBBMRAgAgBQJLbBdqAhsDBgsJ
-CAcDAgQVAggDBBYCAwECHgECF4AACgkQ9C6H+WZQFclGlQCffKOELC160VgHuKqd
-gHjyCWuaUYYAnRqzXThIBCJiqEsEtNL1hEYqI2NFuQENBE6UwPQBCACrWbzUBKDN
-c2AI4K1OP+4ihtXCsDNjr9CcoS23tnDIecbY1sA/3x63j8ypOMBWkKQweQfskS0r
-zMjD3kNfNuU5IQa75dXTkdVq9iq7GjDx/6rMlccmF2I9C74VOj4n56+gLLitdLH/
-q02hNca3u2AkYA4yqdd48IiRnRv+Jl3iRk8cqeu2n/wnp1pNjX3GdNB+qNaRjUsQ
-4PsFhU6LaDFhBph47Qt/79j0XXoIRqKi2BQ27ySBzoS89pKJcW1Z6LBUlQV+XLq3
-DTMH6qFs3C7QBiafHIdg/9XVkA5F7F/srJReHuFyCN29S/Aw/WSu/S+kBQPE6rkZ
-csC+c4fBJyjPABEBAAGJAR8EGAECAAkFAk6UwPQCGwwACgkQDZy17pa3vCLawQf+
-IVRJ16peuX7t1W2j558q2Qfd4UNk0F8Jrf+8h5171AQhB09m0cVq+APLqvQpU89r
-G4uXiq9i2eiDlCcPf3f6MNHb7mXKUPps0r0ODfpaJKhqwjAt7UtVeCVwIWW52tjo
-g0EmbdwnU0ekX9+yiN6GXn7Y1o6qJij7J7nFmEqCnZy//tH1SPlRj2TVKZv67LZ8
-kcmQaMqGZcflKRswTlK087PwS6M0rrvHmguT/gWdl7Aa/5Bfx/8F/kONhcyXJ78k
-cXn8iwmllcuDZxKHXovZgD5+KnhjvlJ3ih8monE9b82XPMLUYmVoL7MNu7q4eRp0
-/+1FR8jz/94jbo7rkW5MFg==
-=7HlO
+SGVkbGV5IDxqb25hdGhhbkBoZWRsZXkubmV0PrkBDQROlMD0AQgAq1m81ASgzXNg
+COCtTj/uIobVwrAzY6/QnKEtt7ZwyHnG2NbAP98et4/MqTjAVpCkMHkH7JEtK8zI
+w95DXzblOSEGu+XV05HVavYquxow8f+qzJXHJhdiPQu+FTo+J+evoCy4rXSx/6tN
+oTXGt7tgJGAOMqnXePCIkZ0b/iZd4kZPHKnrtp/8J6daTY19xnTQfqjWkY1LEOD7
+BYVOi2gxYQaYeO0Lf+/Y9F16CEaiotgUNu8kgc6EvPaSiXFtWeiwVJUFfly6tw0z
+B+qhbNwu0AYmnxyHYP/V1ZAORexf7KyUXh7hcgjdvUvwMP1krv0vpAUDxOq5GXLA
+vnOHwScozwARAQABiQEfBBgBAgAJBQJOlMD0AhsMAAoJEA2cte6Wt7wi2sEH/iFU
+SdeqXrl+7dVto+efKtkH3eFDZNBfCa3/vIede9QEIQdPZtHFavgDy6r0KVPPaxuL
+l4qvYtnog5QnD393+jDR2+5lylD6bNK9Dg36WiSoasIwLe1LVXglcCFludrY6INB
+Jm3cJ1NHpF/fsojehl5+2NaOqiYo+ye5xZhKgp2cv/7R9Uj5UY9k1Smb+uy2fJHJ
+kGjKhmXH5SkbME5StPOz8EujNK67x5oLk/4FnZewGv+QX8f/Bf5DjYXMlye/JHF5
+/IsJpZXLg2cSh16L2YA+fip4Y75Sd4ofJqJxPW/NlzzC1GJlaC+zDbu6uHkadP/t
+RUfI8//eI26O65FuTBY=
+=M2he
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    F659AFA484A4F6AF
@@ -7592,39 +4106,27 @@
 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
-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
-hMS7+GXRE9N+lbHzD69jMIXTfGTRdx0DXZm85Q==
-=BsF7
+tCFDdXJ0aXMgUnVlZGVuIDxjdHJ1ZWRlbkB3aXNjLmVkdT65AQ0EWJHXTwEIAMVz
+VIsiquhTxBvKxCgYhEBwVF78gubW/jUR4QyQ9YF0a6ynIvMBmaOFmjc/+OovNNMs
+ceUha92fWccg3GWlk2Lkeo0edJrcjHwXK3p53fxAy35AZoDNLGA105KPCFJYeWIM
+VErwQzMMmWzSXg6+AE8z4agbP6A3c4m1HJbY05HFBoBWaWN1RY6E01CNuFBgQjJF
+suC3dyBEDu9NJ+fU9Yek/5/1dwRh6skszv2YsZDQdHx4oatDkF2duARDV7xdZfm6
+VEnK7mJ+u/4il4oamnI/13zH0OhNKZeJZzxnB4qi4db7r4A4MF2a/AGjWLiGhi1n
+IMFl6jBLdSubJrzG2fkAEQEAAYkBPAQYAQgAJhYhBIYI5cXgnczoNbULzRFY13zb
+OnC7BQJYkddPAhsMBQkDwmcAAAoJEBFY13zbOnC7MeAH+wSnHQmrtG7KdSBNOXDU
+rrc7S3IDYsFtsR/oMYR2ImB0c8IWOZjNlAUVtnNwGfytHqxD7P2HRhVfTR5FLgZU
+ndMBPTVCFNpfE+N5n6LDxDY/j5Vp1RH7eViqm7e5GmYo8dwYq0m77RgmQlj6JcJQ
+IIdMxwndMvx/cVh60XlppqJnR330DHbjlVZcmEtQdqtbFJ4tkVXsWrKuVQlwVCcg
+Mh5iNdDi5EUljkBYn6dn0Td2VGWLtdZGeQhTfL/BaAPBcce1GJ+pLSUydySRQqsc
+U46qaM2KXOaONOi3jixtK74ZZDap5Pqkdxc6ton6g6AIC2SpzhugFj0K+UZm7LIe
+W+yJATwEGAEKACYCGwwWIQSGCOXF4J3M6DW1C80RWNd82zpwuwUCWmB1IwUJBZJW
+VAAKCRARWNd82zpwu+fdCACpIpxpFglcPUdGF4DPp7YHo9oUMWlz/sC2mhr6H9Ou
+IITrMVDfAc6/jcxHouNtW/UlI4Vwp+BrFye7t0ymkdzM502Klh8HoFd46EZYFneg
+Hiv1OR+WDMmWnU8wQwXaXlqVjdmUnQ2vnN+5GElbOqbt63Z0lb2h/7B07vBQcCBB
++HXIM0qBMaGj/2+32LApui8OtZeximd41jWcVB83Oe/inXXD+62TZFZ2fVlNSi2W
+V4OWOFOTN4nSjH6+NxEWTwLcp+9taRGHIlpq6+hGomlk9NP61z4Bom/aLZ7nzO86
+NPMMxKiExLv4ZdET036VsfMPr2MwhdN8ZNF3HQNdmbzl
+=e3ur
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    F6D4A1D411E9D1AE
@@ -7642,8 +4144,6 @@
 
 pub    F9FD57A03B80305D
 uid    Lars Hupel <lars@hupel.info>
-uid    Lars Hupel <lars.hupel@tum.de>
-uid    Lars Hupel <lars.hupel@mytum.de>
 
 sub    FA6831EE37606774
 sub    FC7403768B4C0CDE
@@ -7677,177 +4177,87 @@
 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
-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
-=hMDC
+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
+=h1le
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    FEFE78456EDDC34A
@@ -7865,18 +4275,8 @@
 /aKuAcOxpGLpdVOMM+y4N5mTQfdlL81G9kbGQarMmwGaJb2a82PaF20wRwVgiVfO
 p/GWgwXr0XdJNLqx13LdM8BMM5vmLomOQOjnpQBOlJWRgrYUJQOReKAEAQqNMsxS
 IW9laXkrewJtblEAEQEAAbQtTWF0dGlhIFRvbW1hc29uZSA8bWF0dGlhLnRvbW1h
-c29uZUBnbWFpbC5jb20+iQHUBBMBCAA+FiEEWbBiJP2JEuNmA755/v54RW7dw0oF
-Al+y5lsCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ/v54RW7d
-w0r9Vgv9HrprTCdG/poFKyk1sybIv8IciQRAE9DI8uYt6oauM+YvrOirR6bV+ZIq
-xekXyxOgZUpjNcLfp91ecT+ZS9u4M/pJ8PAJ98bV1mBkRPrWs0unlFJTySVliVMW
-1+oBx4a82uMY0cVE3K7QdmVT0QT7re1G5ltdvaCPDEe66dzKcMPVviw4wEAhK0R4
-7T6CsYYBdQEQS26zTkFDEBWVfSFBQ3zt2D6q9e6LmABvN39WYs2hc38VOPY5fSG0
-ifbPn4KxzXC2absbgUy6IEbWwZmljhyE//8hLomgwkAlAV8R/Luixpib8I2XhnGS
-ZSzwWvcjYZ4vruoTXKAC46UQbDKYzo2TE0lahE6mwL8iP7cq17qwBvV4XVlN/flD
-kYHpSajbR85BnwvTM5OW74hJzZlrPDTA6bTx914LayoKtc5KOKE+VffSJFFhZo8L
-7UVBf6abEV02M/q3rvZTHTdGsCMYOJrcavlJsgpVcNyPxtJyYPKnMxEN4Hpva0nZ
-OyB7dyen
-=jZIQ
+c29uZUBnbWFpbC5jb20+
+=WrRN
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    012579464D01C06A
@@ -7892,79 +4292,31 @@
 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
-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
-=CtmJ
+PGhib3V0ZW15QGFwYWNoZS5vcmc+uQINBFozxDwBEADSt3ev2gk2G0oOLUkXlEzk
+g3FHnde0XDOM0UZP5jxz6Z9Kn0oAv/JyGQoTKyvVK3nu6AfRscvCucaXfOJS4JL3
+luTwaHz9MmnTiBlgLsfkvond0J4zwWj2i49ExvwD6a1noXdt7U7GAOsPyuw1NGDo
+uHamjaQ8l3tZLNKrcYKQTGZ6J71YwD9ILm+QrHt0B3h1QsUQx3XQ8kIFkUnkZyl0
+XujQAnmQ4NOci6UN6PYGRMVHFu09i0MRaCi7LkJrt2/fZG6m2l72OvMKzBBll1vf
+N7REWin1BaPRAJVjJe9c+cbu1oxtEkmMVc8wq84zCiLwOtlVqusl8zg4Ecy8d8ak
+Ilw9+/UIPaeYm8HE01OElQONC3qtR65zN7kqkDmVtFCKTDCUsRXtsp7pOU2aotn1
+2gE7YkvjgyIPlsH58PBhX3VTZ01LbFSFfwbW/NqtJLx2CemuY82t208rDAVhK2Um
+fV8bRsergDAszkLxmZjec7BM3I8qGl0wiH+NCVQKQ5v+RvCSmQnNALViG2XMbwXD
+GODQwalUECVibeiXi2yYPNafIQUMoNNJoq505FfNb8buyhQOCXFg+8EmU8KDUA0x
+vgU3bYl71iWNmysaPBaBgAIqSeocjok2AkFoBOTCqRZ5b6F83/yMrd2c9mCdUzoF
+oS7Aqa+Drvr+WF0HWtGz7wARAQABiQI2BBgBCAAgFiEEH6N/vkRTwQc+fvYdZEkA
+X5a8l6MFAlozxDwCGwwACgkQZEkAX5a8l6OPPQ//dKhLo/Lt9bYUEiGZFOBv7EMf
+chS25ZAkRNWInu19s8xpD4ZtH3qZEiP4fsEe6bQcrpnMRNSxQDcBs6mJsn0+AmVX
+6e5FwYyj+M/3p2JhIuun8mGK/im1ytyXnmZLvjYhgRXj+/RH8jzht97jlxIWSW1x
+C0ol1mZWSxxl6NePiB020AO1xVAd6otl4OffwufQbAqk7wgev7Lfvw4VHIDoiP21
+2TNXOJX6l7t7+cdif1Ptp9q2S1WaTPdqUC0RkuksvnKwfMpo7XE4d+74tNdgpyUz
+4kU8Sjkjdd9SdyQuEVuAY+K9DUXPF3G/Kz9/8tt1na3Drv6BQIMakRnQgpLeJcvM
+lITx6JE8IycbTLvfKg9wNffRX6i2d99RKvhW9YkVv1pZa9mTWcY+S0dLzQkGeiGH
+LejGSG/AyHJwKK0sl1Y0BXFHH7ZAk5xngtE974vYW696FWiDj9zoMy1xktvsQ+M8
+knXe2Gw8iwyYn7MR5rqNXyImlciwTqM+FRgHXVDldPzs/243XoiHlumAZzFdhTvw
+boM5AbCWYRf/Nu7QXRPuPlD7RcIgDjARdhU1kKdq1jPh2IgOQhNOYE5OmqCwvC4e
+pJTgpK1aGHRa7VJjh45KQD8fU566llfQLx5uC/n1S/62b6ZHycAU+McWFGJZ3dga
+XJ27gO3PB+Sa7jcJMak=
+=9k5I
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    01D734EE5EE9C3F8
@@ -7986,31 +4338,19 @@
 CW4f6XTMD13AY55YnAhMuEkqFxg+hQOSLpEy5k0dc8Z5hJMURS383a6x9qwGvJ3y
 MaXqm6R41aQy6wVnKZ24ZcXXQru9Dg6jvql65WGLDomhNyxGnq15Xik/AwARAQAB
 tDpTdHVhcnQgTWNDdWxsb2NoIChFQ0xJUFNFIFNJR05JTkcgS0VZKSA8bWNjdWxs
-c0BnbWFpbC5jb20+iQI3BBMBCgAhBQJQXaqKAhsDBQsJCAcDBRUKCQgLBRYCAwEA
-Ah4BAheAAAoJEAHXNO5e6cP42Y0QANbFQns8603eSwTkHXP3Zsbp1MKe1LkyiHD2
-zScs+bt4d8KwVrc9D428UtNUIaPultm+RMwXBGcenTz1BkULL1loE8ZKJ7UBaE6i
-SGCh1hq+eIHC5iMPhq3/KPAJiwFAbLGopK2yh7R1GeC9Yx5jGDglzqqngED0P75b
-5Dok7xkMv/oV5f6+FMYImehnd3q6XYpTcppOc6orUBgMEmDu69q6Q2FceCN0+9po
-4+E+bDt2BgdcdRl4zrnf7+d9QPp2A4AOeR2amIlUnfcUpCCxSe5udMLDkb6rkD9J
-cNVGDc9o0QVVCiCjzwhhEkzNrHwTppJLTFJ2XdGAo5rczwW59bXhhePowXRgvDJo
-4488nU9AENZngRcm9TM5S4UJXav4jejX+uSwR0hf7ejZ1HoATHFVx5kqSMSE/5Kn
-i3M5CbpJ6sGHPEgBDAEbB3ZrBWY6PBIXHFn6EmI/y/5Y65wKs6n1bPCuNhOCxFgF
-U/aUgv1mAFQtkSPKi1t8FUugGd7C/ArvhWD0uxDUBNtcoKRNKmZZznRZ5LYSXVRI
-G7SjHbVqi6B5TQH6nqH/z3byqCAsrhrZ4rufXtZzthTXy8kkQmj7xq0+qFVoChTr
-cw20t98cunZlrx4/qK/S0Fo8Zkt5GmFl1VJUWZ/Dv7Yc0iSJj2zxs+herB6UsgiO
-AaYgqY9/uQENBFLmWO0BCADRaHsvfoCCflQSg2DmSCube7NCd35oGGrAbfrjsnK2
-f1JmX49yOdNouJ2FbY8HViWRadCy4eBvgWu7izXU2BIkjhKMI3Mcga9rb3JPfMAx
-gipC5InrK4NCFDOemRgalL3PoWpXvmyqE1d/h8Fn4fCLvqUTx5TUVBqS05wMGrUH
-9d7CbpH0Q/L1ynY+HpvQrTI2027FQS3SVzT0c2Ve+c3x9Qzhh08bx2Cg0bFSlJjG
-Z+CRPoCeeeXSGMcoI2KAeleaEFSMuobnd/XYYapj3gkA2NW1LuFSUAVSWYeljOft
-ZqrnOkjrSfQlieMzpO4VG1rLWpTpJ4qQMyCDFw+Ndfj7ABEBAAGJAR8EGAECAAkF
-AlLmWO0CGwwACgkQQTIUkHWKrW8BlggAjPK92Zr69T2by+t9nszYPxrhR3OXeEti
-aBBHlnoPKkywE5zrnS+LrQQxrNWUNBdm2y8TvQ355+Q7scwQoeDlJrULu0Fs0qmQ
-gHeiwhdoVVP/RcuzU8ADvSSmk2KkpFL3GtoERXufU/22GK2G6ByCqCd4E0NL03uc
-ausmNiQCPsLxPG4ocHRjcCWJRnRRT3s5LJb6gMWWHshW5M0+Hh5gLCyhsGZfWU1l
-aYLfX0dH8BIM4S3wNlJ0m25avC1FWZ2isGe4CORxPu5FqSl2kRJMAth+ONMtCUyl
-P+t9TvKivn2hT3qcum0n3o+ueciTXQ+zgQ44H/yywD+N8NAZVjhkhg==
-=Uo3E
+c0BnbWFpbC5jb20+uQENBFLmWO0BCADRaHsvfoCCflQSg2DmSCube7NCd35oGGrA
+bfrjsnK2f1JmX49yOdNouJ2FbY8HViWRadCy4eBvgWu7izXU2BIkjhKMI3Mcga9r
+b3JPfMAxgipC5InrK4NCFDOemRgalL3PoWpXvmyqE1d/h8Fn4fCLvqUTx5TUVBqS
+05wMGrUH9d7CbpH0Q/L1ynY+HpvQrTI2027FQS3SVzT0c2Ve+c3x9Qzhh08bx2Cg
+0bFSlJjGZ+CRPoCeeeXSGMcoI2KAeleaEFSMuobnd/XYYapj3gkA2NW1LuFSUAVS
+WYeljOftZqrnOkjrSfQlieMzpO4VG1rLWpTpJ4qQMyCDFw+Ndfj7ABEBAAGJAR8E
+GAECAAkFAlLmWO0CGwwACgkQQTIUkHWKrW8BlggAjPK92Zr69T2by+t9nszYPxrh
+R3OXeEtiaBBHlnoPKkywE5zrnS+LrQQxrNWUNBdm2y8TvQ355+Q7scwQoeDlJrUL
+u0Fs0qmQgHeiwhdoVVP/RcuzU8ADvSSmk2KkpFL3GtoERXufU/22GK2G6ByCqCd4
+E0NL03ucausmNiQCPsLxPG4ocHRjcCWJRnRRT3s5LJb6gMWWHshW5M0+Hh5gLCyh
+sGZfWU1laYLfX0dH8BIM4S3wNlJ0m25avC1FWZ2isGe4CORxPu5FqSl2kRJMAth+
+ONMtCUylP+t9TvKivn2hT3qcum0n3o+ueciTXQ+zgQ44H/yywD+N8NAZVjhkhg==
+=e7a5
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    02216ED811210DAA
@@ -8030,52 +4370,42 @@
 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
-bC5jb20+iQHUBBMBCAA+FiEEhWnJXK3FCLCf6Q8wAiFu2BEhDaoFAmADx6ICGwMF
-CQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQAiFu2BEhDap9DAv+IBXv
-DyzXf2/L+GuSFFr7OB/s1BFtZy9CxMnD2BuDlKEQsoXyFUGP2Jl7sWgxzbJfJIst
-6fn/BfJMrm+yGE22mCn/5RsDbJThBv6WEQqHnvQ4n0c7+5ViK4/+6UF6XIL2MasX
-6GJbPiX3kgFhFh5JFkeMlpHRoAfIyaDR8Qi5goiz3WhW0ce28Qlfk4C+UvDRQ/cV
-qSoOAmlg0f8TGvwmsCMzYj/ezydGbZC1UdSGkOOApaUDd4JOfH9RDYKwH8iOhtse
-wYB/bZziQjfsBtnwkvLf5g8CIcLYlMXalGx1kWnV0XrMaoRlfixkDanho1P3eeKF
-2lat0IS488NIjY8fWdjo6Gdkl+qulgr43nWFW29nFcFlJZDMGhBNM8D2IHN0bCDl
-MS1SEVu28UO5H2+kijFjturRE46oiBDHll481glxZhI01nlVEei4UjszodDkdHZ6
-4pqKumd5vUNR/NmjnqiG69eZV5MDNua4ZZf2tzIkxrbwpn/xmdlzEFj66EC+uQIN
-BFf0j5oBEADQ0hDASqehEfOsvrnXynUiz/YDr+oT7AIAh4XLwbJLPosXl/VPSpCu
-74/9ausKHSn/aBG23cpB4CCAdZc2NZe7u/24gLUIU43RGT4f3bd746IKyW5VBAtK
-7fn2Xt9uaHIqHEuAbfSO+U7ZHZ1oJBOLjgSbEpZaVG6nywX2EefM89kc9aZAakU5
-JiRXrVTWBK2ItrVm3iefRo1De8NStxocamzG4MeHRefq4xrPiiPZOo5BnnkB6oQ5
-PhVO9KC1S7h9g5ovSBY5JSphmtwVO1aTcbw8HaFe7bUWBAXAZzzpi9uEp+F4XtjA
-wqoCcMMqIXfa5PJ3lexrWr1Xmk7+m584tYiUYA5XedRxVhvF/sueYuXGFmolx3Rz
-gBtfrd+UFK3M5vfrIJIikpNSCE5RBbH8EZdejUwjBs4z+ZBgpv1vsssrwTMOwpBD
-UHHdt3zLHWkP9lDWCWrtj6ER/5viKEXoSHJhNJ6Sni1Uw3mTQs6Zn2NfE+eNomyu
-PZHY2Yiuhv1cgYSG1hBVbuS5ffvBm3DvbnGGmnlpBUvB4NBWWnzJOfVcSoiAii2e
-YXvwuH9FLLF3ckWFOguQR/FEwkC4NM/Jkki/lm9ohVT659/Q6O2ZvK8uiwQpnwn9
-6SPfUP9VWiw2rqK6KCVd60OZAiRIgHqkLSzr8qFyHsdVW85UVF5hJQARAQABiQIf
-BBgBCAAJBQJX9I+aAhsMAAoJEBZGsBuG5QMQFFEP/2nw9Cix4wpR7AP3RYlvualc
-yPJDzB8DtUHFI3l6weiN0pbjk6y0j4TrKEu9sgGlPYRbqvIPXnJha5d0pKf1OAx2
-V4qBJqM8mUae3lbGh03sJX2jm+oO/r+dpLWHaJnKdwTOb/6T2Qpweay6x9imlFm6
-WOlerarQh2xe837Q6nARDSQ134YgqktfSdPBmRYwxKhg6xI92BJhF2TDez3FxCEj
-yM+jWCOq8OMlR/AF30Fur6T85snB8CNELFVtjUHXGes2bfqYZurITVGzdog3YNwS
-SApiL0H/KoTt2zodAcHNdRSx7jCrhlcZ1jfNh8iLLlAe1gywRxDrYOtWX9ZwBdT0
-CudmLkM9xFiOCy/Tqlan0k2NSY6iH4/rtT2NlB68WX8Zc9TPKV29YK6M4ykOWjag
-Ska3DGTce9YrUea79fKhoxkdoknIrv8Woe8wK6BEK8e9iI2LdN7W5P9NOwr1JVKh
-SBQfJGNW231/CQAPk8XpVJ4lEdgmPHqJuR52j6ShN5Bl2dI1pLnW1m+7rlnFtRRP
-2zvlbXsaQS90qF5sG1TBpySBIlL8vJuns9JMy18yf+CuG/GwzuDYxV9qHCncywPP
-WfeSnGVMRBtDu5GgwrtMdwRIHrgKyS3nA+Y3tv9h1wHb5DgDlIuhnPy4ia7SJ3NL
-1BbHalglCuQKPzCr3TBVuQENBFYqhSkBCADFzmSrmnqV2PRIUE4iWrkigVivGXmz
-rJwbpvf8huSs6LkyClIW78JhFchMtTdAs3oAKfFhjI1xDNur9afbB9ULip7K2bOX
-1dJ4chi2Fz7eEnPIypfckaMa+BrFx0v+ssXDip+VSmpvdDGBpgkGnfrlpOF1YmNT
-zhV9QLm/0B0wgjwmUr+/c+uSqbsrfTVUSOQFCzhSSDOdcpACe2bFpzYJ6/XVTjFQ
-Dji9XVI8zsxSXVT6BLx53fZjkWsjAMEygldsZa+Ohu9r7k7sZf6XoML1aO0Ojnv6
-Kk+S5zFMrrSjEf9DBnWL1fWDJT6jtrR0QO81GNQBPsvo5WLOTdpEegG1ABEBAAGJ
-AR8EGAECAAkFAlYqhSkCGwwACgkQ8N8h0dCjw4TFnwf/aKk6r9sJrnOIrAt0MXFC
-lSrkaN9xPZwKZDYIObiwespv7Jzj2+F9xY0r+OjUpZga7F74fcydM3EGiKLOHPme
-vqh2teJXv0UnhXy/WhiqZH2eoe0pQ01WbF0sh5AXZijye7yIeoT8jl1yaB2tW0tv
-rmzPPhY4aNr3Bl90X9futRoVbl4d59i6MpxI3AhJKcmOgq9OPCZm472aWC/rfe+S
-ozBx7nYZ1jJfuFe6x2Idj3LaXYuesFvkAj5J85s7tHWqJP5luhD++s7tVwk8L41u
-HO0w9sQrSysk6HmXDzwB9/eDDMKGoTB3Uw4tBd2xwDxR9p4Bks7+leXL5ogr7RL8
-tQ==
-=Kcmt
+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
+leXL5ogr7RL8tQ==
+=FOe2
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0315BFB7970A144F
@@ -8113,49 +4443,43 @@
 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
-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
-=Xdp+
+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
+SgeAjndCE/Pi
+=h2M8
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0551FD3684FCBBB7
@@ -8175,32 +4499,8 @@
 2jXm3CeHEvzRq06/m/Kei7kuh8mKByMAHYP0S9xvihuT7qZop35tf/vhSFMvibyt
 nMyU0jqpNmO8GL3y5BQy+PzfocKOKQHP65cZRfLa3U9vcXSRMbPMeshck9dDRXbe
 BMzNvi2XKm+ctDzgrXRKzp92VX6rvr6Xhe+ovqBVXwbIRnvakO7dwwuZEwARAQAB
-tCZUaWJvciBEaWdhxYhhIDx0aWJvcmRpZ2FuYUBhcGFjaGUub3JnPokCHAQQAQgA
-BgUCVqvVhgAKCRBXka1hFF3iO3RfEADBeq6el7WpQd7BR3UJieUw1bp+ZZs89uPb
-dNgsXoRrOxtJ+DhKXPUOm2xqa0sIAOFNsrr8d+bH990OnhSoynPA1Zs++B4KokZl
-wrWEgC9rYuVLABKHUXDVcfhGSHvK+u9pB6I+eOa7/QK9DKLPpZfEVfRJ42CmYmcJ
-KEy9yaNlu6ye6k+f38yJG/2Gv581an5IwkCAMsbtIHjApe4LVawXj0+kOSc+mRSH
-Na+0Ddxr/kq+AkBtsej2biUs5a50VLCIdCJqk9rvv4Vvmocv2P/h5lpXIbsy+oaX
-H18vNC9mnX8TD1h0zSTVV3eibkepsTfBvD+2K5ZE8Lbs2sFDs1DYjdzih95M6ZxY
-+GWRdWaygukQklUtqpWwIXXklotV4MJY+u4VjLyGbgp+Z14OsMrmo4W02IYdvpTt
-Y90V/tHxxvz5k21MObtYier7w5756YvidsRLkbVXA5vLDw/Ih5MqKQ2clAoN8kFA
-eSLhcC+Xpbs3/jk0IvI4NhQYEi7EzI+laHziDqceJyL5xwahL1vWau9bvt3IvayJ
-GWF/wVU/O36KVS6gjF+ZGoX8uCN69BW/x3NMKJ8WhyU0z0y47HtAjoFrbdO5kwqa
-+q9RIJMekdGsLIoXbKk2nY/yZ6igT46G5zu3ft9WmGMu0zxvYlafWp54aaih+cNF
-izpoju6dBYkCOAQTAQIAIgUCVJIDMgIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgEC
-F4AACgkQBVH9NoT8u7ej6A//aeCnyee1Pzd8lZHagW7mucjcvAHrhmRh4pHsfhej
-NEgf7CGY5cvrGs3amufAFfiXBm0TpqxCWzUBLYK1NBEr/FWpvLCL5KvpIrtIdE1n
-sXdWkyCRyKBRoXRcjuUiM3moGCKgVJCAAZejzAeWu4pNs/NGN1zD9BNXBo+J9L4t
-03obc9WHCm4PlA5acI2PiON4j/v4YRjnxbaRKmEEIXrMUiOhdASSZYjMcTgHV9cl
-TyXPxQyy4TYJiEIhpg/Uv4q4AyLL+1RlQ4UW+dWmQlr105q1NjTZW6ach7sx7kvq
-kS/C906qfFBGmKRAccTpyx2c5ZrXUV7sTbIFokRGj8G5C8BjjSPvB97anh3sRdNF
-qlc35g0SEnRve8W32s98J8xlzLn+tBNwgqm1QjUK9+zmzlrk5+7wB5tKlJY8Wbz+
-fyS0R+ouCaESt96MAkSBSNv4liBrT/mCdXEn6Ei/sAzLhUf4+4jlM/02Ej2kjkZJ
-EXUFXZTgrSHxfwcVdD2+JSMQk+0KtK1zr+fhFYv2M+fA/dCHcZjJS8WsgOC6foQw
-uatgSvh2KTuSdzIlgA+0kJ7M16MQxT4Bs+6LBmdrKBC7vCxcdlczEe272ifvKn2h
-OiwCrGSwXF7SDMUUjZlN7f42BJ6LbXgxs/Fs8wJz+cbZRZOMWW9hvmTTxgKM6Kwm
-k8k=
-=0QRA
+tCZUaWJvciBEaWdhxYhhIDx0aWJvcmRpZ2FuYUBhcGFjaGUub3JnPg==
+=02t4
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    056ACA74D46000BF
@@ -8219,32 +4519,30 @@
 5KowA/9LeDjhl3tKizJn5hKf+NR8kTMcFFVMk8tf9/ZdqCG2gVTuB0EFimH47j1+
 YFWftvKg2IwF0qRnYuhpXn3kAtkzSwDr2T4r5CpDjttq+oBwhJ+N6lcPRoU26ijr
 nQ61Ek0jFFE5vfU7UODSLYXYbjf8McM6BtksY1SWfFBU5cVzgrQhTm9ybWFuIE1h
-dXJlciA8bm9ybWFuQGFwYWNoZS5vcmc+iGAEExECACAFAkoo3BYCGwMGCwkIBwMC
-BBUCCAMEFgIDAQIeAQIXgAAKCRAFasp01GAAvwh2AJ9HdFLGMuAGkk/wSv6dr6gM
-Xw32iACaA/Ez8jdn6/8kp/RpqMtUnl9c/Pq5Ag0ETI4i3QEQALvWXtxt+RpCkj4B
-3f6uQXlEkVaPlmNlgG3ELrFV1qXUhjawWc++KrN387qg1DQdnLOtZMTrrLRHj/MD
-Kk/++Sjtiebb/sW5szdbD8pVKoRWlKHvq5LchAET7Am1td6w0ZG21EEwHYdLnSe4
-w87ksif7ib1YpjeXGsbWmMGMJp327srZxkJtj+73A3jjP8WTECWO6Mg4WkCHp6kR
-V9nC47VrP6PY+s9hTCavItnvW7QouOdakKkQRgD1A6WQCz82hYCAftpW9dM4KPER
-KN44tPo4BdX8E8p6Rp6OFIpEhzu4m/f6anZfzkLKshnbB2W+Ts/vlkb8MmipbyuD
-JS1f8ZGMQcU176z2ZGBVLeAP2so+gVqy5sqO0Mr34G1vjuhpMLv4UTR6RN1xMaRR
-ogvygRkpzjArLlXFPtPPNeHMmNU9vWZcXIyM1DMC4BJxtxqzVwFajjfJW8EdZPfZ
-BSNerW2FFyOJrSVUU+4Fzf2fMgmmUzBnPu1EjudkcT++Y4Sdq2eR3g2XeLdcsb5C
-Iexw7RqfT3zcUg83hhPhQlEYryv+2159CicNFd15Tap2rZtarW2aVAGHUWaeJG9V
-hRH2LIL6t/rUMKp1YwuhVvW3kgpEEz0Tb9nOzaewcBWDknWf3GW5muU3hgJngc7P
-m2UkiEdeFavzniwr0ASG1Qdb20iNABEBAAGJAh8EGAEKAAkFAkyOIt0CGwwACgkQ
-MUdOXnxrcDTgXBAAj5J67TlGo222NjC9qTX+b2wjB4whso/jXpzmXKvl+xt4rR04
-/VIt/TW/AHNAOorUHuEkpdGD+ymJcDHlgQByPCcDkYp7EHdkZL0TAc208tpzYs+K
-F+5FAujyWtnryQyEXjFzpprb5Ijt6qP5TQIu9GEM/l12UKdTdMbqyXkrhkAl+Xmb
-pFEzL0aW6duJ91dHxNK5t3TOnjhptmrQQF+IzonEeAU2X69qRmnvsa64SfoLz78z
-Xh5qufF/w10Y/QGk99+isLJEszs1RbvZ0QmH6L1lda6PAUIkWKFpMnCEm/RGyODn
-Fn2cOiyR2Wwga5J0UD84ImVLLjgoRy1Hvnq9oU35o3b23XlOdedBq7l3XhC0vqN/
-L5V5gaA009kaTZX+/tQV6UmWlBn7b6nkT7+eNMxynUmu+tn255qcLqiDdMxcq9UY
-N3bSv0WWViQ4Q/F44+3AIStn+PCK1dLoq9zYpI4SzfPhspqXkC3F8z3sPAiyd4l8
-fcItwJMkicr3G/tmqyCSzV3p7bkE2y+QvN+CL5zff4Z2ThiipVigsOq5bgYpOTOi
-MWJHHW2bXmHJfbnxCoyv4fmq7Y87XsE5Su8LDMXmaW9RW8CH7XssTBLgsditGWd1
-5Q5Dd+TeWgNkSgRMWTt6m2ME8F0FHgPkVGiWPY+RySXSQhi1K0U9gwYJSsU=
-=u2XN
+dXJlciA8bm9ybWFuQGFwYWNoZS5vcmc+uQINBEyOIt0BEAC71l7cbfkaQpI+Ad3+
+rkF5RJFWj5ZjZYBtxC6xVdal1IY2sFnPviqzd/O6oNQ0HZyzrWTE66y0R4/zAypP
+/vko7Ynm2/7FubM3Ww/KVSqEVpSh76uS3IQBE+wJtbXesNGRttRBMB2HS50nuMPO
+5LIn+4m9WKY3lxrG1pjBjCad9u7K2cZCbY/u9wN44z/FkxAljujIOFpAh6epEVfZ
+wuO1az+j2PrPYUwmryLZ71u0KLjnWpCpEEYA9QOlkAs/NoWAgH7aVvXTOCjxESje
+OLT6OAXV/BPKekaejhSKRIc7uJv3+mp2X85CyrIZ2wdlvk7P75ZG/DJoqW8rgyUt
+X/GRjEHFNe+s9mRgVS3gD9rKPoFasubKjtDK9+Btb47oaTC7+FE0ekTdcTGkUaIL
+8oEZKc4wKy5VxT7TzzXhzJjVPb1mXFyMjNQzAuAScbcas1cBWo43yVvBHWT32QUj
+Xq1thRcjia0lVFPuBc39nzIJplMwZz7tRI7nZHE/vmOEnatnkd4Nl3i3XLG+QiHs
+cO0an0983FIPN4YT4UJRGK8r/ttefQonDRXdeU2qdq2bWq1tmlQBh1FmniRvVYUR
+9iyC+rf61DCqdWMLoVb1t5IKRBM9E2/Zzs2nsHAVg5J1n9xluZrlN4YCZ4HOz5tl
+JIhHXhWr854sK9AEhtUHW9tIjQARAQABiQIfBBgBCgAJBQJMjiLdAhsMAAoJEDFH
+Tl58a3A04FwQAI+Seu05RqNttjYwvak1/m9sIweMIbKP416c5lyr5fsbeK0dOP1S
+Lf01vwBzQDqK1B7hJKXRg/spiXAx5YEAcjwnA5GKexB3ZGS9EwHNtPLac2LPihfu
+RQLo8lrZ68kMhF4xc6aa2+SI7eqj+U0CLvRhDP5ddlCnU3TG6sl5K4ZAJfl5m6RR
+My9GlunbifdXR8TSubd0zp44abZq0EBfiM6JxHgFNl+vakZp77GuuEn6C8+/M14e
+arnxf8NdGP0BpPfforCyRLM7NUW72dEJh+i9ZXWujwFCJFihaTJwhJv0Rsjg5xZ9
+nDoskdlsIGuSdFA/OCJlSy44KEctR756vaFN+aN29t15TnXnQau5d14QtL6jfy+V
+eYGgNNPZGk2V/v7UFelJlpQZ+2+p5E+/njTMcp1JrvrZ9ueanC6og3TMXKvVGDd2
+0r9FllYkOEPxeOPtwCErZ/jwitXS6Kvc2KSOEs3z4bKal5AtxfM97DwIsneJfH3C
+LcCTJInK9xv7Zqsgks1d6e25BNsvkLzfgi+c33+Gdk4YoqVYoLDquW4GKTkzojFi
+Rx1tm15hyX258QqMr+H5qu2PO17BOUrvCwzF5mlvUVvAh+17LEwS4LHYrRlndeUO
+Q3fk3loDZEoETFk7eptjBPBdBR4D5FRolj2Pkckl0kIYtStFPYMGCUrF
+=kgOG
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0729A0AFF8999A87
@@ -8262,17 +4560,8 @@
 p5f8alSNjhKp8jRpxljwnmNJQir03UQyfaOArApodCt4zVAT8jc9KXEaIVbZOSJY
 eztmP7C8RiFGtNwRU678guNDRE7pWFmqRmjHyAWj/VU85XcwebkOh+qQvY62fxCf
 oYmgz71fArs8978AEQEAAbQjS290bGluIFJlbGVhc2UgPGt0LWFAamV0YnJhaW5z
-LmNvbT6JAdQEEwEKAD4WIQQvuinQjS4l7oTBMsMHKaCv+JmahwUCXPLieAIbAQUJ
-AeEzgAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRAHKaCv+Jmah7iGDACvleun
-ry35O2z3kGLkPe1o1FKsxKd1i+R9TgtO14+dEcKl6FZxGsRuwnCPy/lh4cID/+J1
-zUCUOz5SK83jfUpjiis9R0PE0XwyZWqdovAD6RgkaC45Zpi3vzeYNVMKo4ivyn4O
-xUzg4VqrBspjN48W2c+zxA3CY4mueLF1tMeTH8uZVs+4xjasr9WjE8MzOBImdRTP
-68MpQIaR1+ZpZM/dDIzfs0JXUfAsi8vLtAN1qYWt9vSn1cKv2ziaTCelN7nKKBFe
-eZiZEJUfIvCKPcwV3J157iH7BMRHSe7xwJhoB9vPDNTTiiPck5OP9dHW1nQkP5Io
-QaPOPX2jsJpvZUnuf9J7PqUqxgUH/FDwaNcx3SOmsLIyc9Ix6lzuHHNkrZFtIydO
-6++j09QM5NReAG4Q1MFnZok1MaM3KVKAPGtEbEH4V5YKUovTCtmpXSsDSmibsp+d
-yHNoa3KZnPRejKHrUIgLapucCbcsPBONWBw50/0RHWvM0cCMzifMZAtthzI=
-=odFf
+LmNvbT4=
+=Qi7x
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    07D3516820BCF6B1
@@ -8287,15 +4576,8 @@
 J2DQeLHqR/tsch7S01hGpPAptBeu19PyAlQsntYN0yLCLKoe9dFXWCDkvd1So5LF
 6So+ryPqupumBbh4WxCmTp9qwDJYJItjAE0zyPe890FurOtxrFTwtRtX6d6qGKkY
 /B4T3r0tTE1EiOUpmSnxmGNItMh7/l5UtnHjABEBAAG0H0JlbiBNYW5lcyA8YmVu
-Lm1hbmVzQGdtYWlsLmNvbT6JAU4EEwEIADgWIQRjXuYnNF88HdQisuIH01FoILz2
-sQUCXceB0wIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRAH01FoILz2sdoo
-B/0YUh73jUMl14MjWvp9zrFHN8h+LqB4NMQcP93RdPTtDKi0a+0h8gQtm0D+K49Q
-BQbFztOObfZS3kdJ3VOqmodScWrGtMU3HsYT2ioQalqbYvl9FIPDrlOjHaZgwgyJ
-We0DVKHRApbtIh+NxTpQUJtanxgF60ZtOoToZe8XMGc9LaCZcrFxK/AlMdDMgUCx
-qzBbXhAcvut2bJVL5B4kLNMABrbUuFMjTNI4JxvgTXKL/jNk6XPtCjdmgIh7mT/G
-Mpu9t3i1zegAPdM5N/MAgiGHqm+blANLniSAbZja8Ny7211fwOYoJ546VPwDjL7B
-rBlymB3COoYZhql2DcBBg39c
-=XhqR
+Lm1hbmVzQGdtYWlsLmNvbT4=
+=ujUI
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    083891AD4774845A
@@ -8385,19 +4667,8 @@
 leqFkpisK4Iz7oQPzQrKj4X6qZLuSkFhDn4vsbyHDNh0rx7aPkiF70Qkr+gHW7fG
 rPhqKP8dLWkxe9RDiGQvHozy1Tmy62ghPUKd6swOIfOQmt4pCoKKu1L5TQARAQAB
 tDJUaG9tYXMgTXVlbGxlciBHcmFmIDx0aG9tYXMudG9tLm11ZWxsZXJAZ21haWwu
-Y29tPokCOQQTAQgAIwUCVcRG1QIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheA
-AAoJEApLND8qVf2uQKgP/2AcRegUEiMKJfAp/Rs6xJozM/4JoMTcT8CWYYLy8bFm
-3uzd1QufkmKZXdmybMN9RUwAc1Y/9sNlPSBu/6WZKS5sMa7YeLQjVn0xp8DnZA30
-r3/kV1Lep9Krvn42Xp3+UGm/nu20fWJdE2OkSGxlpbTgau2k00oKVYP1Ms8BcoXs
-LnjYqk7Hd4zf9x/TfO2TpOuSLirrbH68h8VlcdXH/734bYzH4QAF1zAWwcxIyoIL
-WqQ5pdZAB1MGeIbyVboVLnRWpwiPzkdNi6xRS+UU/+VgJ8A1zh0lUj/97YU9OZTc
-PtlWQXfxvSfN6zOfRnwtoiACaQqb+vKOEa1MoBlbbbLiQOTZzmbqYAixTlFUYX73
-z1zZHNk7So0OxoTY8MGyTaIfUQHxMhIFwrj5KUxbXcpGlP/AViISyZ7C+HWA4X0d
-839YBezIFhQn5cbwrcao3IrN/A7q2H7E8U2w2Re/8SAGLKNcEKCNwsf4RhulsuOn
-LuTmUGaRHv7Pw0BsJOMqDJMZaKsDz06Hsy7PWfprDaDKGWg9ZgQw5x0OZMW0D7Kl
-Ufuq1141PeHiBpxdYEZ2thGchTgV9yMrKHBKiWNXgarieW2G5ZrlfOiN8liky85s
-h5/jPIgk8R+MY/NM7eAk5zH2UlagH59GNJWsG0f8TY0/U1bA0akVo9rAtTqiDCgZ
-=lNda
+Y29tPg==
+=v8+k
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0A4F46866E0A8DF9
@@ -8413,28 +4684,21 @@
 OjV95bMxPuOMMcXuGPynxsuaTuQdqQBj1mnFd/vdHqx4otjucLOn06OeEUxu7IeZ
 qrcY5wHCiqQKWHCxmCBZAkHEkUnMr4cPviHgT0hu+NjCbbVja9XE0kVjQjxUtylR
 cl1+ChknYjxT2CgaEcNJPV8T7tP6DfpGUVZFABEBAAG0G1JvbmFsZCBCcmlsbCA8
-cmJyaUByYnJpLmRlPokBPgQTAQIAKAUCUUIp8AIbAwUJAeEzgAYLCQgHAwIGFQgC
-CQoLBBYCAwECHgECF4AACgkQCk9Ghm4Kjfkcegf+L+An7y7dWk+Kzgh/kexPwMZ4
-nZHzfY7f8eHYUmPfU1ZuwcFNwZFecqlvAz43eVK3Y6SRdtowwMYrNHmQMX30adZd
-TNviiVFysGgJTiF74Sk2Cd9lHHtJRlD8W7zykCvulZu8uwlS4zhBvTE15Hak0Vhw
-JSXkn+z6NYIRrCFwefK9hSjzZ1UVW23AgcYhl0WA7PL01aE4UezvIKLF2eudPRbM
-9ssRHgEfnBDnQF3DjEYS96CQ6xoXSxLJpkKBsX85m9zW/WIBKV+QBxHUHZLP+/vD
-EliOv+/RVZI97By1zOnFTgH93Uan9EJiFuJlXFTl0QSUpDDxkIu+pIlroSiaZbkC
-DQRL1d29EAgAiIFH4jIUnuOh2SyT9a/ybUrIxM4JgxTexcHZPXAms64Ee4G4e9BZ
-AdIDOGJYX43WnzgalAWHGbU8+JYyS9iQW8CB/A3M5noutQR6OJS/+2IFeWBvk09T
-UfowAwjo0bfSkuxQHhbj9cl/q3SJ8vua6kqrOrdvK2XDHgq45Ps8JUoexMUq5vk9
-lJ+2oQasMIol2HWP/HDXke75JfyUzomlxOglhrTSCv3Oh6snsPgEeSLO8Zu0bB+f
-yyMX6B6WSybdtz0AH8GIhgnvPbywmPGBXDiKlel16kBOTRz78262xuQDkWnw1TCD
-Waao6qVzOEOxv111FSxtnGvRhDDkeFsozwADBQf+KQI5S8SCwEB7+DTA6dY6JjQc
-6kJiEK+O7Vcree49rdNe75066WkqOLq9KGZXdEePRNQS1/HlU7usFyNub0m1etl/
-vhScDhu+9hJHSMHs8jCTs9X0yGS47fh53PasCAnFl+xr18UtHaiy69WQiN0E4gM9
-JHREogME0sMCRo49Bst6BPIvQ8QENtqrfaF86Bk9zzTUY5H+IgxQu8jCL5EW4m1u
-xl5heIqFv0MVR6Xho+Fv+0ixVGfO/nPRyOwKFIhW6Fzp+I0srnT2KVrM55JHYvUC
-fwSTx3Q45WuHcteguI8y1a0aIM3tY9RUA/+emTM9+DM+cz3ebBmimMlzqK1Rzohh
-BBgRCAAJBQJL1d29AhsMAAoJEEZsrtbgdH1QV+gBAI6dAR720GUajYzG6NqeJbX8
-YOJ2I3sITQiQEryBfohIAPwNwLrlI8UW5IqauZGwARTTCGIT7eN3ozh5reZ7/pjn
-uA==
-=ePRn
+cmJyaUByYnJpLmRlPrkCDQRL1d29EAgAiIFH4jIUnuOh2SyT9a/ybUrIxM4JgxTe
+xcHZPXAms64Ee4G4e9BZAdIDOGJYX43WnzgalAWHGbU8+JYyS9iQW8CB/A3M5nou
+tQR6OJS/+2IFeWBvk09TUfowAwjo0bfSkuxQHhbj9cl/q3SJ8vua6kqrOrdvK2XD
+Hgq45Ps8JUoexMUq5vk9lJ+2oQasMIol2HWP/HDXke75JfyUzomlxOglhrTSCv3O
+h6snsPgEeSLO8Zu0bB+fyyMX6B6WSybdtz0AH8GIhgnvPbywmPGBXDiKlel16kBO
+TRz78262xuQDkWnw1TCDWaao6qVzOEOxv111FSxtnGvRhDDkeFsozwADBQf+KQI5
+S8SCwEB7+DTA6dY6JjQc6kJiEK+O7Vcree49rdNe75066WkqOLq9KGZXdEePRNQS
+1/HlU7usFyNub0m1etl/vhScDhu+9hJHSMHs8jCTs9X0yGS47fh53PasCAnFl+xr
+18UtHaiy69WQiN0E4gM9JHREogME0sMCRo49Bst6BPIvQ8QENtqrfaF86Bk9zzTU
+Y5H+IgxQu8jCL5EW4m1uxl5heIqFv0MVR6Xho+Fv+0ixVGfO/nPRyOwKFIhW6Fzp
++I0srnT2KVrM55JHYvUCfwSTx3Q45WuHcteguI8y1a0aIM3tY9RUA/+emTM9+DM+
+cz3ebBmimMlzqK1RzohhBBgRCAAJBQJL1d29AhsMAAoJEEZsrtbgdH1QV+gBAI6d
+AR720GUajYzG6NqeJbX8YOJ2I3sITQiQEryBfohIAPwNwLrlI8UW5IqauZGwARTT
+CGIT7eN3ozh5reZ7/pjnuA==
+=0lC/
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0CC0B712FEE75827
@@ -8450,45 +4714,34 @@
 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
-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
-=76Tm
+dGpAYXNzZXJ0ai5naXRodWIuaW8+uQINBFTi8JIBEACq+dSR6serUWrem1itiw0M
+slItsFyHuOV0+K8ZUOLRge/arBSfGjk7YZPFzIMVbxXo7LYiciHCydZ9K7HdqCqy
+gC4k2IV+85Ll07ZfraPHa2vfgXshu03+VZcMcp6Jxs+UPlVHV7SE2R3o2w+KvKqz
+LLRLb6aBREoJCsI60HTWyPjsHiHraJ+XFNl0LT22tIPJFjOTeVKU/8OMBs3O5ql3
+zgdMG3DFGAS2ALiCb1wh+YgJ9c8TA44R52Jp0z1XUYXvV298FzHD6n7ejwif2MNU
+kLF7oFfSknQLkAw1WuqkwYn3QYocfp8aW5u3139vWWR5V2yLWeGI1+/spTJqP8eX
+BnF+jPWuig/GkHGrWCn+MT7Xv8TT2wR4rdhetkYPnPNX0ra+jURZbie6tO/C5OWT
+YjurTSzBDiPxNLcxxUNjrOMzIbcLLhSRQ0DTFLiC56D+5UvPIUY/GiX5O7x4iF1k
+wSPcoXz1w+xzzCwfFZg9oE5voHAybrGkTFCIb5Oo+WKWDCY56K7yHLIUT4UmiF2L
+iaz7gesTc5yFSFJhP0WpkVX6FxDuoCryQx0L38qD+4c445N7aUfVmqbOBBp4ORpJ
+/w0s8Rb946yQ8TTUB06otovyIz1iZsuj0yU9kzZYovrZpKJLeDEY2ThxdU/O3ZkA
+owEeTjW+KyddTT9rUuggAwARAQABiQIfBBgBAgAJBQJU4vCSAhsMAAoJEDec4ZLU
+AathzJIQAJkh7/G8uMQ+GJW1SpwAI/JcvhTu4D5Xk5tlVGYoqWS6ircBAZCz8sDI
+JJggZHDXeECfVfq2eKt5O/68SgwNpfSwHWpTDj1Y64HyHvU1oX9Rho96GNFbI02r
+lSX6Jw3Bzwjy2B+RRQUKb9rmcmHyllZ+j7jTi6MQnMgjZCTpKhmqFurbGtOAKuso
+fEbkan5rflja/5MLw6QA3ca70sGDf23TnzECsGKSTwiOd4JsikNXkW/k70nUa4UQ
+cUfY0iiVoamkQ6zB3QAuzfM6ZDwo5nEtrgaefn2CRDr/wNvXNdNbVBRBaLKW8W17
+Sr59xLCEoSAkrLI3Sm2ThjbutyVnEsy88CNbX1uqdVG2KCNYcmXGYHNfZuh0tZvH
+V5GJLlAh46TfYJmVA3sQTSWeIotU0dF4KsGaIFVEf2SwoVsVp+zawj3sU/ZDJJC6
+6BwzfSoO60DYKijnxKgBgBgz7QmUpLDAVEfSYYM8KLDFLM67gE2UijFCHcTOuyaL
+KIHiFY+f2rNflUzYhe+0vVbO/ytS2kA2Syj0w+OwwB/Vj8oEFpSbRB6GgzAJYX/U
+rCwX0Q27rGifiX4Z+Q+G4KwQcR2pYhUZesMtKyEwZnsd3IC1Qc9dcVXQUJPcqpXh
+SImuMDO/uOp9JJSxdPxB/gf6ELyVN9IFsU1/E/XhP4QkRJrZI1F/
+=3hym
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0D3B328562A119A7
 uid    Aleksey Shipilev <aleksey@shipilev.net>
-uid    Aleksey Shipilev (Red Hat) <shade@redhat.com>
-uid    Aleksey Shipilev <aleksey.shipilev@gmail.com>
-uid    Aleksey Shipilev (Red Hat) <ashipile@redhat.com>
-uid    Aleksey Shipilev (Oracle) <aleksey.shipilev@oracle.com>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -8504,271 +4757,8 @@
 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
-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
-=WDmL
+tCdBbGVrc2V5IFNoaXBpbGV2IDxhbGVrc2V5QHNoaXBpbGV2Lm5ldD4=
+=DTlv
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0D9CB5EE96B7BC22
@@ -8783,14 +4773,8 @@
 WbGzAu20eAHKs+4Hg0I70xkI0zq1uVI00w0J/gTiBATOCRLnxkWYDA4sDQC9hA7V
 4MVmVuwGaV47SaA0WzjwAD4p6PmAPPHvzZJQJOigdFEkAFMl50rhGBKsDS0Mopet
 NaKK11DBY1WB9jl5FqrrERS2I5Vy5XcxelVdABEBAAG0HkRhdmlkIFBoaWxsaXBz
-IDxkYXZpZEBhY3oub3JnPokBOAQTAQIAIgUCTpTA9AIbAwYLCQgHAwIGFQgCCQoL
-BBYCAwECHgECF4AACgkQDZy17pa3vCKheggAw90nr7XrC/BDrtTOjBf2IAsYEhhE
-h6r81KGUueov8AxjnLzahhlNUss/GoL37CDZGm5v+dOGSSPRQx4P1/BbWKYw+alx
-fqAeXgR6MuV3ldukab+kRckxY8Eo7NemAQAVAlOq16m1DlmM64sG3uzkH823d8pf
-7HVRmCIozIWogoGRrsmlVQv0o7VDlxAk2rtUV6E8Tf1kVpM4fNGVngSxEG3PLe4g
-sPAqwyPbAAH3F8i/SQxJn+98UeplaN6Qj/vkg+rrhGG1zSjPG7chKfb8KupAEyNl
-jjwlSyJ68wWZCWUUV/lpS7mHXM+T418PmvmNvmxfCgWS28WvAoavHRru1A==
-=dCXc
+IDxkYXZpZEBhY3oub3JnPg==
+=+zcQ
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0DA8A5EC02D11EAD
@@ -8823,18 +4807,8 @@
 gg4uEZwHrIrTTOB37zNSqZM3l61fn8Z1x3ZHsyYXoBvzY1uVCnp2mrNqtc7L5Lp9
 UcJ/kUuFwKAM90QVFoQ6oK1/RWvvFU0MmXAIwpxrYOx+pjYVkjKBMT6nhLCQKEIa
 sSQRRF/ziVM4WRQCaQcR9VC/1Z5QD1SybFuzqlbRoAhIfcWGdTqbLD8bqncdkZDx
-iz63ntMRMCfaxRsAEQEAAbQcSGFvIE5ndXllbiA8aGFvbkBnb29nbGUuY29tPokB
-1AQTAQoAPhYhBDqC8bVXWUphlL4nHQ5iEnXNrbdgBQJcfxx0AhsDBQkDwmcABQsJ
-CAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEA5iEnXNrbdg9mUL/jzNNGSpLiXwI4IH
-PHcXZ5vCuwP4fYeJgaKSz8D762HQo3BWna9eIJUYL4A7g9oexGddmkAFLETBTAwq
-CtAoy0FsoULjMsE5xBurCpgUrMUIOncZcanXpWvBHdHCVCdZXSSqiwjzIzExypsz
-WKQMwDvrSXvjDVsh7TpqMiGywU9kAaPf8Er8yNuDUCjXZfG9Y6DLZtjBL2Qo7PAJ
-2u+V6y482E1MxjU9WDRGH8lmZMM51jaErsiRA+b86uKz3cOJ6nwbWFqU2OCEce/u
-HaxWNz9KlaUiB4tLYVMeOYvIZfpp6dBm9K24SoJXeQ7JB+uyHeVtMdpspgImPcQc
-/SWNbjQgGBxIeligHvGW4jl8CXN28I3I/vPoGs49xM6hFMSnmDE8/duNsCXuh4R3
-I7cdcBw+5JOkOxjm6ODFiLZ+CUnDzQit+NP6qbJbmsKyFifsRHCP52LFPmtMtEK7
-/hLYcDI/eOwMwYkDwpPGcL0+RfeHaBoLZdmYs8rA4a8ua3lQyw==
-=elvA
+iz63ntMRMCfaxRsAEQEAAbQcSGFvIE5ndXllbiA8aGFvbkBnb29nbGUuY29tPg==
+=yl4S
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0E91C2DE43B72BB1
@@ -8849,14 +4823,8 @@
 hQtD6A19+qjQ02SyPUJS6a2lKx+gwaHNxv4L2FqImCFGOOEToyRb12GD18Mgbf5o
 OtQVVtr3qbT07odFQt8Iyy1DiNUJbOfC+YO2wO7eMTr5xaFr1HejsTvKZiTDC0Nr
 EjtctqGxrjxPmoUPNwtxwEDTEh1lyKMhnqgJABEBAAG0IVBldGVyIFBhbGFnYSA8
-cHBhbGFnYUByZWRoYXQuY29tPokBOAQTAQIAIgUCUEib/AIbAwYLCQgHAwIGFQgC
-CQoLBBYCAwECHgECF4AACgkQDpHC3kO3K7GufQf/Y0ouafFZwXK9hZP2104/Zgmw
-ZVTSb/3777qtvvIu4/FYJnxXHwFfMLRX4zXkUZK28xFD5TOS2HI365mEMEnXQvUG
-LkDTqXBAEdfwsTohSz51fwSHCqcKb1IzOLm9NnqxT9LOdwE154tpk3vVoarDPpSn
-iuQFpaKyJ2QAISc1Zt5HIlngAKrONWWvT+yopCkhIaqr60ArxSfzEE+M+CuXgFR+
-AgCUOnsPE4OsJyY3lje0IMejLp4qDU2PUbzNu+/+ETev5RCMuAF8m2iQvtM57qQO
-kEOMezP7XHLX8tKJ9Vytm2oWcSwMZY6awwqW9Y7VhuptVU3igf702trmY/BEhw==
-=flN4
+cHBhbGFnYUByZWRoYXQuY29tPg==
+=jEtb
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    0EB69F76FD171538
@@ -8884,21 +4852,8 @@
 98LWFCZ7KHjKhZpUbMBurd5f+N2mn06YnVZrHVzX+q6REWGToF3p+mrBwstrGaiU
 j78gMgZnI2tiJ99HqmocjLXbwXxdvFmLkniBHAQ91D9fHfYL3odTEkjZ/Wi7DSZt
 PAbEhbBIKpFKnbazyMB8ACeF+m7jQ/Nq1+g7ABEBAAG0IVNjb3R0IE1hcmxvdyA8
-c21hcmxvd0ByZWRoYXQuY29tPokBPgQTAQIAKAIbAwYLCQgHAwIGFQgCCQoLBBYC
-AwECHgECF4AFAlfyirgFCRkz5iIACgkQEAZqlwcJDPlyAgf9HcssfKyaQDGlM8fr
-0WoFVeJZ9CmTL8y+KnXOTG2XWY1Dt8m9dgAQVEOdu2+9us5ckpdv4otSIhJT64/5
-Kmh2OERXdIt9FIb2a+st6fCNTPqo/3IWjTyBR4ZdtszfbaXWxac9mdLlKHVdjtNY
-olNAxcllIeNcA9QjUjMYslCkrk1SenZHp/pdMsHR0BF+EDg+6cBXySiqhOYNrj7y
-sjnPTQvVHNb/TkZ1L+diL+MhVqUYHJpEJVwniRys1NQ2yxqfTlPKIzJaLrjyLN0r
-TGh+G9MDSMv8cXWUVWpZe/dCc5ct3udkzF2ina7yKQ1B3eTKmAKEvpzgCe1D/rBA
-CLvrxIkBPgQTAQIAKAUCUYqnkAIbAwUJBaOagAYLCQgHAwIGFQgCCQoLBBYCAwEC
-HgECF4AACgkQEAZqlwcJDPnDggf+NLBYLTTUzGrFrQmrKozT/it42miAQYxTfntx
-zs1Ld5ZBGeeIx8aUEu7FDQVeGpMqrX+iKamJaMZZNjUraYpFo8UoyMMBZe2+NcFM
-Og1/SP/wkM9BrchmzCVdzsR+LiLbm67VBglhjCvhLwie9Xo1H8gqDdoRlIHBeXYf
-ce1qsi7o+EX3R4p4vQOFDevmdAtWe460+net79HYIxY4uDLKR7P65nTnOjbFzqM5
-6tJTGXxkWel+XRP30K2e7UTbPHAHG9+ScTIJbaSloWIDtCtPCcASD8vBC47jlxQf
-T8Gnulm28RKdaZaLwAZy3tNZdp8z6oILCJFz+Rl4aEsp+E04sg==
-=9OzM
+c21hcmxvd0ByZWRoYXQuY29tPg==
+=URsG
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    102E05D8DA6C286D
@@ -8913,15 +4868,8 @@
 zgO97xwHs+T5nXcO6jOrJ7NxI9fbnWb6QY6btSkrlpLnwH5yxGpajcR6R8+kj+0W
 dqWWrs9vo0DQGohkoqjTzeVt8UWHqUBva6hULkz6H9CRLfVxuK3lLFtnRBjnjRbu
 bNyOdVdtaXArmMKwsonsaZ0E973yqcmbhKQ3ABEBAAG0LElhcm9zbGF2IFNhdnl0
-c2t5aSA8bmFtZS5zdXJuYW1lQG9yYWNsZS5jb20+iQE+BBMBAgAoBQJTmcsNAhsD
-BQkB4TOABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAQLgXY2mwobdClB/98
-0myMP0+fjU9GRhBCQZCc6PdL8h59vKVPbkPJtmu0WZbIqpED7SHr7D1sfu3jSthC
-dJWGCKSfcjplf9T6AyPlRbNPWhyozvN5+ReFkPmDJ7wdnNf7V5AdMYrIouFb87Y0
-NQE2f5Km2kbhL5mSrJvXEMToMgLJOijBJ7aHL/IyefqyMRDfG0cne+xleEOh8dsf
-tuAAgawKXkGrIjQL+Pn82wVu5MXHPHWyJyUynyMG3kpJ/PHHJ5laZdLHMy0npCNL
-vtmqbZqkhtd31qKiNxWbnaxGqZ8GbtOQGGGQQRelOogdPgLipj9ES/DTyC4sQ62T
-tzkLREJhsbhjys3F3xDU
-=I9Mz
+c2t5aSA8bmFtZS5zdXJuYW1lQG9yYWNsZS5jb20+
+=iCtv
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1063FE98BCECB758
@@ -8939,72 +4887,62 @@
 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
-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
-TwABHy4z3YtQDpFob4bKD5ohhQunm2EOXlWy
-=SY/z
+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
+=OhYu
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    11509ED50EC155E6
 uid    Konrad 'ktoso' Malawski <konrad@malaw.ski>
-uid    Konrad 'ktoso' Malawski <ktoso@lightbend.com>
-uid    Konrad 'ktoso' Malawski <konrad.malawski@project13.pl>
-uid    Konrad 'ktoso' Malawski <ktoso@project13.pl>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -9020,74 +4958,8 @@
 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
-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
-C9bjItngetzpimqM4jNadX1w+zT20y0=
-=9jUi
+tCpLb25yYWQgJ2t0b3NvJyBNYWxhd3NraSA8a29ucmFkQG1hbGF3LnNraT4=
+=JFu8
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1158D77CDB3A70BB
@@ -9104,52 +4976,38 @@
 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
-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
-JeW4j6w41/JtqDLac1NMlbGicGHtzmk=
-=m25C
+ZXIgPHJvdG90b3JAcm90b3Rvci5kZT65AQ0EXceB0wEIAK7dVAoqZqBw9twxk6ps
+NcTmIrfQbvjasKl/+PfLCfBNklD/nwe0CLz799fFSFMezCg43ZPCKIescCQ5t74r
+0D6O/QTGMpUyZD+w4JhZkMQYy2HjImfMNtPVOWb+XaLku/RloPt9MSG2mtA25TTp
+295N4mzMPKq0r1PzSa8jj4Yfn3AzzSXXLq6PwoDiljQ91IWBvJL2IAUAMaxSxzq3
+UqJnlbX2h0yMM+VC0nbSmbkK8mUNltg1EnngJKV1mKRG+Y1GDmZZ1Es19tEwUMsV
+9x1gt05YlecC31woUkL0//8i1kWuWG7dqRMf/8cXaTFA1brVJ33b/G0VQP/a03Ua
+Vd8AEQEAAYkBNgQYAQgAIBYhBGNe5ic0Xzwd1CKy4gfTUWggvPaxBQJdx4HTAhsM
+AAoJEAfTUWggvPaxrJIIAKJsimeWznAmR9fUH/u0ONaTaEQZlhwBKvTGn8MPBKAn
+l50HK5A1AXtEe4KTSR7ohAZ/K0wsmVd4Qi7v0O+A9qTEE/mIb5zT3kVuFG772cIS
+RGpC+inh8Dtl2G56s7K45tbPz6jdnZHLBY9eUr4X/2ImI7+q2+Z05G6KQm031XGC
+8BS8A1lG2XOMvb3qnRMnMapeHJ6vjoIeX9iPtO1foSmdkOCJpBs5ZEU5nnHzXEjv
+1tAPsTAPN+vBAduNAOSqGTA6hXparbf1loeHqnsZcedCQMxDQf/tja3Do8flUzx8
+MmQkoXY8i6QmtRdrToqqt1n1jGq9Ka7lRjq0xJHAZam5AY0EYHW0PgEMALkBcL/X
+7eKvtznC1SE4dJr0skc9rGniiYwVhSNiyiYXlbb0ETdRDbajSHjv53cR5DGbAyPg
+wwLNzHo1K3MrE0CvM09sCMJBOjpCu1X5sma1w76I9ScvJ+Gqr3jFu8CO9JbSs4bj
+2VRr9ITOEZinvwo+Ult1vj1Vib9K4lAlpsXcd0dgFS7jlTBqeDgRR+xyRdt+54yr
+Cay+IXupBF0GuYQ9lHwCVyFhwhOneoQq4xhEmwmNEQOWq28byVLUpMDzKAPt+T5A
+myxNebLRQmE7rWX2PmoCSTby8aQPlG0OSRBQJu3f6lhMZN21QbTcCBeDW5VIsjPt
+ZnJ7nIpOkT2UXulnlRk/QAEydJdPdSz/Idps3JcHyNK0YXKJ3qvrH1k0ur8YfgHu
+Y41d4QqShOZleDcJtDXj7Gh0BtLlijspiscBNt16BpkKzEcqd/6Pt1fMIOExiBXr
+dad9mPpZT14KTXT8dXHtAEKozut37a4hLfJ7al+XBqilnYEm1OjVx/BVKwARAQAB
+iQG2BBgBCAAgFiEE6gIlYKgeW9SNs9GLVKyOLZjP6sYFAmB1tD4CGwwACgkQVKyO
+LZjP6sbEogv+LX9Z8BlnClkJ3lVMZlyzbUf2lleg+QA4cWTsPE4SSi1VFHsRCAox
+esJdKqJuFcIEOFxscGfpkSbhTsTEczI9e2XsaaDAaY1GodvJfRUAu2VdvVAGxHho
+EqUUSbD4r3AW0yLViLlsQVoiuKIizm8PQZr9bfLSkXjGQz1wlPfZVC7raPJ1VYb3
+TQXmIALTc8peel2SlqFLQVZ+3Lb8JKC/9DeHAGmpBipB+VcSC5vRchqpncsHAz0G
+Xtmqzkgcyzq0Kyh0nIshDsGQ/vXoZQRl5hjMWBLaN/y5ik7Qde4bjL7p1ozKVm0S
+2qY0CfrGSzZnLXLOxX90fqXXN9cwmLv++bdyrXElA10u9LIqsViop3UvHitVjkJl
+Ihwt9/duJEhPJnT/qH2O9YAqwlEFggSNSctD3LC1uMjB8JD6XcpQgDP6BiZUwP/y
+CGQGvA27Omf2WgrpGukXiZFSBkCZPL+/rcP5dnVrCwneGCXluI+sONfybagy2nNT
+TJWxonBh7c5p
+=7M2n
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    13BB90CE8EAFBE37
@@ -9167,17 +5025,8 @@
 qC1MRIGb7wvtNCUH5Qh+CweTfBIHR3J34iSLgoo+uui5L7uWeSJ9fQ6YmAjrlG5q
 ffGNRve73ZZPqME6yU6jddHRZ3747CbXKLVXkoZt6DwHLVQsdDSRxLTJDn1Q7ka7
 NtouNxKYGEZFlpkAEQEAAbQlWXVyeSBTZW1pa2hhdHNreSA8eXVyeXNAY2hyb21p
-dW0ub3JnPokB1AQTAQoAPhYhBA4Y6uB7d3Tqxds/IRO7kM6Or743BQJf0l5UAhsD
-BQkDwmcABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEBO7kM6Or743sgcL/19z
-wIOnMIaiavgSkfc7wI3iTir6nKP3v44BHUvTi6OpGfTIK/iODHAO1ogGCUPFA8bs
-kbh+qOUP/dzScnyla9P8ljkMeHqTzud9/UJ17lg52Th3aa2MEAudZMBAWttvbVMr
-t3iUl0rj3fmgpGqx7l9N74T3TXAJHkEzEvGOUJsOVIOtOWMURjlBWHI1B1cbDDxP
-OR4vRZaVp0xI8bVD9Qryr2wj69K/qMQIA9+r2fhSTB3sxm7ubYBy4UrjPZEKIgco
-LL+jBCDmxhI8CEVJNiT6yddFG7VE7KPWbZra5Hw3LRH9u5IuCrCSH8AHHX/yQ82z
-bbhYya8v+cLgIhuDBNoEeh15z7ornsnz1VEcJuvfSVjKsZXKSo5C9AtQ41tUg4ad
-eG9cy6MroBpdKC9zKVeOr94RogcOPZ4mQ4okfdc0wHLSbH0p5gLU7vfb8uVRg51E
-kq/1lWlLJ6ZuPG7NeBulez19WA00KzmbnJbHDvKVeJz0vquovemc78MUtRQpyA==
-=Kl4Y
+dW0ub3JnPg==
+=vZoQ
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    14D16906D9482A92
@@ -9210,20 +5059,8 @@
 AlDHU2YzoWvAHPqSppppjPJmz1tgHqx146tukezuzoRXuEUTmDAjbpLEHxvKQuBr
 DcSfWqe4zfKKqH/CfhxlPGilUcVyLmhaHjs1ti1Bnj4YmQuWo9BR3rPdLi1gQFlp
 wZfzytmmK6Zy4Ek89la7cgt6AF3eXjNmpVtGZlAb7lr3xne9DTp98IW3iwARAQAB
-tC1NYXR0aGlhcyBCbMOkc2luZyA8bWJsYWVzaW5nQGRvcHBlbC1oZWxpeC5ldT6J
-AjcEEwEIACEFAlcyNOoCGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQFccc
-Ck4Ljt3SvA//dPFvWz1whFZZfcMIFnaIXSMKH5CpLspt/pDY6s1uzqXD+iccaPXr
-6o9/xbtGm/3T8wBYq6FudJMQlD2pJ48Ikn2vuRKg8/BtNO1VaAWsiCYlT93ZsuXB
-4GKfxxLFI5K+rmw/Zpxws2+fQwhUjQz6e798loIb3EnzaEFJLTuD+wv9kav6bh2i
-r6PnbOQ0RDonzcrYyRHDfh5LPSzafKff6V1cl45v1GiR3lBi0078G0TF1E+4FA9F
-aYitzPLsjVYwgsU0VYDqijEtCisjC5GFtqvOvX/vDzg7pW8pYiI6s9EfPTn0aTql
-FLpRYtniMUVELUvSs5kv2jehl6qk8ZAkLqT6LkQXsCkibJTp61yNWgstCOkhmKfz
-SOyOUv5S+e9j0XS+xV/Y9OkK3X0e/mVfGijQYak6lx7AMQiXnHkd3BbceP2Vzfg8
-GYDdAavOogBB014cK+c1XEw156G0qIFf6XFQrRytAbZFPF4CCrvNYp6imop1tH+J
-5etdLyvDPW+hjv8JtGb9Z8GnexPdtFObXwnrrBgUt7fbjoT/VikxRMj7D7LqQmRl
-eN1I1CQRXn2dEhCNtXyOX8u87nt/6GunzJDIFwez1YnAbEGLNoKMCNLPSTBQm4Fk
-DYitSS/DRlDo6RUdTa0vhqr+C8hSoH2xJsP/7cnK9rOJNc6finyM7LA=
-=kUpU
+tC1NYXR0aGlhcyBCbMOkc2luZyA8bWJsYWVzaW5nQGRvcHBlbC1oZWxpeC5ldT4=
+=WehC
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1646B01B86E50310
@@ -9243,52 +5080,8 @@
 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
-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
-tTHc1OqR1iSF8Wer/g==
-=B+PI
+tBxZYXJuIFBhY2thZ2luZyA8eWFybkBkYW4uY3g+
+=kaGv
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    164BD2247B936711
@@ -9323,81 +5116,71 @@
 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
-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
-AV1SHs0UbOYeuk0enOFxnXV8T1VazbKVWw4Z
-=J+gi
+b2dsZS5jb20+uQENBE7+huMBCADS5gJ9frZF9KUorujSdK5GmZTG75MXhQLLR3UO
+czqElryVfudgwHfofBymcuiPPfwSNWpWLeylgCxs49SOrNfh2r2mCln7ZO1LwDOo
+RIfD8xUA+TTc4qbQzo0xt6M7WdEiuhLdxUGnM8s4fRsjwvN7wvA6+PGvgaeWIKaP
++S27jZaKbVSGqR37Vuj1JkbsZV1V4BXXOb0gyNsT6s/Hcy0owWtZkVBIgBanYui9
+J2uosgRMhHeEJUO05w6ehCoAkr2ktzj0QRDmljmbGjiHEsOcO7ZDu2JQyI25bVUC
+k21S1CEdIfZ4xmiV5Rj2Lwxb7LCDEe+umc+W2/7j5RtR7RWFABEBAAGJAR8EGAEC
+AAkFAk7+huMCGwwACgkQckdf0wa5yrcgCAgAh0VKQHRwwov6YDV21H0/h0vv934b
+rwNWxIPz46WVa0xI+fmA0wYXg+1OTQBy7rqJHWoK0I3M6qjZvvaKo77yUUtdye16
+gf/SxKPUwXMyTg98ojOvq7orvDF0ktE59stzayjUs3vrR7xDh/mxpwiCN602Jk27
+vCCTdg0AHbu/CsK+Cu1rkqlqa9nQ0O0No0IiGJdDK51/ZluGBhhB73xQ+jqgAf/g
+1qabG0Zyv2N+5tw+Alp9sqDsfyA119B6q3xKoaJqo+9h+zQ0vpZrNMuph0EDebGx
+R9VMiO2F98dacAm/Qr8QTyRj4424rkCSK2VbZtvalewylY514xg2lV/Sh7kBjQRf
+qFJKAQwAxgwuSeMcZuqW+aB8GSwE80QGS1lEz/p5a76idZvMB+u2wrmpIwWIvGL2
+lCGcUfftQMGLHR9/GlAIRAqKLi4NYMBF+PgHFPQD6NLierGf9yicgV/hJsbe2Pu7
+TFQBIfuVzt22EcwG3UK1v8pRX/EF3cNrgYkfFLQp4Rtw9oTzktR364akh3BOIIKw
+aGH8Jt88942YcCwFQ0aVZ9FoS3SHRGj9/f0MW7q7fW4mljBW37ai3Piqu76BgELx
+vV1dbfv6EU55tCWceU0xIuTH530gKw5ohV6BuIQGn7Y5Xwq1WbO2Q7Ly/CZo84Np
+mqv9ZXJseOMPAEkopD84wBcbJTyEndh9QCGWDUVxPLfHs8q0u/mRrO8lFyEcZDIA
+ngwKXV0rL7G3pQLuWZPrSHvnDiQToQyAb5jFCqH9sJsmWqciTsDy2D2lorGUh6zk
+5IpQW+zd2UObIcLuIkfnw6Of7urBJO3y5uSUFhwirK4JBSlHBSQyi6RLTMXdWd5+
+MIzGmxZxABEBAAGJAbwEGAEIACYWIQQMtYcft787NRYUu/bKhf/mONRAegUCX6hS
+SgIbDAUJA8JnAAAKCRDKhf/mONRAeruuC/0e/BaxXy5I5LwyNf5pUr9YToUktoiz
+tCoikQ1TzQGAz3uDenIaR7ShO5GkJhDdU85jUztkBERkOQvRbKCktESfHFTyWB4D
+lwbxe+TVCj3AsrFNc3mYjEUrDfXLWfIVm1naNzQNuRvsIgV31x2EnZme0fYR6z+N
+lmN96NjAcusOIrB9Yb4pYJZDSH76Cg8/ZivFAPjxCaa9tptMtudiN7jCfzjAk76g
+kilRY3jg7Dxs53455NnkSn+NwKKqE8dCofe5POV2rBVVUiC1xHW/CghwHXOGDrMU
++hzHhI7sHWm9wcwHbMDSIHzz0eqfUB3yfY65Ykx6ID4g90AvclJ+A4WNvZFkoJMM
+x79ZD6Mtedz2OM4fGgpEeq/2+KDBW/ZfjKhpZqCj/IA2L7B4M7fMIoq2YuOt3Dc6
+SeF+xkwM8hiH1Yb9xNLdlcTheh92ZKv2xre5AlOTsfqH8diCSjFhmXIynRKJtbDt
+wZ10IKWwR7BJIfkyXFqDBTcYoiFV3CNM59C5AQ0EUwjd3wEIAMGbFKfnAB6/FEOJ
+YVj4Arczs3TlZjCT6JRfFWUzvxCVVRbnwirKQLNCXaCLPJzd/MRRv2tEaJyRyWJk
+G4m7P/yCfkA+wbIJJenzwryct8t0a3vYoOa0IFkqWR79JdD7RnnIXlAvtfnIG+Vm
+9q5XKb3+cQR0v5JXlp43oH+9/H3PQqcarcIPDdfa4KKZr1CM7lqAOVuQbjXkN1fY
+0+w44Dh0/DkT6ByJGxqzb6t7iM2zu2IRdKZiUb2Muk7Snmb5mdA70HE/AzDO9s0U
+Ea8Y28ZCYsU1Khxkw/L7EB2ncm+0m/kSSTh+roG4YFIOhcpqG7Ew3RQgMhiKesFh
+ibopPA0AEQEAAYkBJQQYAQoADwUCUwjd3wIbDAUJB4YfgAAKCRDMVLk8AYURpDqC
+CACoY2NyI+Mti/UScklWgS4wn2b/KKK27vCxvEba6S3+dElycTBBQSjBBpVoYWhs
+wMoPu716R28p6FZ9zKtyp31H4CCLWRkch4Sm2GJvvFfbBGLO4PeFWF9DLMn5vGPn
+aVDqRiYPyXz132sX39vn90ss69D+nkNZAdNmYBGql2WjEKcHWnQA69WSkWM1rpEm
++7f73okrxDKr6GbEDFjpdN8xKVzZnLvsbUN1wx8ec/Dkn/jlmJrGdKmMyJbhPuJN
+LpyZnxWxCbnSqNWlpF/WESxEMzerkVCk06M3jpwfeYrcqOd4r4rALuRX3yA9tpOF
+07+dIsSrGqvQvkvPdTNWfuaFuQINBE2p/+gBEACsA/DhXVY7SGa53MI48zxNpVGv
+PG5YrDt/JZo61pzAm+cQQnQ1xlwHb9DBKp25soJV67x0hr6IGM5uZFI8AUI0sBGM
+Vlsmf10Ojw0SeEZHpB6LtLXCEqqdd5Zg87P8GUUR3llNuXalPt5RhCefKFt5I7mU
+B2ILFNvMp9jmJYYNW00bAJDybOYPPQtEGFdIKzPJZFROnXR+kMCZIDSjBxwv2/gc
+nskpTp5tnUMecQIbPqiUYaY9M6s6ghlvxew+hAkkiJxfuBRr9aZFvTIRk9kawKdA
+mOpX+/lXESnpoYtf6tBw2z6borEEpUlDaj5dwQ+icUXBijs6Im1O81f8MPOA5Ovu
+fYiybJ6WfL25fKdDYJ+1BA0iFES0/F4tU1FPRmTkwoczz+MUyoqEYBvikjUgiYqg
+Riq3DCOY9IDdCu+6QlXkFw7ooDpHJFOrB9hYt5z6jEhnKswdWwTtvaf1LPX2pHGd
+B7UllPAsRAL2o0EIu/qwhpbF3Fex5Jdc+IdOEhjdh6UphkFuzkE66Ste71IqzlFV
+sWjU9tIWFYuaAbdyqv0yRaTc7WlotFuwNMzY64NtvAXIQeWkEBmsq+gCqQrND62K
++rdcutVhNKuYvRsfVeBVzQtHoFsNfJ0ljIDWoPMyydifzstossy6O5CJ7jbCp+pk
+eCCxLDo2MnfuPBhIpwARAQABiQIfBBgBAgAJBQJNqf/oAhsMAAoJEMhBJcE79vLy
+SJUQAIdcFnLQ3Qk3qwPnHWWdomoNDpk7WAyY2KmHAKZDfsNTu5I6l8nlNEqiZYk0
++87vMT6XxaBWT69xTh0UbPr9d6nV+trOBt5Q1FqTP1gQ5HFUMOwyqwbFCsH/b1+s
+OmnjWdaPGiM59foKQgHjrzH53mKUKgKmHURBuiVbeAs8Fh6hgnt3MchqqVxL0bTB
+xbKlpge9DCVhr2K54n5+8yPubEJg3vx19kW8b5M5YvZg3Y/dqyg2NCGQdYoPkOaU
+Y8nTaeWnrpEEEdzZ1qybaLDnuk5J2u2r3DscdVVnDSyG1+5R9JACv9aoHY4CSIiv
+4YY7BKIOWhIKyFY+RF/4UON0yZQIr59gtqA9abkIFkO42WJ29VndzYgBVQzG2kU/
+ZKRFNjwwL4wUsHTXnawgo76WjeiCxz+jug0D8hJ/viZYKtaL9mhh4nnSxKIJUnvQ
+k/pWngfIJPeQvTBReNgu+wREYnOTxOsdJRJUwL87DSfb4CCpZfs7vT9wWipT/R+U
+37aqGeHsDuUTH1fgD2yRGNhOCzJlz38tA4pjLc+TRsgsQEypr2v03ZX6il8kePJQ
+lSNhrNUgvENdVieA/dS3SSrEh9Sv0yyebiNyWbFOIPIGNJIVIe+bXg+d0fxFby0Q
+2FI8W//9m6UvAV1SHs0UbOYeuk0enOFxnXV8T1VazbKVWw4Z
+=Qume
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    18088D07854014B3
@@ -9412,14 +5195,8 @@
 QESdh5A2v+oZ3lYRQyZ/knXs5y1ieU5Ie/FbLkvU/oPUmPUB51v7O9jqzkZswO9H
 WrYHfIGWB9tZCHZ/q2uOn8kArdJ2KGZjkpKAVQAwZJUJqZLv4c517omOxkLbKilz
 lwn1X7VSVMsXtvr4hNg10cn0dFE+A+9oGn6BABEBAAG0PXNjYWxhLXBhcmFsbGVs
-LWNvbGxlY3Rpb25zIDxzY2FsYS1pbnRlcm5hbHNAZ29vZ2xlZ3JvdXBzLmNvbT6J
-ARwEEwECAAYFAljQMF4ACgkQGAiNB4VAFLPhvwf+JlFbGTUxo7fxkag+nxvj5hPX
-m+ziln/9TwJ1oSj7aAg+4pRIXMPaR+ws+4G3fAr3S2iNObSpdc6QTNDicVLGtWNR
-HMPC5Jsy6qrqC0pv+B8Ya77PmcfJlq87B9XtWDWGr6uA5oeXUwxhH7I9JMSUIReH
-dLvOlYcEVAstI1JzEeBJGj0ARH6ogtoKWRtlq8zFHon1dUHK2rHck24+eM6XVAhh
-k01ntjsd0OGwL/d2lLil+n0V2AMb+xXWTlBY6vJb63TXXRSpZLpAb9A5M6K+qmFo
-DPuZ3qEwtCHP3W/Mac+mA7AG8NxsZ8zrWgZ4U21S9KUgXnlNAqWM8brQpphNGw==
-=UuEs
+LWNvbGxlY3Rpb25zIDxzY2FsYS1pbnRlcm5hbHNAZ29vZ2xlZ3JvdXBzLmNvbT4=
+=Vshr
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1861C322C56014B2
@@ -9439,133 +5216,63 @@
 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
-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
-Oi8vd3d3LmNhY2VydC5vcmcvY3BzLnBocCNwNS4yAAoJEH9YT55N2dHEFfUAn3b5
-Is2tga0qszQ7p7CuhWWBMrXzAKC6TY0eya2AyHuGWAwD16bwiO4VtIhyBBARAgAy
-BQJIC2TXKxxCZXJuZCBGb25kZXJtYW5uIChBU0YpIDxiZXJuZGZAYXBhY2hlLm9y
-Zz4ACgkQs5fdGlNtq+2kfQCeOA+F8W3/iBqZey4dRwflRLgDnAMAniPVt65jUBsk
-NcYiSIYGYE9mATJCiJwEEAECAAYFAkf+TdYACgkQMaY9luwUC4GqcAP/Zd/qYdGz
-HzRTrb/yLjmtbHAlZU3PxrSCA9W0HO3jXRQCWloBY/Of1WOgYeqQRpQtK8e0aeUU
-Z966ir7Iqpt8kMuo/vvqnN4LZmTSBZIp3cpp9zDVWU4nCOgZv6/mxhp7EsjtTpCd
-sfy9/GsqKjZhaikyJWyEJWV5o5zdmj6/WsqIxgQQEQIAhgUCR/5F2zEcRHIuIFRo
-b21hcyBWYW5kYWhsIDx0aG9tYXMudmFuZGFobEBjb250ZWMtZHQuZGU+JBxUaG9t
-YXMgVmFuZGFobCA8dGhvbWFzQHZhbmRhaGwub3JnPigcVGhvbWFzIFZhbmRhaGwg
-KEFwYWNoZSkgPHR2QGFwYWNoZS5vcmc+AAoJEC65RoKIgXQCa3QAn3gap5o4ChhE
-FhlzfwpEXOip+thGAKD+RfkOu2mSyMgiDRCH5lja+jF1bojKBBARAgCKBQJIA3kZ
-HxxKaW0gSmFnaWVsc2tpIDxqaW1AYXBhY2hlLm9yZz4gHEppbSBKYWdpZWxza2kg
-PGppbUBqYWd1TkVULmNvbT4fHEppbSBKYWdpZWxza2kgPGppbUBqaW1qYWcuY29t
-PiIcSmltIEphZ2llbHNraSA8amltakBjb3ZhbGVudC5uZXQ+AAoJEIs6YB8IyXXl
-1RcAoJ2V7LiFgAgXpVD0MN2oZmJfjicmAJ45m+NJ2Xhf0/Ccp27kzmMc7C29FYkC
-HAQQAQIABgUCSEQGrQAKCRAZPxgKtV2Zd+F/D/9qMw9DHeD1aK7CAa3rCsazad4j
-j7F2jXAzcjA0ZNyU/X1oQUQMFh9Q4eUh7aYr3JOVwBQysxtQBzFqAvzKJQnsZzaY
-sgg73W7zIV5nZQt2dx1Ae8MseuHLCUKGbhH7BoDO+b7XL111ZGbKWChRPxzdtDth
-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
-=8W+h
+YmVydG9uIChDb2RlIHNpZ25pbmcpIDxuaWFsbHBAYXBhY2hlLm9yZz65Ag0ETnhg
+uBAIALjvrk/Nq5YIb/IWz1w9zV12AmkqSUTGIDpKKz1Q9bU6POlKReStNKsZ7zOu
+mvriNPgtmd4l5Zw2tTVHr/jj84438yNHc7AhF6pJqrqvLUiAF3WX6toVGVkad+6Y
+M/nUt5i2VW+yUc67uMfbdlGO36WGTJ2T54w65Jo6vDRNEhOwzc7jpFd/oM6SsmtY
+VqJ+DMEDNTUHQEwtpJFE5uPGLhrrrzCOZtzfuBHPG9VzGSbc//lVP/vxLpXt77Kz
+X5wRmYNjeLRDxPSLzMdJxw8JrARqpUhxzt/I7B9U5aM2wrH0ZbMZujpGXl7paoBD
+STdjURbJKqG2hDzQnL/bRB+B3aMAAwUH/0tnU3w6piDZ60jyzPqQJuN5q0E23p8F
+yL1SnLKfflP+6LaFUr1NY4Oew937PjQ1A/hqUyPfe0GznbMNDG5DNcID9Gh2gix7
+tULkeXGNBaTl7m+kt1zjqUlBU8c7DD8MlgpwxfpzKuUNcO/ba+rebLl9E2gwgDcz
+CLMF3XQXBISQp9L1K55KzTnUzIvNwb3RhZ8cSdVK6kmIDoMdgVNcVFloEwKP+xmh
++ZAvdsSgA9Yl4+pz2Nxhr3z+9YdmEc1ohAt/zeRrmokZYYpWm99aroj2sFsZkH/a
+jkHaBg3D4kr71yLBbNMuMZTGjw5INhcpQyTuOAWC/DjUS68k74lq6JyIYQQYEQgA
+CQUCTnhguAIbDAAKCRAKB+DlXkX2W9GIAP45z+2mhx9qTo9uGJegHz4O6nZ/9/3w
+2vrKhePgq3ntcAD+Ko2yE5j0iQj9UJS/H4ueCZCLgMAQYU2nJSHIjfRWxHu5Ag0E
+R111zxAIAMqhM93gRCome1WwI/eh7SLxrnyk3WdBESba3TzuBVBlQXF6aYbZz5/i
+TlqJrM1CpCizuMduDmgwhv0hDbD8vjXneMIwQfuLldGBHOV5/ILbN4OGeafmIlbv
+mlnmDzwuQT1OtbVanPfeovWWJGYmDrPReWfnSqM5NysFXIo2gq9CEDIwLSfze9M3
+HIj9tyoazfWdzZ+eF5uxnsw9J8U1Bj/IDO/cyh51YQwaba8SE/2nQs1Pf1F91FQE
+HHavOhX80xKahRt0zO+RwU0IB4oSJJIq87qxPor4emmhd1CcJgwXuVrt4L8RekZd
+CWv2ATZJAojl54F0scL1FEfCiTtO0RsAAwYIAMThVdg3zB7EwbvvoCifeC+lYxE3
+1RRGD6O3uLTtmZ9lJ83cc9gaEyXEGNI+vzN05fYQsITklVS/AKP8KdcpI4F58FeL
+89G3uHf2TC/ihdecfvO99B+04fmkflIOiuiobKfFyMqZiyXGIGCw9Nj5Upw8QqvR
+W79CQ5W6+rW7CYlc6EY4PljRCAdyCKyW0yIjha0QYZaIp2KKLdtekCdayNp0KTBA
+ZUrl+n+I2Behe1+HQGOFeKIYxXiQm+kmJCm4+HGqgQPTla4974fL/7wFCgwfKalY
+aWmYnBaTHdqRBGFTBFOItbCxN1+98vUUbmiTrzwMdhx0/DJVo7A5jXWNjIOISQQY
+EQIACQUCR111zwIbDAAKCRCHOo6GtDchRmSsAJwO0HcK20diAasXfUoKhxf+DAKJ
+gQCfXwPzvlDA2u4ThCS39bRGHL9BEhk=
+=yhXq
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    1939A2520BAB1D90
+uid    Daniel Dekany <ddekany@apache.org>
+
+sub    D068F0D7B6A63980
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQENBFHNxM8BCADYmt+HKkEwu89KQbwV7XIbgwZSfWc7y1HvA2YJpJRXJQsU/Pzv
+BhsHnm9ZIScBLIlgE5OUnMNz8ktPDdsFg3j/L0HREXOAqkOFxWx2kANsRo2HmkM3
+67RAu42fJqJcjD2Rs37wMxlSRRGQ+/bp+Bw2HNO1pw7GwrSgmZwzwT4+1pE/TvXQ
+Wl+Nhdf3swLyBaSuWHJZT3+JOR0kEGSQuurR+57r6fKDmouWSwAKn1z97JelHuXj
+HKZeueCkQvX7dayPP4a1zpoXPcoZhYekFarLWJl411EA3aHIIV8whknsZx/lGGC5
+yF9AVIzHHnhqFC/Fr+GJbwa9oMFXj0pY06ZNABEBAAG0IkRhbmllbCBEZWthbnkg
+PGRkZWthbnlAYXBhY2hlLm9yZz65AQ0EUc3EzwEIAK6rZ7kRp3uj0CrhvuTnLHU7
+nEs+KvoUZKLyhcIys76sJQ7cnhEygcG7tng/EtK8bI6skLwUaF4fnPliDj/yIigY
+08p7TvFL/6HL4cLrIXR9uZe5IdvBKYhy23Ie2JXdLk6zH6jq5+vBE0IA7ljJUQj0
+PgiIL92kB73Bn6dPayvtApzctajXvGajYNfOLTYc3n1L/Kqay+/UwjB5MJVlmFtZ
+1a/EAxyb5yHld/s3RKEaeEIpjaoPSJwXKOWNAcLdtgcPcsyfrV4bkgjx7ABzPvf8
+2gYucthyIx4zPZ29hZfktSV61h7cbJL5HGrk39UcSgfstHbfBQiTY/1kVN9tuHkA
+EQEAAYkBHwQYAQIACQUCUc3EzwIbDAAKCRAZOaJSC6sdkEFjCADEzcJtTbykHeSP
+GykEtUnApHYM8oZixHWFvDdjkGhePMTvBRJpByS/hdS4Mnb2AfBoV696eCFAtm+D
+6iuOA1OYgc1CnGhilxRVpzjgbD0S6bG0tyiKz1dk0HKkGh36wumST1bU2qdA/UN0
+CoRIA9Csb+mg+h8c+y3QixjbpTSS4shhXpzfj8QsZmPn38S1amaSTEv8zqF8pArP
+U93184TQfJBPrjAShTEitAmX3FQlSL5v5sZms7T5S/kOHkcHm4zNlwXRJ9avqb8k
+q2rcDJX4sCe7PjoMX3y2mTk2YezY4LrYbhEeOGcMNg7XOXlhtBBJ4OuqQtXo65Lc
+T7dK1Uyb
+=9sp3
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1B2718089CE964B8
@@ -9593,46 +5300,43 @@
 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
-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
-TOprHs1R+G3pUIZSx5ahhIW0fQ==
-=arWH
+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
+=PZkg
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1C8D5EF0DF2B70D4
@@ -9647,14 +5351,8 @@
 kbMJ/4R7AZvokpr3tflESspVReRt3JA/vr8Nd/V3/bFQBLOda0fNni+xqp2SI+uZ
 dfm10rTD0N2EnMkVAZsae/i91exJ8HGAygp3FVXcstdOeW/vObKPYgpPUaF8uAsb
 /vHZf24LHUEFdeQc96/BwASUB6nu3lvfvCyvABEBAAG0HlNhbSBCZXJsaW4gPHNi
-ZXJsaW5AZ21haWwuY29tPokBOAQTAQIAIgUCVipxCQIbAwYLCQgHAwIGFQgCCQoL
-BBYCAwECHgECF4AACgkQHI1e8N8rcNS9DAf/Td+lGz3F5gv2entWlZT6JH6YQA8n
-q3bYz/F+OmI11GYYhH0oFef739L2XC21RCLqfaE2le2PDeY5ozkdmbnixhH2peZ4
-yPf2JvOyDC5U0cr1A4/wXDUc8iwujSyJBGbyYK43e5aCWxiJceihTVCGQAzgGEkF
-nUBLzucDOYOzYzbq/yemXb8OIasmSw17XyFXEH0JutIGoAIJjKxCqGa/097sD+fj
-b5om8ssP58ZrIpxfTeFEBlpB+yH8DAZtxNobauKZHfvVGKPaw7OLkcX2SJZ9IgLq
-S/ZDh/hPYvTRHGPwRaGhpenWc8LXXK88TPtdphpGtAgqZxzHaHyRnDuLHQ==
-=D67K
+ZXJsaW5AZ21haWwuY29tPg==
+=/IBI
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1D185615D0A84648
@@ -9669,14 +5367,8 @@
 o6xEbH2/pU/D1mkYp9nVX6iLZbit93GEOF8VvhpFaM0amBBpdvI+v4vTB4MCIjz4
 JGz6BIzFl3ueCxfx0LkkAbH4ah+sqlT0RnpStKaSBavVmh1iBr0VhSnQHHD8ZIzV
 SPHGT++lsGdNYXfyru882hLLFXxHh/DNyCQ9ABEBAAG0H1NheG9uaWNhIDxzdXBw
-b3J0QHNheG9uaWNhLmNvbT6JATgEEwECACIFAk9WpyoCGwMGCwkIBwMCBhUIAgkK
-CwQWAgMBAh4BAheAAAoJEB0YVhXQqEZIvngH/25w5s7FU+AKPUvdbdyzHjLJyELm
-mJF2b+2U/SmFsHife8szAgqVLo5Ur7nT0KBNnFxIc9kMDtLPiD07bYfQEYGAXe/o
-4aJHhwEOldPea6++ycwU/jU+JFMfKcKyMbfPhMEeOH6F934F/kqvNNdiFUtlt8qG
-RILi+DGftNPmDAiYsGIkxaPBQ+Zo3lOnNCefgyjntQvD+/tcXZ981gyKpO2fxRlf
-59pUWsm1Zrho0wMDGWQMeR3igmX9wF4Re7DI4t/242A25pfzKEki3cbUgEKiDVQG
-RdfvbZYtupsiBSPXXkHJiuo74mshwPsqL3snvGmK+eMW+K4n0UWd5l0lv4Q=
-=roLf
+b3J0QHNheG9uaWNhLmNvbT4=
+=dFk+
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1DA784CCB5C46DD5
@@ -9697,34 +5389,20 @@
 ZafLc4+NONHm8bQIzn6BhoN0ZjMmEBvLM6apA8AkV06noo5ET26VxoJze5MerO2Z
 lrSLUBHIdgUmwztCep8AdqE38v9G3ie8qMgRLq8gePIdQdegva/urmb6Y5A16gFE
 3/vTI3M9UbAaRy7oXwO6Qw7O+AD4etiuODW4NP9vDnRHV4ihlvDdwadY8wARAQAB
-tCpSYWZhZWwgV2ludGVyaGFsdGVyIDxyYWZhZWwud3RoQGdtYWlsLmNvbT6JAk4E
-EwEKADgWIQS0rIzcFBrwrkaNFpIdp4TMtcRt1QUCXcSnlAIbAwULCQgHAgYVCgkI
-CwIEFgIDAQIeAQIXgAAKCRAdp4TMtcRt1Vc6D/9JpwQateJdJJ6PeOgPKNh5O5F9
-Kg6QbmOVIBfAS4PNVFKO+M7POrRJXi+GN9AqARB/4juxGxd/DnF5KRss8kNScUg/
-A8Lkbkly1C4GBKrHd2m2+tJxqStXfy/rDitC6KOCXM/5AJ8qVridgFjpgcLLQ9x/
-gG+X20f50gPadhmYiLus4pgdRCPFUa+GdjcjgICb/q4fJUiyaCLRcA/0HsR6wHqp
-F/lY/gO9LfPHYyGuXKjhZVIr29QWn6dvhe3pxmiA1XQNxLBUzw3Z4rgg2h9r3ZdF
-JnZreTuciyeWMOie+DentPHjn5UchBkVi4nN8hltPUGeXso5scrwtDxr5z5Tv4QD
-h6JKjADZ28+1ZMvR9xA4Yv9emlXSSmg+Z0VM/mg9TszWqEvBUmfBp3iE2TSeID7w
-MyZ6DoLtjJeeJ4TG5vtgd8TOwZMPXOdVH6UqCBpKBl7+/KvMvZxqyQSqjPpi7z1+
-FBvMTCFhpSBZs5CtDLXUKxVXKVnzNOsXOZgEB/Mclhy4tWjOlnGAuWCm258s8hro
-a48rZemyLunkwpzJRbaGNxNfRLMbBHj7Eti3cDuWgcuFCr8JDcetoXhZdFZk2em+
-YN7FpPZ/nuZVRu+TXAfYHfFD1DpNXzo2x2LMakNNXkpw7UT6lmYeiixDs+JHJqgB
-yFG5drBimAyqP0QWXohdBBARAgAdFiEE9CuWuGSLXEocQ6YvuykUwfoIEcMFAl3E
-qOsACgkQuykUwfoIEcMkFwCgqgCsjTnW3Eqt9ZA31nXVaxn8ANIAn2YD8qCiQBTb
-NW54Xhfxic/UFc16uQENBFbqsT0BCADj5dHK9K27rmkFscDY6x53w4L/X6AYKmVu
-7Qol7VhR+1WtZXgxZaLMxDxj5RK4sNOIqh6R3vEMlAVS+iYbzahI/A0fcSciCoLC
-gjJKCR3STnTu2k0D/MO0la+wF/bGPa0UADGIJLRCjalkl5uv4c7zZbyLnRl8a9XS
-c81cp2WJTxafZJlJkFOU4cyewwFuH0pwMvc9Wmwhkh5IeBF6w8Asj77M5bzJINXY
-xtKMGYA506609HrvN0+robfgx4Aqy4hGKsqXMsSZiuPDvbdtH3gIRV8NPdYRq+dQ
-g/gv222Y3G1xprDVbl1A1CCHlaUqT2lIFPovjoB2O2SBeX9xKRJzABEBAAGJAR8E
-GAECAAkFAlbqsT0CGwwACgkQZKFvquwWpL4yawf/WDI4VqLkR9RqaX3am/kS8481
-pZPWZUlCCL7jONB7X7eGBit/FjmQWzfL5nWAEB5qhm2qqCgvgtPmVxCrQLECVmaG
-mDFmhGIFh8TQsYvQJPK6HZDxZj97lUKsG/ojOY4ZArvZnsXBU6C963QUZF+P5UL5
-2n1pE/ByMV1R3enEfrYIX+wZslOx5uRFOR8dgUpG/ohh2vkFCaKD6KJQHm6C5lGB
-gUNqGMFxp1nknKJaNqYqjvippm6KcocWARfTHx6Xm3mBqxigmpsalUKAGpjcsxsI
-EY6jnnN/5i5y1XeokTY86fqEt2OSFSkWiApqq6lxMRluTiq33bSquTxSomKfQQ==
-=sfpS
+tCpSYWZhZWwgV2ludGVyaGFsdGVyIDxyYWZhZWwud3RoQGdtYWlsLmNvbT65AQ0E
+VuqxPQEIAOPl0cr0rbuuaQWxwNjrHnfDgv9foBgqZW7tCiXtWFH7Va1leDFloszE
+PGPlEriw04iqHpHe8QyUBVL6JhvNqEj8DR9xJyIKgsKCMkoJHdJOdO7aTQP8w7SV
+r7AX9sY9rRQAMYgktEKNqWSXm6/hzvNlvIudGXxr1dJzzVynZYlPFp9kmUmQU5Th
+zJ7DAW4fSnAy9z1abCGSHkh4EXrDwCyPvszlvMkg1djG0owZgDnTrrT0eu83T6uh
+t+DHgCrLiEYqypcyxJmK48O9t20feAhFXw091hGr51CD+C/bbZjcbXGmsNVuXUDU
+IIeVpSpPaUgU+i+OgHY7ZIF5f3EpEnMAEQEAAYkBHwQYAQIACQUCVuqxPQIbDAAK
+CRBkoW+q7BakvjJrB/9YMjhWouRH1Gppfdqb+RLzjzWlk9ZlSUIIvuM40Htft4YG
+K38WOZBbN8vmdYAQHmqGbaqoKC+C0+ZXEKtAsQJWZoaYMWaEYgWHxNCxi9Ak8rod
+kPFmP3uVQqwb+iM5jhkCu9mexcFToL3rdBRkX4/lQvnafWkT8HIxXVHd6cR+tghf
+7BmyU7Hm5EU5Hx2BSkb+iGHa+QUJooPoolAeboLmUYGBQ2oYwXGnWeScolo2piqO
++KmmbopyhxYBF9MfHpebeYGrGKCamxqVQoAamNyzGwgRjqOec3/mLnLVd6iRNjzp
++oS3Y5IVKRaICmqrqXExGW5OKrfdtKq5PFKiYp9B
+=TxTG
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1F318213E56E467A
@@ -9739,15 +5417,92 @@
 st+ieqPUYvBMCRSqCBmLYZZqRCfSw5Wlg5Vry9cIRfiWqTGmiYhFNHNS3aKqV8ki
 hK8Av964WhM4RGmBqz111I+Gl0uVbhMHveJeU1DxCPXUDdQIyHO+n5NIqotiUrfC
 LVeOlybNslq29R1/iSgMPs84QqL+QV00TZq9ABEBAAG0N1JlaSBPZGFpcmEgKGx6
-NC1qYXZhIG1haW50YWluZXIpIDxSZWkuT2RhaXJhQGdtYWlsLmNvbT6JATgEEwEC
-ACIFAll/iFcCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEB8xghPlbkZ6
-gMMH/AiorWaMYNTfe4ElW/emE4XK93ZjuK6XTXhCDCxigrcLkpiwpG3Bx5wsjNnK
-bfWCijRfJHVu5UbeKyzv+rXXdCBwhGUH9T0qoFjwte8VI+DHxbMgkF2GDhLHRI7r
-C8LiwmkR7CzoTx8kXSwD9vHHjo0Qj/YPmhjDIf/qKHQsHxkqWXcT7ddhsIOEipC2
-QViwyDmDyEYwUGouFcLDNSrOrtC9+h/DqkVyth+NdGqSpgLmQXdrZaYFZpvCejZi
-RPdYtGqBAXhAH/+hV7AhPJ+/Mf/WPCOd06eFjkO6CFl2FTEGLFe0UGnPbGylO6B3
-FVAIRjYPu6HTGnojwVhtFvVXLHw=
-=eQtg
+NC1qYXZhIG1haW50YWluZXIpIDxSZWkuT2RhaXJhQGdtYWlsLmNvbT4=
+=/bUM
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    1F7A8F87B9D8F501
+uid    Download <download@jetbrains.com>
+
+sub    7D1BE4480B61E2A7
+sub    C2148900BCD3C2AF
+sub    CFF46EE3C17E53E9
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+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
+nE2l2ony
+=2zHN
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    1F8CF885D537A431
@@ -9763,26 +5518,20 @@
 GWVUnL7+Cv53rmvwj8bQEuvrQjaeXMGxJKUSm0qpJ7BvkSEmQZ7qnRS7vZ05Iz4m
 uJsTNnuxtf0+fmWNrIqpv/J4RvaZVdA5OW/zA1kKYaGlZoLtWhKOht9gPAKlBk4k
 /fBfHoKqm+vnaOnxSzuYpQkYWqeEu6qzfZOHABEBAAG0JU5pZWsgSGFhcm1hbiA8
-aGFhcm1hbi5uaWVrQGdtYWlsLmNvbT6JATcEEwEIACEFAlkyw7oCGwMFCwkIBwIG
-FQgJCgsCBBYCAwECHgECF4AACgkQH4z4hdU3pDF9hggAoh6EV67Ep7HGBScS+Ow9
-X/gxOWzFS9aMVJw+MzSRRBm5YDSsJ3QfSEZHSDc7gkD6cHUjpUCaB2bAnrfv2wKO
-4EawnXkj9vhnZ/M5lVl2TOi/95Yep3aa+qCgl3jNefzANgrRgXhW3m5ao3YazHVy
-eBezQGKbuBfLTuLd5sknl369zY7IOuP59KaB2BYZfPYqpRrquFzZhdQoReSyhu0Q
-DFewliiCF5QA2AkW7yqrQOh7mEpCPvjX2GfqCsvIF0HoIL6zQuBcif7ELoSnFWYX
-RRpD7b3zshMX4Yt6yOMkcoN/JOGe0F9L0eoK/Cl1oPW1j7tdnnEn0+56CxbNExQx
-mLkBDQRQyEUUAQgA1umj9/gC7yS3arb94xWBPrMpJwLoScdpwrARfh3Acz/LiVzN
-8nfJN/Bs1xlGSa7lIJu8xA5cAflFeCBeVL5ERAQ4aqaIFG5zNXMM4zzHJplfm5ms
-Uw8Q/nD7yXL0I7l1lsdiLl84KNwgFT9eE2mbguaNYDhqN+pD+QisUF7nbPJl70vt
-0lRfv6C5jnHuDZ3RM03vaO6jAWNxWIKGufSYmnUeGdQnNuPeVrlrujzDx+iiRE8s
-xkXFQ6iBiwkRhFj2MHWEpq+TzbfRjEpY5ibJ/B5MiS4wNops8M1o/aam7Q0mIu/J
-M+PLnqfxQ7Wn2yU6RazON86SjOo+Ve6wwRaN2wARAQABiQEfBBgBAgAJBQJQyEUU
-AhsMAAoJEI4m/ySLx66tGbMH/jl9IuuTRZ1l2EDGlD4kLkAWrFUnTdyqQ2JVbUh/
-4kRRh+Od7PcYmC8PwWvT1qGZcYdzdbZEE4sMl5VTCc16jLJXRa4VwhGEMxdvr7ba
-J6ZcfzKBDNaD5byahpnSlxWyEhBy/jfykmBlHn1flV1cPWySFHyyTAMVd6AYpCRS
-E0yw/ezY3ziCgoTEDIqElujNSVWd4pbptswwmwh+YJaviRWlk2THHRX1hMfq3YWk
-07JXS77N1Gu5PG4WJr3DeKXQNRgwEe0Z1Y2m1RZepR44dQuCXrbUXrgc5l8HwJw8
-55T9coGvaQIQsoYHzC3sO4Z8K5L5IH6s0IBCCl8PwRJGRLk=
-=Rjqd
+aGFhcm1hbi5uaWVrQGdtYWlsLmNvbT65AQ0EUMhFFAEIANbpo/f4Au8kt2q2/eMV
+gT6zKScC6EnHacKwEX4dwHM/y4lczfJ3yTfwbNcZRkmu5SCbvMQOXAH5RXggXlS+
+REQEOGqmiBRuczVzDOM8xyaZX5uZrFMPEP5w+8ly9CO5dZbHYi5fOCjcIBU/XhNp
+m4LmjWA4ajfqQ/kIrFBe52zyZe9L7dJUX7+guY5x7g2d0TNN72juowFjcViChrn0
+mJp1HhnUJzbj3la5a7o8w8fookRPLMZFxUOogYsJEYRY9jB1hKavk8230YxKWOYm
+yfweTIkuMDaKbPDNaP2mpu0NJiLvyTPjy56n8UO1p9slOkWszjfOkozqPlXusMEW
+jdsAEQEAAYkBHwQYAQIACQUCUMhFFAIbDAAKCRCOJv8ki8eurRmzB/45fSLrk0Wd
+ZdhAxpQ+JC5AFqxVJ03cqkNiVW1If+JEUYfjnez3GJgvD8Fr09ahmXGHc3W2RBOL
+DJeVUwnNeoyyV0WuFcIRhDMXb6+22iemXH8ygQzWg+W8moaZ0pcVshIQcv438pJg
+ZR59X5VdXD1skhR8skwDFXegGKQkUhNMsP3s2N84goKExAyKhJbozUlVneKW6bbM
+MJsIfmCWr4kVpZNkxx0V9YTH6t2FpNOyV0u+zdRruTxuFia9w3il0DUYMBHtGdWN
+ptUWXqUeOHULgl621F64HOZfB8CcPOeU/XKBr2kCELKGB8wt7DuGfCuS+SB+rNCA
+QgpfD8ESRkS5
+=+Ii6
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    21939FF0CA2A6567
@@ -9841,61 +5590,77 @@
 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
-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
+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
@@ -9906,42 +5671,19 @@
 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
-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
-NahuGnIeJLlv509JYAf/HN4ARyvvOpM=
-=hIaP
+uaP818Ix5nyDVpU9fjESrwAzJliFnn+WYz7XdF2/xlboUrsdD/4gArOe3E6PDQzM
+JeSSwp2Yg5nZst45UjxpItpSahPkIu6iJr2DuIUeVitJj7TWuJGsBo+1jHTHyid/
+JXEv12Vb8W/n6pzV8XkXF2HIiB2qDObzZOdce7+FpTFiZS82tuyaY2XlRNM5/5hA
+cxxwoV1h9nasIG1s3aeJcbW27Pb6TzemjO7iYnWs44s0CxChjfJHo44O17UXI6Cn
+tuXAOFMihBvz9SInsIwEjc4+t+hcUCeFijF5FIA3hQW738ciSp9pCBE8JlLWA16t
+YXqvrLP0BlZRmqNImyyMGpcK0r1sZtNgFmT6nDcf013REnZYn2o+qaWZSvXB2py3
+QKGwnHXEBPzpCuyyJwn8cUWYXjmGWcaQdQFYtpUm3xiQtv8Oo4htVvDIItC0nzRV
+AsR66l+a7lA2xOme1Fg0p/xyvTS5zy9JhdgVHFrkfmhFPV6gUQSMMO+iQ2cdd9P4
+fJrjgiNinNqq8CB4OnqxiU521Yi/9Xez62vEALBORE3vPDeUtNtPw8r2zMrR9hnk
+ocbX8Aof6Ztnqo1z/bk81sAVG220sa4QgLccnXYgCnYl2OKh4PY6dYu7hl4JPYIo
+oWyV5k+tLIKufeDJk+Z9i+/mHYltA07q7B46tkQ1ZflJTjc4GimVu4olmww/qno1
+qG4ach4kuW/nT0lgB/8c3gBHK+86kw==
+=AWT4
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    249DEE8E2C07A0A2
@@ -9992,55 +5734,30 @@
 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
-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
-=MM/8
+QHZsYWRzY2guY29tPrkCDQRVxEbVARAA6ThBzqqNB5UeeyU9CAwxlHl+x+r7/2dV
+dvLHdN7mxj0KJR8qtLl2jquedx3eAxKYW1+SgOliZuTyJpy51YIjgyWWsfJ0Tlof
+cLYNgo0H3EgV5mNGeybeDpAlA4m5fzrjh0Jvus3+VxYd9CCF6JiePcxJinnlHLFc
+g2VrvecDzkLPWzfaed4JmX53OeZQgFeyqfRH0GqGldeXRVuEeetd9XmiveX3li23
+CMtZF/OC6JD00Lpgf3nBPy5ianQ1NFc0+Y8ytGKHSwX1qfEe7QIRZf8IEtNFCxEj
+ty6FMLgMAq1LADu3Uuf+tkhG1a3/J0GHhSsO5kIlxZkOais5/c6qmnNw6XbSnvS3
+XU4/BMc5xJfDLpz3cmkftLCcS64PlHT4aL8EbSkBrrZxDwfdiCS9YdSd76VZsP7U
+s5lS336bfn2Ol9flXw5nwEpYq0grRYE2tn6NEDga2pXhix4kjO9XFg8mPYmPotub
+anWGfu26BV7JPM2USU+8wpqe7bujTy00eUJVZ//9GxumuhxsRdy27hGhQHYc0KDj
+65DwTsgT8FZx1q+sM5APnDz48WBZAc/Dyd41BUNO49EBzgpLj50Bo0IGvXW4yC2+
+6O6Qe96jQPBgA69eblU8uxeX03R8ENTrEj1FFZcX6QlBTBJdt94o0/S/SDf16ABx
+565OAsm35PsAEQEAAYkCHwQYAQgACQUCVcRG1QIbDAAKCRAKSzQ/KlX9rgJpD/wP
+6yXH/oVDS4d4GwMXzy7UoLt0OP/0nUpDzHuYXpMrYFSgUpK50xYqKK5xyRsKyFTg
+Q71btZYa9ZWePZroOn3QQY7xLnzIwfGZUijp7bJ+zk4skGo/OPHeR0tHT91P9YwG
+QgeoVS97zEscNamifj5jGEOrhLy9jZRT8Xo652xnqYAVJAgpwn+HtWNkM6Y1sASF
+4zwv6Xkpx4xA/wntVk0rOYutHOnVaZBPP3pIG0OnYOYGCX5lMW+KAL7oVxiioiDQ
+AAcHCQEx6XMG1stKBBufetML1uu7NV6QX+5qeQDRx4OT8NsdHMGACG68NHJppb7K
+oVzXd9V7PwOHJvlsi1QPTg452EHQZSmlZM4kziVr2SBy2+z4pGquqPvsvhLucvEi
+ylSjGn8Shq2Bgiek130f2iqsvsN+1G+uLdlBQaGfsHIqIyvuAGUjh2x2zgQrcPXA
+qNkL0jzmAA6/UwmHdOneS1Jus6uMgCMipvsT7+SCg6Dy1fi9z6kSAsxH1D7Cxi6b
+TDpg5VxYq1pe19iH4t/SAi8BqxJhXbKOwl1h+mp/mSqNKt4YZilapdKsu8t1C1z+
+TX/1GGOtCp0Kau7/03hSbXUlWDwH6wbI2w9+lU7mXAI+m3N9+8dSKHiXHAiFoO30
+1A8V8Kp9kQnoMl8mxsWxam5wk3dKnf5OoThqicD+nQ==
+=vTQd
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    28852008A47BB995
@@ -10059,40 +5776,30 @@
 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
-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
-RtE1D3ZEqt2nbQ==
-=jdnj
+PrkCDQRSLJqSARAAyUMk9KNCW5epIzb0Q32XbFii3RB+2K6yy/shRYygiDGSvTf2
+UUAXiR2cN46kaM1iJreGslTely4pR5+7Tg2OJPkwEOx+9w3t5dAHUj94Ybv4eD15
+CrFGduWHrd05J93x+RJnqRY1tXaAzkPtN9rlc6gazpf8M4jz2NtkC3Zh9IR5Qp2z
+HGiYFsFLmoo1Bw0VA6reUg70zgSLN3Jq+DUNGV1lslbmPw35saYGskm+5s9j9vyP
+fBGgu/nnepdmb09ThosY98ZLUB+AGBM/Cr6gihvEuvdUrnxzYymyCdbdJnJODEwu
+BUflHlN0ji+gJr/1nXmqREpJXOu8vNtoDARkX5/y77IBqG09jo/gaFWjeaIKGlHm
+InnK9gfORKe/GrJN5M2QzneUnh6TH9kX5jRbSU/ItmkY1ip1Db2jbTi5bG/BuUpe
+pR9z6kJ9D4TwQZ/bGLtdcYhqsalf9Zn6dIs3zvnVxDcQ9TsVCOyOF2GXZJIAOmWb
+V8ptnJE8rSNj7HyDEOAYCy/U40xxvNfrZ8B8Ch8stGd6VWna6Dzj4Anl110V5Rde
+N4vcBvS45jlKEa3gh67zKQmNTRJFzErTz3FsCQyS2/skyyfUd3busYEniFUMxUl5
+y/4A3ao7Dt13NXfobY7+5QKW/RrYlXLG6EqFjskcBrsIPLgOSRuTL2mEY0sAEQEA
+AYkCHwQYAQIACQUCUiyakgIbDAAKCRC1qegbVl6J4GWWD/9PqD/y7qb1mrYly6Z2
+X00WZ1cBhh8nUm6zC0qCQGsR6yPTaPRHw9jP5yrqkAmq2kmd0Jn4lu2jVWxfCltD
+q9+Do1I1qKlqHBsfV0fTuSlMNnzzBylRPdcdCOo0AFX/9qW13pgVP1IMmUPbOPIz
++7t8UbaO5971Y+LKz5cMpGMCgImhLpg0y7PJ2heaj4q0KN5e+T5tp0RjPzlgwPNW
+4akye4bnGfeOsCQofFVYeWO5LTf8y4irV/BjOgWp6ZpHJQBgkHGxsWUX1xWc+F6V
+gNP555u/gr5Y8p30xvnur7l9iH9+R32vUwbpELwdr93Mx1qhL1pzP+h4y45e+esG
+9C+Te8zU1wkCvadNN2suk+/+S1tTthisTAOD7U0j9fVSplf8v9cv9EeQiQjUbFtv
+L18fnxnLFhlC6HSLjFzsjoUM828+iibFXCdQt86o+/VozdZALKsfI0m9Sv0DRMDh
+13EBGe0vdo+WuBMUeszV1Ah0ovO4cynJG2mA4FIFoEEFSyUpRO5sijj/p7HUVAr2
+brz7bqO5bQs0xBxHQ4fsBfpqGiOwD3uxNyKKx5+IP9azLfinOMRWoB0ESfc1Dxb3
+btnboZvkG+qAhJnsYDqf8RcNm4mEu/K+osYaOeiJc247nZkJyeFGL4dIA2cIu4dO
+g9yZ0992trWjRtE1D3ZEqt2nbQ==
+=yzxr
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    29579F18FA8FD93B
@@ -10123,87 +5830,97 @@
 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
-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
-FP3glQK9Ng==
-=rg/l
+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
+=3leo
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    2BFD7825A8984FBE
+uid    Lerbscher <jean-pierre.lerbscher@jperf.com>
+
+sub    9F621F1AE6EF2591
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQGNBGI0UjABDAC98lMPbj3vwHKSwLc8o4oAbOf9PKi/zWxjEYj7KZky4ue9JE7R
+QiaUCY387lWc/fXcq8Wa6/IteqekJBQw2KCrDaKBcAXLjnvO93q51ecc7Eo5U1w9
+xJj31YDQOR7R5XY2x2nvSSgk5CmV3czajM3ZeRYHoVqsqp6QwhjDllp+EaPJRtuS
+Fk5mWk8e//iZcZa6GK5wFEtrZMCSjjofaeE5wrsatguhrXwbtDshbN7oW48vK5Kv
+zM+zpwjLUu3gsvydyV4TPhl0Z5gzHef1GYe88WM5dus+/A8yED8BUJ7I6OkJKNbN
+ivC5aJg3BGvLZQiLCsOLZCYIcJt3ADYBgMVwHLSLmCoNym6sS61Rklu5Ig5Ax1eh
+X1aGL06v2B5oM+pHhEvl22iXIi+AQJYXYR8Lg1MBJzEz+aCaX+h46u+GcjEt0EV+
+WGzvQgBcA5qwEvbSKewu4fG/5SuyEk53ZXHwIJHC11kFQ9SPExPnqIdHHIMFjDj2
+8OG1WtLMdGzxMCkAEQEAAbQrTGVyYnNjaGVyIDxqZWFuLXBpZXJyZS5sZXJic2No
+ZXJAanBlcmYuY29tPrkBjQRiNFIwAQwAqL0vVX+5uzucp6vQitUmOuEyiS0SWz2w
+ldKVJXKABnNKSP7Irq0pF0FbfvEne8pRGz35NVcAi7KPYwnbXtX43tmUJsO8w0Yt
+i/eKBJOzPq5bxLvMabKTLaO3y/NLmRuxCBwre1zkdQGMK21O0TdGu0bxJK6YEJKx
+/8cX+8f8WiVIlcN7IXlTgkeyz4CohaQfJibuPi0YSYmbhnjIuS8m6S/aMduCvAim
+qzIMk9rgRcbs27JafreWG+sLZ8sYYodixyBHBsLKVnGdkJXNAOwkBD6OCfQSW2DO
+ZcNLpAb0yzwxk3sS6pMu7a8lqKDTmC6xCjNhdykoKn9r66AdFLZqRtld8S/iTwrd
+RyLokVZU8cfn+g7uOLe6scKYnKh4cff++QHSjDm5elY/Gr3b371N4qtngUl5TECi
+QK7M311nYBpiGnIu3e+IuB3VFET0HTTetQn5o8qTDoq0BOyhgOErQRvy9DmJpKfq
+3cbMgRORbbJvCuZL3NSEJ8Y05rEKgmabABEBAAGJAbwEGAEIACYWIQT8LDH8Jf7e
+TnrQ0Ywr/XglqJhPvgUCYjRSMAIbDAUJA8JnAAAKCRAr/XglqJhPvmJQDACS8HNp
+MORfA+QT6/GGlk/lsrI26czkJaB/8iHoWj8cY+9l7AvBroVVF3x+HGsE5NfPpmOr
+cqcV2DQDFtOGJltQAFE1OONXkPyU4ZM1xqMKe57miF8JP/XkExIPwkwWWAEHquxz
+cHNVW1TVTCW9RlJ+C+tiEQvSWPxQfcl+gXQmZhnplJK/rYz37J1DTRZavj14sk7+
+BlEqvQTQxleStxAHQUii/v0CBRvsLeb7ahE9K4ur61w90jfohPaKwXBSmSDgkrbO
+FlxqvO0CrMXnoLg9pd1f5SSscKlUf6h6Bc7I/sRO66vuMM934otQ6LTxFFCs4GnK
+EekW28EKAWq2Nir1hf1Hfv8Yujm66nzmlCLfTyaE5nWdh+4j1ndkpF6hPjYT8eba
+3EHUrlA6mkn9dULm/AK9iiuYcpm4n/gD0d2dkualqCnwXYkRa5aqZyBrzebS+P2s
+Z/dgWN8Go/OXuVsg2aBbH3vXvKCvJ7ZXSx5DyrHl3ckKrNgsl4RkTgS6lxs=
+=roL7
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    2C7B12F2A511E325
@@ -10218,14 +5935,8 @@
 ypldMGfrkfLsPrUZTT689ubbig978e7eYmJEqldtaIwaAzHQnB70wIJyg/rEwFUM
 ldsvs6t6czSuJ4zPMvmh8TMpTg9e6+DMktPl1CWRONl8RPpgYMIC96gb4OnfDDjk
 Ex6clSCwgbDwdeAyOjjR6pVq+pCNTo1Pcj5jABEBAAG0GENla2kgR3VsY3UgPGNl
-a2lAcW9zLmNoPokBOAQTAQIAIgUCT5k74QIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC
-HgECF4AACgkQLHsS8qUR4yVwIwgAlj6R9Lxk/GnclmSvgtTyk1r5GxJ75iSCzs7q
-DlNuM6EfFbuZvLWORGgP8KtdQdh/bdICtRbh2eoW0vEKJKkl0ZI0/RnVMYKYpIHi
-KVUSjPq0svwDk5tUa7taNSdHfi9o1G1doBHyPu1u1xDVzMSWsbEvx6aVr2HkBDRy
-A+VcmnX6tokZjyRX4C9ldX3tR+ffn7v2Iy0bIesM92nMPnH/fPXWlnbQKTxQeMj2
-qYSl3H/NGp57G7KNSl1na5U8ueMcUtYb+eHphMsmaEb9xw8dM96CzIPZh1xMHNoQ
-NNJibURysbUpFlGUTOv6K0A392QkX3GwuCQGOeLWHOoQHD6Jtw==
-=yvny
+a2lAcW9zLmNoPg==
+=RDsv
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    2C82DBE6C780E2AF
@@ -10244,56 +5955,8 @@
 gZmxpkG266qwACmI9F0BbLlTHbokJwQlaj1gXmroZlsuEGhghPrcfumYG8wiNeVU
 0M905+/Lr3bolq5V9RVmfOGP2BM0BtB1YUktttGg1pK2daRrCLQ6U2llZ2ZyaWVk
 IEdvZXNjaGwgKENPREUgU0lHTklORyBLRVkpIDxzZ29lc2NobEBhcGFjaGUub3Jn
-PohGBBARAgAGBQJH/UYPAAoJEJA4TZo1x+lCx28AoKWTDvQTptjJp8vrSnqfjm9Y
-pwINAJ9v2cXOh9GcZXNjUnVMr8Wm/qP0s4hGBBARAgAGBQJH/cZBAAoJEBg4H9dL
-G+aYugYAnR3x/3SJUEgbPyOwW+cFo4WlvU4PAJ4+abGz9fIJZ+Jkdg7s7GJx65Tk
-24hGBBARAgAGBQJH/dvQAAoJEHPdjBYBUwI1A24Anipn0BRnhV430DqGmM4Ccq7W
-AKrhAJkBNbdwpou7qpndbwKNjeS6H86zEIhGBBARAgAGBQJH/k9zAAoJEP1viMYh
-0KcbRNMAn1Pdh3vyICqtbxS3E9Shpdc54PG3AJ9/7xO1nAmp2t3vQdDdJOGTI+pm
-IIhGBBARAgAGBQJH/1LzAAoJEDyaQgQMCIW0T9EAoJcZjzM+GAJyBKnY1arM9vD1
-KbU/AJ466BIuracd6SxJgvL5q1uZ/aUX8YhGBBARAgAGBQJIAgfzAAoJEPXCYBZM
-7tdffEsAn2wgrvwSQUgfNflj/L+0Zs4Mmyv/AJ0ZmfIq3TVQalOj0wp2KnyufPlg
-rYhGBBARAgAGBQJIBT8bAAoJEI6jsGhMdlfxMJQAnik2JWgwdK9RtPa+RoOij0bD
-SjWPAKCY/d1kVFQkP88dT1nHr/k57zH6gohGBBARAgAGBQJID01JAAoJEBhhwyLF
-YBSyW94AoLdqsfcd+8DRM8e9B5DWJ9/NEf/dAKC/Bx9xXJHiZHjWa3SDQlae2Fk3
-M4hGBBARAgAGBQJIIhYcAAoJEA9FCiZiEL/Akb0AoIfnoDgX8rf+EGP0IXGcLQlh
-Vu68AJ9B1L+z4Mr9IPnm97tB7j8Ps9uM3ohGBBARAgAGBQJIWUNcAAoJENUzTnWx
-MT3ibvoAn3Ck4VQVU1s75oB8zatche7tTIgaAJ9HdaEGkNYOZRKsyvyr2CyzxXXl
-7IhGBBIRAgAGBQJH/b/VAAoJEIuWKUP8JD88aCIAoKaCE48ZlYWn3p3Lsv6UZJi8
-QPyTAKCl3kvf92hwgvj9DJ5nSX3sgJlSOohGBBMRAgAGBQJH/fhMAAoJEC65RoKI
-gXQCxKcAn3E+eyLDiv7/epmYZYZkn3oVu6hdAKCTHPuqCm4pRzR8pGsx97GhIkRy
-u4hGBBMRAgAGBQJH/hGoAAoJEIEvIIXC1Qv7+14An3bt2P2VgRvWsjsq2jI/mYwt
-G8JKAJ0VuSGPEbr22PWbi1SlIiZJyn4p4YhGBBMRAgAGBQJH/6dFAAoJEEwEKBgx
-Gj3lcvgAoIFvmPRb+p9Y1Lr2NKRXzjmKhP0DAKCCW9XxnFXIvaew4wXgJjpwCk0I
-gohGBBMRAgAGBQJICfTsAAoJEN6A5lYZ+SkY7YkAn1vQVctGYbOmjM9YDazk7/LQ
-LU+sAJ4vEKHOpXQdw98vXdfPHZZOiGYhmIhgBBMRAgAgBQJHAOUeAhsDBgsJCAcD
-AgQVAggDBBYCAwECHgECF4AACgkQLILb5seA4q/1RwCePJtZosV9r90X3+KwN/Q9
-9oEe2ZIAn33mm7ERj3Z0j3twcRrOWLNHG5jqiGsEExECACsFAkf9KmEkGmh0dHBz
-Oi8vd3d3LmNhY2VydC5vcmcvY3BzLnBocCNwNS4yAAoJEH9YT55N2dHEmgcAnjZK
-ZeA4XX45RsPtpGqP9GkIUpnjAKDay4XNQZYXAKKQA5p7o07V97OcW4hyBBARAgAy
-BQJIC2TXKxxCZXJuZCBGb25kZXJtYW5uIChBU0YpIDxiZXJuZGZAYXBhY2hlLm9y
-Zz4ACgkQs5fdGlNtq+0UyACfcp7V5M3urmcSXqrCAHG48BHu3ecAn1sViO88ZsnR
-J7lkAxrJXyRg8ql0iJwEEAECAAYFAkf+Td0ACgkQMaY9luwUC4GJAQP+J/gB8pBi
-LgbU04hG4fADK7zdgqKMMncAfZTlOGWi6/sJxJMLdYKFxyFHt+aP37WVWFawFfzD
-oYAUH873+pzEjdnP7KtCyq+/aNLVUGHRRUEXlyQuJuNui5E29iPKj2aTODOOLfEP
-RCysoYfI2YAQZeP3aDyI2gMEAZUx8Oi3PUWIygQQEQIAigUCSAN5GR8cSmltIEph
-Z2llbHNraSA8amltQGFwYWNoZS5vcmc+IBxKaW0gSmFnaWVsc2tpIDxqaW1AamFn
-dU5FVC5jb20+HxxKaW0gSmFnaWVsc2tpIDxqaW1AamltamFnLmNvbT4iHEppbSBK
-YWdpZWxza2kgPGppbWpAY292YWxlbnQubmV0PgAKCRCLOmAfCMl15Uu1AJ4+yrE8
-dHUwlkfYKzURqQY8Wae4uwCePizcxOJZQ7Crcj0Aorqo8MugLpaJAhwEEAECAAYF
-AkhEBr4ACgkQGT8YCrVdmXd+ghAAqa0TTEEZymPDFLGRKXVnLAVAL3zk7MhOnVvd
-CgpneQtTpkpc9NTMh4zB5UuXXTKCu9upvGuNHZ7WEyIvRf4RJch6ygh+PDtF83r1
-hx2nM21erhPs+0TbroRv/cVJCRVcJ1FfJnbz8kU8xR4s2Zy7inktdNW/wTbn9OGo
-7Dpujn9QXhuIwp79LSTInhXGem2elVCW8ZRSYB4QxqEREQYLFi7ZWZaZNi7kOsG+
-Em00eTmbq/1TEtqz7JE9BV9OgMEXpD/PUaUz50/d708AU+bsnlWVfa7FrGKb/FKr
-veY3LeJkkHhEci0lU6bQLOWv9lVN0VMy8Xn2ZGZfvWBOgYqtR3h7Qzj+9YjxhbRx
-OecjqgJvGRGhlnuJa4ZUrjfDZRIjrrZxpRWvh8YVXWodlysjs3g5FufTdCq6PzF8
-o3PlOajx3/ZcsSvej629Dha7iNd5dO8SlzUs9HUpteRyYkjhzb6pmCwfevF8j5HH
-/E4La5Ki2rjFHVkOpTEs9i+c8nb2lWpkEUqtAGepuF6L87/IK6VK4PiuL1v05DwI
-Jn0YDnXmHDIzAo1WD/HmXfQ+Z/EexQAAgWD5WY+jHEipabqcTse7FH5ZPnnycDP4
-fxsJaoH0Q8T5xAlU9oKzdQunGazaZSHoAEibLk92LQlVkSjFLjzOXD7G5KvkXgFr
-d7So9BY=
-=0Kn8
+Pg==
+=yJC6
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    2D0E1FB8FE4B68B4
@@ -10314,32 +5977,20 @@
 7qOldsje5PxeJ+x6sgtcJ0kKrK5uv3Hk9gTA9fq5i1UKz8C0b3ChPdus7WoYDTiw
 RUB4+2WMtAscGnmh+8jtNVSJIaT6Azc3v+8JiF9lbek49+sMLfTZyxI2Wt8tACpY
 EpiuNTn0R4U4+bKXxfMh2OJ+CfVYvR7/xdNw1OonK5zk2nN58cllAuEZLwARAQAB
-tClKb2FraW0gRXJkZmVsdCA8am9ha2ltLmVyZGZlbHRAZ21haWwuY29tPokCOAQT
-AQIAIgUCVhVPgQIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQLQ4fuP5L
-aLRzbA//bzEX/3KdtPGEEuWXaF1oz6VjiqfdudZdJH/GVrwUda7f48wPnMM6ji7Z
-VXgULYK5CkF40M0WfCSQeI1UEmtiqSYx11J/fo82oI48/A1k88IzcBS4eG78+D2p
-+hU1WOBaRJ0p2/IORc3dGGIhg50I8m14OuAHiCIKudwfg5o16UUlcOy8Fg+zOKYT
-uqB/lWzO0czKZE6L2aYHnj4cNLuqVyy2TezV5fd1xQnuoYr+CpQnjvVaugzPn7SM
-0ljfu7Vy0W/h8B+V96G9h4BSmSbHqhLhc0yIoH0SyjnTvSTSjWrn7iRNkIvczaBy
-3LmiSw5rvmTjNX8KHuWrWWSlZEfP/cnel6NHvn3CIu4PTSwbNNByNVEQ3kh9qddf
-13eKCKlKy6JSQhbFeAikVZjhB+9+Fafam/KB4Us4D0bO44+U1A0fB6iyf9VYzTCv
-Ch+mMoamEaRQgMwgiIeUCIrvrrfGVpweprJCOyMWHhnIyxCxnxxJboj6Wcl8G5OX
-i9on/zl36h/S473xgi6lG6cWuyQ26FDxBG5R4fs+u+i9x3gdlng+guwSYat+KlwA
-LjRLLd5sbAqLgbZOz2+3PiU8gvVIKSHthvRssVwMiZB4BLPgFak3Jap5lzalfswA
-/kvubnz7ZcBH+OJNWJ98R6HgfRtfVAd5tog43GZ1H27HDXN58vu5AQ0EU8p/tQEI
-ANUMg0+IW8Nk5/7LTwSv1r5HTJgz/88wuzBSZwl5vphs1QDNoSUJ+jyCYxu8r/rP
-GAv82vLLfsqveQwovXPqg6IkTGCqS7hswReLGyFQs19wmS5kPd7cTue6DY2GozGp
-qN54iucvpl+RiEedl2aoPJsczWZcHrPdCcAmmG5+PwjquccDLKPsRSttjJGYBoTT
-3uE6Nss1VwPJrpBaMIgKjJKLUfpAhVMJYh2B/ZCFPAdNTUb5RRf3tBshaAG8BzBB
-kjmMFQ6NLpW9bY33mXhddr2XilzpwiGBHBMW0IL8gX+lwYgq6k4N1AFNwxy5EZho
-KvoZrAprkTd1hJqYvvKCwu8AEQEAAYkBHwQYAQIACQUCU8p/tQIbDAAKCRBSkx9L
-crT1TDCmCACbHE/My4R/BKoAMtGOt72oLBmfEucBjuVNfek4UnZxLUR+zTK7k3xa
-0kxYWamDfZLo4GZnih75G9W2mkB31KlnNwbr6Bs+rBdFQEL7AeRj0V+Cv89ndq/5
-lod8oa0ZBkeWBEBUwlAbrkUzpXjD1ww15bobfT199Icw/RssYB60rXmRSnZ84U1i
-p6ssgfzgoxPLX6QFbmtyXmssF9z+iZK4dWne9Qh2umrVtw6SZcsvEZclMQ4d9ofY
-F8oOLx6dci1rfeDygr5URDIjPkyBhoMyvIS6s3xR7HmiEnthnMZ+VXFa+BoBG92c
-JSV516QXSWWjZXNhYZjVqNCOxcVymFfL
-=2gUB
+tClKb2FraW0gRXJkZmVsdCA8am9ha2ltLmVyZGZlbHRAZ21haWwuY29tPrkBDQRT
+yn+1AQgA1QyDT4hbw2Tn/stPBK/WvkdMmDP/zzC7MFJnCXm+mGzVAM2hJQn6PIJj
+G7yv+s8YC/za8st+yq95DCi9c+qDoiRMYKpLuGzBF4sbIVCzX3CZLmQ93txO57oN
+jYajMamo3niK5y+mX5GIR52XZqg8mxzNZlwes90JwCaYbn4/COq5xwMso+xFK22M
+kZgGhNPe4To2yzVXA8mukFowiAqMkotR+kCFUwliHYH9kIU8B01NRvlFF/e0GyFo
+AbwHMEGSOYwVDo0ulb1tjfeZeF12vZeKXOnCIYEcExbQgvyBf6XBiCrqTg3UAU3D
+HLkRmGgq+hmsCmuRN3WEmpi+8oLC7wARAQABiQEfBBgBAgAJBQJTyn+1AhsMAAoJ
+EFKTH0tytPVMMKYIAJscT8zLhH8EqgAy0Y63vagsGZ8S5wGO5U196ThSdnEtRH7N
+MruTfFrSTFhZqYN9kujgZmeKHvkb1baaQHfUqWc3BuvoGz6sF0VAQvsB5GPRX4K/
+z2d2r/mWh3yhrRkGR5YEQFTCUBuuRTOleMPXDDXluht9PX30hzD9GyxgHrSteZFK
+dnzhTWKnqyyB/OCjE8tfpAVua3JeaywX3P6Jkrh1ad71CHa6atW3DpJlyy8RlyUx
+Dh32h9gXyg4vHp1yLWt94PKCvlREMiM+TIGGgzK8hLqzfFHseaISe2Gcxn5VcVr4
+GgEb3ZwlJXnXpBdJZaNlc2FhmNWo0I7FxXKYV8s=
+=tb1M
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    2E2010F8A7FF4A41
@@ -10361,74 +6012,72 @@
 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
-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
-=mulX
+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
+=SjFy
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    31474E5E7C6B7034
@@ -10452,66 +6101,54 @@
 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
-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
-=Mj2z
+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
+=c09t
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    32BAF781C2D456C2
@@ -10632,183 +6269,20 @@
 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
-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
-=n2cl
+b3JnPrkCDQRLlcpNEAgAgTuLUinzEE35uMLPPLoSgifw6tHs6aeynKk45FwNKOKH
+XYlK9TC4rkJg3Ql+MM4i9BjKjCKggdn4XVd16bB107oWogrgHq0SWAjUR5Aj5Bt9
+Q9LWmIbGtm6NY54K+vlyi6EGngGIE1XXEqxSn3cHYB1Eod019ntwxTovIXSj8S1/
+qY3TX8rB+pZJJCefFq0XmhnvONo9Hgu2C1K3vyoQMQ8cpzyUStQ39LOPtZiV3iSz
+E1AbirIJ+DFAaiXz7re1vWdcSGIFF3QtLOvwMl/PR2m+WzxZzX43e2uMUraFwS1v
+sBB1O5V9oXvFvQZe9Y3WNLaAb2rEDRmkosvXC71eVwAEDQf/WpApwwk3KNxIDJgo
+hCtbqJ4qWE/LrOKQbd7CduXQffyALXh/5Xo1SxSUlWZxM8yN8x33TyQplbnhCnWk
+dUPrv9wCFVy3kMyATTbXr0rbTYEDtE4Oeb/vhyCOTNu6+23q4VOMaGrf5kAvcUMn
+zrMu1kLy5ONX/lK/SJHQAQIiW7KUL24ljgGWx72i2M3k+XGTT++oF4/x6AqAcPTP
+IxFU/5E1windChnIWP2iEvdmMxQcZqO/3xHunaAhqc9DhTWMwzooyKcmpe4U2y5P
+bwrLxVgM1rCLuoo9Lok8Ejjcwm3XWfwPPNckJsVoHoiI9kF4P2ZdXCq6NoktQM/+
+hdzOvYhJBBgRAgAJBQJLlcpNAhsMAAoJEOeug5zXxYiG+EEAoKyXQKMPuPkYKOca
+9ka1cLl2cblQAJ9aRA6o9FEQjKMxy1iZPUGT1Mrong==
+=dHJq
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    368557390486F2C5
@@ -10823,15 +6297,8 @@
 3kdvcD0+J2cESp/XIwGEKU6QxYglbaXy75BvyMhCLcPll0GO9JPzrqLwPlXO6RHw
 dmjT6wWBpu5UPJI57BCCNToCQf6VJTXqsEBYD2NBt+xgBP2DGqbCArGKRSUBXeTG
 d1WXACnGfAv+73E1Ix66/40sfeJCGajV5wvZABEBAAG0JUpvaGFuIEhhbGVieSA8
-am9oYW4uaGFsZWJ5QGdtYWlsLmNvbT6JATgEEwECACIFAky0nNECGwMGCwkIBwMC
-BhUIAgkKCwQWAgMBAh4BAheAAAoJEDaFVzkEhvLFXokIANGwT57KaHcvjQToJJgJ
-fPPMxoYzJsGijjJIgtxReJT/64kjhb+AGLHO2ru36tDOkQeRjvfq7AOVTXlotN5t
-PNspBH5Uy+GHGUzjE/uTegI0efEpYF/Psrw2YsOa/toNyWDYzXllF8WdZuWX1e33
-9lCR6pY7vroto1UDgff7eMwq9hdUMYCUOIyIO5x1fk/VHE7rZQUPUmoPCzwruofs
-v2jNroi/AMJt+U5SCoA/JUWl9fSehWe7up4ScXfqo0LzeHs9QZtMlnQHwYOQyiMA
-F0hdL5xkKpWqkyXbduJG3q1ItKZ5w+H9yGgudXqyUhr8dikwTomoFUj5KQ2gVDCd
-9Dc=
-=nnSm
+am9oYW4uaGFsZWJ5QGdtYWlsLmNvbT4=
+=sqrd
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    36B9CF3BEB52D771
@@ -10851,20 +6318,8 @@
 kNbb0eP+IoWBMsB3oIcfB8un9ewVGepX6Pi+7VtQrGWaY0ClP8fNpkBEwrNNudXP
 9DA/JDet61Ak+unKJqNjg2gfJ0b6pYPq98KuPPc7nbWS9fHoD9fUlD3BifvdXJOz
 xKdQQegL30N5q3/LWOtw0g70bpDvM2CG2jOO98SSJNjM9sqyXQ06HTWOuwARAQAB
-tCJEYW5pZWwgRmlja2xpbmcgPG5ld3RhYkBnbWFpbC5jb20+iQI9BBMBCgAnBQJW
-tIFhAhsDBQkHhh+ABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEDa5zzvrUtdx
-3VMP+wdtQLTm8S2+8MX+7IwE/nv7/hgdlMlNH84s9aKBC9xy1r3CTb8z6uFcnd+b
-Ci0nP+268CU2NzrZB6G6iE+Iz3YAStKwSq8jlFs2ZMSNxoFAWkUBekZ0BYAmP1y3
-ihYbqlX/CW7F8ak7Dxm0YcDoSUSntsxQkhYEir3mN32Jrh8BL7nHJJDHKCwRpCFC
-ccrcnE2QMRe+syjkFzaZWsHOyKgjF/1/LyR+wKwAP/7bCJEx704OIZ802P7/n00/
-pL+cF1yXPlTLBIQ7fSCYP1GpB4vmPeNLLxnaclXllAqjk7TEFVdZqpmC3QoXmh+/
-/XJmFwMMUWHqZkQ0UCSZ5gCOpEwQz/T7Sstbgd7uvKoU2G6XIqWKTCSAWZygWkBT
-hg83mgkvTsru+6ox3Z4EdXHG/U7JJxOe9K5F9TbIEmmSrCO66gWZOqfJWc70SXCV
-6m8DDBC0ApGmrryFqfTf7C/XKPxzV4vzX2KbUTbahfyY0TCwlpH/vN0qxHxBgaCT
-LZDGbwtBLy4tiAW/jrsUxafOxc7UHkfLzL3yWcOgTkEf2HjDcgnCTralzjHb6nN8
-I56L2rd7FldRHMlr9j/q+G7/w245iHCj8bjlE7jZiT5nyCDfDypLNSgHbdbZ66du
-shtOTNl8U5Hp1puF0091jNVwpnVniUfSAXdXZVl8lQ3959Um
-=N2Ed
+tCJEYW5pZWwgRmlja2xpbmcgPG5ld3RhYkBnbWFpbC5jb20+
+=3Ose
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    36D4E9618F3ADAB5
@@ -10945,50 +6400,38 @@
 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
-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
-x5IFzhjJ
-=pygD
+tChCaW50cmF5IChieSBKRnJvZykgPGJpbnRyYXlAYmludHJheS5jb20+uQGNBFu8
++5UBDADRdVG7GKTQp0fnj34zpntbfzMxImRrEZ4l0IlmjkmmChTKyt0XbrfpnUUl
+FnQwaaEWQZTt5OUBU+VqA2G4H3ZnLyjdI76E7iURzAHtJXHpF+fo6x/ouDMC2JlX
+UveReR2o+5WO2WNw0mN3nmXX0vykibl/20SUWZno7DNixeq8DUYZQAXMiDFNWwUQ
+kKXdTYApD3CqGco3gnOoV3zFSLglIQiYFRMNgjjsMIGJvuYyji+koVeXo5OuDZIZ
+hl+mcQsbs+adpg/FiT2tu5YDRggFuSUf7DFzorJgDwH8AXsDAwbNQ+IW7P4sDEn2
+GglttzMXmmdQwPzHHKKTHC4gf62MFeBuqVUOQBWCgKdmN60vCf+Cz9r+1DBctkPz
+QVLj0VfApV/UnDxF/xoRKWLMiFXffDf5BBhmGAu9xxRCygoYsyQN0teFvbqYX/S/
+4BD5maVDLGY6VE/PQnG5xwV2rFIgoEPZyJYCVS/mVczbY9dHjBEJoi5M9a1XpNsL
+kO2ALtsAEQEAAYkBvAQYAQoAJhYhBFzjJZlqNSEzJq4saJEtLA7M2lXABQJbvPuV
+AhsMBQkDwmcAAAoJEJEtLA7M2lXABgYL/i7OMbPiMydJNpZK09xZarK0DT6+Skfo
+tP0EcubvNrWJyEpyjQyFV+XKnZ3qgYEpanPnX4PJwmsWBrJJIVNjWt/8oJOqVnUD
+Q+aFHN9cOGHaOZ7kvxi6WeqgmXrs54VHYrebovdf7Aj4eHCBFk5jzSfFYyR34cnr
+bLkY/DyiRK1PPSIRnMW2+8YIzVZtsXD5ua4tEMLpIjb5qrpZH8Ahk7L7UVPlzGzx
+/JJDVvAbgnrx95DqaxWTYJGQCIFeeBQduwtUwhVjPgFHGatJdZ3HO6lZmKAzB5fD
+9yXgeJc8+40CeOV3QvOfvv4hdgsrm5FcsRSiioHQf3CSJ9UGacJShxG0C+0I7BKd
+hPh1PWfiAeIeHywEQ30tferUPv/iyyGI0eZB3yQyezJd6XlB+HEk5O1oJYL1t7aW
+V+z2lUHnimg5F8PlawWf70h3RptfxxGymOiXvVQBITsU282nX6lj73aJld56jz5Y
+El6wbOtdFkIeOAWOUx/hzMmSQXxEHCKqGLkBDQROOU3bAQgApHFSdgwxy8CX4DNi
+fdRQoprGG96dpzKG7E1nNo9sLbbY9Nl3bAB7uPNc1gDVpxvgS4mm5k3F3H2KMes2
+XYvMq34n7PQ3r/le+gOM437YpWA4QAdQYZxVZoj5mVcII2jvD9BO8NULr1VyvBJu
+ULTON4sOKIOvqnGLLW0Pd5igukgKBxqFpjYGfJqadE3yrFiOJ1z+DkIfudiBlNqj
+Of0BzK+vL+08Nqae/bEAnPUwWdGx2QI+QHNVjIOlHRe0M7WO7naen3EdzTkfH5NU
+OCh/o8aLyfxwHSr9aJ5FCTsXdQkn/NtNE9igOyL+E539tSoQJKG4j3OFsgQiH/RX
+1KasYwARAQABiQEfBBgBAgAJBQJOOU3bAhsMAAoJEAmKMfOBgZBXYewH/2mytdow
+DcKknPDjiL5MwAvqs6sEk30MqCvZohhFnnC1arLU0pO1QaSZqQPtliHARCwIYdlu
+IfvHIMrFX8wDvSCntzw/johbJ5Bgrgg9QW674S2PjqrC2foUnksSt1WtGWCQr2xx
+3NY0uy/hTefV+LFtCgpIHpiJ4jHad32+cIJWuKGetWhdnbTfnG4ArGZ+AVicgb0O
+jh6XWW3pmvsSl3judWv7p/2hywYFhpMierlFSJvycQXp1svYS8UOiLT6qehkQVGQ
+UmJCvpsFmyy5Jbro56q4JEosWCAi9uXVeW3SSBW4jbu3plJNMrnBvBgPM8TI8G3+
+5hEGf2vHkgXOGMk=
+=nYS8
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    37E9D58EA4598641
@@ -11005,39 +6448,33 @@
 2dyGJ59/gjtVaKD1geiHlKEdFFKn9+ZuBB3mcTxpuCi6w29LQaoju4NIPc11xH08
 dOydnfSTInTbwxIipiwU7hFOcSzguni5OH9d5KmI7XSCkl8L6BFMprm9n6XyBAEB
 G26971QRcqD+tiFd5Pm8axMjpDr3BMu1q9l1ABEBAAG0HkRhaW4gU3VuZHN0cm9t
-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
-=j80v
+IDxkYWluQGlxODAuY29tPrkCDQRSk89cEAgAjxMmDMl2ElKXFXgWpsITsNUuxm3M
+wQV0oRBDA6YSgLxpf0FqHh0+5W2owt//Wnr7jjPF9xmyapTwkEUJr7W/m8XYM5e/
+VraPJdruYI9D8PTxbiLheUfVjXvds65FK55Fg2pj3tNFTDY1sUcWjvxW1sqlRJpu
+mALTNKLEUSXeFTokHmRyZ8knEIc/0GiyDIw1aQ+q+BNDwE7hUP0C6CP7Ddtg1nVx
+PJhn1WdqzM+hPzO1thUyEQTGRtoskVV+jYUXyU8iK5WSoNiGoxLCIWiqaC3BuzMb
++Q8//zwYdcWC36UZqSpIKuJVmGulQVxO/1bFuQsHqa4ovGmITRO+D/I39wADBQf+
+OlVr0544kUJspemRYSkNP4zQtvFCmUW66diQNxrHTHkOkHQeJbx9S0LvuZoL7nle
+AbKGoPEAD+lgF7jpHV4JrhmSLih1Ocuw3gcnhrJws/+jUcFZPtarJ1pEgBQdCxZO
+9sc0/MG3j1Z1zB/EA7KN5w3Xbh9f1VrGar4G6ZXkIcdRPb7Ka8WwfKAVkpU8j4zp
+Y0rV7ilbUY8liYeccw0wCjUYF/8Ww5Tj8/dP9i7G0hutvbFQFCWKfJ8pIF3QZsV4
+nl+mjzXpsG2kTpqj1dRWadYFKf9KBw2emi3T/GwAXdGVcwbo2QLD5OqILmdmlWtH
+o1bsxRiUY3ALKcsV/XrfhIhhBBgRCAAJBQJSk89cAhsMAAoJEK5af7YIoCIckdQA
+/37R5B1w9Xr816QBoNrJIRSKXoaqqt0nvvzB6IAo6+u3AQDURXWfVKKr73oZFosn
+Ot63VHnW6BV9mMqjj3wenu9FlbkBDQRNR3a/AQgAytfGzI6+vWpyCqklxlOH07xF
+wPKwFLoxFdpfg/BuG5bWNHPeB9MI+oDhApxqMf1Nqa5vtJYOmY9exz0OatgHBCy3
+5PUXHE/hckOMrNQvIRIqtgs2vRA5WUwzRvAmC9lxVLRTKXuawf9UiVeiEYzBlsPv
+SQIvoQ4yTbfNfVIz+T59JHraXzhR/xgFsHn5i40R4TtP186PGJSx4gcnfK+VSujB
+vjagGU4Mx5kVlO7//3BN9fEmTjjAnt7PmA5OrJL+H7oUAIriY3Co/r/Bd/qVRzH4
++KU1Ax+9PO8jORK90DtK+kbjjQvc15jzGBMlT4lh40l5gjH4rwPHmCehXgJmwQAR
+AQABiQEfBBgBAgAJBQJNR3a/AhsMAAoJEIBmL4GS10mgmo8H/196lX9UEXrRRBsq
+ATkCdy/m61+mfud5e7XN+w+u8HKjexJoBgo4Nv045/LcHOV6jALj9nnEGuhhBhit
+EZofWSz9z7h6PZYtsp0LAunkDweAoCSu9prDOwv5X/LJqao00XQFiazkhjewEspu
+CYrupYt8LpuQy8J/6xNoWi7Gd/XJ7gQGhhdx7GqNE3Dr7JeqLM+IhISGq6ZsJI+W
++XUhhtVA2oPB+W9jsyAaZXsPd8H8v4AAANzqi9xlQ/DPx83BZszDbNUk8PwLadSs
+ClHJKbNa7hhLu+IEJm46OEGjZ0D79FwmBvSZxlrjfX28JwiTKSi4l8lARMzxJP+k
+BRbqLK4=
+=qP9f
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    38362FD5CFA2668B
@@ -11052,15 +6489,8 @@
 5b4l0kz0XHHaGDUXhTLjR1s0gaXpFEETVLZne8x+CM1gGDliwQmv+mQ6DaqrPpLy
 rx4UYaHE5YxlsMvhcxkikF5wEfjOIgDqrvXiBz0YeabQVBA3h6IRekzpzu5R0FiA
 Q4gRc7nXHSuT0zy1m0wEfswKLqua25b5DPZLABEBAAG0NkNocmlzIE1haXIgKFNv
-dXJjZWZvcmdlIGtleSkgPGNocmlzbWFpckBlYXJ0aGxpbmsubmV0PokBOAQTAQIA
-IgUCUEap6QIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQODYv1c+iZouv
-LAf+Juc4mE1UMlShzNfebTCOY8k64zs0bN9QTGdujTvRIIRc3MvxermOP4Oyh0gr
-N1I0pM7wLqhOeQBo8lDzhVm2OIBQABT5vVoN+XnvHIF7ZXf1+1Vg/AbD9mySfAAO
-3FUmb1fzjxECw2Uivhj87Nos0kE9iLZfjR4/A7eF8MnmJSaEK7PE6Z+/+rwvtfSY
-YaIILJpKTsPuwNo+qXGcsoYHbyaGfnThIcI83eko3r3EhujafFfyVoma+kmn260u
-sRe0hkUs2VF3QOoE6hIWgnE/OPCrTMO+rXvvdyJNrv81P/Vog2gjNQb2+DxkGRsU
-QR9YsxOvGLNx4YYp9nEMFPbdLQ==
-=Fk9E
+dXJjZWZvcmdlIGtleSkgPGNocmlzbWFpckBlYXJ0aGxpbmsubmV0Pg==
+=et3J
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    38EE757D69184620
@@ -11080,109 +6510,8 @@
 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
-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
-=i15c
+tCdMYXNzZSBDb2xsaW4gPGxhc3NlLmNvbGxpbkB0dWthYW5pLm9yZz4=
+=iwam
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    3A1959EEF8726006
@@ -11258,6 +6587,52 @@
 =bdnk
 -----END PGP PUBLIC KEY BLOCK-----
 
+pub    3D12CA2AC19F3181
+uid    Tatu Saloranta (cowtowncoder) <tatu.saloranta@iki.fi>
+
+sub    575D6C921D84AC76
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+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
+96Um/Inf
+=ec6I
+-----END PGP PUBLIC KEY BLOCK-----
+
 pub    3E3D777C909A447C
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -11283,15 +6658,8 @@
 6KW3V3eAwz0LbckpSi9ZNyCNssjINvr6bRf10HMucViAB0O1DjrwO6aiFnatTCyO
 ig5MMZjgRPKjMASsPG8Ri1xivBYS/tpgamQzq+htWkbEydNv9+g31S5sN0pPw2km
 e8SjKp3C8RkSgz+dJz+P6tQrmGvZNs+pm2M9ABEBAAG0JEdyZWdvcnkgQnJhaWwg
-PGdyZWdicmFpbEBnb29nbGUuY29tPokBOAQTAQIAIgUCWZ9LBgIbAwYLCQgHAwIG
-FQgCCQoLBBYCAwECHgECF4AACgkQPzaIXCTfS3UQlAf+NU9xAM065XuZHMQgVgUQ
-S5546Ep33wrL1RDujgYrp76vaC9rwjVDNm7Bs7K5C80yQza8aGn4wFw6Q9VBjgEK
-NB2UY6OlxhJVwmArqAoOI9SDvs4+4VjJNdtEBjAJtQGecHyEiHWKbGFdztPZUApZ
-2suPM6n4U6ONdINZtta6tSLZ1LOCksAzT+6gOMzqNOzjlAfSn3RZZzEHVn4ZeteT
-jSYLx4iQ+dqUMVfJCF6rqp4c2AreCphORuXBqHJi4t+2b4ej3CMWOdQmBNV+QauE
-jRIFOh+IeJ+7w+F65+8RTT3L0JSTL6uwTz5OkRWsiMvA+nMuodnAxT4w56DTOWts
-RQ==
-=ui3d
+PGdyZWdicmFpbEBnb29nbGUuY29tPg==
+=8Xys
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    3FAAD2CD5ECBB314
@@ -11313,199 +6681,49 @@
 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
-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
-ZOkO/g==
-=yDi+
+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
+=z8kc
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    40A3C4432BD7308C
@@ -11522,23 +6740,7 @@
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    411063A3A0FFD119
-uid    Stian Soiland-Reyes <stain@apache.org>
-uid    Stian Soiland-Reyes <stian@soiland-reyes.com>
-uid    Stian Soiland <stian@soiland.no>
-uid    Stian Soiland-Reyes <s.soilandreyes@uva.nl>
-uid    Stian Soiland-Reyes <stian@s11.no>
-uid    Stian Soiland-Reyes <stian@esciencelab.org.uk>
-uid    Stian Soiland-Reyes <stian@mygrid.org.uk>
-uid    Stian Soiland-Reyes <stian@bioexcel.eu>
-uid    Stian Soiland-Reyes <soiland-reyes@manchester.ac.uk>
-uid    Stian Soiland <stain@soiland.no>
-uid    Stian Soiland-Reyes <soiland-reyes@cs.manchester.ac.uk>
-uid    Stian Soiland <stain@itea.ntnu.no>
-uid    Stian Soiland <stain@linpro.no>
-uid    Stian Soiland <stain@nvg.org>
 uid    Stian Soiland <stain@s11.no>
-uid    Stian Soiland <stain@stud.ntnu.no>
-uid    Stian Soiland <stian.soiland@ntnu.no>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -11551,841 +6753,9 @@
 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
-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
-wY5jFAi1tXc6wdqm0M05mqHzyj0es2sjhvgk0hhLDzX9Z0BwqbVcBqO7ditGjcjC
-AQixibZPNYqTmjAaqPqa2nHFRcIvYGfK4EumNJ4EVTEsUDVOZcFAQGaWW6AR7hFE
-Jfkh7qdYqF7+aZAyTQhXC07+5xvJ7Z+6B8f5OEml0Xm3qAVQjEFZZr9np2fMZf4l
-obQuU3RpYW4gU29pbGFuZC1SZXllcyA8c3RpYW5AZXNjaWVuY2VsYWIub3JnLnVr
-PohgBBMRCgAhBQJa6FN+AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEEEQ
-Y6Og/9EZrokAl2S7MTLubXudLxzsPlthT8z1qZsAn15GCFyxylTFUKqMsWEnTm1p
-B80RiQIzBBABCAAdFiEERb6+7JUKvQXPDvXDUKBNDDtlF/IFAls3jksACgkQUKBN
-DDtlF/J9pA/+ICxYrJrD9A0oWVd3GPqFMcWcOADLMckXpLxTSlintqIzVstpozBY
-NWYfHjiB0xGtHRyuL6EKWn4ktqlOOPl+OH/VB5VfBnjL00ZW+BiGVOinxAsAf02S
-buL8YmyKxkwJaIiKy9DVLfnWLYgx/ZsRXAUQClwP1jUDiojKvvKY1VOqyCMARgj2
-myxUfU7qfbRPJpU08/MegqLqkhi21DuQlUJ0BdV8YlGWmZnJl5y3CblSjL+9GQGO
-aV0NJhBz8SzhQ5yMlNsewIdIKLRhiggpz8ftQ9U9uu+TuO3kyhH/eTSATyK40XdG
-eNm4H++wdqqOm6+orayM6GMW0jqUf2lPTr/+3AHWf0U2lKQ9ph+qMyHBOr1oTK2y
-p4dVo+noveFOjPsUxNlwhsJPRhnRy4NonhZmR1mEHvbKKC7rzX4rlVK63HJ2JvZV
-MaAk58l8X59iTVFOIYk6lOclETQ5J5zR8b/3A3wuQERsMSqp3s1aY7twTgBrKfZM
-d63AZLa7tL5uhIE8qBpU9aK7MBa0ZhelF/K85UqMlzG3kXfNQ45WaZ91hJl4AN50
-9BgIjyXRAnVzZ2qhbAvF/tnjt8gS0WUiUKj+T+cMWsfnEfwEbKd81VRAjSzjBADG
-sHE1reCTGLm7igijJ6i7jook2fZn+amJI1yiP9C4Ha7S/yHUwmoPtLC0KVN0aWFu
-IFNvaWxhbmQtUmV5ZXMgPHN0aWFuQG15Z3JpZC5vcmcudWs+iGEEExEKACEFAlro
-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
-=A4Zh
+KkjTUMG4WrBsimi+hp5PKQeP8Tlv9eRtBeKMKHvgieVsPI1Xq7QcU3RpYW4gU29p
+bGFuZCA8c3RhaW5AczExLm5vPg==
+=u3Mi
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    41321490758AAD6F
@@ -12416,18 +6786,8 @@
 Q6yAVK2EmLqMZwbIA3S6CrToE/Juudd1fYOlV6ov+XCN3ZaQCZY/glOHCuEfa8EV
 MsFRn8rUQxc+qjBHAOTVjDjx9Gqc6Wg4BzX2uzF2GTXI51zEloivqE1wowxmWPRe
 o7f/VL6UCETNoRUAEQEAAbQzUm9nZXIgSG93ZWxsIDxNeXN0ZXJBaXRjaEB1c2Vy
-cy5ub3JlcGx5LmdpdGh1Yi5jb20+iQHUBBMBCgA+FiEEpVgwEgrtF34apPZqQxuP
-gEA6fcEFAmCUUBoCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ
-QxuPgEA6fcGZiwv/RDbQ7aLP7qURVv8Picyi9nL1GJhzQqqZhac0IXMh+WARsLJf
-1XozjhPRLzM7I49p762iXo2hlKFxeMmjcI4Z3v18qwDJcBQzZrHfnXNA0grWmNVF
-sI3Mvk9cezgwXsVLLQuANMx/92fXemb6psWCopkA44+cSqEBOB9Wefktmgu1S9su
-+ZEoyGg9ijUuPfB383RCcJVoXql/tws5Rc5RW6bV469BZcJBHoxCwrY+DcJ1zBc5
-Ws1KTJTUN0fceqNap15yaIEU4SgNBR3Qc+X5eBkB7quUoxhO7voXp/ztvE/yLEX3
-14rPNIC85ff75agL3JjM3G43+aIEaIMB4XmAr9UbX6EKUSk6QJl5rcO2nXLorIoc
-Vm0ZmDzfWdGAzvS5SVTKSfZnyf3pKLEYjqKlKYcw6jH+TCLHytfEx4gkCFbv1+/5
-rQAKdA6W6+GmFKicjdIMqkD5ZyqrWWcv0EzcOK7NBcCaOoeQ8xF8lvj0+Pp/oeF8
-w67RMICKeAxICj4O
-=vbXN
+cy5ub3JlcGx5LmdpdGh1Yi5jb20+
+=RWZD
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    44CE7BF2825EA2CD
@@ -12443,15 +6803,8 @@
 SoKRiBeLejW+t4kpdMnEfC9ILAYBEEjNYvBIyPdPKBwNfy0yjRebsUf0eNmjGTpk
 VPlfofjVaUaOZytUOQvntYpocMX+377DGQIdABEBAAG0X0lDVSBQcm9qZWN0IEBJ
 Qk0gKEludGVybmF0aW9uYWwgQ29tcG9uZW50cyBmb3IgVW5pY29kZSBEZXZlbG9w
-bWVudCBhdCBJQk0pIDxpY3VpbnRsQHVzLmlibS5jb20+iQE4BBMBAgAiBQJM0EDF
-AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBEznvygl6izdyHB/0c++b/
-5s355bj5P7AuMxLsMCIu60hkBN3O4IBkI5zRah+rdChAlbDLIDbqepKTyTNYIKPT
-m3szJAy3g8e8C7Spn+p8I2HFNrLg1uKmeiO1bJoorseRQXb0tJuwgpmkAaXWzCX/
-ZQFPygWzJXKeREdI8gTyFjGMwUyfpIfAGD8cz0f+AYgCiLGM9VXa4jXPXOLco8R/
-TeHe09Pu914RWNSQIFsI+dGyzdrHG9taHJ5K0q71iiZOEGoWBQWlCMq8bHrImvjP
-ipHyWdwowYg3bDNrs25GMexiS7O5rrJyezchIWbcV/54vTcNQy4co7hZCad/Ar4B
-SYb1E6lBZnFf3FmS
-=UUPg
+bWVudCBhdCBJQk0pIDxpY3VpbnRsQHVzLmlibS5jb20+
+=Bu54
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    45145F826CF4BF98
@@ -12467,37 +6820,31 @@
 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
-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
-hvuJwiMcuA7Wf7wF6Yt6o2KOu+jNqT7oglS+Bpw=
-=HLKj
+ZHQgPGRhbmllbC5kcmVpYnJvZHRAZ21haWwuY29tPrkCDQRK/JJ4ARAApvI3R28W
+7n+v/l28jtOYdyYm2CibHgY/ubvxAL+PdB0EDC1OLVHO2I7y3XIacgaacULMji8P
+ssRhBDG6pspSLpu41wnsBQsTokHIEBlaAYaNqJfwLSjDszJrdFvIHZ7V5lseditB
+1Q+Jhz1dJSn4PUYN5Trsg67xrc/8CqUOhAbHYNC8LJHlFx5O+k2lL/a9oaUWWHhC
+diGqTb3KZ5fa0lRRu8zu35iusmH93xTI3iFyQAeR6qvQ1baqK58NWzwoqZrmKjSt
+7jniZGzUyAvHEFXNYvMlq7cNJzkJqkS53L0yeKI7ymvV2tUDlyh+HFfegU0eltT4
+dnpEwqxDEgVef1zCkMKcI/z41BvAa5U0j4JqOk9wDemyaK+y20Bnl+S3dVVBhXLQ
+Kw+Zm/Z0DrxytAo5lJrWsk2OSHBJ4go3ltXb9350lKflj6kG25SAYzF51kjaF2UU
+mdKjE8mY4KKrIm8AQrG0qXWDGUGGbK3U4QWhN169NVPAjNLOqJE/4wxpoLz24MYD
+5h86TiC8ZC3MHeQLu+VDVm154BAhk8XTQFly5BJGwyi6cH755hhtpfhzO1wq2piR
+RPvZ7P9eb38OIcoQqxhO0jUG84R5IUqgOasENmNdrQhDr8QkKNFmgtAsSo4G42Vp
+234xmL+d6uJoF6tNM44BxBOrY0ERh3H1uA0AEQEAAYkCHwQYAQIACQUCSvySeAIb
+DAAKCRByOF/wrzONUib+D/42MsKIXnvvTa5Y2Pdo8ZTHvmbpCCqutVmAJOhg3m2/
+mBOlRrdq+Lhq5rc4bRFQMpTe4U7WdTlvD9/6r1hPRGVOOh/QzY+uTAZTzLvT1/Q1
+xyuSzGdt2mo3JY2mPgsKlqbX/LcZ4rQ0+Q/MrUOLOtZ0KWGEGAIr+fvPONloGVfh
+7xH93w7dXY9mPIUh/YHcP+tJ9/NjhWGjdKwJlV9rmZbxru1Qs4Z69p+56LzJGMFk
+bqRnkIxYzQL0nRbwRn182HuxkqAsoASNlOV0fJcB/y+5vAgplJxaGTtCuoJrd3hx
+9bCAi4XHmy4tga0fbYXx/Q+htsRNC0W1JkBfaFKy4XgywU6p43ZBz+9RnMrBOcPi
+JRjSTtSsGjH076JRcpbYrtGkgdAvrKIET/10xMidco2ki4FOwf93Ldzo0GTF2WQl
+fN9sRYKiEXrHUp0HAYrovHSMiu1NqZgK4K4XBCtzrA7CQGNL9ZD0IkNJaiSMzz+f
+LHyhUAF4PnMB7TnYdkFHxjZmpG5xlys3Cd9SovrVbw2udz5imusRWUyZwdxO3IFG
+P5hr7HhRgv6GfkeyGfCiYMud/m5tbNUEahyGQNAMlu+KoO+P/sVtBLfWB5QA3AOa
+i1W3QsvyX45qdVIp1ZsXOfzWP8CG+4nCIxy4DtZ/vAXpi3qjYo676M2pPuiCVL4G
+nA==
+=FVYP
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    461A804F2609FD89
@@ -12545,7 +6892,6 @@
 
 pub    466CAED6E0747D50
 uid    Kristian Rosenvold <krosenvold@apache.org>
-uid    keybase.io/krosenvold <krosenvold@keybase.io>
 
 sub    46C2130DFD2497F5
 -----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -12568,76 +6914,65 @@
 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
-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
-=fSHY
+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
+ClYc7sp9xk7YNcP0+gmmFVwY3BxZ
+=x44H
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    4A1BAC7F1E3B89B3
@@ -12653,26 +6988,20 @@
 g60IHYze/WTAZK678rGvyxwKF4zX2mwYTFKkoL3PaFM/7HbZbLe0HVB8sIF7x+c5
 glEVb9wMApjq83SmhtFM3u/ep0uzcg3c0ks5JXZQ/KtPDYMq0NFp7zGwaFiAF5D1
 BaLWHRFeD6k4c38EPnSQLGBTeVwm+gWHaTjBABEBAAG0IUphbiBPdXdlbnMgPGph
-bi5vdXdlbnNAZ21haWwuY29tPokBOAQTAQIAIgUCTkV1CgIbAwYLCQgHAwIGFQgC
-CQoLBBYCAwECHgECF4AACgkQShusfx47ibOKKgf/dswo95QMKtG70E5b0obkwlaQ
-Y/D78V1eLARyBS49q4NUN5VGC4+ZFCBH6QVbF8KD/NYRMYruij7TVLgdfjJyj9SQ
-mqx0pmIc4xqzBmNjB7h+IU4aqzcwOphEbH4/H4UJ6vLIjTsHplQbKRtpg4if9WUY
-cZkXvf75bwJs9nQHt6/hakTDBgEPx47IpTTAb3Qs4nDKAJuBEc+JGsVwkiYRSP0G
-OFak5qbqyHWLAO1MbBjY8Q5sxFRbVfJCrvJM0Zby84r1MijdcGswXjD3NxT9dlrQ
-EQMBx9EicMoIsDzimv3yubbM7NMk1/TG3gqjHOTLLKAqqkSnnYwnwU/nfUyiWLkB
-DQROCnqsAQgAuRyzCH5nCVqkP8cduQSBvBVSGFZGTi+e7rXjaqKRuhux86SyoIbs
-zC2lj57uwTQrpN9pWNDgI8P0ZdYJTY54K4FNxjpqWi1f2n08wKhElJubpVWiExsq
-EMm96USXL+ZizlIbYi1tn1qLEkOPQ+5eIRz6N7Dmz7ywSkkAJYaYehl7LC7WMvTz
-kEXAmeP48msFMyBCsNB/8GpCjrmmORdk4r20TaWPwVwLKD+7tky5SMHW7hLOxslf
-fccyPxjB+EkbU8yi5LAAEv6CIeMIKu/5oBO4ucj8JrnJRY8s+LGW4JQoGzaq0D5X
-LKwuNDyQ7NslXqIla0d7rse2E/pGXIkvswARAQABiQEfBBgBAgAJBQJOCnqsAhsM
-AAoJEDfp1Y6kWYZBLP8H/01PbJGaAvnSKEmk1ni5SRNOE2GHoX5XHHwpApIQzHcX
-QEB2Xn+NPso+Zt7YkuW1OgZOXK5F7sOT+CSaRVBAxD4e8XYDRGGux89PdbmTtkgT
-wGDX7jw8ROtTZA5+Wpsg4oG3RYxCCS6JKWHokfxENdIF8iJULkFgjyC9+Sxl+OUY
-Moc+N04njDnE7htSaccyCa7gI1M8a3DjPAiw8adzHXNuMpIz8WQ7ibY0td1gVQnh
-ESP0uZEXuAO1b5ck7wt+C+IOh4IH0F713S12QamZ+5N7mTlpXCJj5O7CA5UwK8Sd
-Uob+x9VAndXlADcbheTjM72Z1+ugvbU5+EXGGPJCJa4=
-=xyd1
+bi5vdXdlbnNAZ21haWwuY29tPrkBDQROCnqsAQgAuRyzCH5nCVqkP8cduQSBvBVS
+GFZGTi+e7rXjaqKRuhux86SyoIbszC2lj57uwTQrpN9pWNDgI8P0ZdYJTY54K4FN
+xjpqWi1f2n08wKhElJubpVWiExsqEMm96USXL+ZizlIbYi1tn1qLEkOPQ+5eIRz6
+N7Dmz7ywSkkAJYaYehl7LC7WMvTzkEXAmeP48msFMyBCsNB/8GpCjrmmORdk4r20
+TaWPwVwLKD+7tky5SMHW7hLOxslffccyPxjB+EkbU8yi5LAAEv6CIeMIKu/5oBO4
+ucj8JrnJRY8s+LGW4JQoGzaq0D5XLKwuNDyQ7NslXqIla0d7rse2E/pGXIkvswAR
+AQABiQEfBBgBAgAJBQJOCnqsAhsMAAoJEDfp1Y6kWYZBLP8H/01PbJGaAvnSKEmk
+1ni5SRNOE2GHoX5XHHwpApIQzHcXQEB2Xn+NPso+Zt7YkuW1OgZOXK5F7sOT+CSa
+RVBAxD4e8XYDRGGux89PdbmTtkgTwGDX7jw8ROtTZA5+Wpsg4oG3RYxCCS6JKWHo
+kfxENdIF8iJULkFgjyC9+Sxl+OUYMoc+N04njDnE7htSaccyCa7gI1M8a3DjPAiw
+8adzHXNuMpIz8WQ7ibY0td1gVQnhESP0uZEXuAO1b5ck7wt+C+IOh4IH0F713S12
+QamZ+5N7mTlpXCJj5O7CA5UwK8SdUob+x9VAndXlADcbheTjM72Z1+ugvbU5+EXG
+GPJCJa4=
+=RtVk
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    4B2277C6358670EE
@@ -12691,35 +7020,26 @@
 rgwmI1DaEN74N4L7XLASBlQsREOH47NqV3AX1X5apMUeiccBpaoECC9hxYDyvW0J
 XmNzvtqxd7h0MoYOHASAurx4t2HD4xfUC1hdQ34z1uC2I1BvCfucsmkLMG5ro4xk
 VB5lcmYnlvY6CrUAEQEAAbQ3Sm9lbCBDb3N0aWdsaW9sYSAoQXNzZXJ0SikgPGpv
-ZWwuY29zdGlnbGlvbGFAZ21haWwuY29tPokB1AQTAQoAPhYhBHeZPTKVaYxGkS6D
-mEsid8Y1hnDuBQJeokd5AhsDBQkSzAMABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
-AAoJEEsid8Y1hnDuyxIL/RyemUbf3jwzNKyvlhfGwawaKwlmuOPYY2tnQ7I5rzsa
-UMhe4nL3vnXs0ky3wiFxbmC4Rk9GzFHZ++om/3TV3M4WeUmYZYYXbeOEXgQs1BIO
-PBZXN0UhpZHfF68Uz6IOKbpMIOCsXsm5AecEmhFDRydUaiDfjJIrOfKqmfTkMZzS
-BXuEhaEbpNF5gpzf8EgNugIwJN+qBGBq5GI0KDxIPRzq86miebV/df+2HzEnSWNw
-QO3W7ZNZ7pHT449qU773KfqmXFHjGkDlyko5/saQExxEvnMb64m3AGsi9FaLQAvW
-OpiVOJ7UL2hUSKAG5lJr31f3ovTvmlTQlzCoHw+AwqAm7WwAaNkbpcftYv3b5afr
-7cBpP3BHpQc6PDvfWdzqu1Enc+fUaPg66UNRXraWfFkuIYYdGEWoYEwzfoXeWOFF
-5n7VGK/0IIUThfwf5vqDuRWOieH2xr0y7UrATuQ73hIbvntKUj0Sv6mHMnkxuPvL
-vTMjJWL56/EipkYUdcnUGbkBjQRb9RBMAQwA7UCAsQ8KxX8nYO4Sy2pzlh9W5FMP
-wGluuokPA2A6g2Fz3vF62RqeaE4HrRQMpijQCsN3JTJVwDid41X84XCMItkdAxMj
-mn5zeF/yCcRuHe2Ci/+ae5BzrBaKE/VWRAkaZSZWJ1MoDdpSxJhLHNFnVrwTkM/S
-eSNUBk9ZDEC+43b0hciefX9bFlc6XPHgV+yr5ohhwcNcrZ/gbAhhN3/xIVmvKoib
-mb+ZIajhiCP1OOH+GpZAPT93w9qZWq3+2gvP4ZZ7bO+8N8Gmz24GL3/0eYI6aMUM
-wWGjy5J+iRiFjb6E+Iv/zToyZFWm2VOuOUqy5t4u+Vyk5bl0hATpJICmKa5OFtQw
-G5Uvfztk6rujjat90xv8yzsBvoEUqKqzIzjHdN36qop5hLMnBljdLdFY+Rk9CHdF
-7MW8Nf0YWbP/3uUk19utGW686Lolt8gvBQc4B5N7VtNoXFCKM/I3ufgnHQvDlf8p
-gdJOcyx/a90V/DpUI1ANlwg6IsmFZXbBQw7tABEBAAGJAbwEGAEKACYWIQTjqflQ
-eehM4gH3z2C+3hHq8RZEgAUCW/UQTAIbDAUJA8JnAAAKCRC+3hHq8RZEgEy+C/4l
-sgrKCmq2Nc7eTdN1AxwMkj28XQFmkqO8orfJm1hAtVK1KRizkX52RNeRN6QX3pX9
-s1e3DjJi3Hpa1UWqeicPA0kKTi2ytUlxR/iZDkaQkLyCCZtWnGHr/eRBdOjblprl
-5O+v/tcyrmQGC04TqOntMumuk7JNjZ0QAVkZUxdmfi9bHaF5W5vlcaFYT5gdWpkO
-Q0YaWXXw5ynh6Ookjhq0g4pZNjl2rdWWyTC59YIvC9THx0+vuyN7xnSWIb8J1IjE
-EYvPqRfpd8s1Vf2AA0JRPjUG2UV8MZqu8k8x4iC2gbdji/vyg/ycdlRT/ULyNprz
-1nTLMfhBT0Wmy8B5lFVme3URmld8T90RPln6Dy+c+IKb/79z3FPujuSbipXzx3Qv
-GwVYyP80JFn7CJluOl/u8vxi2EVFN6aVqdzwoswFE3+0W0AfbpHUUT4oeBW5OBTJ
-5i1Qb0DT6WXk3Y2j1Z08xxhY1RITnc2C33wjXAW0h+qq7/7Yq3w3/7ncv9sWIzU=
-=CKJz
+ZWwuY29zdGlnbGlvbGFAZ21haWwuY29tPrkBjQRb9RBMAQwA7UCAsQ8KxX8nYO4S
+y2pzlh9W5FMPwGluuokPA2A6g2Fz3vF62RqeaE4HrRQMpijQCsN3JTJVwDid41X8
+4XCMItkdAxMjmn5zeF/yCcRuHe2Ci/+ae5BzrBaKE/VWRAkaZSZWJ1MoDdpSxJhL
+HNFnVrwTkM/SeSNUBk9ZDEC+43b0hciefX9bFlc6XPHgV+yr5ohhwcNcrZ/gbAhh
+N3/xIVmvKoibmb+ZIajhiCP1OOH+GpZAPT93w9qZWq3+2gvP4ZZ7bO+8N8Gmz24G
+L3/0eYI6aMUMwWGjy5J+iRiFjb6E+Iv/zToyZFWm2VOuOUqy5t4u+Vyk5bl0hATp
+JICmKa5OFtQwG5Uvfztk6rujjat90xv8yzsBvoEUqKqzIzjHdN36qop5hLMnBljd
+LdFY+Rk9CHdF7MW8Nf0YWbP/3uUk19utGW686Lolt8gvBQc4B5N7VtNoXFCKM/I3
+ufgnHQvDlf8pgdJOcyx/a90V/DpUI1ANlwg6IsmFZXbBQw7tABEBAAGJAbwEGAEK
+ACYWIQTjqflQeehM4gH3z2C+3hHq8RZEgAUCW/UQTAIbDAUJA8JnAAAKCRC+3hHq
+8RZEgEy+C/4lsgrKCmq2Nc7eTdN1AxwMkj28XQFmkqO8orfJm1hAtVK1KRizkX52
+RNeRN6QX3pX9s1e3DjJi3Hpa1UWqeicPA0kKTi2ytUlxR/iZDkaQkLyCCZtWnGHr
+/eRBdOjblprl5O+v/tcyrmQGC04TqOntMumuk7JNjZ0QAVkZUxdmfi9bHaF5W5vl
+caFYT5gdWpkOQ0YaWXXw5ynh6Ookjhq0g4pZNjl2rdWWyTC59YIvC9THx0+vuyN7
+xnSWIb8J1IjEEYvPqRfpd8s1Vf2AA0JRPjUG2UV8MZqu8k8x4iC2gbdji/vyg/yc
+dlRT/ULyNprz1nTLMfhBT0Wmy8B5lFVme3URmld8T90RPln6Dy+c+IKb/79z3FPu
+juSbipXzx3QvGwVYyP80JFn7CJluOl/u8vxi2EVFN6aVqdzwoswFE3+0W0AfbpHU
+UT4oeBW5OBTJ5i1Qb0DT6WXk3Y2j1Z08xxhY1RITnc2C33wjXAW0h+qq7/7Yq3w3
+/7ncv9sWIzU=
+=7ZF/
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    4CC08E7F47C3EC76
@@ -12738,142 +7058,135 @@
 YqvVyzpJqNTDLFjLpEcditVn4EioVVMcvUu4YVwmUSdBjrMLp0xC8PvbyWiw4dCA
 T4C2zFycrr4M2legiZv/N6Tw0fPRE/EALYtIhgLSeqx5Pg9ku/KA6zMraFovnMDM
 haJ9+ZsCPxd9JaJ021C9I7EXDA+W5t+DPODDABEBAAG0OEJyZXR0IFdvb2xkcmlk
-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
-cc9rVdA=
-=XjZp
+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
+4+dF0n6LBoN1aHNh1woiBG5nZtw3cc9rVdA=
+=m8wP
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    5208812E1E4A6DB0
@@ -12888,15 +7201,8 @@
 q+Dm8P0vs4IFTD8q5f1Vr78FmUth2srIBmsIH1wNV1nAUTmQppNBFlCmcvnWTYI1
 0UMcsFFrJ2pFT1yP2AEGUNl4Lgj4hmVHZwX38/lu9pQ7iWtHSLOwZsfuC34/goS+
 ldFt63JqDV7ZaqwAgk7Iq6jbr4pSVsB4VdglABEBAAG0HUdyYWRsZSBJbmMuIDxp
-bmZvQGdyYWRsZS5jb20+iQFOBBMBCAA4FiEEMU/oLlpMU3e8ou3sUgiBLh5KbbAF
-Al6+3fgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQUgiBLh5KbbDr4ggA
-tmAjpH0VPImxcfV+by6itQkY6BQQ0VO9adO9Q/C7JIaiPQ8Rz+2f1SzAtLB/44SZ
-4mseP/Hb5CQEAysRPd3/3GP56GXzXgGURT9/4j/1RPyHIndBd9mmvd9L9+lKWMbZ
-Y7JzPC4Ew/2WPeB3GVsOgn+sMBVHZcVDATqZ5OdfSmuIil7DueWSPUNbFFJkLy7P
-6nkRkk4GMRXSlAjKOLNNQV41cUChqAqf8Yj1sD6cX75YZylsm9voV6JoR2u8lAfT
-zr1VPKdy9xLfDh9Fizth7r0t3OmrUvlEey2IkOoT5cUl/2/jPaz/ypVw4G8nbzW3
-0lyW43RWIIXZC8WAMRdWUQ==
-=WJqH
+bmZvQGdyYWRsZS5jb20+
+=hQYb
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    52931F4B72B4F54C
@@ -12911,19 +7217,14 @@
 dVxgHPhXvSMwtZFrImNjaDbAMpYLjklLIk8urhL5G0O5XMzTIUtiC9o2YzD//sz/
 bdCDA3vXAIgnshQGdeIuwXGWeiD/uig1Fxq/Y6tHttZuwbJGYftVKQs+M6Q9TeD2
 xNUr/lRd4Kr022xpa33ftKr/6ULpdZ2GDNAlABEBAAG0H1JvbWFuIExldmVudG92
-IDxsZXZlbnRvdkB5YS5ydT6JATgEEwECACIFAlPKf7UCGwMGCwkIBwMCBhUIAgkK
-CwQWAgMBAh4BAheAAAoJEFKTH0tytPVMamMIAMgLSH5decNKpIxYFCMHP05duDJn
-3odVw6m3JfMKmSn7YD5syZkmTPZBWituBqZUeR+fkWSBNiBzy2NU4QiBzu9Fgg3h
-5YLzMmio8xpL+Ek6nK8N49IUPqLxaB0EHWnoNs48JjGyyZ/onw9mFeYXhTsHIvMS
-KrqvSda/K2xkFjUe8mv4v8v2HunNvZRSZuWLvPeOa8eBiE2pt63wxjqiqzX9sUxc
-fdiTbanvFF5tEMomasqRfyCCDYW0HQTmAhhlcz323+axYZ9S80J+CCFHKuwjq2ms
-03ayDPZNgbwCrpOkSrBHm0z8Zosqz4mkhce5BDPo0t9q1be0/gPmchLalfI=
-=X89U
+IDxsZXZlbnRvdkB5YS5ydT4=
+=LcOd
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    54AC8E2D98CFEAC6
 uid    gitlab@ysb33r.org
 
+sub    1309825C79629B3C
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
 
@@ -12935,18 +7236,26 @@
 3onEnoR9tU06P576wjrJ44KGi4YJz/L5yKreKBfSe7vfGhBWDmrcjSb3thj+pP30
 Q7LQFmY5EaAz/Wpr0oDMAxtBBbii+QCllEFxu397OMrZZYqQjt9K/l4krYjDZc12
 fQZqCT889kRBA8nqhPpueY5iELmL5oGHTKmWDbKFo6fEPAq6F3e9p50s9pfhT1g2
-GUM9m+0LDs+N+38AEQEAAbQRZ2l0bGFiQHlzYjMzci5vcmeJAdQEEwEIAD4WIQTq
-AiVgqB5b1I2z0YtUrI4tmM/qxgUCYHW0PgIbAwUJA8JnAAULCQgHAgYVCgkICwIE
-FgIDAQIeAQIXgAAKCRBUrI4tmM/qxo+qDACEJmKz1WkRT6L8mFmQTlo182Sgx2oj
-oNgII1biYI5xZINn91Hf5pqOYceIdxow2ZH57JnuKK6sGvT5uMdtSMQHcJH5pI0O
-kILx0Dk6kBjcay3u7PjjgytRKIR26vI/k/hjBcAhtA7MQ4ZgJ99OZK2Zw82j1erJ
-WKMrySQGRLbGGi78M6quTsDK/4UWBDstM7NRYzc5WgOGac5sjXcziE77EKIOsQEv
-tVRtNyPbal+6XUnqVy8a6wplF+TxBs/ZpjNrIzfnRkX6n/gFDnIgUFEniVe0u4yF
-ngTUNzOegVRxc184phPMGHadXVTud1Vb4ANnD7OiEnYe32PI8C6FnQrALEDkuUz3
-kK8iT7RrlpCIACu+WwxQv9iuCZ4JQjfEByzbcAyOwVXX6l3/RVC6M1OmcbFJqSFt
-+RjxLsaSrUSyAe3lJhTtuoN8aCKOzfZUnk6hyXx7030ml+8qeAFbbh6MIYPl7MCH
-cO7dXFSwW4eXNER1/XW6C+m7ctSlC2n3mew=
-=Ckwv
+GUM9m+0LDs+N+38AEQEAAbQRZ2l0bGFiQHlzYjMzci5vcme5AY0EYHW0PgEMALkB
+cL/X7eKvtznC1SE4dJr0skc9rGniiYwVhSNiyiYXlbb0ETdRDbajSHjv53cR5DGb
+AyPgwwLNzHo1K3MrE0CvM09sCMJBOjpCu1X5sma1w76I9ScvJ+Gqr3jFu8CO9JbS
+s4bj2VRr9ITOEZinvwo+Ult1vj1Vib9K4lAlpsXcd0dgFS7jlTBqeDgRR+xyRdt+
+54yrCay+IXupBF0GuYQ9lHwCVyFhwhOneoQq4xhEmwmNEQOWq28byVLUpMDzKAPt
++T5AmyxNebLRQmE7rWX2PmoCSTby8aQPlG0OSRBQJu3f6lhMZN21QbTcCBeDW5VI
+sjPtZnJ7nIpOkT2UXulnlRk/QAEydJdPdSz/Idps3JcHyNK0YXKJ3qvrH1k0ur8Y
+fgHuY41d4QqShOZleDcJtDXj7Gh0BtLlijspiscBNt16BpkKzEcqd/6Pt1fMIOEx
+iBXrdad9mPpZT14KTXT8dXHtAEKozut37a4hLfJ7al+XBqilnYEm1OjVx/BVKwAR
+AQABiQG2BBgBCAAgFiEE6gIlYKgeW9SNs9GLVKyOLZjP6sYFAmB1tD4CGwwACgkQ
+VKyOLZjP6sbEogv+LX9Z8BlnClkJ3lVMZlyzbUf2lleg+QA4cWTsPE4SSi1VFHsR
+CAoxesJdKqJuFcIEOFxscGfpkSbhTsTEczI9e2XsaaDAaY1GodvJfRUAu2VdvVAG
+xHhoEqUUSbD4r3AW0yLViLlsQVoiuKIizm8PQZr9bfLSkXjGQz1wlPfZVC7raPJ1
+VYb3TQXmIALTc8peel2SlqFLQVZ+3Lb8JKC/9DeHAGmpBipB+VcSC5vRchqpncsH
+Az0GXtmqzkgcyzq0Kyh0nIshDsGQ/vXoZQRl5hjMWBLaN/y5ik7Qde4bjL7p1ozK
+Vm0S2qY0CfrGSzZnLXLOxX90fqXXN9cwmLv++bdyrXElA10u9LIqsViop3UvHitV
+jkJlIhwt9/duJEhPJnT/qH2O9YAqwlEFggSNSctD3LC1uMjB8JD6XcpQgDP6BiZU
+wP/yCGQGvA27Omf2WgrpGukXiZFSBkCZPL+/rcP5dnVrCwneGCXluI+sONfybagy
+2nNTTJWxonBh7c5p
+=ksoJ
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    55C7E5E701832382
@@ -12965,11 +7274,7 @@
 PHgq59mLPVueo2/M5k/fFrCe36jHePP31gYpFtueeYDfsofHwod0WhsHyC7JfG8d
 jEnSczTCmOHRZ3ed9ef6SeWUozYCQAX/tAbpoCthe0lTDYhFhkzVCe/FABEBAAG0
 MUFuZHJleSBTb21vdiAoU25ha2VZQU1MKSA8cHVibGljLnNvbW92QGdtYWlsLmNv
-bT6IvgQTAQIAKAUCVdDLQQIbAwUJCWYBgAYLCQgHAwIGFQgCCQoLBBYCAwECHgEC
-F4AACgkQVcfl5wGDI4LjiQP/eMEcbkMet6nulUtE3rmRZjq1uSYTPirvtPF8Apwn
-VH6a0FepOC0uqNnYLrZiOFR8xy4kq2r1cLkX8iS2AdZlFOHNxXMDSGnnbsATKBdZ
-j6NKhQYS/cah2yV52Nnl8+Oox84lWEkYLCmuYB00HlRxytf+jSXxS3SnKpGz/bdG
-BVi5Ag0EWfhM1QEQALsFWkf2O7SspUGONCf2uTDUqBgYcmHUzWYdIL3XfI++LbxT
+bT65Ag0EWfhM1QEQALsFWkf2O7SspUGONCf2uTDUqBgYcmHUzWYdIL3XfI++LbxT
 OqAYL70HjFwC83qFnbTdyra1W3crKEXODKsYKCxJwA2l37TyFlXQtcdhTiwSwBiB
 VIdrYzJbygLoQP1BXnDFL10M6q0LrKCvpDrErGkJdceFbUeMLakodx5B/VxOoiqZ
 /ZU0ZDQNa7RE3hifhkDvDKHCoSSqnXbNtTkzzjWaFH1RICpvt9DNfIIWYefF1+TV
@@ -13118,7 +7423,128 @@
 p6P7m4EzFgqvBf4fR+vye/P8rjuPGptybPkWNdxo76+p3+0tAygXCo2vTM7Yo53y
 18tQWK6q9OPGix6kYYmlSbrHk5LPlnbung6kr4jtrROimaWGtMo+92E93Jl5p9EY
 p/MkO79hIlVZBzQD+/aaynB50P88BKE6gabbJDprqvds/TetkrU=
-=+ki6
+=ygky
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    56028DF552BA32E2
+uid    Dokka Release <dokka@jetbrains.com>
+
+sub    B5681E477AD61C38
+sub    D89D05374952262B
+sub    7EC19439E4D4C2A0
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQGNBF+7lwIBDACcXIXAwFDoWvCCWn+OImyyJQvSnnte93Mc1ZJtlArkrjeGU7Mu
+5giUH+FOyiXlj7CU4G9RTnAzDgM8XPncWOERgRG2dXtO03Li7iUEX4Z8PCUGsTxP
+2VKGuCF4Je1ZPGxeKG49N0L3IIBBxfCzumE37LP3diw7Ups8xJUhZE1ecF/Ow4uE
+y6lBOyaJM8VJN65GLOdlbjOTKaFKR6aY7lPcEGyClh+SnMcGTocLf9joBpDI8WZM
+NQoQlVtiT6ItvbxjxJmA2hsodm1Ix/xX2xo3hdXH+opmsxlNGSK26hOSMKTMQXXR
+m96Slky889SPpT+Rnbp+zPSsWqUJBzTT83DAfH7PJ210bRuzHJZzSox/2iiVbm09
+e8rny09ju/OTA5sVvg0ibNscO2wyGsFjoBTFB4x27Bl+4bloBot2lBO7mRkhOIaT
+KxDkKPSw6vQnhA3a7p5nGMo20MgNhP09ui9CwO5Yh3kwnA81clldlMcjQMLy35ch
+kSoqW9jnqm2CI5EAEQEAAbQjRG9ra2EgUmVsZWFzZSA8ZG9ra2FAamV0YnJhaW5z
+LmNvbT65AY0EX7uXigEMAOfr/RbJjVYWLEQ8CtzX43V7K5f+akzxEUi2R09koiaW
+UrLJSwBJBYfq0e+3o3j5sf3eFIwEc5EKmMQvpGb0RsF3J4YCiO/wEsPb/Gd3S5wX
+1WYnCvP7e6gQ2V8umyom16p9omr26gOewSb449yrBh1HpbAAUjRL1N9F8xdbW8Dv
+iEa9lkwNxaPEYTwg0IezTKUiaUf/G5pZzD2hNlKJVJsx5i4ntXVdCBykySg5Ty+w
+JkXw6wnubBO0yWk2IZ/QlH4plCuFkLsw47U9v2OoCv4jGZpRULjnHmqorInygHF2
+hei7SPlqCRyns5pENEGKaPHUHUlDzkJ3HTRHQUnVUONdgwBwJvVEXFwFcnOGk7j9
+mvxsZUN8qvE8FbmNcFmPJuV6a9zqjNpspmFnuM3P0gn+WXNZB4eRrx8YxlZTRdgx
+fHbQwwPna6p73DqGQQ86DGe4r7YS/ajqwkhEqW0aY2+SCj95E3GU0AerL4J+EMHW
+H5fUV7HKGbKKhm+5HGOsdwARAQABiQG8BBgBCgAmAhsMFiEExTaii8ifslAnLJ9/
+VgKN9VK6MuIFAmNRxNsFCQdYlFEACgkQVgKN9VK6MuIstQv+LE+VB4LqWONFs7fe
+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
+8Iv3sH9nWh+E7A4=
+=gpOc
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    59A252FB1199D873
@@ -13137,78 +7563,71 @@
 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
-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
-iz3yNIpSfutJyJY8/w==
-=TKVA
+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
+=PWCW
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    5AD66315FC018797
@@ -13228,25 +7647,12 @@
 TvONnGQUi5VKQIZqFBfxA8u7T8eyvusvHMnnpXTTSclmmqtmW0t53NHY8HUoe/dw
 S/ohZwtS+dN1s/rQh8kl1SBzgMbsBR/h0WLTjymMtk7aPO3NtHuhSWJL3YeabR5v
 NRkTgy39x3Sv6TEgPsYU1uL5kRbjOrtOVGkMkMnrbqYKSDkAr4+PVhAJMwARAQAB
-tB9DZWRyaWMgQmV1c3QgPGNlZHJpY0BiZXVzdC5jb20+iQI9BBMBCgAnBQJYRxmW
-AhsDBQkHhh+ABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEFrWYxX8AYeXIbkP
-/i7z4kQQf6EdM1Y/LSaSNO9ZfuO0Df11fHtlfxHidVs6+pJKYtNPSZ0DzlYxZ7S8
-HmDK3EmYR76Pdu9vLNthdBoipbA1C5N+cXezt8OLzUkoUWFNZumFafxcuORsxlS6
-EqbmoJieuEFjT7WRKaxE48LmmFZXV+zAeFPkOX4pQRotvNBPeFXq+SE9JBCtx4pM
-F+F5RY2UbZbra5FtnGtwMRnfgEFHxEFPUUIATsaOrTeljQnN3zhO+hN0LnRDdFQ2
-YAA5EpTIpsshc9qsgjPtrq4z0WAmTH7MFmMnPR+Rxj2ItQ11EpviBRv29o22Boe/
-A7+9Xol6kw08BaJuvDoisNinbhrapaz90qePXMOMn+rNJfVlhLpdBDBSkRAgnU27
-6lYSb1Ykfg/Hs6WtCNCIWr9yq7nxBUIS89G1TXxIURVBbKHYyZQYzIn5rDH3SqRa
-4nkGzAUY9sMSPD7VwkCHRDX4QQxE1yDcAywC0S4jvBy0kO1cah/hLJDJxo93x9mo
-x1jp8Vry+nPMP9Nxf9QCWjGguvzb8l2on+sAzYuCdWF45Dt93Y5FEi3wDTmd7ncI
-9+L+YcIkmtzdv/j5f7BeGdkeB2eB6gb9ttj7tsXBzVbMcd+RriHQOhMA40LrSWKc
-cJ38gyimEVR0TelENoh2re5IXc1aJSfXSuUu/Ic07OFU
-=d7+p
+tB9DZWRyaWMgQmV1c3QgPGNlZHJpY0BiZXVzdC5jb20+
+=CMCf
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    5B05CCDE140C2876
 uid    Matthias Sohn <matthias.sohn@sap.com>
-uid    Matthias Sohn <matthias.sohn@googlemail.com>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -13268,23 +7674,8 @@
 f9RsjU0sQQFzLW8+2xahqK3oZkLWOkSxzLtVwJbm7EGaGIYxEBjg87OnGQkAi9vv
 tVPwdO3VWyvgKLuPHudLDhTpeH3AMbzKgnru1Pnh/ZpiRhPzsbuFtFPEX8PMuCyE
 n4OLzUALl98kXuPjG5ww+24UsNgKMbKbu8qq/zRu7IHlpZvd730RoCWU2/i18tnY
-zLQlTWF0dGhpYXMgU29obiA8bWF0dGhpYXMuc29obkBzYXAuY29tPoiABBMRCAAo
-BQJMFWTlAhsDBQkLRzUABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRBbBcze
-FAwodoedAP9DQ6dmCHklm7ZLIv0qKqXUUnlMdhSnNjR+S9F2lTTQZgD+NPpDq6O5
-CEznuALlT0Em6gNRI3VH+p18oWRgPSiq2hOIgwQTEQgAKwIbAwUJC0c1AAYLCQgH
-AwIGFQgCCQoLBBYCAwECHgECF4AFAkwVZjsCGQEACgkQWwXM3hQMKHa9+wEAr0Mh
-5M4jO2l+rrg32SY3H6sncqftP1892nAf5T1W+9cA/242RKt0E4PkgZmnZdGskraY
-fBRxps9MoL1MoSihTKIBiIMEExEIACsCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B
-AheAAhkBBQJXapPNBQkUuzBjAAoJEFsFzN4UDCh23XMA/iwdnS3wIaJbIW2dRtR2
-6Hda4UzlVzdLDeQCcwmMXCULAP9PghpcGFcKalpZV2VO6M3diIfCpdgm9T+8dA6p
-jf+nQLQsTWF0dGhpYXMgU29obiA8bWF0dGhpYXMuc29obkBnb29nbGVtYWlsLmNv
-bT6IgAQTEQgAKAIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AFAldqk9EFCRS7
-MGMACgkQWwXM3hQMKHZsfQEAxwdkLDI3Gee7065wo/mYxlTEUYmmDCiQ7HErmPSU
-uWgBALkWCDFLzfjcUr7RHc7WvV/6i+8z3bfayS8FzkKHXSi8iIAEExEIACgFAkwV
-Zc0CGwMFCQtHNQAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEFsFzN4UDCh2
-e5IA/0b25vzO8iXS6pxTkSHYAuK0tMWlIBA4NOarDjE2ofV3AQCksef3ZRS3QSOs
-1eL3k/4kw6GSEyEWxMKVutH9jQfAyA==
-=xdpl
+zLQlTWF0dGhpYXMgU29obiA8bWF0dGhpYXMuc29obkBzYXAuY29tPg==
+=1oYO
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    5BCD80B78ACD3A37
@@ -13305,20 +7696,8 @@
 EbkcphrYx8C6zjnWZiS6Mo+yH5vo8IRQGD74J05IjewXMd8F4uTdb3dIksyLvQA2
 0xXxjS9ooDV0zsOOh5tuQf+nSV5lmpWJBhaWcRKg1i10lYAB0Rho1yNBUQARAQAB
 tDNPcmFjbGUgTWF2ZW4gUmVsZWFzZSA8bWF2ZW5fcmVsZWFzZV9ncnBAb3JhY2xl
-LmNvbT6JAj4EEwECACgFAl16e8cCGwMFCQWjmoAGCwkIBwMCBhUIAgkKCwQWAgMB
-Ah4BAheAAAoJEFvNgLeKzTo3LP8QANwMvE964dfSwTqsFdfw5WW1ZBgwzDzbWDO8
-UHW+3N5DVYQc+I0HGaDB0ynaZTc3TjIR57cenucBbTb0mBuy92/ogXCx59GYPFpt
-n5uf2r2s81sSkpA7CZUsJnITGJKwjVwcMAwQcIwdc7r/kz5FZvXz/uxwhGnDnVtp
-VHonLqPYgZGgUUIjIvTV6txgW1vGVKuqNqDwlnD4lYVtx5HgiWEG/jBhltwNT38A
-bcJdmiUEIWfHE8c+dJg3MBSPXtDGVQyXdSNqn6DOtSYaVwLpGV3veUIHieTZ9oQI
-gblSg5s73gRLPiDEIfCYNGmL6QG3Ip4bLPVFEqeNFGzjTKRxQxp++v2ncK0VDQLh
-C+Mryp/reizaBBPRdsGuMt+FIgPLwNEYQH8MOu5BWhdW0+G8ncjQ3pa2koZLX/Tf
-pz36rNsJexSzAcTVgTZ5vVAoREgUEjZER7Y0cBSNEPf5kD/2kDHoZr6yUmxAzi01
-FQV/E54Wzirsh0G71ysIXEW2aPqVEMu4pnjSsySgk6ASjY0SKtus3TNuyF4RSJmr
-a1t+cUdrEM0J9f46ZmFKwgnnLlqWcJ3YwghvVcJ6tX8tcuZQerHOI7VpDDfFpF/t
-Omfi07gPd15HFEKmWPiNmQHUZ8PhBH1+w30ZcJsrKQCfI+nzmh9tWt3ppjfb6K/Z
-FWwpicDy
-=6G9B
+LmNvbT4=
+=vgy9
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    5BCEE695141E6086
@@ -13354,37 +7733,32 @@
 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
-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
-=BlFB
+b2VuZSAoQ09ERSBTSUdOSU5HIEtFWSkgPG1hYXJ0ZW5jQGFwYWNoZS5vcmc+uQEN
+BFCyc6wBCADbQJtZT43Zn/qPLAzAMGNXs29GZeXBOPpyeh1drDOfx/AN/oI2T1Tt
+rqEBLTlliAoi0bMa69+9hNZMKKw6VQLXF2nL4IlN1YeEqh4tT7iww7jbLOzvGlLF
+KTGO6CQrZ/wc5ceu3ZbOZZkJ301P9BJw5Q3lUgZzrZdv2RsPsA+vKhUVKAnDc9t4
+uu0IBfL82lOKmJh/fhE3ggdOuH26au5MM3Xl97GeNSWNtttIloxXMwsEPwVjY7cZ
+r/MOBHeQXL/7OCXIWpegbnFLk4KgLxY6EuBR8td/Z9ewCICNp4WMu2Nnd4Yjbe7q
+LqOTxfglyNCLPUER5YUg7VDh1XfUvWjPABEBAAGJAR8EGAECAAkFAlCyc6wCGwwA
+CgkQMrr3gcLUVsJ+agf+KcDn43tNFnN2mTPFMovErL0gzOsDe4KpbUIH5nIDuPEm
+coNJJYsx7mxLqzfjfcmGnxwtAPPj9MmmmvgJ3s/T/Hrf6SaIPPpxLH2ysA8XtofL
+o83jkKCah6olvwD8WE2FKzHggDErKGrfIMIc0xnA3DyxvtELOrrctM1aj4TWS//C
+3m7TPsuRr5mvglsyWWLXdI5VHaEQTNuN+ofrvdubkG6deuqkjcZmHBEJixpcND9S
+kP0HN/lAYTcA+Tg4TKcAGAd0CBLiivuLeVVUTDUPALt5yTMr9/TYdkjSFOoyoznk
+uuubfPDg7esnkQbX7U2GDnboYcK5xxzu4lDMGDyxELkBDQRdGM3GAQgApruxRlvP
+SDLIkbQn1oeWj8C85lmnMy13lIXK5XJdhhyosv/8zPtT4RY9Pdk5f1gon9OveJ17
+Cq5cfTztKunGvS0sh2asAFfAGyx8sZJepkLPhx+QhxDK0/xaTywn01AksjUcrkSV
+pGyYwnjJAJgfnlRx0C32kUR1UwVuBH8QhwLcC38rP9DzvNvmgh8AoFIe701Gq2uJ
+KzzR1fuyn0m6ggApN29Hm2Od3AL2vqfgWWnPryefAoPTLp71EYoHdddT2TpIipQM
+escHKtWdh3CRU4/o7UxsGnlyRK7rD14VtTcYlKgninuKPSMPg9qWCKqmoVRip6j2
+A2GN3QTFNxh4gwARAQABiQE8BBgBCAAmFiEE3LoDOB72yJCWrNmFrF7HSYH5zaYF
+Al0YzcYCGwwFCQPCZwAACgkQrF7HSYH5zaYV5Af/fzNQ6qVfxQUYT2DIsnI9C/qi
+lw4yXTEpXpvGuxefTxBpAdKErRFAtL6AHjt4Ty74JtDFQLF/VC7Vh5Txvrg2BvPw
+pxZWvAWhQJE0pU0LJhNP9I9F060eLjuWiQUUfsgyhnkd9SclhnnBO1H8EDzMUmRD
+S1X7dKGVFEXPLyzQZ+wHJf5G8NbiiNgElZiAOHC2AbmLiNeEOtxGUB5RkNgL4rmj
+Edt6mEb+EaubEbtUScP3yE2c8BZplU9DCb5Ft8tKRNFQAO2hn8ecnJxD5jZakuF3
+45PovOuINTY3FCqcb+UCJ/6dz/FyylSaFF1NsuDIia3lMYUCPApVQbA4f0Paaw==
+=F7bC
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    5DFB46C505B694FE
@@ -13400,37 +7774,30 @@
 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
-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
-4fOBwDmHT4p9h9rCZoQtHv4yDebNAU8DE/A=
-=lsDk
+PGIuYm90dGVtYUBwcm9qZWN0bmliYmxlLm9yZz65Ag0EVXeXiAEQAMbQ6igGw0hd
+CU6yMeB4KSzLLh/OdmMr12KUklmg2BKfzfXnBhIgizCeZZ0K6pStA2hxaok9BfF5
+BXlxqauUMIZhX15lpkwe0seHu4Fed822unru7NpJvPdcRs5q8UjBulY0tG+0Zyou
+evVUPJpizS3ILzXfI/9mrpfHG9HF8XeHY6Md0WOspiY1MfIRNf1Cq0T4j2kE43lB
+U9uPI8BRJjEJsfGHnLtfVgp/VTFP9WNXjcnkgW5gpoIbEjlvHC77d5DCPE5WloIj
+Z9kZNokVAXXOPMVl8NP7jYAyDlc9Kz35jTswvMmSVq7QnWXvPDoifxIHL3L9gi3R
+myPq3xisCnCTpMESWDphRnHHV/LbcoeALTAmU7jR0c1IHBckxJnhuqFCa0BuksGU
+Dsmj1vBCia1XIOKNt3LM92Jfx/p/YckP8SL5TrKm4iLiVKtTNyYg7kZiISzDrEYl
+za7b3ZMHryXEQxYfzgTjY3kl73GpUyBsEzqkAT/8kP95X2cRSdsubvyd/CiKRDq0
+Qcs3tL3KhoVrqVFNSxHLc5W2InVSynV5WGQiUWlPZpWj36W1xWShWORJcN37fOBn
+EJuC+nlGXXVXrP9AYX/WIotgKQ/KUZMKmgxQ97GeSsyXKdU7cNeY9o0+lqlp6Ydj
+VRM5M6aq9nC+8Z+gSmnnIGIWLG4J+1flABEBAAGJAh8EGAEIAAkFAlV3l4gCGwwA
+CgkQfcMHb+ItT4hWtBAAoGaz9WCT1lwWde2YUUjNmEj6Yn8tUvpEqzSz7u/rI0+f
+BH/yrWsOJHrriYb7j49ppWx1b3IXSOQJjizCJlr6Gt1Z0e3Dtn8jxjS0RT6TRVab
+LHPCM1y9HCbcpHGNrSmLDGAQuqph9cQ1T3hFxugRe9KzjgdW1fUph4d4CHZigtVD
+saBGgLdNzyGiRALhUdilHapst4CZjCu8JsTe4AOeuoKEtUvvt2UNjIbzkEsKq4Li
+aj1w4gcs6uQrtNQPi9efB7IITHINnUPLV7s/cLwFybX9Nmf+o0ELzX72a61VyQwE
+5xOkj9CvZSidCl2IA9TPVG9GWzxE/N7gtPi4e2E0aGu5HemzTodHorcnFbBMHLGt
+ToguUIFh4Cb5h/kX/ncyNffZj4e+fmdkQoBR+8t/P54jftGGHP/ZZhIaP0q+pmdj
+OtuUoqkTXJHSYPvBWDzjkXOm4fyYXjsV7cYiw4nVjO+BuE6YaGMI4oTw5vrnwcQF
+/RZWXsQtQlSyGORR61LBz0n83P0BLEqljhSG+2Tyr/PXS5DmS82GMEzMTkP8GaRd
+myhGkX7r0SUiOWQfBsi6ORfGchK1/mGtrLX9WZwdgaLH4pQIf+4n/w5A960m6qUx
+qD0RAZLvC/MX1THVh03aTFkErifa4fOBwDmHT4p9h9rCZoQtHv4yDebNAU8DE/A=
+=UX3M
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    5F69AD087600B22C
@@ -13449,80 +7816,73 @@
 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
-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
-BTS+AGV0QPZy+bJ0C1S7fYNtHb1sdA==
-=4Z54
+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
+=J+FP
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    62C82E50836EB3EE
@@ -13542,20 +7902,8 @@
 XY6sjXHLIyM1AfDfeM0DHReDR4aIkdnQ4yzyzshmhTodguDbZe2H1FlPNqzriS6c
 4s7CoyO8tTTSHmrxkVLJhMaTc1XazhcQrsp/1s6SJ5MUy0rFUt9QyIt8zfGqzQVH
 pqXmFZd+ISd8Eivsyh5gEU4JER+3J7kSuMQx/wMMZqzu1RczUz4gI0nzTwARAQAB
-tB9EYXZpZCBHdW5kZXJzZW4gPGd1bmR5QGFjbS5vcmc+iQI9BBMBCgAnBQJXjhmN
-AhsDBQkHhh+ABQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEGLILlCDbrPu4MUP
-/A3dWNkuEdFm9ZH34l4jL0Ovnr9UrP+WVQzQdEU/X/O6kO2dPCob6C9c0PH09zlq
-RGfcJLtEqQuJTU81OZKa+YTU9N+P2wVnvl74C92kyZG4pY1o/WYySHpjqRm3p17n
-zNCt8mICaMxpZ3d+f1IX/0tUS+wSYpjt7ipfjGSeCtCYIPd75qayO1fcP5m7wyGv
-sgAkq1k5biMyb/ElusTP+XM3pkJSPG83wzbb3aOT8+/1SvnFAt/SbZYs2p8+pHp0
-ZPAVY1fD/PJ7Lbm8wYpsLFXhFI7WLQzqCkITVWxr14vwZwGpQcODhttAj5tJFX9f
-FU5NpMQ/uP8c48YPz9I+/nIbSaYY4Y7g1K1WbkI/EBtZwgPiJ9GwfriBeD5ehORf
-0CcOGpAeqt/yhVqwdybNuKvJXbf5LMbZ4hkJO3OKYikb0BLvy/YtvQ/6Ma54WlbA
-Bi6WKRvmKs/KAHq+Tb5shHl0G/92XsNlpcjzMt9/ifQC+/Hj0K4XVX3fd7QCnAuI
-rzKZNSz7ae5WHK6R3TM5f6AeQDE3rpaduhaKbNGGbgXRj10s9JirncWBSAaB9615
-riw52LArt+n06GK3z0npwFWi+VMikYiJQU4ut6QKvO5w5RT594eyLUbyq5dbXzdQ
-8Vdt+JCJIs6QgGpVs+rqpWeHFBg8J7c5W+64Bb5f1XtE
-=Fwsp
+tB9EYXZpZCBHdW5kZXJzZW4gPGd1bmR5QGFjbS5vcmc+
+=xonp
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    62EBFC78FE4156D1
@@ -13571,23 +7919,17 @@
 0f+YYgxdKn7AwgGNScEsiyds1dLZOlyieEh5ZNDq0N79ha1Q4397l7NsWY79TlP3
 HB5cvhLl+B1cb8VZOL1V3B5zifRtWhqWPYCndVIva8kkrYOMqLMzu5syBHKYzfnK
 RkQAUt8pRSCc/NIr/wYC2/ZES5ICP9XNUl5TABEBAAG0JVRob21hcyBFLiBFbmVi
-byA8dG9tLmVuZWJvQGdtYWlsLmNvbT6JATgEEwECACIFAlI7ev4CGwMGCwkIBwMC
-BhUIAgkKCwQWAgMBAh4BAheAAAoJEGLr/Hj+QVbR52cIAJ6jYqO5WF/1XDNHzsOk
-1z/Z1pJG5T7Q9XI6buh85vnVjHhbQWMCea6UBO9kVH2DEUzKbOHxVfouYWUS7cnK
-ws7OwmeRZstexZs6u/v8OOHFsezLt+qdwakPAFUkIjQSFvlhB4imwvW9WpJS7XbR
-st1D6vsRfDEd1zpGrDwcJY39iWJzRs+HdydaX1o2vmhO/jxw/GWAVGk3M+UabZXH
-ttcvZzTFaKjtZIk+EQFhLrFHQszn1sfOJxNv1FJGPiZt33My5/Ocu6Uu7npHlgiD
-c6qrOhLbRNyzat1+wwtMpytU2BUIYQMKZPtLUnrt5nszPHm7v0L9FQdC7WrcdtKh
-Otu5AQ0EOxIiVBAEAM1SlkvEK5MrMnW0ybtv9eMCG89gqIvd2gBnpcAsF0sX+dCa
-WHWNy5HL3dBak/G3BJ8+NzAksfL5Srm0LVKcfVjBiG+IsbUoSyeJQGuhSZXYcnIc
-/3Z8Ujcs+TfFurG8uHU1cWnNK5aMYwDrqxmp4Ru0zLYHw4tHBBKF0cgFaCsjAAMF
-A/49aSZuDaatppSaBOzCt7wIYCsGBxX5ZibrJqr0gLUbhXU9eaWzCawOWwCvpQN0
-lTjoYVkwiLZaYUkdqsSQgHAU3jjKlIuaIRXApEkTb8Jg7R/vNAdwXoZRLBCjZPGd
-5qGtnIezsZ2+lxFx+bRieUL8fUInemXwWl8e23PMisgm+IhGBBgRAgAGBQI7EiJU
-AAoJEKIRWuFfa4tyDTIAoJ3NtpI/E345LVOruElFKrnduWWXAJ9Adm9Mz4yoxros
-HSkp5BWzXBUt4IhOBBgRAgAGBQI7EiJUABIJEKIRWuFfa4tyB2VHUEcAAQENMgCg
-nc22kj8TfjktU6u4SUUqud25ZZcAn0B2b0zPjKjGuiwdKSnkFbNcFS3g
-=+Tqc
+byA8dG9tLmVuZWJvQGdtYWlsLmNvbT65AQ0EOxIiVBAEAM1SlkvEK5MrMnW0ybtv
+9eMCG89gqIvd2gBnpcAsF0sX+dCaWHWNy5HL3dBak/G3BJ8+NzAksfL5Srm0LVKc
+fVjBiG+IsbUoSyeJQGuhSZXYcnIc/3Z8Ujcs+TfFurG8uHU1cWnNK5aMYwDrqxmp
+4Ru0zLYHw4tHBBKF0cgFaCsjAAMFA/49aSZuDaatppSaBOzCt7wIYCsGBxX5Zibr
+Jqr0gLUbhXU9eaWzCawOWwCvpQN0lTjoYVkwiLZaYUkdqsSQgHAU3jjKlIuaIRXA
+pEkTb8Jg7R/vNAdwXoZRLBCjZPGd5qGtnIezsZ2+lxFx+bRieUL8fUInemXwWl8e
+23PMisgm+IhGBBgRAgAGBQI7EiJUAAoJEKIRWuFfa4tyDTIAoJ3NtpI/E345LVOr
+uElFKrnduWWXAJ9Adm9Mz4yoxrosHSkp5BWzXBUt4IhOBBgRAgAGBQI7EiJUABIJ
+EKIRWuFfa4tyB2VHUEcAAQENMgCgnc22kj8TfjktU6u4SUUqud25ZZcAn0B2b0zP
+jKjGuiwdKSnkFbNcFS3g
+=i5o/
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    6425559C47CC79C4
@@ -13602,26 +7944,8 @@
 oyneDbzM7j2nYXXJPNXJOfvyVxi1+rsc7xcjMvAj5ievYlWwYlAIgYbAiz969NdL
 RkoA1Wg+cQg+59k7Wvi6xwTfzMsO4jfkV2p24xn4fpcch9J49UhADh6O7XEls1Xr
 80WjysMJWTOX1O2oTtV/BMjpI4gj08SgZRhzABEBAAG0ImphdmFfcmUgPEdGX1JF
-TEVBU0VfV1dAb3JhY2xlLmNvbT6JATgEEwECACIFAk3XFIUCGwMGCwkIBwMCBhUI
-AgkKCwQWAgMBAh4BAheAAAoJEGQlVZxHzHnEMlgIAJrrq+q6k8jwxT0DJO5peszc
-0X4Z4tge6FBwb/O1K/1ViYv4wmzwidouB7lAidDF/wPh0XQxBOqXpcE9V8vAqMnQ
-DzWS4a2H6/m5jUdgc6S43CvOszSfUg70X/N53+WG2XiuYMvrxD4j/bZbLH5vDD29
-Hh8dJVOsMPNtLaTKnkQvabBnWFXovy4vDebKj2/Ln3CVtqm6lwAw4KvwFZgeuOWr
-5g55O9R03czI9AaflTYVsceB/MaKRw7ut3VuoFr/HJQnfEonZLCyHZwjXqmqJuSO
-QHR0ABrHHn/Um2/eHiPAERctmJnVYv06S/mXMr4VCYSmYTU/LH96jFCW0rHKFsiJ
-AjMEEAEIAB0WIQRFvr7slQq9Bc8O9cNQoE0MO2UX8gUCWzeh6wAKCRBQoE0MO2UX
-8jkHD/9DGURSVUovLwFcBRLl9uh11cn1ULdUffNC8UkzBgP97cC3y3XUH2nf07as
-+QJRKYF8mbIP8gHBul0/Kybd8LRajMDRVtgcRSRu1Bgj4qALP0vAztrlhFDuztOg
-adxUfZhdaCX3Y9tqEhqygonQqYiJ1AKGcrZXgA5e8JuotYm93h/wuXf0atho9Gx9
-U3ZIJfe3IkWySKg2OKErT4oDi3PQd50iZwBH83YnWLtYPsNBAGvfIXLGbvPxqjqu
-17yY6kx5vE8+kNRAzl8dldajT2fke9ixPM0tp4qgQvajQRUXa7wnE1Gtz8lylJLq
-Ys8w18lm2/mvD/RZh4B8c9Zd1bedp6fTCo4i9tRGkBALbyMQknNvCdpjs71B+w9f
-OeYOL1nI/+olvtad5dtCK7/CQnDxnEB4WbfCZXll3QArGxko8FLBiLs9fJ84aWxx
-+i94vS+Ul5BMilaLEAcwDgHjB9XgSlJ8YuXSUQ9g/XWk7DgVVXHZFiC76FdQayEc
-adJPJkmz3qAchnLhN1e0xlHMKS4PlNGPy7L2rfMWv9/FXEs40knpfCHqYwZPn9Cu
-749FMUVkXFqdxOkTtaioI+sQ+fsoITguKk5rZapm3lfKwndv7uRXRFWfWdMtvBgo
-BWdRgsO59UMeaxk/Li10+JqCuB4XSiCuFY3OFwcC3r6qBDx+oQ==
-=3RyQ
+TEVBU0VfV1dAb3JhY2xlLmNvbT4=
+=N/U+
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    6449005F96BC97A3
@@ -13667,14 +7991,8 @@
 Gc5Shwk7Hr/F3vw9EQKa1nNffWmcGEx9B+WcC9ALPVd/fpQVXvlqfbi+kaIbqv2x
 NHQr7BL8j3SpN6vhfZM/3zeghlxQ5HYWER983XwkmvbNdMxt5HWsMKWZ0utt4ocK
 TnQP8NFGlPWEQhPvRRFNb9BI0wvGD0NUb1gjABEBAAG0H0V2YW4gV2FyZCA8ZXZh
-bndhcmRAYXBhY2hlLm9yZz6JATkEEwECACMFAlbqsT0CGwMHCwkIBwMCAQYVCAIJ
-CgsEFgIDAQIeAQIXgAAKCRBkoW+q7BakvnygB/41oiYgfDqkG5srQ4nC7jE0Pe5V
-MnuLVHqsfJBGPvt2tz5+Z1ciIFFwUi/xsafX5DhC+FVOOGdeEnkKnskPBOI7uMFh
-v/s90lbhNV62LfwcS9hptE4qn0JTg7mYiiL0Zue99mlkeP105+GlMmvH5q54X2Le
-hIDBVR8DehL8ZqZCvNEVK1ftpx45mvF/4yh0YK0oVuCAAzwF9+6OxeWTCUTRHTZC
-4CWjtXKUHMq4nTRSp0wGdqd5UV0VbMn0bKTkhgRNCJAKyFw6lJ0FZWwmuG28T0s+
-bKuRAJHTAZmSM7UmBnKo22t9whNcozcqxWhK1lcS4OWEArXpCKxAx4kXmbwn
-=MhSO
+bndhcmRAYXBhY2hlLm9yZz4=
+=/1BP
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    6525FD70CC303655
@@ -13695,90 +8013,73 @@
 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
-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
-=A9vV
+Tmljb2xsIDxzbmljb2xsQGFwYWNoZS5vcmc+uQENBFOv78cBCAC8jPNMapGqKWTd
+ANbTS+ilJ6C2cf05ntaqddCUPgLWD+Xhaj6TNskCJBYFqEBRUWd7Gj7KhI2JQAHs
+q0SzieuTxkcnB3JHJHYchJhhQmx5eGyZd6p3yWbgKjklJZsTML4RU4ux3AQjnFjq
+ggZvfWPdSCMujRngfg+Tnu0RqMeGvYgw4fzX8mAk00aNb0ehluCSucv6CRn1hsdF
+NwpM68D8OXn4QoEl5qNFPrUMlElipfifQ7XUfpD+ZjaFjjsWDDd8OA+vACA13Q/R
+AhAF2SnCgJRF0NwJFUPhn4tuEuM+XsLB5vIXuhxTrGG/hWLRAcnsnbJFpVPNvSze
+PcaxtrJtABEBAAGJAR8EGAECAAkFAlOv78cCGwwACgkQvqvPvuBZ5OUvTAf8Cw7q
+rSX9QDU03+6ckAZo8ng/UBP087+tOrZy8WcJGSqrRGv/txCbjyS2DlR5J16VpeFZ
+THSb+gxIOA6zkLiQxL79Ee2KyEaimXGTUZ/vXaJyOjyPY6q22PvGVrKANZKJvoVN
+lRKLrT4MZGbGTxzI9y31kxqLk2t/xxDj+XMYuZXoqiZmCyigVybOIjDQTgbKjUEI
+w/9Ky3sodSZVRf26bGqUCSdpIo6MLeWKoNKzzgtqquxEFFeeBFYYquPuNIsp+STT
+5l2kRx/JRQmrzVizVIfR8TWR1Dm+/1JhRvU++63mOAE05dIWVGCnQmB3l3jqtm8V
+xIR5UQespLY2pO85PLkCDQRYRxmWARAA40bekjTmpm0scJqi0kKrpJFUMcA/6Vtr
+HM/MzQwAX9DDP6TxUCsSLSocX2u5T+wnhUGlVorNCbXpBwqw/jpy5JE4NV4w5GuN
+GaOkg30dT45MhKT4Cl0nKL7Y0GYsqcu7jOAwZGtH8yWDAFeVLCR9nfA88jbbrP+p
+ZXoI6oQRu03ln0w6wnGYHH1KGQyoPJDQCVleNMjesIScH6S63ddBfPL2KL54bPHt
+7tijNYG476/2CUFT/gKfI71onfwA0rM0L2IXiERVVlaw2be64xzDXVtXMsUXQ6ol
+JBSpf+RVL9G+I1dHJkchoNHaVZbXUqW3inP/FadLB2gkzlAD458aAKqBblGt37Dm
+hVRZxfJWiT6eIk+gtDdKj3+TBimC2K95hFDIupw7MzgYLxLUSiWDqzaFvwU6eTXG
+wVspRcFrbkvWAwJL8Dhb0pvCW/nkeyQFlhzsAERrvxS07Afu3yr3prnKjlcf2jjI
+VyH53CC0zVgEbvh6+YDekvS+0zmu6fDmcDe1MWIBo4s8t6KMp0afOLKcuCM/UiYU
+65cQOz95C9/MMquzH+nKu4BslcjN6qzu0EhU19T41D17g1gweMUYQsW55y2po+tg
+E6SPWZcH3W0KBR+t9assF7oe9ZQA5LWQCbk0ToV/tHn1nKf2wHwyxQNZ0/L9yKsc
+uS2v1On1NUsAEQEAAYkCJQQYAQoADwUCWEcZlgIbDAUJB4YfgAAKCRBa1mMV/AGH
+lyG4EACB8m92ujINjToVeQl9tWyK0FoTogZthx154l5arzR06wZXUu2eBS7T4Ckl
++e0dOeNWkmkhjSPJgemsK/ZkKpfnjJphcxwjtN3Yw+6LXDnZwt48d+p174lntbvx
+7tVqq1u2SaTYZGPSkEKTNndrCbuaRKwDbiGF1JBamrvjspmnCXuOUy1tSZBhs8I5
+YOM8aaa3AI4KI0HGFlkiBP+fC1BzfobL5/iba/K2lX2oZU+zcp4HGrhwlvRp+hrF
+XulnvQSpa9/Y0aciG2liT5Uo57p0wmehqRapskRkSr/Xqwsaf5rqQdaT61Pg5Xhd
+VhqO2KBd+F30D1rS9F7VSUqVPoJP/b6Wkd6bXZ3yE4UyF+ibnAWOIyCr5/FFT5SW
+pgZH7CVy7zeyL+pPLQVRAY+HZTtqI4MbMRAhUcf5ck4M4UtCNLaESlTcp/V7B2vv
+lQbfYdWn8xUiVN73ETB9yMzkqz7k5d0tm1A46t+V9Hbp3sHwDL//1x+J//Wt7b7M
+GOL3rOe+ejbGzVXhDuM93bqbhaZMoh4IUhgN/XtLg3R435lMxvi1GgYgDkbHBdWI
+tjS3TH0n2tnNLuVrXc5y1HNqHA7oDI/yNZxRRl9gpxXFiEx1SFKrkf/quZz0SYCG
+fJifmeTNVcBSisCxzh85FWl5cyhWAcfZKwmGnM8LNgy/SelvmbkBjQRc8uPAAQwA
+41wQFsOD+5c+VerdXQzsqLguAIaCEgdXeZfZCykOL8u54+0EGNXorKrWCtSc2N8b
+niYIQq2MiraOhG4Q64xVjcojPrgswkMS4uX9saZ1HFAPSdonzPeE8rorAy2Iw+DW
+1SjFYcmPHnQfSAkEHejEtWsoq/imI7X+XdF6njoBCYFyuJBdH0l89qOrEvFMFHaC
+9vNrmLCHeVCoqjAxphnwR5ChBm3qAafwIEswHlKGq5eRFSa+QW3o6DOAsKEE5oVL
+9vmONx8uJG4ZJXg3PspDcjs51hG1TJg7DikOwe1gG4t+RSSE1oE00MOC7OHo4Ss3
+8a+9PG1lqHprZnUmGv3sMDAR6cMMf2Hxg5OGF5PXctccgCP5wv2Ux2NmI/tmnHRy
+hFhectL2xz9ZGKJLPkCtds/AAROg/IYpD2K6OJaU4GLRc4yFKv4oJFi8bMy++kQ3
+zTH3jgiym4FpyJpMOg0Efb6GK7EYKmpngL5LNCRwW4PIQcJK4r+vJiR4CGpK6j1h
+ABEBAAGJA3IEGAEKACYWIQQvuinQjS4l7oTBMsMHKaCv+JmahwUCXPLjwAIbAgUJ
+AeEzgAHACRAHKaCv+Jmah8D0IAQZAQoAHRYhBG9TgHTM6/NfKK+bBmoJdfixEnuD
+BQJc8uPAAAoJEGoJdfixEnuDQnYL/2460wqw09iE2I/FUItF5hekKo1dmWwgBe+3
+Kt4byj5MvygqlJLA58GAqQDJAKqE+TjdwjN+IG7gIkCindKOh+aG2Vwi3sLZTam5
+FHjQh16wTYWmlMXWBK7sgXbKI9EonN3YZdasiGEorrYCRzYRwbG0qKgfYtmZSY9u
+6sdbL5M/0IbVE6Hmm2rha+SVGUtDFbrlyNzk46Mv6hsd0wmDcVFQOufG7c3j2KFa
+TlXNLgkG0+S/rGfj9VlKjf68vKM/n8NDGBPiWee5WUz7CIOWeVGXnQoKVUXt/XSG
+Berq88lzpx/yKJnmGTZA6Cdx6SE3LLdDROPHSb1QoxUhDL5AebuyMU3oy1/ffdL3
+E2tNGkrD23fRJKdPu85h+jZXoBL3oiiUR6ftveRyUYEa5rhzTE+xvRnrIx7WF1C9
+WmNJycfGQPESzsX+0yZUyxOgppdk//VuHDDqQNaqyi2GPo2aHGeVp4OuEMnzzuBJ
+z2QovUAiqbCfHSu/+b/Iegy8872qitowDACnIn8Cqi+ubc9neQ9hjzJsDp/QeU6H
+LNylIsvye2URCYlWL9w9R1lrJewC/zAcHbJXRnd7zMqDkrdHZjU/U+a91NlNoK8f
+DaRTQ5iBmmB4YRt7D41FlSTw43G18MfgfmOhhR4Fi6n6q9ALoI4NO3DNx5fStI/g
+f4WkuD6bIjwp2+wIRI0e3YcnH/92BYaKc+lVssZp4qNuicq1N4qYT8bTk2L311o7
+bNxO3fDNWSHDahd/oW4BT6R7K5dyMw6mTyxWux/qrX1/NvsGRx0TY9zDvItpjYh1
+6Vj/N+X5ISdSjzRZGXcFt/8IQ7j7vbOx91iHlN6AFNfB4VLmC6vL26s+7Y8DugAU
+kgw5VzZNj3fu+EB3fFktksufc0A8O5P1JT9nxvM24030Vj3hydy2y+mqc9J6+Ogp
+1ZLZTvd9tB4C6GaVNkPWP91D22EqGTUhaI5C8aS+94iJABKzpJ0XAcnnapFmu1Gp
+HEricpsxTh7hn5ZQcqdVSY8bAJbru+GizME=
+=iq0r
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    6A65176A0FB1CD0B
 uid    Paul King <paulk@apache.org>
-uid    keybase.io/paulk_asert <paulk_asert@keybase.io>
-uid    Paul King <kingp@ociweb.com>
-uid    Paul King <paulk@asert.com.au>
-uid    Paul King <paul.king.asert@gmail.com>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -13794,157 +8095,8 @@
 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
-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
-lWDb7QYtk0aPkBUnAds6U+A=
-=ukjf
+tBxQYXVsIEtpbmcgPHBhdWxrQGFwYWNoZS5vcmc+
+=pTAF
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    6A7AE74683A8AE1A
@@ -13959,14 +8111,8 @@
 AqLQE4laC3FJ4IuuUfxyRXN35a3J0/jx/cmgK/U2WWU8w1eXT8iTVC2EDKc5gaFp
 7lQSNMiBHllLIdUCobR1DS/3M6wU/YDEejRPvOG8gIYQin0JAYYEEOwu5U+6DOE8
 YUaxQrvNUUug55lBRSaIpQB3H/n5bCIA7tcRABEBAAG0IUFobWVkIEFzaG91ciA8
-YXNhc2hvdXJAeWFob28uY29tPokBOAQTAQIAIgUCT/1FigIbAwYLCQgHAwIGFQgC
-CQoLBBYCAwECHgECF4AACgkQanrnRoOorhrjHQf/c4fAlI1x2+gMqluCLXBypluQ
-x8qU9CdRidWl+tGSR2LdnFMZbM9QePeMy6DiidlrQ0LzcSaW6YJJmku7yl9vSQ4Y
-9dUbMfFjI0splV48UUxGLTRPIQ6oxuEP2WRAI7+pXqSogUBVo1pePjEtLXwGG69f
-7LdigP2zXP7bB0ttOAHu3Pmgx9QKWSQNU7LMCve0kZ+zYk9PMjMRbNnNCyUiyf4F
-vX0tX8jqYckHc+5fnPpHvT2jHAF7PbeeoJAmQa559XfA+SUEcxfiQ/Sk31Vsxoev
-1u/YoryHa45IaRnFjWpBy2jV7hKbsUJJIFF8rO/BTVHn4709YQuMLX24zlb9xg==
-=CtOv
+YXNhc2hvdXJAeWFob28uY29tPg==
+=3fKt
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    6B1B008864323B92
@@ -13984,109 +8130,102 @@
 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
-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
-6tvrWeekk14pCO1QLymgWnJiOg==
-=k0Uk
+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
+=fSy5
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7090AF43A5E10D0B
@@ -14102,27 +8241,21 @@
 WfMGdfvgQFl2vKJohrUbpKIYnhPa4HEu9FUFjVWn4iemeUVZ5OWzfEWNymrWDdLC
 q5j1YMfjVvrtT3DhQD+HnDM4l5FNFxl7DHJkeMZZl+pp6RxS++m+/xMK5WmGj2Un
 JUKzdoXGJdBA2q3erk5Dq6++ivvLqABt2J8DABEBAAG0O3NjYWxhLXBhcnNlci1j
-b21iaW5hdG9ycyA8c2NhbGEtaW50ZXJuYWxzQGdvb2dsZWdyb3Vwcy5jb20+iQEc
-BBMBAgAGBQJVUMAKAAoJEHCQr0Ol4Q0LX7MH/1GTgBitKA/RNXK04k//P9U4k7bX
-ofJDUrtwx+WNg2bi2er6RQhsWPWQ3p/clgK7by93XkgDrBPLsUTIUTCHGa/Dn9R+
-h5syQfjI5iDi1AZ47ARmSZisadG6RAzLNewQUFcYwBTmGxLBrGBjcxvrmUN1XLml
-jA4mqzvApDvwMrzWKdE6eNBf7G2k4dlwG4AzkSNMHfCDFXUgqsqvodrAp+WmGpbN
-kZzrAVYoZtfKfalakjZDdn6EqKgw0VgZynSCX1gfwrwLric12fCBWbqXARiMVaM2
-EUqbFszdNRkD/TT9vDIabQqZvLsJO6Ql50hrOJ7IPoEmxJukuS64Je/AYiO5Ag0E
-Tcsx8xAIANaw+9A7wnMvdUoUCj9rJ2eHCyZxcCgK7pQ1HMaAWdABtKjG2hPI4oiU
-xyMIFa6rCmktSRDb+rVXVEvMR7MLt9M73dKG2o/uCBl5TFXXOtOdmOEiwBjWxlcP
-fQIdTpEO6L/doJmAGH6qaTrUbdbcpc+yX/henq9LpnVQWZvHyfkI808N+gMBEY9m
-41W3OqIDOzrE8q7uaGRjifqCVPjPf5ofxqtwMq3H1PHOmy9qWnF8MpTBOJccuHwQ
-ewbDKID114qZWdl0R+0ZpWYgHyx790rcVQdUay11XI3Z6iDtoLlWKyupsR/VjBuC
-5zhr3J2E3xrVnsRJjQOZfCDkoLCviJ8AAwUIANN+3JQxYt11mmQVV7LeyH5FOmyq
-vb4ZqYUuH7gceMKezAQOtpNJBg5mVnG4p2Vp8m8qYpYvYoWEh5adxGMwjU21DFpW
-4XP332B7r62bCE9g8TD29ogp4z7uRzBnoEzPIk06LVuMWJiVLBAyL4EFM9P1DbDx
-01+V1NPUqHC92Dj+TMXBG4DtBaY58t1++q3PhEwHfU5gyYuxzptuIKynTkFx0Ca+
-uY+503PTN81p08Tj93y/Y3FZxk5yxkVaEe5HnQ6WuRbKBarWPGDGhbzT1byOPpvo
-6M4zcE4NnRhup1NnRAtKoAxHijEicBdcIDtxXuplx78a2rYDa6n3WP67RBmISQQY
-EQIACQUCTcsx8wIbDAAKCRANqKXsAtEerTSoAJ4oJBwwjbuzsJvMdNRPfmi+9yZg
-1wCgo+0Toe1rtYmUG43iX8qTOgkPn3c=
-=l9rk
+b21iaW5hdG9ycyA8c2NhbGEtaW50ZXJuYWxzQGdvb2dsZWdyb3Vwcy5jb20+uQIN
+BE3LMfMQCADWsPvQO8JzL3VKFAo/aydnhwsmcXAoCu6UNRzGgFnQAbSoxtoTyOKI
+lMcjCBWuqwppLUkQ2/q1V1RLzEezC7fTO93ShtqP7ggZeUxV1zrTnZjhIsAY1sZX
+D30CHU6RDui/3aCZgBh+qmk61G3W3KXPsl/4Xp6vS6Z1UFmbx8n5CPNPDfoDARGP
+ZuNVtzqiAzs6xPKu7mhkY4n6glT4z3+aH8arcDKtx9TxzpsvalpxfDKUwTiXHLh8
+EHsGwyiA9deKmVnZdEftGaVmIB8se/dK3FUHVGstdVyN2eog7aC5VisrqbEf1Ywb
+guc4a9ydhN8a1Z7ESY0DmXwg5KCwr4ifAAMFCADTftyUMWLddZpkFVey3sh+RTps
+qr2+GamFLh+4HHjCnswEDraTSQYOZlZxuKdlafJvKmKWL2KFhIeWncRjMI1NtQxa
+VuFz999ge6+tmwhPYPEw9vaIKeM+7kcwZ6BMzyJNOi1bjFiYlSwQMi+BBTPT9Q2w
+8dNfldTT1Khwvdg4/kzFwRuA7QWmOfLdfvqtz4RMB31OYMmLsc6bbiCsp05BcdAm
+vrmPudNz0zfNadPE4/d8v2NxWcZOcsZFWhHuR50OlrkWygWq1jxgxoW809W8jj6b
+6OjOM3BODZ0YbqdTZ0QLSqAMR4oxInAXXCA7cV7qZce/Gtq2A2up91j+u0QZiEkE
+GBECAAkFAk3LMfMCGwwACgkQDail7ALRHq00qACeKCQcMI27s7CbzHTUT35ovvcm
+YNcAoKPtE6Hta7WJlBuN4l/KkzoJD593
+=cEMj
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7186B06E1ED139E7
@@ -14143,186 +8276,8 @@
 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
-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
-JehEanFH1GH8jRLhA2xh7MZvFIaWMrQ8vA==
-=ZKHF
+YWNoZS5vcmc+
+=KDxr
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    72385FF0AF338D52
@@ -14355,14 +8310,8 @@
 KdobRO+x8v1scsrnUG83J7sTSaja0McmgUhKrhJqrgSk1Tod45SxprxOyp0cgATY
 xjHrf2rkafBn7K7aFDe8a73iCJPWS77gxTZCZ72xkcnMLR0m7QI8TzFa4lRjTovA
 QcTpr7jwjmyjA1+68peL6VHdVr0cdXm34mTVABEBAAG0IERhbmllbCBMZW1pcmUg
-PGxlbWlyZUBnbWFpbC5jb20+iQE4BBMBAgAiBQJO/objAhsDBgsJCAcDAgYVCAIJ
-CgsEFgIDAQIeAQIXgAAKCRByR1/TBrnKtw4gB/9WDUhNXrY8Vaft4Zm/NAuvcnR4
-S8y/lHRicztw85kI7SCxRutv1ClgBTGRaS9tdsf1NtDiY0rBgOTOLSgVPUb6EVGW
-PPpt/gWm92+k6vkyEhWkMFqF/Y6RyZtUmnYOPHNXWfFbS30HjMO1GprB32FW1RZx
-JYI3EICWqDrO1Ud/+WO76O8Mf0vZiLPO6qlxeaOQ412hHo8AenTiFs6a69xM7Mmq
-kt9o6SKbZuQTBgvsUij6FT+cdHDr1i8+r9VrgKBPHG8nHYrKH8p9HDh3qWZsA5SB
-CQf8NTei48+qk54T3Ev4fLuidr2PQWLds+HqMfobkP/GmnsUchaYMdhQJtkO
-=6AW2
+PGxlbWlyZUBnbWFpbC5jb20+
+=W7gc
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    72FEFD1572EB75E1
@@ -14412,171 +8361,164 @@
 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
-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
-9icHs3H+moEQvI6foH7MvyM=
-=ykmk
+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
+=R6dm
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7A01B0F236E5430F
@@ -14591,15 +8533,8 @@
 /TqQn1tpMh7XfZZ+XqQKANVRECUiCYT4iJKWMqcBpLZW8aa+iYW8yCQ1xfmNXjrx
 jpTqFCiQjvwCw4dDffNe/A1Dbq0wE6mw3YHW3OC1fnLiP+TEM9P9v02bZyem6uW2
 +krrToLTTHSqIGF9wUUF6S3Ikrw2EtJiRQtnABEBAAG0K0luZGVyamVldCBTaW5n
-aCA8aW5kZXJAYWx1bW5pLnN0YW5mb3JkLmVkdT6JATgEEwECACIFAlZUsiQCGwMG
-CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEHoBsPI25UMP6r8H/Rq70+JN+EyS
-6iK6ilytB1vY5GdwBzAxpsRUhTL1lXSqgjCXy/6sgrTQv/9/EgQznm7hEFVkwr5Z
-CacXeajvFJ3FVtjgn0aLRADp88Ry75DsttjwV5nIBNvFBJbydvJhfruHY8gzdUv9
-yOHAX4qtoNDFRn1HDbNsYMlRSXgjHX0JvBexdtZArtsd3aLOM4m8r+t1GFM6ePgj
-CD1q8yBH2RAIETAId/jiu07AqsJXp+UPBgJQi8o7cEtEE1HgkI+IoEsZXFJldRqF
-GJS6eKS8bpjX16nHI0SnR/gtsWpblAdZsY0YieK7o2pU8lvlCFffaUxXDOP7+BCq
-8W29nfaD6YQ=
-=gacu
+aCA8aW5kZXJAYWx1bW5pLnN0YW5mb3JkLmVkdT4=
+=v0cO
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7A3C11434EC6434E
@@ -14614,21 +8549,8 @@
 6Jl2JC3PvD9sF20wE9anxe1GuZUHQVldac/VEGFR/OgfxCHrv5NR+9wPWbjg3usm
 PYON//Wnoa8jloiYdBtSAnaKidXBHGPxkBBJsgbzbtiKjAVeeKnxkrA/lHilPns7
 m/QGX6Va0wXv2BLKEzbnqZpHKhzJJeIrXSTBABEBAAG0I05hZGF2IFMuIFNhbWV0
-IDx0aGVzYW1ldEBnbWFpbC5jb20+iQE/BBMBAgApAhsDBwsJCAcDAgEGFQgCCQoL
-BBYCAwECHgECF4AFAldLg9IFCQbajTMACgkQejwRQ07GQ04FjQf/QXLHb089ztuB
-YXulwmjt6m9LnJLxwbUo91CtIoL0KGFp4peXASghXx354c10OjZS/CkmYUgUEGtD
-OWE9f2w5oRkeqvZRphWb1siNZ9IF0sqSy5uDS9bBZNeyUxBF0wVNKguyuN4NnSJF
-y1MggAoAhg00Ldh+2cNLMBPyI7dJRg8lmyxtk9zFU5y2BhpDbWwnZXaI6EdcGn5v
-4jp5RkJPKgpe2lc6++pwjV9bmkifdpRz1sRcrC5a0OqCkcZizmXcNR87SBBD8aBV
-l9HUAofEAOXpKfGb29RHfOmU29bZLkqW52ThzWm9on70gKoF0328fEDbAn3EVsvX
-3KgXsE8hV4kBPwQTAQIAKQUCVDNdlAIbAwUJAO1OAAcLCQgHAwIBBhUIAgkKCwQW
-AgMBAh4BAheAAAoJEHo8EUNOxkNOmNIIALSPDWJj8mgy+aDmGy//GFFL64I4r0d4
-/QwwlmwpF6mZ5s3H2atPgLlgNN47pYneB27h7+A7O7b+FJNKA1SdA/f/myrQSclQ
-yGogHcpXB3OCrlDGfqzG4mJ/TZqWg7ZRyBpBscQly52w1NP+ayQ5n0ZLueNcO5+i
-EtVYXC1JRfUAvHIdiOUdaE4yUxi5QkNbS+6i9Cqw7eOv8O1pf/un8KqwNVbBLxFz
-BQ5qy7XMFq1cNUByTc/oJS8pX59dNw2IQUXcJmFo2e03YvyolAhwdK6b7aYDOmXn
-J/prHoPLZ0LbX/qzjghMAZTKzHUpYsNxPM/AL8BpRegWVpnJGRBsrAc=
-=KP6r
+IDx0aGVzYW1ldEBnbWFpbC5jb20+
+=o9GX
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7A3C9FE21DFDBF44
@@ -14650,126 +8572,59 @@
 JPBbXPMSYcwAfS5cyP6WfbsxFFuWyGF3TcvGxLEqEg0HmQ2THYzAXmv7XNDbxI/X
 L+g9DSfq37+uOdRY10PlfGS3N/5XwfN98N8ZOqHDg0sgoKLxRLQ5QW5kcmVhcyBM
 ZWhta3VlaGxlciAoQ09ERSBTSUdOSU5HIEtFWSkgPGxlaG1pQGFwYWNoZS5vcmc+
-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
-=YjSd
+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
+I3m63cg7kkd57Rh2
+=kQmC
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7C25280EAE63EBE5
 uid    Oleg Kalnichevski <oleg@ural.ru>
-uid    Oleg Kalnichevski <olegk@apache.org>
 
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 Version: BCPG v1.68
@@ -14783,45 +8638,129 @@
 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
-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
-=2qrS
+aWNoZXZza2kgPG9sZWdAdXJhbC5ydT4=
+=4NbF
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    7C30F7B1329DBA87
+uid    Ktor Release <ktor@jetbrains.com>
+
+sub    72FF58594F983302
+sub    3967D4EDA591B991
+sub    0588BC69A286FF16
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQGNBF+TCd4BDACbIA94MfIWL0SpvZwBddXgx36Lp9GYOWNgGoQCWSvk9vaMrLaI
+rEll0xnoP98CfBQYrVSAmHDMhSLBCjNB3V1Sdz8GRdOG7HUffF7Cqwbm3Fxo3H/h
++Tsrodv23NuvKsDpgglUL6nJy5e/FO8y9dcxLXRRVdPFDhJubi08SiUJy9FQbnfA
+yb2LuTzXtjDmjEsMZpdpQUlQkk0xNDkrrq+2miwxemVd35cnVQCFP0K7c4T0ksGg
+Rf9A2r45DBbPfvwTL+ZbrGtCssUpCneWhPl79UsMxeY+vJjEggqqqRqbHRn6nOQd
+3gKSaEqdALZURPzvkKxLUeUUtMk/tkFdsNe/ea7edk6G3MI4dbUY7p0XLS54S9cB
+1JUAHNEFtuJQKGWNuwWO58Yun1EBtOdUEvnIIoQ+CIN/XeKrnEIXE3LSblB8BR3H
+bqX54BMe9AzsmDQtc5pUOm2pfvCoiv8xFXQznBg24dGqo2A/jMoUnGj6oRj7k8mt
+i9AdPLigldr0S0sAEQEAAbQhS3RvciBSZWxlYXNlIDxrdG9yQGpldGJyYWlucy5j
+b20+uQGNBF+TCpABDADRarOqvERlpMCJjNXGZpK5sV7Umndyu1rwVOfEBhINkRX1
+vzzFJFciIfWEZ2c+vSNnXZC+vFuAYtwnHqTWwyodHU+/jwHeEWQ9WcD2buSwJvps
+kSei7ZMSWx7zAGWM4ae0FmjJrVHEQhM1CgeDwrxIzJqoOhrC26IorT7bGB5M2Z2n
+NStGz9fen71jNeyo0fHvvy9xkcEWsfDd9A5V0odRb5y1yKiHH4Puz+o6Gys43/PQ
+Gzf2NBx1sjzQjmJrrufvMIzRWrJwySYJQZkr/qdJyqbKZgbA/BWTmpN9POranNd0
+YO2/lbD7eiDkBflNGnWcb305VVzyZSD1kXXeLAc+y4cQugj+FkA/9Tv2c75sIhXP
+QAlZAG3ldj8WSiAlyyVuuWZh3eyxxH8J9LKDXJpBqvNvzucso1PQS2HzKhT98GxX
+45LRTsZo6yM5XAFgqw42KqTmcOy97mzluVCos090d25zYwCYsFoSaIX06wYz1GuS
+sW/JHXyUwsG6BWScgqMAEQEAAYkBvAQYAQoAJgIbIBYhBDlMtDbFaRb8Ae6kp3ww
+97EynbqHBQJjUcQsBQkHgSCcAAoJEHww97EynbqHUwEL/1F9/YVb44+iEUpwTS14
+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
+bsFtKMUFPmME
+=SjRY
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7C7D8456294423BA
@@ -14840,33 +8779,31 @@
 FZMiA/9cWPkRx0RfAJmBPKmKkbBkEtBbNau3G7MY1OEAkEkRnzmnyyjr5IP84A7K
 RdjTCvkbiQrOQH00Ki4sHIg+9Xv1gDg1XLkFDzRARKA1TxjL0OeS4RWF3iia7Swk
 MOnTdhR50pjb18W8kB4mEMZY7duP4nwDfQwHMwbFZGHrjImaurQpSGVucmkgVHJl
-bWJsYXkgPGhlbnJpLnRyZW1ibGF5QGdtYWlsLmNvbT6IYAQTEQIAIAUCS+xnDgIb
-AwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEHx9hFYpRCO6PVEAn2zcXoOXcW0I
-7Ht0qnUs4ZMM84ahAKCAKHGR83czneSiWoCsXKJQo9ZQ3LkCDQRas4woARAAufAI
-5QH0KZpOM2ZwkUbDqEJLOhxXhFXdvY03TPZ7wajL9dOlotV6yHGlKZAInHPJla5c
-+Z8sgZzAmLaOg4Xp0qk4gsmg5uzlkttRrFzoN00iZP0tYUXKGRbsYZyFm9AezBnU
-oNbrZfu61JumfPY1x4ggHEKUaO910CjtjHTlPa94gFpaEFlzG9NdmFgVvpCrxEOy
-8phErVSmXQAp9vgBIVur5TOvg5RP4zrQQcjjkjAVjGMJQ8oVGtfFXcpve6gzfnl1
-Ho0QtaEAy6HbyzpyNZdp2/6XOG69MlZP6TAZ0WAQ7S9r3FpE/8ebUjteE7uq5LzB
-mVDYXOjhDqtwuCzGwPt9Hjf0XPnea288HdfsLuLQH4UDvLxZKYuoQIvkBWxm60Wk
-IlQl+hySGfxn/Ng7mU0ktxiQouxtjAZDzEEheYRZWj3nguh5rTgCrys89bkv+CgQ
-49PjSTrILe++/QQYzxIa/VmtUrDeIaIOzd/j18l/1qvR/7GDeVWj6mX7MplDmegR
-gXVTpTRALhwFV/AKWoXZa/YaueFNVPiutHyMpo3eg+NOL2xmCtwlG1q3mgVACHRZ
-rszCa4v73iFYhuObQaMY8+TJwKaLn9siLxcvRAhb1vGpoNuqGfTX+yDpnld+RDzF
-OjvNvb7lyDpGHpiShGg1UryOPu597s8tRF0agCkAEQEAAYkCJQQYAQIADwUCWrOM
-KAIbDAUJCWYBgAAKCRADFb+3lwoUT4IBEACkLeajT/i+p8HSkq7aiEPVEN+sKNzx
-u+x1UDkF9XGtmYFCvPUDJddt22X8stVQU9313Wyj42VgJark/O38dhXNuQxqFhYa
-+3ioIBshyYVnHX5MN0O0vmf6kY3w5EHu7FE7cVJlvoTxqIK+aOvWNmbzmj+w/g04
-DX+7Uy/BRQKIX7c7kjNYGVdlhbWbybv/XqqD0vD4LRUvWhZiOwMSfvzw9nY8YUmr
-pv/4ezmZMztPdivUdMLHfZrxXi6VFEVG+7u7LGcEl4ssVRzu9YyQj5IMs91jj8j8
-O+Skw5RoDUNQauMRKyzwdn84pYX0I/vHuWsuyLC2R6vTbj47z5L7/BgQZLvoGh8Z
-0jcd2Jqh2ryY9PwsaOedZF3ZbYsDLN8tvdss9QfUmfVtiexNeSh/nbUy9Xpv92RL
-02RNIznUe3lv15CcQ3JEBAMj+quh3DRpCY+U2RgEUyvvW0s5r7RL3GTHoSTNlKgI
-sC8ydWA+RT5a3/DP+LA1aLvcEIjesSxlikPzJo2KzfUojysu1QDVBpikwrIwK9Sn
-h/fHX/Wd50GmVQxPM04Fvi/NEH8UpRw8wCRvVW9eP560ycuD9AGKz1mchszh44i0
-yB8DHVneTzEfq3CgoERJCJl7YD5wGQdNHavNLP72RkEMov8fTyW5CUsFo9wQlCvf
-BosOf7XRtyG9cg==
-=PuoS
+bWJsYXkgPGhlbnJpLnRyZW1ibGF5QGdtYWlsLmNvbT65Ag0EWrOMKAEQALnwCOUB
+9CmaTjNmcJFGw6hCSzocV4RV3b2NN0z2e8Goy/XTpaLVeshxpSmQCJxzyZWuXPmf
+LIGcwJi2joOF6dKpOILJoObs5ZLbUaxc6DdNImT9LWFFyhkW7GGchZvQHswZ1KDW
+62X7utSbpnz2NceIIBxClGjvddAo7Yx05T2veIBaWhBZcxvTXZhYFb6Qq8RDsvKY
+RK1Upl0AKfb4ASFbq+Uzr4OUT+M60EHI45IwFYxjCUPKFRrXxV3Kb3uoM355dR6N
+ELWhAMuh28s6cjWXadv+lzhuvTJWT+kwGdFgEO0va9xaRP/Hm1I7XhO7quS8wZlQ
+2Fzo4Q6rcLgsxsD7fR439Fz53mtvPB3X7C7i0B+FA7y8WSmLqECL5AVsZutFpCJU
+Jfockhn8Z/zYO5lNJLcYkKLsbYwGQ8xBIXmEWVo954Loea04Aq8rPPW5L/goEOPT
+40k6yC3vvv0EGM8SGv1ZrVKw3iGiDs3f49fJf9ar0f+xg3lVo+pl+zKZQ5noEYF1
+U6U0QC4cBVfwClqF2Wv2GrnhTVT4rrR8jKaN3oPjTi9sZgrcJRtat5oFQAh0Wa7M
+wmuL+94hWIbjm0GjGPPkycCmi5/bIi8XL0QIW9bxqaDbqhn01/sg6Z5XfkQ8xTo7
+zb2+5cg6Rh6YkoRoNVK8jj7ufe7PLURdGoApABEBAAGJAiUEGAECAA8FAlqzjCgC
+GwwFCQlmAYAACgkQAxW/t5cKFE+CARAApC3mo0/4vqfB0pKu2ohD1RDfrCjc8bvs
+dVA5BfVxrZmBQrz1AyXXbdtl/LLVUFPd9d1so+NlYCWq5Pzt/HYVzbkMahYWGvt4
+qCAbIcmFZx1+TDdDtL5n+pGN8ORB7uxRO3FSZb6E8aiCvmjr1jZm85o/sP4NOA1/
+u1MvwUUCiF+3O5IzWBlXZYW1m8m7/16qg9Lw+C0VL1oWYjsDEn788PZ2PGFJq6b/
++Hs5mTM7T3Yr1HTCx32a8V4ulRRFRvu7uyxnBJeLLFUc7vWMkI+SDLPdY4/I/Dvk
+pMOUaA1DUGrjESss8HZ/OKWF9CP7x7lrLsiwtker024+O8+S+/wYEGS76BofGdI3
+Hdiaodq8mPT8LGjnnWRd2W2LAyzfLb3bLPUH1Jn1bYnsTXkof521MvV6b/dkS9Nk
+TSM51Ht5b9eQnENyRAQDI/qrodw0aQmPlNkYBFMr71tLOa+0S9xkx6EkzZSoCLAv
+MnVgPkU+Wt/wz/iwNWi73BCI3rEsZYpD8yaNis31KI8rLtUA1QaYpMKyMCvUp4f3
+x1/1nedBplUMTzNOBb4vzRB/FKUcPMAkb1VvXj+etMnLg/QBis9ZnIbM4eOItMgf
+Ax1Z3k8xH6twoKBESQiZe2A+cBkHTR2rzSz+9kZBDKL/H08luQlLBaPcEJQr3waL
+Dn+10bchvXI=
+=L0Hd
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7D713008CC07E9AD
@@ -14886,21 +8823,8 @@
 tgdEd559iwpblEnQirIMP7jzH84yp2JY09gWoaUeSjUjD4s0N+6QZlc1bMI2U9c0
 LQRi7e3OigloDo/+4JFcOemHONjuNB7Sr8yFMZH+daAdbabkROoFWQg6+6gj1biu
 E0OmqL5o7eA/dxQGp+3bAtH3uBkrIkuBTVurclzRFNmftwaGq7Y4S4jAXQARAQAB
-tCVBdXLDqWxpZW4gUHVwaWVyIDxhcHVwaWVyQHJlZGhhdC5jb20+iQJOBBMBCAA4
-FiEEbLh7GKRTmQ6slFP4fXEwCMwH6a0FAl99kCMCGwMFCwkIBwIGFQoJCAsCBBYC
-AwECHgECF4AACgkQfXEwCMwH6a3JvA/+OQ6O5Qt5+Rh1tg744yvRIOLMwNMn9sJv
-px1/d7DdsCvpI+1pVdwIdfHt987DXjV5e51jgBp9rQfVZw/V/ddpyYivl77EMjzq
-gn35K5jjv4rHHxQZBsOWyZ+j4xfKmdRF+ZP+maU6mQjdelY2PGmkBQ3Xe7LOHetN
-vqw6xvI4AHHddCjC50Sh4hyKcJrAeA8fgrB19weQCMMm/ThAUfUuzlps4/+Zk0wP
-+aFcVED4lO25IDet19f5ori292pNDlvcjlS5fjLHgLgkGGzW/2DiQ3nZdMc8Gs1M
-1rsUiEJbWSe0NCWh2noRXqS5+eoQbSCUpd/gLKoEgI5NeAdFKIGZLC4i5UALcvSZ
-tM7tx8N7LOSW0dDhYiq4S9xtWJvWTm1h5JE6udeSogdawvyAUdUIVTRxYPiGax8G
-uKd3i+v9yTk4Of2HdEPhfVR3oOv1sz/cf7bNylOPuIxgYJdilITTHEwpZeqN7sZA
-z9U3ukE6I48bY3tov1jMoAF/hzDOwW5WOm5ItAHrd4Cc3J9w5vLhOB1REflsvzTy
-+7SblrmDMP/E/+6KrJ5bvEaUHWZYe1SvIHrwfWsYZze5LEK0iS4dByVPvJn9KJID
-VlpsvIMqtYpwRQf+XvCsTxNnP7JaYDjkC5IgDiNpzgODZWMJF2+wXAhLwAjJooJx
-bgwILJD/yu4=
-=6NUQ
+tCVBdXLDqWxpZW4gUHVwaWVyIDxhcHVwaWVyQHJlZGhhdC5jb20+
+=Uvz8
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7DAB2C1BDF8B7D84
@@ -14915,15 +8839,8 @@
 U09Kg6/j5KCiWhy9Vu/52/LSa7oIj9WsG+XNqZKRauJPqdWzVn1MzjW6p0Zph/VN
 OPr3LbDPzawr/vk6S+JTr9U84JW7ultpdwW1dLtr5ewgDXw8pOQZL/JZszjP6leE
 ZEcgZ+4cc5O6MVBUJp9OazI6L9xmx9EsyQDxABEBAAG0LkthbXJhbiBaYWZhciAo
-bXkga2V5KSA8a2FtcmFuQGthbXJhbnphZmFyLm9yZz6JATgEEwECACIFAk/vdHMC
-GwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEH2rLBvfi32EDfgH/3Wqzqiu
-eHLZCfx5qqXlrnyLhodfn+NJgGjEs4zREIzB9wduW9YNvifSKcIzx8JkgTRhFeCL
-f1J+Lb2DGEVctSJfwrDkck+Dp0xIWYnBNTR3npzdX0ytgoheN1Yv1Hi6dxD4QVHZ
-8JuFM7SIsrVGvw6D+8+NqTQtArE5iBbe7O+VD5/Ea6nAYTWw9W1LHy/1n/rIHlOn
-4RIAormJPpdcrlLJDdsV2LGIV/dSzlL0Dp7oSo7eSNPA2RW0+ceuYIjP4bVdX0bP
-Sy5s2tot3+8JGw414hcgoQujW704X3YoqZl2qjEe2WQa4STnvA96xSAAaCYaNoUm
-2iS5PF+PHWwQZk4=
-=YEzN
+bXkga2V5KSA8a2FtcmFuQGthbXJhbnphZmFyLm9yZz4=
+=rogS
 -----END PGP PUBLIC KEY BLOCK-----
 
 pub    7DC3076FE22D4F88
@@ -15017,361 +8934,3 @@
 zGhH2sSZ2OZp
 =uFiY
 -----END PGP PUBLIC KEY BLOCK-----
-
-pub    3D12CA2AC19F3181
-uid    Tatu Saloranta (cowtowncoder) <tatu.saloranta@iki.fi>
-
-sub    575D6C921D84AC76
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: BCPG v1.68
-
-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
-FdEbaZ88HZxTa30iXtOXOLvWIYRp8hv3pSb8id8=
-=hALf
------END PGP PUBLIC KEY BLOCK-----
-
-pub    54AC8E2D98CFEAC6
-uid    gitlab@ysb33r.org
-
-sub    1309825C79629B3C
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: BCPG v1.68
-
-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
-7c5p
-=sqwf
------END PGP PUBLIC KEY BLOCK-----
-
-pub    54AC8E2D98CFEAC6
-uid    gitlab@ysb33r.org
-
-sub    1309825C79629B3C
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: BCPG v1.68
-
-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
-7c5p
-=sqwf
------END PGP PUBLIC KEY BLOCK-----
-
-pub    54AC8E2D98CFEAC6
-uid    gitlab@ysb33r.org
-
-sub    1309825C79629B3C
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: BCPG v1.68
-
-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
-7c5p
-=sqwf
------END PGP PUBLIC KEY BLOCK-----
-
-pub    2BFD7825A8984FBE
-uid    Lerbscher <jean-pierre.lerbscher@jperf.com>
-
-sub    9F621F1AE6EF2591
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: BCPG v1.68
-
-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
-=Ps3/
------END PGP PUBLIC KEY BLOCK-----
-
-pub    1F7A8F87B9D8F501
-uid    Download <download@jetbrains.com>
-
-sub    CFF46EE3C17E53E9
-sub    C2148900BCD3C2AF
-sub    7D1BE4480B61E2A7
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: BCPG v1.68
-
-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
-=LGA9
------END PGP PUBLIC KEY BLOCK-----
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index ec0409a..1e27772 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -20,7 +20,7 @@
 -->
 <!--
    This file declares the dependency verification metadata for dependencies used by Gradle.
-   When you upgrade a dependency, verification is likely to fail so you will need to update
+   When you upgrade a dependency, verification is likely to fail, so you will need to update
    it.
 
    If, for whatever reason, verification fails for you, DO NOT DISABLE IT but follow the
@@ -31,7 +31,8 @@
    ./gradlew -i -write-verification-metadata sha256,pgp -export-keys resolveAllDependencies
 
    Then check the diff, remove the unnecessary entries (for old dependencies) and make sure
-   the new ones are correct.
+   the new ones are correct. You also need to delete the verification-keyring.gpg file as we use the
+   text based keyring.
 
    You have to update all entries that have the "Generated by Gradle" origin. The usual reasons are:
    * Artifact is not signed
@@ -77,8 +78,10 @@
          <trusted-key id="07dbc3c1ab1f4468471656061c8d5ef0df2b70d4" group="cglib"/>
          <trusted-key id="07e20f0103d9dfc697c490d0368557390486f2c5" group="org.awaitility"/>
          <trusted-key id="08f0aab4d0c1a4bdde340765b341ddb020fcb6ab" group="org.bouncycastle"/>
+         <trusted-key id="0a123c1ed3f13a6a0140e166c71fb765cd9de313" group="org.apache.ant"/>
          <trusted-key id="0cb5871fb7bf3b351614bbf6ca85ffe638d4407a" group="it.unimi.dsi" name="fastutil"/>
          <trusted-key id="0e18eae07b7774eac5db3f2113bb90ce8eafbe37" group="com.microsoft.playwright"/>
+         <trusted-key id="120d6f34e627ed3a772ebbfe55c7e5e701832382" group="org.yaml" name="snakeyaml"/>
          <trusted-key id="160a7a9cf46221a56b06ad64461a804f2609fd89" group="^com[.]github[.]shyiko($|([.].*))" regex="true"/>
          <trusted-key id="1616273079fe63e31c938f10f0df21d1d0a3c384" group="com.google.inject" name="guice" version="4.2.3"/>
          <trusted-key id="19beab2d799c020f17c69126b16698a4adf4d638" group="org.checkerframework" name="checker-qual"/>
@@ -190,8 +193,10 @@
             <trusting group="^org[.]jetbrains($|([.].*))" regex="true"/>
             <trusting group="^org[.]rnorth($|([.].*))" regex="true"/>
          </trusted-key>
+         <trusted-key id="8a10792983023d5d14c93b488d7f1bec1e2ecae7" group="^com[.]fasterxml($|([.].*))" regex="true"/>
          <trusted-key id="8c03da5873b13e40c8c0dce46a7ae74683a8ae1a" group="net.sourceforge.htmlunit"/>
          <trusted-key id="8da70c00df7af1b0d2f9dc74ddbcc1270a29d081" group="org.apache.ant"/>
+         <trusted-key id="98465301a4939c0279f2e847d89d05374952262b" group="org.jetbrains.dokka"/>
          <trusted-key id="9857c388d7d1d9d031274cd0a5def5a76f94a471" group="com.github.spotbugs"/>
          <trusted-key id="9e84765a7aa3e3d3d5598a408e3f0de7ae354651" group="^com[.]squareup($|([.].*))" regex="true"/>
          <trusted-key id="a2570288e10932263e8326cbaa49c633b4734832">
@@ -232,7 +237,10 @@
          <trusted-key id="bd95a93175bfe816fc152de5f9fd57a03b80305d" group="com.lihaoyi"/>
          <trusted-key id="bdb5fa4fe719d787fb3d3197f6d4a1d411e9d1ae" group="com.google.guava"/>
          <trusted-key id="c0822d185fbfd963c33acbfea318a06284d602d5" group="com.tngtech.archunit"/>
-         <trusted-key id="c7be5bcc9fec15518cfda882b0f3710fa64900e7" group="com.google.code.gson" name="gson"/>
+         <trusted-key id="c7be5bcc9fec15518cfda882b0f3710fa64900e7">
+            <trusting group="com.google.code.gson" name="gson"/>
+            <trusting group="^com[.]google[.]auto($|([.].*))" regex="true"/>
+         </trusted-key>
          <trusted-key id="c9a503282182ec4ecdb914ea102e05d8da6c286d" group="com.sun.xml.bind"/>
          <trusted-key id="cacfbd4755a2fc78709bdd92be096e29edb8d141" group="net.sf.proguard"/>
          <trusted-key id="cc4483cd6a3eb2939b948667a1b4460d8ba7b9af" group="org.mockito" name="mockito-core"/>
@@ -262,6 +270,7 @@
             <trusting group="org.apache-extras.beanshell"/>
          </trusted-key>
          <trusted-key id="df49fa14952e59a7b21931ec8edf2667d0ecffaf" group="io.mockk"/>
+         <trusted-key id="e62231331bca7e1f292c9b88c1b12a5d99c0729d" group="org.jetbrains"/>
          <trusted-key id="e77417ac194160a3fabd04969a259c7ee636c5ed">
             <trusting group="com.google.errorprone" name="error_prone_annotations"/>
             <trusting group="^com[.]google($|([.].*))" regex="true"/>
@@ -281,6 +290,7 @@
             <trusting group="^org[.]codehaus($|([.].*))" regex="true"/>
          </trusted-key>
          <trusted-key id="f3184bcd55f4d016e30d4c9bf42e87f9665015c9" group="org.jsoup" name="jsoup"/>
+         <trusted-key id="f3a90e6b10e809f851ab4fc54cc08e7f47c3ec76" group="com.zaxxer" name="HikariCP"/>
          <trusted-key id="f42b96b8648b5c4a1c43a62fbb2914c1fa0811c3" group="net.bytebuddy"/>
          <trusted-key id="fa77dcfef2ee6eb2debedd2c012579464d01c06a" group="org.codehaus.plexus"/>
          <trusted-key id="fa7929f83ad44c4590f6cc6815c71c0a4e0b8edd" group="net.java.dev.jna"/>
@@ -304,18 +314,21 @@
       </trusted-keys>
    </configuration>
    <components>
-      <component group="android-studio" name="android-studio" version="2022.1.1.12">
-         <artifact name="android-studio-2022.1.1.12.linux.tar.gz">
-            <sha256 value="f224853c7d0f4d0933575b7c6c4abb081b4e53ca737670ff3eba557203e73ba4" origin="Verified" reason="Artifact is not signed"/>
-         </artifact>
+      <component group="android-studio" name="android-studio" version="2022.3.1.8">
          <artifact name="android-studio-2022.1.1.12.mac.zip">
-            <sha256 value="e9e154f7d5714f2f46463646dc7fa15b8dcb6f4c79bb87c8295ab7aa3eb6b0a4" reason="Artifact is not signed"/>
+            <sha256 value="e9e154f7d5714f2f46463646dc7fa15b8dcb6f4c79bb87c8295ab7aa3eb6b0a4" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
-         <artifact name="android-studio-2022.1.1.12.mac_arm.zip">
-            <sha256 value="c372719eb01b895c95aff5d072eb2afa5c64c581c640f78997251e9a33c2f043" origin="Verified" reason="Artifact is not signed"/>
+         <artifact name="android-studio-2022.3.1.8.linux.tar.gz">
+            <sha256 value="3face014d1862064e18fb5490854e64f66f9e16a512c8b52476481b21611cee4" origin="Verified" reason="Artifact is not signed"/>
          </artifact>
-         <artifact name="android-studio-2022.1.1.12.windows.zip">
-            <sha256 value="6d198058cc39d568ac65cef58f84a5927f5e5a32ff6aeb76c1020f421ae0c4f7" reason="Artifact is not signed"/>
+         <artifact name="android-studio-2022.3.1.8.mac.zip">
+            <sha256 value="3e57fd3d71cf7c1369ccb8eed7e8955fa1413fcae4ffa71813456b7c6fd0dcb3" origin="Verified" reason="Artifact is not signed"/>
+         </artifact>
+         <artifact name="android-studio-2022.3.1.8.mac_arm.zip">
+            <sha256 value="a32c50e7b1aad29cf1ebfef50dfbb1ce5847ca9cf1a45c6a13b9763b76e66293" origin="Verified" reason="Artifact is not signed"/>
+         </artifact>
+         <artifact name="android-studio-2022.3.1.8.windows.zip">
+            <sha256 value="c44e13ea1f3b09c9d3d0a1763435dc59129ff2c8518a943c256ce9fde1473896" origin="Verified" reason="Artifact is not signed"/>
          </artifact>
       </component>
       <component group="antlr" name="antlr" version="2.7.7">
@@ -509,6 +522,11 @@
             <pgp value="fc2c31fc25fede4e7ad0d18c2bfd7825a8984fbe"/>
          </artifact>
       </component>
+      <component group="com.github.javaparser" name="javaparser-core" version="3.25.1">
+         <artifact name="javaparser-core-3.25.1.jar">
+            <pgp value="fc2c31fc25fede4e7ad0d18c2bfd7825a8984fbe"/>
+         </artifact>
+      </component>
       <component group="com.github.siom79.japicmp" name="japicmp" version="0.15.7">
          <artifact name="japicmp-0.15.7.jar">
             <pgp value="fffd810fffde203d5fa27263beabcfbee059e4e5"/>
@@ -572,6 +590,16 @@
             <pgp value="3a82f1b557594a6194be271d0e621275cdadb760"/>
          </artifact>
       </component>
+      <component group="com.google.testing.compile" name="compile-testing" version="0.21.0">
+         <artifact name="compile-testing-0.21.0.jar">
+            <pgp value="bdb5fa4fe719d787fb3d3197f6d4a1d411e9d1ae"/>
+         </artifact>
+      </component>
+      <component group="com.google.truth" name="truth" version="1.1.3">
+         <artifact name="truth-1.1.3.jar">
+            <pgp value="41cd49b4ef5876f9e9f691dabac30622339994c4"/>
+         </artifact>
+      </component>
       <component group="com.googlecode.concurrent-trees" name="concurrent-trees" version="2.6.1">
          <artifact name="concurrent-trees-2.6.1.jar">
             <pgp value="a9789342f598ad5b1175ef357eb97d110dfadd60"/>
@@ -612,6 +640,11 @@
             <pgp value="0cfa413799e2464c7d7e26220a4b343f2a55fdae"/>
          </artifact>
       </component>
+      <component group="com.h2database" name="h2" version="2.1.214">
+         <artifact name="h2-2.1.214.jar">
+            <pgp value="0cfa413799e2464c7d7e26220a4b343f2a55fdae"/>
+         </artifact>
+      </component>
       <component group="com.ibm.icu" name="icu4j" version="59.1">
          <artifact name="icu4j-59.1.jar">
             <pgp value="cb3190ca7842439e57f3712e44ce7bf2825ea2cd"/>
@@ -642,6 +675,11 @@
             <pgp value="5dceb296690bd5101756f2441f8cf885d537a431"/>
          </artifact>
       </component>
+      <component group="com.squareup" name="javapoet" version="1.13.0">
+         <artifact name="javapoet-1.13.0.jar">
+            <pgp value="1d0a8b5e77c678a7c724445abf984b4145ea13f7"/>
+         </artifact>
+      </component>
       <component group="com.squareup" name="javawriter" version="2.5.0">
          <artifact name="javawriter-2.5.0.jar">
             <pgp value="90ee19787a7bcf6fd37a1e9180c08b1c29100955"/>
@@ -682,11 +720,6 @@
             <pgp value="6d98490c6f1acddd448e45954f77679369475baa"/>
          </artifact>
       </component>
-      <component group="com.zaxxer" name="HikariCP" version="4.0.2">
-         <artifact name="HikariCP-4.0.2.jar">
-            <pgp value="f3a90e6b10e809f851ab4fc54cc08e7f47c3ec76"/>
-         </artifact>
-      </component>
       <component group="commons-codec" name="commons-codec" version="1.15">
          <artifact name="commons-codec-1.15.jar">
             <pgp value="bc87a3fd0a54480f0badbebd21939ff0ca2a6567"/>
@@ -733,6 +766,11 @@
             </sha256>
          </artifact>
       </component>
+      <component group="dev.adamko.dokkatoo" name="dokkatoo-plugin" version="1.3.0">
+         <artifact name="dokkatoo-plugin-1.3.0.jar">
+            <sha256 value="70ba2b912d823dd511be2ed6a033e86b0e62faef9f17d09eb5c50d96542a4254" origin="Verified" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
       <component group="flot" name="flot" version="0.8.1">
          <artifact name="flot-0.8.1-min.js">
             <sha256 value="889e1e07300265211222ad70b0644b9af0954eb2f9eaf06544c0e436967f434f" origin="Verified" reason="Artifact is not signed"/>
@@ -1149,6 +1187,11 @@
             <pgp value="f254b35617dc255d9344bcfa873a8e86b4372146"/>
          </artifact>
       </component>
+      <component group="org.codehaus.woodstox" name="stax2-api" version="4.2.1">
+         <artifact name="stax2-api-4.2.1.jar">
+            <pgp value="6214760097dc5cfad0175ac2c9fbaa83a8753994"/>
+         </artifact>
+      </component>
       <component group="org.ec4j.core" name="ec4j-core" version="0.2.0">
          <artifact name="ec4j-core-0.2.0.jar">
             <pgp value="2e92113263fc31c74ccbaab20e91c2de43b72bb1"/>
@@ -1190,6 +1233,11 @@
             <pgp value="7c669810892cbd3148fa92995b05ccde140c2876"/>
          </artifact>
       </component>
+      <component group="org.freemarker" name="freemarker" version="2.3.31">
+         <artifact name="freemarker-2.3.31.jar">
+            <pgp value="13ac2213964abe1d1c147c0e1939a2520bab1d90"/>
+         </artifact>
+      </component>
       <component group="org.fusesource.jansi" name="jansi" version="1.18">
          <artifact name="jansi-1.18.jar">
             <pgp value="ea23db1360d9029481e7f2efecdfea3cb4493b94"/>
@@ -1205,9 +1253,9 @@
             <sha256 value="843245599385e906a4133fed1765c4d9f2e697b0091bb681cffd71dc426ff76b" origin="Verified" reason="Artifact is not signed"/>
          </artifact>
       </component>
-      <component group="org.gradle" name="test-retry-gradle-plugin" version="1.5.0">
-         <artifact name="test-retry-gradle-plugin-1.5.0.jar">
-            <sha256 value="8fdac2ede2237b381b2bd99e735b93d648bc580e1115ee95ced673f6a1c4bbcd" origin="Verified" reason="Artifact is not signed"/>
+      <component group="org.gradle" name="test-retry-gradle-plugin" version="1.5.2">
+         <artifact name="test-retry-gradle-plugin-1.5.2.jar">
+            <sha256 value="9d83780b9df3d7d704c7f9c0feed2bfb5a295833392a666250db7f4cf91bda5a" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
       <component group="org.gradle.ci.health" name="common" version="0.63">
@@ -1265,9 +1313,14 @@
             <sha256 value="b4182982109d85f9a623fa597932c3f09651467559bb99965c41d45159e89a68" origin="Verified" reason="Artifact is not signed"/>
          </artifact>
       </component>
-      <component group="org.gradle.kotlin" name="gradle-kotlin-dsl-plugins" version="4.0.7">
-         <artifact name="gradle-kotlin-dsl-plugins-4.0.7.jar">
-            <sha256 value="7700af78c35bdd8c164e3f3a3026a568c66cf9dbd182a1756e087b2dfa3b02fb" origin="Verified" reason="Artifact is not signed"/>
+      <component group="org.gradle.kotlin" name="gradle-kotlin-dsl-plugins" version="4.0.11">
+         <artifact name="gradle-kotlin-dsl-plugins-4.0.11.jar">
+            <sha256 value="64ab98090c9cde6968eb7195d707529638722d0b8ad8bac2c701926049233bff" origin="Verified" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
+      <component group="org.gradle.kotlin" name="gradle-kotlin-dsl-plugins" version="4.0.14">
+         <artifact name="gradle-kotlin-dsl-plugins-4.0.14.jar">
+            <sha256 value="8bde305e1b349f7d1fa3d2a9e6fba8fda08571306b70bc9858464db15e6bbce7" origin="Verified" reason="Artifact is not signed"/>
          </artifact>
       </component>
       <component group="org.gradle.kotlin" name="plugins" version="1.3.6">
@@ -1295,6 +1348,11 @@
             <pgp value="33fd4bfd33554634053d73c0c2148900bcd3c2af"/>
          </artifact>
       </component>
+      <component group="org.jetbrains" name="markdown-jvm" version="0.3.1">
+         <artifact name="markdown-jvm-0.3.1.jar">
+            <pgp value="e62231331bca7e1f292c9b88c1b12a5d99c0729d"/>
+         </artifact>
+      </component>
       <component group="org.jetbrains.kotlin" name="kotlin-scripting-compiler-impl-embeddable" version="1.6.21">
          <artifact name="kotlin-scripting-compiler-impl-embeddable-1.6.21.jar">
             <sha256 value="e4044b8de01e84633923706f43cda80d37d8bbef8ce9a866f9d95962ddd20480" reason="Artifact is not signed"/>
@@ -1310,6 +1368,11 @@
             <sha256 value="4ec93d0516066b0b648ba987c6d3d65f6fb067dc0bbba95c1ece61e0428488e5" reason="Artifact is not signed"/>
          </artifact>
       </component>
+      <component group="org.jetbrains.kotlinx" name="kotlinx-html-jvm" version="0.8.0">
+         <artifact name="kotlinx-html-jvm-0.8.0.jar">
+            <pgp value="8e3a02905a1ae67e7b0f9acd3967d4eda591b991"/>
+         </artifact>
+      </component>
       <component group="org.jlleitschuh.gradle" name="ktlint-gradle" version="10.3.0">
          <artifact name="ktlint-gradle-10.3.0.jar">
             <sha256 value="6923398b3a77a1fb3722aacbcdde6ab0527cfcc98eb3fef6974074f96ae7fe76" origin="Verified" reason="Artifact is not signed"/>
@@ -1439,11 +1502,6 @@
             <sha256 value="003785669f921aafe4f137468dd20a01a36111e94fd7449f26c16e7924d82d23" origin="Verified" reason="Artifact is not signed"/>
          </artifact>
       </component>
-      <component group="org.yaml" name="snakeyaml" version="1.32">
-         <artifact name="snakeyaml-1.32.jar">
-            <pgp value="120d6f34e627ed3a772ebbfe55c7e5e701832382"/>
-         </artifact>
-      </component>
       <component group="org.ysb33r.gradle" name="grolifant" version="0.16.1">
          <artifact name="grolifant-0.16.1.jar">
             <pgp value="ea022560a81e5bd48db3d18b54ac8e2d98cfeac6"/>
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index c1962a7..8409aab 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4d7f74d..fb9dfe5 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-8.1-20230204100855+0000-bin.zip
+distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-8.3-20230511225831+0000-bin.zip
 networkTimeout=10000
+validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 5580fce..ae5eddb 100755
--- a/gradlew
+++ b/gradlew
@@ -85,9 +85,6 @@
 APP_BASE_NAME=${0##*/}
 APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
 
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"'
-
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
 
@@ -133,10 +130,13 @@
     fi
 else
     JAVACMD=java
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
+    fi
 fi
 
 # Increase the maximum file descriptors if we can.
@@ -197,6 +197,10 @@
     done
 fi
 
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"'
+
 # Collect all arguments for the java command;
 #   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
 #     shell script including quotes and variable substitutions, so put them in
diff --git a/released-versions.json b/released-versions.json
index 7149bd6..bae1160 100644
--- a/released-versions.json
+++ b/released-versions.json
@@ -1,14 +1,26 @@
 {
   "latestReleaseSnapshot": {
-    "version": "8.0.2-20230223092022+0000",
-    "buildTime": "20230223092022+0000"
+    "version": "8.2-20230517231707+0000",
+    "buildTime": "20230517231707+0000"
   },
   "latestRc": {
-    "version": "8.0-rc-5",
-    "buildTime": "20230209233943+0000"
+    "version": "8.1-rc-4",
+    "buildTime": "20230411093751+0000"
   },
   "finalReleases": [
     {
+      "version": "8.1.1",
+      "buildTime": "20230421123126+0000"
+    },
+    {
+      "version": "8.1",
+      "buildTime": "20230412120745+0000"
+    },
+    {
+      "version": "8.0.2",
+      "buildTime": "20230303164137+0000"
+    },
+    {
       "version": "8.0.1",
       "buildTime": "20230217200948+0000"
     },
@@ -17,6 +29,10 @@
       "buildTime": "20230213131521+0000"
     },
     {
+      "version": "7.6.1",
+      "buildTime": "20230224135442+0000"
+    },
+    {
       "version": "7.6",
       "buildTime": "20221125133510+0000"
     },
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 4304043..e7d2ae5 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,7 +1,6 @@
 import org.gradle.api.internal.FeaturePreviews
 
 pluginManagement {
-    includeBuild("build-logic-settings")
     repositories {
         maven {
             url = uri("https://repo.gradle.org/gradle/enterprise-libs-release-candidates")
@@ -24,9 +23,8 @@
 }
 
 plugins {
-    id("com.gradle.enterprise").version("3.12.3") // Sync with `build-logic/build-platform/build.gradle.kts`
+    id("com.gradle.enterprise").version("3.13.2") // Sync with `build-logic/build-platform/build.gradle.kts`
     id("io.github.gradle.gradle-enterprise-conventions-plugin").version("0.7.6")
-    id("gradlebuild.internal.cc-experiment")
     id("org.gradle.toolchains.foojay-resolver-convention") version("0.4.0")
 }
 
@@ -146,11 +144,14 @@
 include("build-operations")
 include("problems")
 include("instrumentation-agent")
+include("instrumentation-declarations")
 
 // Plugin portal projects
 include("kotlin-dsl-plugins")
 
 // Internal utility and verification projects
+include("internal-instrumentation-api")
+include("internal-instrumentation-processor")
 include("docs")
 include("docs-asciidoctor-extensions-base")
 include("docs-asciidoctor-extensions")
diff --git a/subprojects/antlr/src/main/java/org/gradle/api/plugins/antlr/AntlrPlugin.java b/subprojects/antlr/src/main/java/org/gradle/api/plugins/antlr/AntlrPlugin.java
index 5a6fb3e..145031e 100644
--- a/subprojects/antlr/src/main/java/org/gradle/api/plugins/antlr/AntlrPlugin.java
+++ b/subprojects/antlr/src/main/java/org/gradle/api/plugins/antlr/AntlrPlugin.java
@@ -21,6 +21,7 @@
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.DefaultSourceSet;
 import org.gradle.api.internal.tasks.JvmConstants;
 import org.gradle.api.model.ObjectFactory;
@@ -54,10 +55,8 @@ public void apply(final Project project) {
 
         // set up a configuration named 'antlr' for the user to specify the antlr libs to use in case
         // they want a specific version etc.
-        final Configuration antlrConfiguration = project.getConfigurations().create(ANTLR_CONFIGURATION_NAME)
+        final Configuration antlrConfiguration = ((ProjectInternal) project).getConfigurations().resolvableBucket(ANTLR_CONFIGURATION_NAME)
             .setVisible(false);
-        antlrConfiguration.setCanBeResolved(true);
-        antlrConfiguration.setCanBeConsumed(false);
 
         antlrConfiguration.defaultDependencies(dependencies -> dependencies.add(project.getDependencies().create("antlr:antlr:2.7.7@jar")));
 
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 5fc9c7b..4017e10 100644
--- a/subprojects/architecture-test/src/changes/accepted-public-api-changes.json
+++ b/subprojects/architecture-test/src/changes/accepted-public-api-changes.json
@@ -1,164 +1,3 @@
 {
-    "acceptedApiChanges": [
-        {
-            "type": "org.gradle.api.plugins.quality.CodeNarc",
-            "member": "Method org.gradle.api.plugins.quality.CodeNarc.getAntBuilder()",
-            "acceptation": "Removing internal only type exposed on the public api for injection",
-            "changes": [
-                "Method has been removed"
-            ]
-        },
-        {
-            "type": "org.gradle.api.plugins.quality.CodeNarc",
-            "member": "Method org.gradle.api.plugins.quality.CodeNarc.getObjectFactory()",
-            "acceptation": "Injected service method is now abstract",
-            "changes": [
-                "Method is now abstract"
-            ]
-        },
-        {
-            "type": "org.gradle.api.tasks.testing.Test",
-            "member": "Method org.gradle.api.tasks.testing.Test.setForkEvery(long)",
-            "acceptation": "Test.forkEvery setter with same nullability as the getter",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.KotlinAssignmentExtensionsKt",
-            "member": "Method org.gradle.kotlin.dsl.KotlinAssignmentExtensionsKt.assign(org.gradle.api.file.FileSystemLocationProperty,java.io.File)",
-            "acceptation": "Compatibility check has an issue with generic extension function",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.KotlinAssignmentExtensionsKt",
-            "member": "Method org.gradle.kotlin.dsl.KotlinAssignmentExtensionsKt.assign(org.gradle.api.provider.Property,java.lang.Object)",
-            "acceptation": "Compatibility check has an issue with generic extension function",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.KotlinSettingsScriptKt",
-            "member": "Class org.gradle.kotlin.dsl.KotlinSettingsScriptKt",
-            "acceptation": "Kotlin DSL standalone scripts now use implicit receivers and are source compatible",
-            "changes": [
-                "Class has been removed"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Constructor org.gradle.kotlin.dsl.ScriptHandlerScope(org.gradle.api.initialization.dsl.ScriptHandler,kotlin.jvm.internal.DefaultConstructorMarker)",
-            "acceptation": "Simplify usage of ScriptHandlerScope",
-            "changes": [
-                "Constructor has been removed"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Field Companion",
-            "acceptation": "Simplify usage of ScriptHandlerScope",
-            "changes": [
-                "Field has been removed"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.access$getDelegate$p(org.gradle.kotlin.dsl.ScriptHandlerScope)",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.dependencies(groovy.lang.Closure)",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.dependencyLocking(groovy.lang.Closure)",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.getClassLoader()",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.getConfigurations()",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.getDependencies()",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.getDependencyLocking()",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.getRepositories()",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.getSourceFile()",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.getSourceURI()",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Method org.gradle.kotlin.dsl.ScriptHandlerScope.repositories(groovy.lang.Closure)",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Method added to public class"
-            ]
-        },
-        {
-            "type": "org.gradle.kotlin.dsl.ScriptHandlerScope",
-            "member": "Superclass [removed]",
-            "acceptation": "Removed usage of ScriptHandlerDelegate, now ScriptHandlerScope : ScriptHandler",
-            "changes": [
-                "Superclass has been removed"
-            ]
-        }
-    ]
-}
+    "acceptedApiChanges": []
+}
\ No newline at end of file
diff --git a/subprojects/architecture-test/src/changes/archunit_store/internal-api-nullability.txt b/subprojects/architecture-test/src/changes/archunit_store/internal-api-nullability.txt
new file mode 100644
index 0000000..d55fa3e
--- /dev/null
+++ b/subprojects/architecture-test/src/changes/archunit_store/internal-api-nullability.txt
@@ -0,0 +1,6516 @@
+Class <org.gradle.api.distribution.internal.DefaultDistribution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDistribution.java:0)
+Class <org.gradle.api.distribution.internal.DefaultDistributionContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDistributionContainer.java:0)
+Class <org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentIdentifierFactory.java:0)
+Class <org.gradle.api.internal.artifacts.component.DefaultComponentIdentifierFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentIdentifierFactory.java:0)
+Class <org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachePolicy.java:0)
+Class <org.gradle.api.internal.artifacts.configurations.dynamicversion.Expiry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Expiry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.clientmodule.ClientModuleResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientModuleResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.clientmodule.ClientModuleResolver$ClientModuleComponentResolveMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientModuleResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.clientmodule.ClientModuleResolver$ClientModuleConfigurationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientModuleResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.clientmodule.ClientModuleResolver$ClientModuleDependencyMetadataWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientModuleResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.AbstractModuleDescriptorParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.BuildableIvyArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableIvyArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DefaultParseResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MetaDataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DescriptorParseContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DescriptorParseContext.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DisconnectedDescriptorParseContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DisconnectedDescriptorParseContext.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DisconnectedIvyXmlModuleDescriptorParser$DisconnectedParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DisconnectedIvyXmlModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.DisconnectedIvyXmlModuleDescriptorParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DisconnectedIvyXmlModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradleModuleMetadataParser$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleModuleMetadataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradleModuleMetadataParser$ModuleDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleModuleMetadataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradleModuleMetadataParser$ModuleDependencyConstraint> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleModuleMetadataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradleModuleMetadataParser$ModuleFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleModuleMetadataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradleModuleMetadataParser$VariantCapability> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleModuleMetadataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradleModuleMetadataParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleModuleMetadataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradlePomModuleDescriptorBuilder$JarDependencyType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlePomModuleDescriptorBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradlePomModuleDescriptorBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlePomModuleDescriptorBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradlePomModuleDescriptorParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlePomModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyModuleDescriptorConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyModuleDescriptorConverter.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyModuleResolveMetaDataBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyModuleResolveMetaDataBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyXmlModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser$AbstractParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyXmlModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser$Parser$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyXmlModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser$Parser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyXmlModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser$ParserHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyXmlModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.IvyXmlModuleDescriptorParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyXmlModuleDescriptorParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MavenVersionUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenVersionUtils.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParseException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MetaDataParseException.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser$ParseResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MetaDataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MetaDataParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomDomParser$AddDTDFilterInputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomDomParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomDomParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomDomParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomParent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomParent.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomReader.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader$GavProperty> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomReader.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader$PomDependencyData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomReader.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader$PomDependencyMgtElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomReader.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader$PomProfileElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomReader.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomReader.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.RootPomParent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RootPomParent.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.UnresolvedDependencyVersionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnresolvedDependencyVersionException.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.MavenDependencyKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenDependencyKey.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomDependencyMgt.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomProfile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomProfile.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.AbstractStringVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractStringVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.AbstractVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.AbstractVersionVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractVersionVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.CachingVersionSelectorScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingVersionSelectorScheme.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.CompositeVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionComparator.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionSelectorScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionSelectorScheme.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ExactVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExactVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.InverseVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InverseVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.LatestVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LatestVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.MavenVersionSelectorScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenVersionSelectorScheme.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.StaticVersionComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StaticVersionComparator.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.SubVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SubVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.UnionVersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnionVersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.Version> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Version.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionComparator.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser$DefaultVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionParser.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionRangeSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionRangeSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionSelector.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionSelectorScheme.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.AbstractModuleMetadataCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.DefaultCachedMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedMetadata.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.FileStoreAndIndexProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileStoreAndIndexProvider.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.InMemoryModuleMetadataCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.MissingModuleCacheEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MissingModuleCacheEntry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleComponentAtRepositoryKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleComponentAtRepositoryKey.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleComponentResolveMetadataSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleComponentResolveMetadataSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataCache$CachedMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataCacheEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataCacheEntry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataCacheEntrySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataCacheEntrySerializer.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataSerializer$Reader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataSerializer$Writer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleMetadataStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataStore.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleRepositoryCacheProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleRepositoryCacheProvider.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleRepositoryCaches> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleRepositoryCaches.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ModuleSourcesSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleSourcesSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.PersistentModuleMetadataCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.PersistentModuleMetadataCache$RevisionKeySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.PersistentModuleMetadataCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ReadOnlyModuleMetadataCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReadOnlyModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ResolvedArtifactCaches$ResolvedArtifactCacheProvidingModuleComponentRepository> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvedArtifactCaches.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.ResolvedArtifactCaches> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvedArtifactCaches.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.StringDeduplicatingDecoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringDeduplicatingDecoder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.SuppliedComponentMetadataSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SuppliedComponentMetadataSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.TwoStageModuleMetadataCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageModuleMetadataCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.AbstractArtifactsCache$ModuleArtifactsCacheEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.AbstractArtifactsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.ArtifactAtRepositoryKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactAtRepositoryKey.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.ArtifactsAtRepositoryKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactsAtRepositoryKey.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.CachedArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.CachedArtifacts> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedArtifacts.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultCachedArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultCachedArtifacts> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedArtifacts.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultModuleArtifactCache$ArtifactAtRepositoryKeySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleArtifactCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultModuleArtifactCache$CachedArtifactSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleArtifactCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultModuleArtifactCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleArtifactCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultModuleArtifactsCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultModuleArtifactsCache$ModuleArtifactsCacheEntrySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultModuleArtifactsCache$ModuleArtifactsKeySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.DefaultModuleArtifactsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.InMemoryModuleArtifactCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryModuleArtifactCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.InMemoryModuleArtifactsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryModuleArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.ModuleArtifactCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleArtifactCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.ModuleArtifactsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.ReadOnlyModuleArtifactCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReadOnlyModuleArtifactCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.ReadOnlyModuleArtifactsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReadOnlyModuleArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.TwoStageArtifactsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageArtifactsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.artifacts.TwoStageModuleArtifactCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageModuleArtifactCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.AbstractModuleVersionsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.DefaultCachedModuleVersionList> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedModuleVersionList.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.DefaultModuleVersionsCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.DefaultModuleVersionsCache$ModuleKeySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.DefaultModuleVersionsCache$ModuleVersionsCacheEntrySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.DefaultModuleVersionsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.DefaultResolvedModuleVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResolvedModuleVersion.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.InMemoryModuleVersionsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.ModuleAtRepositoryKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleAtRepositoryKey.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.ModuleVersionsCache$CachedModuleVersionList> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.ModuleVersionsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.ModuleVersionsCacheEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleVersionsCacheEntry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.ReadOnlyModuleVersionsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReadOnlyModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.TwoStageModuleVersionsCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageModuleVersionsCache.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRootComponentMetadataBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder$Factory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRootComponentMetadataBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder$MetadataHolder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRootComponentMetadataBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRootComponentMetadataBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.moduleconverter.RootComponentMetadataBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RootComponentMetadataBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultLocalComponentRegistry$MetadataSupplier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocalComponentRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultLocalComponentRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocalComponentRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectDependencyPublicationResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectDependencyPublicationResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectLocalComponentProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectLocalComponentProvider.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectPublication.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublicationRegistry$ReferenceImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectPublicationRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublicationRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectPublicationRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.LocalComponentInAnotherBuildProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalComponentInAnotherBuildProvider.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.LocalComponentProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalComponentProvider.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.LocalComponentRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalComponentRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectArtifactResolver$ResolvingCalculator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectArtifactResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectArtifactResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectArtifactResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectComponentPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectComponentPublication.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyPublicationResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectDependencyPublicationResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectDependencyResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectPublication.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry$Reference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectPublicationRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectPublicationRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectPublicationRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleExclusions.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.PatternMatchers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatchers.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.CachingExcludeFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.CachingExcludeFactory$ConcurrentCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.CachingExcludeFactory$ExcludePair> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.CachingExcludeFactory$ExcludesKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.CachingExcludeFactory$MergeCaches> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.CachingExcludeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.DelegatingExcludeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DelegatingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.ExcludeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.Intersections> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Intersections.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.LoggingExcludeFactory$Subject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.LoggingExcludeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.NormalizingExcludeFactory$FlattenOperationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NormalizingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.NormalizingExcludeFactory$UnionOf> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NormalizingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.NormalizingExcludeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NormalizingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.Optimizations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Optimizations.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.OptimizingExcludeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptimizingExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.factories.Unions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Unions.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultCompositeExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCompositeExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeAllOf> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExcludeAllOf.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeAnyOf> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExcludeAnyOf.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeEverything> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExcludeEverything.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExcludeFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultExcludeNothing> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExcludeNothing.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultGroupExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGroupExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultGroupSetExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGroupSetExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultIvyPatternMatcherExcludeRuleSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyPatternMatcherExcludeRuleSpec.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultModuleExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultModuleIdExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleIdExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultModuleIdSetExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleIdSetExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.DefaultModuleSetExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleSetExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.simple.ExcludeJsonHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExcludeJsonHelper.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ArtifactExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.CompositeExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAllOf> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExcludeAllOf.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeAnyOf> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExcludeAnyOf.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeEverything> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExcludeEverything.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeNothing> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExcludeNothing.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExcludeSpec.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroupExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.GroupSetExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroupSetExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.IvyPatternMatcherExcludeRuleSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyPatternMatcherExcludeRuleSpec.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleIdExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleIdSetExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleIdSetExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ModuleSetExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleSetExclude.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ComponentStateFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentStateFactory.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.ResolvableSelectorState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvableSelectorState.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SelectorStateResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolver$DescendingResolveResultComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SelectorStateResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SelectorStateResolver.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolverResults$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SelectorStateResolverResults.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolverResults$Registration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SelectorStateResolverResults.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolverResults> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SelectorStateResolverResults.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.DefaultResolvedProjectConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResolvedProjectConfiguration.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedLocalComponentsResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvedLocalComponentsResult.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedLocalComponentsResultGraphVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvedLocalComponentsResultGraphVisitor.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvedProjectConfiguration.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.strict.StrictVersionConstraints$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StrictVersionConstraints.java:0)
+Class <org.gradle.api.internal.artifacts.ivyservice.resolveengine.strict.StrictVersionConstraints> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StrictVersionConstraints.java:0)
+Class <org.gradle.api.internal.artifacts.metadata.ComponentArtifactIdentifierSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentArtifactIdentifierSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.metadata.ComponentArtifactMetadataSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentArtifactMetadataSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.metadata.ComponentFileArtifactIdentifierSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentFileArtifactIdentifierSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.metadata.ModuleComponentFileArtifactIdentifierSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleComponentFileArtifactIdentifierSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.metadata.PublishArtifactLocalArtifactMetadataSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublishArtifactLocalArtifactMetadataSerializer.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.CannotLocateLocalMavenRepositoryException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CannotLocateLocalMavenRepositoryException.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.DefaultLocalMavenRepositoryLocator$CurrentSystemPropertyAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocalMavenRepositoryLocator.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.DefaultLocalMavenRepositoryLocator$SystemPropertyAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocalMavenRepositoryLocator.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.DefaultLocalMavenRepositoryLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocalMavenRepositoryLocator.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.DefaultMavenFileLocations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenFileLocations.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.DefaultMavenSettingsProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenSettingsProvider.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalMavenRepositoryLocator.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.MavenFileLocations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenFileLocations.java:0)
+Class <org.gradle.api.internal.artifacts.mvnsettings.MavenSettingsProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenSettingsProvider.java:0)
+Class <org.gradle.api.internal.artifacts.publish.AbstractPublishArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPublishArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchivePublishArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.publish.DecoratingPublishArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DecoratingPublishArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPublishArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.publish.ImmutablePublishArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImmutablePublishArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.FlatDirRepositoryDescriptor$Property> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FlatDirRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.FlatDirRepositoryDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FlatDirRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor$Property> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.MavenRepositoryDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.MavenRepositoryDescriptor$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.MavenRepositoryDescriptor$Property> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.MavenRepositoryDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor$Type> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.UrlRepositoryDescriptor$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UrlRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.UrlRepositoryDescriptor$Property> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UrlRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.descriptor.UrlRepositoryDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UrlRepositoryDescriptor.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.layout.AbstractRepositoryLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractRepositoryLayout.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.layout.DefaultIvyPatternRepositoryLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyPatternRepositoryLayout.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.layout.GradleRepositoryLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleRepositoryLayout.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.layout.IvyRepositoryLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyRepositoryLayout.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.layout.MavenRepositoryLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenRepositoryLayout.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.layout.ResolvedPattern> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvedPattern.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.maven.MavenMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenMetadata.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenMetadataLoader.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenMetadataLoader.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenMetadataLoader.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenMetadataLoader.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.maven.MavenVersionLister> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenVersionLister.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.transport.NetworkOperationBackOffAndRetry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NetworkOperationBackOffAndRetry.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.transport.NetworkingIssueVerifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NetworkingIssueVerifier.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RepositoryTransport.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RepositoryTransportFactory.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory$DefaultResourceConnectorSpecification> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RepositoryTransportFactory.java:0)
+Class <org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RepositoryTransportFactory.java:0)
+Class <org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactTypeRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.type.DefaultArtifactTypeContainer$DefaultArtifactTypeDefinition> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactTypeContainer.java:0)
+Class <org.gradle.api.internal.artifacts.type.DefaultArtifactTypeContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactTypeContainer.java:0)
+Class <org.gradle.api.internal.artifacts.type.DefaultArtifactTypeRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactTypeRegistry.java:0)
+Class <org.gradle.api.internal.artifacts.verification.exceptions.ComponentVerificationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentVerificationException.java:0)
+Class <org.gradle.api.internal.artifacts.verification.exceptions.DependencyVerificationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationException.java:0)
+Class <org.gradle.api.internal.artifacts.verification.exceptions.InvalidGpgKeyIdsException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidGpgKeyIdsException.java:0)
+Class <org.gradle.api.internal.artifacts.verification.model.ArtifactVerificationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactVerificationMetadata.java:0)
+Class <org.gradle.api.internal.artifacts.verification.model.Checksum> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Checksum.java:0)
+Class <org.gradle.api.internal.artifacts.verification.model.ChecksumKind> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChecksumKind.java:0)
+Class <org.gradle.api.internal.artifacts.verification.model.ComponentVerificationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentVerificationMetadata.java:0)
+Class <org.gradle.api.internal.artifacts.verification.model.IgnoredKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IgnoredKey.java:0)
+Class <org.gradle.api.internal.artifacts.verification.model.ImmutableArtifactVerificationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImmutableArtifactVerificationMetadata.java:0)
+Class <org.gradle.api.internal.artifacts.verification.model.ImmutableComponentVerificationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImmutableComponentVerificationMetadata.java:0)
+Class <org.gradle.api.internal.artifacts.verification.serializer.DependencyVerificationXmlTags> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationXmlTags.java:0)
+Class <org.gradle.api.internal.artifacts.verification.serializer.DependencyVerificationsXmlReader$VerifiersHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationsXmlReader.java:0)
+Class <org.gradle.api.internal.artifacts.verification.serializer.DependencyVerificationsXmlReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationsXmlReader.java:0)
+Class <org.gradle.api.internal.artifacts.verification.serializer.DependencyVerificationsXmlWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationsXmlWriter.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.BuildTreeDefinedKeys> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeDefinedKeys.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService$CacheEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService$FingerprintListCacheEntrySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService$FingerprintSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService$LookupPublicKeyResultBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService$PublicKeyRingCacheEntrySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildCachingKeyService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingKeyService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildSignatureVerificationService$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSignatureVerificationService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildSignatureVerificationService$CacheEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSignatureVerificationService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildSignatureVerificationService$CacheEntryBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSignatureVerificationService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildSignatureVerificationService$CacheEntrySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSignatureVerificationService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildSignatureVerificationService$CacheKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSignatureVerificationService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildSignatureVerificationService$CacheKeySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSignatureVerificationService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.CrossBuildSignatureVerificationService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSignatureVerificationService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.DefaultSignatureVerificationServiceFactory$DefaultSignatureVerificationService$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSignatureVerificationServiceFactory.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.DefaultSignatureVerificationServiceFactory$DefaultSignatureVerificationService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSignatureVerificationServiceFactory.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.DefaultSignatureVerificationServiceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSignatureVerificationServiceFactory.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.PublicKeySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicKeySerializer.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.SignatureVerificationResultBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignatureVerificationResultBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.SignatureVerificationService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignatureVerificationService.java:0)
+Class <org.gradle.api.internal.artifacts.verification.signatures.SignatureVerificationServiceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignatureVerificationServiceFactory.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.AbstractVerificationFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractVerificationFailure.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.ArtifactVerificationResultBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactVerificationResultBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.ChecksumVerificationFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChecksumVerificationFailure.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DeletedArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DeletedArtifact.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerificationConfiguration$TrustCoordinates> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationConfiguration.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerificationConfiguration$TrustedArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationConfiguration.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerificationConfiguration$TrustedKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationConfiguration.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerificationConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerificationConfiguration.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerifier$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerifier.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerifier$DefaultSignatureVerificationResultBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerifier.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerifier.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerifierBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerifierBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerifierBuilder$ArtifactVerificationBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerifierBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerifierBuilder$ChecksumBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerifierBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerifierBuilder$ComponentVerificationsBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerifierBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.DependencyVerifierBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyVerifierBuilder.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.MissingChecksums> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MissingChecksums.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.MissingSignature> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MissingSignature.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.OnlyIgnoredKeys> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OnlyIgnoredKeys.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.SignatureVerificationFailure$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignatureVerificationFailure.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.SignatureVerificationFailure$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignatureVerificationFailure.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.SignatureVerificationFailure$FailureKind> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignatureVerificationFailure.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.SignatureVerificationFailure$SignatureError> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignatureVerificationFailure.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.SignatureVerificationFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignatureVerificationFailure.java:0)
+Class <org.gradle.api.internal.artifacts.verification.verifier.VerificationFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VerificationFailure.java:0)
+Class <org.gradle.api.internal.cache.CacheConfigurationsInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CacheConfigurationsInternal.java:0)
+Class <org.gradle.api.internal.cache.CacheDirTagMarkingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CacheDirTagMarkingStrategy.java:0)
+Class <org.gradle.api.internal.cache.CacheDirUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CacheDirUtil.java:0)
+Class <org.gradle.api.internal.cache.CacheResourceConfigurationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CacheResourceConfigurationInternal.java:0)
+Class <org.gradle.api.internal.cache.CleanupInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CleanupInternal.java:0)
+Class <org.gradle.api.internal.cache.DefaultCacheCleanupStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCacheCleanupStrategy.java:0)
+Class <org.gradle.api.internal.cache.DefaultCacheConfigurations$ContextualErrorMessageProperty> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCacheConfigurations.java:0)
+Class <org.gradle.api.internal.cache.DefaultCacheConfigurations$DefaultCacheResourceConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCacheConfigurations.java:0)
+Class <org.gradle.api.internal.cache.DefaultCacheConfigurations$MustBeConfiguredCleanupFrequency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCacheConfigurations.java:0)
+Class <org.gradle.api.internal.cache.DefaultCacheConfigurations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCacheConfigurations.java:0)
+Class <org.gradle.api.internal.cache.DefaultCleanup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCleanup.java:0)
+Class <org.gradle.api.internal.cache.DefaultDecompressionCacheFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDecompressionCacheFactory.java:0)
+Class <org.gradle.api.internal.cache.NoMarkingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoMarkingStrategy.java:0)
+Class <org.gradle.api.internal.cache.StringInterner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringInterner.java:0)
+Class <org.gradle.api.internal.capabilities.CapabilitiesMetadataInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CapabilitiesMetadataInternal.java:0)
+Class <org.gradle.api.internal.capabilities.CapabilityInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CapabilityInternal.java:0)
+Class <org.gradle.api.internal.capabilities.ImmutableCapability> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImmutableCapability.java:0)
+Class <org.gradle.api.internal.capabilities.ShadowedCapability> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShadowedCapability.java:0)
+Class <org.gradle.api.internal.catalog.parser.DependenciesModelHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependenciesModelHelper.java:0)
+Class <org.gradle.api.internal.catalog.parser.StrictVersionParser$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StrictVersionParser.java:0)
+Class <org.gradle.api.internal.catalog.parser.StrictVersionParser$RichVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StrictVersionParser.java:0)
+Class <org.gradle.api.internal.catalog.parser.StrictVersionParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StrictVersionParser.java:0)
+Class <org.gradle.api.internal.catalog.parser.TomlCatalogFileParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TomlCatalogFileParser.java:0)
+Class <org.gradle.api.internal.catalog.problems.DefaultCatalogProblemBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCatalogProblemBuilder.java:0)
+Class <org.gradle.api.internal.catalog.problems.DefaultCatalogProblemBuilder$DocLink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCatalogProblemBuilder.java:0)
+Class <org.gradle.api.internal.catalog.problems.DefaultCatalogProblemBuilder$SimpleSolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCatalogProblemBuilder.java:0)
+Class <org.gradle.api.internal.catalog.problems.DefaultCatalogProblemBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCatalogProblemBuilder.java:0)
+Class <org.gradle.api.internal.catalog.problems.VersionCatalogProblem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionCatalogProblem.java:0)
+Class <org.gradle.api.internal.catalog.problems.VersionCatalogProblemBuilder$DescribedProblem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionCatalogProblemBuilder.java:0)
+Class <org.gradle.api.internal.catalog.problems.VersionCatalogProblemBuilder$DescribedProblemWithCause> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionCatalogProblemBuilder.java:0)
+Class <org.gradle.api.internal.catalog.problems.VersionCatalogProblemBuilder$ProblemWithId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionCatalogProblemBuilder.java:0)
+Class <org.gradle.api.internal.catalog.problems.VersionCatalogProblemBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionCatalogProblemBuilder.java:0)
+Class <org.gradle.api.internal.catalog.problems.VersionCatalogProblemId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionCatalogProblemId.java:0)
+Class <org.gradle.api.internal.changedetection.TaskExecutionMode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionMode.java:0)
+Class <org.gradle.api.internal.changedetection.TaskExecutionModeResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionModeResolver.java:0)
+Class <org.gradle.api.internal.classloading.ClassInfoCleaningGroovySystemLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassInfoCleaningGroovySystemLoader.java:0)
+Class <org.gradle.api.internal.classloading.CompositeGroovySystemLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeGroovySystemLoader.java:0)
+Class <org.gradle.api.internal.classloading.GroovySystemLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroovySystemLoader.java:0)
+Class <org.gradle.api.internal.classloading.GroovySystemLoaderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroovySystemLoaderFactory.java:0)
+Class <org.gradle.api.internal.classloading.NoOpGroovySystemLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoOpGroovySystemLoader.java:0)
+Class <org.gradle.api.internal.classloading.PreferenceCleaningGroovySystemLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PreferenceCleaningGroovySystemLoader.java:0)
+Class <org.gradle.api.internal.classpath.DefaultModuleRegistry$DefaultModule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleRegistry.java:0)
+Class <org.gradle.api.internal.classpath.DefaultModuleRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleRegistry.java:0)
+Class <org.gradle.api.internal.classpath.DefaultPluginModuleRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginModuleRegistry.java:0)
+Class <org.gradle.api.internal.classpath.EffectiveClassPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EffectiveClassPath.java:0)
+Class <org.gradle.api.internal.classpath.ManifestUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManifestUtil.java:0)
+Class <org.gradle.api.internal.classpath.Module> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Module.java:0)
+Class <org.gradle.api.internal.classpath.ModuleRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleRegistry.java:0)
+Class <org.gradle.api.internal.classpath.PluginModuleRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginModuleRegistry.java:0)
+Class <org.gradle.api.internal.classpath.UnknownModuleException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnknownModuleException.java:0)
+Class <org.gradle.api.internal.coerce.MethodArgumentsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodArgumentsTransformer.java:0)
+Class <org.gradle.api.internal.coerce.PropertySetTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertySetTransformer.java:0)
+Class <org.gradle.api.internal.coerce.StringToEnumTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringToEnumTransformer.java:0)
+Class <org.gradle.api.internal.collections.AbstractIterationOrderRetainingElementSource$Element> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractIterationOrderRetainingElementSource.java:0)
+Class <org.gradle.api.internal.collections.AbstractIterationOrderRetainingElementSource$RealizedElementCollectionIterator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractIterationOrderRetainingElementSource.java:0)
+Class <org.gradle.api.internal.collections.AbstractIterationOrderRetainingElementSource$ValuePointer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractIterationOrderRetainingElementSource.java:0)
+Class <org.gradle.api.internal.collections.AbstractIterationOrderRetainingElementSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractIterationOrderRetainingElementSource.java:0)
+Class <org.gradle.api.internal.collections.CollectionEventRegister> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionEventRegister.java:0)
+Class <org.gradle.api.internal.collections.CollectionFilter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionFilter.java:0)
+Class <org.gradle.api.internal.collections.CollectionFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionFilter.java:0)
+Class <org.gradle.api.internal.collections.DefaultCollectionEventRegister$FilteredEventRegister> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCollectionEventRegister.java:0)
+Class <org.gradle.api.internal.collections.DefaultCollectionEventRegister> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCollectionEventRegister.java:0)
+Class <org.gradle.api.internal.collections.DefaultDomainObjectCollectionFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDomainObjectCollectionFactory.java:0)
+Class <org.gradle.api.internal.collections.DefaultPendingSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPendingSource.java:0)
+Class <org.gradle.api.internal.collections.DomainObjectCollectionFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DomainObjectCollectionFactory.java:0)
+Class <org.gradle.api.internal.collections.ElementSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ElementSource.java:0)
+Class <org.gradle.api.internal.collections.FilteredElementSource$FilteringIterator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FilteredElementSource.java:0)
+Class <org.gradle.api.internal.collections.FilteredElementSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FilteredElementSource.java:0)
+Class <org.gradle.api.internal.collections.FilteredIndexedElementSource$FilteredListIterator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FilteredIndexedElementSource.java:0)
+Class <org.gradle.api.internal.collections.FilteredIndexedElementSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FilteredIndexedElementSource.java:0)
+Class <org.gradle.api.internal.collections.IndexedElementSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IndexedElementSource.java:0)
+Class <org.gradle.api.internal.collections.IterationOrderRetainingSetElementSource$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IterationOrderRetainingSetElementSource.java:0)
+Class <org.gradle.api.internal.collections.IterationOrderRetainingSetElementSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IterationOrderRetainingSetElementSource.java:0)
+Class <org.gradle.api.internal.collections.ListElementSource$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ListElementSource.java:0)
+Class <org.gradle.api.internal.collections.ListElementSource$RealizedElementListIterator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ListElementSource.java:0)
+Class <org.gradle.api.internal.collections.ListElementSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ListElementSource.java:0)
+Class <org.gradle.api.internal.collections.PendingSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PendingSource.java:0)
+Class <org.gradle.api.internal.collections.SortedSetElementSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SortedSetElementSource.java:0)
+Class <org.gradle.api.internal.component.AbstractSoftwareComponentVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractSoftwareComponentVariant.java:0)
+Class <org.gradle.api.internal.component.ArtifactType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactType.java:0)
+Class <org.gradle.api.internal.component.ComponentTypeRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentTypeRegistration.java:0)
+Class <org.gradle.api.internal.component.ComponentTypeRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentTypeRegistry.java:0)
+Class <org.gradle.api.internal.component.DefaultComponentTypeRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentTypeRegistry.java:0)
+Class <org.gradle.api.internal.component.DefaultComponentTypeRegistry$DefaultComponentTypeRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentTypeRegistry.java:0)
+Class <org.gradle.api.internal.component.DefaultComponentTypeRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentTypeRegistry.java:0)
+Class <org.gradle.api.internal.component.DefaultSoftwareComponentContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSoftwareComponentContainer.java:0)
+Class <org.gradle.api.internal.component.DefaultSoftwareComponentVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSoftwareComponentVariant.java:0)
+Class <org.gradle.api.internal.component.IvyPublishingAwareVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyPublishingAwareVariant.java:0)
+Class <org.gradle.api.internal.component.MavenPublishingAwareVariant$ScopeMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublishingAwareVariant.java:0)
+Class <org.gradle.api.internal.component.MavenPublishingAwareVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublishingAwareVariant.java:0)
+Class <org.gradle.api.internal.component.SoftwareComponentContainerInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SoftwareComponentContainerInternal.java:0)
+Class <org.gradle.api.internal.component.SoftwareComponentInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SoftwareComponentInternal.java:0)
+Class <org.gradle.api.internal.component.UsageContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UsageContext.java:0)
+Class <org.gradle.api.internal.composite.CompositeBuildContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildContext.java:0)
+Class <org.gradle.api.internal.credentials.CredentialListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CredentialListener.java:0)
+Class <org.gradle.api.internal.exceptions.MarkedVerificationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MarkedVerificationException.java:0)
+Class <org.gradle.api.internal.file.ant.AntFileResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntFileResource.java:0)
+Class <org.gradle.api.internal.file.ant.BaseDirSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseDirSelector.java:0)
+Class <org.gradle.api.internal.file.archive.compression.AbstractArchiver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractArchiver.java:0)
+Class <org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchiveOutputStreamFactory.java:0)
+Class <org.gradle.api.internal.file.archive.compression.Bzip2Archiver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Bzip2Archiver.java:0)
+Class <org.gradle.api.internal.file.archive.compression.CompressedReadableResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompressedReadableResource.java:0)
+Class <org.gradle.api.internal.file.archive.compression.GzipArchiver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GzipArchiver.java:0)
+Class <org.gradle.api.internal.file.archive.compression.SimpleCompressor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleCompressor.java:0)
+Class <org.gradle.api.internal.file.archive.compression.URIBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (URIBuilder.java:0)
+Class <org.gradle.api.internal.file.archive.impl.AbstractZipEntry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractZipEntry.java:0)
+Class <org.gradle.api.internal.file.archive.impl.AbstractZipEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractZipEntry.java:0)
+Class <org.gradle.api.internal.file.archive.impl.FileZipInput$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileZipInput.java:0)
+Class <org.gradle.api.internal.file.archive.impl.FileZipInput$FileZipEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileZipInput.java:0)
+Class <org.gradle.api.internal.file.archive.impl.FileZipInput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileZipInput.java:0)
+Class <org.gradle.api.internal.file.archive.impl.StreamZipInput$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamZipInput.java:0)
+Class <org.gradle.api.internal.file.archive.impl.StreamZipInput$StreamZipEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamZipInput.java:0)
+Class <org.gradle.api.internal.file.archive.impl.StreamZipInput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamZipInput.java:0)
+Class <org.gradle.api.internal.file.delete.DefaultDeleteSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDeleteSpec.java:0)
+Class <org.gradle.api.internal.file.delete.DeleteSpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DeleteSpecInternal.java:0)
+Class <org.gradle.api.internal.file.pattern.AnyWildcardPatternStep> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnyWildcardPatternStep.java:0)
+Class <org.gradle.api.internal.file.pattern.AnythingMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnythingMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.EndOfPathMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EndOfPathMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.FixedPatternStep> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FixedPatternStep.java:0)
+Class <org.gradle.api.internal.file.pattern.FixedStepPathMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FixedStepPathMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.GreedyPathMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GreedyPathMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.HasPrefixAndSuffixPatternStep> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HasPrefixAndSuffixPatternStep.java:0)
+Class <org.gradle.api.internal.file.pattern.HasPrefixPatternStep> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HasPrefixPatternStep.java:0)
+Class <org.gradle.api.internal.file.pattern.HasSuffixPatternStep> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HasSuffixPatternStep.java:0)
+Class <org.gradle.api.internal.file.pattern.PathMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PathMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternMatcher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternMatcher$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternMatcher$And> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternMatcher$Or> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatcher.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternMatcherFactory$DefaultPatternMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatcherFactory.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternMatcherFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatcherFactory.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternStep> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternStep.java:0)
+Class <org.gradle.api.internal.file.pattern.PatternStepFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternStepFactory.java:0)
+Class <org.gradle.api.internal.file.pattern.RegExpPatternStep> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RegExpPatternStep.java:0)
+Class <org.gradle.api.internal.file.temp.DefaultTemporaryFileProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTemporaryFileProvider.java:0)
+Class <org.gradle.api.internal.file.temp.DefaultTemporaryFileProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTemporaryFileProvider.java:0)
+Class <org.gradle.api.internal.file.temp.GradleUserHomeTemporaryFileProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleUserHomeTemporaryFileProvider.java:0)
+Class <org.gradle.api.internal.file.temp.GradleUserHomeTemporaryFileProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleUserHomeTemporaryFileProvider.java:0)
+Class <org.gradle.api.internal.file.temp.TempFiles> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TempFiles.java:0)
+Class <org.gradle.api.internal.file.temp.TemporaryFileProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemporaryFileProvider.java:0)
+Class <org.gradle.api.internal.filestore.ArtifactIdentifierFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactIdentifierFileStore.java:0)
+Class <org.gradle.api.internal.filestore.DefaultArtifactIdentifierFileStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactIdentifierFileStore.java:0)
+Class <org.gradle.api.internal.filestore.DefaultArtifactIdentifierFileStore$Factory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactIdentifierFileStore.java:0)
+Class <org.gradle.api.internal.filestore.DefaultArtifactIdentifierFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactIdentifierFileStore.java:0)
+Class <org.gradle.api.internal.filestore.TwoStageArtifactIdentifierFileStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageArtifactIdentifierFileStore.java:0)
+Class <org.gradle.api.internal.filestore.TwoStageArtifactIdentifierFileStore$DelegatingFileAccessTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageArtifactIdentifierFileStore.java:0)
+Class <org.gradle.api.internal.filestore.TwoStageArtifactIdentifierFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageArtifactIdentifierFileStore.java:0)
+Class <org.gradle.api.internal.java.DefaultJavaPlatformExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaPlatformExtension.java:0)
+Class <org.gradle.api.internal.java.WebApplication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WebApplication.java:0)
+Class <org.gradle.api.internal.java.usagecontext.ConfigurationVariantDetailsInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationVariantDetailsInternal.java:0)
+Class <org.gradle.api.internal.java.usagecontext.ConfigurationVariantMapping$DefaultConfigurationVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationVariantMapping.java:0)
+Class <org.gradle.api.internal.java.usagecontext.ConfigurationVariantMapping$DefaultConfigurationVariantDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationVariantMapping.java:0)
+Class <org.gradle.api.internal.java.usagecontext.ConfigurationVariantMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationVariantMapping.java:0)
+Class <org.gradle.api.internal.java.usagecontext.FeatureConfigurationVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureConfigurationVariant.java:0)
+Class <org.gradle.api.internal.jvm.ClassDirectoryBinaryNamingScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDirectoryBinaryNamingScheme.java:0)
+Class <org.gradle.api.internal.notations.ClientModuleNotationParserFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientModuleNotationParserFactory.java:0)
+Class <org.gradle.api.internal.notations.ComponentIdentifierParserFactory$ComponentIdentifierMapNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentIdentifierParserFactory.java:0)
+Class <org.gradle.api.internal.notations.ComponentIdentifierParserFactory$StringNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentIdentifierParserFactory.java:0)
+Class <org.gradle.api.internal.notations.ComponentIdentifierParserFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentIdentifierParserFactory.java:0)
+Class <org.gradle.api.internal.notations.DependencyClassPathNotationConverter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyClassPathNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.DependencyClassPathNotationConverter$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyClassPathNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.DependencyClassPathNotationConverter$GeneratedFileCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyClassPathNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.DependencyClassPathNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyClassPathNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.DependencyConstraintNotationParser$MinimalExternalDependencyNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyConstraintNotationParser.java:0)
+Class <org.gradle.api.internal.notations.DependencyConstraintNotationParser$ProjectDependencyNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyConstraintNotationParser.java:0)
+Class <org.gradle.api.internal.notations.DependencyConstraintNotationParser$UnsupportedCapabilitiesHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyConstraintNotationParser.java:0)
+Class <org.gradle.api.internal.notations.DependencyConstraintNotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyConstraintNotationParser.java:0)
+Class <org.gradle.api.internal.notations.DependencyConstraintProjectNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyConstraintProjectNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.DependencyFilesNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyFilesNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.DependencyMapNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyMapNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.DependencyMetadataNotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyMetadataNotationParser.java:0)
+Class <org.gradle.api.internal.notations.DependencyNotationParser$MinimalExternalDependencyNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyNotationParser.java:0)
+Class <org.gradle.api.internal.notations.DependencyNotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyNotationParser.java:0)
+Class <org.gradle.api.internal.notations.DependencyProjectNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyProjectNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.DependencyStringNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyStringNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.ModuleIdentifierNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleIdentifierNotationConverter.java:0)
+Class <org.gradle.api.internal.notations.ModuleNotationValidation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleNotationValidation.java:0)
+Class <org.gradle.api.internal.notations.ProjectDependencyFactory$ProjectDependencyMapNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectDependencyFactory.java:0)
+Class <org.gradle.api.internal.notations.ProjectDependencyFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectDependencyFactory.java:0)
+Class <org.gradle.api.internal.plugins.ApplyPluginBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApplyPluginBuildOperationType.java:0)
+Class <org.gradle.api.internal.plugins.ApplyPluginBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApplyPluginBuildOperationType.java:0)
+Class <org.gradle.api.internal.plugins.ApplyPluginBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApplyPluginBuildOperationType.java:0)
+Class <org.gradle.api.internal.plugins.BuildConfigurationRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildConfigurationRule.java:0)
+Class <org.gradle.api.internal.plugins.ClassloaderBackedPluginDescriptorLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassloaderBackedPluginDescriptorLocator.java:0)
+Class <org.gradle.api.internal.plugins.DefaultAppliedPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAppliedPlugin.java:0)
+Class <org.gradle.api.internal.plugins.DefaultArtifactPublicationSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactPublicationSet.java:0)
+Class <org.gradle.api.internal.plugins.DefaultArtifactPublicationSet$DefaultArtifactProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactPublicationSet.java:0)
+Class <org.gradle.api.internal.plugins.DefaultArtifactPublicationSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactPublicationSet.java:0)
+Class <org.gradle.api.internal.plugins.DefaultJavaAppStartScriptGenerationDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaAppStartScriptGenerationDetails.java:0)
+Class <org.gradle.api.internal.plugins.DefaultObjectConfigurationAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectConfigurationAction.java:0)
+Class <org.gradle.api.internal.plugins.DefaultObjectConfigurationAction$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectConfigurationAction.java:0)
+Class <org.gradle.api.internal.plugins.DefaultObjectConfigurationAction$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectConfigurationAction.java:0)
+Class <org.gradle.api.internal.plugins.DefaultObjectConfigurationAction$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectConfigurationAction.java:0)
+Class <org.gradle.api.internal.plugins.DefaultObjectConfigurationAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectConfigurationAction.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginCollection.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginContainer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginContainer.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginContainer$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginContainer.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginContainer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginContainer.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginContainer.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginManager$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManager.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginManager$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManager.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginManager$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManager.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManager.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginManager$OperationDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManager.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManager.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginRegistry.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginRegistry$PluginIdLookupCacheKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginRegistry.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginRegistry$PotentialPluginCacheLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginRegistry.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginRegistry$RegistryAwarePluginImplementation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginRegistry.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPluginRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginRegistry.java:0)
+Class <org.gradle.api.internal.plugins.DefaultPotentialPluginWithId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPotentialPluginWithId.java:0)
+Class <org.gradle.api.internal.plugins.DefaultTemplateBasedStartScriptGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTemplateBasedStartScriptGenerator.java:0)
+Class <org.gradle.api.internal.plugins.DefaultTemplateBasedStartScriptGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTemplateBasedStartScriptGenerator.java:0)
+Class <org.gradle.api.internal.plugins.DslObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DslObject.java:0)
+Class <org.gradle.api.internal.plugins.ExtensionContainerInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExtensionContainerInternal.java:0)
+Class <org.gradle.api.internal.plugins.GroovyJarFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroovyJarFile.java:0)
+Class <org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImperativeOnlyPluginTarget.java:0)
+Class <org.gradle.api.internal.plugins.PluginApplicationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginApplicationException.java:0)
+Class <org.gradle.api.internal.plugins.PluginAwareInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginAwareInternal.java:0)
+Class <org.gradle.api.internal.plugins.PluginDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginDescriptor.java:0)
+Class <org.gradle.api.internal.plugins.PluginDescriptorLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginDescriptorLocator.java:0)
+Class <org.gradle.api.internal.plugins.PluginImplementation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginImplementation.java:0)
+Class <org.gradle.api.internal.plugins.PluginInspector$PotentialHybridImperativeAndRulesPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginInspector.java:0)
+Class <org.gradle.api.internal.plugins.PluginInspector$PotentialImperativeClassPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginInspector.java:0)
+Class <org.gradle.api.internal.plugins.PluginInspector$PotentialPureRuleSourceClassPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginInspector.java:0)
+Class <org.gradle.api.internal.plugins.PluginInspector$PotentialUnknownTypePlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginInspector.java:0)
+Class <org.gradle.api.internal.plugins.PluginInspector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginInspector.java:0)
+Class <org.gradle.api.internal.plugins.PluginManagerInternal$PluginWithId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginManagerInternal.java:0)
+Class <org.gradle.api.internal.plugins.PluginManagerInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginManagerInternal.java:0)
+Class <org.gradle.api.internal.plugins.PluginRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginRegistry.java:0)
+Class <org.gradle.api.internal.plugins.PluginTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginTarget.java:0)
+Class <org.gradle.api.internal.plugins.PotentialPlugin$Type> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PotentialPlugin.java:0)
+Class <org.gradle.api.internal.plugins.PotentialPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PotentialPlugin.java:0)
+Class <org.gradle.api.internal.plugins.RuleBasedPluginTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBasedPluginTarget.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptGenerator.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptGenerator$DefaultUnixFileOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptGenerator.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptGenerator$Generate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptGenerator.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptGenerator$UnixFileOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptGenerator.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptGenerator.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptTemplateBindingFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptTemplateBindingFactory.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptTemplateBindingFactory$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptTemplateBindingFactory.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptTemplateBindingFactory$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptTemplateBindingFactory.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptTemplateBindingFactory$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptTemplateBindingFactory.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptTemplateBindingFactory$ScriptBindingParameter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptTemplateBindingFactory.java:0)
+Class <org.gradle.api.internal.plugins.StartScriptTemplateBindingFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartScriptTemplateBindingFactory.java:0)
+Class <org.gradle.api.internal.plugins.UnixStartScriptGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnixStartScriptGenerator.java:0)
+Class <org.gradle.api.internal.plugins.WindowsStartScriptGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsStartScriptGenerator.java:0)
+Class <org.gradle.api.internal.project.ant.AntLoggingAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntLoggingAdapter.java:0)
+Class <org.gradle.api.internal.project.ant.AntLoggingAdapterFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntLoggingAdapterFactory.java:0)
+Class <org.gradle.api.internal.project.ant.BasicAntBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BasicAntBuilder.java:0)
+Class <org.gradle.api.internal.project.ant.DefaultAntLoggingAdapterFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAntLoggingAdapterFactory.java:0)
+Class <org.gradle.api.internal.project.antbuilder.AntBuilderDelegate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntBuilderDelegate.java:0)
+Class <org.gradle.api.internal.project.antbuilder.CacheEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CacheEntry.java:0)
+Class <org.gradle.api.internal.project.antbuilder.CachedClassLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedClassLoader.java:0)
+Class <org.gradle.api.internal.project.antbuilder.ClassPathToClassLoaderCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassPathToClassLoaderCache.java:0)
+Class <org.gradle.api.internal.project.antbuilder.Cleanup$Mode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Cleanup.java:0)
+Class <org.gradle.api.internal.project.antbuilder.Cleanup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Cleanup.java:0)
+Class <org.gradle.api.internal.project.antbuilder.DefaultIsolatedAntBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIsolatedAntBuilder.java:0)
+Class <org.gradle.api.internal.project.antbuilder.FinalizerThread> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FinalizerThread.java:0)
+Class <org.gradle.api.internal.properties.GradleProperties> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProperties.java:0)
+Class <org.gradle.api.internal.provider.sources.AbstractPropertyValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPropertyValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.AbstractPropertyValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPropertyValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.EnvironmentVariableValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnvironmentVariableValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.EnvironmentVariableValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnvironmentVariableValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.EnvironmentVariablesPrefixedByValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnvironmentVariablesPrefixedByValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.EnvironmentVariablesPrefixedByValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnvironmentVariablesPrefixedByValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.FileBytesValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileBytesValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.FileContentValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileContentValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.FileContentValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileContentValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.FileTextValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileTextValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.GradlePropertiesPrefixedByValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlePropertiesPrefixedByValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.GradlePropertiesPrefixedByValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlePropertiesPrefixedByValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.GradlePropertyValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlePropertyValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.GradlePropertyValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlePropertyValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.MapWithPrefixedKeysValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MapWithPrefixedKeysValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.MapWithPrefixedKeysValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MapWithPrefixedKeysValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.SystemPropertiesPrefixedByValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPropertiesPrefixedByValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.SystemPropertiesPrefixedByValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPropertiesPrefixedByValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.SystemPropertyValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPropertyValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.SystemPropertyValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPropertyValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.process.DefaultExecOutput$DefaultStandardStreamContent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecOutput.java:0)
+Class <org.gradle.api.internal.provider.sources.process.DefaultExecOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecOutput.java:0)
+Class <org.gradle.api.internal.provider.sources.process.DelegatingBaseExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DelegatingBaseExecSpec.java:0)
+Class <org.gradle.api.internal.provider.sources.process.DelegatingExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DelegatingExecSpec.java:0)
+Class <org.gradle.api.internal.provider.sources.process.DelegatingJavaExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DelegatingJavaExecSpec.java:0)
+Class <org.gradle.api.internal.provider.sources.process.ExecSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecSpecFactory.java:0)
+Class <org.gradle.api.internal.provider.sources.process.ProcessOutputProviderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessOutputProviderFactory.java:0)
+Class <org.gradle.api.internal.provider.sources.process.ProcessOutputValueSource$ExecOutputData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessOutputValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.process.ProcessOutputValueSource$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessOutputValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.process.ProcessOutputValueSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessOutputValueSource.java:0)
+Class <org.gradle.api.internal.provider.sources.process.ProviderCompatibleBaseExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderCompatibleBaseExecSpec.java:0)
+Class <org.gradle.api.internal.provider.sources.process.ProviderCompatibleExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderCompatibleExecSpec.java:0)
+Class <org.gradle.api.internal.provider.sources.process.ProviderCompatibleJavaExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderCompatibleJavaExecSpec.java:0)
+Class <org.gradle.api.internal.provider.support.LazyGroovySupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LazyGroovySupport.java:0)
+Class <org.gradle.api.internal.resolve.DefaultLocalLibraryResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocalLibraryResolver.java:0)
+Class <org.gradle.api.internal.resolve.DefaultProjectModelResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectModelResolver.java:0)
+Class <org.gradle.api.internal.resolve.LibraryResolutionErrorMessageBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibraryResolutionErrorMessageBuilder.java:0)
+Class <org.gradle.api.internal.resolve.LibraryResolutionResult$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibraryResolutionResult.java:0)
+Class <org.gradle.api.internal.resolve.LibraryResolutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibraryResolutionResult.java:0)
+Class <org.gradle.api.internal.resolve.LocalLibraryMetaDataAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalLibraryMetaDataAdapter.java:0)
+Class <org.gradle.api.internal.resolve.LocalLibraryResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalLibraryResolver.java:0)
+Class <org.gradle.api.internal.resolve.ProjectModelResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectModelResolver.java:0)
+Class <org.gradle.api.internal.resolve.VariantBinarySelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VariantBinarySelector.java:0)
+Class <org.gradle.api.internal.rules.NamedDomainObjectFactoryRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NamedDomainObjectFactoryRegistry.java:0)
+Class <org.gradle.api.internal.runtimeshaded.ImplementationDependencyRelocator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImplementationDependencyRelocator.java:0)
+Class <org.gradle.api.internal.runtimeshaded.ImplementationDependencyRelocator$ClassLiteralRemapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImplementationDependencyRelocator.java:0)
+Class <org.gradle.api.internal.runtimeshaded.ImplementationDependencyRelocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImplementationDependencyRelocator.java:0)
+Class <org.gradle.api.internal.runtimeshaded.RuntimeShadedJarCreator$ShadingClassRemapper$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuntimeShadedJarCreator.java:0)
+Class <org.gradle.api.internal.runtimeshaded.RuntimeShadedJarCreator$ShadingClassRemapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuntimeShadedJarCreator.java:0)
+Class <org.gradle.api.internal.runtimeshaded.RuntimeShadedJarCreator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuntimeShadedJarCreator.java:0)
+Class <org.gradle.api.internal.runtimeshaded.RuntimeShadedJarFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuntimeShadedJarFactory.java:0)
+Class <org.gradle.api.internal.runtimeshaded.RuntimeShadedJarFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuntimeShadedJarFactory.java:0)
+Class <org.gradle.api.internal.runtimeshaded.RuntimeShadedJarType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuntimeShadedJarType.java:0)
+Class <org.gradle.api.internal.specs.ExplainingSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExplainingSpec.java:0)
+Class <org.gradle.api.internal.specs.ExplainingSpecs$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExplainingSpecs.java:0)
+Class <org.gradle.api.internal.specs.ExplainingSpecs$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExplainingSpecs.java:0)
+Class <org.gradle.api.internal.specs.ExplainingSpecs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExplainingSpecs.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.AbstractDaemonCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDaemonCompiler.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.AbstractIsolatedCompilerWorkerExecutor$CompilerWorkAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractIsolatedCompilerWorkerExecutor.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.AbstractIsolatedCompilerWorkerExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractIsolatedCompilerWorkerExecutor.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.ClassloaderIsolatedCompilerWorkerExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassloaderIsolatedCompilerWorkerExecutor.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.CompilerWorkerExecutor$CompilerParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerWorkerExecutor.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.CompilerWorkerExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerWorkerExecutor.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.DaemonGroovyCompiler$GroovyCompilerParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonGroovyCompiler.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.DaemonGroovyCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonGroovyCompiler.java:0)
+Class <org.gradle.api.internal.tasks.compile.daemon.ProcessIsolatedCompilerWorkerExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessIsolatedCompilerWorkerExecutor.java:0)
+Class <org.gradle.api.internal.tasks.compile.filter.AnnotationProcessorFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessorFilter.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.IncrementalCompilerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompilerFactory.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.IncrementalResultStoringCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalResultStoringCompiler.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.RecompilationNotNecessary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RecompilationNotNecessary.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.SelectiveCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SelectiveCompiler.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.analyzer.CachingClassDependenciesAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingClassDependenciesAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.analyzer.ClassDependenciesAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDependenciesAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.analyzer.DefaultClassDependenciesAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassDependenciesAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.asm.ClassDependenciesVisitor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDependenciesVisitor.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.asm.ClassDependenciesVisitor$AnnotationVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDependenciesVisitor.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.asm.ClassDependenciesVisitor$FieldVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDependenciesVisitor.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.asm.ClassDependenciesVisitor$MethodVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDependenciesVisitor.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.asm.ClassDependenciesVisitor$RetentionPolicyVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDependenciesVisitor.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.asm.ClassDependenciesVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDependenciesVisitor.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.asm.ClassRelevancyFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassRelevancyFilter.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.cache.GeneralCompileCaches> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GeneralCompileCaches.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.cache.UserHomeScopedCompileCaches> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserHomeScopedCompileCaches.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.classpath.CachingClassSetAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingClassSetAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.classpath.ClassSetAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassSetAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.classpath.DefaultClassSetAnalyzer$DirectoryEntryVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassSetAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.classpath.DefaultClassSetAnalyzer$EntryVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassSetAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.classpath.DefaultClassSetAnalyzer$JarEntryVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassSetAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.classpath.DefaultClassSetAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassSetAnalyzer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.CompilerApiData$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerApiData.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.CompilerApiData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerApiData.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.constants.ConstantToDependentsMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantToDependentsMapping.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.constants.ConstantToDependentsMappingBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantToDependentsMappingBuilder.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.constants.ConstantToDependentsMappingMerger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantToDependentsMappingMerger.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.constants.ConstantsAnalysisResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantsAnalysisResult.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.DependentSetSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentSetSerializer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.DependentsSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentsSet.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.DependentsSet$DefaultDependentsSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentsSet.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.DependentsSet$DependencyToAll> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentsSet.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.DependentsSet$EmptyDependentsSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentsSet.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.DependentsSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentsSet.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.GeneratedResource$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GeneratedResource.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.GeneratedResource$Location> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GeneratedResource.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.compilerapi.deps.GeneratedResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GeneratedResource.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.deps.ClassAnalysis$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassAnalysis.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.deps.ClassAnalysis> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassAnalysis.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.deps.ClassDependentsAccumulator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassDependentsAccumulator.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysis$ClassSetDiff> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassSetAnalysis.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysis> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassSetAnalysis.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassSetAnalysisData.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.deps.ClassSetAnalysisData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassSetAnalysisData.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.processing.AnnotationProcessingData$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessingData.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.processing.AnnotationProcessingData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessingData.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.processing.AnnotationProcessingResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessingResult.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.processing.AnnotationProcessorResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessorResult.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.processing.GeneratedResourceSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GeneratedResourceSerializer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.processing.IncrementalAnnotationProcessorType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalAnnotationProcessorType.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.AbstractRecompilationSpecProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractRecompilationSpecProvider.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.CurrentCompilation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CurrentCompilation.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.CurrentCompilationAccess$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CurrentCompilationAccess.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.CurrentCompilationAccess$CreateSnapshot> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CurrentCompilationAccess.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.CurrentCompilationAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CurrentCompilationAccess.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.DefaultIncrementalCompileResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncrementalCompileResult.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.DefaultSourceFileClassNameConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceFileClassNameConverter.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.FileNameDerivingClassNameConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileNameDerivingClassNameConverter.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.GroovyRecompilationSpecProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroovyRecompilationSpecProvider.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.IncrementalCompilationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompilationResult.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.JavaRecompilationSpecProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaRecompilationSpecProvider.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.PreviousCompilation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PreviousCompilation.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.PreviousCompilationAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PreviousCompilationAccess.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.PreviousCompilationData$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PreviousCompilationData.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.PreviousCompilationData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PreviousCompilationData.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.RecompilationSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RecompilationSpec.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.RecompilationSpecProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RecompilationSpecProvider.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.SourceFileChangeProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceFileChangeProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.recomp.SourceFileClassNameConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceFileClassNameConverter.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.serialization.HierarchicalNameSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HierarchicalNameSerializer.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.transaction.CompileTransaction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileTransaction.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.transaction.CompileTransaction$StashedFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileTransaction.java:0)
+Class <org.gradle.api.internal.tasks.compile.incremental.transaction.CompileTransaction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileTransaction.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.AggregatingProcessingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AggregatingProcessingStrategy.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.AggregatingProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AggregatingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.AnnotationProcessorDeclaration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessorDeclaration.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.AnnotationProcessorDeclarationSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessorDeclarationSerializer.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.AnnotationProcessorDetector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessorDetector.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.AnnotationProcessorDetector$ProcessorServiceLocator$MetadataLineProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessorDetector.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.AnnotationProcessorDetector$ProcessorServiceLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessorDetector.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.AnnotationProcessorDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotationProcessorDetector.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DelegatingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.DynamicProcessingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DynamicProcessingStrategy.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.DynamicProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DynamicProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.ElementUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ElementUtils.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.IncrementalFiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalFiler.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.IncrementalProcessingEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalProcessingEnvironment.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.IncrementalProcessingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalProcessingStrategy.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.IsolatingProcessingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatingProcessingStrategy.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.IsolatingProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NonIncrementalProcessingStrategy.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NonIncrementalProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.SupportedOptionsCollectingProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SupportedOptionsCollectingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeTrackingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeTrackingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeTrackingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeTrackingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeTrackingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeTrackingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeTrackingProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.reflect.GradleStandardJavaFileManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleStandardJavaFileManager.java:0)
+Class <org.gradle.api.internal.tasks.compile.tooling.JavaCompileTaskSuccessResultPostProcessor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaCompileTaskSuccessResultPostProcessor.java:0)
+Class <org.gradle.api.internal.tasks.compile.tooling.JavaCompileTaskSuccessResultPostProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaCompileTaskSuccessResultPostProcessor.java:0)
+Class <org.gradle.api.internal.tasks.execution.statistics.TaskExecutionStatistics> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionStatistics.java:0)
+Class <org.gradle.api.internal.tasks.execution.statistics.TaskExecutionStatisticsEventAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionStatisticsEventAdapter.java:0)
+Class <org.gradle.api.internal.tasks.execution.statistics.TaskExecutionStatisticsEventAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionStatisticsEventAdapter.java:0)
+Class <org.gradle.api.internal.tasks.options.AbstractOptionElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.BooleanOptionElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BooleanOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.BuiltInOptionElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuiltInOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.FieldOptionElement$FieldSetter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FieldOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.FieldOptionElement$PropertyFieldSetter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FieldOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.FieldOptionElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FieldOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.InstanceOptionDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstanceOptionDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.options.MethodOptionElement$MethodInvokingSetter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.MethodOptionElement$MethodPropertySetter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.MethodOptionElement$PropertyValueSetter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.MethodOptionElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.MethodSignature> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodSignature.java:0)
+Class <org.gradle.api.internal.tasks.options.MultipleValueOptionElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultipleValueOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.OptionDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.options.OptionElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionElement.java:0)
+Class <org.gradle.api.internal.tasks.options.OptionReader$OptionElementAndSignature> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionReader.java:0)
+Class <org.gradle.api.internal.tasks.options.OptionReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionReader.java:0)
+Class <org.gradle.api.internal.tasks.options.OptionValidationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionValidationException.java:0)
+Class <org.gradle.api.internal.tasks.options.OptionValueNotationParserFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionValueNotationParserFactory.java:0)
+Class <org.gradle.api.internal.tasks.options.OptionValueNotationParserFactory$NoDescriptionValuesJustReturningParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionValueNotationParserFactory.java:0)
+Class <org.gradle.api.internal.tasks.options.OptionValueNotationParserFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionValueNotationParserFactory.java:0)
+Class <org.gradle.api.internal.tasks.options.PropertySetter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertySetter.java:0)
+Class <org.gradle.api.internal.tasks.options.SingleValueOptionElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SingleValueOptionElement.java:0)
+Class <org.gradle.api.internal.tasks.scala.AnalysisStoreProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnalysisStoreProvider.java:0)
+Class <org.gradle.api.internal.tasks.scala.DaemonScalaCompiler$ScalaCompilerParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonScalaCompiler.java:0)
+Class <org.gradle.api.internal.tasks.scala.DaemonScalaCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonScalaCompiler.java:0)
+Class <org.gradle.api.internal.tasks.scala.DefaultScalaJavaJointCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScalaJavaJointCompileSpec.java:0)
+Class <org.gradle.api.internal.tasks.scala.DefaultScalaPluginExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScalaPluginExtension.java:0)
+Class <org.gradle.api.internal.tasks.scala.GuavaBackedClassLoaderCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GuavaBackedClassLoaderCache.java:0)
+Class <org.gradle.api.internal.tasks.scala.GuavaBackedClassLoaderCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GuavaBackedClassLoaderCache.java:0)
+Class <org.gradle.api.internal.tasks.scala.HashedClasspath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HashedClasspath.java:0)
+Class <org.gradle.api.internal.tasks.scala.MappedPosition> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MappedPosition.java:0)
+Class <org.gradle.api.internal.tasks.scala.MinimalScalaCompileOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MinimalScalaCompileOptions.java:0)
+Class <org.gradle.api.internal.tasks.scala.MinimalScalaCompilerDaemonForkOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MinimalScalaCompilerDaemonForkOptions.java:0)
+Class <org.gradle.api.internal.tasks.scala.NormalizingScalaCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NormalizingScalaCompiler.java:0)
+Class <org.gradle.api.internal.tasks.scala.SbtLoggerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SbtLoggerAdapter.java:0)
+Class <org.gradle.api.internal.tasks.scala.ScalaCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalaCompileSpec.java:0)
+Class <org.gradle.api.internal.tasks.scala.ScalaCompilerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalaCompilerFactory.java:0)
+Class <org.gradle.api.internal.tasks.scala.ScalaCompilerInterfaceSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalaCompilerInterfaceSpec.java:0)
+Class <org.gradle.api.internal.tasks.scala.ScalaCompilerLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalaCompilerLoader.java:0)
+Class <org.gradle.api.internal.tasks.scala.ScalaJavaJointCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalaJavaJointCompileSpec.java:0)
+Class <org.gradle.api.internal.tasks.scala.TimeCheckingClassLoaderCache$TimestampedFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeCheckingClassLoaderCache.java:0)
+Class <org.gradle.api.internal.tasks.scala.TimeCheckingClassLoaderCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeCheckingClassLoaderCache.java:0)
+Class <org.gradle.api.internal.tasks.scala.ZincScalaCompiler$AnalysisBakedDefineClass> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZincScalaCompiler.java:0)
+Class <org.gradle.api.internal.tasks.scala.ZincScalaCompiler$ClearableMapBackedCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZincScalaCompiler.java:0)
+Class <org.gradle.api.internal.tasks.scala.ZincScalaCompiler$EntryLookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZincScalaCompiler.java:0)
+Class <org.gradle.api.internal.tasks.scala.ZincScalaCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZincScalaCompiler.java:0)
+Class <org.gradle.api.internal.tasks.scala.ZincScalaCompilerArgumentsGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZincScalaCompilerArgumentsGenerator.java:0)
+Class <org.gradle.api.internal.tasks.scala.ZincScalaCompilerFacade> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZincScalaCompilerFacade.java:0)
+Class <org.gradle.api.internal.tasks.scala.ZincScalaCompilerFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZincScalaCompilerFactory.java:0)
+Class <org.gradle.api.internal.tasks.scala.ZincScalaCompilerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZincScalaCompilerFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.AbstractTestDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTestDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.testing.DecoratingTestDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DecoratingTestDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultAggregateTestReport$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAggregateTestReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultAggregateTestReport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAggregateTestReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultJUnitXmlReport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJUnitXmlReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultNestedTestSuiteDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNestedTestSuiteDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestClassDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestClassDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestClassRunInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestClassRunInfo.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFailure.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestFailureDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFailureDetails.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestMethodDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestMethodDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestOutputEvent.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestSuiteDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestSuiteDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.testing.DefaultTestTaskReports> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestTaskReports.java:0)
+Class <org.gradle.api.internal.tasks.testing.FailFastTestListenerInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailFastTestListenerInternal.java:0)
+Class <org.gradle.api.internal.tasks.testing.JULRedirector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JULRedirector.java:0)
+Class <org.gradle.api.internal.tasks.testing.JvmTestExecutionSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmTestExecutionSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SuiteTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestClassLoaderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestClassLoaderFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestClassRunInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestClassRunInfo.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestCompleteEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestCompleteEvent.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestDescriptorInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestDescriptorInternal.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestExecutableUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutableUtils.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecuter.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestExecutionSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestFailureSerializationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestFailureSerializationException.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestFramework> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestFramework.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestFrameworkDistributionModule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestFrameworkDistributionModule.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestResultProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestResultProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestStartEvent.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestSuiteExecutionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSuiteExecutionException.java:0)
+Class <org.gradle.api.internal.tasks.testing.TestingBasePluginServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestingBasePluginServiceRegistry.java:0)
+Class <org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerTestClassProcessorFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.AbstractTestFrameworkDetector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTestFrameworkDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.AbstractTestFrameworkDetector$TestClass> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTestFrameworkDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.AbstractTestFrameworkDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTestFrameworkDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.ClassFileExtractionManager$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassFileExtractionManager.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.ClassFileExtractionManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassFileExtractionManager.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.DefaultTestClassScanner$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestClassScanner.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.DefaultTestClassScanner$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestClassScanner.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.DefaultTestClassScanner$ClassFileVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestClassScanner.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.DefaultTestClassScanner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestClassScanner.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.DefaultTestExecuter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestExecuter.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.DefaultTestExecuter$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestExecuter.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.DefaultTestExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestExecuter.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.ForkedTestClasspathFactory$AdditionalClasspath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkedTestClasspathFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.ForkedTestClasspathFactory$ClassDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkedTestClasspathFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.ForkedTestClasspathFactory$ClassDetectorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkedTestClasspathFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.ForkedTestClasspathFactory$ClassLoadingClassDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkedTestClasspathFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.ForkedTestClasspathFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkedTestClasspathFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.JarFilePackageListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JarFilePackageListener.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.JarFilePackageLister> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JarFilePackageLister.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.TestClassVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestClassVisitor.java:0)
+Class <org.gradle.api.internal.tasks.testing.detection.TestFrameworkDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestFrameworkDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFilter.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestFilterSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestFilterSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher$ClassNameSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher$FullQualifiedClassNameSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher$LastElementMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher$NoWildcardMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher$SimpleClassNameSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher$TestPattern> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher$WildcardMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestSelectionMatcher.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractJUnitTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.AllExceptIgnoredTestRunnerBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllExceptIgnoredTestRunnerBuilder.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.AllExceptIgnoredTestRunnerBuilder$FallbackJUnit4Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllExceptIgnoredTestRunnerBuilder.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.AllExceptIgnoredTestRunnerBuilder$IgnoredIgnoredBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllExceptIgnoredTestRunnerBuilder.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.AllExceptIgnoredTestRunnerBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllExceptIgnoredTestRunnerBuilder.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.CategoryFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CategoryFilter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.IgnoredTestDescriptorProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IgnoredTestDescriptorProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitSupport.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitTestClassDetector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitTestClassDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitTestClassDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitTestClassDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor$MethodNameFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitTestClassExecutor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitTestClassExecutor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitTestClassProcessorFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitTestEventAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitTestEventAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.JUnitTestFramework> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitTestFramework.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.TestClassExecutionEventGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestClassExecutionEventGenerator.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.TestClassExecutionListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestClassExecutionListener.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.AggregateTestResultsProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AggregateTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.AggregateTestResultsProvider$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AggregateTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.AggregateTestResultsProvider$DelegateProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AggregateTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.AggregateTestResultsProvider$OverlaidIdProxyingTestClassResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AggregateTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.AggregateTestResultsProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AggregateTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.Binary2JUnitXmlReportGenerator$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Binary2JUnitXmlReportGenerator.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.Binary2JUnitXmlReportGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Binary2JUnitXmlReportGenerator.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.Binary2JUnitXmlReportGenerator$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Binary2JUnitXmlReportGenerator.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.Binary2JUnitXmlReportGenerator$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Binary2JUnitXmlReportGenerator.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.Binary2JUnitXmlReportGenerator$JUnitXmlReportFileGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Binary2JUnitXmlReportGenerator.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.Binary2JUnitXmlReportGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Binary2JUnitXmlReportGenerator.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.BinaryResultBackedTestResultsProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryResultBackedTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.BinaryResultBackedTestResultsProvider$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryResultBackedTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.BinaryResultBackedTestResultsProvider$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryResultBackedTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.BinaryResultBackedTestResultsProvider$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryResultBackedTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.BinaryResultBackedTestResultsProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryResultBackedTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.InMemoryTestResultsProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.InMemoryTestResultsProvider$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.InMemoryTestResultsProvider$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.InMemoryTestResultsProvider$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.InMemoryTestResultsProvider$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.InMemoryTestResultsProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryTestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultOptions.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$BackedOutputProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$FailureType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$NullOutputProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$OutputProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$TestCase> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$TestCaseExecution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$TestCaseExecutionFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$TestCaseExecutionSkipped> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter$TestCaseExecutionSuccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.JUnitXmlResultWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitXmlResultWriter.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestCaseRerunHandling> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestCaseRerunHandling.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestClassResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestClassResult.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestFailure.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestMethodResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestMethodResult.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStore.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStore$Index> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStore.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStore$IndexBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStore.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStore$Reader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStore.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStore$Region> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStore.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStore$TestCaseRegion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStore.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStore$Writer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStore.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStore.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestOutputStoreBackedResultsProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputStoreBackedResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestReportDataCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestReportDataCollector.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestResultSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestResultSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.junit.result.TestResultsProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestResultsProvider.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$BackwardsCompatibleLauncherSession> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$ClassMethodNameFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformTestClassProcessorFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestExecutionListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformTestExecutionListener.java:0)
+Class <org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestFramework> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JUnitPlatformTestFramework.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.AbstractTestLogger$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTestLogger.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.AbstractTestLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTestLogger.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.ClassMethodNameStackTraceSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassMethodNameStackTraceSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.DefaultTestLogging> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLogging.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.DefaultTestLoggingContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLoggingContainer.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.FullExceptionFormatter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FullExceptionFormatter.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.FullExceptionFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FullExceptionFormatter.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.GroovyStackTraceSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroovyStackTraceSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.JavaClassNameFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaClassNameFormatter.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.ShortExceptionFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShortExceptionFormatter.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.StackTraceFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StackTraceFilter.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.TestCountLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestCountLogger.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.TestEventLogger$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventLogger.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.TestEventLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventLogger.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.TestExceptionFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExceptionFormatter.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.TestWorkerProgressListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestWorkerProgressListener.java:0)
+Class <org.gradle.api.internal.tasks.testing.logging.TruncatedStackTraceSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TruncatedStackTraceSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.ExecuteTestBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteTestBuildOperationType.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.ExecuteTestBuildOperationType$Output> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteTestBuildOperationType.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.ExecuteTestBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteTestBuildOperationType.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.ExecuteTestBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteTestBuildOperationType.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.TestExecutionBuildOperationBuildSessionScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionBuildOperationBuildSessionScopeServices.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.TestListenerBuildOperationAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestListenerBuildOperationAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.TestListenerBuildOperationAdapter$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestListenerBuildOperationAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.TestListenerBuildOperationAdapter$InProgressExecuteTestBuildOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestListenerBuildOperationAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.TestListenerBuildOperationAdapter$OutputProgress> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestListenerBuildOperationAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.TestListenerBuildOperationAdapter$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestListenerBuildOperationAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.operations.TestListenerBuildOperationAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestListenerBuildOperationAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.CaptureTestOutputTestResultProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CaptureTestOutputTestResultProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.DefaultStandardOutputRedirector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStandardOutputRedirector.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.DefaultStandardOutputRedirector$DiscardAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStandardOutputRedirector.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.DefaultStandardOutputRedirector$WriteAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStandardOutputRedirector.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.DefaultStandardOutputRedirector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStandardOutputRedirector.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.MaxNParallelTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MaxNParallelTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.PatternMatchTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternMatchTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.RestartEveryNTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RestartEveryNTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.RunPreviousFailedFirstTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunPreviousFailedFirstTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.StandardOutputRedirector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StandardOutputRedirector.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.TestMainAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestMainAction.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.TestMainAction$RootTestSuiteDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestMainAction.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.TestMainAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestMainAction.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.TestOutputRedirector$Forwarder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputRedirector.java:0)
+Class <org.gradle.api.internal.tasks.testing.processors.TestOutputRedirector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOutputRedirector.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.AllTestResults> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllTestResults.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.ClassPageRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassPageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.ClassPageRenderer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassPageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.ClassPageRenderer$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassPageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.ClassPageRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassPageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.ClassTestResults> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassTestResults.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.CompositeTestResults> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeTestResults.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.DefaultTestReport$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.DefaultTestReport$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.DefaultTestReport$3$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.DefaultTestReport$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.DefaultTestReport$HtmlReportFileGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.DefaultTestReport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestReport.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.LocaleSafeDecimalFormat> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocaleSafeDecimalFormat.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.OverviewPageRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OverviewPageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.OverviewPageRenderer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OverviewPageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.OverviewPageRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OverviewPageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PackagePageRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PackagePageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PackagePageRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PackagePageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PackageTestResults> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PackageTestResults.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PageRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PageRenderer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PageRenderer$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PageRenderer$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PageRenderer$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.PageRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PageRenderer.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.TestReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestReporter.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.TestResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestResult.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.TestResultModel$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestResultModel.java:0)
+Class <org.gradle.api.internal.tasks.testing.report.TestResultModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestResultModel.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.AttachParentTestResultProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AttachParentTestResultProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.DefaultTestResult$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestResult.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.DefaultTestResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestResult.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.StateTrackingTestResultProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateTrackingTestResultProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.TestListenerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestListenerAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.TestListenerInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestListenerInternal.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.TestState$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestState.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.TestState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestState.java:0)
+Class <org.gradle.api.internal.tasks.testing.results.UnknownTestDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnknownTestDescriptor.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGClassListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGClassListener.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGConfigurationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGConfigurationListener.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGListenerAdapterFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGListenerAdapterFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGListenerAdapterFactory$AdaptedListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGListenerAdapterFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGListenerAdapterFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGListenerAdapterFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGSpec.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestClassDetector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestClassDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestClassDetector$TestNGTestMethodDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestClassDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestClassDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestClassDetector.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor$SelectedTestsFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestFramework$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestFramework.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestFramework> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestFramework.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestResultProcessorAdapter$TestClassInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestResultProcessorAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNGTestResultProcessorAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNGTestResultProcessorAdapter.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.TestNgTestClassProcessorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestNgTestClassProcessorFactory.java:0)
+Class <org.gradle.api.internal.tasks.testing.testng.UnrepresentableParameterException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnrepresentableParameterException.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.ForkedTestClasspath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkedTestClasspath.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.ForkingTestClassProcessor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkingTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.ForkingTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkingTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.RemoteTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RemoteTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$DefaultNestedTestSuiteDescriptorSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$DefaultTestClassDescriptorSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$DefaultTestClassRunInfoSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$DefaultTestDescriptorSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$DefaultTestFailureSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$DefaultTestMethodDescriptorSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$DefaultTestOutputEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$DefaultTestSuiteDescriptorSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$IdSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$NullableSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$TestCompleteEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$TestStartEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer$WorkerTestSuiteDescriptorSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestEventSerializer.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestWorker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestWorker.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestWorker$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestWorker.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestWorker$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestWorker.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestWorker$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestWorker.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestWorker$TestFrameworkServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestWorker.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.TestWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestWorker.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.WorkerTestClassProcessor$WorkerTestSuiteDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.testing.worker.WorkerTestClassProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerTestClassProcessor.java:0)
+Class <org.gradle.api.internal.tasks.userinput.BuildScanUserInputHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanUserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.DefaultBuildScanUserInputHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildScanUserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.DefaultUserInputHandler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.DefaultUserInputHandler$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.DefaultUserInputHandler$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.DefaultUserInputHandler$BooleanParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.DefaultUserInputHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.DefaultUserInputReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUserInputReader.java:0)
+Class <org.gradle.api.internal.tasks.userinput.NonInteractiveUserInputHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NonInteractiveUserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.UserInputHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserInputHandler.java:0)
+Class <org.gradle.api.internal.tasks.userinput.UserInputReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserInputReader.java:0)
+Class <org.gradle.api.java.archives.internal.CustomManifestInternalWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CustomManifestInternalWrapper.java:0)
+Class <org.gradle.api.java.archives.internal.DefaultAttributes> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAttributes.java:0)
+Class <org.gradle.api.java.archives.internal.DefaultManifest$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultManifest.java:0)
+Class <org.gradle.api.java.archives.internal.DefaultManifest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultManifest.java:0)
+Class <org.gradle.api.java.archives.internal.DefaultManifestMergeDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultManifestMergeDetails.java:0)
+Class <org.gradle.api.java.archives.internal.DefaultManifestMergeSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultManifestMergeSpec.java:0)
+Class <org.gradle.api.java.archives.internal.ManifestInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManifestInternal.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrExecuter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrExecuter.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrExecuter$Antlr2Tool> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrExecuter.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrExecuter$Antlr3Tool> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrExecuter.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrExecuter$Antlr4Tool> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrExecuter.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrExecuter$AntlrTool> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrExecuter.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrExecuter.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrResult.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrSourceGenerationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrSourceGenerationException.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrSpec.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrSpecFactory.java:0)
+Class <org.gradle.api.plugins.antlr.internal.AntlrWorkerManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntlrWorkerManager.java:0)
+Class <org.gradle.api.plugins.antlr.internal.DefaultAntlrSourceDirectorySet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAntlrSourceDirectorySet.java:0)
+Class <org.gradle.api.plugins.antlr.internal.antlr2.GenerationPlan> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GenerationPlan.java:0)
+Class <org.gradle.api.plugins.antlr.internal.antlr2.GenerationPlanBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GenerationPlanBuilder.java:0)
+Class <org.gradle.api.plugins.antlr.internal.antlr2.GrammarDelegate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GrammarDelegate.java:0)
+Class <org.gradle.api.plugins.antlr.internal.antlr2.GrammarFileMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GrammarFileMetadata.java:0)
+Class <org.gradle.api.plugins.antlr.internal.antlr2.GrammarMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GrammarMetadata.java:0)
+Class <org.gradle.api.plugins.antlr.internal.antlr2.MetadataExtracter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MetadataExtracter.java:0)
+Class <org.gradle.api.plugins.antlr.internal.antlr2.XRef> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XRef.java:0)
+Class <org.gradle.api.plugins.internal.ConfigurationSoftwareComponentVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationSoftwareComponentVariant.java:0)
+Class <org.gradle.api.plugins.internal.DefaultAdhocSoftwareComponent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAdhocSoftwareComponent.java:0)
+Class <org.gradle.api.plugins.internal.DefaultApplicationPluginConvention> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultApplicationPluginConvention.java:0)
+Class <org.gradle.api.plugins.internal.DefaultBasePluginConvention> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBasePluginConvention.java:0)
+Class <org.gradle.api.plugins.internal.DefaultBasePluginExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBasePluginExtension.java:0)
+Class <org.gradle.api.plugins.internal.DefaultJavaApplication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaApplication.java:0)
+Class <org.gradle.api.plugins.internal.DefaultJavaFeatureSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaFeatureSpec.java:0)
+Class <org.gradle.api.plugins.internal.DefaultJavaPluginConvention> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaPluginConvention.java:0)
+Class <org.gradle.api.plugins.internal.DefaultJavaPluginExtension$DefaultJavaResolutionConsistency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaPluginExtension.java:0)
+Class <org.gradle.api.plugins.internal.DefaultJavaPluginExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaPluginExtension.java:0)
+Class <org.gradle.api.plugins.internal.DefaultSoftwareComponentFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSoftwareComponentFactory.java:0)
+Class <org.gradle.api.plugins.internal.DefaultWarPluginConvention> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWarPluginConvention.java:0)
+Class <org.gradle.api.plugins.internal.FeatureSpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureSpecInternal.java:0)
+Class <org.gradle.api.plugins.internal.HelpBuiltInCommand> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HelpBuiltInCommand.java:0)
+Class <org.gradle.api.plugins.internal.HelpTasksAutoApplyAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HelpTasksAutoApplyAction.java:0)
+Class <org.gradle.api.plugins.internal.JavaConfigurationVariantMapping$UnpublishableArtifactTypeSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaConfigurationVariantMapping.java:0)
+Class <org.gradle.api.plugins.internal.JavaConfigurationVariantMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaConfigurationVariantMapping.java:0)
+Class <org.gradle.api.plugins.internal.JavaPluginHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaPluginHelper.java:0)
+Class <org.gradle.api.plugins.internal.JvmPluginsHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmPluginsHelper.java:0)
+Class <org.gradle.api.plugins.internal.PluginAuthorServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginAuthorServices.java:0)
+Class <org.gradle.api.plugins.internal.PluginAuthorServices$GlobalScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginAuthorServices.java:0)
+Class <org.gradle.api.plugins.internal.PluginAuthorServices$ProjectScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginAuthorServices.java:0)
+Class <org.gradle.api.plugins.internal.PluginAuthorServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginAuthorServices.java:0)
+Class <org.gradle.api.publish.internal.CompositePublicationArtifactSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositePublicationArtifactSet.java:0)
+Class <org.gradle.api.publish.internal.DefaultPublicationArtifactSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPublicationArtifactSet.java:0)
+Class <org.gradle.api.publish.internal.DefaultPublicationArtifactSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPublicationArtifactSet.java:0)
+Class <org.gradle.api.publish.internal.DefaultPublicationContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPublicationContainer.java:0)
+Class <org.gradle.api.publish.internal.DefaultPublishingExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPublishingExtension.java:0)
+Class <org.gradle.api.publish.internal.PublicationArtifactInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationArtifactInternal.java:0)
+Class <org.gradle.api.publish.internal.PublicationArtifactSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationArtifactSet.java:0)
+Class <org.gradle.api.publish.internal.PublicationFieldValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationFieldValidator.java:0)
+Class <org.gradle.api.publish.internal.PublicationInternal$DerivedArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationInternal.java:0)
+Class <org.gradle.api.publish.internal.PublicationInternal$PublishedFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationInternal.java:0)
+Class <org.gradle.api.publish.internal.PublicationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationInternal.java:0)
+Class <org.gradle.api.publish.internal.PublishOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublishOperation.java:0)
+Class <org.gradle.api.publish.internal.metadata.ComponentData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentData.java:0)
+Class <org.gradle.api.publish.internal.metadata.DependencyAttributesValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyAttributesValidator.java:0)
+Class <org.gradle.api.publish.internal.metadata.EnforcedPlatformPublicationValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnforcedPlatformPublicationValidator.java:0)
+Class <org.gradle.api.publish.internal.metadata.GradleModuleMetadataWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleModuleMetadataWriter.java:0)
+Class <org.gradle.api.publish.internal.metadata.InvalidPublicationChecker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidPublicationChecker.java:0)
+Class <org.gradle.api.publish.internal.metadata.InvalidPublicationChecker$VariantIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidPublicationChecker.java:0)
+Class <org.gradle.api.publish.internal.metadata.InvalidPublicationChecker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidPublicationChecker.java:0)
+Class <org.gradle.api.publish.internal.metadata.JsonWriterScope$Contents> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JsonWriterScope.java:0)
+Class <org.gradle.api.publish.internal.metadata.JsonWriterScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JsonWriterScope.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataJsonWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataJsonWriter.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$Artifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$ArtifactSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$Attribute> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$AvailableAt> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$Capability> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$Dependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$DependencyConstraint> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$DependencyCoordinates> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$Identity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$LocalVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$RemoteVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$Variant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec$Version> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpec.java:0)
+Class <org.gradle.api.publish.internal.metadata.ModuleMetadataSpecBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMetadataSpecBuilder.java:0)
+Class <org.gradle.api.publish.internal.service.PublishServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublishServices.java:0)
+Class <org.gradle.api.publish.internal.validation.CollectedWarnings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectedWarnings.java:0)
+Class <org.gradle.api.publish.internal.validation.DuplicatePublicationTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DuplicatePublicationTracker.java:0)
+Class <org.gradle.api.publish.internal.validation.PublicationErrorChecker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationErrorChecker.java:0)
+Class <org.gradle.api.publish.internal.validation.PublicationWarningsCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationWarningsCollector.java:0)
+Class <org.gradle.api.publish.internal.validation.PublicationWithProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationWithProject.java:0)
+Class <org.gradle.api.publish.internal.versionmapping.DefaultVariantVersionMappingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVariantVersionMappingStrategy.java:0)
+Class <org.gradle.api.publish.internal.versionmapping.DefaultVersionMappingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionMappingStrategy.java:0)
+Class <org.gradle.api.publish.internal.versionmapping.VariantVersionMappingStrategyInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VariantVersionMappingStrategyInternal.java:0)
+Class <org.gradle.api.publish.internal.versionmapping.VersionMappingStrategyInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionMappingStrategyInternal.java:0)
+Class <org.gradle.api.publish.ivy.internal.IvyServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyServices.java:0)
+Class <org.gradle.api.publish.ivy.internal.IvyServices$BuildServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyServices.java:0)
+Class <org.gradle.api.publish.ivy.internal.IvyServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyServices.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.AbstractIvyArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractIvyArtifact.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.ArchiveTaskBasedIvyArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchiveTaskBasedIvyArtifact.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.DefaultIvyArtifactSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyArtifactSet.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.DefaultIvyArtifactSet$ArtifactsFileCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyArtifactSet.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.DefaultIvyArtifactSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyArtifactSet.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.DerivedIvyArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DerivedIvyArtifact.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.FileBasedIvyArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileBasedIvyArtifact.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.IvyArtifactNotationParserFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.IvyArtifactNotationParserFactory$ArchiveTaskNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.IvyArtifactNotationParserFactory$FileNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.IvyArtifactNotationParserFactory$IvyArtifactMapNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.IvyArtifactNotationParserFactory$PublishArtifactNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.IvyArtifactNotationParserFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.PublishArtifactBasedIvyArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublishArtifactBasedIvyArtifact.java:0)
+Class <org.gradle.api.publish.ivy.internal.artifact.SingleOutputTaskIvyArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SingleOutputTaskIvyArtifact.java:0)
+Class <org.gradle.api.publish.ivy.internal.dependency.DefaultIvyDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyDependency.java:0)
+Class <org.gradle.api.publish.ivy.internal.dependency.DefaultIvyDependencySet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyDependencySet.java:0)
+Class <org.gradle.api.publish.ivy.internal.dependency.DefaultIvyExcludeRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyExcludeRule.java:0)
+Class <org.gradle.api.publish.ivy.internal.dependency.DefaultIvyProjectDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyProjectDependency.java:0)
+Class <org.gradle.api.publish.ivy.internal.dependency.IvyDependencyInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDependencyInternal.java:0)
+Class <org.gradle.api.publish.ivy.internal.dependency.IvyExcludeRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyExcludeRule.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyConfiguration.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyConfigurationContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyConfigurationContainer.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyExtraInfoSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyExtraInfoSpec.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyModuleDescriptorAuthor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyModuleDescriptorAuthor.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyModuleDescriptorDescription> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyModuleDescriptorDescription.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyModuleDescriptorLicense> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyModuleDescriptorLicense.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyModuleDescriptorSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyModuleDescriptorSpec.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublication$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyPublication.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublication$GradleModuleDescriptorDerivedArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyPublication.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyPublication.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublicationIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIvyPublicationIdentity.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.IvyModuleDescriptorSpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyModuleDescriptorSpecInternal.java:0)
+Class <org.gradle.api.publish.ivy.internal.publication.IvyPublicationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyPublicationInternal.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.ContextualizingIvyPublisher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextualizingIvyPublisher.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.ContextualizingIvyPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextualizingIvyPublisher.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.DefaultReadOnlyIvyPublicationIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultReadOnlyIvyPublicationIdentity.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.DependencyResolverIvyPublisher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResolverIvyPublisher.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.DependencyResolverIvyPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResolverIvyPublisher.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyArtifactInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyArtifactInternal.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyDescriptorFileGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDescriptorFileGenerator.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyDescriptorFileGenerator$DescriptorFileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDescriptorFileGenerator.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyDescriptorFileGenerator$Model> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDescriptorFileGenerator.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyDescriptorFileGenerator$ModelWriter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDescriptorFileGenerator.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyDescriptorFileGenerator$ModelWriter$OptionalAttributeXmlWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDescriptorFileGenerator.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyDescriptorFileGenerator$ModelWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDescriptorFileGenerator.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyDescriptorFileGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDescriptorFileGenerator.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyDuplicatePublicationTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyDuplicatePublicationTracker.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyNormalizedPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyNormalizedPublication.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyPublicationIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyPublicationIdentity.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.IvyPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IvyPublisher.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.MutableIvyPublicationidentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MutableIvyPublicationidentity.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.NormalizedIvyArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NormalizedIvyArtifact.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.ValidatingIvyPublisher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidatingIvyPublisher.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.ValidatingIvyPublisher$IvyFieldValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidatingIvyPublisher.java:0)
+Class <org.gradle.api.publish.ivy.internal.publisher.ValidatingIvyPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidatingIvyPublisher.java:0)
+Class <org.gradle.api.publish.ivy.internal.versionmapping.DefaultVersionMappingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionMappingStrategy.java:0)
+Class <org.gradle.api.publish.maven.internal.MavenPublishServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublishServices.java:0)
+Class <org.gradle.api.publish.maven.internal.MavenPublishServices$ComponentRegistrationAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublishServices.java:0)
+Class <org.gradle.api.publish.maven.internal.MavenPublishServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublishServices.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.AbstractMavenArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMavenArtifact.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.ArchiveTaskBasedMavenArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchiveTaskBasedMavenArtifact.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.DefaultMavenArtifactSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenArtifactSet.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.DefaultMavenArtifactSet$ArtifactsFileCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenArtifactSet.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.DefaultMavenArtifactSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenArtifactSet.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.DerivedMavenArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DerivedMavenArtifact.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.FileBasedMavenArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileBasedMavenArtifact.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.MavenArtifactNotationParserFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.MavenArtifactNotationParserFactory$ArchiveTaskNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.MavenArtifactNotationParserFactory$FileNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.MavenArtifactNotationParserFactory$MavenArtifactMapNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.MavenArtifactNotationParserFactory$ProviderNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.MavenArtifactNotationParserFactory$PublishArtifactNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.MavenArtifactNotationParserFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenArtifactNotationParserFactory.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.PublishArtifactBasedMavenArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublishArtifactBasedMavenArtifact.java:0)
+Class <org.gradle.api.publish.maven.internal.artifact.SingleOutputTaskMavenArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SingleOutputTaskMavenArtifact.java:0)
+Class <org.gradle.api.publish.maven.internal.dependencies.DefaultMavenDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenDependency.java:0)
+Class <org.gradle.api.publish.maven.internal.dependencies.DefaultMavenProjectDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenProjectDependency.java:0)
+Class <org.gradle.api.publish.maven.internal.dependencies.MavenDependencyInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenDependencyInternal.java:0)
+Class <org.gradle.api.publish.maven.internal.dependencies.MavenVersionRangeMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenVersionRangeMapper.java:0)
+Class <org.gradle.api.publish.maven.internal.dependencies.VersionRangeMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionRangeMapper.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPom> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPom.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPomDeveloper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPomDeveloper.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPomDistributionManagement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPomDistributionManagement.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPomLicense> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPomLicense.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPomMailingList> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPomMailingList.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPomOrganization> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPomOrganization.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPomProjectManagement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPomProjectManagement.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPomRelocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPomRelocation.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPomScm> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPomScm.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPublication.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPublication.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication$ArtifactKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPublication.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication$PublishedDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPublication.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication$SerializableMavenArtifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPublication.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMavenPublication.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.MavenPomDistributionManagementInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPomDistributionManagementInternal.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.MavenPomInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPomInternal.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublicationInternal.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.ReadableMavenProjectIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReadableMavenProjectIdentity.java:0)
+Class <org.gradle.api.publish.maven.internal.publication.WritableMavenProjectIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WritableMavenProjectIdentity.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.AbstractMavenPublisher$ModuleArtifactPublisher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMavenPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.AbstractMavenPublisher$ModuleArtifactPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMavenPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.AbstractMavenPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMavenPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.MavenDuplicatePublicationTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenDuplicatePublicationTracker.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.MavenLocalPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenLocalPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.MavenNormalizedPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenNormalizedPublication.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.MavenProjectIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenProjectIdentity.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.MavenPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.MavenPublishers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublishers.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.MavenRemotePublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenRemotePublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.MutableMavenProjectIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MutableMavenProjectIdentity.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.StaticLockingMavenPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StaticLockingMavenPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.ValidatingMavenPublisher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidatingMavenPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.ValidatingMavenPublisher$MavenFieldValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidatingMavenPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.publisher.ValidatingMavenPublisher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidatingMavenPublisher.java:0)
+Class <org.gradle.api.publish.maven.internal.tasks.MavenPomFileGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPomFileGenerator.java:0)
+Class <org.gradle.api.publish.maven.internal.tasks.MavenPomFileGenerator$MavenPomSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPomFileGenerator.java:0)
+Class <org.gradle.api.publish.maven.internal.tasks.MavenPomFileGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPomFileGenerator.java:0)
+Class <org.gradle.api.publish.maven.internal.validation.MavenPublicationErrorChecker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenPublicationErrorChecker.java:0)
+Class <org.gradle.api.reporting.components.internal.AbstractBinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractBinaryRenderer.java:0)
+Class <org.gradle.api.reporting.components.internal.BinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryRenderer.java:0)
+Class <org.gradle.api.reporting.components.internal.ComponentRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentRenderer.java:0)
+Class <org.gradle.api.reporting.components.internal.ComponentReportRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentReportRenderer.java:0)
+Class <org.gradle.api.reporting.components.internal.DiagnosticsServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DiagnosticsServices.java:0)
+Class <org.gradle.api.reporting.components.internal.DiagnosticsServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DiagnosticsServices.java:0)
+Class <org.gradle.api.reporting.components.internal.RendererUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RendererUtils.java:0)
+Class <org.gradle.api.reporting.components.internal.SourceSetRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceSetRenderer.java:0)
+Class <org.gradle.api.reporting.components.internal.SourceSetRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceSetRenderer.java:0)
+Class <org.gradle.api.reporting.components.internal.TrackingReportRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TrackingReportRenderer.java:0)
+Class <org.gradle.api.reporting.components.internal.TypeAwareBinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeAwareBinaryRenderer.java:0)
+Class <org.gradle.api.reporting.model.internal.ModelNodeRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelNodeRenderer.java:0)
+Class <org.gradle.api.reporting.model.internal.TextModelReportRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextModelReportRenderer.java:0)
+Class <org.gradle.api.resources.internal.LocalResourceAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalResourceAdapter.java:0)
+Class <org.gradle.api.resources.internal.ReadableResourceInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReadableResourceInternal.java:0)
+Class <org.gradle.api.resources.internal.TextResourceInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextResourceInternal.java:0)
+Class <org.gradle.api.specs.internal.ClosureSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureSpec.java:0)
+Class <org.gradle.api.tasks.bundling.internal.Zip64RequiredException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Zip64RequiredException.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.dependencies.AsciiDependencyReportRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsciiDependencyReportRenderer.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.dependencies.AsciiDependencyReportRenderer$ConfigurationDetailsAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsciiDependencyReportRenderer.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.dependencies.AttributeMatchDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AttributeMatchDetails.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.dependencies.MatchType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MatchType.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.dsl.DependencyResultSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResultSpec.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.dsl.DependencyResultSpecNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResultSpecNotationConverter.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.graph.DependencyGraphsRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyGraphsRenderer.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.graph.LegendRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegendRenderer.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.graph.NodeRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeRenderer.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.graph.SimpleNodeRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleNodeRenderer.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.graph.SimpleNodeRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleNodeRenderer.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.insight.DependencyInsightReporter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyInsightReporter.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.insight.DependencyInsightReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyInsightReporter.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.insight.DependencyResultSorter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResultSorter.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.insight.DependencyResultSorter$DependencyComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResultSorter.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.insight.DependencyResultSorter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResultSorter.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.text.DefaultTextReportBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTextReportBuilder.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.text.StyledTable$Renderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTable.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.text.StyledTable$Row> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTable.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.text.StyledTable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTable.java:0)
+Class <org.gradle.api.tasks.diagnostics.internal.text.TextReportBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextReportBuilder.java:0)
+Class <org.gradle.api.tasks.internal.JavaExecExecutableUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaExecExecutableUtils.java:0)
+Class <org.gradle.api.tasks.scala.internal.GenerateScaladoc> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GenerateScaladoc.java:0)
+Class <org.gradle.api.tasks.scala.internal.ScalaCompileOptionsConfigurer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalaCompileOptionsConfigurer.java:0)
+Class <org.gradle.api.tasks.scala.internal.ScalaRuntimeHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalaRuntimeHelper.java:0)
+Class <org.gradle.api.tasks.scala.internal.ScaladocParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScaladocParameters.java:0)
+Class <org.gradle.api.tasks.util.internal.CachingPatternSpecFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingPatternSpecFactory.java:0)
+Class <org.gradle.api.tasks.util.internal.CachingPatternSpecFactory$CachingSpec$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingPatternSpecFactory.java:0)
+Class <org.gradle.api.tasks.util.internal.CachingPatternSpecFactory$CachingSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingPatternSpecFactory.java:0)
+Class <org.gradle.api.tasks.util.internal.CachingPatternSpecFactory$SpecKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingPatternSpecFactory.java:0)
+Class <org.gradle.api.tasks.util.internal.CachingPatternSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingPatternSpecFactory.java:0)
+Class <org.gradle.api.tasks.util.internal.IntersectionPatternSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IntersectionPatternSet.java:0)
+Class <org.gradle.api.tasks.util.internal.IntersectionPatternSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IntersectionPatternSet.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSetAntBuilderDelegate$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSetAntBuilderDelegate.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSetAntBuilderDelegate$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSetAntBuilderDelegate.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSetAntBuilderDelegate$2$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSetAntBuilderDelegate.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSetAntBuilderDelegate$2$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSetAntBuilderDelegate.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSetAntBuilderDelegate$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSetAntBuilderDelegate.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSetAntBuilderDelegate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSetAntBuilderDelegate.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSets$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSets.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSets$InternalPatternSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSets.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSets$PatternSetFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSets.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSets> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSets.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSpecFactory$CaseSensitivity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSpecFactory.java:0)
+Class <org.gradle.api.tasks.util.internal.PatternSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternSpecFactory.java:0)
+Class <org.gradle.architecture.library.freeze.LineNumberIgnoringViolationStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LineNumberIgnoringViolationStore.java:0)
+Class <org.gradle.buildinit.plugins.internal.BasicProjectGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BasicProjectGenerator.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildContentGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildContentGenerator.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildConverter.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildInitException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInitException.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInitializer.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$AbstractStatement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$BlockBody> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$BlockStatement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ChainedPropertyExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ConfigSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ConfigurationStatements> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ContainerElement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ContainerElementExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ConventionSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$DepSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$DependenciesBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$EnumValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$Expression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ExpressionValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$GroovySyntax> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$KotlinSyntax> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$LiteralValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MapLiteralValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MavenRepoExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MavenRepositoryURLHandler$AbstractMavenRepositoryURLHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MavenRepositoryURLHandler$AllowingHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MavenRepositoryURLHandler$FailingHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MavenRepositoryURLHandler$UpgradingHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MavenRepositoryURLHandler$WarningHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MavenRepositoryURLHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MethodInvocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$MethodInvocationExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$NoArgClosureExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$PlatformDepSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$PluginSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$PrettyPrinter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ProjectDepSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$PropertyAssignment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$RepositoriesBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ScriptBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$ScriptBlockImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$SelfDepSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$SingleLineComment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$Statement$Type> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$Statement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$StringValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$SuiteConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$SuiteRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$SuiteSpec$TestSuiteFramework> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$SuiteSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$Syntax> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$TargetSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$TargetsBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$TaskConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$TaskRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$TaskSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$TaskTypeSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$TestingBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder$TopLevelBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.BuildScriptBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptBuilderFactory.java:0)
+Class <org.gradle.buildinit.plugins.internal.CompositeProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.ConditionalTemplateOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConditionalTemplateOperation.java:0)
+Class <org.gradle.buildinit.plugins.internal.CppApplicationProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppApplicationProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.CppLibraryProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppLibraryProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.CppProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.DefaultTemplateLibraryVersionProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTemplateLibraryVersionProvider.java:0)
+Class <org.gradle.buildinit.plugins.internal.DependenciesBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependenciesBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.GitAttributesGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GitAttributesGenerator.java:0)
+Class <org.gradle.buildinit.plugins.internal.GitIgnoreGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GitIgnoreGenerator.java:0)
+Class <org.gradle.buildinit.plugins.internal.GradlePropertiesGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlePropertiesGenerator.java:0)
+Class <org.gradle.buildinit.plugins.internal.GroovyGradlePluginProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroovyGradlePluginProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.InitSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitSettings.java:0)
+Class <org.gradle.buildinit.plugins.internal.JavaGradlePluginProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaGradlePluginProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.JvmApplicationProjectInitDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmApplicationProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.JvmApplicationProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmApplicationProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.JvmGradlePluginProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmGradlePluginProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.JvmLibraryProjectInitDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmLibraryProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.JvmLibraryProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmLibraryProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.JvmProjectInitDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.JvmProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.KotlinGradlePluginProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KotlinGradlePluginProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.LanguageLibraryProjectInitDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LanguageLibraryProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.LanguageLibraryProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LanguageLibraryProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.LanguageSpecificAdaptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LanguageSpecificAdaptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.LanguageSpecificProjectGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LanguageSpecificProjectGenerator.java:0)
+Class <org.gradle.buildinit.plugins.internal.ModuleNameBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleNameBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.NamespaceBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NamespaceBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.PackageNameBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PackageNameBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.ProjectGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectGenerator.java:0)
+Class <org.gradle.buildinit.plugins.internal.ProjectLayoutSetupRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLayoutSetupRegistry.java:0)
+Class <org.gradle.buildinit.plugins.internal.RepositoriesBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RepositoriesBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.ResourceDirsGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceDirsGenerator.java:0)
+Class <org.gradle.buildinit.plugins.internal.ScriptBlockBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptBlockBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.SimpleGlobalFilesBuildSettingsDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleGlobalFilesBuildSettingsDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.SimpleTemplateOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleTemplateOperation.java:0)
+Class <org.gradle.buildinit.plugins.internal.SwiftApplicationProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftApplicationProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.SwiftLibraryProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftLibraryProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.SwiftProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.TargetsBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TargetsBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.TemplateFactory$SourceFileTemplate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemplateFactory.java:0)
+Class <org.gradle.buildinit.plugins.internal.TemplateFactory$TemplateDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemplateFactory.java:0)
+Class <org.gradle.buildinit.plugins.internal.TemplateFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemplateFactory.java:0)
+Class <org.gradle.buildinit.plugins.internal.TemplateLibraryVersionProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemplateLibraryVersionProvider.java:0)
+Class <org.gradle.buildinit.plugins.internal.TemplateOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemplateOperation.java:0)
+Class <org.gradle.buildinit.plugins.internal.TemplateOperationFactory$TemplateOperationBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemplateOperationFactory.java:0)
+Class <org.gradle.buildinit.plugins.internal.TemplateOperationFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemplateOperationFactory.java:0)
+Class <org.gradle.buildinit.plugins.internal.TemplateValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TemplateValue.java:0)
+Class <org.gradle.buildinit.plugins.internal.TestingBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestingBuilder.java:0)
+Class <org.gradle.buildinit.plugins.internal.action.BuildInitAutoApplyAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInitAutoApplyAction.java:0)
+Class <org.gradle.buildinit.plugins.internal.action.InitBuiltInCommand> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitBuiltInCommand.java:0)
+Class <org.gradle.buildinit.plugins.internal.action.WrapperPluginAutoApplyAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WrapperPluginAutoApplyAction.java:0)
+Class <org.gradle.buildinit.plugins.internal.maven.PomProjectInitDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PomProjectInitDescriptor.java:0)
+Class <org.gradle.buildinit.plugins.internal.model.Description> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Description.java:0)
+Class <org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInitDsl.java:0)
+Class <org.gradle.buildinit.plugins.internal.modifiers.BuildInitTestFramework> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInitTestFramework.java:0)
+Class <org.gradle.buildinit.plugins.internal.modifiers.ComponentType$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentType.java:0)
+Class <org.gradle.buildinit.plugins.internal.modifiers.ComponentType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentType.java:0)
+Class <org.gradle.buildinit.plugins.internal.modifiers.Language> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Language.java:0)
+Class <org.gradle.buildinit.plugins.internal.modifiers.ModularizationOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModularizationOption.java:0)
+Class <org.gradle.buildinit.plugins.internal.modifiers.Names> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Names.java:0)
+Class <org.gradle.buildinit.plugins.internal.modifiers.WithIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WithIdentifier.java:0)
+Class <org.gradle.buildinit.plugins.internal.services.BuildInitServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInitServices.java:0)
+Class <org.gradle.buildinit.plugins.internal.services.BuildInitServices$ProjectScopeBuildInitServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInitServices.java:0)
+Class <org.gradle.buildinit.plugins.internal.services.BuildInitServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInitServices.java:0)
+Class <org.gradle.buildinit.plugins.internal.services.ProjectLayoutSetupRegistryFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLayoutSetupRegistryFactory.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$DataBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$DataBlockUpdateResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$HeaderBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$IndexBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$IndexEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$IndexRoot> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache$Lookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.BTreePersistentIndexedCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BTreePersistentIndexedCache.java:0)
+Class <org.gradle.cache.internal.btree.Block> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Block.java:0)
+Class <org.gradle.cache.internal.btree.BlockPayload> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BlockPayload.java:0)
+Class <org.gradle.cache.internal.btree.BlockPointer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BlockPointer.java:0)
+Class <org.gradle.cache.internal.btree.BlockStore$Factory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BlockStore.java:0)
+Class <org.gradle.cache.internal.btree.BlockStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BlockStore.java:0)
+Class <org.gradle.cache.internal.btree.ByteInput$ResettableBufferedInputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ByteInput.java:0)
+Class <org.gradle.cache.internal.btree.ByteInput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ByteInput.java:0)
+Class <org.gradle.cache.internal.btree.ByteOutput$ResettableBufferedOutputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ByteOutput.java:0)
+Class <org.gradle.cache.internal.btree.ByteOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ByteOutput.java:0)
+Class <org.gradle.cache.internal.btree.CachingBlockStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.CorruptedCacheException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CorruptedCacheException.java:0)
+Class <org.gradle.cache.internal.btree.FileBackedBlockStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileBackedBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.FileBackedBlockStore$BlockImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileBackedBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.FileBackedBlockStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileBackedBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.FreeListBlockStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FreeListBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.FreeListBlockStore$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FreeListBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.FreeListBlockStore$FreeListBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FreeListBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.FreeListBlockStore$FreeListEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FreeListBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.FreeListBlockStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FreeListBlockStore.java:0)
+Class <org.gradle.cache.internal.btree.KeyHasher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KeyHasher.java:0)
+Class <org.gradle.cache.internal.btree.KeyHasher$MessageDigestStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KeyHasher.java:0)
+Class <org.gradle.cache.internal.btree.KeyHasher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KeyHasher.java:0)
+Class <org.gradle.cache.internal.btree.StateCheckBlockStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateCheckBlockStore.java:0)
+Class <org.gradle.cache.internal.cacheops.CacheAccessOperationsStack> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CacheAccessOperationsStack.java:0)
+Class <org.gradle.cache.internal.cacheops.CacheOperationStack> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CacheOperationStack.java:0)
+Class <org.gradle.cache.internal.filelock.DefaultLockStateSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLockStateSerializer.java:0)
+Class <org.gradle.cache.internal.filelock.DefaultLockStateSerializer$SequenceNumberLockState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLockStateSerializer.java:0)
+Class <org.gradle.cache.internal.filelock.DefaultLockStateSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLockStateSerializer.java:0)
+Class <org.gradle.cache.internal.filelock.FileLockOutcome$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileLockOutcome.java:0)
+Class <org.gradle.cache.internal.filelock.FileLockOutcome$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileLockOutcome.java:0)
+Class <org.gradle.cache.internal.filelock.FileLockOutcome$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileLockOutcome.java:0)
+Class <org.gradle.cache.internal.filelock.FileLockOutcome> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileLockOutcome.java:0)
+Class <org.gradle.cache.internal.filelock.LockFileAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockFileAccess.java:0)
+Class <org.gradle.cache.internal.filelock.LockInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockInfo.java:0)
+Class <org.gradle.cache.internal.filelock.LockInfoAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockInfoAccess.java:0)
+Class <org.gradle.cache.internal.filelock.LockInfoSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockInfoSerializer.java:0)
+Class <org.gradle.cache.internal.filelock.LockOptionsBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockOptionsBuilder.java:0)
+Class <org.gradle.cache.internal.filelock.LockState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockState.java:0)
+Class <org.gradle.cache.internal.filelock.LockStateAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockStateAccess.java:0)
+Class <org.gradle.cache.internal.filelock.LockStateSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockStateSerializer.java:0)
+Class <org.gradle.cache.internal.filelock.Version1LockStateSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Version1LockStateSerializer.java:0)
+Class <org.gradle.cache.internal.filelock.Version1LockStateSerializer$DirtyFlagLockState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Version1LockStateSerializer.java:0)
+Class <org.gradle.cache.internal.filelock.Version1LockStateSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Version1LockStateSerializer.java:0)
+Class <org.gradle.cache.internal.locklistener.DefaultFileLockContentionHandler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileLockContentionHandler.java:0)
+Class <org.gradle.cache.internal.locklistener.DefaultFileLockContentionHandler$ContendedAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileLockContentionHandler.java:0)
+Class <org.gradle.cache.internal.locklistener.DefaultFileLockContentionHandler$ContendedAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileLockContentionHandler.java:0)
+Class <org.gradle.cache.internal.locklistener.DefaultFileLockContentionHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileLockContentionHandler.java:0)
+Class <org.gradle.cache.internal.locklistener.FileLockCommunicator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileLockCommunicator.java:0)
+Class <org.gradle.cache.internal.locklistener.FileLockContentionHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileLockContentionHandler.java:0)
+Class <org.gradle.cache.internal.locklistener.FileLockPacketPayload> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileLockPacketPayload.java:0)
+Class <org.gradle.cache.internal.locklistener.FileLockPacketType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileLockPacketType.java:0)
+Class <org.gradle.cache.internal.locklistener.GracefullyStoppedException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GracefullyStoppedException.java:0)
+Class <org.gradle.cache.internal.scopes.AbstractScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.internal.scopes.DefaultBuildScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.internal.scopes.DefaultBuildTreeScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.internal.scopes.DefaultCacheScopeMapping$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCacheScopeMapping.java:0)
+Class <org.gradle.cache.internal.scopes.DefaultCacheScopeMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCacheScopeMapping.java:0)
+Class <org.gradle.cache.internal.scopes.DefaultGlobalScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGlobalScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.internal.scopes.DefaultProjectScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.internal.streams.BlockAddress> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BlockAddress.java:0)
+Class <org.gradle.cache.internal.streams.BlockAddressSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BlockAddressSerializer.java:0)
+Class <org.gradle.cache.internal.streams.DefaultValueStore$BlockInputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultValueStore.java:0)
+Class <org.gradle.cache.internal.streams.DefaultValueStore$Sink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultValueStore.java:0)
+Class <org.gradle.cache.internal.streams.DefaultValueStore$Source> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultValueStore.java:0)
+Class <org.gradle.cache.internal.streams.DefaultValueStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultValueStore.java:0)
+Class <org.gradle.cache.internal.streams.ValueStore$Reader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValueStore.java:0)
+Class <org.gradle.cache.internal.streams.ValueStore$Writer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValueStore.java:0)
+Class <org.gradle.cache.internal.streams.ValueStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValueStore.java:0)
+Class <org.gradle.cache.scopes.BuildScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.scopes.BuildTreeScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GlobalScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.scopes.ProjectScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cache.scopes.ScopedCacheBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScopedCacheBuilderFactory.java:0)
+Class <org.gradle.cli.AbstractCommandLineConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractCommandLineConverter.java:0)
+Class <org.gradle.cli.AbstractPropertiesCommandLineConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPropertiesCommandLineConverter.java:0)
+Class <org.gradle.cli.CommandLineArgumentException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineArgumentException.java:0)
+Class <org.gradle.cli.CommandLineConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineConverter.java:0)
+Class <org.gradle.cli.CommandLineOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineOption.java:0)
+Class <org.gradle.cli.CommandLineParser$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$AfterFirstSubCommand> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$AfterOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$BeforeFirstSubCommand> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$CaseInsensitiveStringComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$KnownOptionParserState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$MissingOptionArgState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$OptionAwareParserState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$OptionComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$OptionParserState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$OptionString> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$OptionStringComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$ParserState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser$UnknownOptionParserState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.CommandLineParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineParser.java:0)
+Class <org.gradle.cli.ParsedCommandLine> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ParsedCommandLine.java:0)
+Class <org.gradle.cli.ParsedCommandLineOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ParsedCommandLineOption.java:0)
+Class <org.gradle.cli.ProjectPropertiesCommandLineConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectPropertiesCommandLineConverter.java:0)
+Class <org.gradle.cli.SystemPropertiesCommandLineConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPropertiesCommandLineConverter.java:0)
+Class <org.gradle.composite.internal.AbstractCompositeParticipantBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractCompositeParticipantBuildState.java:0)
+Class <org.gradle.composite.internal.BuildController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildController.java:0)
+Class <org.gradle.composite.internal.BuildControllers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildControllers.java:0)
+Class <org.gradle.composite.internal.BuildStateFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildStateFactory.java:0)
+Class <org.gradle.composite.internal.BuildTreeWorkGraphController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeWorkGraphController.java:0)
+Class <org.gradle.composite.internal.CompositeBuildClassPathInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildClassPathInitializer.java:0)
+Class <org.gradle.composite.internal.CompositeBuildDependencySubstitutions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildDependencySubstitutions.java:0)
+Class <org.gradle.composite.internal.CompositeBuildServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildServices.java:0)
+Class <org.gradle.composite.internal.CompositeBuildServices$CompositeBuildSessionScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildServices.java:0)
+Class <org.gradle.composite.internal.CompositeBuildServices$CompositeBuildTreeScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildServices.java:0)
+Class <org.gradle.composite.internal.CompositeBuildServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildServices.java:0)
+Class <org.gradle.composite.internal.CompositeBuildsValueSnapshotterSerializerRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildsValueSnapshotterSerializerRegistry.java:0)
+Class <org.gradle.composite.internal.CompositeProjectComponentArtifactMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeProjectComponentArtifactMetadata.java:0)
+Class <org.gradle.composite.internal.CompositeProjectComponentArtifactMetadataSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeProjectComponentArtifactMetadataSerializer.java:0)
+Class <org.gradle.composite.internal.DefaultBuildController$BuildOpRunnable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildController.java:0)
+Class <org.gradle.composite.internal.DefaultBuildController$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildController.java:0)
+Class <org.gradle.composite.internal.DefaultBuildController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildController.java:0)
+Class <org.gradle.composite.internal.DefaultBuildControllers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildControllers.java:0)
+Class <org.gradle.composite.internal.DefaultBuildableCompositeBuildContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableCompositeBuildContext.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuild$IncludedBuildImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuild.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuild.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildFactory.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildRegistry.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildTaskGraph.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph$1$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildTaskGraph.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildTaskGraph.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildTaskGraph.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$DefaultBuildTreeWorkGraphBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildTaskGraph.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildTaskGraph.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildTaskGraph$TaskBackedResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildTaskGraph.java:0)
+Class <org.gradle.composite.internal.DefaultIncludedBuildTaskGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludedBuildTaskGraph.java:0)
+Class <org.gradle.composite.internal.DefaultLocalComponentInAnotherBuildProvider$MetadataSupplier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocalComponentInAnotherBuildProvider.java:0)
+Class <org.gradle.composite.internal.DefaultLocalComponentInAnotherBuildProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocalComponentInAnotherBuildProvider.java:0)
+Class <org.gradle.composite.internal.DefaultNestedBuild$DoNothingBuildFinishExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNestedBuild.java:0)
+Class <org.gradle.composite.internal.DefaultNestedBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNestedBuild.java:0)
+Class <org.gradle.composite.internal.DefaultNestedBuildTree> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNestedBuildTree.java:0)
+Class <org.gradle.composite.internal.DefaultRootBuildState$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRootBuildState.java:0)
+Class <org.gradle.composite.internal.DefaultRootBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRootBuildState.java:0)
+Class <org.gradle.composite.internal.IncludedBuildDependencyMetadataBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildDependencyMetadataBuilder.java:0)
+Class <org.gradle.composite.internal.IncludedBuildDependencySubstitutionsBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildDependencySubstitutionsBuilder.java:0)
+Class <org.gradle.composite.internal.IncludedBuildTaskReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildTaskReference.java:0)
+Class <org.gradle.composite.internal.IncludedBuildTaskResource$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildTaskResource.java:0)
+Class <org.gradle.composite.internal.IncludedBuildTaskResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildTaskResource.java:0)
+Class <org.gradle.composite.internal.RootOfNestedBuildTree$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RootOfNestedBuildTree.java:0)
+Class <org.gradle.composite.internal.RootOfNestedBuildTree$1$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RootOfNestedBuildTree.java:0)
+Class <org.gradle.composite.internal.RootOfNestedBuildTree$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RootOfNestedBuildTree.java:0)
+Class <org.gradle.composite.internal.RootOfNestedBuildTree> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RootOfNestedBuildTree.java:0)
+Class <org.gradle.composite.internal.TaskIdentifier$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskIdentifier.java:0)
+Class <org.gradle.composite.internal.TaskIdentifier$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskIdentifier.java:0)
+Class <org.gradle.composite.internal.TaskIdentifier$TaskBasedTaskIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskIdentifier.java:0)
+Class <org.gradle.composite.internal.TaskIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskIdentifier.java:0)
+Class <org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildPluginResolverContributor.java:0)
+Class <org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor$CompositeBuildPluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildPluginResolverContributor.java:0)
+Class <org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor$PluginResult$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildPluginResolverContributor.java:0)
+Class <org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor$PluginResult$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildPluginResolverContributor.java:0)
+Class <org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor$PluginResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildPluginResolverContributor.java:0)
+Class <org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor$ResolvedPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildPluginResolverContributor.java:0)
+Class <org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildPluginResolverContributor.java:0)
+Class <org.gradle.composite.internal.plugins.LocalPluginResolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalPluginResolution.java:0)
+Class <org.gradle.configuration.ApplyScriptPluginBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApplyScriptPluginBuildOperationType.java:0)
+Class <org.gradle.configuration.ApplyScriptPluginBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApplyScriptPluginBuildOperationType.java:0)
+Class <org.gradle.configuration.ApplyScriptPluginBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApplyScriptPluginBuildOperationType.java:0)
+Class <org.gradle.configuration.BuildOperationFiringProjectsPreparer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringProjectsPreparer.java:0)
+Class <org.gradle.configuration.BuildOperationFiringProjectsPreparer$ConfigureBuild$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringProjectsPreparer.java:0)
+Class <org.gradle.configuration.BuildOperationFiringProjectsPreparer$ConfigureBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringProjectsPreparer.java:0)
+Class <org.gradle.configuration.BuildOperationFiringProjectsPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringProjectsPreparer.java:0)
+Class <org.gradle.configuration.BuildOperationScriptPlugin$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationScriptPlugin.java:0)
+Class <org.gradle.configuration.BuildOperationScriptPlugin$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationScriptPlugin.java:0)
+Class <org.gradle.configuration.BuildOperationScriptPlugin$OperationDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationScriptPlugin.java:0)
+Class <org.gradle.configuration.BuildOperationScriptPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationScriptPlugin.java:0)
+Class <org.gradle.configuration.BuildTreePreparingProjectsPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreePreparingProjectsPreparer.java:0)
+Class <org.gradle.configuration.CompileOperationFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileOperationFactory.java:0)
+Class <org.gradle.configuration.ConfigurationTargetIdentifier$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationTargetIdentifier.java:0)
+Class <org.gradle.configuration.ConfigurationTargetIdentifier$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationTargetIdentifier.java:0)
+Class <org.gradle.configuration.ConfigurationTargetIdentifier$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationTargetIdentifier.java:0)
+Class <org.gradle.configuration.ConfigurationTargetIdentifier$Type> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationTargetIdentifier.java:0)
+Class <org.gradle.configuration.ConfigurationTargetIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationTargetIdentifier.java:0)
+Class <org.gradle.configuration.DefaultImportsReader$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultImportsReader.java:0)
+Class <org.gradle.configuration.DefaultImportsReader$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultImportsReader.java:0)
+Class <org.gradle.configuration.DefaultImportsReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultImportsReader.java:0)
+Class <org.gradle.configuration.DefaultInitScriptProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInitScriptProcessor.java:0)
+Class <org.gradle.configuration.DefaultProjectsPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectsPreparer.java:0)
+Class <org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptPluginFactory.java:0)
+Class <org.gradle.configuration.DefaultScriptPluginFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptPluginFactory.java:0)
+Class <org.gradle.configuration.DefaultScriptTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptTarget.java:0)
+Class <org.gradle.configuration.GradleLauncherMetaData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleLauncherMetaData.java:0)
+Class <org.gradle.configuration.Help> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Help.java:0)
+Class <org.gradle.configuration.ImportsReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImportsReader.java:0)
+Class <org.gradle.configuration.InitScriptProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitScriptProcessor.java:0)
+Class <org.gradle.configuration.InitScriptTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitScriptTarget.java:0)
+Class <org.gradle.configuration.InitialPassSettingScriptTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitialPassSettingScriptTarget.java:0)
+Class <org.gradle.configuration.ProjectScriptTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectScriptTarget.java:0)
+Class <org.gradle.configuration.ProjectsPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectsPreparer.java:0)
+Class <org.gradle.configuration.ScriptPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptPlugin.java:0)
+Class <org.gradle.configuration.ScriptPluginFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptPluginFactory.java:0)
+Class <org.gradle.configuration.ScriptPluginFactorySelector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptPluginFactorySelector.java:0)
+Class <org.gradle.configuration.ScriptPluginFactorySelector$ProviderInstantiator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptPluginFactorySelector.java:0)
+Class <org.gradle.configuration.ScriptPluginFactorySelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptPluginFactorySelector.java:0)
+Class <org.gradle.configuration.ScriptTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptTarget.java:0)
+Class <org.gradle.configuration.SettingScriptTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SettingScriptTarget.java:0)
+Class <org.gradle.configuration.TaskDetails$OptionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskDetails.java:0)
+Class <org.gradle.configuration.TaskDetailsModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskDetailsModel.java:0)
+Class <org.gradle.configuration.project.BuildScriptProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptProcessor.java:0)
+Class <org.gradle.configuration.project.BuiltInCommand> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuiltInCommand.java:0)
+Class <org.gradle.configuration.project.ConfigureActionsProjectEvaluator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureActionsProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.ConfigureProjectBuildOperationType$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureProjectBuildOperationType.java:0)
+Class <org.gradle.configuration.project.ConfigureProjectBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureProjectBuildOperationType.java:0)
+Class <org.gradle.configuration.project.ConfigureProjectBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureProjectBuildOperationType.java:0)
+Class <org.gradle.configuration.project.ConfigureProjectBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureProjectBuildOperationType.java:0)
+Class <org.gradle.configuration.project.DefaultCompileOperationFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCompileOperationFactory.java:0)
+Class <org.gradle.configuration.project.DefaultProjectConfigurationActionContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationActionContainer.java:0)
+Class <org.gradle.configuration.project.DelayedConfigurationActions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DelayedConfigurationActions.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator$ConfigureProjectDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyBeforeEvaluate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyProjectAfterEvaluatedDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyProjectBeforeEvaluatedDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.LifecycleProjectEvaluator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LifecycleProjectEvaluator.java:0)
+Class <org.gradle.configuration.project.NotifyProjectAfterEvaluatedBuildOperationType$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyProjectAfterEvaluatedBuildOperationType.java:0)
+Class <org.gradle.configuration.project.NotifyProjectAfterEvaluatedBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyProjectAfterEvaluatedBuildOperationType.java:0)
+Class <org.gradle.configuration.project.NotifyProjectAfterEvaluatedBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyProjectAfterEvaluatedBuildOperationType.java:0)
+Class <org.gradle.configuration.project.NotifyProjectAfterEvaluatedBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyProjectAfterEvaluatedBuildOperationType.java:0)
+Class <org.gradle.configuration.project.NotifyProjectBeforeEvaluatedBuildOperationType$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyProjectBeforeEvaluatedBuildOperationType.java:0)
+Class <org.gradle.configuration.project.NotifyProjectBeforeEvaluatedBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyProjectBeforeEvaluatedBuildOperationType.java:0)
+Class <org.gradle.configuration.project.NotifyProjectBeforeEvaluatedBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyProjectBeforeEvaluatedBuildOperationType.java:0)
+Class <org.gradle.configuration.project.NotifyProjectBeforeEvaluatedBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyProjectBeforeEvaluatedBuildOperationType.java:0)
+Class <org.gradle.configuration.project.PluginsProjectConfigureActions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginsProjectConfigureActions.java:0)
+Class <org.gradle.configuration.project.ProjectConfigurationActionContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectConfigurationActionContainer.java:0)
+Class <org.gradle.configuration.project.ProjectConfigureAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectConfigureAction.java:0)
+Class <org.gradle.configuration.project.ProjectEvaluator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectEvaluator.java:0)
+Class <org.gradle.execution.commandline.CommandLineTaskConfigurer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineTaskConfigurer.java:0)
+Class <org.gradle.execution.commandline.CommandLineTaskParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineTaskParser.java:0)
+Class <org.gradle.execution.commandline.TaskConfigurationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskConfigurationException.java:0)
+Class <org.gradle.execution.plan.edges.ComplexDependencyNodesSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComplexDependencyNodesSet.java:0)
+Class <org.gradle.execution.plan.edges.ComplexDependentNodesSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComplexDependentNodesSet.java:0)
+Class <org.gradle.execution.plan.edges.DependencyNodesSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyNodesSet.java:0)
+Class <org.gradle.execution.plan.edges.DependencyNodesSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyNodesSet.java:0)
+Class <org.gradle.execution.plan.edges.DependencyPredecessorsOnlyNodeSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyPredecessorsOnlyNodeSet.java:0)
+Class <org.gradle.execution.plan.edges.DependencySuccessorsOnlyNodeSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencySuccessorsOnlyNodeSet.java:0)
+Class <org.gradle.execution.plan.edges.DependentNodesSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentNodesSet.java:0)
+Class <org.gradle.execution.plan.edges.DependentNodesSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentNodesSet.java:0)
+Class <org.gradle.execution.selection.BuildTaskSelector$BuildSpecificSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTaskSelector.java:0)
+Class <org.gradle.execution.selection.BuildTaskSelector$Filter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTaskSelector.java:0)
+Class <org.gradle.execution.selection.BuildTaskSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTaskSelector.java:0)
+Class <org.gradle.execution.selection.DefaultBuildTaskSelector$LazyFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTaskSelector.java:0)
+Class <org.gradle.execution.selection.DefaultBuildTaskSelector$ProjectResolutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTaskSelector.java:0)
+Class <org.gradle.execution.selection.DefaultBuildTaskSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTaskSelector.java:0)
+Class <org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskExecutionGraph.java:0)
+Class <org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskExecutionGraph.java:0)
+Class <org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskExecutionGraph.java:0)
+Class <org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskExecutionGraph.java:0)
+Class <org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$NotifyTaskGraphWhenReady> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskExecutionGraph.java:0)
+Class <org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$NotifyTaskGraphWhenReadyDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskExecutionGraph.java:0)
+Class <org.gradle.execution.taskgraph.NotifyTaskGraphWhenReadyBuildOperationType$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyTaskGraphWhenReadyBuildOperationType.java:0)
+Class <org.gradle.execution.taskgraph.NotifyTaskGraphWhenReadyBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyTaskGraphWhenReadyBuildOperationType.java:0)
+Class <org.gradle.execution.taskgraph.NotifyTaskGraphWhenReadyBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyTaskGraphWhenReadyBuildOperationType.java:0)
+Class <org.gradle.execution.taskgraph.NotifyTaskGraphWhenReadyBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyTaskGraphWhenReadyBuildOperationType.java:0)
+Class <org.gradle.execution.taskgraph.TaskExecutionGraphInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionGraphInternal.java:0)
+Class <org.gradle.execution.taskgraph.TaskListenerInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskListenerInternal.java:0)
+Class <org.gradle.execution.taskpath.ResolvedTaskPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvedTaskPath.java:0)
+Class <org.gradle.external.javadoc.internal.AbstractJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.AbstractListJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractListJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.BooleanJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BooleanJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.EnumJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnumJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.FileJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.GroupsJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroupsJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.JavadocExecHandleBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavadocExecHandleBuilder.java:0)
+Class <org.gradle.external.javadoc.internal.JavadocOptionFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavadocOptionFile.java:0)
+Class <org.gradle.external.javadoc.internal.JavadocOptionFileOptionInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavadocOptionFileOptionInternal.java:0)
+Class <org.gradle.external.javadoc.internal.JavadocOptionFileOptionInternalAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavadocOptionFileOptionInternalAdapter.java:0)
+Class <org.gradle.external.javadoc.internal.JavadocOptionFileWriter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavadocOptionFileWriter.java:0)
+Class <org.gradle.external.javadoc.internal.JavadocOptionFileWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavadocOptionFileWriter.java:0)
+Class <org.gradle.external.javadoc.internal.JavadocOptionFileWriterContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavadocOptionFileWriterContext.java:0)
+Class <org.gradle.external.javadoc.internal.LinksOfflineJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LinksOfflineJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.MultilineMultiValueJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultilineMultiValueJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.MultilineStringsJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultilineStringsJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.OptionLessJavadocOptionFileOptionInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionLessJavadocOptionFileOptionInternal.java:0)
+Class <org.gradle.external.javadoc.internal.OptionLessStringsJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionLessStringsJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.PathJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PathJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.StringJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringJavadocOptionFileOption.java:0)
+Class <org.gradle.external.javadoc.internal.StringsJavadocOptionFileOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringsJavadocOptionFileOption.java:0)
+Class <org.gradle.groovy.scripts.BasicScript$ScriptDynamicObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BasicScript.java:0)
+Class <org.gradle.groovy.scripts.BasicScript> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BasicScript.java:0)
+Class <org.gradle.groovy.scripts.CachingScriptSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingScriptSource.java:0)
+Class <org.gradle.groovy.scripts.DefaultScript> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScript.java:0)
+Class <org.gradle.groovy.scripts.DefaultScriptCompilerFactory$ScriptCompilerImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilerFactory.java:0)
+Class <org.gradle.groovy.scripts.DefaultScriptCompilerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilerFactory.java:0)
+Class <org.gradle.groovy.scripts.DelegatingScriptSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DelegatingScriptSource.java:0)
+Class <org.gradle.groovy.scripts.Script> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Script.java:0)
+Class <org.gradle.groovy.scripts.ScriptCompilationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptCompilationException.java:0)
+Class <org.gradle.groovy.scripts.ScriptCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptCompiler.java:0)
+Class <org.gradle.groovy.scripts.ScriptCompilerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptCompilerFactory.java:0)
+Class <org.gradle.groovy.scripts.ScriptRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptRunner.java:0)
+Class <org.gradle.groovy.scripts.ScriptSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptSource.java:0)
+Class <org.gradle.groovy.scripts.TextResourceScriptSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextResourceScriptSource.java:0)
+Class <org.gradle.groovy.scripts.Transformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Transformer.java:0)
+Class <org.gradle.groovy.scripts.internal.AbstractScriptTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.AstUtils$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AstUtils.java:0)
+Class <org.gradle.groovy.scripts.internal.AstUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AstUtils.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildOperationBackedScriptCompilationHandler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationBackedScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildOperationBackedScriptCompilationHandler$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationBackedScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildOperationBackedScriptCompilationHandler$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationBackedScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildOperationBackedScriptCompilationHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationBackedScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildScopeInMemoryCachingScriptClassCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScopeInMemoryCachingScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildScriptData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptData.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildScriptDataSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptDataSerializer.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildScriptTransformer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.BuildScriptTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.CompileOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileOperation.java:0)
+Class <org.gradle.groovy.scripts.internal.CompiledScript> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompiledScript.java:0)
+Class <org.gradle.groovy.scripts.internal.CrossBuildInMemoryCachingScriptClassCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildInMemoryCachingScriptClassCache.java:0)
+Class <org.gradle.groovy.scripts.internal.CrossBuildInMemoryCachingScriptClassCache$CachedCompiledScript> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildInMemoryCachingScriptClassCache.java:0)
+Class <org.gradle.groovy.scripts.internal.CrossBuildInMemoryCachingScriptClassCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildInMemoryCachingScriptClassCache.java:0)
+Class <org.gradle.groovy.scripts.internal.CustomCompilationUnit$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CustomCompilationUnit.java:0)
+Class <org.gradle.groovy.scripts.internal.CustomCompilationUnit> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CustomCompilationUnit.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler$ClassesDirCompiledScript> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler$EmptyScriptDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler$InstrumentingScriptClassLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler$NoOpGroovyResourceLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler$PackageStatementDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler$ScriptClassLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptRunnerFactory.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptRunnerFactory.java:0)
+Class <org.gradle.groovy.scripts.internal.DefaultScriptSourceHasher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptSourceHasher.java:0)
+Class <org.gradle.groovy.scripts.internal.ExpressionReplacingVisitorSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExpressionReplacingVisitorSupport.java:0)
+Class <org.gradle.groovy.scripts.internal.FactoryBackedCompileOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FactoryBackedCompileOperation.java:0)
+Class <org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileCacheBackedScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$BuildScriptRemapper$MethodRenamer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileCacheBackedScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$BuildScriptRemapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileCacheBackedScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$CompileToCrossBuildCacheAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileCacheBackedScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$EmptyCompiledScript> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileCacheBackedScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$ProgressReportingInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileCacheBackedScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileCacheBackedScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.FilteringScriptTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FilteringScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.FixMainScriptTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FixMainScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.GradleResolveVisitor$ConstructedClassWithPackage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleResolveVisitor.java:0)
+Class <org.gradle.groovy.scripts.internal.GradleResolveVisitor$ConstructedNestedClass> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleResolveVisitor.java:0)
+Class <org.gradle.groovy.scripts.internal.GradleResolveVisitor$LowerCaseClass> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleResolveVisitor.java:0)
+Class <org.gradle.groovy.scripts.internal.GradleResolveVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleResolveVisitor.java:0)
+Class <org.gradle.groovy.scripts.internal.ImperativeStatementDetectingTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImperativeStatementDetectingTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.InitialPassStatementTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitialPassStatementTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.NoDataCompileOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoDataCompileOperation.java:0)
+Class <org.gradle.groovy.scripts.internal.Permits> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Permits.java:0)
+Class <org.gradle.groovy.scripts.internal.RegistryAwareClassLoaderHierarchyHasher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RegistryAwareClassLoaderHierarchyHasher.java:0)
+Class <org.gradle.groovy.scripts.internal.RemappingScriptSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RemappingScriptSource.java:0)
+Class <org.gradle.groovy.scripts.internal.RestrictiveCodeVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RestrictiveCodeVisitor.java:0)
+Class <org.gradle.groovy.scripts.internal.ScriptBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptBlock.java:0)
+Class <org.gradle.groovy.scripts.internal.ScriptCacheKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptCacheKey.java:0)
+Class <org.gradle.groovy.scripts.internal.ScriptClassCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptClassCompiler.java:0)
+Class <org.gradle.groovy.scripts.internal.ScriptCompilationHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptCompilationHandler.java:0)
+Class <org.gradle.groovy.scripts.internal.ScriptRunnerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptRunnerFactory.java:0)
+Class <org.gradle.groovy.scripts.internal.ScriptSourceHasher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptSourceHasher.java:0)
+Class <org.gradle.groovy.scripts.internal.ScriptSourceListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptSourceListener.java:0)
+Class <org.gradle.groovy.scripts.internal.StatementLabelsScriptTransformer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StatementLabelsScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.StatementLabelsScriptTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StatementLabelsScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.StatementReplacingVisitorSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StatementReplacingVisitorSupport.java:0)
+Class <org.gradle.groovy.scripts.internal.StatementTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StatementTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.SubsetScriptTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SubsetScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.TaskDefinitionScriptTransformer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskDefinitionScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.TaskDefinitionScriptTransformer$TaskDefinitionTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskDefinitionScriptTransformer.java:0)
+Class <org.gradle.groovy.scripts.internal.TaskDefinitionScriptTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskDefinitionScriptTransformer.java:0)
+Class <org.gradle.ide.visualstudio.internal.AbstractCppBinaryVisualStudioTargetBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractCppBinaryVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.CppApplicationVisualStudioTargetBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppApplicationVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.CppSharedLibraryVisualStudioTargetBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppSharedLibraryVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.CppStaticLibraryVisualStudioTargetBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppStaticLibraryVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.DefaultVisualStudioExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioExtension.java:0)
+Class <org.gradle.ide.visualstudio.internal.DefaultVisualStudioProject$DefaultConfigFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioProject.java:0)
+Class <org.gradle.ide.visualstudio.internal.DefaultVisualStudioProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioProject.java:0)
+Class <org.gradle.ide.visualstudio.internal.DefaultVisualStudioRootExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioRootExtension.java:0)
+Class <org.gradle.ide.visualstudio.internal.DefaultVisualStudioSolution$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioSolution.java:0)
+Class <org.gradle.ide.visualstudio.internal.DefaultVisualStudioSolution$SolutionFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioSolution.java:0)
+Class <org.gradle.ide.visualstudio.internal.DefaultVisualStudioSolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioSolution.java:0)
+Class <org.gradle.ide.visualstudio.internal.NativeSpecVisualStudioTargetBinary$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeSpecVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.NativeSpecVisualStudioTargetBinary$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeSpecVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.NativeSpecVisualStudioTargetBinary$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeSpecVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.NativeSpecVisualStudioTargetBinary$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeSpecVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.NativeSpecVisualStudioTargetBinary$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeSpecVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.NativeSpecVisualStudioTargetBinary$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeSpecVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.NativeSpecVisualStudioTargetBinary$LanguageSourceSetCollectionAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeSpecVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.NativeSpecVisualStudioTargetBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeSpecVisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioExtensionInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioExtensionInternal.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioProjectConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectConfiguration.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioProjectConfigurationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectConfigurationMetadata.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioProjectInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectInternal.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioProjectMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectMetadata.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioProjectRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectRegistry.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioProjectResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectResolver.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioSolutionInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionInternal.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioTargetBinary$LanguageStandard> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioTargetBinary$ProjectType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.internal.VisualStudioTargetBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioTargetBinary.java:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.RelativeFileNameTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RelativeFileNameTransformer.java:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioFiltersFile$_getFilters_closure1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioFiltersFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioFiltersFile$_getHeaders_closure3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioFiltersFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioFiltersFile$_getSources_closure2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioFiltersFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioFiltersFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioFiltersFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addConfiguration_closure6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addConfiguration_closure7$_closure12> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addConfiguration_closure7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addConfiguration_closure8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addConfiguration_closure9$_closure13> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addConfiguration_closure9> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addHeaderFile_closure5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addResource_closure4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_addSourceFile_closure3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_getConfigurations_closure10> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_setProjectUuid_closure1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_setSdkVersion_closure2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile$_toPath_closure11> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioProjectFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioProjectFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$SimpleTextProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_generateContent_closure3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_generateContent_closure4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_generateContent_closure5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_generateContent_closure6$_closure8$_closure10> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_generateContent_closure6$_closure8$_closure11> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_generateContent_closure6$_closure8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_generateContent_closure6$_closure9> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_generateContent_closure6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_setProjects_closure1$_closure7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_setProjects_closure1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile$_store_closure2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.visualstudio.tasks.internal.VisualStudioSolutionFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioSolutionFile.groovy:0)
+Class <org.gradle.ide.xcode.internal.DefaultXcodeExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultXcodeExtension.java:0)
+Class <org.gradle.ide.xcode.internal.DefaultXcodeProject$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultXcodeProject.java:0)
+Class <org.gradle.ide.xcode.internal.DefaultXcodeProject$Groups> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultXcodeProject.java:0)
+Class <org.gradle.ide.xcode.internal.DefaultXcodeProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultXcodeProject.java:0)
+Class <org.gradle.ide.xcode.internal.DefaultXcodeRootExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultXcodeRootExtension.java:0)
+Class <org.gradle.ide.xcode.internal.DefaultXcodeWorkspace> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultXcodeWorkspace.java:0)
+Class <org.gradle.ide.xcode.internal.XcodeBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeBinary.java:0)
+Class <org.gradle.ide.xcode.internal.XcodeProjectMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeProjectMetadata.java:0)
+Class <org.gradle.ide.xcode.internal.XcodePropertyAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodePropertyAdapter.java:0)
+Class <org.gradle.ide.xcode.internal.XcodeTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeTarget.java:0)
+Class <org.gradle.ide.xcode.internal.XcodeUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeUtils.java:0)
+Class <org.gradle.ide.xcode.internal.services.XcodeServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeServices.java:0)
+Class <org.gradle.ide.xcode.internal.services.XcodeServices$GlobalIdGeneratorServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeServices.java:0)
+Class <org.gradle.ide.xcode.internal.services.XcodeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeServices.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.FileTypes> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileTypes.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.GidGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GidGenerator.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXBuildFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXBuildFile.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXBuildPhase> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXBuildPhase.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXBuildStyle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXBuildStyle.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXContainer.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXContainerItem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXContainerItem.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXFileReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXFileReference.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXGroup$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXGroup.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXGroup$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXGroup.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXGroup$SortPolicy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXGroup.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXGroup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXGroup.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXLegacyTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXLegacyTarget.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXNativeTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXNativeTarget.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXObject.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXProject$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXProject.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXProject.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXProjectItem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXProjectItem.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXReference$SourceTree> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXReference.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXReference.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXShellScriptBuildPhase> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXShellScriptBuildPhase.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXSourcesBuildPhase> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXSourcesBuildPhase.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXTarget$ProductType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXTarget.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.PBXTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PBXTarget.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.XCBuildConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCBuildConfiguration.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.XCConfigurationList$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCConfigurationList.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.XCConfigurationList$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCConfigurationList.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.XCConfigurationList> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCConfigurationList.java:0)
+Class <org.gradle.ide.xcode.internal.xcodeproj.XcodeprojSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeprojSerializer.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeProjectFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeProjectFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$AnalyzeAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$ArchiveAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$BuildAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$BuildActionEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$BuildableReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$LaunchAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$ProfileAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$TestAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile$TestableEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeSchemeFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeWorkspaceFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeWorkspaceFile.java:0)
+Class <org.gradle.ide.xcode.tasks.internal.XcodeWorkspaceSettingsFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XcodeWorkspaceSettingsFile.java:0)
+Class <org.gradle.initialization.definition.DefaultInjectedPluginDependencies> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInjectedPluginDependencies.java:0)
+Class <org.gradle.initialization.definition.DefaultInjectedPluginDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInjectedPluginDependency.java:0)
+Class <org.gradle.initialization.definition.InjectedPluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InjectedPluginResolver.java:0)
+Class <org.gradle.initialization.exception.DefaultExceptionAnalyser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExceptionAnalyser.java:0)
+Class <org.gradle.initialization.exception.ExceptionAnalyser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionAnalyser.java:0)
+Class <org.gradle.initialization.exception.ExceptionCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionCollector.java:0)
+Class <org.gradle.initialization.exception.InitializationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitializationException.java:0)
+Class <org.gradle.initialization.exception.MultipleBuildFailuresExceptionAnalyser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultipleBuildFailuresExceptionAnalyser.java:0)
+Class <org.gradle.initialization.exception.StackTraceSanitizingExceptionAnalyser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StackTraceSanitizingExceptionAnalyser.java:0)
+Class <org.gradle.initialization.layout.BuildLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLayout.java:0)
+Class <org.gradle.initialization.layout.BuildLayoutConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLayoutConfiguration.java:0)
+Class <org.gradle.initialization.layout.BuildLayoutFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLayoutFactory.java:0)
+Class <org.gradle.initialization.layout.GlobalCacheDir> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GlobalCacheDir.java:0)
+Class <org.gradle.initialization.layout.ProjectCacheDir> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectCacheDir.java:0)
+Class <org.gradle.initialization.layout.ResolvedBuildLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolvedBuildLayout.java:0)
+Class <org.gradle.initialization.properties.DefaultProjectPropertiesLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectPropertiesLoader.java:0)
+Class <org.gradle.initialization.properties.DefaultSystemPropertiesInstaller> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSystemPropertiesInstaller.java:0)
+Class <org.gradle.initialization.properties.MutableGradleProperties> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MutableGradleProperties.java:0)
+Class <org.gradle.initialization.properties.ProjectPropertiesLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectPropertiesLoader.java:0)
+Class <org.gradle.initialization.properties.SystemPropertiesInstaller> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPropertiesInstaller.java:0)
+Class <org.gradle.internal.accesscontrol.AllowUsingApiForExternalUse> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllowUsingApiForExternalUse.java:0)
+Class <org.gradle.internal.accesscontrol.ForExternalUse> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForExternalUse.java:0)
+Class <org.gradle.internal.action.ConfigurableRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableRule.java:0)
+Class <org.gradle.internal.action.ConfigurableRules> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableRules.java:0)
+Class <org.gradle.internal.action.DefaultConfigurableRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConfigurableRule.java:0)
+Class <org.gradle.internal.action.DefaultConfigurableRules> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConfigurableRules.java:0)
+Class <org.gradle.internal.action.InstantiatingAction$ExceptionHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstantiatingAction.java:0)
+Class <org.gradle.internal.action.InstantiatingAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstantiatingAction.java:0)
+Class <org.gradle.internal.actor.Actor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Actor.java:0)
+Class <org.gradle.internal.actor.ActorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ActorFactory.java:0)
+Class <org.gradle.internal.actor.internal.DefaultActorFactory$BlockingActor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultActorFactory.java:0)
+Class <org.gradle.internal.actor.internal.DefaultActorFactory$NonBlockingActor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultActorFactory.java:0)
+Class <org.gradle.internal.actor.internal.DefaultActorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultActorFactory.java:0)
+Class <org.gradle.internal.agents.AgentControl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AgentControl.java:0)
+Class <org.gradle.internal.agents.AgentInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AgentInitializer.java:0)
+Class <org.gradle.internal.agents.AgentStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AgentStatus.java:0)
+Class <org.gradle.internal.agents.AgentUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AgentUtils.java:0)
+Class <org.gradle.internal.agents.DefaultAgentStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAgentStatus.java:0)
+Class <org.gradle.internal.agents.DefaultClassFileTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassFileTransformer.java:0)
+Class <org.gradle.internal.agents.DisabledAgentStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DisabledAgentStatus.java:0)
+Class <org.gradle.internal.agents.InstrumentingClassLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingClassLoader.java:0)
+Class <org.gradle.internal.artifacts.repositories.AuthenticationSupportedInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AuthenticationSupportedInternal.java:0)
+Class <org.gradle.internal.authentication.AbstractAuthentication$DefaultHostAndPort> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAuthentication.java:0)
+Class <org.gradle.internal.authentication.AbstractAuthentication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAuthentication.java:0)
+Class <org.gradle.internal.authentication.AllSchemesAuthentication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllSchemesAuthentication.java:0)
+Class <org.gradle.internal.authentication.AuthenticationInternal$HostAndPort> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AuthenticationInternal.java:0)
+Class <org.gradle.internal.authentication.AuthenticationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AuthenticationInternal.java:0)
+Class <org.gradle.internal.authentication.AuthenticationSchemeRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AuthenticationSchemeRegistry.java:0)
+Class <org.gradle.internal.authentication.DefaultAuthenticationContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAuthenticationContainer.java:0)
+Class <org.gradle.internal.authentication.DefaultAuthenticationSchemeRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAuthenticationSchemeRegistry.java:0)
+Class <org.gradle.internal.authentication.DefaultAwsImAuthentication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAwsImAuthentication.java:0)
+Class <org.gradle.internal.authentication.DefaultBasicAuthentication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBasicAuthentication.java:0)
+Class <org.gradle.internal.authentication.DefaultDigestAuthentication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDigestAuthentication.java:0)
+Class <org.gradle.internal.authentication.DefaultHttpHeaderAuthentication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHttpHeaderAuthentication.java:0)
+Class <org.gradle.internal.build.AbstractBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractBuildState.java:0)
+Class <org.gradle.internal.build.BuildActionTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionTarget.java:0)
+Class <org.gradle.internal.build.BuildAddedListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildAddedListener.java:0)
+Class <org.gradle.internal.build.BuildIncluder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildIncluder.java:0)
+Class <org.gradle.internal.build.BuildLayoutValidator$BuildLayoutException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLayoutValidator.java:0)
+Class <org.gradle.internal.build.BuildLayoutValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLayoutValidator.java:0)
+Class <org.gradle.internal.build.BuildLifecycleController$WorkGraphBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLifecycleController.java:0)
+Class <org.gradle.internal.build.BuildLifecycleController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLifecycleController.java:0)
+Class <org.gradle.internal.build.BuildLifecycleControllerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLifecycleControllerFactory.java:0)
+Class <org.gradle.internal.build.BuildModelController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildModelController.java:0)
+Class <org.gradle.internal.build.BuildModelControllerServices$Supplier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildModelControllerServices.java:0)
+Class <org.gradle.internal.build.BuildModelControllerServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildModelControllerServices.java:0)
+Class <org.gradle.internal.build.BuildModelLifecycleListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildModelLifecycleListener.java:0)
+Class <org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringBuildWorkPreparer.java:0)
+Class <org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph$CalculateTaskGraphResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringBuildWorkPreparer.java:0)
+Class <org.gradle.internal.build.BuildOperationFiringBuildWorkPreparer$PopulateWorkGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringBuildWorkPreparer.java:0)
+Class <org.gradle.internal.build.BuildProjectRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildProjectRegistry.java:0)
+Class <org.gradle.internal.build.BuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildState.java:0)
+Class <org.gradle.internal.build.BuildStateRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildStateRegistry.java:0)
+Class <org.gradle.internal.build.BuildToolingModelController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildToolingModelController.java:0)
+Class <org.gradle.internal.build.BuildToolingModelControllerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildToolingModelControllerFactory.java:0)
+Class <org.gradle.internal.build.BuildWorkGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildWorkGraph.java:0)
+Class <org.gradle.internal.build.BuildWorkGraphController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildWorkGraphController.java:0)
+Class <org.gradle.internal.build.BuildWorkPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildWorkPreparer.java:0)
+Class <org.gradle.internal.build.CompositeBuildParticipantBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildParticipantBuildState.java:0)
+Class <org.gradle.internal.build.DefaultBuildLifecycleController$DefaultBuildWorkPlan> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLifecycleController.java:0)
+Class <org.gradle.internal.build.DefaultBuildLifecycleController$DefaultWorkGraphBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLifecycleController.java:0)
+Class <org.gradle.internal.build.DefaultBuildLifecycleController$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLifecycleController.java:0)
+Class <org.gradle.internal.build.DefaultBuildLifecycleController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLifecycleController.java:0)
+Class <org.gradle.internal.build.DefaultBuildLifecycleControllerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLifecycleControllerFactory.java:0)
+Class <org.gradle.internal.build.DefaultBuildToolingModelController$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildToolingModelController.java:0)
+Class <org.gradle.internal.build.DefaultBuildToolingModelController$AbstractToolingScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildToolingModelController.java:0)
+Class <org.gradle.internal.build.DefaultBuildToolingModelController$BuildToolingScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildToolingModelController.java:0)
+Class <org.gradle.internal.build.DefaultBuildToolingModelController$ProjectToolingScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildToolingModelController.java:0)
+Class <org.gradle.internal.build.DefaultBuildToolingModelController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildToolingModelController.java:0)
+Class <org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildWorkGraphController.java:0)
+Class <org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultExportedTaskNode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildWorkGraphController.java:0)
+Class <org.gradle.internal.build.DefaultBuildWorkGraphController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildWorkGraphController.java:0)
+Class <org.gradle.internal.build.DefaultBuildWorkPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildWorkPreparer.java:0)
+Class <org.gradle.internal.build.DefaultPublicBuildPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPublicBuildPath.java:0)
+Class <org.gradle.internal.build.ExecutionResult$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutionResult.java:0)
+Class <org.gradle.internal.build.ExecutionResult$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutionResult.java:0)
+Class <org.gradle.internal.build.ExecutionResult$Failure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutionResult.java:0)
+Class <org.gradle.internal.build.ExecutionResult$Success> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutionResult.java:0)
+Class <org.gradle.internal.build.ExecutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutionResult.java:0)
+Class <org.gradle.internal.build.ExportedTaskNode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExportedTaskNode.java:0)
+Class <org.gradle.internal.build.IncludedBuildFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildFactory.java:0)
+Class <org.gradle.internal.build.IncludedBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildState.java:0)
+Class <org.gradle.internal.build.NestedBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedBuildState.java:0)
+Class <org.gradle.internal.build.NestedRootBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedRootBuild.java:0)
+Class <org.gradle.internal.build.NestedRootBuildRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedRootBuildRunner.java:0)
+Class <org.gradle.internal.build.PlannedNodeGraph$Collector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlannedNodeGraph.java:0)
+Class <org.gradle.internal.build.PlannedNodeGraph$DependencyTraverser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlannedNodeGraph.java:0)
+Class <org.gradle.internal.build.PlannedNodeGraph$DetailLevel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlannedNodeGraph.java:0)
+Class <org.gradle.internal.build.PlannedNodeGraph$IdentityProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlannedNodeGraph.java:0)
+Class <org.gradle.internal.build.PlannedNodeGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlannedNodeGraph.java:0)
+Class <org.gradle.internal.build.PublicBuildPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicBuildPath.java:0)
+Class <org.gradle.internal.build.RootBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RootBuildState.java:0)
+Class <org.gradle.internal.build.StandAloneNestedBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StandAloneNestedBuild.java:0)
+Class <org.gradle.internal.build.event.BuildEventListenerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildEventListenerFactory.java:0)
+Class <org.gradle.internal.build.event.BuildEventListenerRegistryInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildEventListenerRegistryInternal.java:0)
+Class <org.gradle.internal.build.event.BuildEventServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildEventServices.java:0)
+Class <org.gradle.internal.build.event.BuildEventServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildEventServices.java:0)
+Class <org.gradle.internal.build.event.BuildEventSubscriptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildEventSubscriptions.java:0)
+Class <org.gradle.internal.build.event.DefaultBuildEventsListenerRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEventsListenerRegistry.java:0)
+Class <org.gradle.internal.build.event.DefaultBuildEventsListenerRegistry$AbstractListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEventsListenerRegistry.java:0)
+Class <org.gradle.internal.build.event.DefaultBuildEventsListenerRegistry$ForwardingBuildEventConsumer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEventsListenerRegistry.java:0)
+Class <org.gradle.internal.build.event.DefaultBuildEventsListenerRegistry$ForwardingBuildOperationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEventsListenerRegistry.java:0)
+Class <org.gradle.internal.build.event.DefaultBuildEventsListenerRegistry$ListenerCleanup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEventsListenerRegistry.java:0)
+Class <org.gradle.internal.build.event.DefaultBuildEventsListenerRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEventsListenerRegistry.java:0)
+Class <org.gradle.internal.build.event.OperationResultPostProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperationResultPostProcessor.java:0)
+Class <org.gradle.internal.build.event.OperationResultPostProcessorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperationResultPostProcessorFactory.java:0)
+Class <org.gradle.internal.build.event.types.AbstractOperationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractOperationResult.java:0)
+Class <org.gradle.internal.build.event.types.AbstractProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractProgressEvent.java:0)
+Class <org.gradle.internal.build.event.types.AbstractProjectConfigurationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractProjectConfigurationResult.java:0)
+Class <org.gradle.internal.build.event.types.AbstractResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractResult.java:0)
+Class <org.gradle.internal.build.event.types.AbstractTaskResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTaskResult.java:0)
+Class <org.gradle.internal.build.event.types.AbstractTestFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTestFailure.java:0)
+Class <org.gradle.internal.build.event.types.AbstractTestResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTestResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultAnnotationProcessorResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAnnotationProcessorResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultBinaryPluginIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBinaryPluginIdentifier.java:0)
+Class <org.gradle.internal.build.event.types.DefaultBuildPhaseDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildPhaseDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.DefaultFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFailure.java:0)
+Class <org.gradle.internal.build.event.types.DefaultFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFailureResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultFileDownloadDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileDownloadDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.DefaultFileDownloadFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileDownloadFailureResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultFileDownloadSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileDownloadSuccessResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultJavaCompileTaskSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaCompileTaskSuccessResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultOperationDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.DefaultOperationFinishedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultOperationFinishedProgressEvent.java:0)
+Class <org.gradle.internal.build.event.types.DefaultOperationStartedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultOperationStartedProgressEvent.java:0)
+Class <org.gradle.internal.build.event.types.DefaultPluginApplicationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginApplicationResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultProjectConfigurationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.DefaultProjectConfigurationFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationFailureResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultProjectConfigurationSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationSuccessResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultScriptPluginIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptPluginIdentifier.java:0)
+Class <org.gradle.internal.build.event.types.DefaultStatusEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStatusEvent.java:0)
+Class <org.gradle.internal.build.event.types.DefaultSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSuccessResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTaskDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTaskFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskFailureResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTaskFinishedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskFinishedProgressEvent.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTaskSkippedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskSkippedResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTaskStartedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskStartedProgressEvent.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTaskSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskSuccessResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestAssertionFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestAssertionFailure.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFailureResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestFinishedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFinishedProgressEvent.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestFrameworkFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFrameworkFailure.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestOutputDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestOutputDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestOutputEvent.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestOutputResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestOutputResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestSkippedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestSkippedResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestStartedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestStartedProgressEvent.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTestSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestSuccessResult.java:0)
+Class <org.gradle.internal.build.event.types.DefaultTransformDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTransformDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.DefaultWorkItemDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkItemDescriptor.java:0)
+Class <org.gradle.internal.build.event.types.NotFoundFileDownloadSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotFoundFileDownloadSuccessResult.java:0)
+Class <org.gradle.internal.buildevents.BuildExceptionReporter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildExceptionReporter.java:0)
+Class <org.gradle.internal.buildevents.BuildExceptionReporter$ContextImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildExceptionReporter.java:0)
+Class <org.gradle.internal.buildevents.BuildExceptionReporter$ExceptionFormattingVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildExceptionReporter.java:0)
+Class <org.gradle.internal.buildevents.BuildExceptionReporter$ExceptionStyle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildExceptionReporter.java:0)
+Class <org.gradle.internal.buildevents.BuildExceptionReporter$FailureDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildExceptionReporter.java:0)
+Class <org.gradle.internal.buildevents.BuildExceptionReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildExceptionReporter.java:0)
+Class <org.gradle.internal.buildevents.BuildLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLogger.java:0)
+Class <org.gradle.internal.buildevents.BuildLoggerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLoggerFactory.java:0)
+Class <org.gradle.internal.buildevents.BuildResultLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildResultLogger.java:0)
+Class <org.gradle.internal.buildevents.BuildStartedTime> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildStartedTime.java:0)
+Class <org.gradle.internal.buildevents.TaskExecutionStatisticsReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionStatisticsReporter.java:0)
+Class <org.gradle.internal.buildoption.AbstractBuildOption$OptionValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractBuildOption.java:0)
+Class <org.gradle.internal.buildoption.AbstractBuildOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractBuildOption.java:0)
+Class <org.gradle.internal.buildoption.BooleanBuildOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BooleanBuildOption.java:0)
+Class <org.gradle.internal.buildoption.BooleanCommandLineOptionConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BooleanCommandLineOptionConfiguration.java:0)
+Class <org.gradle.internal.buildoption.BuildOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOption.java:0)
+Class <org.gradle.internal.buildoption.BuildOptionSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOptionSet.java:0)
+Class <org.gradle.internal.buildoption.BuildOptionSet$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOptionSet.java:0)
+Class <org.gradle.internal.buildoption.BuildOptionSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOptionSet.java:0)
+Class <org.gradle.internal.buildoption.CommandLineOptionConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineOptionConfiguration.java:0)
+Class <org.gradle.internal.buildoption.DefaultFeatureFlags> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFeatureFlags.java:0)
+Class <org.gradle.internal.buildoption.DefaultInternalOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInternalOptions.java:0)
+Class <org.gradle.internal.buildoption.EnabledOnlyBooleanBuildOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnabledOnlyBooleanBuildOption.java:0)
+Class <org.gradle.internal.buildoption.EnumBuildOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnumBuildOption.java:0)
+Class <org.gradle.internal.buildoption.FeatureFlag> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureFlag.java:0)
+Class <org.gradle.internal.buildoption.FeatureFlagListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureFlagListener.java:0)
+Class <org.gradle.internal.buildoption.FeatureFlags> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureFlags.java:0)
+Class <org.gradle.internal.buildoption.IntegerBuildOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IntegerBuildOption.java:0)
+Class <org.gradle.internal.buildoption.IntegerInternalOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IntegerInternalOption.java:0)
+Class <org.gradle.internal.buildoption.InternalFlag> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalFlag.java:0)
+Class <org.gradle.internal.buildoption.InternalOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalOption.java:0)
+Class <org.gradle.internal.buildoption.InternalOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalOptions.java:0)
+Class <org.gradle.internal.buildoption.ListBuildOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ListBuildOption.java:0)
+Class <org.gradle.internal.buildoption.Option$Value$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Option.java:0)
+Class <org.gradle.internal.buildoption.Option$Value$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Option.java:0)
+Class <org.gradle.internal.buildoption.Option$Value> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Option.java:0)
+Class <org.gradle.internal.buildoption.Option> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Option.java:0)
+Class <org.gradle.internal.buildoption.Origin$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Origin.java:0)
+Class <org.gradle.internal.buildoption.Origin$CommandLineOrigin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Origin.java:0)
+Class <org.gradle.internal.buildoption.Origin$GradlePropertyOrigin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Origin.java:0)
+Class <org.gradle.internal.buildoption.Origin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Origin.java:0)
+Class <org.gradle.internal.buildoption.PropertiesConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertiesConverter.java:0)
+Class <org.gradle.internal.buildoption.StringBuildOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringBuildOption.java:0)
+Class <org.gradle.internal.buildoption.StringInternalOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringInternalOption.java:0)
+Class <org.gradle.internal.buildtree.BuildActionModelRequirements> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionModelRequirements.java:0)
+Class <org.gradle.internal.buildtree.BuildActionRunner$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionRunner.java:0)
+Class <org.gradle.internal.buildtree.BuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionRunner.java:0)
+Class <org.gradle.internal.buildtree.BuildInclusionCoordinator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInclusionCoordinator.java:0)
+Class <org.gradle.internal.buildtree.BuildModelParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildModelParameters.java:0)
+Class <org.gradle.internal.buildtree.BuildOperationFiringBuildTreeWorkExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringBuildTreeWorkExecutor.java:0)
+Class <org.gradle.internal.buildtree.BuildOperationFiringBuildTreeWorkExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationFiringBuildTreeWorkExecutor.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeActionExecutor.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeContext.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeFinishExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeFinishExecutor.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeLifecycleController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeLifecycleController.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeLifecycleControllerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeLifecycleControllerFactory.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeLifecycleListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeLifecycleListener.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeModelAction.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeModelController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeModelController.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeModelControllerServices$Supplier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeModelControllerServices.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeModelControllerServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeModelControllerServices.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeModelCreator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeModelCreator.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeScopeServices.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeState.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeWorkController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeWorkController.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeWorkExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeWorkExecutor.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeWorkGraph$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeWorkGraph.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeWorkGraph$FinalizedGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeWorkGraph.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeWorkGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeWorkGraph.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeWorkGraphPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeWorkGraphPreparer.java:0)
+Class <org.gradle.internal.buildtree.BuildTreeWorkPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeWorkPreparer.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeContext.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeFinishExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeFinishExecutor.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeLifecycleController.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeLifecycleController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeLifecycleController.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeModelCreator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeModelCreator.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeModelCreator$DefaultBuildTreeModelController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeModelCreator.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeModelCreator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeModelCreator.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeWorkExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeWorkExecutor.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeWorkGraphPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeWorkGraphPreparer.java:0)
+Class <org.gradle.internal.buildtree.DefaultBuildTreeWorkPreparer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTreeWorkPreparer.java:0)
+Class <org.gradle.internal.buildtree.DeprecationsReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DeprecationsReporter.java:0)
+Class <org.gradle.internal.buildtree.GlobalDependencySubstitutionRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GlobalDependencySubstitutionRegistry.java:0)
+Class <org.gradle.internal.buildtree.InitDeprecationLoggingActionExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitDeprecationLoggingActionExecutor.java:0)
+Class <org.gradle.internal.buildtree.InitDeprecationLoggingActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitDeprecationLoggingActionExecutor.java:0)
+Class <org.gradle.internal.buildtree.NestedBuildTree> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedBuildTree.java:0)
+Class <org.gradle.internal.buildtree.ProblemReportingBuildActionRunner$RootProjectBuildDirCollectingListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProblemReportingBuildActionRunner.java:0)
+Class <org.gradle.internal.buildtree.ProblemReportingBuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProblemReportingBuildActionRunner.java:0)
+Class <org.gradle.internal.buildtree.RunTasksRequirements> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunTasksRequirements.java:0)
+Class <org.gradle.internal.cache.MonitoredCleanupAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MonitoredCleanupAction.java:0)
+Class <org.gradle.internal.classanalysis.AsmConstants> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmConstants.java:0)
+Class <org.gradle.internal.classanalysis.JavaClassUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaClassUtil.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingEnvMap$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingEnvMap.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingEnvMap$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingEnvMap.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingEnvMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingEnvMap.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingProperties$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingProperties.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingProperties$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingProperties.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingProperties$Listener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingProperties.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingProperties$TrackingEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingProperties.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingProperties> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingProperties.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingSet$Listener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingSet.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingSet.java:0)
+Class <org.gradle.internal.classpath.AccessTrackingUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AccessTrackingUtils.java:0)
+Class <org.gradle.internal.classpath.CachedClasspathTransformer$StandardTransform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedClasspathTransformer.java:0)
+Class <org.gradle.internal.classpath.CachedClasspathTransformer$Transform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedClasspathTransformer.java:0)
+Class <org.gradle.internal.classpath.CachedClasspathTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedClasspathTransformer.java:0)
+Class <org.gradle.internal.classpath.ClassData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassData.java:0)
+Class <org.gradle.internal.classpath.ClassPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassPath.java:0)
+Class <org.gradle.internal.classpath.ClasspathBuilder$Action> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathBuilder.java:0)
+Class <org.gradle.internal.classpath.ClasspathBuilder$EntryBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathBuilder.java:0)
+Class <org.gradle.internal.classpath.ClasspathBuilder$ZipEntryBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathBuilder.java:0)
+Class <org.gradle.internal.classpath.ClasspathBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathBuilder.java:0)
+Class <org.gradle.internal.classpath.ClasspathEntryVisitor$Entry$CompressionMethod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathEntryVisitor.java:0)
+Class <org.gradle.internal.classpath.ClasspathEntryVisitor$Entry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathEntryVisitor.java:0)
+Class <org.gradle.internal.classpath.ClasspathEntryVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathEntryVisitor.java:0)
+Class <org.gradle.internal.classpath.ClasspathFileTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathFileTransformer.java:0)
+Class <org.gradle.internal.classpath.ClasspathTransformerCacheFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathTransformerCacheFactory.java:0)
+Class <org.gradle.internal.classpath.ClasspathWalker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathWalker.java:0)
+Class <org.gradle.internal.classpath.ClasspathWalker$FileEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathWalker.java:0)
+Class <org.gradle.internal.classpath.ClasspathWalker$ZipClasspathEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathWalker.java:0)
+Class <org.gradle.internal.classpath.ClasspathWalker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathWalker.java:0)
+Class <org.gradle.internal.classpath.CompositeTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeTransformer.java:0)
+Class <org.gradle.internal.classpath.CopyingClasspathFileTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CopyingClasspathFileTransformer.java:0)
+Class <org.gradle.internal.classpath.DefaultCachedClasspathTransformer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedClasspathTransformer.java:0)
+Class <org.gradle.internal.classpath.DefaultCachedClasspathTransformer$Convert> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedClasspathTransformer.java:0)
+Class <org.gradle.internal.classpath.DefaultCachedClasspathTransformer$TransformPipeline> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedClasspathTransformer.java:0)
+Class <org.gradle.internal.classpath.DefaultCachedClasspathTransformer$ValueOrTransformProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedClasspathTransformer.java:0)
+Class <org.gradle.internal.classpath.DefaultCachedClasspathTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedClasspathTransformer.java:0)
+Class <org.gradle.internal.classpath.DefaultClassPath$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassPath.java:0)
+Class <org.gradle.internal.classpath.DefaultClassPath$ImmutableUniqueList$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassPath.java:0)
+Class <org.gradle.internal.classpath.DefaultClassPath$ImmutableUniqueList> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassPath.java:0)
+Class <org.gradle.internal.classpath.DefaultClassPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassPath.java:0)
+Class <org.gradle.internal.classpath.DefaultClasspathTransformerCacheFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClasspathTransformerCacheFactory.java:0)
+Class <org.gradle.internal.classpath.FileUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileUtils.java:0)
+Class <org.gradle.internal.classpath.Instrumented$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$BooleanGetBooleanInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$IntegerGetIntegerInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$Listener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$LongGetLongInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$ProcessBuilderStartInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$ProcessBuilderStartPipelineInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$ProcessGroovyMethodsExecuteInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$RuntimeExecInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$SystemClearPropertyInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$SystemGetPropertiesInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$SystemGetPropertyInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$SystemGetenvInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$SystemSetPropertiesInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented$SystemSetPropertyInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.Instrumented> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Instrumented.java:0)
+Class <org.gradle.internal.classpath.InstrumentedExecutionAccess$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentedExecutionAccess.java:0)
+Class <org.gradle.internal.classpath.InstrumentingBackwardsCompatibilityVisitor$BackwardCompatibilityMethodVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingBackwardsCompatibilityVisitor.java:0)
+Class <org.gradle.internal.classpath.InstrumentingBackwardsCompatibilityVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingBackwardsCompatibilityVisitor.java:0)
+Class <org.gradle.internal.classpath.InstrumentingClasspathFileTransformer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingClasspathFileTransformer.java:0)
+Class <org.gradle.internal.classpath.InstrumentingClasspathFileTransformer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingClasspathFileTransformer.java:0)
+Class <org.gradle.internal.classpath.InstrumentingClasspathFileTransformer$Policy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingClasspathFileTransformer.java:0)
+Class <org.gradle.internal.classpath.InstrumentingClasspathFileTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingClasspathFileTransformer.java:0)
+Class <org.gradle.internal.classpath.InstrumentingTransformer$InstrumentingMethodVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingTransformer.java:0)
+Class <org.gradle.internal.classpath.InstrumentingTransformer$InstrumentingVisitor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingTransformer.java:0)
+Class <org.gradle.internal.classpath.InstrumentingTransformer$InstrumentingVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingTransformer.java:0)
+Class <org.gradle.internal.classpath.InstrumentingTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstrumentingTransformer.java:0)
+Class <org.gradle.internal.classpath.LambdaSerializationTransformer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LambdaSerializationTransformer.java:0)
+Class <org.gradle.internal.classpath.MethodHandleUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodHandleUtils.java:0)
+Class <org.gradle.internal.classpath.TransformedClassPath$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransformedClassPath.java:0)
+Class <org.gradle.internal.classpath.TransformedClassPath$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransformedClassPath.java:0)
+Class <org.gradle.internal.classpath.TransformedClassPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransformedClassPath.java:0)
+Class <org.gradle.internal.classpath.declarations.FileInputStreamInterceptorsDeclaration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileInputStreamInterceptorsDeclaration.java:0)
+Class <org.gradle.internal.classpath.declarations.FileInterceptorsDeclaration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileInterceptorsDeclaration.java:0)
+Class <org.gradle.internal.classpath.declarations.GroovyFileInterceptors> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroovyFileInterceptors.java:0)
+Class <org.gradle.internal.classpath.declarations.Handles> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KotlinStdlibFileInterceptors.java:0)
+Class <org.gradle.internal.classpath.declarations.KotlinFileExtensionsInterceptorsDeclaration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KotlinFileExtensionsInterceptorsDeclaration.java:0)
+Class <org.gradle.internal.classpath.declarations.KotlinStdlibFileInterceptors> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KotlinStdlibFileInterceptors.java:0)
+Class <org.gradle.internal.classpath.declarations.NioFileInterceptors> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NioFileInterceptors.java:0)
+Class <org.gradle.internal.classpath.intercept.AbstractInvocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractInvocation.java:0)
+Class <org.gradle.internal.classpath.intercept.CallInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CallInterceptor.java:0)
+Class <org.gradle.internal.classpath.intercept.CallInterceptorsSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CallInterceptorsSet.java:0)
+Class <org.gradle.internal.classpath.intercept.CallInterceptorsSet$DecoratingCallSite$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CallInterceptorsSet.java:0)
+Class <org.gradle.internal.classpath.intercept.CallInterceptorsSet$DecoratingCallSite$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CallInterceptorsSet.java:0)
+Class <org.gradle.internal.classpath.intercept.CallInterceptorsSet$DecoratingCallSite$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CallInterceptorsSet.java:0)
+Class <org.gradle.internal.classpath.intercept.CallInterceptorsSet$DecoratingCallSite$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CallInterceptorsSet.java:0)
+Class <org.gradle.internal.classpath.intercept.CallInterceptorsSet$DecoratingCallSite> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CallInterceptorsSet.java:0)
+Class <org.gradle.internal.classpath.intercept.ClassBoundCallInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassBoundCallInterceptor.java:0)
+Class <org.gradle.internal.classpath.intercept.InterceptScope$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterceptScope.java:0)
+Class <org.gradle.internal.classpath.intercept.InterceptScope$CallType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterceptScope.java:0)
+Class <org.gradle.internal.classpath.intercept.InterceptScope$ConstructorScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterceptScope.java:0)
+Class <org.gradle.internal.classpath.intercept.InterceptScope$NamedMemberScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterceptScope.java:0)
+Class <org.gradle.internal.classpath.intercept.InterceptScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterceptScope.java:0)
+Class <org.gradle.internal.classpath.intercept.Invocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Invocation.java:0)
+Class <org.gradle.internal.classpath.intercept.InvocationUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvocationUtils.java:0)
+Class <org.gradle.internal.classpath.intercept.MethodHandleInvocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodHandleInvocation.java:0)
+Class <org.gradle.internal.collect.PersistentList$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentList.java:0)
+Class <org.gradle.internal.collect.PersistentList$Cons> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentList.java:0)
+Class <org.gradle.internal.collect.PersistentList> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentList.java:0)
+Class <org.gradle.internal.collections.ImmutableFilteredList> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImmutableFilteredList.java:0)
+Class <org.gradle.internal.compiler.java.IncrementalCompileTask> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileTask.java:0)
+Class <org.gradle.internal.compiler.java.listeners.classnames.ClassNameCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassNameCollector.java:0)
+Class <org.gradle.internal.compiler.java.listeners.constants.ConstantDependentsConsumer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantDependentsConsumer.java:0)
+Class <org.gradle.internal.compiler.java.listeners.constants.ConstantsCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantsCollector.java:0)
+Class <org.gradle.internal.compiler.java.listeners.constants.ConstantsTreeVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantsTreeVisitor.java:0)
+Class <org.gradle.internal.compiler.java.listeners.constants.ConstantsVisitorContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantsVisitorContext.java:0)
+Class <org.gradle.internal.component.external.descriptor.Artifact> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Artifact.java:0)
+Class <org.gradle.internal.component.external.descriptor.Configuration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Configuration.java:0)
+Class <org.gradle.internal.component.external.descriptor.DefaultExclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExclude.java:0)
+Class <org.gradle.internal.component.external.descriptor.MavenScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenScope.java:0)
+Class <org.gradle.internal.composite.ChildBuildRegisteringSettingsLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChildBuildRegisteringSettingsLoader.java:0)
+Class <org.gradle.internal.composite.CommandLineIncludedBuildSettingsLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineIncludedBuildSettingsLoader.java:0)
+Class <org.gradle.internal.composite.CompositeBuildSettingsLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeBuildSettingsLoader.java:0)
+Class <org.gradle.internal.composite.DefaultBuildIncluder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildIncluder.java:0)
+Class <org.gradle.internal.composite.DefaultConfigurableIncludedBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConfigurableIncludedBuild.java:0)
+Class <org.gradle.internal.composite.DefaultConfigurableIncludedPluginBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConfigurableIncludedPluginBuild.java:0)
+Class <org.gradle.internal.composite.IncludedBuildInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildInternal.java:0)
+Class <org.gradle.internal.composite.IncludedRootBuild$IncludedRootBuildTaskReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedRootBuild.java:0)
+Class <org.gradle.internal.composite.IncludedRootBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedRootBuild.java:0)
+Class <org.gradle.internal.concurrent.AbstractDelegatingExecutorService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDelegatingExecutorService.java:0)
+Class <org.gradle.internal.concurrent.AbstractManagedExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractManagedExecutor.java:0)
+Class <org.gradle.internal.concurrent.AbstractManagedExecutor$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractManagedExecutor.java:0)
+Class <org.gradle.internal.concurrent.AbstractManagedExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractManagedExecutor.java:0)
+Class <org.gradle.internal.concurrent.AsyncStoppable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsyncStoppable.java:0)
+Class <org.gradle.internal.concurrent.CompositeStoppable$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeStoppable.java:0)
+Class <org.gradle.internal.concurrent.CompositeStoppable$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeStoppable.java:0)
+Class <org.gradle.internal.concurrent.CompositeStoppable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeStoppable.java:0)
+Class <org.gradle.internal.concurrent.DefaultExecutorFactory$TrackedManagedExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecutorFactory.java:0)
+Class <org.gradle.internal.concurrent.DefaultExecutorFactory$TrackedScheduledManagedExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecutorFactory.java:0)
+Class <org.gradle.internal.concurrent.DefaultExecutorFactory$TrackedThreadPoolManagedExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecutorFactory.java:0)
+Class <org.gradle.internal.concurrent.DefaultExecutorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecutorFactory.java:0)
+Class <org.gradle.internal.concurrent.DefaultParallelismConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultParallelismConfiguration.java:0)
+Class <org.gradle.internal.concurrent.ExecutorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutorFactory.java:0)
+Class <org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutorPolicy.java:0)
+Class <org.gradle.internal.concurrent.ExecutorPolicy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutorPolicy.java:0)
+Class <org.gradle.internal.concurrent.InterruptibleRunnable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterruptibleRunnable.java:0)
+Class <org.gradle.internal.concurrent.ManagedExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedExecutor.java:0)
+Class <org.gradle.internal.concurrent.ManagedExecutorImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedExecutorImpl.java:0)
+Class <org.gradle.internal.concurrent.ManagedScheduledExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedScheduledExecutor.java:0)
+Class <org.gradle.internal.concurrent.ManagedScheduledExecutorImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedScheduledExecutorImpl.java:0)
+Class <org.gradle.internal.concurrent.ManagedThreadPoolExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedThreadPoolExecutor.java:0)
+Class <org.gradle.internal.concurrent.ManagedThreadPoolExecutorImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedThreadPoolExecutorImpl.java:0)
+Class <org.gradle.internal.concurrent.ServiceLifecycle$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceLifecycle.java:0)
+Class <org.gradle.internal.concurrent.ServiceLifecycle$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceLifecycle.java:0)
+Class <org.gradle.internal.concurrent.ServiceLifecycle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceLifecycle.java:0)
+Class <org.gradle.internal.concurrent.Stoppable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Stoppable.java:0)
+Class <org.gradle.internal.concurrent.Synchronizer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Synchronizer.java:0)
+Class <org.gradle.internal.concurrent.ThreadFactoryImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ThreadFactoryImpl.java:0)
+Class <org.gradle.internal.concurrent.ThreadSafe> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ThreadSafe.java:0)
+Class <org.gradle.internal.configurationcache.ConfigurationCacheLoadBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationCacheLoadBuildOperationType.java:0)
+Class <org.gradle.internal.configurationcache.ConfigurationCacheLoadBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationCacheLoadBuildOperationType.java:0)
+Class <org.gradle.internal.configurationcache.ConfigurationCacheLoadBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationCacheLoadBuildOperationType.java:0)
+Class <org.gradle.internal.configurationcache.ConfigurationCacheStoreBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationCacheStoreBuildOperationType.java:0)
+Class <org.gradle.internal.configurationcache.ConfigurationCacheStoreBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationCacheStoreBuildOperationType.java:0)
+Class <org.gradle.internal.configurationcache.ConfigurationCacheStoreBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationCacheStoreBuildOperationType.java:0)
+Class <org.gradle.internal.configurationcache.options.ConfigurationCacheSettingsFinalizedProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationCacheSettingsFinalizedProgressDetails.java:0)
+Class <org.gradle.internal.credentials.DefaultAwsCredentials> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAwsCredentials.java:0)
+Class <org.gradle.internal.credentials.DefaultHttpHeaderCredentials> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHttpHeaderCredentials.java:0)
+Class <org.gradle.internal.credentials.DefaultPasswordCredentials> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPasswordCredentials.java:0)
+Class <org.gradle.internal.deployment.JavaApplicationHandle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaApplicationHandle.java:0)
+Class <org.gradle.internal.deployment.RunApplication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunApplication.java:0)
+Class <org.gradle.internal.dispatch.AsyncDispatch$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsyncDispatch.java:0)
+Class <org.gradle.internal.dispatch.AsyncDispatch$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsyncDispatch.java:0)
+Class <org.gradle.internal.dispatch.AsyncDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsyncDispatch.java:0)
+Class <org.gradle.internal.dispatch.BoundedDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BoundedDispatch.java:0)
+Class <org.gradle.internal.dispatch.ContextClassLoaderDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextClassLoaderDispatch.java:0)
+Class <org.gradle.internal.dispatch.ContextClassLoaderProxy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextClassLoaderProxy.java:0)
+Class <org.gradle.internal.dispatch.Dispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Dispatch.java:0)
+Class <org.gradle.internal.dispatch.DispatchException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DispatchException.java:0)
+Class <org.gradle.internal.dispatch.DispatchFailureHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DispatchFailureHandler.java:0)
+Class <org.gradle.internal.dispatch.ExceptionTrackingFailureHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionTrackingFailureHandler.java:0)
+Class <org.gradle.internal.dispatch.FailureHandlingDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailureHandlingDispatch.java:0)
+Class <org.gradle.internal.dispatch.MethodInvocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodInvocation.java:0)
+Class <org.gradle.internal.dispatch.ProxyDispatchAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProxyDispatchAdapter.java:0)
+Class <org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProxyDispatchAdapter.java:0)
+Class <org.gradle.internal.dispatch.ProxyDispatchAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProxyDispatchAdapter.java:0)
+Class <org.gradle.internal.dispatch.Receive> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Receive.java:0)
+Class <org.gradle.internal.dispatch.ReflectionDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReflectionDispatch.java:0)
+Class <org.gradle.internal.dispatch.StreamCompletion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamCompletion.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginBackgroundJobExecutors> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginBackgroundJobExecutors.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginBuildState.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginCheckInResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginCheckInResult.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginCheckInService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginConfig$BuildScanRequest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginConfig.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginConfig.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginEndOfBuildListener$BuildResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginEndOfBuildListener.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginEndOfBuildListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginEndOfBuildListener.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginMetadata.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginRequiredServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginRequiredServices.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginService.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginServiceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginServiceFactory.java:0)
+Class <org.gradle.internal.enterprise.GradleEnterprisePluginServiceRef> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginServiceRef.java:0)
+Class <org.gradle.internal.enterprise.core.GradleEnterprisePluginAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginAdapter.java:0)
+Class <org.gradle.internal.enterprise.core.GradleEnterprisePluginManager$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginManager.java:0)
+Class <org.gradle.internal.enterprise.core.GradleEnterprisePluginManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginManager.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginAdapter.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginAdapter.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginBackgroundJobExecutors$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginBackgroundJobExecutors.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginBackgroundJobExecutors$BackgroundThread> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginBackgroundJobExecutors.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginBackgroundJobExecutors$BackgroundThreadFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginBackgroundJobExecutors.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginBackgroundJobExecutors> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginBackgroundJobExecutors.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginBuildState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginBuildState.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginCheckInService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginConfig.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginRequiredServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginRequiredServices.java:0)
+Class <org.gradle.internal.enterprise.impl.DefaultGradleEnterprisePluginServiceRef> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleEnterprisePluginServiceRef.java:0)
+Class <org.gradle.internal.enterprise.impl.GradleEnterpriseAutoAppliedPluginRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterpriseAutoAppliedPluginRegistry.java:0)
+Class <org.gradle.internal.enterprise.impl.GradleEnterprisePluginAutoApplicationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginAutoApplicationListener.java:0)
+Class <org.gradle.internal.enterprise.impl.GradleEnterprisePluginAutoAppliedStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginAutoAppliedStatus.java:0)
+Class <org.gradle.internal.enterprise.impl.GradleEnterprisePluginServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleEnterprisePluginServices.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.DefaultBuildScanBuildStartedTime> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildScanBuildStartedTime.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.DefaultBuildScanClock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildScanClock.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.DefaultBuildScanScopeIds> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildScanScopeIds.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService$Adapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService$Adapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService$Attributes> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService$Config> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService$Requestedness> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.LegacyGradleEnterprisePluginCheckInService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyGradleEnterprisePluginCheckInService.java:0)
+Class <org.gradle.internal.enterprise.impl.legacy.UnsupportedBuildScanPluginVersionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsupportedBuildScanPluginVersionException.java:0)
+Class <org.gradle.internal.environment.GradleBuildEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleBuildEnvironment.java:0)
+Class <org.gradle.internal.event.AbstractBroadcastDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractBroadcastDispatch.java:0)
+Class <org.gradle.internal.event.AnonymousListenerBroadcast> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnonymousListenerBroadcast.java:0)
+Class <org.gradle.internal.event.BroadcastDispatch$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BroadcastDispatch.java:0)
+Class <org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BroadcastDispatch.java:0)
+Class <org.gradle.internal.event.BroadcastDispatch$CompositeDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BroadcastDispatch.java:0)
+Class <org.gradle.internal.event.BroadcastDispatch$EmptyDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BroadcastDispatch.java:0)
+Class <org.gradle.internal.event.BroadcastDispatch$SingletonDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BroadcastDispatch.java:0)
+Class <org.gradle.internal.event.BroadcastDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BroadcastDispatch.java:0)
+Class <org.gradle.internal.event.DefaultListenerManager$EventBroadcast$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultListenerManager.java:0)
+Class <org.gradle.internal.event.DefaultListenerManager$EventBroadcast$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultListenerManager.java:0)
+Class <org.gradle.internal.event.DefaultListenerManager$EventBroadcast$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultListenerManager.java:0)
+Class <org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultListenerManager.java:0)
+Class <org.gradle.internal.event.DefaultListenerManager$EventBroadcast> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultListenerManager.java:0)
+Class <org.gradle.internal.event.DefaultListenerManager$ListenerDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultListenerManager.java:0)
+Class <org.gradle.internal.event.DefaultListenerManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultListenerManager.java:0)
+Class <org.gradle.internal.event.ListenerBroadcast> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ListenerBroadcast.java:0)
+Class <org.gradle.internal.event.ListenerManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ListenerManager.java:0)
+Class <org.gradle.internal.event.ListenerNotificationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ListenerNotificationException.java:0)
+Class <org.gradle.internal.exceptions.CompilationFailedIndicator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilationFailedIndicator.java:0)
+Class <org.gradle.internal.exceptions.ConfigurationNotConsumableException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurationNotConsumableException.java:0)
+Class <org.gradle.internal.exceptions.ContextAwareException$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextAwareException.java:0)
+Class <org.gradle.internal.exceptions.ContextAwareException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextAwareException.java:0)
+Class <org.gradle.internal.exceptions.Contextual> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Contextual.java:0)
+Class <org.gradle.internal.exceptions.DefaultMultiCauseException$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMultiCauseException.java:0)
+Class <org.gradle.internal.exceptions.DefaultMultiCauseException$HideStacktrace> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMultiCauseException.java:0)
+Class <org.gradle.internal.exceptions.DefaultMultiCauseException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMultiCauseException.java:0)
+Class <org.gradle.internal.exceptions.DefaultMultiCauseExceptionNoStackTrace> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMultiCauseExceptionNoStackTrace.java:0)
+Class <org.gradle.internal.exceptions.DiagnosticsVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DiagnosticsVisitor.java:0)
+Class <org.gradle.internal.exceptions.ExceptionContextVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionContextVisitor.java:0)
+Class <org.gradle.internal.exceptions.FailureResolutionAware$Context> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailureResolutionAware.java:0)
+Class <org.gradle.internal.exceptions.FailureResolutionAware> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailureResolutionAware.java:0)
+Class <org.gradle.internal.exceptions.FormattingDiagnosticsVisitor$Candidate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FormattingDiagnosticsVisitor.java:0)
+Class <org.gradle.internal.exceptions.FormattingDiagnosticsVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FormattingDiagnosticsVisitor.java:0)
+Class <org.gradle.internal.exceptions.LocationAwareException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocationAwareException.java:0)
+Class <org.gradle.internal.exceptions.MultiCauseException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiCauseException.java:0)
+Class <org.gradle.internal.exceptions.NonGradleCause> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NonGradleCause.java:0)
+Class <org.gradle.internal.exceptions.NonGradleCauseExceptionsHolder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NonGradleCauseExceptionsHolder.java:0)
+Class <org.gradle.internal.exceptions.StyledException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledException.java:0)
+Class <org.gradle.internal.exceptions.ValueCollectingDiagnosticsVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValueCollectingDiagnosticsVisitor.java:0)
+Class <org.gradle.internal.execution.timeout.Timeout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Timeout.java:0)
+Class <org.gradle.internal.execution.timeout.TimeoutHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeoutHandler.java:0)
+Class <org.gradle.internal.execution.timeout.impl.DefaultTimeoutHandler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTimeoutHandler.java:0)
+Class <org.gradle.internal.execution.timeout.impl.DefaultTimeoutHandler$DefaultTimeout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTimeoutHandler.java:0)
+Class <org.gradle.internal.execution.timeout.impl.DefaultTimeoutHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTimeoutHandler.java:0)
+Class <org.gradle.internal.featurelifecycle.DefaultDeprecatedUsageProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDeprecatedUsageProgressDetails.java:0)
+Class <org.gradle.internal.featurelifecycle.DeprecatedUsageProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DeprecatedUsageProgressDetails.java:0)
+Class <org.gradle.internal.featurelifecycle.FeatureHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureHandler.java:0)
+Class <org.gradle.internal.featurelifecycle.FeatureUsage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureUsage.java:0)
+Class <org.gradle.internal.featurelifecycle.IncubatingFeatureUsage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncubatingFeatureUsage.java:0)
+Class <org.gradle.internal.featurelifecycle.LoggingDeprecatedFeatureHandler$DoNothingReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingDeprecatedFeatureHandler.java:0)
+Class <org.gradle.internal.featurelifecycle.LoggingDeprecatedFeatureHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingDeprecatedFeatureHandler.java:0)
+Class <org.gradle.internal.featurelifecycle.LoggingIncubatingFeatureHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingIncubatingFeatureHandler.java:0)
+Class <org.gradle.internal.featurelifecycle.ScriptUsageLocationReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptUsageLocationReporter.java:0)
+Class <org.gradle.internal.featurelifecycle.UsageLocationReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UsageLocationReporter.java:0)
+Class <org.gradle.internal.file.excludes.FileSystemDefaultExcludesListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileSystemDefaultExcludesListener.java:0)
+Class <org.gradle.internal.graph.CachingDirectedGraphWalker$GraphWithEmptyEdges> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingDirectedGraphWalker.java:0)
+Class <org.gradle.internal.graph.CachingDirectedGraphWalker$NodeDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingDirectedGraphWalker.java:0)
+Class <org.gradle.internal.graph.CachingDirectedGraphWalker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingDirectedGraphWalker.java:0)
+Class <org.gradle.internal.graph.DirectedGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectedGraph.java:0)
+Class <org.gradle.internal.graph.DirectedGraphRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectedGraphRenderer.java:0)
+Class <org.gradle.internal.graph.DirectedGraphRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectedGraphRenderer.java:0)
+Class <org.gradle.internal.graph.DirectedGraphWithEdgeValues> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectedGraphWithEdgeValues.java:0)
+Class <org.gradle.internal.graph.GraphNodeRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GraphNodeRenderer.java:0)
+Class <org.gradle.internal.graph.GraphRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GraphRenderer.java:0)
+Class <org.gradle.internal.html.SimpleHtmlWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleHtmlWriter.java:0)
+Class <org.gradle.internal.installation.CurrentGradleInstallation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CurrentGradleInstallation.java:0)
+Class <org.gradle.internal.installation.CurrentGradleInstallationLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CurrentGradleInstallationLocator.java:0)
+Class <org.gradle.internal.installation.GradleInstallation$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleInstallation.java:0)
+Class <org.gradle.internal.installation.GradleInstallation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleInstallation.java:0)
+Class <org.gradle.internal.installation.GradleRuntimeShadedJarDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleRuntimeShadedJarDetector.java:0)
+Class <org.gradle.internal.installation.beacon.InstallationBeacon> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstallationBeacon.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$AbstractInjectedPropertyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$ClassGenerationHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$ClassGenerationVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$ClassInspectionVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$ClassMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$ClassValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$CustomInjectAnnotationPropertyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$DisabledAnnotationValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$DslMixInPropertyType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$ExtensibleTypePropertyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$GeneratedClassImpl$GeneratedConstructorImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$GeneratedClassImpl$SerializationConstructorImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$GeneratedClassImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$InjectAnnotationPropertyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$InjectionAnnotationValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$InstantiationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$LazyGroovySupportTypePropertyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$ManagedPropertiesHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$MethodMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$NamePropertyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$PropertyMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$ServicesPropertyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator$UnclaimedPropertyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AbstractClassGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$AttachedProperty> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$10> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$11> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$12> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$13> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$14> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$15> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$16> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$17> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$18> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$19> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$20> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$21> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$22> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$23> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$24> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$25> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$26> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$27> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$28> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$29> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$30> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$31> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$32> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$33> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$34> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$35> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$36> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$37> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$38> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$39> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$40> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$41> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$42> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$9> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$LocalMethodVisitorScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassBuilderImpl$ReturnTypeEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ClassInspectionVisitorImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$InvokeConstructorStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$InvokeSerializationConstructorAndInitializeFieldsStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$NoOpBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator$ObjectCreationDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.AsmBackedClassGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmBackedClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.ClassGenerator$GeneratedClass> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.ClassGenerator$GeneratedConstructor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.ClassGenerator$SerializationConstructor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.ClassGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassGenerator.java:0)
+Class <org.gradle.internal.instantiation.generator.ConstructorComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstructorComparator.java:0)
+Class <org.gradle.internal.instantiation.generator.ConstructorSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstructorSelector.java:0)
+Class <org.gradle.internal.instantiation.generator.DefaultInstantiationScheme$DefaultDeserializationInstantiator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInstantiationScheme.java:0)
+Class <org.gradle.internal.instantiation.generator.DefaultInstantiationScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInstantiationScheme.java:0)
+Class <org.gradle.internal.instantiation.generator.DefaultInstantiatorFactory$ManagedTypeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInstantiatorFactory.java:0)
+Class <org.gradle.internal.instantiation.generator.DefaultInstantiatorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInstantiatorFactory.java:0)
+Class <org.gradle.internal.instantiation.generator.DependencyInjectingInstantiator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyInjectingInstantiator.java:0)
+Class <org.gradle.internal.instantiation.generator.DependencyInjectingInstantiator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyInjectingInstantiator.java:0)
+Class <org.gradle.internal.instantiation.generator.InjectUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InjectUtil.java:0)
+Class <org.gradle.internal.instantiation.generator.Jsr330ConstructorSelector$CachedConstructor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Jsr330ConstructorSelector.java:0)
+Class <org.gradle.internal.instantiation.generator.Jsr330ConstructorSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Jsr330ConstructorSelector.java:0)
+Class <org.gradle.internal.instantiation.generator.ManagedObjectFactory$ManagedPropertyName> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedObjectFactory.java:0)
+Class <org.gradle.internal.instantiation.generator.ManagedObjectFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedObjectFactory.java:0)
+Class <org.gradle.internal.instantiation.generator.MixInExtensibleDynamicObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MixInExtensibleDynamicObject.java:0)
+Class <org.gradle.internal.instantiation.generator.ParamsMatchingConstructorSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ParamsMatchingConstructorSelector.java:0)
+Class <org.gradle.internal.instrumentation.api.annotations.features.withstaticreference.WithExtensionReferences> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WithExtensionReferences.java:0)
+Class <org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterceptorDeclaration.java:0)
+Class <org.gradle.internal.instrumentation.api.jvmbytecode.JvmBytecodeCallInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmBytecodeCallInterceptor.java:0)
+Class <org.gradle.internal.invocation.BuildAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildAction.java:0)
+Class <org.gradle.internal.invocation.DefaultBuildInvocationDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildInvocationDetails.java:0)
+Class <org.gradle.internal.isolated.IsolationScheme$ServicesForIsolatedObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolationScheme.java:0)
+Class <org.gradle.internal.isolated.IsolationScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolationScheme.java:0)
+Class <org.gradle.internal.jacoco.AbstractAntJacocoReport$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.AbstractAntJacocoReport$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.AbstractAntJacocoReport$2$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.AbstractAntJacocoReport$2$2$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.AbstractAntJacocoReport$2$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.AbstractAntJacocoReport$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.AbstractAntJacocoReport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.AntJacocoCheck$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntJacocoCheck.java:0)
+Class <org.gradle.internal.jacoco.AntJacocoCheck$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntJacocoCheck.java:0)
+Class <org.gradle.internal.jacoco.AntJacocoCheck$3$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntJacocoCheck.java:0)
+Class <org.gradle.internal.jacoco.AntJacocoCheck$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntJacocoCheck.java:0)
+Class <org.gradle.internal.jacoco.AntJacocoCheck> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntJacocoCheck.java:0)
+Class <org.gradle.internal.jacoco.AntJacocoReport$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.AntJacocoReport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntJacocoReport.java:0)
+Class <org.gradle.internal.jacoco.DefaultJacocoCoverageReport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJacocoCoverageReport.java:0)
+Class <org.gradle.internal.jacoco.JacocoAgentJar$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoAgentJar.java:0)
+Class <org.gradle.internal.jacoco.JacocoAgentJar$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoAgentJar.java:0)
+Class <org.gradle.internal.jacoco.JacocoAgentJar$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoAgentJar.java:0)
+Class <org.gradle.internal.jacoco.JacocoAgentJar> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoAgentJar.java:0)
+Class <org.gradle.internal.jacoco.JacocoCheckResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoCheckResult.java:0)
+Class <org.gradle.internal.jacoco.JacocoReportsContainerImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoReportsContainerImpl.java:0)
+Class <org.gradle.internal.jacoco.rules.JacocoLimitImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoLimitImpl.java:0)
+Class <org.gradle.internal.jacoco.rules.JacocoViolationRuleImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoViolationRuleImpl.java:0)
+Class <org.gradle.internal.jacoco.rules.JacocoViolationRulesContainerImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JacocoViolationRulesContainerImpl.java:0)
+Class <org.gradle.internal.jvm.DefaultModularitySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModularitySpec.java:0)
+Class <org.gradle.internal.jvm.GradleVersionNumberLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleVersionNumberLoader.java:0)
+Class <org.gradle.internal.jvm.JavaHomeException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaHomeException.java:0)
+Class <org.gradle.internal.jvm.JavaInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaInfo.java:0)
+Class <org.gradle.internal.jvm.JavaModuleDetector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaModuleDetector.java:0)
+Class <org.gradle.internal.jvm.JavaModuleDetector$ModuleInfoLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaModuleDetector.java:0)
+Class <org.gradle.internal.jvm.JavaModuleDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaModuleDetector.java:0)
+Class <org.gradle.internal.jvm.JpmsConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JpmsConfiguration.java:0)
+Class <org.gradle.internal.jvm.Jvm$AppleJvm> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Jvm.java:0)
+Class <org.gradle.internal.jvm.Jvm$IbmJvm> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Jvm.java:0)
+Class <org.gradle.internal.jvm.Jvm$JvmImplementation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Jvm.java:0)
+Class <org.gradle.internal.jvm.Jvm> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Jvm.java:0)
+Class <org.gradle.internal.jvm.UnsupportedJavaRuntimeException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsupportedJavaRuntimeException.java:0)
+Class <org.gradle.internal.jvm.inspection.CachingJvmMetadataDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingJvmMetadataDetector.java:0)
+Class <org.gradle.internal.jvm.inspection.ConditionalInvalidation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConditionalInvalidation.java:0)
+Class <org.gradle.internal.jvm.inspection.DefaultJvmMetadataDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJvmMetadataDetector.java:0)
+Class <org.gradle.internal.jvm.inspection.DefaultJvmVersionDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJvmVersionDetector.java:0)
+Class <org.gradle.internal.jvm.inspection.InvalidInstallationWarningReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidInstallationWarningReporter.java:0)
+Class <org.gradle.internal.jvm.inspection.InvalidJvmInstallationCacheInvalidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidJvmInstallationCacheInvalidator.java:0)
+Class <org.gradle.internal.jvm.inspection.JavaInstallationRegistry$Installations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaInstallationRegistry.java:0)
+Class <org.gradle.internal.jvm.inspection.JavaInstallationRegistry$ToolchainDetectionBuildOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaInstallationRegistry.java:0)
+Class <org.gradle.internal.jvm.inspection.JavaInstallationRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaInstallationRegistry.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmInstallationMetadata$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmInstallationMetadata.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmInstallationMetadata$DefaultJvmInstallationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmInstallationMetadata.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmInstallationMetadata$FailureInstallationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmInstallationMetadata.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmInstallationMetadata$JavaInstallationCapability> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmInstallationMetadata.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmInstallationMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmInstallationMetadata.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmInstallationMetadataComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmInstallationMetadataComparator.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmMetadataDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmMetadataDetector.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmToolchainMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmToolchainMetadata.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmVendor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmVendor.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmVendor$KnownJvmVendor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmVendor.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmVendor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmVendor.java:0)
+Class <org.gradle.internal.jvm.inspection.JvmVersionDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmVersionDetector.java:0)
+Class <org.gradle.internal.jvm.inspection.MetadataProbe$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MetadataProbe.java:0)
+Class <org.gradle.internal.jvm.inspection.MetadataProbe> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MetadataProbe.java:0)
+Class <org.gradle.internal.jvm.inspection.ProbedSystemProperty> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProbedSystemProperty.java:0)
+Class <org.gradle.internal.jvm.inspection.ReportingJvmMetadataDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReportingJvmMetadataDetector.java:0)
+Class <org.gradle.internal.lazy.Lazy$Factory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Lazy.java:0)
+Class <org.gradle.internal.lazy.Lazy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Lazy.java:0)
+Class <org.gradle.internal.lazy.LockingLazy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockingLazy.java:0)
+Class <org.gradle.internal.lazy.UnsafeLazy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsafeLazy.java:0)
+Class <org.gradle.internal.logging.ConsoleRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsoleRenderer.java:0)
+Class <org.gradle.internal.logging.DefaultLoggingConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLoggingConfiguration.java:0)
+Class <org.gradle.internal.logging.LogLevelMapping$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogLevelMapping.java:0)
+Class <org.gradle.internal.logging.LogLevelMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogLevelMapping.java:0)
+Class <org.gradle.internal.logging.LoggingConfigurationBuildOptions$ConsoleOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingConfigurationBuildOptions.java:0)
+Class <org.gradle.internal.logging.LoggingConfigurationBuildOptions$LogLevelOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingConfigurationBuildOptions.java:0)
+Class <org.gradle.internal.logging.LoggingConfigurationBuildOptions$StacktraceOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingConfigurationBuildOptions.java:0)
+Class <org.gradle.internal.logging.LoggingConfigurationBuildOptions$WarningsOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingConfigurationBuildOptions.java:0)
+Class <org.gradle.internal.logging.LoggingConfigurationBuildOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingConfigurationBuildOptions.java:0)
+Class <org.gradle.internal.logging.LoggingManagerInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingManagerInternal.java:0)
+Class <org.gradle.internal.logging.LoggingOutputInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingOutputInternal.java:0)
+Class <org.gradle.internal.logging.StandardOutputCapture> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StandardOutputCapture.java:0)
+Class <org.gradle.internal.logging.config.LoggingConfigurer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingConfigurer.java:0)
+Class <org.gradle.internal.logging.config.LoggingRouter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingRouter.java:0)
+Class <org.gradle.internal.logging.config.LoggingSourceSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingSourceSystem.java:0)
+Class <org.gradle.internal.logging.config.LoggingSystem$Snapshot> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingSystem.java:0)
+Class <org.gradle.internal.logging.config.LoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingSystem.java:0)
+Class <org.gradle.internal.logging.config.LoggingSystemAdapter$SnapshotImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingSystemAdapter.java:0)
+Class <org.gradle.internal.logging.config.LoggingSystemAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingSystemAdapter.java:0)
+Class <org.gradle.internal.logging.console.AbstractUserInputRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractUserInputRenderer.java:0)
+Class <org.gradle.internal.logging.console.AnsiConsole$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiConsole.java:0)
+Class <org.gradle.internal.logging.console.AnsiConsole$Listener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiConsole.java:0)
+Class <org.gradle.internal.logging.console.AnsiConsole> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiConsole.java:0)
+Class <org.gradle.internal.logging.console.AnsiContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiContext.java:0)
+Class <org.gradle.internal.logging.console.AnsiExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiExecutor.java:0)
+Class <org.gradle.internal.logging.console.AnsiFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiFactory.java:0)
+Class <org.gradle.internal.logging.console.BuildLogLevelFilterRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLogLevelFilterRenderer.java:0)
+Class <org.gradle.internal.logging.console.BuildProgressArea> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildProgressArea.java:0)
+Class <org.gradle.internal.logging.console.BuildStatusRenderer$Phase> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildStatusRenderer.java:0)
+Class <org.gradle.internal.logging.console.BuildStatusRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildStatusRenderer.java:0)
+Class <org.gradle.internal.logging.console.ColorMap$Color> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ColorMap.java:0)
+Class <org.gradle.internal.logging.console.ColorMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ColorMap.java:0)
+Class <org.gradle.internal.logging.console.Console> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Console.java:0)
+Class <org.gradle.internal.logging.console.ConsoleLayoutCalculator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsoleLayoutCalculator.java:0)
+Class <org.gradle.internal.logging.console.Cursor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Cursor.java:0)
+Class <org.gradle.internal.logging.console.DefaultAnsiExecutor$AnsiContextImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAnsiExecutor.java:0)
+Class <org.gradle.internal.logging.console.DefaultAnsiExecutor$NewLineListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAnsiExecutor.java:0)
+Class <org.gradle.internal.logging.console.DefaultAnsiExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAnsiExecutor.java:0)
+Class <org.gradle.internal.logging.console.DefaultAnsiFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAnsiFactory.java:0)
+Class <org.gradle.internal.logging.console.DefaultColorMap$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultColorMap.java:0)
+Class <org.gradle.internal.logging.console.DefaultColorMap$AttributeColor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultColorMap.java:0)
+Class <org.gradle.internal.logging.console.DefaultColorMap$BrightForegroundColor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultColorMap.java:0)
+Class <org.gradle.internal.logging.console.DefaultColorMap$CompositeColor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultColorMap.java:0)
+Class <org.gradle.internal.logging.console.DefaultColorMap$ForegroundColor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultColorMap.java:0)
+Class <org.gradle.internal.logging.console.DefaultColorMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultColorMap.java:0)
+Class <org.gradle.internal.logging.console.DefaultRedrawableLabel$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRedrawableLabel.java:0)
+Class <org.gradle.internal.logging.console.DefaultRedrawableLabel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRedrawableLabel.java:0)
+Class <org.gradle.internal.logging.console.DefaultTextArea$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTextArea.java:0)
+Class <org.gradle.internal.logging.console.DefaultTextArea$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTextArea.java:0)
+Class <org.gradle.internal.logging.console.DefaultTextArea$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTextArea.java:0)
+Class <org.gradle.internal.logging.console.DefaultTextArea> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTextArea.java:0)
+Class <org.gradle.internal.logging.console.DefaultWorkInProgressFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkInProgressFormatter.java:0)
+Class <org.gradle.internal.logging.console.FlushConsoleListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FlushConsoleListener.java:0)
+Class <org.gradle.internal.logging.console.Label> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Label.java:0)
+Class <org.gradle.internal.logging.console.MultiLineBuildProgressArea> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiLineBuildProgressArea.java:0)
+Class <org.gradle.internal.logging.console.ProgressBar> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressBar.java:0)
+Class <org.gradle.internal.logging.console.ProgressOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressOperation.java:0)
+Class <org.gradle.internal.logging.console.ProgressOperations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressOperations.java:0)
+Class <org.gradle.internal.logging.console.RedrawableLabel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RedrawableLabel.java:0)
+Class <org.gradle.internal.logging.console.StyledLabel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledLabel.java:0)
+Class <org.gradle.internal.logging.console.StyledTextOutputBackedRenderer$OutputEventTextOutputImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextOutputBackedRenderer.java:0)
+Class <org.gradle.internal.logging.console.StyledTextOutputBackedRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextOutputBackedRenderer.java:0)
+Class <org.gradle.internal.logging.console.TextArea> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextArea.java:0)
+Class <org.gradle.internal.logging.console.ThrottlingOutputEventListener$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ThrottlingOutputEventListener.java:0)
+Class <org.gradle.internal.logging.console.ThrottlingOutputEventListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ThrottlingOutputEventListener.java:0)
+Class <org.gradle.internal.logging.console.UserInputConsoleRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserInputConsoleRenderer.java:0)
+Class <org.gradle.internal.logging.console.UserInputStandardOutputRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserInputStandardOutputRenderer.java:0)
+Class <org.gradle.internal.logging.console.WorkInProgressRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkInProgressRenderer.java:0)
+Class <org.gradle.internal.logging.console.WorkInProgressRenderer$AssociationLabel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkInProgressRenderer.java:0)
+Class <org.gradle.internal.logging.console.WorkInProgressRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkInProgressRenderer.java:0)
+Class <org.gradle.internal.logging.events.CategorisedOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CategorisedOutputEvent.java:0)
+Class <org.gradle.internal.logging.events.EndOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EndOutputEvent.java:0)
+Class <org.gradle.internal.logging.events.FlushOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FlushOutputEvent.java:0)
+Class <org.gradle.internal.logging.events.LogEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogEvent.java:0)
+Class <org.gradle.internal.logging.events.LogLevelChangeEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogLevelChangeEvent.java:0)
+Class <org.gradle.internal.logging.events.LogLevelConverter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogLevelConverter.java:0)
+Class <org.gradle.internal.logging.events.LogLevelConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogLevelConverter.java:0)
+Class <org.gradle.internal.logging.events.OutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEvent.java:0)
+Class <org.gradle.internal.logging.events.OutputEventListener$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventListener.java:0)
+Class <org.gradle.internal.logging.events.OutputEventListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventListener.java:0)
+Class <org.gradle.internal.logging.events.ProgressCompleteEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressCompleteEvent.java:0)
+Class <org.gradle.internal.logging.events.ProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressEvent.java:0)
+Class <org.gradle.internal.logging.events.ProgressStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressStartEvent.java:0)
+Class <org.gradle.internal.logging.events.PromptOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PromptOutputEvent.java:0)
+Class <org.gradle.internal.logging.events.RenderableOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RenderableOutputEvent.java:0)
+Class <org.gradle.internal.logging.events.StyledTextOutputEvent$Span> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextOutputEvent.java:0)
+Class <org.gradle.internal.logging.events.StyledTextOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextOutputEvent.java:0)
+Class <org.gradle.internal.logging.events.UpdateNowEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UpdateNowEvent.java:0)
+Class <org.gradle.internal.logging.events.UserInputRequestEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserInputRequestEvent.java:0)
+Class <org.gradle.internal.logging.events.UserInputResumeEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserInputResumeEvent.java:0)
+Class <org.gradle.internal.logging.events.operations.LogEventBuildOperationProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogEventBuildOperationProgressDetails.java:0)
+Class <org.gradle.internal.logging.events.operations.ProgressStartBuildOperationProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressStartBuildOperationProgressDetails.java:0)
+Class <org.gradle.internal.logging.events.operations.StyledTextBuildOperationProgressDetails$Span> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextBuildOperationProgressDetails.java:0)
+Class <org.gradle.internal.logging.events.operations.StyledTextBuildOperationProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextBuildOperationProgressDetails.java:0)
+Class <org.gradle.internal.logging.format.DurationFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DurationFormatter.java:0)
+Class <org.gradle.internal.logging.format.LogHeaderFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogHeaderFormatter.java:0)
+Class <org.gradle.internal.logging.format.PrettyPrefixedLogHeaderFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrettyPrefixedLogHeaderFormatter.java:0)
+Class <org.gradle.internal.logging.format.TersePrettyDurationFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TersePrettyDurationFormatter.java:0)
+Class <org.gradle.internal.logging.progress.DefaultProgressLoggerFactory$ProgressLoggerImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProgressLoggerFactory.java:0)
+Class <org.gradle.internal.logging.progress.DefaultProgressLoggerFactory$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProgressLoggerFactory.java:0)
+Class <org.gradle.internal.logging.progress.DefaultProgressLoggerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProgressLoggerFactory.java:0)
+Class <org.gradle.internal.logging.progress.ProgressListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressListener.java:0)
+Class <org.gradle.internal.logging.progress.ProgressLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLogger.java:0)
+Class <org.gradle.internal.logging.progress.ProgressLoggerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLoggerFactory.java:0)
+Class <org.gradle.internal.logging.serializer.LogEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogEventSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.LogLevelChangeEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogLevelChangeEventSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.ProgressCompleteEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressCompleteEventSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.ProgressEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressEventSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.ProgressStartEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressStartEventSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.PromptOutputEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PromptOutputEventSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.SpanSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpanSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.StyledTextOutputEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextOutputEventSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.UserInputRequestEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserInputRequestEventSerializer.java:0)
+Class <org.gradle.internal.logging.serializer.UserInputResumeEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserInputResumeEventSerializer.java:0)
+Class <org.gradle.internal.logging.services.DefaultLoggingManager$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLoggingManager.java:0)
+Class <org.gradle.internal.logging.services.DefaultLoggingManager$ConsoleAttachment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLoggingManager.java:0)
+Class <org.gradle.internal.logging.services.DefaultLoggingManager$ProcessConsoleAttachment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLoggingManager.java:0)
+Class <org.gradle.internal.logging.services.DefaultLoggingManager$StartableLoggingRouter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLoggingManager.java:0)
+Class <org.gradle.internal.logging.services.DefaultLoggingManager$StartableLoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLoggingManager.java:0)
+Class <org.gradle.internal.logging.services.DefaultLoggingManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLoggingManager.java:0)
+Class <org.gradle.internal.logging.services.DefaultLoggingManagerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLoggingManagerFactory.java:0)
+Class <org.gradle.internal.logging.services.DefaultStyledTextOutputFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStyledTextOutputFactory.java:0)
+Class <org.gradle.internal.logging.services.LoggingBackedStyledTextOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingBackedStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.services.LoggingServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingServiceRegistry.java:0)
+Class <org.gradle.internal.logging.services.LoggingServiceRegistry$CommandLineLogging> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingServiceRegistry.java:0)
+Class <org.gradle.internal.logging.services.LoggingServiceRegistry$NestedLogging> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingServiceRegistry.java:0)
+Class <org.gradle.internal.logging.services.LoggingServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingServiceRegistry.java:0)
+Class <org.gradle.internal.logging.services.ProgressLoggingBridge> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLoggingBridge.java:0)
+Class <org.gradle.internal.logging.services.TextStreamOutputEventListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextStreamOutputEventListener.java:0)
+Class <org.gradle.internal.logging.sink.AnsiConsoleUtil$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiConsoleUtil.java:0)
+Class <org.gradle.internal.logging.sink.AnsiConsoleUtil$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiConsoleUtil.java:0)
+Class <org.gradle.internal.logging.sink.AnsiConsoleUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnsiConsoleUtil.java:0)
+Class <org.gradle.internal.logging.sink.ConsoleConfigureAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsoleConfigureAction.java:0)
+Class <org.gradle.internal.logging.sink.ErrorOutputDispatchingListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ErrorOutputDispatchingListener.java:0)
+Class <org.gradle.internal.logging.sink.GroupingProgressLogEventGenerator$OperationGroup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroupingProgressLogEventGenerator.java:0)
+Class <org.gradle.internal.logging.sink.GroupingProgressLogEventGenerator$OperationState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroupingProgressLogEventGenerator.java:0)
+Class <org.gradle.internal.logging.sink.GroupingProgressLogEventGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroupingProgressLogEventGenerator.java:0)
+Class <org.gradle.internal.logging.sink.LogEventDispatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogEventDispatcher.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventListenerManager$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventListenerManager.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventListenerManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventListenerManager.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventRenderer.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventRenderer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventRenderer.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventRenderer$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventRenderer.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventRenderer$LazyListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventRenderer.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventRenderer$SnapshotImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventRenderer.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventRenderer.java:0)
+Class <org.gradle.internal.logging.sink.OutputEventTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventTransformer.java:0)
+Class <org.gradle.internal.logging.sink.ProgressLogEventGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLogEventGenerator.java:0)
+Class <org.gradle.internal.logging.sink.ProgressLogEventGenerator$Operation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLogEventGenerator.java:0)
+Class <org.gradle.internal.logging.sink.ProgressLogEventGenerator$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLogEventGenerator.java:0)
+Class <org.gradle.internal.logging.sink.ProgressLogEventGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLogEventGenerator.java:0)
+Class <org.gradle.internal.logging.slf4j.BuildOperationAwareLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationAwareLogger.java:0)
+Class <org.gradle.internal.logging.slf4j.ContextAwareTaskLogger$MessageRewriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextAwareTaskLogger.java:0)
+Class <org.gradle.internal.logging.slf4j.ContextAwareTaskLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextAwareTaskLogger.java:0)
+Class <org.gradle.internal.logging.slf4j.DefaultContextAwareTaskLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultContextAwareTaskLogger.java:0)
+Class <org.gradle.internal.logging.slf4j.MessageRewritingBuildOperationAwareLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageRewritingBuildOperationAwareLogger.java:0)
+Class <org.gradle.internal.logging.slf4j.OutputEventListenerBackedLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventListenerBackedLogger.java:0)
+Class <org.gradle.internal.logging.slf4j.OutputEventListenerBackedLoggerContext$NoOpLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventListenerBackedLoggerContext.java:0)
+Class <org.gradle.internal.logging.slf4j.OutputEventListenerBackedLoggerContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputEventListenerBackedLoggerContext.java:0)
+Class <org.gradle.internal.logging.slf4j.Slf4jLoggingConfigurer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Slf4jLoggingConfigurer.java:0)
+Class <org.gradle.internal.logging.source.DefaultStdErrLoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStdErrLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.DefaultStdOutLoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStdOutLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.JavaUtilLoggingSystem$SnapshotImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaUtilLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.JavaUtilLoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaUtilLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.NoOpLoggingSystem$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoOpLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.NoOpLoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoOpLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.PrintStreamLoggingSystem$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrintStreamLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.PrintStreamLoggingSystem$OutputEventDestination> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrintStreamLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.PrintStreamLoggingSystem$PrintStreamDestination> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrintStreamLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.PrintStreamLoggingSystem$SnapshotImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrintStreamLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.PrintStreamLoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrintStreamLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.StdErrLoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StdErrLoggingSystem.java:0)
+Class <org.gradle.internal.logging.source.StdOutLoggingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StdOutLoggingSystem.java:0)
+Class <org.gradle.internal.logging.text.AbstractLineChoppingStyledTextOutput$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLineChoppingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractLineChoppingStyledTextOutput$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLineChoppingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractLineChoppingStyledTextOutput$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLineChoppingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractLineChoppingStyledTextOutput$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLineChoppingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractLineChoppingStyledTextOutput$SeenFromEol> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLineChoppingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractLineChoppingStyledTextOutput$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLineChoppingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractLineChoppingStyledTextOutput$StateContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLineChoppingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractLineChoppingStyledTextOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLineChoppingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractStyledTextOutput$StyleOverrideTextOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractStyledTextOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.AbstractStyledTextOutputFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractStyledTextOutputFactory.java:0)
+Class <org.gradle.internal.logging.text.BufferingStyledTextOutput$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BufferingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.BufferingStyledTextOutput$ChangeStyleAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BufferingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.BufferingStyledTextOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BufferingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.DiagnosticsVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DiagnosticsVisitor.java:0)
+Class <org.gradle.internal.logging.text.LinePrefixingStyledTextOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LinePrefixingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.Span> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Span.java:0)
+Class <org.gradle.internal.logging.text.StreamBackedStandardOutputListener$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamBackedStandardOutputListener.java:0)
+Class <org.gradle.internal.logging.text.StreamBackedStandardOutputListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamBackedStandardOutputListener.java:0)
+Class <org.gradle.internal.logging.text.StreamingStyledTextOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamingStyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.StreamingStyledTextOutputFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamingStyledTextOutputFactory.java:0)
+Class <org.gradle.internal.logging.text.Style$Color> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Style.java:0)
+Class <org.gradle.internal.logging.text.Style$Emphasis> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Style.java:0)
+Class <org.gradle.internal.logging.text.Style> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Style.java:0)
+Class <org.gradle.internal.logging.text.StyledTextOutput$Style> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.StyledTextOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextOutput.java:0)
+Class <org.gradle.internal.logging.text.StyledTextOutputFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextOutputFactory.java:0)
+Class <org.gradle.internal.logging.text.TreeFormatter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeFormatter.java:0)
+Class <org.gradle.internal.logging.text.TreeFormatter$DefaultPrefixer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeFormatter.java:0)
+Class <org.gradle.internal.logging.text.TreeFormatter$Node> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeFormatter.java:0)
+Class <org.gradle.internal.logging.text.TreeFormatter$NumberedPrefixer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeFormatter.java:0)
+Class <org.gradle.internal.logging.text.TreeFormatter$Prefixer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeFormatter.java:0)
+Class <org.gradle.internal.logging.text.TreeFormatter$Separator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeFormatter.java:0)
+Class <org.gradle.internal.logging.text.TreeFormatter$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeFormatter.java:0)
+Class <org.gradle.internal.logging.text.TreeFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeFormatter.java:0)
+Class <org.gradle.internal.logging.util.Log4jBannedVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Log4jBannedVersion.java:0)
+Class <org.gradle.internal.management.DefaultDependencyResolutionManagement$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDependencyResolutionManagement.java:0)
+Class <org.gradle.internal.management.DefaultDependencyResolutionManagement$ComponentMetadataRulesRegistar> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDependencyResolutionManagement.java:0)
+Class <org.gradle.internal.management.DefaultVersionCatalogBuilderContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionCatalogBuilderContainer.java:0)
+Class <org.gradle.internal.management.DependencyResolutionManagementInternal$RepositoriesModeInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResolutionManagementInternal.java:0)
+Class <org.gradle.internal.management.DependencyResolutionManagementInternal$RulesModeInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResolutionManagementInternal.java:0)
+Class <org.gradle.internal.management.DependencyResolutionManagementInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyResolutionManagementInternal.java:0)
+Class <org.gradle.internal.management.ToolchainManagementInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolchainManagementInternal.java:0)
+Class <org.gradle.internal.management.VersionCatalogBuilderInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionCatalogBuilderInternal.java:0)
+Class <org.gradle.internal.metaobject.AbstractDynamicObject$CustomMessageMissingMethodException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.AbstractDynamicObject$CustomMissingMethodExecutionFailed> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.AbstractDynamicObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.BeanDynamicObject$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BeanDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.BeanDynamicObject$ClassAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BeanDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.BeanDynamicObject$GroovyObjectAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BeanDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.BeanDynamicObject$MapAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BeanDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BeanDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.BeanDynamicObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BeanDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.CompositeDynamicObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeDynamicObject.java:0)
+Class <org.gradle.internal.metaobject.ConfigureDelegate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureDelegate.java:0)
+Class <org.gradle.internal.metaobject.DynamicInvokeResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DynamicInvokeResult.java:0)
+Class <org.gradle.internal.metaobject.DynamicObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DynamicObject.java:0)
+Class <org.gradle.internal.metaobject.DynamicObjectUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DynamicObjectUtil.java:0)
+Class <org.gradle.internal.metaobject.MethodAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodAccess.java:0)
+Class <org.gradle.internal.metaobject.MethodMixIn> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodMixIn.java:0)
+Class <org.gradle.internal.metaobject.PropertyAccess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertyAccess.java:0)
+Class <org.gradle.internal.metaobject.PropertyMixIn> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertyMixIn.java:0)
+Class <org.gradle.internal.model.CalculatedModelValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CalculatedModelValue.java:0)
+Class <org.gradle.internal.model.CalculatedValue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CalculatedValue.java:0)
+Class <org.gradle.internal.model.CalculatedValueContainer$CalculationState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CalculatedValueContainer.java:0)
+Class <org.gradle.internal.model.CalculatedValueContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CalculatedValueContainer.java:0)
+Class <org.gradle.internal.model.CalculatedValueContainerFactory$SupplierBackedCalculator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CalculatedValueContainerFactory.java:0)
+Class <org.gradle.internal.model.CalculatedValueContainerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CalculatedValueContainerFactory.java:0)
+Class <org.gradle.internal.model.ModelContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelContainer.java:0)
+Class <org.gradle.internal.model.RuleBasedPluginListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBasedPluginListener.java:0)
+Class <org.gradle.internal.model.StateTransitionController$CurrentState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateTransitionController.java:0)
+Class <org.gradle.internal.model.StateTransitionController$Failed> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateTransitionController.java:0)
+Class <org.gradle.internal.model.StateTransitionController$InState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateTransitionController.java:0)
+Class <org.gradle.internal.model.StateTransitionController$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateTransitionController.java:0)
+Class <org.gradle.internal.model.StateTransitionController$TransitioningToNewState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateTransitionController.java:0)
+Class <org.gradle.internal.model.StateTransitionController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateTransitionController.java:0)
+Class <org.gradle.internal.model.StateTransitionControllerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StateTransitionControllerFactory.java:0)
+Class <org.gradle.internal.model.ValueCalculator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValueCalculator.java:0)
+Class <org.gradle.internal.nativeintegration.EnvironmentModificationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnvironmentModificationResult.java:0)
+Class <org.gradle.internal.nativeintegration.NativeCapabilities> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeCapabilities.java:0)
+Class <org.gradle.internal.nativeintegration.NativeIntegrationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeIntegrationException.java:0)
+Class <org.gradle.internal.nativeintegration.NativeIntegrationUnavailableException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeIntegrationUnavailableException.java:0)
+Class <org.gradle.internal.nativeintegration.ProcessEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessEnvironment.java:0)
+Class <org.gradle.internal.nativeintegration.ReflectiveEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReflectiveEnvironment.java:0)
+Class <org.gradle.internal.nativeintegration.console.ConsoleDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsoleDetector.java:0)
+Class <org.gradle.internal.nativeintegration.console.ConsoleMetaData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsoleMetaData.java:0)
+Class <org.gradle.internal.nativeintegration.console.FallbackConsoleDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FallbackConsoleDetector.java:0)
+Class <org.gradle.internal.nativeintegration.console.FallbackConsoleMetaData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FallbackConsoleMetaData.java:0)
+Class <org.gradle.internal.nativeintegration.console.NativePlatformConsoleDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformConsoleDetector.java:0)
+Class <org.gradle.internal.nativeintegration.console.NativePlatformConsoleMetaData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformConsoleMetaData.java:0)
+Class <org.gradle.internal.nativeintegration.console.TestConsoleMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestConsoleMetadata.java:0)
+Class <org.gradle.internal.nativeintegration.console.TestOverrideConsoleDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOverrideConsoleDetector.java:0)
+Class <org.gradle.internal.nativeintegration.console.WindowsConsoleDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsConsoleDetector.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.FileCanonicalizer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileCanonicalizer.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.FileMetadataAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileMetadataAccessor.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.FileModeAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileModeAccessor.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.FileModeMutator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileModeMutator.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.FileSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileSystem.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.Symlink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Symlink.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.jdk7.Jdk7FileCanonicalizer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Jdk7FileCanonicalizer.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.jdk7.Jdk7Symlink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Jdk7Symlink.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.jdk7.NioFileMetadataAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NioFileMetadataAccessor.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.jdk7.PosixFilePermissionConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PosixFilePermissionConverter.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.jdk7.PosixJdk7FilePermissionHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PosixJdk7FilePermissionHandler.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.jdk7.WindowsJdk7Symlink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsJdk7Symlink.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.EmptyChmod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EmptyChmod.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.FallbackFileCanonicalizer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FallbackFileCanonicalizer.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.FallbackFileMetadataAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FallbackFileMetadataAccessor.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.FallbackStat> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FallbackStat.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.FileSystemServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileSystemServices.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.GenericFileSystem$Factory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GenericFileSystem.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.GenericFileSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GenericFileSystem.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.JdkFallbackHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JdkFallbackHelper.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.NativePlatformBackedChmod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformBackedChmod.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.NativePlatformBackedFileMetadataAccessor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformBackedFileMetadataAccessor.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.NativePlatformBackedFileMetadataAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformBackedFileMetadataAccessor.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.NativePlatformBackedStat> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformBackedStat.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.NativePlatformBackedSymlink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformBackedSymlink.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.UnavailablePosixFiles> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnavailablePosixFiles.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.UnsupportedFilePermissions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsupportedFilePermissions.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.UnsupportedSymlink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsupportedSymlink.java:0)
+Class <org.gradle.internal.nativeintegration.filesystem.services.WindowsSymlink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsSymlink.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.DefaultJansiRuntimeResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJansiRuntimeResolver.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.JansiBootPathConfigurer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JansiBootPathConfigurer.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.JansiLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JansiLibrary.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.JansiLibraryFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JansiLibraryFactory.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.JansiLibraryFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JansiLibraryFactory.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.JansiOperatingSystemSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JansiOperatingSystemSupport.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.JansiRuntimeResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JansiRuntimeResolver.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.JansiStorage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JansiStorage.java:0)
+Class <org.gradle.internal.nativeintegration.jansi.JansiStorageLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JansiStorageLocator.java:0)
+Class <org.gradle.internal.nativeintegration.jna.UnsupportedEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsupportedEnvironment.java:0)
+Class <org.gradle.internal.nativeintegration.processenvironment.AbstractProcessEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractProcessEnvironment.java:0)
+Class <org.gradle.internal.nativeintegration.processenvironment.NativePlatformBackedProcessEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformBackedProcessEnvironment.java:0)
+Class <org.gradle.internal.nativeintegration.services.FileSystems> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileSystems.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices$BrokenService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices$FixedHostname> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices$NativeFeatures$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices$NativeFeatures$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices$NativeFeatures> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.nativeintegration.services.NativeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeServices.java:0)
+Class <org.gradle.internal.operations.logging.BuildOperationLogInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationLogInfo.java:0)
+Class <org.gradle.internal.operations.logging.BuildOperationLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationLogger.java:0)
+Class <org.gradle.internal.operations.logging.BuildOperationLoggerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationLoggerFactory.java:0)
+Class <org.gradle.internal.operations.logging.DefaultBuildOperationLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildOperationLogger.java:0)
+Class <org.gradle.internal.operations.logging.DefaultBuildOperationLoggerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildOperationLoggerFactory.java:0)
+Class <org.gradle.internal.operations.logging.LogEventBuildOperationProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogEventBuildOperationProgressDetails.java:0)
+Class <org.gradle.internal.operations.logging.LogEventLevel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogEventLevel.java:0)
+Class <org.gradle.internal.operations.logging.LoggingBuildOperationProgressBroadcaster> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingBuildOperationProgressBroadcaster.java:0)
+Class <org.gradle.internal.operations.logging.ProgressStartBuildOperationProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressStartBuildOperationProgressDetails.java:0)
+Class <org.gradle.internal.operations.logging.StyledTextBuildOperationProgressDetails$Span> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextBuildOperationProgressDetails.java:0)
+Class <org.gradle.internal.operations.logging.StyledTextBuildOperationProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StyledTextBuildOperationProgressDetails.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationRecord$Progress> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationRecord.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationRecord> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationRecord.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTrace$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTrace.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTrace$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTrace.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTrace$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTrace.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTrace$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTrace.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTrace$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTrace.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTrace$PendingOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTrace.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTrace> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTrace.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTree$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTree.java:0)
+Class <org.gradle.internal.operations.trace.BuildOperationTree> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTree.java:0)
+Class <org.gradle.internal.operations.trace.CustomOperationTraceSerialization> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CustomOperationTraceSerialization.java:0)
+Class <org.gradle.internal.operations.trace.SerializedOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SerializedOperation.java:0)
+Class <org.gradle.internal.operations.trace.SerializedOperationFinish> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SerializedOperationFinish.java:0)
+Class <org.gradle.internal.operations.trace.SerializedOperationProgress> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SerializedOperationProgress.java:0)
+Class <org.gradle.internal.operations.trace.SerializedOperationStart> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SerializedOperationStart.java:0)
+Class <org.gradle.internal.operations.trace.StrictMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StrictMap.java:0)
+Class <org.gradle.internal.os.OperatingSystem$FreeBSD> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperatingSystem.java:0)
+Class <org.gradle.internal.os.OperatingSystem$Linux> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperatingSystem.java:0)
+Class <org.gradle.internal.os.OperatingSystem$MacOs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperatingSystem.java:0)
+Class <org.gradle.internal.os.OperatingSystem$Solaris> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperatingSystem.java:0)
+Class <org.gradle.internal.os.OperatingSystem$Unix> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperatingSystem.java:0)
+Class <org.gradle.internal.os.OperatingSystem$Windows> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperatingSystem.java:0)
+Class <org.gradle.internal.os.OperatingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperatingSystem.java:0)
+Class <org.gradle.internal.problems.DefaultProblemLocationAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProblemLocationAnalyzer.java:0)
+Class <org.gradle.internal.process.ArgCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgCollector.java:0)
+Class <org.gradle.internal.process.ArgWriter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgWriter.java:0)
+Class <org.gradle.internal.process.ArgWriter$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgWriter.java:0)
+Class <org.gradle.internal.process.ArgWriter$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgWriter.java:0)
+Class <org.gradle.internal.process.ArgWriter$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgWriter.java:0)
+Class <org.gradle.internal.process.ArgWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgWriter.java:0)
+Class <org.gradle.internal.progress.PercentageProgressFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PercentageProgressFormatter.java:0)
+Class <org.gradle.internal.progress.ProgressFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressFormatter.java:0)
+Class <org.gradle.internal.progress.SimpleProgressFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleProgressFormatter.java:0)
+Class <org.gradle.internal.reflect.problems.ValidationProblemId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidationProblemId.java:0)
+Class <org.gradle.internal.reflect.validation.AbstractValidationProblemBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractValidationProblemBuilder.java:0)
+Class <org.gradle.internal.reflect.validation.DefaultPropertyValidationProblemBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPropertyValidationProblemBuilder.java:0)
+Class <org.gradle.internal.reflect.validation.DefaultSolutionBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSolutionBuilder.java:0)
+Class <org.gradle.internal.reflect.validation.DefaultTypeValidationProblemBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeValidationProblemBuilder.java:0)
+Class <org.gradle.internal.reflect.validation.Location> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Location.java:0)
+Class <org.gradle.internal.reflect.validation.PropertyProblemBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertyProblemBuilder.java:0)
+Class <org.gradle.internal.reflect.validation.PropertyProblemBuilderInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertyProblemBuilderInternal.java:0)
+Class <org.gradle.internal.reflect.validation.ReplayingTypeValidationContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReplayingTypeValidationContext.java:0)
+Class <org.gradle.internal.reflect.validation.Severity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Severity.java:0)
+Class <org.gradle.internal.reflect.validation.SolutionBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SolutionBuilder.java:0)
+Class <org.gradle.internal.reflect.validation.TypeProblemBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeProblemBuilder.java:0)
+Class <org.gradle.internal.reflect.validation.TypeValidationContext$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeValidationContext.java:0)
+Class <org.gradle.internal.reflect.validation.TypeValidationContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeValidationContext.java:0)
+Class <org.gradle.internal.reflect.validation.TypeValidationProblem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeValidationProblem.java:0)
+Class <org.gradle.internal.reflect.validation.TypeValidationProblemLocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeValidationProblemLocation.java:0)
+Class <org.gradle.internal.reflect.validation.TypeValidationProblemRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeValidationProblemRenderer.java:0)
+Class <org.gradle.internal.reflect.validation.UserManualReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserManualReference.java:0)
+Class <org.gradle.internal.reflect.validation.ValidationProblemBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidationProblemBuilder.java:0)
+Class <org.gradle.internal.reflect.validation.WithDocumentationBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WithDocumentationBuilder.java:0)
+Class <org.gradle.internal.remote.Address> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Address.java:0)
+Class <org.gradle.internal.remote.ConnectionAcceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionAcceptor.java:0)
+Class <org.gradle.internal.remote.MessagingClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessagingClient.java:0)
+Class <org.gradle.internal.remote.MessagingServer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessagingServer.java:0)
+Class <org.gradle.internal.remote.ObjectConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectConnection.java:0)
+Class <org.gradle.internal.remote.ObjectConnectionBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectConnectionBuilder.java:0)
+Class <org.gradle.internal.remote.internal.ConnectCompletion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectCompletion.java:0)
+Class <org.gradle.internal.remote.internal.ConnectException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectException.java:0)
+Class <org.gradle.internal.remote.internal.Connection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Connection.java:0)
+Class <org.gradle.internal.remote.internal.IncomingConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncomingConnector.java:0)
+Class <org.gradle.internal.remote.internal.KryoBackedMessageSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KryoBackedMessageSerializer.java:0)
+Class <org.gradle.internal.remote.internal.MessageIOException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageIOException.java:0)
+Class <org.gradle.internal.remote.internal.MessageSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageSerializer.java:0)
+Class <org.gradle.internal.remote.internal.OutgoingConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutgoingConnector.java:0)
+Class <org.gradle.internal.remote.internal.RecoverableMessageIOException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RecoverableMessageIOException.java:0)
+Class <org.gradle.internal.remote.internal.RemoteConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RemoteConnection.java:0)
+Class <org.gradle.internal.remote.internal.hub.ConnectionSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionSet.java:0)
+Class <org.gradle.internal.remote.internal.hub.ConnectionState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionState.java:0)
+Class <org.gradle.internal.remote.internal.hub.DefaultMethodArgsSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.DefaultMethodArgsSerializer$ArraySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.DefaultMethodArgsSerializer$EmptyArraySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.DefaultMethodArgsSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.IncomingQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncomingQueue.java:0)
+Class <org.gradle.internal.remote.internal.hub.InterHubMessageSerializer$MessageReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterHubMessageSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.InterHubMessageSerializer$MessageWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterHubMessageSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.InterHubMessageSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterHubMessageSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.JavaSerializationBackedMethodArgsSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaSerializationBackedMethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.JavaSerializationBackedMethodArgsSerializer$ArraySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaSerializationBackedMethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.JavaSerializationBackedMethodArgsSerializer$EmptyArraySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaSerializationBackedMethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.JavaSerializationBackedMethodArgsSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaSerializationBackedMethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHub$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHub.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHub$ChannelDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHub.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHub$ConnectionDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHub.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHub$ConnectionReceive> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHub.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHub$Discard> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHub.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHub$Handler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHub.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHub$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHub.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHub> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHub.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHubBackedClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHubBackedClient.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHubBackedObjectConnection.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHubBackedObjectConnection.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHubBackedObjectConnection.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHubBackedObjectConnection.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHubBackedServer$ConnectEventAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHubBackedServer.java:0)
+Class <org.gradle.internal.remote.internal.hub.MessageHubBackedServer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessageHubBackedServer.java:0)
+Class <org.gradle.internal.remote.internal.hub.MethodArgsSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodArgsSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.MethodInvocationSerializer$MethodDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodInvocationSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.MethodInvocationSerializer$MethodInvocationReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodInvocationSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.MethodInvocationSerializer$MethodInvocationWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodInvocationSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.MethodInvocationSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodInvocationSerializer.java:0)
+Class <org.gradle.internal.remote.internal.hub.OutgoingQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutgoingQueue.java:0)
+Class <org.gradle.internal.remote.internal.hub.RejectedMessageListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RejectedMessageListener.java:0)
+Class <org.gradle.internal.remote.internal.hub.StreamFailureHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamFailureHandler.java:0)
+Class <org.gradle.internal.remote.internal.hub.protocol.ChannelIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChannelIdentifier.java:0)
+Class <org.gradle.internal.remote.internal.hub.protocol.ChannelMessage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChannelMessage.java:0)
+Class <org.gradle.internal.remote.internal.hub.protocol.EndOfStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EndOfStream.java:0)
+Class <org.gradle.internal.remote.internal.hub.protocol.InterHubMessage$Delivery> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterHubMessage.java:0)
+Class <org.gradle.internal.remote.internal.hub.protocol.InterHubMessage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterHubMessage.java:0)
+Class <org.gradle.internal.remote.internal.hub.protocol.RejectedMessage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RejectedMessage.java:0)
+Class <org.gradle.internal.remote.internal.hub.protocol.Routable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Routable.java:0)
+Class <org.gradle.internal.remote.internal.hub.protocol.StreamFailureMessage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamFailureMessage.java:0)
+Class <org.gradle.internal.remote.internal.hub.queue.EndPointQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EndPointQueue.java:0)
+Class <org.gradle.internal.remote.internal.hub.queue.MultiChannelQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiChannelQueue.java:0)
+Class <org.gradle.internal.remote.internal.hub.queue.MultiEndPointQueue$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiEndPointQueue.java:0)
+Class <org.gradle.internal.remote.internal.hub.queue.MultiEndPointQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiEndPointQueue.java:0)
+Class <org.gradle.internal.remote.internal.hub.queue.QueueInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (QueueInitializer.java:0)
+Class <org.gradle.internal.remote.services.MessagingServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MessagingServices.java:0)
+Class <org.gradle.internal.resolve.ArtifactNotFoundException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactNotFoundException.java:0)
+Class <org.gradle.internal.resolve.ArtifactResolveException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactResolveException.java:0)
+Class <org.gradle.internal.resolve.ModuleVersionNotFoundException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleVersionNotFoundException.java:0)
+Class <org.gradle.internal.resolve.ModuleVersionResolveException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleVersionResolveException.java:0)
+Class <org.gradle.internal.resolve.RejectedByAttributesVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RejectedByAttributesVersion.java:0)
+Class <org.gradle.internal.resolve.RejectedByRuleVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RejectedByRuleVersion.java:0)
+Class <org.gradle.internal.resolve.RejectedBySelectorVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RejectedBySelectorVersion.java:0)
+Class <org.gradle.internal.resolve.RejectedVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RejectedVersion.java:0)
+Class <org.gradle.internal.resolve.ResolveExceptionAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolveExceptionAnalyzer.java:0)
+Class <org.gradle.internal.resolve.caching.CachingRuleExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.ComponentMetadataRuleExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentMetadataRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.ComponentMetadataRuleExecutor$SimpleResolvedModuleVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentMetadataRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.ComponentMetadataRuleExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentMetadataRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.ComponentMetadataSupplierRuleExecutor$SimpleResolvedModuleVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentMetadataSupplierRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.ComponentMetadataSupplierRuleExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentMetadataSupplierRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.CrossBuildCachingRuleExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.CrossBuildCachingRuleExecutor$AnySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.CrossBuildCachingRuleExecutor$CacheEntrySerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.CrossBuildCachingRuleExecutor$CacheEntrySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.CrossBuildCachingRuleExecutor$CachedEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.CrossBuildCachingRuleExecutor$DefaultImplicitInputRegistrar> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.CrossBuildCachingRuleExecutor$EntryValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.CrossBuildCachingRuleExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingRuleExecutor.java:0)
+Class <org.gradle.internal.resolve.caching.DesugaringAttributeContainerSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DesugaringAttributeContainerSerializer.java:0)
+Class <org.gradle.internal.resolve.caching.ImplicitInputRecord> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImplicitInputRecord.java:0)
+Class <org.gradle.internal.resolve.caching.ImplicitInputRecorder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImplicitInputRecorder.java:0)
+Class <org.gradle.internal.resolve.caching.ImplicitInputsCapturingInstantiator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImplicitInputsCapturingInstantiator.java:0)
+Class <org.gradle.internal.resolve.caching.ImplicitInputsCapturingInstantiator$DefaultCapturingServicesRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImplicitInputsCapturingInstantiator.java:0)
+Class <org.gradle.internal.resolve.caching.ImplicitInputsProvidingService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImplicitInputsProvidingService.java:0)
+Class <org.gradle.internal.resolve.resolver.ArtifactResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactResolver.java:0)
+Class <org.gradle.internal.resolve.resolver.ArtifactSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactSelector.java:0)
+Class <org.gradle.internal.resolve.resolver.ComponentMetaDataResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentMetaDataResolver.java:0)
+Class <org.gradle.internal.resolve.resolver.DefaultArtifactSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArtifactSelector.java:0)
+Class <org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependencyToComponentIdResolver.java:0)
+Class <org.gradle.internal.resolve.resolver.OriginArtifactSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OriginArtifactSelector.java:0)
+Class <org.gradle.internal.resolve.resolver.ResolveContextToComponentResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolveContextToComponentResolver.java:0)
+Class <org.gradle.internal.resolve.result.BuildableArtifactFileResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableArtifactFileResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableArtifactResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableArtifactResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableArtifactSetResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableComponentIdResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableComponentIdResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableComponentResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableComponentResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableModuleComponentMetaDataResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableModuleComponentMetaDataResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableModuleVersionListingResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableModuleVersionListingResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.BuildableTypedResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableTypedResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.ComponentIdResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentIdResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.ComponentResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.ComponentSelectionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentSelectionContext.java:0)
+Class <org.gradle.internal.resolve.result.DefaultBuildableArtifactFileResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableArtifactFileResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.DefaultBuildableArtifactResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableArtifactResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.DefaultBuildableArtifactSetResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableArtifactSetResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.DefaultBuildableComponentIdResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableComponentIdResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.DefaultBuildableComponentResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableComponentResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.DefaultBuildableModuleComponentMetaDataResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableModuleComponentMetaDataResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.DefaultBuildableModuleVersionListingResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableModuleVersionListingResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.DefaultBuildableTypedResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildableTypedResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.DefaultResourceAwareResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceAwareResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.ErroringResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ErroringResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.ResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResolveResult.java:0)
+Class <org.gradle.internal.resolve.result.ResourceAwareResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceAwareResolveResult.java:0)
+Class <org.gradle.internal.resource.AbstractExternalResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractExternalResource.java:0)
+Class <org.gradle.internal.resource.CachingTextResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingTextResource.java:0)
+Class <org.gradle.internal.resource.DefaultTextFileResourceLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTextFileResourceLoader.java:0)
+Class <org.gradle.internal.resource.DownloadedUriTextResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DownloadedUriTextResource.java:0)
+Class <org.gradle.internal.resource.EmptyFileTextResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EmptyFileTextResource.java:0)
+Class <org.gradle.internal.resource.ExternalResource$ContentAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResource.java:0)
+Class <org.gradle.internal.resource.ExternalResource$ContentAndMetadataAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResource.java:0)
+Class <org.gradle.internal.resource.ExternalResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResource.java:0)
+Class <org.gradle.internal.resource.ExternalResourceListBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceListBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceListBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceListBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceListBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceListBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceReadBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceReadBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceReadBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceReadBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceReadBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceReadBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceReadMetadataBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceReadMetadataBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceReadMetadataBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceReadMetadataBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceReadMetadataBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceReadMetadataBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceReadResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceReadResult.java:0)
+Class <org.gradle.internal.resource.ExternalResourceRepository> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceRepository.java:0)
+Class <org.gradle.internal.resource.ExternalResourceWriteBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceWriteBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceWriteBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceWriteBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceWriteBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceWriteBuildOperationType.java:0)
+Class <org.gradle.internal.resource.ExternalResourceWriteResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceWriteResult.java:0)
+Class <org.gradle.internal.resource.LocalBinaryResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalBinaryResource.java:0)
+Class <org.gradle.internal.resource.ReadableContent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReadableContent.java:0)
+Class <org.gradle.internal.resource.Resource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Resource.java:0)
+Class <org.gradle.internal.resource.ResourceExceptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceExceptions.java:0)
+Class <org.gradle.internal.resource.ResourceIsAFolderException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceIsAFolderException.java:0)
+Class <org.gradle.internal.resource.ResourceLocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceLocation.java:0)
+Class <org.gradle.internal.resource.StringTextResource$StringResourceLocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringTextResource.java:0)
+Class <org.gradle.internal.resource.StringTextResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringTextResource.java:0)
+Class <org.gradle.internal.resource.TextFileResourceLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextFileResourceLoader.java:0)
+Class <org.gradle.internal.resource.TextResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextResource.java:0)
+Class <org.gradle.internal.resource.TextUriResourceLoader$Factory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextUriResourceLoader.java:0)
+Class <org.gradle.internal.resource.TextUriResourceLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextUriResourceLoader.java:0)
+Class <org.gradle.internal.resource.UriTextResource$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UriTextResource.java:0)
+Class <org.gradle.internal.resource.UriTextResource$UriResourceLocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UriTextResource.java:0)
+Class <org.gradle.internal.resource.UriTextResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UriTextResource.java:0)
+Class <org.gradle.internal.resource.cached.AbstractCachedIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractCachedIndex.java:0)
+Class <org.gradle.internal.resource.cached.ByUrlCachedExternalResourceIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ByUrlCachedExternalResourceIndex.java:0)
+Class <org.gradle.internal.resource.cached.CachedExternalResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedExternalResource.java:0)
+Class <org.gradle.internal.resource.cached.CachedExternalResourceIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedExternalResourceIndex.java:0)
+Class <org.gradle.internal.resource.cached.CachedItem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachedItem.java:0)
+Class <org.gradle.internal.resource.cached.DefaultCachedExternalResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedExternalResource.java:0)
+Class <org.gradle.internal.resource.cached.DefaultCachedExternalResourceIndex$CachedExternalResourceSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedExternalResourceIndex.java:0)
+Class <org.gradle.internal.resource.cached.DefaultCachedExternalResourceIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCachedExternalResourceIndex.java:0)
+Class <org.gradle.internal.resource.cached.DefaultExternalResourceFileStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExternalResourceFileStore.java:0)
+Class <org.gradle.internal.resource.cached.DefaultExternalResourceFileStore$Factory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExternalResourceFileStore.java:0)
+Class <org.gradle.internal.resource.cached.DefaultExternalResourceFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExternalResourceFileStore.java:0)
+Class <org.gradle.internal.resource.cached.ExternalResourceFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceFileStore.java:0)
+Class <org.gradle.internal.resource.cached.TwoStageByUrlCachedExternalResourceIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageByUrlCachedExternalResourceIndex.java:0)
+Class <org.gradle.internal.resource.cached.TwoStageExternalResourceFileStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageExternalResourceFileStore.java:0)
+Class <org.gradle.internal.resource.cached.TwoStageExternalResourceFileStore$DelegatingFileAccessTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageExternalResourceFileStore.java:0)
+Class <org.gradle.internal.resource.cached.TwoStageExternalResourceFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TwoStageExternalResourceFileStore.java:0)
+Class <org.gradle.internal.resource.connector.ResourceConnectorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceConnectorFactory.java:0)
+Class <org.gradle.internal.resource.connector.ResourceConnectorSpecification> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceConnectorSpecification.java:0)
+Class <org.gradle.internal.resource.local.AbstractLocallyAvailableResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLocallyAvailableResource.java:0)
+Class <org.gradle.internal.resource.local.AbstractLocallyAvailableResourceFinder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLocallyAvailableResourceFinder.java:0)
+Class <org.gradle.internal.resource.local.ByteArrayReadableContent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ByteArrayReadableContent.java:0)
+Class <org.gradle.internal.resource.local.CompositeLocallyAvailableResourceFinder$CompositeLocallyAvailableResourceCandidates> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeLocallyAvailableResourceFinder.java:0)
+Class <org.gradle.internal.resource.local.CompositeLocallyAvailableResourceFinder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeLocallyAvailableResourceFinder.java:0)
+Class <org.gradle.internal.resource.local.DefaultLocallyAvailableExternalResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocallyAvailableExternalResource.java:0)
+Class <org.gradle.internal.resource.local.DefaultLocallyAvailableResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLocallyAvailableResource.java:0)
+Class <org.gradle.internal.resource.local.DefaultPathKeyFileStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPathKeyFileStore.java:0)
+Class <org.gradle.internal.resource.local.DefaultPathKeyFileStore$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPathKeyFileStore.java:0)
+Class <org.gradle.internal.resource.local.DefaultPathKeyFileStore$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPathKeyFileStore.java:0)
+Class <org.gradle.internal.resource.local.FileReadableContent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileReadableContent.java:0)
+Class <org.gradle.internal.resource.local.FileResourceConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileResourceConnector.java:0)
+Class <org.gradle.internal.resource.local.FileResourceListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileResourceListener.java:0)
+Class <org.gradle.internal.resource.local.FileResourceRepository> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileResourceRepository.java:0)
+Class <org.gradle.internal.resource.local.FileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileStore.java:0)
+Class <org.gradle.internal.resource.local.FileStoreAddActionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileStoreAddActionException.java:0)
+Class <org.gradle.internal.resource.local.FileStoreException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileStoreException.java:0)
+Class <org.gradle.internal.resource.local.FileStoreSearcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileStoreSearcher.java:0)
+Class <org.gradle.internal.resource.local.GroupedAndNamedUniqueFileStore$Grouper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroupedAndNamedUniqueFileStore.java:0)
+Class <org.gradle.internal.resource.local.GroupedAndNamedUniqueFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroupedAndNamedUniqueFileStore.java:0)
+Class <org.gradle.internal.resource.local.LazyLocallyAvailableResourceCandidates> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LazyLocallyAvailableResourceCandidates.java:0)
+Class <org.gradle.internal.resource.local.LocalFileStandInExternalResource$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalFileStandInExternalResource.java:0)
+Class <org.gradle.internal.resource.local.LocalFileStandInExternalResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocalFileStandInExternalResource.java:0)
+Class <org.gradle.internal.resource.local.LocallyAvailableExternalResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocallyAvailableExternalResource.java:0)
+Class <org.gradle.internal.resource.local.LocallyAvailableResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocallyAvailableResource.java:0)
+Class <org.gradle.internal.resource.local.LocallyAvailableResourceCandidates> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocallyAvailableResourceCandidates.java:0)
+Class <org.gradle.internal.resource.local.LocallyAvailableResourceFinder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocallyAvailableResourceFinder.java:0)
+Class <org.gradle.internal.resource.local.LocallyAvailableResourceFinderSearchableFileStoreAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocallyAvailableResourceFinderSearchableFileStoreAdapter.java:0)
+Class <org.gradle.internal.resource.local.ModificationTimeFileAccessTimeJournal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModificationTimeFileAccessTimeJournal.java:0)
+Class <org.gradle.internal.resource.local.PathKeyFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PathKeyFileStore.java:0)
+Class <org.gradle.internal.resource.local.PathNormalisingKeyFileStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PathNormalisingKeyFileStore.java:0)
+Class <org.gradle.internal.resource.local.ivy.LocallyAvailableResourceFinderFactory$NoMavenLocalRepositoryResourceFinder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocallyAvailableResourceFinderFactory.java:0)
+Class <org.gradle.internal.resource.local.ivy.LocallyAvailableResourceFinderFactory$NoMavenLocalRepositoryResourceFinder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocallyAvailableResourceFinderFactory.java:0)
+Class <org.gradle.internal.resource.local.ivy.LocallyAvailableResourceFinderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LocallyAvailableResourceFinderFactory.java:0)
+Class <org.gradle.internal.resource.local.ivy.PatternBasedLocallyAvailableResourceFinder$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternBasedLocallyAvailableResourceFinder.java:0)
+Class <org.gradle.internal.resource.local.ivy.PatternBasedLocallyAvailableResourceFinder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternBasedLocallyAvailableResourceFinder.java:0)
+Class <org.gradle.internal.resource.local.ivy.PatternBasedLocallyAvailableResourceFinder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PatternBasedLocallyAvailableResourceFinder.java:0)
+Class <org.gradle.internal.resource.metadata.DefaultExternalResourceMetaData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExternalResourceMetaData.java:0)
+Class <org.gradle.internal.resource.metadata.ExternalResourceMetaData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceMetaData.java:0)
+Class <org.gradle.internal.resource.metadata.ExternalResourceMetaDataCompare> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExternalResourceMetaDataCompare.java:0)
+Class <org.gradle.internal.resource.transport.AbstractRepositoryTransport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractRepositoryTransport.java:0)
+Class <org.gradle.internal.resource.transport.DefaultExternalResourceRepository> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExternalResourceRepository.java:0)
+Class <org.gradle.internal.resource.transport.ResourceConnectorRepositoryTransport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceConnectorRepositoryTransport.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3Client> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3Client.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ConnectionProperties> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ConnectionProperties.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ConnectorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ConnectorFactory.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3RegionalResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3RegionalResource.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3Resource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3Resource.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ResourceConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ResourceConnector.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ResourceResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ResourceResolver.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ResourceResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ResourceResolver.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ResourcesPluginServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ResourcesPluginServiceRegistry$AuthenticationSchemeAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ResourcesPluginServiceRegistry$GlobalScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.aws.s3.S3ResourcesPluginServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (S3ResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.file.FileConnectorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileConnectorFactory.java:0)
+Class <org.gradle.internal.resource.transport.file.FileTransport$FileCacheAwareExternalResourceAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileTransport.java:0)
+Class <org.gradle.internal.resource.transport.file.FileTransport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileTransport.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsClient$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsClient.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsClient.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsConnectionProperties> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsConnectionProperties.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsConnectorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsConnectorFactory.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsResource.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsResourceConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsResourceConnector.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsResourcesPluginServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsResourcesPluginServiceRegistry$GlobalScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.GcsResourcesPluginServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GcsResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.ResourceMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceMapper.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.RetryHttpInitializerWrapper$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RetryHttpInitializerWrapper.java:0)
+Class <org.gradle.internal.resource.transport.gcp.gcs.RetryHttpInitializerWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RetryHttpInitializerWrapper.java:0)
+Class <org.gradle.internal.resource.transport.http.AllowFollowForMutatingMethodRedirectStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllowFollowForMutatingMethodRedirectStrategy.java:0)
+Class <org.gradle.internal.resource.transport.http.AlwaysFollowAndPreserveMethodRedirectStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AlwaysFollowAndPreserveMethodRedirectStrategy.java:0)
+Class <org.gradle.internal.resource.transport.http.ApacheDirectoryListingParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApacheDirectoryListingParser.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultHttpSettings$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHttpSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultHttpSettings$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHttpSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultHttpSettings$2$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHttpSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultHttpSettings$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHttpSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultHttpSettings$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHttpSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultHttpSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHttpSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultSslContextFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSslContextFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultSslContextFactory$SslContextCacheLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSslContextFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultSslContextFactory$SynchronizedSystemPropertiesCacheLoader$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSslContextFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultSslContextFactory$SynchronizedSystemPropertiesCacheLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSslContextFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.DefaultSslContextFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSslContextFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientConfigurer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientConfigurer.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientConfigurer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientConfigurer.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientConfigurer$PreemptiveAuth> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientConfigurer.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientConfigurer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientConfigurer.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientHelper$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientHelper.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientHelper$Factory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientHelper.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientHelper$FailureFromRedirectLocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientHelper.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientHelper.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientHttpHeaderCredentials> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientHttpHeaderCredentials.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpClientResponse> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpClientResponse.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpConnectorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpConnectorFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpErrorStatusCodeException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpErrorStatusCodeException.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpHeaderAuthScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpHeaderAuthScheme.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpHeaderSchemeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpHeaderSchemeFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpProxySettings$HttpProxy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpProxySettings.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpProxySettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpProxySettings.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpRequestException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpRequestException.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpResourceAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpResourceAccessor.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpResourceLister> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpResourceLister.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpResourceUploader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpResourceUploader.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpResourcesPluginServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpResourcesPluginServiceRegistry$AuthenticationSchemeAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpResourcesPluginServiceRegistry$GlobalScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpResourcesPluginServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpResponseResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpResponseResource.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpSettings$RedirectMethodHandlingStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.HttpTimeoutSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpTimeoutSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.JavaSystemPropertiesHttpProxySettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaSystemPropertiesHttpProxySettings.java:0)
+Class <org.gradle.internal.resource.transport.http.JavaSystemPropertiesHttpTimeoutSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaSystemPropertiesHttpTimeoutSettings.java:0)
+Class <org.gradle.internal.resource.transport.http.JavaSystemPropertiesProxySettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaSystemPropertiesProxySettings.java:0)
+Class <org.gradle.internal.resource.transport.http.JavaSystemPropertiesSecureHttpProxySettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaSystemPropertiesSecureHttpProxySettings.java:0)
+Class <org.gradle.internal.resource.transport.http.RedirectVerifyingStrategyDecorator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RedirectVerifyingStrategyDecorator.java:0)
+Class <org.gradle.internal.resource.transport.http.RepeatableInputStreamEntity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RepeatableInputStreamEntity.java:0)
+Class <org.gradle.internal.resource.transport.http.SslContextFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SslContextFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.ntlm.NTLMCredentials> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NTLMCredentials.java:0)
+Class <org.gradle.internal.resource.transport.http.ntlm.NTLMSchemeFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NTLMSchemeFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.ntlm.NTLMSchemeFactory$JCIFSEngine> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NTLMSchemeFactory.java:0)
+Class <org.gradle.internal.resource.transport.http.ntlm.NTLMSchemeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NTLMSchemeFactory.java:0)
+Class <org.gradle.internal.resource.transport.sftp.LockableSftpClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockableSftpClient.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpClientFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpClientFactory.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpClientFactory$DefaultLockableSftpClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpClientFactory.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpClientFactory$SftpClientCreator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpClientFactory.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpClientFactory$SftpClientCreator$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpClientFactory.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpClientFactory$SftpClientCreator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpClientFactory.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpClientFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpClientFactory.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpConnectorFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpConnectorFactory.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpHost> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpHost.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpResource.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpResourceAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpResourceAccessor.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpResourceLister> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpResourceLister.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpResourceUploader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpResourceUploader.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpResourcesPluginServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpResourcesPluginServiceRegistry$GlobalScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resource.transport.sftp.SftpResourcesPluginServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SftpResourcesPluginServiceRegistry.java:0)
+Class <org.gradle.internal.resources.AbstractResourceLockRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractResourceLockRegistry.java:0)
+Class <org.gradle.internal.resources.AbstractResourceLockRegistry$ResourceLockProducer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractResourceLockRegistry.java:0)
+Class <org.gradle.internal.resources.AbstractResourceLockRegistry$ThreadLockDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractResourceLockRegistry.java:0)
+Class <org.gradle.internal.resources.AbstractResourceLockRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractResourceLockRegistry.java:0)
+Class <org.gradle.internal.resources.AbstractTrackedResourceLock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTrackedResourceLock.java:0)
+Class <org.gradle.internal.resources.AllProjectsLock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllProjectsLock.java:0)
+Class <org.gradle.internal.resources.DefaultLease> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLease.java:0)
+Class <org.gradle.internal.resources.DefaultResourceLockCoordinationService$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceLockCoordinationService.java:0)
+Class <org.gradle.internal.resources.DefaultResourceLockCoordinationService$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceLockCoordinationService.java:0)
+Class <org.gradle.internal.resources.DefaultResourceLockCoordinationService$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceLockCoordinationService.java:0)
+Class <org.gradle.internal.resources.DefaultResourceLockCoordinationService$AcquireLocks> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceLockCoordinationService.java:0)
+Class <org.gradle.internal.resources.DefaultResourceLockCoordinationService$DefaultResourceLockState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceLockCoordinationService.java:0)
+Class <org.gradle.internal.resources.DefaultResourceLockCoordinationService$ReleaseLocks> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceLockCoordinationService.java:0)
+Class <org.gradle.internal.resources.DefaultResourceLockCoordinationService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceLockCoordinationService.java:0)
+Class <org.gradle.internal.resources.ExclusiveAccessResourceLock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExclusiveAccessResourceLock.java:0)
+Class <org.gradle.internal.resources.LeaseHolder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LeaseHolder.java:0)
+Class <org.gradle.internal.resources.LockCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockCache.java:0)
+Class <org.gradle.internal.resources.LockCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LockCache.java:0)
+Class <org.gradle.internal.resources.ProjectLeaseRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLeaseRegistry.java:0)
+Class <org.gradle.internal.resources.ProjectLock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLock.java:0)
+Class <org.gradle.internal.resources.ProjectLockRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLockRegistry.java:0)
+Class <org.gradle.internal.resources.ProjectLockRegistry$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLockRegistry.java:0)
+Class <org.gradle.internal.resources.ProjectLockRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLockRegistry.java:0)
+Class <org.gradle.internal.resources.ProjectLockStatistics> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLockStatistics.java:0)
+Class <org.gradle.internal.resources.ResourceLock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceLock.java:0)
+Class <org.gradle.internal.resources.ResourceLockContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceLockContainer.java:0)
+Class <org.gradle.internal.resources.ResourceLockCoordinationService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceLockCoordinationService.java:0)
+Class <org.gradle.internal.resources.ResourceLockRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceLockRegistry.java:0)
+Class <org.gradle.internal.resources.ResourceLockState$Disposition> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceLockState.java:0)
+Class <org.gradle.internal.resources.ResourceLockState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResourceLockState.java:0)
+Class <org.gradle.internal.resources.SharedResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SharedResource.java:0)
+Class <org.gradle.internal.resources.SharedResourceLeaseRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SharedResourceLeaseRegistry.java:0)
+Class <org.gradle.internal.resources.TaskExecutionLock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionLock.java:0)
+Class <org.gradle.internal.resources.TaskExecutionLockRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionLockRegistry.java:0)
+Class <org.gradle.internal.resources.TaskExecutionLockRegistry$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionLockRegistry.java:0)
+Class <org.gradle.internal.resources.TaskExecutionLockRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionLockRegistry.java:0)
+Class <org.gradle.internal.rules.ClosureBackedRuleAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureBackedRuleAction.java:0)
+Class <org.gradle.internal.rules.DefaultRuleActionAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRuleActionAdapter.java:0)
+Class <org.gradle.internal.rules.DefaultRuleActionValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRuleActionValidator.java:0)
+Class <org.gradle.internal.rules.NoInputsRuleAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoInputsRuleAction.java:0)
+Class <org.gradle.internal.rules.RuleAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleAction.java:0)
+Class <org.gradle.internal.rules.RuleActionAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleActionAdapter.java:0)
+Class <org.gradle.internal.rules.RuleActionValidationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleActionValidationException.java:0)
+Class <org.gradle.internal.rules.RuleActionValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleActionValidator.java:0)
+Class <org.gradle.internal.rules.RuleSourceBackedRuleAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleSourceBackedRuleAction.java:0)
+Class <org.gradle.internal.rules.RuleSourceBackedRuleAction$MultiMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleSourceBackedRuleAction.java:0)
+Class <org.gradle.internal.rules.RuleSourceBackedRuleAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleSourceBackedRuleAction.java:0)
+Class <org.gradle.internal.rules.SpecRuleAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecRuleAction.java:0)
+Class <org.gradle.internal.scan.NotUsedByScanPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotUsedByScanPlugin.java:0)
+Class <org.gradle.internal.scan.UsedByScanPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UsedByScanPlugin.java:0)
+Class <org.gradle.internal.scan.config.BuildScanConfig$Attributes> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanConfig.java:0)
+Class <org.gradle.internal.scan.config.BuildScanConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanConfig.java:0)
+Class <org.gradle.internal.scan.config.BuildScanConfigProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanConfigProvider.java:0)
+Class <org.gradle.internal.scan.config.BuildScanPluginMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanPluginMetadata.java:0)
+Class <org.gradle.internal.scan.eob.BuildScanEndOfBuildNotifier$BuildResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanEndOfBuildNotifier.java:0)
+Class <org.gradle.internal.scan.eob.BuildScanEndOfBuildNotifier$BuildScanResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanEndOfBuildNotifier.java:0)
+Class <org.gradle.internal.scan.eob.BuildScanEndOfBuildNotifier$Listener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanEndOfBuildNotifier.java:0)
+Class <org.gradle.internal.scan.eob.BuildScanEndOfBuildNotifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanEndOfBuildNotifier.java:0)
+Class <org.gradle.internal.scan.scopeids.BuildScanScopeIds> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanScopeIds.java:0)
+Class <org.gradle.internal.scan.time.BuildScanBuildStartedTime> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanBuildStartedTime.java:0)
+Class <org.gradle.internal.scan.time.BuildScanClock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScanClock.java:0)
+Class <org.gradle.internal.scopeids.DefaultPersistentScopeIdLoader$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPersistentScopeIdLoader.java:0)
+Class <org.gradle.internal.scopeids.DefaultPersistentScopeIdLoader$ScopeParams> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPersistentScopeIdLoader.java:0)
+Class <org.gradle.internal.scopeids.DefaultPersistentScopeIdLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPersistentScopeIdLoader.java:0)
+Class <org.gradle.internal.scopeids.PersistentScopeIdLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentScopeIdLoader.java:0)
+Class <org.gradle.internal.scopeids.PersistentScopeIdStoreFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentScopeIdStoreFactory.java:0)
+Class <org.gradle.internal.scopeids.ScopeIdsServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScopeIdsServices.java:0)
+Class <org.gradle.internal.scopeids.UniqueIdSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UniqueIdSerializer.java:0)
+Class <org.gradle.internal.scopeids.id.BuildInvocationScopeId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInvocationScopeId.java:0)
+Class <org.gradle.internal.scopeids.id.ScopeId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScopeId.java:0)
+Class <org.gradle.internal.scopeids.id.UserScopeId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UserScopeId.java:0)
+Class <org.gradle.internal.scopeids.id.WorkspaceScopeId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkspaceScopeId.java:0)
+Class <org.gradle.internal.serialization.Cached$Deferred> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Cached.java:0)
+Class <org.gradle.internal.serialization.Cached$Fixed> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Cached.java:0)
+Class <org.gradle.internal.serialization.Cached> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Cached.java:0)
+Class <org.gradle.internal.serialization.Transient$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Transient.java:0)
+Class <org.gradle.internal.serialization.Transient$Discarded> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Transient.java:0)
+Class <org.gradle.internal.serialization.Transient$ImmutableTransient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Transient.java:0)
+Class <org.gradle.internal.serialization.Transient$MutableTransient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Transient.java:0)
+Class <org.gradle.internal.serialization.Transient$Var> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Transient.java:0)
+Class <org.gradle.internal.serialization.Transient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Transient.java:0)
+Class <org.gradle.internal.serialize.AbstractCollectionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractCollectionSerializer.java:0)
+Class <org.gradle.internal.serialize.AbstractDecoder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDecoder.java:0)
+Class <org.gradle.internal.serialize.AbstractDecoder$DecoderStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDecoder.java:0)
+Class <org.gradle.internal.serialize.AbstractDecoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDecoder.java:0)
+Class <org.gradle.internal.serialize.AbstractEncoder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractEncoder.java:0)
+Class <org.gradle.internal.serialize.AbstractEncoder$EncoderStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractEncoder.java:0)
+Class <org.gradle.internal.serialize.AbstractEncoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractEncoder.java:0)
+Class <org.gradle.internal.serialize.AbstractSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractSerializer.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$BigDecimalSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$BigIntegerSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$BooleanSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$ByteArraySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$ByteSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$CharSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$DoubleSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$EnumSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$FileSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$FloatSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$IntegerSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$LongSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$PathSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$ShortSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$StringMapSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$StringSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory$ThrowableSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.BaseSerializerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSerializerFactory.java:0)
+Class <org.gradle.internal.serialize.ContextualPlaceholderException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContextualPlaceholderException.java:0)
+Class <org.gradle.internal.serialize.Decoder$DecodeAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Decoder.java:0)
+Class <org.gradle.internal.serialize.Decoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Decoder.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializer.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry$HierarchySerializerMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry$InstanceBasedSerializerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry$SerializerClassMatcherStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry$SerializerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry$StrictSerializerMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry$TaggedTypeSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry$TypeInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.DefaultSerializerRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.Encoder$EncodeAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Encoder.java:0)
+Class <org.gradle.internal.serialize.Encoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Encoder.java:0)
+Class <org.gradle.internal.serialize.ExceptionPlaceholder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.ExceptionPlaceholder$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.ExceptionPlaceholder$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.ExceptionPlaceholder$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.ExceptionPlaceholder$Java14NullPointerExceptionUsefulMessageSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.ExceptionPlaceholder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.ExceptionReplacingObjectInputStream$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionReplacingObjectInputStream.java:0)
+Class <org.gradle.internal.serialize.ExceptionReplacingObjectInputStream$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionReplacingObjectInputStream.java:0)
+Class <org.gradle.internal.serialize.ExceptionReplacingObjectInputStream$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionReplacingObjectInputStream.java:0)
+Class <org.gradle.internal.serialize.ExceptionReplacingObjectInputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionReplacingObjectInputStream.java:0)
+Class <org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionReplacingObjectOutputStream.java:0)
+Class <org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionReplacingObjectOutputStream.java:0)
+Class <org.gradle.internal.serialize.ExceptionReplacingObjectOutputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionReplacingObjectOutputStream.java:0)
+Class <org.gradle.internal.serialize.FlushableEncoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FlushableEncoder.java:0)
+Class <org.gradle.internal.serialize.HashCodeSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HashCodeSerializer.java:0)
+Class <org.gradle.internal.serialize.InputStreamBackedDecoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputStreamBackedDecoder.java:0)
+Class <org.gradle.internal.serialize.IntSetSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IntSetSerializer.java:0)
+Class <org.gradle.internal.serialize.InterningStringSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InterningStringSerializer.java:0)
+Class <org.gradle.internal.serialize.ListSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ListSerializer.java:0)
+Class <org.gradle.internal.serialize.MapSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MapSerializer.java:0)
+Class <org.gradle.internal.serialize.Message> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Message.java:0)
+Class <org.gradle.internal.serialize.NestedExceptionPlaceholder$Kind> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.NestedExceptionPlaceholder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.NullSafeStringSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NullSafeStringSerializer.java:0)
+Class <org.gradle.internal.serialize.ObjectReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectReader.java:0)
+Class <org.gradle.internal.serialize.ObjectWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectWriter.java:0)
+Class <org.gradle.internal.serialize.OutputStreamBackedEncoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputStreamBackedEncoder.java:0)
+Class <org.gradle.internal.serialize.PlaceholderAssertionError> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlaceholderAssertionError.java:0)
+Class <org.gradle.internal.serialize.PlaceholderException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlaceholderException.java:0)
+Class <org.gradle.internal.serialize.PlaceholderExceptionSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlaceholderExceptionSupport.java:0)
+Class <org.gradle.internal.serialize.Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Serializer.java:0)
+Class <org.gradle.internal.serialize.SerializerRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SerializerRegistry.java:0)
+Class <org.gradle.internal.serialize.Serializers$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Serializers.java:0)
+Class <org.gradle.internal.serialize.Serializers$StatefulSerializerAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Serializers.java:0)
+Class <org.gradle.internal.serialize.Serializers$StatefulSerializerAdapter$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Serializers.java:0)
+Class <org.gradle.internal.serialize.Serializers$StatefulSerializerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Serializers.java:0)
+Class <org.gradle.internal.serialize.Serializers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Serializers.java:0)
+Class <org.gradle.internal.serialize.SetSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SetSerializer.java:0)
+Class <org.gradle.internal.serialize.StackTraceElementPlaceholder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StackTraceElementPlaceholder.java:0)
+Class <org.gradle.internal.serialize.StatefulSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StatefulSerializer.java:0)
+Class <org.gradle.internal.serialize.TopLevelExceptionPlaceholder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TopLevelExceptionPlaceholder.java:0)
+Class <org.gradle.internal.serialize.kryo.KryoBackedDecoder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KryoBackedDecoder.java:0)
+Class <org.gradle.internal.serialize.kryo.KryoBackedDecoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KryoBackedDecoder.java:0)
+Class <org.gradle.internal.serialize.kryo.KryoBackedEncoder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KryoBackedEncoder.java:0)
+Class <org.gradle.internal.serialize.kryo.KryoBackedEncoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KryoBackedEncoder.java:0)
+Class <org.gradle.internal.serialize.kryo.StringDeduplicatingKryoBackedDecoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringDeduplicatingKryoBackedDecoder.java:0)
+Class <org.gradle.internal.serialize.kryo.StringDeduplicatingKryoBackedEncoder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StringDeduplicatingKryoBackedEncoder.java:0)
+Class <org.gradle.internal.serialize.kryo.TypeSafeSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeSafeSerializer.java:0)
+Class <org.gradle.internal.serialize.kryo.TypeSafeSerializer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeSafeSerializer.java:0)
+Class <org.gradle.internal.serialize.kryo.TypeSafeSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeSafeSerializer.java:0)
+Class <org.gradle.internal.service.AbstractServiceMethod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractServiceMethod.java:0)
+Class <org.gradle.internal.service.AnnotatedServiceLifecycleHandler$Registration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotatedServiceLifecycleHandler.java:0)
+Class <org.gradle.internal.service.AnnotatedServiceLifecycleHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnnotatedServiceLifecycleHandler.java:0)
+Class <org.gradle.internal.service.CachingServiceLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingServiceLocator.java:0)
+Class <org.gradle.internal.service.ContainsServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ContainsServices.java:0)
+Class <org.gradle.internal.service.DefaultServiceLocator$ServiceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceLocator.java:0)
+Class <org.gradle.internal.service.DefaultServiceLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceLocator.java:0)
+Class <org.gradle.internal.service.DefaultServiceMethodFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceMethodFactory.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$ClassInspector$ClassDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$ClassInspector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$CollectingVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$CollectionService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$CompositeServiceProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$ConstructorService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$FactoryMethodService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$FactoryService$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$FactoryService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$FixedInstanceService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$InstanceUnpackingVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$ManagedObjectServiceProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$OwnServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$ParentServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$RegistrationWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$SingletonService$BindState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$SingletonService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry$ThisAsService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.DefaultServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultServiceRegistry.java:0)
+Class <org.gradle.internal.service.InjectUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InjectUtil.java:0)
+Class <org.gradle.internal.service.MethodHandleBasedServiceMethod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodHandleBasedServiceMethod.java:0)
+Class <org.gradle.internal.service.MethodHandleBasedServiceMethodFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodHandleBasedServiceMethodFactory.java:0)
+Class <org.gradle.internal.service.ReflectionBasedServiceMethod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReflectionBasedServiceMethod.java:0)
+Class <org.gradle.internal.service.ReflectionBasedServiceMethodFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReflectionBasedServiceMethodFactory.java:0)
+Class <org.gradle.internal.service.RelevantMethods> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RelevantMethods.java:0)
+Class <org.gradle.internal.service.RelevantMethodsBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RelevantMethodsBuilder.java:0)
+Class <org.gradle.internal.service.Service> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Service.java:0)
+Class <org.gradle.internal.service.ServiceCreationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceCreationException.java:0)
+Class <org.gradle.internal.service.ServiceLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceLocator.java:0)
+Class <org.gradle.internal.service.ServiceLookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceLookup.java:0)
+Class <org.gradle.internal.service.ServiceLookupException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceLookupException.java:0)
+Class <org.gradle.internal.service.ServiceMethod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceMethod.java:0)
+Class <org.gradle.internal.service.ServiceMethodFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceMethodFactory.java:0)
+Class <org.gradle.internal.service.ServiceProvider$Visitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceProvider.java:0)
+Class <org.gradle.internal.service.ServiceProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceProvider.java:0)
+Class <org.gradle.internal.service.ServiceRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceRegistration.java:0)
+Class <org.gradle.internal.service.ServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceRegistry.java:0)
+Class <org.gradle.internal.service.ServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceRegistry.java:0)
+Class <org.gradle.internal.service.ServiceRegistryBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceRegistryBuilder.java:0)
+Class <org.gradle.internal.service.ServiceValidationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ServiceValidationException.java:0)
+Class <org.gradle.internal.service.TypeStringFormatter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeStringFormatter.java:0)
+Class <org.gradle.internal.service.UnknownServiceException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnknownServiceException.java:0)
+Class <org.gradle.internal.session.BuildSessionActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionActionExecutor.java:0)
+Class <org.gradle.internal.session.BuildSessionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionContext.java:0)
+Class <org.gradle.internal.session.BuildSessionLifecycleListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionLifecycleListener.java:0)
+Class <org.gradle.internal.session.BuildSessionScopeServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionScopeServices.java:0)
+Class <org.gradle.internal.session.BuildSessionScopeServices$CrossBuildFileHashCacheWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionScopeServices.java:0)
+Class <org.gradle.internal.session.BuildSessionScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionScopeServices.java:0)
+Class <org.gradle.internal.session.BuildSessionState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionState.java:0)
+Class <org.gradle.internal.session.CrossBuildSessionState$Services> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSessionState.java:0)
+Class <org.gradle.internal.session.CrossBuildSessionState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildSessionState.java:0)
+Class <org.gradle.internal.session.DefaultBuildSessionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildSessionContext.java:0)
+Class <org.gradle.internal.state.DefaultManagedFactoryRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultManagedFactoryRegistry.java:0)
+Class <org.gradle.internal.state.Managed> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Managed.java:0)
+Class <org.gradle.internal.state.ManagedFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedFactory.java:0)
+Class <org.gradle.internal.state.ManagedFactoryRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedFactoryRegistry.java:0)
+Class <org.gradle.internal.state.ModelObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelObject.java:0)
+Class <org.gradle.internal.state.OwnerAware> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OwnerAware.java:0)
+Class <org.gradle.internal.time.Clock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Clock.java:0)
+Class <org.gradle.internal.time.CountdownTimer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CountdownTimer.java:0)
+Class <org.gradle.internal.time.DefaultCountdownTimer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCountdownTimer.java:0)
+Class <org.gradle.internal.time.DefaultTimer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTimer.java:0)
+Class <org.gradle.internal.time.MonotonicClock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MonotonicClock.java:0)
+Class <org.gradle.internal.time.Time> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Time.java:0)
+Class <org.gradle.internal.time.TimeFormatting> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeFormatting.java:0)
+Class <org.gradle.internal.time.TimeSource$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeSource.java:0)
+Class <org.gradle.internal.time.TimeSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeSource.java:0)
+Class <org.gradle.internal.time.Timer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Timer.java:0)
+Class <org.gradle.internal.time.TimestampSuppliers$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimestampSuppliers.java:0)
+Class <org.gradle.internal.time.TimestampSuppliers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimestampSuppliers.java:0)
+Class <org.gradle.internal.typeconversion.CharSequenceNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CharSequenceNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.CharSequenceNotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CharSequenceNotationParser.java:0)
+Class <org.gradle.internal.typeconversion.ClosureToSpecNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureToSpecNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.CompositeNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.CrossBuildCachingNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CrossBuildCachingNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$BigDecimalNumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$BigIntegerNumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$BooleanConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$ByteNumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$CharSequenceConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$CharacterConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$DoubleNumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$FloatNumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$IntegerNumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$LongNumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$NumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$ShortNumberConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter$StringConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.DefaultTypeConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.EnumFromCharSequenceNotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnumFromCharSequenceNotationParser.java:0)
+Class <org.gradle.internal.typeconversion.ErrorHandlingNotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ErrorHandlingNotationParser.java:0)
+Class <org.gradle.internal.typeconversion.FlatteningNotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FlatteningNotationParser.java:0)
+Class <org.gradle.internal.typeconversion.IntegerFromCharSequenceNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IntegerFromCharSequenceNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.JustReturningParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JustReturningParser.java:0)
+Class <org.gradle.internal.typeconversion.MapKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MapKey.java:0)
+Class <org.gradle.internal.typeconversion.MapNotationConverter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MapNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.MapNotationConverter$ConvertMethod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MapNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.MapNotationConverter$ConvertMethodCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MapNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.MapNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MapNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.NormalizedTimeUnit> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NormalizedTimeUnit.java:0)
+Class <org.gradle.internal.typeconversion.NotationConvertResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotationConvertResult.java:0)
+Class <org.gradle.internal.typeconversion.NotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.NotationConverterToNotationParserAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotationConverterToNotationParserAdapter.java:0)
+Class <org.gradle.internal.typeconversion.NotationConverterToNotationParserAdapter$ResultImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotationConverterToNotationParserAdapter.java:0)
+Class <org.gradle.internal.typeconversion.NotationConverterToNotationParserAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotationConverterToNotationParserAdapter.java:0)
+Class <org.gradle.internal.typeconversion.NotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotationParser.java:0)
+Class <org.gradle.internal.typeconversion.NotationParserBuilder$LazyDisplayName> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotationParserBuilder.java:0)
+Class <org.gradle.internal.typeconversion.NotationParserBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotationParserBuilder.java:0)
+Class <org.gradle.internal.typeconversion.TimeUnitsParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TimeUnitsParser.java:0)
+Class <org.gradle.internal.typeconversion.TypeConversionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeConversionException.java:0)
+Class <org.gradle.internal.typeconversion.TypeConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeConverter.java:0)
+Class <org.gradle.internal.typeconversion.TypeFilteringNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeFilteringNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.TypeInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeInfo.java:0)
+Class <org.gradle.internal.typeconversion.TypedNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypedNotationConverter.java:0)
+Class <org.gradle.internal.typeconversion.UnsupportedNotationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsupportedNotationException.java:0)
+Class <org.gradle.internal.util.NumberUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NumberUtil.java:0)
+Class <org.gradle.internal.util.PropertiesUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertiesUtils.java:0)
+Class <org.gradle.internal.util.Trie$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Trie.java:0)
+Class <org.gradle.internal.util.Trie$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Trie.java:0)
+Class <org.gradle.internal.util.Trie> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Trie.java:0)
+Class <org.gradle.internal.verifier.HttpRedirectVerifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpRedirectVerifier.java:0)
+Class <org.gradle.internal.verifier.HttpRedirectVerifierFactory$NoopHttpRedirectVerifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpRedirectVerifierFactory.java:0)
+Class <org.gradle.internal.verifier.HttpRedirectVerifierFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HttpRedirectVerifierFactory.java:0)
+Class <org.gradle.internal.watch.options.FileSystemWatchingSettingsFinalizedProgressDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileSystemWatchingSettingsFinalizedProgressDetails.java:0)
+Class <org.gradle.internal.work.AbstractConditionalExecution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractConditionalExecution.java:0)
+Class <org.gradle.internal.work.AsyncWorkCompletion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsyncWorkCompletion.java:0)
+Class <org.gradle.internal.work.AsyncWorkTracker$ProjectLockRetention> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsyncWorkTracker.java:0)
+Class <org.gradle.internal.work.AsyncWorkTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsyncWorkTracker.java:0)
+Class <org.gradle.internal.work.ConditionalExecution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConditionalExecution.java:0)
+Class <org.gradle.internal.work.ConditionalExecutionQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConditionalExecutionQueue.java:0)
+Class <org.gradle.internal.work.ConditionalExecutionQueueFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConditionalExecutionQueueFactory.java:0)
+Class <org.gradle.internal.work.DefaultAsyncWorkTracker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAsyncWorkTracker.java:0)
+Class <org.gradle.internal.work.DefaultAsyncWorkTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAsyncWorkTracker.java:0)
+Class <org.gradle.internal.work.DefaultConditionalExecutionQueue$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConditionalExecutionQueue.java:0)
+Class <org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConditionalExecutionQueue.java:0)
+Class <org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConditionalExecutionQueue.java:0)
+Class <org.gradle.internal.work.DefaultConditionalExecutionQueue$QueueState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConditionalExecutionQueue.java:0)
+Class <org.gradle.internal.work.DefaultConditionalExecutionQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConditionalExecutionQueue.java:0)
+Class <org.gradle.internal.work.DefaultConditionalExecutionQueueFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConditionalExecutionQueueFactory.java:0)
+Class <org.gradle.internal.work.DefaultSynchronizer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSynchronizer.java:0)
+Class <org.gradle.internal.work.DefaultSynchronizer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSynchronizer.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService$4$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService$DefaultWorkerLease> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService$ProjectLockStatisticsImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService$WorkerLeaseLockRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.DefaultWorkerLeaseService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLeaseService.java:0)
+Class <org.gradle.internal.work.NoAvailableWorkerLeaseException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoAvailableWorkerLeaseException.java:0)
+Class <org.gradle.internal.work.Synchronizer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Synchronizer.java:0)
+Class <org.gradle.internal.work.WorkerLeaseRegistry$WorkerLease> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerLeaseRegistry.java:0)
+Class <org.gradle.internal.work.WorkerLeaseRegistry$WorkerLeaseCompletion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerLeaseRegistry.java:0)
+Class <org.gradle.internal.work.WorkerLeaseRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerLeaseRegistry.java:0)
+Class <org.gradle.internal.work.WorkerLeaseService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerLeaseService.java:0)
+Class <org.gradle.internal.work.WorkerThreadRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerThreadRegistry.java:0)
+Class <org.gradle.internal.xml.SimpleMarkupWriter$Context> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleMarkupWriter.java:0)
+Class <org.gradle.internal.xml.SimpleMarkupWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleMarkupWriter.java:0)
+Class <org.gradle.internal.xml.SimpleXmlWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleXmlWriter.java:0)
+Class <org.gradle.internal.xml.XmlTransformer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XmlTransformer.java:0)
+Class <org.gradle.internal.xml.XmlTransformer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XmlTransformer.java:0)
+Class <org.gradle.internal.xml.XmlTransformer$XmlProviderImpl$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XmlTransformer.java:0)
+Class <org.gradle.internal.xml.XmlTransformer$XmlProviderImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XmlTransformer.java:0)
+Class <org.gradle.internal.xml.XmlTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XmlTransformer.java:0)
+Class <org.gradle.internal.xml.XmlValidation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XmlValidation.java:0)
+Class <org.gradle.invocation.DefaultGradle$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradle.java:0)
+Class <org.gradle.invocation.DefaultGradle$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradle.java:0)
+Class <org.gradle.invocation.DefaultGradle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradle.java:0)
+Class <org.gradle.jvm.component.internal.DefaultJvmSoftwareComponent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJvmSoftwareComponent.java:0)
+Class <org.gradle.jvm.component.internal.JvmSoftwareComponentInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmSoftwareComponentInternal.java:0)
+Class <org.gradle.jvm.internal.services.PlatformJvmServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlatformJvmServices.java:0)
+Class <org.gradle.jvm.internal.services.PlatformJvmServices$BuildServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlatformJvmServices.java:0)
+Class <org.gradle.jvm.internal.services.PlatformJvmServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlatformJvmServices.java:0)
+Class <org.gradle.jvm.toolchain.internal.task.ShowToolchainsTask> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShowToolchainsTask.java:0)
+Class <org.gradle.jvm.toolchain.internal.task.ShowToolchainsTaskConfigurator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShowToolchainsTaskConfigurator.java:0)
+Class <org.gradle.jvm.toolchain.internal.task.ToolchainReportRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolchainReportRenderer.java:0)
+Class <org.gradle.language.assembler.internal.DefaultAssembleSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAssembleSpec.java:0)
+Class <org.gradle.language.assembler.plugins.internal.AssembleTaskConfig$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AssembleTaskConfig.java:0)
+Class <org.gradle.language.assembler.plugins.internal.AssembleTaskConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AssembleTaskConfig.java:0)
+Class <org.gradle.language.base.internal.AbstractLanguageSourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLanguageSourceSet.java:0)
+Class <org.gradle.language.base.internal.DefaultProjectSourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectSourceSet.java:0)
+Class <org.gradle.language.base.internal.JointCompileTaskConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JointCompileTaskConfig.java:0)
+Class <org.gradle.language.base.internal.LanguageSourceSetInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LanguageSourceSetInternal.java:0)
+Class <org.gradle.language.base.internal.ProjectLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLayout.java:0)
+Class <org.gradle.language.base.internal.SourceTransformTaskConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceTransformTaskConfig.java:0)
+Class <org.gradle.language.base.internal.compile.CompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileSpec.java:0)
+Class <org.gradle.language.base.internal.compile.Compiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Compiler.java:0)
+Class <org.gradle.language.base.internal.compile.CompilerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerFactory.java:0)
+Class <org.gradle.language.base.internal.compile.CompilerUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerUtil.java:0)
+Class <org.gradle.language.base.internal.compile.VersionAwareCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionAwareCompiler.java:0)
+Class <org.gradle.language.base.internal.plugins.CleanRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CleanRule.java:0)
+Class <org.gradle.language.base.internal.registry.DefaultLanguageTransformContainer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLanguageTransformContainer.java:0)
+Class <org.gradle.language.base.internal.registry.DefaultLanguageTransformContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLanguageTransformContainer.java:0)
+Class <org.gradle.language.base.internal.registry.LanguageTransform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LanguageTransform.java:0)
+Class <org.gradle.language.base.internal.registry.LanguageTransformContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LanguageTransformContainer.java:0)
+Class <org.gradle.language.base.internal.resolve.LibraryResolveException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibraryResolveException.java:0)
+Class <org.gradle.language.base.internal.tasks.StaleOutputCleaner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StaleOutputCleaner.java:0)
+Class <org.gradle.language.c.internal.DefaultCCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCCompileSpec.java:0)
+Class <org.gradle.language.c.internal.DefaultCPCHCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCPCHCompileSpec.java:0)
+Class <org.gradle.language.c.internal.DefaultCSourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCSourceSet.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppApplication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppApplication.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppBinary.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppComponent$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppComponent.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppComponent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppComponent.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppExecutable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppExecutable.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppLibrary.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppPlatform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppPlatform.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppSharedLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppSharedLibrary.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppSourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppSourceSet.java:0)
+Class <org.gradle.language.cpp.internal.DefaultCppStaticLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppStaticLibrary.java:0)
+Class <org.gradle.language.cpp.internal.MainExecutableVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MainExecutableVariant.java:0)
+Class <org.gradle.language.cpp.internal.MainLibraryVariant> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MainLibraryVariant.java:0)
+Class <org.gradle.language.cpp.internal.NativeDependencyCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependencyCache.java:0)
+Class <org.gradle.language.cpp.internal.NativeDependencyCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependencyCache.java:0)
+Class <org.gradle.language.cpp.internal.NativeVariantIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeVariantIdentity.java:0)
+Class <org.gradle.language.cpp.internal.tooling.CppModelBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppModelBuilder.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCompilationDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCompilationDetails.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppApplicationModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppApplicationModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppBinaryModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppBinaryModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppComponentModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppComponentModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppExecutableModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppExecutableModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppLibraryModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppLibraryModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppProjectModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppProjectModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppSharedLibraryModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppSharedLibraryModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppStaticLibraryModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppStaticLibraryModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultCppTestSuiteModel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppTestSuiteModel.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultLinkageDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLinkageDetails.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultMacroDirective> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMacroDirective.java:0)
+Class <org.gradle.language.cpp.internal.tooling.DefaultSourceFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceFile.java:0)
+Class <org.gradle.language.cpp.internal.tooling.ToolingNativeServices$ToolingModelRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingNativeServices.java:0)
+Class <org.gradle.language.cpp.internal.tooling.ToolingNativeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingNativeServices.java:0)
+Class <org.gradle.language.cpp.tasks.internal.DefaultCppCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppCompileSpec.java:0)
+Class <org.gradle.language.cpp.tasks.internal.DefaultCppPCHCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppPCHCompileSpec.java:0)
+Class <org.gradle.language.internal.DefaultBinaryCollection$SingleElementProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBinaryCollection.java:0)
+Class <org.gradle.language.internal.DefaultBinaryCollection$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBinaryCollection.java:0)
+Class <org.gradle.language.internal.DefaultBinaryCollection$TypeFilteringAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBinaryCollection.java:0)
+Class <org.gradle.language.internal.DefaultBinaryCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBinaryCollection.java:0)
+Class <org.gradle.language.internal.DefaultComponentDependencies> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentDependencies.java:0)
+Class <org.gradle.language.internal.DefaultLibraryDependencies> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLibraryDependencies.java:0)
+Class <org.gradle.language.internal.DefaultNativeBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeBinary.java:0)
+Class <org.gradle.language.internal.DefaultNativeComponentFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeComponentFactory.java:0)
+Class <org.gradle.language.internal.NativeComponentFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponentFactory.java:0)
+Class <org.gradle.language.jvm.internal.JvmPluginServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmPluginServiceRegistry.java:0)
+Class <org.gradle.language.jvm.internal.JvmPluginServiceRegistry$ComponentRegistrationAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmPluginServiceRegistry.java:0)
+Class <org.gradle.language.jvm.internal.JvmPluginServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmPluginServiceRegistry.java:0)
+Class <org.gradle.language.nativeplatform.internal.AbstractHeaderExportingDependentSourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractHeaderExportingDependentSourceSet.java:0)
+Class <org.gradle.language.nativeplatform.internal.AbstractHeaderExportingSourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractHeaderExportingSourceSet.java:0)
+Class <org.gradle.language.nativeplatform.internal.AbstractNativeCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractNativeCompileSpec.java:0)
+Class <org.gradle.language.nativeplatform.internal.BuildType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildType.java:0)
+Class <org.gradle.language.nativeplatform.internal.CompileTaskConfig$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileTaskConfig.java:0)
+Class <org.gradle.language.nativeplatform.internal.CompileTaskConfig$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileTaskConfig.java:0)
+Class <org.gradle.language.nativeplatform.internal.CompileTaskConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompileTaskConfig.java:0)
+Class <org.gradle.language.nativeplatform.internal.ComponentWithNames> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentWithNames.java:0)
+Class <org.gradle.language.nativeplatform.internal.ConfigurableComponentWithExecutable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableComponentWithExecutable.java:0)
+Class <org.gradle.language.nativeplatform.internal.ConfigurableComponentWithLinkUsage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableComponentWithLinkUsage.java:0)
+Class <org.gradle.language.nativeplatform.internal.ConfigurableComponentWithRuntimeUsage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableComponentWithRuntimeUsage.java:0)
+Class <org.gradle.language.nativeplatform.internal.ConfigurableComponentWithSharedLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableComponentWithSharedLibrary.java:0)
+Class <org.gradle.language.nativeplatform.internal.ConfigurableComponentWithStaticLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableComponentWithStaticLibrary.java:0)
+Class <org.gradle.language.nativeplatform.internal.DefaultNativeComponent$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeComponent.java:0)
+Class <org.gradle.language.nativeplatform.internal.DefaultNativeComponent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeComponent.java:0)
+Class <org.gradle.language.nativeplatform.internal.DependentSourceSetInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentSourceSetInternal.java:0)
+Class <org.gradle.language.nativeplatform.internal.Dimensions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Dimensions.java:0)
+Class <org.gradle.language.nativeplatform.internal.Expression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Expression.java:0)
+Class <org.gradle.language.nativeplatform.internal.Include> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Include.java:0)
+Class <org.gradle.language.nativeplatform.internal.IncludeDirectives$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.IncludeDirectives> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.IncludeType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeType.java:0)
+Class <org.gradle.language.nativeplatform.internal.Macro> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Macro.java:0)
+Class <org.gradle.language.nativeplatform.internal.MacroFunction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MacroFunction.java:0)
+Class <org.gradle.language.nativeplatform.internal.Names$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Names.java:0)
+Class <org.gradle.language.nativeplatform.internal.Names$Main> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Names.java:0)
+Class <org.gradle.language.nativeplatform.internal.Names$Other> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Names.java:0)
+Class <org.gradle.language.nativeplatform.internal.Names> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Names.java:0)
+Class <org.gradle.language.nativeplatform.internal.NativeLanguageTransform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeLanguageTransform.java:0)
+Class <org.gradle.language.nativeplatform.internal.PCHCompileTaskConfig$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PCHCompileTaskConfig.java:0)
+Class <org.gradle.language.nativeplatform.internal.PCHCompileTaskConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PCHCompileTaskConfig.java:0)
+Class <org.gradle.language.nativeplatform.internal.PublicationAwareComponent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationAwareComponent.java:0)
+Class <org.gradle.language.nativeplatform.internal.SourceCompileTaskConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceCompileTaskConfig.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.BuildableCompilationState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildableCompilationState.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.CollectingMacroLookup$MacroSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectingMacroLookup.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.CollectingMacroLookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectingMacroLookup.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.CompilationState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilationState.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.CompilationStateCacheFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilationStateCacheFactory.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.CompilationStateSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilationStateSerializer.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultCompilationStateCacheFactory$SimplePersistentObjectHolder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCompilationStateCacheFactory.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultCompilationStateCacheFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCompilationStateCacheFactory.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultHeaderDependenciesCollector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultHeaderDependenciesCollector.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultIncrementalCompilation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncrementalCompilation.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultIncrementalCompilerBuilder$StateCollectingIncrementalCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncrementalCompilerBuilder.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultIncrementalCompilerBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncrementalCompilerBuilder.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesParser.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$BuildableResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$CachedIncludeFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$CollectTokens> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$DirectoryContents> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$ExpressionVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$FixedIncludePath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$IncludePath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$MissingIncludeFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$PathResolvingVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$PrefixedIncludePath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$SystemIncludeFile$QuotedIncludeFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver$SystemIncludeFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.DefaultSourceIncludesResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncludeFileEdge> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeFileEdge.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompilation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompilation.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileFilesFactory$DefaultIncrementalCompileSourceProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileFilesFactory.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileFilesFactory$FileDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileFilesFactory.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileFilesFactory$FileVisitResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileFilesFactory.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileFilesFactory$IncludeFileResolutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileFilesFactory.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileFilesFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileFilesFactory.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileProcessor$1$ProcessSourceFilesDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileProcessor.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileProcessor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileProcessor.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileProcessor.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompileSourceProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompileSourceProcessor.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompilerBuilder$IncrementalCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompilerBuilder.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.IncrementalCompilerBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalCompilerBuilder.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.MacroLookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MacroLookup.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.SourceFileState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceFileState.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.SourceIncludesParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceIncludesParser.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.SourceIncludesResolver$IncludeFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.SourceIncludesResolver$IncludeResolutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.SourceIncludesResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceIncludesResolver.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.TokenLookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TokenLookup.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.AbstractExpression$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractExpression.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.AbstractExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractExpression.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.AbstractInclude> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractInclude.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.AbstractMacro> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMacro.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.AbstractMacroFunction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMacroFunction.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.ArgsMappingMacroFunction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgsMappingMacroFunction.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.CSourceParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CSourceParser.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.CachingCSourceParser$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingCSourceParser.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.CachingCSourceParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingCSourceParser.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.ComplexExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComplexExpression.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.DefaultIncludeDirectives$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.DefaultIncludeDirectives$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.DefaultIncludeDirectives$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.DefaultIncludeDirectives$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.DefaultIncludeDirectives$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.DefaultIncludeDirectives$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.DefaultIncludeDirectives> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncludeDirectives.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeDirectivesSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectivesSerializer.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeDirectivesSerializer$CollectionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectivesSerializer.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeDirectivesSerializer$ExpressionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectivesSerializer.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeDirectivesSerializer$IncludeSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectivesSerializer.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeDirectivesSerializer$MacroFunctionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectivesSerializer.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeDirectivesSerializer$MacroSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectivesSerializer.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeDirectivesSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeDirectivesSerializer.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeWithMacroFunctionCallExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeWithMacroFunctionCallExpression.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.IncludeWithSimpleExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludeWithSimpleExpression.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.MacroWithComplexExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MacroWithComplexExpression.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.MacroWithSimpleExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MacroWithSimpleExpression.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.PreprocessingReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PreprocessingReader.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.RegexBackedCSourceParser$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RegexBackedCSourceParser.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.RegexBackedCSourceParser$Buffer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RegexBackedCSourceParser.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.RegexBackedCSourceParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RegexBackedCSourceParser.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.ReturnFixedValueMacroFunction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReturnFixedValueMacroFunction.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.ReturnParameterMacroFunction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReturnParameterMacroFunction.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.SimpleExpression> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleExpression.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.UnresolvableMacro> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnresolvableMacro.java:0)
+Class <org.gradle.language.nativeplatform.internal.incremental.sourceparser.UnresolvableMacroFunction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnresolvableMacroFunction.java:0)
+Class <org.gradle.language.nativeplatform.internal.registry.NativeLanguageServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeLanguageServices.java:0)
+Class <org.gradle.language.nativeplatform.internal.toolchains.DefaultToolChainSelector$DefaultResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolChainSelector.java:0)
+Class <org.gradle.language.nativeplatform.internal.toolchains.DefaultToolChainSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolChainSelector.java:0)
+Class <org.gradle.language.nativeplatform.internal.toolchains.ToolChainSelector$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolChainSelector.java:0)
+Class <org.gradle.language.nativeplatform.internal.toolchains.ToolChainSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolChainSelector.java:0)
+Class <org.gradle.language.objectivec.internal.DefaultObjectiveCCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectiveCCompileSpec.java:0)
+Class <org.gradle.language.objectivec.internal.DefaultObjectiveCPCHCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectiveCPCHCompileSpec.java:0)
+Class <org.gradle.language.objectivec.internal.DefaultObjectiveCSourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectiveCSourceSet.java:0)
+Class <org.gradle.language.objectivecpp.internal.DefaultObjectiveCppCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectiveCppCompileSpec.java:0)
+Class <org.gradle.language.objectivecpp.internal.DefaultObjectiveCppPCHCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectiveCppPCHCompileSpec.java:0)
+Class <org.gradle.language.objectivecpp.internal.DefaultObjectiveCppSourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultObjectiveCppSourceSet.java:0)
+Class <org.gradle.language.rc.internal.DefaultWindowsResourceCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWindowsResourceCompileSpec.java:0)
+Class <org.gradle.language.rc.internal.DefaultWindowsResourceSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWindowsResourceSet.java:0)
+Class <org.gradle.language.rc.plugins.internal.WindowsResourcesCompileTaskConfig$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsResourcesCompileTaskConfig.java:0)
+Class <org.gradle.language.rc.plugins.internal.WindowsResourcesCompileTaskConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsResourcesCompileTaskConfig.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftApplication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftApplication.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftBinary$ModulePath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftBinary.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftBinary.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftComponent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftComponent.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftExecutable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftExecutable.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftLibrary.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftPlatform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftPlatform.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftSharedLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftSharedLibrary.java:0)
+Class <org.gradle.language.swift.internal.DefaultSwiftStaticLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftStaticLibrary.java:0)
+Class <org.gradle.language.swift.tasks.internal.DefaultSwiftCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftCompileSpec.java:0)
+Class <org.gradle.language.swift.tasks.internal.SymbolHider$COFFHeader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolHider.java:0)
+Class <org.gradle.language.swift.tasks.internal.SymbolHider$DataReader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolHider.java:0)
+Class <org.gradle.language.swift.tasks.internal.SymbolHider$SymbolRecord> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolHider.java:0)
+Class <org.gradle.language.swift.tasks.internal.SymbolHider$SymbolTable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolHider.java:0)
+Class <org.gradle.language.swift.tasks.internal.SymbolHider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolHider.java:0)
+Class <org.gradle.launcher.cli.converter.BuildLayoutConverter$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLayoutConverter.java:0)
+Class <org.gradle.launcher.cli.converter.BuildLayoutConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLayoutConverter.java:0)
+Class <org.gradle.launcher.cli.converter.BuildOptionBackedConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOptionBackedConverter.java:0)
+Class <org.gradle.launcher.cli.converter.InitialPropertiesConverter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitialPropertiesConverter.java:0)
+Class <org.gradle.launcher.cli.converter.InitialPropertiesConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitialPropertiesConverter.java:0)
+Class <org.gradle.launcher.cli.converter.LayoutToPropertiesConverter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LayoutToPropertiesConverter.java:0)
+Class <org.gradle.launcher.cli.converter.LayoutToPropertiesConverter$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LayoutToPropertiesConverter.java:0)
+Class <org.gradle.launcher.cli.converter.LayoutToPropertiesConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LayoutToPropertiesConverter.java:0)
+Class <org.gradle.launcher.cli.converter.StartParameterConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartParameterConverter.java:0)
+Class <org.gradle.launcher.cli.converter.WelcomeMessageBuildOptions$WelcomeMessageOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WelcomeMessageBuildOptions.java:0)
+Class <org.gradle.launcher.cli.converter.WelcomeMessageBuildOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WelcomeMessageBuildOptions.java:0)
+Class <org.gradle.launcher.configuration.AllProperties> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllProperties.java:0)
+Class <org.gradle.launcher.configuration.BuildLayoutResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLayoutResult.java:0)
+Class <org.gradle.launcher.configuration.InitialProperties> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InitialProperties.java:0)
+Class <org.gradle.launcher.daemon.DaemonExecHandleBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonExecHandleBuilder.java:0)
+Class <org.gradle.launcher.daemon.bootstrap.DaemonMain$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMain.java:0)
+Class <org.gradle.launcher.daemon.bootstrap.DaemonMain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMain.java:0)
+Class <org.gradle.launcher.daemon.bootstrap.DaemonOutputConsumer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonOutputConsumer.java:0)
+Class <org.gradle.launcher.daemon.bootstrap.DaemonStartupCommunication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStartupCommunication.java:0)
+Class <org.gradle.launcher.daemon.bootstrap.ForegroundDaemonAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForegroundDaemonAction.java:0)
+Class <org.gradle.launcher.daemon.bootstrap.GradleDaemon> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleDaemon.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonCancelForwarder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonCancelForwarder.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonCancelForwarder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonCancelForwarder.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClient.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientConnection$StaleAddressDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientConnection.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientConnection.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientFactory.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientGlobalServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientGlobalServices.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientInputForwarder$ForwardTextStreamToConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientInputForwarder.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientInputForwarder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientInputForwarder.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientInterruptedException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientInterruptedException.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientServices.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonClientServicesSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonClientServicesSupport.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonConnectionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonConnectionException.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonConnector.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonDisappearedException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonDisappearedException.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonGreeter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonGreeter.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonInitialConnectException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonInitialConnectException.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonStartListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStartListener.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonStarter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStarter.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonStartupMessage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStartupMessage.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonStopClient$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStopClient.java:0)
+Class <org.gradle.launcher.daemon.client.DaemonStopClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStopClient.java:0)
+Class <org.gradle.launcher.daemon.client.DefaultDaemonConnector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnector.java:0)
+Class <org.gradle.launcher.daemon.client.DefaultDaemonConnector$CleanupOnStaleAddress> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnector.java:0)
+Class <org.gradle.launcher.daemon.client.DefaultDaemonConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnector.java:0)
+Class <org.gradle.launcher.daemon.client.DefaultDaemonStarter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonStarter.java:0)
+Class <org.gradle.launcher.daemon.client.InputForwarder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputForwarder.java:0)
+Class <org.gradle.launcher.daemon.client.InputForwarder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputForwarder.java:0)
+Class <org.gradle.launcher.daemon.client.JvmVersionValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmVersionValidator.java:0)
+Class <org.gradle.launcher.daemon.client.NoUsableDaemonFoundException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoUsableDaemonFoundException.java:0)
+Class <org.gradle.launcher.daemon.client.NotifyDaemonAboutChangedPathsClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyDaemonAboutChangedPathsClient.java:0)
+Class <org.gradle.launcher.daemon.client.ReportDaemonStatusClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReportDaemonStatusClient.java:0)
+Class <org.gradle.launcher.daemon.client.ReportStatusDispatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReportStatusDispatcher.java:0)
+Class <org.gradle.launcher.daemon.client.SingleUseDaemonClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SingleUseDaemonClient.java:0)
+Class <org.gradle.launcher.daemon.client.SingleUseDaemonClientServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SingleUseDaemonClientServices.java:0)
+Class <org.gradle.launcher.daemon.client.StaleDaemonAddressException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StaleDaemonAddressException.java:0)
+Class <org.gradle.launcher.daemon.client.StopDispatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StopDispatcher.java:0)
+Class <org.gradle.launcher.daemon.configuration.BuildProcess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildProcess.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$ApplyInstrumentationAgentOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$BaseDirOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$DaemonOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$DebugHostOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$DebugOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$DebugPortOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$DebugServerOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$DebugSuspendOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$ForegroundOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$HealthCheckOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$IdleTimeoutOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$JavaHomeOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$JvmArgsOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$PriorityOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$StatusOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions$StopOption> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonBuildOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonJvmOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonJvmOptions.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonParameters$Priority> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonParameters.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonParameters.java:0)
+Class <org.gradle.launcher.daemon.configuration.DaemonServerConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonServerConfiguration.java:0)
+Class <org.gradle.launcher.daemon.configuration.DefaultDaemonServerConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonServerConfiguration.java:0)
+Class <org.gradle.launcher.daemon.configuration.ForegroundDaemonConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForegroundDaemonConfiguration.java:0)
+Class <org.gradle.launcher.daemon.context.DaemonCompatibilitySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonCompatibilitySpec.java:0)
+Class <org.gradle.launcher.daemon.context.DaemonConnectDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonConnectDetails.java:0)
+Class <org.gradle.launcher.daemon.context.DaemonContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonContext.java:0)
+Class <org.gradle.launcher.daemon.context.DaemonContextBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonContextBuilder.java:0)
+Class <org.gradle.launcher.daemon.context.DefaultDaemonContext$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonContext.java:0)
+Class <org.gradle.launcher.daemon.context.DefaultDaemonContext$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonContext.java:0)
+Class <org.gradle.launcher.daemon.context.DefaultDaemonContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonContext.java:0)
+Class <org.gradle.launcher.daemon.diagnostics.DaemonDiagnostics> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonDiagnostics.java:0)
+Class <org.gradle.launcher.daemon.diagnostics.DaemonStartupInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStartupInfo.java:0)
+Class <org.gradle.launcher.daemon.logging.DaemonMessages> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessages.java:0)
+Class <org.gradle.launcher.daemon.protocol.Build> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Build.java:0)
+Class <org.gradle.launcher.daemon.protocol.BuildEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildEvent.java:0)
+Class <org.gradle.launcher.daemon.protocol.BuildStarted> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildStarted.java:0)
+Class <org.gradle.launcher.daemon.protocol.Cancel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Cancel.java:0)
+Class <org.gradle.launcher.daemon.protocol.CloseInput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CloseInput.java:0)
+Class <org.gradle.launcher.daemon.protocol.Command> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Command.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$BuildActionParametersSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$BuildEventSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$BuildSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$BuildStartedSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$CancelSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$CloseInputSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$DaemonUnavailableSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$FailureSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$FinishedSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$ForwardInputSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$OutputMessageSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer$SuccessSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonMessageSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonMessageSerializer.java:0)
+Class <org.gradle.launcher.daemon.protocol.DaemonUnavailable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonUnavailable.java:0)
+Class <org.gradle.launcher.daemon.protocol.Failure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Failure.java:0)
+Class <org.gradle.launcher.daemon.protocol.Finished> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Finished.java:0)
+Class <org.gradle.launcher.daemon.protocol.ForwardInput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForwardInput.java:0)
+Class <org.gradle.launcher.daemon.protocol.InputMessage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputMessage.java:0)
+Class <org.gradle.launcher.daemon.protocol.InvalidateVirtualFileSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidateVirtualFileSystem.java:0)
+Class <org.gradle.launcher.daemon.protocol.Message> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Message.java:0)
+Class <org.gradle.launcher.daemon.protocol.OutputMessage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputMessage.java:0)
+Class <org.gradle.launcher.daemon.protocol.ReportStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReportStatus.java:0)
+Class <org.gradle.launcher.daemon.protocol.Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Result.java:0)
+Class <org.gradle.launcher.daemon.protocol.Status> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Status.java:0)
+Class <org.gradle.launcher.daemon.protocol.Stop> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Stop.java:0)
+Class <org.gradle.launcher.daemon.protocol.StopWhenIdle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StopWhenIdle.java:0)
+Class <org.gradle.launcher.daemon.protocol.Success> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Success.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonDir> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonDir.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonInfo$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonInfo.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonInfo$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonInfo.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonInfo.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonRegistry$EmptyRegistryException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonRegistryContent$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistryContent.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonRegistryContent$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistryContent.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonRegistryContent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistryContent.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonRegistryServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistryServices.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonStopEvent$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStopEvent.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonStopEvent$Serializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStopEvent.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonStopEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStopEvent.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonStopEvents$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStopEvents.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonStopEvents$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStopEvents.java:0)
+Class <org.gradle.launcher.daemon.registry.DaemonStopEvents> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStopEvents.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry$7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry$8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.registry.PersistentDaemonRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentDaemonRegistry.java:0)
+Class <org.gradle.launcher.daemon.server.BadlyFormedRequestException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BadlyFormedRequestException.java:0)
+Class <org.gradle.launcher.daemon.server.CompatibleDaemonExpirationStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompatibleDaemonExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.CompatibleDaemonExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompatibleDaemonExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.Daemon$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Daemon.java:0)
+Class <org.gradle.launcher.daemon.server.Daemon$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Daemon.java:0)
+Class <org.gradle.launcher.daemon.server.Daemon$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Daemon.java:0)
+Class <org.gradle.launcher.daemon.server.Daemon$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Daemon.java:0)
+Class <org.gradle.launcher.daemon.server.Daemon$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Daemon.java:0)
+Class <org.gradle.launcher.daemon.server.Daemon$DaemonExpirationPeriodicCheck> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Daemon.java:0)
+Class <org.gradle.launcher.daemon.server.Daemon$DefaultDaemonExpirationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Daemon.java:0)
+Class <org.gradle.launcher.daemon.server.Daemon> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Daemon.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonIdleTimeoutExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonIdleTimeoutExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonRegistryUnavailableExpirationStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistryUnavailableExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonRegistryUnavailableExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistryUnavailableExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonRegistryUpdater> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRegistryUpdater.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonServerConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonServerConnector.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonServices.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonStateCoordinator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStateCoordinator.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonStateCoordinator$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStateCoordinator.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonStateCoordinator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStateCoordinator.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonTcpServerConnector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonTcpServerConnector.java:0)
+Class <org.gradle.launcher.daemon.server.DaemonTcpServerConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonTcpServerConnector.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultDaemonConnection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultDaemonConnection$CancelQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultDaemonConnection$CommandQueue$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultDaemonConnection$CommandQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultDaemonConnection$DisconnectQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultDaemonConnection$ReceiveQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultDaemonConnection$StdinQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultDaemonConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncomingConnectionHandler.java:0)
+Class <org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIncomingConnectionHandler.java:0)
+Class <org.gradle.launcher.daemon.server.IncomingConnectionHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncomingConnectionHandler.java:0)
+Class <org.gradle.launcher.daemon.server.MasterExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MasterExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.NotMostRecentlyUsedDaemonExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotMostRecentlyUsedDaemonExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.SynchronizedDispatchConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SynchronizedDispatchConnection.java:0)
+Class <org.gradle.launcher.daemon.server.api.DaemonCommandAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonCommandAction.java:0)
+Class <org.gradle.launcher.daemon.server.api.DaemonCommandExecution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonCommandExecution.java:0)
+Class <org.gradle.launcher.daemon.server.api.DaemonConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonConnection.java:0)
+Class <org.gradle.launcher.daemon.server.api.DaemonStateControl$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStateControl.java:0)
+Class <org.gradle.launcher.daemon.server.api.DaemonStateControl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStateControl.java:0)
+Class <org.gradle.launcher.daemon.server.api.DaemonStoppedException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonStoppedException.java:0)
+Class <org.gradle.launcher.daemon.server.api.DaemonUnavailableException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonUnavailableException.java:0)
+Class <org.gradle.launcher.daemon.server.api.HandleInvalidateVirtualFileSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HandleInvalidateVirtualFileSystem.java:0)
+Class <org.gradle.launcher.daemon.server.api.HandleReportStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HandleReportStatus.java:0)
+Class <org.gradle.launcher.daemon.server.api.HandleStop> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HandleStop.java:0)
+Class <org.gradle.launcher.daemon.server.api.StdinHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StdinHandler.java:0)
+Class <org.gradle.launcher.daemon.server.exec.BuildCommandOnly> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildCommandOnly.java:0)
+Class <org.gradle.launcher.daemon.server.exec.DaemonCommandExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonCommandExecuter.java:0)
+Class <org.gradle.launcher.daemon.server.exec.DaemonConnectionBackedEventConsumer$ForwardEvents> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonConnectionBackedEventConsumer.java:0)
+Class <org.gradle.launcher.daemon.server.exec.DaemonConnectionBackedEventConsumer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonConnectionBackedEventConsumer.java:0)
+Class <org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EstablishBuildEnvironment.java:0)
+Class <org.gradle.launcher.daemon.server.exec.ExecuteBuild$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteBuild.java:0)
+Class <org.gradle.launcher.daemon.server.exec.ExecuteBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteBuild.java:0)
+Class <org.gradle.launcher.daemon.server.exec.ForwardClientInput$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForwardClientInput.java:0)
+Class <org.gradle.launcher.daemon.server.exec.ForwardClientInput$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForwardClientInput.java:0)
+Class <org.gradle.launcher.daemon.server.exec.ForwardClientInput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForwardClientInput.java:0)
+Class <org.gradle.launcher.daemon.server.exec.HandleCancel$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HandleCancel.java:0)
+Class <org.gradle.launcher.daemon.server.exec.HandleCancel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HandleCancel.java:0)
+Class <org.gradle.launcher.daemon.server.exec.LogAndCheckHealth> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogAndCheckHealth.java:0)
+Class <org.gradle.launcher.daemon.server.exec.LogToClient$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogToClient.java:0)
+Class <org.gradle.launcher.daemon.server.exec.LogToClient$AsynchronousLogDispatcher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogToClient.java:0)
+Class <org.gradle.launcher.daemon.server.exec.LogToClient$AsynchronousLogDispatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogToClient.java:0)
+Class <org.gradle.launcher.daemon.server.exec.LogToClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LogToClient.java:0)
+Class <org.gradle.launcher.daemon.server.exec.NoOpDaemonCommandAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoOpDaemonCommandAction.java:0)
+Class <org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RequestStopIfSingleUsedDaemon.java:0)
+Class <org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResetDeprecationLogger.java:0)
+Class <org.gradle.launcher.daemon.server.exec.ReturnResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReturnResult.java:0)
+Class <org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartBuildOrRespondWithBusy.java:0)
+Class <org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartBuildOrRespondWithBusy.java:0)
+Class <org.gradle.launcher.daemon.server.exec.WatchForDisconnection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WatchForDisconnection.java:0)
+Class <org.gradle.launcher.daemon.server.exec.WatchForDisconnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WatchForDisconnection.java:0)
+Class <org.gradle.launcher.daemon.server.expiry.AllDaemonExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AllDaemonExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.expiry.AnyDaemonExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AnyDaemonExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.expiry.DaemonExpirationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonExpirationListener.java:0)
+Class <org.gradle.launcher.daemon.server.expiry.DaemonExpirationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonExpirationResult.java:0)
+Class <org.gradle.launcher.daemon.server.expiry.DaemonExpirationStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonExpirationStatus.java:0)
+Class <org.gradle.launcher.daemon.server.expiry.DaemonExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.health.DaemonHealthCheck> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonHealthCheck.java:0)
+Class <org.gradle.launcher.daemon.server.health.DaemonHealthStats> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonHealthStats.java:0)
+Class <org.gradle.launcher.daemon.server.health.HealthExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HealthExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.health.LowMemoryDaemonExpirationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LowMemoryDaemonExpirationStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.DefaultGarbageCollectionMonitor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGarbageCollectionMonitor.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.DefaultGarbageCollectionMonitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGarbageCollectionMonitor.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.DefaultSlidingWindow> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSlidingWindow.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.GarbageCollectionCheck> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GarbageCollectionCheck.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.GarbageCollectionEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GarbageCollectionEvent.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.GarbageCollectionInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GarbageCollectionInfo.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.GarbageCollectionMonitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GarbageCollectionMonitor.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.GarbageCollectionStats> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GarbageCollectionStats.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.GarbageCollectorMonitoringStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GarbageCollectorMonitoringStrategy.java:0)
+Class <org.gradle.launcher.daemon.server.health.gc.SlidingWindow> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SlidingWindow.java:0)
+Class <org.gradle.launcher.daemon.server.scaninfo.DaemonScanInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonScanInfo.java:0)
+Class <org.gradle.launcher.daemon.server.scaninfo.DefaultDaemonScanInfo$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonScanInfo.java:0)
+Class <org.gradle.launcher.daemon.server.scaninfo.DefaultDaemonScanInfo$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonScanInfo.java:0)
+Class <org.gradle.launcher.daemon.server.scaninfo.DefaultDaemonScanInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDaemonScanInfo.java:0)
+Class <org.gradle.launcher.daemon.server.stats.DaemonRunningStats> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonRunningStats.java:0)
+Class <org.gradle.launcher.exec.AbstractToolingModelRequirements> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractToolingModelRequirements.java:0)
+Class <org.gradle.launcher.exec.BuildActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionExecuter.java:0)
+Class <org.gradle.launcher.exec.BuildActionParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionParameters.java:0)
+Class <org.gradle.launcher.exec.BuildActionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionResult.java:0)
+Class <org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildCompletionNotifyingBuildActionRunner.java:0)
+Class <org.gradle.launcher.exec.BuildExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildExecuter.java:0)
+Class <org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOutcomeReportingBuildActionRunner.java:0)
+Class <org.gradle.launcher.exec.BuildTreeLifecycleBuildActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildTreeLifecycleBuildActionExecutor.java:0)
+Class <org.gradle.launcher.exec.ChainingBuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChainingBuildActionRunner.java:0)
+Class <org.gradle.launcher.exec.DefaultBuildActionParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildActionParameters.java:0)
+Class <org.gradle.launcher.exec.QueryModelRequirements> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (QueryModelRequirements.java:0)
+Class <org.gradle.launcher.exec.RootBuildLifecycleBuildActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RootBuildLifecycleBuildActionExecutor.java:0)
+Class <org.gradle.launcher.exec.RunActionRequirements> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunActionRequirements.java:0)
+Class <org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunAsBuildOperationBuildActionExecutor.java:0)
+Class <org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunAsBuildOperationBuildActionExecutor.java:0)
+Class <org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunAsBuildOperationBuildActionExecutor.java:0)
+Class <org.gradle.launcher.exec.RunAsBuildOperationBuildActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunAsBuildOperationBuildActionExecutor.java:0)
+Class <org.gradle.launcher.exec.RunAsWorkerThreadBuildActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunAsWorkerThreadBuildActionExecutor.java:0)
+Class <org.gradle.launcher.exec.RunBuildBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunBuildBuildOperationType.java:0)
+Class <org.gradle.launcher.exec.RunBuildBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunBuildBuildOperationType.java:0)
+Class <org.gradle.launcher.exec.RunBuildBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunBuildBuildOperationType.java:0)
+Class <org.gradle.launcher.exec.RunPhasedActionRequirements> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunPhasedActionRequirements.java:0)
+Class <org.gradle.listener.ClosureBackedMethodInvocationDispatch> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureBackedMethodInvocationDispatch.java:0)
+Class <org.gradle.model.collection.internal.BridgedCollections$1$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BridgedCollections.java:0)
+Class <org.gradle.model.collection.internal.BridgedCollections$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BridgedCollections.java:0)
+Class <org.gradle.model.collection.internal.BridgedCollections$1$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BridgedCollections.java:0)
+Class <org.gradle.model.collection.internal.BridgedCollections$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BridgedCollections.java:0)
+Class <org.gradle.model.collection.internal.BridgedCollections$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BridgedCollections.java:0)
+Class <org.gradle.model.collection.internal.BridgedCollections$ExtractFromParentContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BridgedCollections.java:0)
+Class <org.gradle.model.collection.internal.BridgedCollections> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BridgedCollections.java:0)
+Class <org.gradle.model.dsl.internal.NonTransformedModelDslBacking> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NonTransformedModelDslBacking.java:0)
+Class <org.gradle.model.dsl.internal.TransformedModelDslBacking$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransformedModelDslBacking.java:0)
+Class <org.gradle.model.dsl.internal.TransformedModelDslBacking> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransformedModelDslBacking.java:0)
+Class <org.gradle.model.dsl.internal.inputs.PotentialInput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PotentialInput.java:0)
+Class <org.gradle.model.dsl.internal.inputs.PotentialInputs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PotentialInputs.java:0)
+Class <org.gradle.model.dsl.internal.transform.ClosureBackedRuleFactory$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureBackedRuleFactory.java:0)
+Class <org.gradle.model.dsl.internal.transform.ClosureBackedRuleFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureBackedRuleFactory.java:0)
+Class <org.gradle.model.dsl.internal.transform.ClosureBackedRuleFactory$RelativePathSourceLocationTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureBackedRuleFactory.java:0)
+Class <org.gradle.model.dsl.internal.transform.ClosureBackedRuleFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureBackedRuleFactory.java:0)
+Class <org.gradle.model.dsl.internal.transform.ClosureCreationInterceptingVerifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureCreationInterceptingVerifier.java:0)
+Class <org.gradle.model.dsl.internal.transform.InputReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputReference.java:0)
+Class <org.gradle.model.dsl.internal.transform.InputReferences> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputReferences.java:0)
+Class <org.gradle.model.dsl.internal.transform.ModelBlockTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelBlockTransformer.java:0)
+Class <org.gradle.model.dsl.internal.transform.RuleVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleVisitor.java:0)
+Class <org.gradle.model.dsl.internal.transform.RulesBlock> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RulesBlock.java:0)
+Class <org.gradle.model.dsl.internal.transform.RulesVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RulesVisitor.java:0)
+Class <org.gradle.model.dsl.internal.transform.SourceLocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceLocation.java:0)
+Class <org.gradle.model.dsl.internal.transform.TransformedClosure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransformedClosure.java:0)
+Class <org.gradle.model.internal.asm.AsmClassGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmClassGenerator.java:0)
+Class <org.gradle.model.internal.asm.AsmClassGeneratorUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsmClassGeneratorUtils.java:0)
+Class <org.gradle.model.internal.asm.BytecodeFragment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BytecodeFragment.java:0)
+Class <org.gradle.model.internal.asm.ClassGeneratorSuffixRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassGeneratorSuffixRegistry.java:0)
+Class <org.gradle.model.internal.asm.ClassVisitorScope$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassVisitorScope.java:0)
+Class <org.gradle.model.internal.asm.ClassVisitorScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassVisitorScope.java:0)
+Class <org.gradle.model.internal.asm.MethodVisitorScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodVisitorScope.java:0)
+Class <org.gradle.model.internal.core.AbstractModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModelAction.java:0)
+Class <org.gradle.model.internal.core.AbstractModelActionWithView> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModelActionWithView.java:0)
+Class <org.gradle.model.internal.core.AddProjectionsAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AddProjectionsAction.java:0)
+Class <org.gradle.model.internal.core.ChainingModelProjection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChainingModelProjection.java:0)
+Class <org.gradle.model.internal.core.ChainingModelProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChainingModelProjection.java:0)
+Class <org.gradle.model.internal.core.ChildNodeInitializerStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChildNodeInitializerStrategy.java:0)
+Class <org.gradle.model.internal.core.ChildNodeInitializerStrategyAccessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChildNodeInitializerStrategyAccessor.java:0)
+Class <org.gradle.model.internal.core.ChildNodeInitializerStrategyAccessors$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChildNodeInitializerStrategyAccessors.java:0)
+Class <org.gradle.model.internal.core.ChildNodeInitializerStrategyAccessors$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChildNodeInitializerStrategyAccessors.java:0)
+Class <org.gradle.model.internal.core.ChildNodeInitializerStrategyAccessors> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChildNodeInitializerStrategyAccessors.java:0)
+Class <org.gradle.model.internal.core.DefaultModelRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistration.java:0)
+Class <org.gradle.model.internal.core.DefaultModelViewState$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelViewState.java:0)
+Class <org.gradle.model.internal.core.DefaultModelViewState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelViewState.java:0)
+Class <org.gradle.model.internal.core.DefaultNodeInitializerRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNodeInitializerRegistry.java:0)
+Class <org.gradle.model.internal.core.DefaultNodeInitializerRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNodeInitializerRegistry.java:0)
+Class <org.gradle.model.internal.core.DeferredModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DeferredModelAction.java:0)
+Class <org.gradle.model.internal.core.DirectNodeInputUsingModelAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectNodeInputUsingModelAction.java:0)
+Class <org.gradle.model.internal.core.DirectNodeInputUsingModelAction$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectNodeInputUsingModelAction.java:0)
+Class <org.gradle.model.internal.core.DirectNodeInputUsingModelAction$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectNodeInputUsingModelAction.java:0)
+Class <org.gradle.model.internal.core.DirectNodeInputUsingModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectNodeInputUsingModelAction.java:0)
+Class <org.gradle.model.internal.core.DirectNodeNoInputsModelAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectNodeNoInputsModelAction.java:0)
+Class <org.gradle.model.internal.core.DirectNodeNoInputsModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectNodeNoInputsModelAction.java:0)
+Class <org.gradle.model.internal.core.DomainObjectCollectionBackedModelMap$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DomainObjectCollectionBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.DomainObjectCollectionBackedModelMap$HasNamePredicate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DomainObjectCollectionBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.DomainObjectCollectionBackedModelMap$ToName> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DomainObjectCollectionBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.DomainObjectCollectionBackedModelMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DomainObjectCollectionBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.DuplicateModelException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DuplicateModelException.java:0)
+Class <org.gradle.model.internal.core.EmptyModelProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EmptyModelProjection.java:0)
+Class <org.gradle.model.internal.core.EmptyReferenceProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EmptyReferenceProjection.java:0)
+Class <org.gradle.model.internal.core.Hidden> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Hidden.java:0)
+Class <org.gradle.model.internal.core.InputUsingModelAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputUsingModelAction.java:0)
+Class <org.gradle.model.internal.core.InputUsingModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputUsingModelAction.java:0)
+Class <org.gradle.model.internal.core.InstanceModelView> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstanceModelView.java:0)
+Class <org.gradle.model.internal.core.ModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelAction.java:0)
+Class <org.gradle.model.internal.core.ModelActionRole> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelActionRole.java:0)
+Class <org.gradle.model.internal.core.ModelAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelAdapter.java:0)
+Class <org.gradle.model.internal.core.ModelMapGroovyView> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapGroovyView.java:0)
+Class <org.gradle.model.internal.core.ModelMapModelProjection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapModelProjection.java:0)
+Class <org.gradle.model.internal.core.ModelMapModelProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapModelProjection.java:0)
+Class <org.gradle.model.internal.core.ModelMaps$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMaps.java:0)
+Class <org.gradle.model.internal.core.ModelMaps> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMaps.java:0)
+Class <org.gradle.model.internal.core.ModelNode$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelNode.java:0)
+Class <org.gradle.model.internal.core.ModelNode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelNode.java:0)
+Class <org.gradle.model.internal.core.ModelNodes$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelNodes.java:0)
+Class <org.gradle.model.internal.core.ModelNodes> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelNodes.java:0)
+Class <org.gradle.model.internal.core.ModelPath$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPath.java:0)
+Class <org.gradle.model.internal.core.ModelPath$Cache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPath.java:0)
+Class <org.gradle.model.internal.core.ModelPath$InvalidNameException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPath.java:0)
+Class <org.gradle.model.internal.core.ModelPath$InvalidPathException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPath.java:0)
+Class <org.gradle.model.internal.core.ModelPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPath.java:0)
+Class <org.gradle.model.internal.core.ModelPredicate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPredicate.java:0)
+Class <org.gradle.model.internal.core.ModelProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelProjection.java:0)
+Class <org.gradle.model.internal.core.ModelPromise> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPromise.java:0)
+Class <org.gradle.model.internal.core.ModelReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelReference.java:0)
+Class <org.gradle.model.internal.core.ModelRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistration.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations$Builder$AbstractBuilderAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations$Builder$DescriptorReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations$Builder$InputsUsingBuilderAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations$Builder$NoInputsBuilderAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations$Builder$SingleInputNodeBiAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRegistrations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistrations.java:0)
+Class <org.gradle.model.internal.core.ModelRuleExecutionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExecutionException.java:0)
+Class <org.gradle.model.internal.core.ModelSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSpec.java:0)
+Class <org.gradle.model.internal.core.ModelTypeInitializationException$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypeInitializationException.java:0)
+Class <org.gradle.model.internal.core.ModelTypeInitializationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypeInitializationException.java:0)
+Class <org.gradle.model.internal.core.ModelView> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelView.java:0)
+Class <org.gradle.model.internal.core.ModelViewFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelViewFactory.java:0)
+Class <org.gradle.model.internal.core.ModelViewState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelViewState.java:0)
+Class <org.gradle.model.internal.core.ModelViews> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelViews.java:0)
+Class <org.gradle.model.internal.core.MutableModelNode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MutableModelNode.java:0)
+Class <org.gradle.model.internal.core.NamedEntityInstantiator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NamedEntityInstantiator.java:0)
+Class <org.gradle.model.internal.core.NamedEntityInstantiators$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NamedEntityInstantiators.java:0)
+Class <org.gradle.model.internal.core.NamedEntityInstantiators> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NamedEntityInstantiators.java:0)
+Class <org.gradle.model.internal.core.NoInputsModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoInputsModelAction.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$4$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$4$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$9> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$ApplyRuleSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$ChainedElementFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$DeferredActionWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$ElementFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap$FilteringActionWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelMap.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelSet.java:0)
+Class <org.gradle.model.internal.core.NodeBackedModelSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeBackedModelSet.java:0)
+Class <org.gradle.model.internal.core.NodeInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeInitializer.java:0)
+Class <org.gradle.model.internal.core.NodeInitializerContext$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeInitializerContext.java:0)
+Class <org.gradle.model.internal.core.NodeInitializerContext$PropertyContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeInitializerContext.java:0)
+Class <org.gradle.model.internal.core.NodeInitializerContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeInitializerContext.java:0)
+Class <org.gradle.model.internal.core.NodeInitializerRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeInitializerRegistry.java:0)
+Class <org.gradle.model.internal.core.NodePredicate$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodePredicate.java:0)
+Class <org.gradle.model.internal.core.NodePredicate$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodePredicate.java:0)
+Class <org.gradle.model.internal.core.NodePredicate$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodePredicate.java:0)
+Class <org.gradle.model.internal.core.NodePredicate$BasicPredicate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodePredicate.java:0)
+Class <org.gradle.model.internal.core.NodePredicate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodePredicate.java:0)
+Class <org.gradle.model.internal.core.SpecializedModelMapProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedModelMapProjection.java:0)
+Class <org.gradle.model.internal.core.TypeCompatibilityModelProjectionSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeCompatibilityModelProjectionSupport.java:0)
+Class <org.gradle.model.internal.core.TypedModelProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypedModelProjection.java:0)
+Class <org.gradle.model.internal.core.UnmanagedModelProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedModelProjection.java:0)
+Class <org.gradle.model.internal.core.UnmanagedStruct> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedStruct.java:0)
+Class <org.gradle.model.internal.core.rule.describe.AbstractModelRuleDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.MethodModelRuleDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.MethodModelRuleDescriptor$Cache$CacheEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.MethodModelRuleDescriptor$Cache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.MethodModelRuleDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.ModelRuleDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.NestedModelRuleDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.core.rule.describe.SimpleModelRuleDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleModelRuleDescriptor.java:0)
+Class <org.gradle.model.internal.inspect.AbstractAnnotationDrivenModelRuleExtractor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAnnotationDrivenModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.AbstractAnnotationDrivenModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAnnotationDrivenModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.AbstractExtractedModelRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractExtractedModelRule.java:0)
+Class <org.gradle.model.internal.inspect.AbstractMethodRuleAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMethodRuleAction.java:0)
+Class <org.gradle.model.internal.inspect.AbstractModelCreationRuleExtractor$ExtractedCreationRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.AbstractModelCreationRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.AbstractMutationModelRuleExtractor$ExtractedMutationRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMutationModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.AbstractMutationModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMutationModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.DefaultMethodModelRuleExtractionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMethodModelRuleExtractionContext.java:0)
+Class <org.gradle.model.internal.inspect.DefaultMethodRuleDefinition$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMethodRuleDefinition.java:0)
+Class <org.gradle.model.internal.inspect.DefaultMethodRuleDefinition> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMethodRuleDefinition.java:0)
+Class <org.gradle.model.internal.inspect.DefaultModelRuleInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRuleInvoker.java:0)
+Class <org.gradle.model.internal.inspect.DefaultRuleSourceValidationProblemCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRuleSourceValidationProblemCollector.java:0)
+Class <org.gradle.model.internal.inspect.DefaultsModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultsModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ExtractedModelRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExtractedModelRule.java:0)
+Class <org.gradle.model.internal.inspect.ExtractedRuleSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExtractedRuleSource.java:0)
+Class <org.gradle.model.internal.inspect.FactoryBasedStructNodeInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FactoryBasedStructNodeInitializer.java:0)
+Class <org.gradle.model.internal.inspect.FinalizeModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FinalizeModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.FormattingValidationProblemCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FormattingValidationProblemCollector.java:0)
+Class <org.gradle.model.internal.inspect.ManagedChildNodeCreatorStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedChildNodeCreatorStrategy.java:0)
+Class <org.gradle.model.internal.inspect.ManagedModelCreationRuleExtractor$ExtractedManagedCreationRule$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ManagedModelCreationRuleExtractor$ExtractedManagedCreationRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ManagedModelCreationRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.MethodBackedModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodBackedModelAction.java:0)
+Class <org.gradle.model.internal.inspect.MethodModelRuleApplicationContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodModelRuleApplicationContext.java:0)
+Class <org.gradle.model.internal.inspect.MethodModelRuleExtractionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodModelRuleExtractionContext.java:0)
+Class <org.gradle.model.internal.inspect.MethodModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.MethodModelRuleExtractors> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodModelRuleExtractors.java:0)
+Class <org.gradle.model.internal.inspect.MethodRuleAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodRuleAction.java:0)
+Class <org.gradle.model.internal.inspect.MethodRuleDefinition> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodRuleDefinition.java:0)
+Class <org.gradle.model.internal.inspect.ModelElementProjection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelElementProjection.java:0)
+Class <org.gradle.model.internal.inspect.ModelElementProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelElementProjection.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$AbstractRuleSourceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$CachedRuleSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$ConcreteRuleSourceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$DefaultExtractedRuleSource$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$DefaultExtractedRuleSource$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$DefaultExtractedRuleSource$ContextualizedModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$DefaultExtractedRuleSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$ExtractedRuleDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$ParameterizedExtractedRuleSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$ParameterizedRuleSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$StatelessExtractedRuleSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor$StatelessRuleSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleInvoker.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleSourceDetector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleSourceDetector.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleSourceDetector$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleSourceDetector.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleSourceDetector$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleSourceDetector.java:0)
+Class <org.gradle.model.internal.inspect.ModelRuleSourceDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRuleSourceDetector.java:0)
+Class <org.gradle.model.internal.inspect.MutateModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MutateModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ProjectionOnlyNodeInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectionOnlyNodeInitializer.java:0)
+Class <org.gradle.model.internal.inspect.ReadonlyImmutableManagedPropertyException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReadonlyImmutableManagedPropertyException.java:0)
+Class <org.gradle.model.internal.inspect.RuleApplicationScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleApplicationScope.java:0)
+Class <org.gradle.model.internal.inspect.RuleDefinitionRuleExtractor$ExtractedRuleSourceDefinitionRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleDefinitionRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.RuleDefinitionRuleExtractor$RuleSourceApplicationAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleDefinitionRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.RuleDefinitionRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleDefinitionRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.RuleExtractorUtils$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleExtractorUtils.java:0)
+Class <org.gradle.model.internal.inspect.RuleExtractorUtils$NonReferenceDescendantsSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleExtractorUtils.java:0)
+Class <org.gradle.model.internal.inspect.RuleExtractorUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleExtractorUtils.java:0)
+Class <org.gradle.model.internal.inspect.RuleSourceValidationProblemCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleSourceValidationProblemCollector.java:0)
+Class <org.gradle.model.internal.inspect.StructNodeInitializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructNodeInitializer.java:0)
+Class <org.gradle.model.internal.inspect.StructNodeInitializer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructNodeInitializer.java:0)
+Class <org.gradle.model.internal.inspect.StructNodeInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructNodeInitializer.java:0)
+Class <org.gradle.model.internal.inspect.UnmanagedModelCreationRuleExtractor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.UnmanagedModelCreationRuleExtractor$ExtractedUnmanagedCreationRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.UnmanagedModelCreationRuleExtractor$UnmanagedElementCreationAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.UnmanagedModelCreationRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedModelCreationRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.UnmanagedPropertyMissingSetterException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedPropertyMissingSetterException.java:0)
+Class <org.gradle.model.internal.inspect.ValidateModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidateModelRuleExtractor.java:0)
+Class <org.gradle.model.internal.inspect.ValidationProblemCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ValidationProblemCollector.java:0)
+Class <org.gradle.model.internal.manage.binding.AbstractDelegationBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDelegationBinding.java:0)
+Class <org.gradle.model.internal.manage.binding.AbstractStructMethodBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractStructMethodBinding.java:0)
+Class <org.gradle.model.internal.manage.binding.BridgeMethodBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BridgeMethodBinding.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindings.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore$7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore$CacheKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DefaultStructBindingsStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.DelegateMethodBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DelegateMethodBinding.java:0)
+Class <org.gradle.model.internal.manage.binding.DirectMethodBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DirectMethodBinding.java:0)
+Class <org.gradle.model.internal.manage.binding.InvalidManagedTypeException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidManagedTypeException.java:0)
+Class <org.gradle.model.internal.manage.binding.ManagedProperty> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProperty.java:0)
+Class <org.gradle.model.internal.manage.binding.ManagedPropertyMethodBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedPropertyMethodBinding.java:0)
+Class <org.gradle.model.internal.manage.binding.StructBindingExtractionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructBindingExtractionContext.java:0)
+Class <org.gradle.model.internal.manage.binding.StructBindingValidationProblemCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructBindingValidationProblemCollector.java:0)
+Class <org.gradle.model.internal.manage.binding.StructBindings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructBindings.java:0)
+Class <org.gradle.model.internal.manage.binding.StructBindingsStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructBindingsStore.java:0)
+Class <org.gradle.model.internal.manage.binding.StructMethodBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructMethodBinding.java:0)
+Class <org.gradle.model.internal.manage.binding.StructMethodImplementationBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructMethodImplementationBinding.java:0)
+Class <org.gradle.model.internal.manage.instance.GeneratedViewState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GeneratedViewState.java:0)
+Class <org.gradle.model.internal.manage.instance.ManagedInstance> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedInstance.java:0)
+Class <org.gradle.model.internal.manage.instance.ManagedProxyFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProxyFactory.java:0)
+Class <org.gradle.model.internal.manage.instance.ManagedProxyFactory$CacheKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProxyFactory.java:0)
+Class <org.gradle.model.internal.manage.instance.ManagedProxyFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProxyFactory.java:0)
+Class <org.gradle.model.internal.manage.instance.ModelElementState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelElementState.java:0)
+Class <org.gradle.model.internal.manage.projection.ManagedModelProjection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedModelProjection.java:0)
+Class <org.gradle.model.internal.manage.projection.ManagedModelProjection$2$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedModelProjection.java:0)
+Class <org.gradle.model.internal.manage.projection.ManagedModelProjection$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedModelProjection.java:0)
+Class <org.gradle.model.internal.manage.projection.ManagedModelProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedModelProjection.java:0)
+Class <org.gradle.model.internal.manage.schema.AbstractModelSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractModelSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.AbstractStructSchema$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractStructSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.AbstractStructSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractStructSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.CollectionSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.CompositeSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.ManagedImplSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedImplSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.ManagedImplStructSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedImplStructSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.ModelMapSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.ModelProperty> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelProperty.java:0)
+Class <org.gradle.model.internal.manage.schema.ModelSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.ModelSchemaStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaStore.java:0)
+Class <org.gradle.model.internal.manage.schema.ModelSetSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.RuleSourceSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleSourceSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.ScalarCollectionSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.ScalarValueSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarValueSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.SpecializedMapSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.StructSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.UnmanagedImplStructSchema> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedImplStructSchema.java:0)
+Class <org.gradle.model.internal.manage.schema.cache.ModelSchemaCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaCache.java:0)
+Class <org.gradle.model.internal.manage.schema.cache.MultiWeakClassSet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiWeakClassSet.java:0)
+Class <org.gradle.model.internal.manage.schema.cache.MultiWeakClassSet$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiWeakClassSet.java:0)
+Class <org.gradle.model.internal.manage.schema.cache.MultiWeakClassSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiWeakClassSet.java:0)
+Class <org.gradle.model.internal.manage.schema.cache.SingleWeakClassSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SingleWeakClassSet.java:0)
+Class <org.gradle.model.internal.manage.schema.cache.WeakClassSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WeakClassSet.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.AbstractProxyClassGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractProxyClassGenerator.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.CandidateMethods$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CandidateMethods.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.CandidateMethods> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CandidateMethods.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.CollectionNodeInitializerExtractionSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionNodeInitializerExtractionSupport.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaExtractionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelSchemaExtractionContext.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaExtractor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelSchemaExtractor.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelSchemaExtractor.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelSchemaStore.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.EnumStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EnumStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.FactoryBasedStructNodeInitializerExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FactoryBasedStructNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.InvalidManagedModelElementTypeException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidManagedModelElementTypeException.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.JavaUtilCollectionStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaUtilCollectionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.JavaUtilCollectionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaUtilCollectionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.JdkValueTypeStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JdkValueTypeStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ManagedCollectionProxyClassGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedCollectionProxyClassGenerator.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ManagedImplStructNodeInitializerExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedImplStructNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ManagedImplStructStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedImplStructStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ManagedProxyClassGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProxyClassGenerator.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ManagedProxyClassGenerator$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProxyClassGenerator.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ManagedProxyClassGenerator$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProxyClassGenerator.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ManagedProxyClassGenerator$GeneratedView> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProxyClassGenerator.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ManagedProxyClassGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ManagedProxyClassGenerator.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelMapNodeInitializerExtractionStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelMapNodeInitializerExtractionStrategy$ModelMapNodeInitializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelMapNodeInitializerExtractionStrategy$ModelMapNodeInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelMapNodeInitializerExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelMapStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelMapStrategy$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelMapStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelPropertyExtractionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPropertyExtractionContext.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelPropertyExtractionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPropertyExtractionResult.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaAspect> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaAspect.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaAspectExtractionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaAspectExtractionResult.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaAspectExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaAspectExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaAspectExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaAspectExtractor.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaExtractionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaExtractionContext.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaExtractor.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaUtils$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaUtils.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaUtils$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaUtils.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSchemaUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSchemaUtils.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSetNodeInitializerExtractionStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSetNodeInitializerExtractionStrategy$ModelSetModelViewFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSetNodeInitializerExtractionStrategy$ModelSetNodeInitializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSetNodeInitializerExtractionStrategy$ModelSetNodeInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSetNodeInitializerExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSetStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSetStrategy$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ModelSetStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelSetStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.NodeInitializerExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.PrimitiveStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrimitiveStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.PrimitiveTypes> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrimitiveTypes.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.PropertyAccessorExtractionContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertyAccessorExtractionContext.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.RuleSourceSchemaExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleSourceSchemaExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionModelView> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionModelView.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$ListBackedCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$ListModelView> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$MutationSafeCollection$MutationSafeIterator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$MutationSafeCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$ScalarCollectionModelProjection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$ScalarCollectionModelProjection$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$ScalarCollectionModelProjection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$SetBackedCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy$SetModelView> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarCollectionNodeInitializerExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarCollectionNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.ScalarTypes> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScalarTypes.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.SpecializedMapNodeInitializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapNodeInitializer.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.SpecializedMapNodeInitializer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapNodeInitializer.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.SpecializedMapNodeInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapNodeInitializer.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.SpecializedMapNodeInitializerExtractionStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.SpecializedMapNodeInitializerExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapNodeInitializerExtractionStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.SpecializedMapStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.SpecializedMapStrategy$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.SpecializedMapStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SpecializedMapStrategy.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.StructSchemaExtractionStrategySupport$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructSchemaExtractionStrategySupport.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.StructSchemaExtractionStrategySupport$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructSchemaExtractionStrategySupport.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.StructSchemaExtractionStrategySupport$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructSchemaExtractionStrategySupport.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.StructSchemaExtractionStrategySupport$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructSchemaExtractionStrategySupport.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.StructSchemaExtractionStrategySupport$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructSchemaExtractionStrategySupport.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.StructSchemaExtractionStrategySupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StructSchemaExtractionStrategySupport.java:0)
+Class <org.gradle.model.internal.manage.schema.extract.UnmanagedImplStructStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnmanagedImplStructStrategy.java:0)
+Class <org.gradle.model.internal.method.WeaklyTypeReferencingMethod$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WeaklyTypeReferencingMethod.java:0)
+Class <org.gradle.model.internal.method.WeaklyTypeReferencingMethod$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WeaklyTypeReferencingMethod.java:0)
+Class <org.gradle.model.internal.method.WeaklyTypeReferencingMethod$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WeaklyTypeReferencingMethod.java:0)
+Class <org.gradle.model.internal.method.WeaklyTypeReferencingMethod> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WeaklyTypeReferencingMethod.java:0)
+Class <org.gradle.model.internal.registry.BindingPredicate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BindingPredicate.java:0)
+Class <org.gradle.model.internal.registry.BoringProjectState$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BoringProjectState.java:0)
+Class <org.gradle.model.internal.registry.BoringProjectState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BoringProjectState.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$ApplyActions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$CloseGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$DelegatingListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$Discover> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$GoalGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$MakeKnown> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$ModelGoal$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$ModelGoal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$ModelNodeGoal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$NotifyDiscovered> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$RunModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TransitionChildrenOrReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TransitionDependents> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TransitionNodeToState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TryBindInputs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TryDefineScopeForType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TryDiscoverSelfDiscoveringInScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TryResolveAndDiscoverPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TryResolvePath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry$TryResolveReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.DefaultModelRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.ModelBinding> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelBinding.java:0)
+Class <org.gradle.model.internal.registry.ModelElementNode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelElementNode.java:0)
+Class <org.gradle.model.internal.registry.ModelGraph$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelGraph.java:0)
+Class <org.gradle.model.internal.registry.ModelGraph$PendingState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelGraph.java:0)
+Class <org.gradle.model.internal.registry.ModelGraph> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelGraph.java:0)
+Class <org.gradle.model.internal.registry.ModelListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelListener.java:0)
+Class <org.gradle.model.internal.registry.ModelNodeInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelNodeInternal.java:0)
+Class <org.gradle.model.internal.registry.ModelPathSuggestionProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPathSuggestionProvider.java:0)
+Class <org.gradle.model.internal.registry.ModelPathSuggestionProvider$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPathSuggestionProvider.java:0)
+Class <org.gradle.model.internal.registry.ModelPathSuggestionProvider$Suggestion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPathSuggestionProvider.java:0)
+Class <org.gradle.model.internal.registry.ModelPathSuggestionProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelPathSuggestionProvider.java:0)
+Class <org.gradle.model.internal.registry.ModelReferenceNode$ModelViewWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelReferenceNode.java:0)
+Class <org.gradle.model.internal.registry.ModelReferenceNode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelReferenceNode.java:0)
+Class <org.gradle.model.internal.registry.ModelRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistry.java:0)
+Class <org.gradle.model.internal.registry.ModelRegistryInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistryInternal.java:0)
+Class <org.gradle.model.internal.registry.ModelRegistryScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelRegistryScope.java:0)
+Class <org.gradle.model.internal.registry.NodeAtState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NodeAtState.java:0)
+Class <org.gradle.model.internal.registry.OneOfTypeBinderCreationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OneOfTypeBinderCreationListener.java:0)
+Class <org.gradle.model.internal.registry.PathBinderCreationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PathBinderCreationListener.java:0)
+Class <org.gradle.model.internal.registry.RuleBinder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBinder.java:0)
+Class <org.gradle.model.internal.registry.RuleBinder$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBinder.java:0)
+Class <org.gradle.model.internal.registry.RuleBinder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBinder.java:0)
+Class <org.gradle.model.internal.registry.RuleBindings$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBindings.java:0)
+Class <org.gradle.model.internal.registry.RuleBindings$NodeAtStateIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBindings.java:0)
+Class <org.gradle.model.internal.registry.RuleBindings$PathPredicateIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBindings.java:0)
+Class <org.gradle.model.internal.registry.RuleBindings$PredicateMatches> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBindings.java:0)
+Class <org.gradle.model.internal.registry.RuleBindings$Reference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBindings.java:0)
+Class <org.gradle.model.internal.registry.RuleBindings$ScopeIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBindings.java:0)
+Class <org.gradle.model.internal.registry.RuleBindings$TypePredicateIndex> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBindings.java:0)
+Class <org.gradle.model.internal.registry.RuleBindings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleBindings.java:0)
+Class <org.gradle.model.internal.registry.RuleContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuleContext.java:0)
+Class <org.gradle.model.internal.registry.UnboundModelRulesException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundModelRulesException.java:0)
+Class <org.gradle.model.internal.registry.UnboundRulesProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundRulesProcessor.java:0)
+Class <org.gradle.model.internal.report.AmbiguousBindingReporter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AmbiguousBindingReporter.java:0)
+Class <org.gradle.model.internal.report.AmbiguousBindingReporter$Provider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AmbiguousBindingReporter.java:0)
+Class <org.gradle.model.internal.report.AmbiguousBindingReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AmbiguousBindingReporter.java:0)
+Class <org.gradle.model.internal.report.IncompatibleTypeReferenceReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncompatibleTypeReferenceReporter.java:0)
+Class <org.gradle.model.internal.report.unbound.UnboundRule$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundRule.java:0)
+Class <org.gradle.model.internal.report.unbound.UnboundRule$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundRule.java:0)
+Class <org.gradle.model.internal.report.unbound.UnboundRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundRule.java:0)
+Class <org.gradle.model.internal.report.unbound.UnboundRuleInput$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundRuleInput.java:0)
+Class <org.gradle.model.internal.report.unbound.UnboundRuleInput$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundRuleInput.java:0)
+Class <org.gradle.model.internal.report.unbound.UnboundRuleInput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundRuleInput.java:0)
+Class <org.gradle.model.internal.report.unbound.UnboundRulesReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnboundRulesReporter.java:0)
+Class <org.gradle.model.internal.type.ClassTypeWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassTypeWrapper.java:0)
+Class <org.gradle.model.internal.type.GenericArrayTypeWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GenericArrayTypeWrapper.java:0)
+Class <org.gradle.model.internal.type.ModelType$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelType.java:0)
+Class <org.gradle.model.internal.type.ModelType$Builder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelType.java:0)
+Class <org.gradle.model.internal.type.ModelType$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelType.java:0)
+Class <org.gradle.model.internal.type.ModelType$Parameter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelType.java:0)
+Class <org.gradle.model.internal.type.ModelType$Parameter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelType.java:0)
+Class <org.gradle.model.internal.type.ModelType$Simple> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelType.java:0)
+Class <org.gradle.model.internal.type.ModelType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelType.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes$9> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ModelTypes> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelTypes.java:0)
+Class <org.gradle.model.internal.type.ParameterizedTypeWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ParameterizedTypeWrapper.java:0)
+Class <org.gradle.model.internal.type.TypeVariableTypeWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeVariableTypeWrapper.java:0)
+Class <org.gradle.model.internal.type.TypeWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeWrapper.java:0)
+Class <org.gradle.model.internal.type.WildcardTypeWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WildcardTypeWrapper.java:0)
+Class <org.gradle.model.internal.type.WildcardWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WildcardWrapper.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$ImplementationFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$ImplementationInfoImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$ImplementationRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$InternalViewRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$RegistrationHierarchyVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$TypeRegistration$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$TypeRegistration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$TypeRegistrationBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory$TypeRegistrationBuilderImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.BaseInstanceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.InstanceFactory$ImplementationInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstanceFactory.java:0)
+Class <org.gradle.model.internal.typeregistration.InstanceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InstanceFactory.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractBinaryToolSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractBinaryToolSpec.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractNativeBinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractNativeBinaryRenderer.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractNativeBinarySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractNativeBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractNativeComponentSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractNativeComponentSpec.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractNativeLibraryBinarySpec$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractNativeLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractNativeLibraryBinarySpec$HeaderFileSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractNativeLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractNativeLibraryBinarySpec$LibraryOutputs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractNativeLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractNativeLibraryBinarySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractNativeLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.AbstractTargetedNativeComponentSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractTargetedNativeComponentSpec.java:0)
+Class <org.gradle.nativeplatform.internal.BinaryToolSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryToolSpec.java:0)
+Class <org.gradle.nativeplatform.internal.BuildOperationLoggingCompilerDecorator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationLoggingCompilerDecorator.java:0)
+Class <org.gradle.nativeplatform.internal.BundleLinkerSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BundleLinkerSpec.java:0)
+Class <org.gradle.nativeplatform.internal.CompilerOutputFileNamingScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerOutputFileNamingScheme.java:0)
+Class <org.gradle.nativeplatform.internal.CompilerOutputFileNamingSchemeFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerOutputFileNamingSchemeFactory.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultBuildType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildType.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultBuildTypeContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTypeContainer.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultFlavor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFlavor.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultFlavorContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFlavorContainer.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultLinkerSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLinkerSpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultNativeExecutableBinarySpec$DefaultTasksCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeExecutableBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultNativeExecutableBinarySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeExecutableBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultNativeExecutableSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeExecutableSpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultNativeLibrarySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeLibrarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultPreprocessingTool> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPreprocessingTool.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultSharedLibraryBinarySpec$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSharedLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultSharedLibraryBinarySpec$DefaultTasksCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSharedLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultSharedLibraryBinarySpec$SharedLibraryLinkOutputs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSharedLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultSharedLibraryBinarySpec$SharedLibraryRuntimeOutputs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSharedLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultSharedLibraryBinarySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSharedLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultStaticLibraryArchiverSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStaticLibraryArchiverSpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultStaticLibraryBinarySpec$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStaticLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultStaticLibraryBinarySpec$DefaultTasksCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStaticLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultStaticLibraryBinarySpec$StaticLibraryLinkOutputs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStaticLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultStaticLibraryBinarySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStaticLibraryBinarySpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultStripperSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStripperSpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultSymbolExtractorSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSymbolExtractorSpec.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultTargetMachine> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTargetMachine.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultTargetMachineFactory$TargetMachineImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTargetMachineFactory.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultTargetMachineFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTargetMachineFactory.java:0)
+Class <org.gradle.nativeplatform.internal.DefaultTool> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTool.java:0)
+Class <org.gradle.nativeplatform.internal.LinkerSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LinkerSpec.java:0)
+Class <org.gradle.nativeplatform.internal.NativeBinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryRenderer.java:0)
+Class <org.gradle.nativeplatform.internal.NativeBinarySpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinarySpecInternal.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents$BinaryLibs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeComponents> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponents.java:0)
+Class <org.gradle.nativeplatform.internal.NativeDependentBinariesResolutionStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.nativeplatform.internal.NativeDependentBinariesResolutionStrategy$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.nativeplatform.internal.NativeDependentBinariesResolutionStrategy$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.nativeplatform.internal.NativeDependentBinariesResolutionStrategy$State> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.nativeplatform.internal.NativeDependentBinariesResolutionStrategy$TestSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.nativeplatform.internal.NativeDependentBinariesResolutionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.nativeplatform.internal.NativeExecutableBinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeExecutableBinaryRenderer.java:0)
+Class <org.gradle.nativeplatform.internal.NativeExecutableBinarySpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeExecutableBinarySpecInternal.java:0)
+Class <org.gradle.nativeplatform.internal.NativePlatformResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformResolver.java:0)
+Class <org.gradle.nativeplatform.internal.NativePlatformResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformResolver.java:0)
+Class <org.gradle.nativeplatform.internal.ProjectNativeLibraryRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectNativeLibraryRequirement.java:0)
+Class <org.gradle.nativeplatform.internal.SharedLibraryBinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SharedLibraryBinaryRenderer.java:0)
+Class <org.gradle.nativeplatform.internal.SharedLibraryBinarySpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SharedLibraryBinarySpecInternal.java:0)
+Class <org.gradle.nativeplatform.internal.SharedLibraryLinkerSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SharedLibraryLinkerSpec.java:0)
+Class <org.gradle.nativeplatform.internal.StaticLibraryArchiverSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StaticLibraryArchiverSpec.java:0)
+Class <org.gradle.nativeplatform.internal.StaticLibraryBinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StaticLibraryBinaryRenderer.java:0)
+Class <org.gradle.nativeplatform.internal.StaticLibraryBinarySpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StaticLibraryBinarySpecInternal.java:0)
+Class <org.gradle.nativeplatform.internal.StripperSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StripperSpec.java:0)
+Class <org.gradle.nativeplatform.internal.SymbolExtractorSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolExtractorSpec.java:0)
+Class <org.gradle.nativeplatform.internal.TargetedNativeComponentInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TargetedNativeComponentInternal.java:0)
+Class <org.gradle.nativeplatform.internal.configure.DefaultNativeComponentExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeComponentExtension.java:0)
+Class <org.gradle.nativeplatform.internal.configure.NativeBinaries$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaries.java:0)
+Class <org.gradle.nativeplatform.internal.configure.NativeBinaries> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaries.java:0)
+Class <org.gradle.nativeplatform.internal.configure.NativeBinaryRules> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryRules.java:0)
+Class <org.gradle.nativeplatform.internal.configure.NativeComponentRules> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeComponentRules.java:0)
+Class <org.gradle.nativeplatform.internal.modulemap.GenerateModuleMapFile$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GenerateModuleMapFile.java:0)
+Class <org.gradle.nativeplatform.internal.modulemap.GenerateModuleMapFile> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GenerateModuleMapFile.java:0)
+Class <org.gradle.nativeplatform.internal.modulemap.ModuleMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleMap.java:0)
+Class <org.gradle.nativeplatform.internal.pch.PchEnabledLanguageTransform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PchEnabledLanguageTransform.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.AbstractPrebuiltLibraryBinary$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPrebuiltLibraryBinary.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.AbstractPrebuiltLibraryBinary$ValidatingFileSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPrebuiltLibraryBinary.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.AbstractPrebuiltLibraryBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPrebuiltLibraryBinary.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.DefaultPrebuiltLibraries> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPrebuiltLibraries.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.DefaultPrebuiltLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPrebuiltLibrary.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.DefaultPrebuiltSharedLibraryBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPrebuiltSharedLibraryBinary.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.DefaultPrebuiltStaticLibraryBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPrebuiltStaticLibraryBinary.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.PrebuiltLibraryBinaryLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrebuiltLibraryBinaryLocator.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.PrebuiltLibraryInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrebuiltLibraryInitializer.java:0)
+Class <org.gradle.nativeplatform.internal.prebuilt.PrebuiltLibraryResolveException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PrebuiltLibraryResolveException.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.ApiRequirementNativeDependencyResolver$ApiAdaptedNativeLibraryRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApiRequirementNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.ApiRequirementNativeDependencyResolver$ApiNativeDependencySet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApiRequirementNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.ApiRequirementNativeDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApiRequirementNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.CachingLibraryBinaryLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingLibraryBinaryLocator.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.ChainedLibraryBinaryLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ChainedLibraryBinaryLocator.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.DefaultLibraryResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLibraryResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.DefaultLibraryResolver$LibraryResolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLibraryResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.DefaultLibraryResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLibraryResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.DefaultNativeDependencySet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeDependencySet.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.InputHandlingNativeDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputHandlingNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.LibraryBinaryLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibraryBinaryLocator.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.LibraryIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibraryIdentifier.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.LibraryNativeDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibraryNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeBinaryRequirementResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryRequirementResolveResult.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeBinaryResolveResult$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryResolveResult.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeBinaryResolveResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryResolveResult.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeDependencyNotationParser$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependencyNotationParser.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeDependencyNotationParser$LibraryConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependencyNotationParser.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeDependencyNotationParser$NativeLibraryRequirementMapNotationConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependencyNotationParser.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeDependencyNotationParser> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependencyNotationParser.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.NativeDependencyResolverServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependencyResolverServices.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.ProjectLibraryBinaryLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectLibraryBinaryLocator.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.RequirementParsingNativeDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RequirementParsingNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.SourceSetNativeDependencyResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceSetNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.SourceSetNativeDependencyResolver$EmptyNativeDependencySet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceSetNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.SourceSetNativeDependencyResolver$LanguageSourceSetNativeDependencySet$HeaderFileCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceSetNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.SourceSetNativeDependencyResolver$LanguageSourceSetNativeDependencySet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceSetNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.resolve.SourceSetNativeDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceSetNativeDependencyResolver.java:0)
+Class <org.gradle.nativeplatform.internal.services.NativeBinaryServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryServices.java:0)
+Class <org.gradle.nativeplatform.internal.services.NativeBinaryServices$BuildSessionScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryServices.java:0)
+Class <org.gradle.nativeplatform.internal.services.NativeBinaryServices$ProjectCompilerServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryServices.java:0)
+Class <org.gradle.nativeplatform.internal.services.NativeBinaryServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeBinaryServices.java:0)
+Class <org.gradle.nativeplatform.platform.internal.ArchitectureInternal$InstructionSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureInternal.java:0)
+Class <org.gradle.nativeplatform.platform.internal.ArchitectureInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureInternal.java:0)
+Class <org.gradle.nativeplatform.platform.internal.Architectures$KnownArchitecture> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Architectures.java:0)
+Class <org.gradle.nativeplatform.platform.internal.Architectures> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Architectures.java:0)
+Class <org.gradle.nativeplatform.platform.internal.DefaultArchitecture> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultArchitecture.java:0)
+Class <org.gradle.nativeplatform.platform.internal.DefaultNativePlatform$HostPlatform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativePlatform.java:0)
+Class <org.gradle.nativeplatform.platform.internal.DefaultNativePlatform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativePlatform.java:0)
+Class <org.gradle.nativeplatform.platform.internal.DefaultOperatingSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultOperatingSystem.java:0)
+Class <org.gradle.nativeplatform.platform.internal.NativePlatformInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatformInternal.java:0)
+Class <org.gradle.nativeplatform.platform.internal.NativePlatforms> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativePlatforms.java:0)
+Class <org.gradle.nativeplatform.platform.internal.OperatingSystemInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperatingSystemInternal.java:0)
+Class <org.gradle.nativeplatform.test.cpp.internal.DefaultCppTestExecutable$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppTestExecutable.java:0)
+Class <org.gradle.nativeplatform.test.cpp.internal.DefaultCppTestExecutable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppTestExecutable.java:0)
+Class <org.gradle.nativeplatform.test.cpp.internal.DefaultCppTestSuite> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCppTestSuite.java:0)
+Class <org.gradle.nativeplatform.test.cunit.internal.DefaultCUnitTestSuiteBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCUnitTestSuiteBinary.java:0)
+Class <org.gradle.nativeplatform.test.cunit.internal.DefaultCUnitTestSuiteSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCUnitTestSuiteSpec.java:0)
+Class <org.gradle.nativeplatform.test.googletest.internal.DefaultGoogleTestTestSuiteBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGoogleTestTestSuiteBinary.java:0)
+Class <org.gradle.nativeplatform.test.googletest.internal.DefaultGoogleTestTestSuiteSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGoogleTestTestSuiteSpec.java:0)
+Class <org.gradle.nativeplatform.test.internal.DefaultNativeTestSuiteBinarySpec$DefaultTasksCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeTestSuiteBinarySpec.java:0)
+Class <org.gradle.nativeplatform.test.internal.DefaultNativeTestSuiteBinarySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeTestSuiteBinarySpec.java:0)
+Class <org.gradle.nativeplatform.test.internal.NativeDependentBinariesResolutionStrategyTestSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeDependentBinariesResolutionStrategyTestSupport.java:0)
+Class <org.gradle.nativeplatform.test.internal.NativeTestSuiteBinaryRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeTestSuiteBinaryRenderer.java:0)
+Class <org.gradle.nativeplatform.test.internal.NativeTestSuiteBinarySpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeTestSuiteBinarySpecInternal.java:0)
+Class <org.gradle.nativeplatform.test.internal.NativeTestSuites$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeTestSuites.java:0)
+Class <org.gradle.nativeplatform.test.internal.NativeTestSuites$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeTestSuites.java:0)
+Class <org.gradle.nativeplatform.test.internal.NativeTestSuites$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeTestSuites.java:0)
+Class <org.gradle.nativeplatform.test.internal.NativeTestSuites> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeTestSuites.java:0)
+Class <org.gradle.nativeplatform.test.internal.services.NativeTestingServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeTestingServices.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestBinary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftXCTestBinary.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestBundle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftXCTestBundle.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestExecutable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftXCTestExecutable.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.DefaultSwiftXCTestSuite> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftXCTestSuite.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.execution.XCTestDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCTestDescriptor.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.execution.XCTestExecuter$XCTestDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCTestExecuter.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.execution.XCTestExecuter$XCTestProcessor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCTestExecuter.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.execution.XCTestExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCTestExecuter.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.execution.XCTestScraper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCTestScraper.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.execution.XCTestSelection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCTestSelection.java:0)
+Class <org.gradle.nativeplatform.test.xctest.internal.execution.XCTestTestExecutionSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XCTestTestExecutionSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.AbstractCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.AbstractCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.AbstractPlatformToolProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.ArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgsTransformer.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.CommandLineToolContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineToolContext.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineToolInvocation.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineToolInvocationFailure.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.CommandLineToolInvocationWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineToolInvocationWorker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultCommandLineToolInvocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCommandLineToolInvocation.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultMutableCommandLineToolContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMutableCommandLineToolContext.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeToolChainRegistry.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeToolChainRegistry.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry$AbstractUnavailabilityToolChainSearchDescription> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeToolChainRegistry.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry$UnavailableNativeToolChain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeToolChainRegistry.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry$UnavailableToolChainDescription> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeToolChainRegistry.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry$UnsupportedNativeToolChain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeToolChainRegistry.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry$UnsupportedToolChainDescription> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeToolChainRegistry.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.DefaultNativeToolChainRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultNativeToolChainRegistry.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.EmptySystemLibraries> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EmptySystemLibraries.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.ExecutableTools> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutableTools.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.ExtendableToolChain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExtendableToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.MacroArgsConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MacroArgsConverter.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.MutableCommandLineToolContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MutableCommandLineToolContext.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeLanguage$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeLanguage.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeLanguage$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeLanguage.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeLanguage$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeLanguage.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeLanguage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeLanguage.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeLanguageTools> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeLanguageTools.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal$Identifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeToolChainInternal.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeToolChainInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeToolChainInternal.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.NativeToolChainRegistryInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeToolChainRegistryInternal.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.OptionsFileArgsWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OptionsFileArgsWriter.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.OutputCleaningCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputCleaningCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.PCHUtils$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PCHUtils.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.PCHUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PCHUtils.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.PreCompiledHeader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PreCompiledHeader.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.Stripper$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Stripper.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.Stripper$StripperArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Stripper.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.Stripper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Stripper.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.SymbolExtractor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolExtractor.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.SymbolExtractor$SymbolExtractorArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolExtractor.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.SymbolExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolExtractor.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.SymbolExtractorOsConfig$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolExtractorOsConfig.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.SymbolExtractorOsConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SymbolExtractorOsConfig.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.SystemLibraries> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemLibraries.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.ToolType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolType.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.UnavailablePlatformToolProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnavailablePlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.UnsupportedPlatformToolProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsupportedPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.AssembleSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AssembleSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.CCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.CPCHCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CPCHCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.CppCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.CppPCHCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppPCHCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCPCHCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCPCHCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCppCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCppCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.ObjectiveCppPCHCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCppPCHCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.SwiftCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.compilespec.WindowsResourceCompileSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsResourceCompileSpec.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$CompilerMetaDataProviderWithDefaultArgs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$DefaultTargetPlatformConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$Intel32Architecture$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$Intel32Architecture> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$Intel64Architecture$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$Intel64Architecture> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$OsxArm64Architecture$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain$OsxArm64Architecture> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractGccCompatibleToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ArStaticLibraryArchiver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArStaticLibraryArchiver.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ArStaticLibraryArchiver$ArchiverSpecToArguments> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArStaticLibraryArchiver.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ArStaticLibraryArchiver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArStaticLibraryArchiver.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.Assembler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Assembler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.Assembler$AssemblerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Assembler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.Assembler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Assembler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CCompiler$CCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CPCHCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CPCHCompiler$CPCHCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CPCHCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CppCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CppCompiler$CppCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CppCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CppPCHCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CppPCHCompiler$CppPCHCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.CppPCHCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.GccCompatibleNativeCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccCompatibleNativeCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.GccCompilerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccCompilerArgsTransformer.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.GccLinker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.GccLinker$GccLinkerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.GccLinker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.GccOptionsFileArgsWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccOptionsFileArgsWriter.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.GccPlatformToolProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCCompiler$ObjectiveCCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCPCHCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCPCHCompiler$ObjectiveCPCHCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCPCHCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCppCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCppCompiler$ObjectiveCppCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCppCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCppPCHCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCppPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCppPCHCompiler$ObjectiveCppPCHCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCppPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.ObjectiveCppPCHCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectiveCppPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.TargetPlatformConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TargetPlatformConfiguration.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.metadata.GccCompilerType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccCompilerType.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.metadata.GccMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccMetadata.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.metadata.GccMetadataProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.metadata.GccMetadataProvider$DefaultGccMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.metadata.GccMetadataProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.gcc.metadata.SystemLibraryDiscovery> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemLibraryDiscovery.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.AbstractMetadataProvider$BrokenResultException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.AbstractMetadataProvider$DefaultCompilerExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.AbstractMetadataProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.CompilerMetaDataProvider$CompilerExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerMetaDataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.CompilerMetaDataProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerMetaDataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.CompilerMetaDataProviderFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerMetaDataProviderFactory.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.CompilerMetaDataProviderFactory$CachingCompilerMetaDataProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerMetaDataProviderFactory.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.CompilerMetaDataProviderFactory$Key> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerMetaDataProviderFactory.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.CompilerMetaDataProviderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerMetaDataProviderFactory.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.CompilerMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerMetadata.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.metadata.CompilerType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompilerType.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.AbstractWindowsKitComponentLocator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWindowsKitComponentLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.AbstractWindowsKitComponentLocator$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWindowsKitComponentLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.AbstractWindowsKitComponentLocator$DescendingComponentVersionComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWindowsKitComponentLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.AbstractWindowsKitComponentLocator$DiscoveryType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWindowsKitComponentLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.AbstractWindowsKitComponentLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWindowsKitComponentLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder$9> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureDescriptorBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureDescriptorBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.ArchitectureSpecificVisualCpp> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArchitectureSpecificVisualCpp.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.Assembler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Assembler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.Assembler$AssemblerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Assembler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.Assembler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Assembler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.CCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.CCompiler$CCompilerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.CCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.CPCHCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.CppCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.CppCompiler$CppCompilerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.CppCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.CppPCHCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CppPCHCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultUcrtLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUcrtLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultVisualCppPlatformToolChain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualCppPlatformToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultVisualStudioLocator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultVisualStudioLocator$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultVisualStudioLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultWindowsSdkLocator$SdkSearchResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWindowsSdkLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.DefaultWindowsSdkLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWindowsSdkLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.EscapeUserArgs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EscapeUserArgs.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LegacyWindowsSdkInstall$LegacyPlatformWindowsSdk> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyWindowsSdkInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LegacyWindowsSdkInstall> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyWindowsSdkInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LegacyWindowsSdkLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyWindowsSdkLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LibExeStaticLibraryArchiver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibExeStaticLibraryArchiver.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LibExeStaticLibraryArchiver$LibExeSpecToArguments> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibExeStaticLibraryArchiver.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LibExeStaticLibraryArchiver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LibExeStaticLibraryArchiver.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LinkExeLinker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LinkExeLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LinkExeLinker$LinkerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LinkExeLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.LinkExeLinker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LinkExeLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.UcrtInstall$UcrtSystemLibraries> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UcrtInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.UcrtInstall> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UcrtInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.UcrtLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UcrtLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCpp> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCpp.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppCompilerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppCompilerArgsTransformer.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppInstall> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppNativeCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppNativeCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppOptionsFileArgsWriter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppOptionsFileArgsWriter.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppPCHCompilerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppPCHCompilerArgsTransformer.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppPlatformToolProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppPlatformToolProvider$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppPlatformToolProvider$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppPlatformToolProvider$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppPlatformToolProvider$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppPlatformToolProvider$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppPlatformToolProvider$CompositeLibraries> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioInstall> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualStudioLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsComponentLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsComponentLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsKitInstall> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsKitInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsKitSdkInstall$WindowsKitBackedSdk> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsKitSdkInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsKitSdkInstall> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsKitSdkInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsKitWindowsSdkLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsKitWindowsSdkLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsResourceCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsResourceCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsResourceCompiler$RcCompilerArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsResourceCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsResourceCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsResourceCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsSdk> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsSdk.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsSdkInstall> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsSdkInstall.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsSdkLibraries> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsSdkLibraries.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsSdkLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsSdkLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.WindowsTools> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsTools.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.metadata.VisualCppMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppMetadata.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.AbstractVisualStudioVersionLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractVisualStudioVersionLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.CommandLineToolVersionLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineToolVersionLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.DefaultVisualCppMetadataProvider$DefaultVisualCppMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualCppMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.DefaultVisualCppMetadataProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualCppMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.DefaultVisualStudioMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVisualStudioMetadata.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.DefaultVswhereVersionLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVswhereVersionLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.SystemPathVersionLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPathVersionLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualCppInstallCandidate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppInstallCandidate.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualCppMetadataProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualCppMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualStudioInstallCandidate$Compatibility> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioInstallCandidate.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualStudioInstallCandidate> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioInstallCandidate.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualStudioMetaDataProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioMetaDataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualStudioMetadataBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioMetadataBuilder.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualStudioVersionDeterminer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioVersionDeterminer.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualStudioVersionDeterminer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioVersionDeterminer.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualStudioVersionDeterminer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioVersionDeterminer.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VisualStudioVersionLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisualStudioVersionLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.VswhereVersionLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VswhereVersionLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.msvcpp.version.WindowsRegistryVersionLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WindowsRegistryVersionLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.plugins.StandardToolChainsPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StandardToolChainsPlugin.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.DefaultSwiftcPlatformToolChain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSwiftcPlatformToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.IncrementalSwiftCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncrementalSwiftCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftCompiler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftCompiler$OutputFileMap$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftCompiler$OutputFileMap$Entry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftCompiler$OutputFileMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftCompiler$SwiftCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftCompiler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftCompiler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftDepsHandler$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftDepsHandler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftDepsHandler$SwiftDeps> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftDepsHandler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftDepsHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftDepsHandler.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftLinker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftLinker$SwiftCompileArgsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftLinker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftLinker.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftPlatformToolProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftPlatformToolProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftcToolChain$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftcToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.SwiftcToolChain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftcToolChain.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.metadata.SwiftcMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftcMetadata.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.metadata.SwiftcMetadataProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftcMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.metadata.SwiftcMetadataProvider$DefaultSwiftcMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftcMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.swift.metadata.SwiftcMetadataProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftcMetadataProvider.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.CommandLineToolConfigurationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineToolConfigurationInternal.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.CommandLineToolSearchResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CommandLineToolSearchResult.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.DefaultCommandLineToolConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCommandLineToolConfiguration.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.DefaultGccCommandLineToolConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGccCommandLineToolConfiguration.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.GccCommandLineToolConfigurationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GccCommandLineToolConfigurationInternal.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.ToolSearchPath$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolSearchPath.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.ToolSearchPath$FoundTool> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolSearchPath.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.ToolSearchPath$MissingTool> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolSearchPath.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.tools.ToolSearchPath> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolSearchPath.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.xcode.AbstractLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.xcode.MacOSSdkPathLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MacOSSdkPathLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.xcode.MacOSSdkPlatformPathLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MacOSSdkPlatformPathLocator.java:0)
+Class <org.gradle.nativeplatform.toolchain.internal.xcode.SwiftStdlibToolLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftStdlibToolLocator.java:0)
+Class <org.gradle.normalization.internal.DefaultInputNormalizationHandler$DefaultCachedState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInputNormalizationHandler.java:0)
+Class <org.gradle.normalization.internal.DefaultInputNormalizationHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInputNormalizationHandler.java:0)
+Class <org.gradle.normalization.internal.DefaultRuntimeClasspathNormalization$DefaultCachedState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRuntimeClasspathNormalization.java:0)
+Class <org.gradle.normalization.internal.DefaultRuntimeClasspathNormalization$DefaultPropertiesFileFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRuntimeClasspathNormalization.java:0)
+Class <org.gradle.normalization.internal.DefaultRuntimeClasspathNormalization$EvaluatableFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRuntimeClasspathNormalization.java:0)
+Class <org.gradle.normalization.internal.DefaultRuntimeClasspathNormalization$RuntimeMetaInfNormalization> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRuntimeClasspathNormalization.java:0)
+Class <org.gradle.normalization.internal.DefaultRuntimeClasspathNormalization> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRuntimeClasspathNormalization.java:0)
+Class <org.gradle.normalization.internal.InputNormalizationHandlerInternal$CachedState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputNormalizationHandlerInternal.java:0)
+Class <org.gradle.normalization.internal.InputNormalizationHandlerInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InputNormalizationHandlerInternal.java:0)
+Class <org.gradle.normalization.internal.RuntimeClasspathNormalizationInternal$CachedState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuntimeClasspathNormalizationInternal.java:0)
+Class <org.gradle.normalization.internal.RuntimeClasspathNormalizationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RuntimeClasspathNormalizationInternal.java:0)
+Class <org.gradle.platform.base.component.internal.AbstractComponentSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractComponentSpec.java:0)
+Class <org.gradle.platform.base.component.internal.ComponentSpecFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentSpecFactory.java:0)
+Class <org.gradle.platform.base.component.internal.ComponentSpecFactory$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentSpecFactory.java:0)
+Class <org.gradle.platform.base.component.internal.ComponentSpecFactory$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentSpecFactory.java:0)
+Class <org.gradle.platform.base.component.internal.ComponentSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentSpecFactory.java:0)
+Class <org.gradle.platform.base.component.internal.DefaultComponentSpec$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentSpec.java:0)
+Class <org.gradle.platform.base.component.internal.DefaultComponentSpec$ComponentInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentSpec.java:0)
+Class <org.gradle.platform.base.component.internal.DefaultComponentSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentSpec.java:0)
+Class <org.gradle.platform.base.internal.BinaryBuildAbility> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryBuildAbility.java:0)
+Class <org.gradle.platform.base.internal.BinaryNamingScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryNamingScheme.java:0)
+Class <org.gradle.platform.base.internal.BinarySpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinarySpecInternal.java:0)
+Class <org.gradle.platform.base.internal.BinaryTasksCollectionWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryTasksCollectionWrapper.java:0)
+Class <org.gradle.platform.base.internal.ComponentSpecIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentSpecIdentifier.java:0)
+Class <org.gradle.platform.base.internal.ComponentSpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentSpecInternal.java:0)
+Class <org.gradle.platform.base.internal.DefaultBinaryNamingScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBinaryNamingScheme.java:0)
+Class <org.gradle.platform.base.internal.DefaultBinaryTasksCollection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBinaryTasksCollection.java:0)
+Class <org.gradle.platform.base.internal.DefaultComponentSpecIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultComponentSpecIdentifier.java:0)
+Class <org.gradle.platform.base.internal.DefaultDependencySpecContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDependencySpecContainer.java:0)
+Class <org.gradle.platform.base.internal.DefaultLibraryBinaryDependencySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLibraryBinaryDependencySpec.java:0)
+Class <org.gradle.platform.base.internal.DefaultModuleDependencySpec$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleDependencySpec.java:0)
+Class <org.gradle.platform.base.internal.DefaultModuleDependencySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModuleDependencySpec.java:0)
+Class <org.gradle.platform.base.internal.DefaultPlatformContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPlatformContainer.java:0)
+Class <org.gradle.platform.base.internal.DefaultPlatformRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPlatformRequirement.java:0)
+Class <org.gradle.platform.base.internal.DefaultPlatformResolvers$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPlatformResolvers.java:0)
+Class <org.gradle.platform.base.internal.DefaultPlatformResolvers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPlatformResolvers.java:0)
+Class <org.gradle.platform.base.internal.DefaultProjectDependencySpec$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectDependencySpec.java:0)
+Class <org.gradle.platform.base.internal.DefaultProjectDependencySpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectDependencySpec.java:0)
+Class <org.gradle.platform.base.internal.FixedBuildAbility> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FixedBuildAbility.java:0)
+Class <org.gradle.platform.base.internal.HasIntermediateOutputsComponentSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HasIntermediateOutputsComponentSpec.java:0)
+Class <org.gradle.platform.base.internal.PlatformAwareComponentSpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlatformAwareComponentSpecInternal.java:0)
+Class <org.gradle.platform.base.internal.PlatformRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlatformRequirement.java:0)
+Class <org.gradle.platform.base.internal.PlatformResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlatformResolver.java:0)
+Class <org.gradle.platform.base.internal.PlatformResolvers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PlatformResolvers.java:0)
+Class <org.gradle.platform.base.internal.ToolSearchBuildAbility> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolSearchBuildAbility.java:0)
+Class <org.gradle.platform.base.internal.VariantAspect> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VariantAspect.java:0)
+Class <org.gradle.platform.base.internal.VariantAspectExtractionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VariantAspectExtractionStrategy.java:0)
+Class <org.gradle.platform.base.internal.builder.TypeBuilderInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeBuilderInternal.java:0)
+Class <org.gradle.platform.base.internal.dependents.AbstractDependentBinariesResolutionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractDependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.platform.base.internal.dependents.BaseDependentBinariesResolutionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseDependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.platform.base.internal.dependents.DefaultDependentBinariesResolutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDependentBinariesResolutionResult.java:0)
+Class <org.gradle.platform.base.internal.dependents.DefaultDependentBinariesResolvedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDependentBinariesResolvedResult.java:0)
+Class <org.gradle.platform.base.internal.dependents.DefaultDependentBinariesResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDependentBinariesResolver.java:0)
+Class <org.gradle.platform.base.internal.dependents.DependentBinariesResolutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentBinariesResolutionResult.java:0)
+Class <org.gradle.platform.base.internal.dependents.DependentBinariesResolutionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentBinariesResolutionStrategy.java:0)
+Class <org.gradle.platform.base.internal.dependents.DependentBinariesResolvedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentBinariesResolvedResult.java:0)
+Class <org.gradle.platform.base.internal.dependents.DependentBinariesResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DependentBinariesResolver.java:0)
+Class <org.gradle.platform.base.internal.registry.AbstractAnnotationDrivenComponentModelRuleExtractor$RuleMethodDataCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAnnotationDrivenComponentModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.AbstractAnnotationDrivenComponentModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractAnnotationDrivenComponentModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.BinaryTasksModelRuleExtractor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryTasksModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.BinaryTasksModelRuleExtractor$BinaryTaskRule$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryTasksModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.BinaryTasksModelRuleExtractor$BinaryTaskRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryTasksModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.BinaryTasksModelRuleExtractor$ExtractedBinaryTasksRule$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryTasksModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.BinaryTasksModelRuleExtractor$ExtractedBinaryTasksRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryTasksModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.BinaryTasksModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BinaryTasksModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentBinariesModelRuleExtractor$ComponentBinariesRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentBinariesModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentBinariesModelRuleExtractor$ExtractedComponentBinariesRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentBinariesModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentBinariesModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentBinariesModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentModelBaseServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentModelBaseServiceRegistry.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentModelBaseServiceRegistry$BuildScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentModelBaseServiceRegistry.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentModelBaseServiceRegistry$GlobalScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentModelBaseServiceRegistry.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentModelBaseServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentModelBaseServiceRegistry.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentTypeModelRuleExtractor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentTypeModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentTypeModelRuleExtractor$ComponentTypeRegistrationInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentTypeModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentTypeModelRuleExtractor$ExtractedTypeRule$TypeRegistrationAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentTypeModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentTypeModelRuleExtractor$ExtractedTypeRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentTypeModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.ComponentTypeModelRuleExtractor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentTypeModelRuleExtractor.java:0)
+Class <org.gradle.platform.base.internal.registry.DefaultTypeBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTypeBuilder.java:0)
+Class <org.gradle.platform.base.internal.registry.ModelMapBasedRule$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapBasedRule.java:0)
+Class <org.gradle.platform.base.internal.registry.ModelMapBasedRule$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapBasedRule.java:0)
+Class <org.gradle.platform.base.internal.registry.ModelMapBasedRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapBasedRule.java:0)
+Class <org.gradle.platform.base.internal.toolchain.ComponentFound> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentFound.java:0)
+Class <org.gradle.platform.base.internal.toolchain.ComponentNotFound> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ComponentNotFound.java:0)
+Class <org.gradle.platform.base.internal.toolchain.SearchResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SearchResult.java:0)
+Class <org.gradle.platform.base.internal.toolchain.ToolChainAvailability$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolChainAvailability.java:0)
+Class <org.gradle.platform.base.internal.toolchain.ToolChainAvailability$FixedMessageToolSearchResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolChainAvailability.java:0)
+Class <org.gradle.platform.base.internal.toolchain.ToolChainAvailability> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolChainAvailability.java:0)
+Class <org.gradle.platform.base.internal.toolchain.ToolChainInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolChainInternal.java:0)
+Class <org.gradle.platform.base.internal.toolchain.ToolProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolProvider.java:0)
+Class <org.gradle.platform.base.internal.toolchain.ToolSearchResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolSearchResult.java:0)
+Class <org.gradle.platform.internal.DefaultBuildPlatform$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildPlatform.java:0)
+Class <org.gradle.platform.internal.DefaultBuildPlatform> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildPlatform.java:0)
+Class <org.gradle.plugin.internal.InvalidPluginIdException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidPluginIdException.java:0)
+Class <org.gradle.plugin.internal.InvalidPluginVersionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidPluginVersionException.java:0)
+Class <org.gradle.plugin.internal.PluginUsePluginServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginUsePluginServiceRegistry.java:0)
+Class <org.gradle.plugin.internal.PluginUsePluginServiceRegistry$BuildScopeServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginUsePluginServiceRegistry.java:0)
+Class <org.gradle.plugin.internal.PluginUsePluginServiceRegistry$BuildScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginUsePluginServiceRegistry.java:0)
+Class <org.gradle.plugin.internal.PluginUsePluginServiceRegistry$SettingsScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginUsePluginServiceRegistry.java:0)
+Class <org.gradle.plugin.internal.PluginUsePluginServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginUsePluginServiceRegistry.java:0)
+Class <org.gradle.plugin.management.internal.DefaultPluginManagementSpec$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManagementSpec.java:0)
+Class <org.gradle.plugin.management.internal.DefaultPluginManagementSpec$PluginDependenciesSpecImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManagementSpec.java:0)
+Class <org.gradle.plugin.management.internal.DefaultPluginManagementSpec$PluginDependencySpecImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManagementSpec.java:0)
+Class <org.gradle.plugin.management.internal.DefaultPluginManagementSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginManagementSpec.java:0)
+Class <org.gradle.plugin.management.internal.DefaultPluginRequest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginRequest.java:0)
+Class <org.gradle.plugin.management.internal.DefaultPluginResolutionStrategy$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginResolutionStrategy.java:0)
+Class <org.gradle.plugin.management.internal.DefaultPluginResolutionStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginResolutionStrategy.java:0)
+Class <org.gradle.plugin.management.internal.DefaultPluginResolveDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginResolveDetails.java:0)
+Class <org.gradle.plugin.management.internal.InvalidPluginRequestException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InvalidPluginRequestException.java:0)
+Class <org.gradle.plugin.management.internal.MergedPluginRequests> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MergedPluginRequests.java:0)
+Class <org.gradle.plugin.management.internal.MultiPluginRequests> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiPluginRequests.java:0)
+Class <org.gradle.plugin.management.internal.PluginManagementSpecInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginManagementSpecInternal.java:0)
+Class <org.gradle.plugin.management.internal.PluginRequestInternal$Origin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginRequestInternal.java:0)
+Class <org.gradle.plugin.management.internal.PluginRequestInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginRequestInternal.java:0)
+Class <org.gradle.plugin.management.internal.PluginRequests$EmptyPluginRequests> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginRequests.java:0)
+Class <org.gradle.plugin.management.internal.PluginRequests> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginRequests.java:0)
+Class <org.gradle.plugin.management.internal.PluginResolutionStrategyInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginResolutionStrategyInternal.java:0)
+Class <org.gradle.plugin.management.internal.SingletonPluginRequests> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SingletonPluginRequests.java:0)
+Class <org.gradle.plugin.management.internal.autoapply.AutoAppliedGradleEnterprisePlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AutoAppliedGradleEnterprisePlugin.java:0)
+Class <org.gradle.plugin.management.internal.autoapply.AutoAppliedPluginHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AutoAppliedPluginHandler.java:0)
+Class <org.gradle.plugin.management.internal.autoapply.AutoAppliedPluginRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AutoAppliedPluginRegistry.java:0)
+Class <org.gradle.plugin.management.internal.autoapply.CompositeAutoAppliedPluginRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositeAutoAppliedPluginRegistry.java:0)
+Class <org.gradle.plugin.management.internal.autoapply.DefaultAutoAppliedPluginHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAutoAppliedPluginHandler.java:0)
+Class <org.gradle.plugin.management.internal.autoapply.InjectedAutoAppliedPluginRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InjectedAutoAppliedPluginRegistry.java:0)
+Class <org.gradle.plugin.use.resolve.internal.AlreadyOnClasspathPluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AlreadyOnClasspathPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.internal.ArtifactRepositoriesPluginResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactRepositoriesPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.internal.ArtifactRepositoriesPluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArtifactRepositoriesPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.internal.ClassPathPluginResolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassPathPluginResolution.java:0)
+Class <org.gradle.plugin.use.resolve.internal.CompositePluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CompositePluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.internal.CorePluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CorePluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.internal.NoopPluginResolver$NoopPlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoopPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.internal.NoopPluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoopPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.internal.PluginArtifactRepositories> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginArtifactRepositories.java:0)
+Class <org.gradle.plugin.use.resolve.internal.PluginArtifactRepositoriesProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginArtifactRepositoriesProvider.java:0)
+Class <org.gradle.plugin.use.resolve.internal.PluginResolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginResolution.java:0)
+Class <org.gradle.plugin.use.resolve.internal.PluginResolutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginResolutionResult.java:0)
+Class <org.gradle.plugin.use.resolve.internal.PluginResolveContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginResolveContext.java:0)
+Class <org.gradle.plugin.use.resolve.internal.PluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.internal.PluginResolverContributor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginResolverContributor.java:0)
+Class <org.gradle.plugin.use.resolve.internal.SimplePluginResolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimplePluginResolution.java:0)
+Class <org.gradle.plugin.use.resolve.internal.local.PluginPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginPublication.java:0)
+Class <org.gradle.plugin.use.resolve.service.internal.ClientInjectedClasspathPluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientInjectedClasspathPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.service.internal.DefaultInjectedClasspathPluginResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInjectedClasspathPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.service.internal.DefaultInjectedClasspathPluginResolver$InjectedClasspathPluginResolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInjectedClasspathPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.service.internal.DefaultInjectedClasspathPluginResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInjectedClasspathPluginResolver.java:0)
+Class <org.gradle.plugin.use.resolve.service.internal.InjectedClasspathInstrumentationStrategy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InjectedClasspathInstrumentationStrategy.java:0)
+Class <org.gradle.plugin.use.tracker.internal.PluginVersionTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginVersionTracker.java:0)
+Class <org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDeploymentDescriptor.java:0)
+Class <org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDeploymentDescriptor.java:0)
+Class <org.gradle.plugins.ear.descriptor.internal.DefaultEarModule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEarModule.java:0)
+Class <org.gradle.plugins.ear.descriptor.internal.DefaultEarSecurityRole> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEarSecurityRole.java:0)
+Class <org.gradle.plugins.ear.descriptor.internal.DefaultEarWebModule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEarWebModule.java:0)
+Class <org.gradle.plugins.ear.internal.DefaultEarPluginConvention> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEarPluginConvention.java:0)
+Class <org.gradle.plugins.ide.eclipse.internal.AfterEvaluateHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AfterEvaluateHelper.java:0)
+Class <org.gradle.plugins.ide.eclipse.internal.EclipsePluginConstants> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipsePluginConstants.java:0)
+Class <org.gradle.plugins.ide.eclipse.internal.EclipseProjectMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseProjectMetadata.java:0)
+Class <org.gradle.plugins.ide.eclipse.internal.LinkedResourcesCreator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LinkedResourcesCreator.java:0)
+Class <org.gradle.plugins.ide.eclipse.internal.LinkedResourcesCreator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LinkedResourcesCreator.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.ClassFoldersCreator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassFoldersCreator.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.ClasspathFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathFactory.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.DefaultResourceFilter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceFilter.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.DefaultResourceFilterMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultResourceFilterMatcher.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.EclipseDependenciesCreator$EclipseDependenciesVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseDependenciesCreator.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.EclipseDependenciesCreator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseDependenciesCreator.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.EclipseJavaVersionMapper$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseJavaVersionMapper.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.EclipseJavaVersionMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseJavaVersionMapper.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory$FileReferenceImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileReferenceFactory.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileReferenceFactory.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.PathUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PathUtil.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.ProjectDependencyBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectDependencyBuilder.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.SourceFoldersCreator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SourceFoldersCreator.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.WtpClasspathAttributeSupport$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WtpClasspathAttributeSupport.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.WtpClasspathAttributeSupport$WtpClasspathAttributeDependencyVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WtpClasspathAttributeSupport.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.WtpClasspathAttributeSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WtpClasspathAttributeSupport.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.WtpComponentFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WtpComponentFactory.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.WtpComponentFactory$WtpDependenciesVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WtpComponentFactory.java:0)
+Class <org.gradle.plugins.ide.eclipse.model.internal.WtpComponentFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WtpComponentFactory.java:0)
+Class <org.gradle.plugins.ide.idea.internal.IdeaModuleMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaModuleMetadata.java:0)
+Class <org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer$1$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaScalaConfigurer.java:0)
+Class <org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaScalaConfigurer.java:0)
+Class <org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer$1$2$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaScalaConfigurer.java:0)
+Class <org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer$1$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaScalaConfigurer.java:0)
+Class <org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaScalaConfigurer.java:0)
+Class <org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaScalaConfigurer.java:0)
+Class <org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaScalaConfigurer.java:0)
+Class <org.gradle.plugins.ide.idea.model.internal.GeneratedIdeaScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GeneratedIdeaScope.java:0)
+Class <org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesOptimizer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaDependenciesOptimizer.java:0)
+Class <org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaDependenciesProvider.java:0)
+Class <org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider$IdeaDependenciesVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaDependenciesProvider.java:0)
+Class <org.gradle.plugins.ide.idea.model.internal.IdeaDependenciesProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaDependenciesProvider.java:0)
+Class <org.gradle.plugins.ide.idea.model.internal.ModuleDependencyBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModuleDependencyBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.DefaultIdeArtifactRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeArtifactRegistry.java:0)
+Class <org.gradle.plugins.ide.internal.DefaultIdeArtifactRegistry$MetadataReference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeArtifactRegistry.java:0)
+Class <org.gradle.plugins.ide.internal.DefaultIdeArtifactRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeArtifactRegistry.java:0)
+Class <org.gradle.plugins.ide.internal.IdeArtifactRegistry$Reference> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeArtifactRegistry.java:0)
+Class <org.gradle.plugins.ide.internal.IdeArtifactRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeArtifactRegistry.java:0)
+Class <org.gradle.plugins.ide.internal.IdeArtifactStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeArtifactStore.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$6> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$7> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$8$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$8> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$9$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$9$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin$9> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdePlugin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdePlugin.java:0)
+Class <org.gradle.plugins.ide.internal.IdeProjectMetadata> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeProjectMetadata.java:0)
+Class <org.gradle.plugins.ide.internal.IdeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeServices.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.DefaultUniqueProjectNameProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUniqueProjectNameProvider.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.DefaultUniqueProjectNameProvider$ProjectPathDeduplicationAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUniqueProjectNameProvider.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.DefaultUniqueProjectNameProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultUniqueProjectNameProvider.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.EclipseModelAwareUniqueProjectNameProvider$ProjectPathDeduplicationAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseModelAwareUniqueProjectNameProvider.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.EclipseModelAwareUniqueProjectNameProvider$ProjectStateWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseModelAwareUniqueProjectNameProvider.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.EclipseModelAwareUniqueProjectNameProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseModelAwareUniqueProjectNameProvider.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.HierarchicalElementAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HierarchicalElementAdapter.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.HierarchicalElementDeduplicator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HierarchicalElementDeduplicator.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.HierarchicalElementDeduplicator$StatefulDeduplicator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HierarchicalElementDeduplicator.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.HierarchicalElementDeduplicator$StatefulDeduplicator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HierarchicalElementDeduplicator.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.HierarchicalElementDeduplicator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HierarchicalElementDeduplicator.java:0)
+Class <org.gradle.plugins.ide.internal.configurer.UniqueProjectNameProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UniqueProjectNameProvider.java:0)
+Class <org.gradle.plugins.ide.internal.generator.AbstractPersistableConfigurationObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPersistableConfigurationObject.java:0)
+Class <org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertiesPersistableConfigurationObject.java:0)
+Class <org.gradle.plugins.ide.internal.generator.PropertyListPersistableConfigurationObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PropertyListPersistableConfigurationObject.java:0)
+Class <org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XmlPersistableConfigurationObject.java:0)
+Class <org.gradle.plugins.ide.internal.generator.XmlPersistableConfigurationObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (XmlPersistableConfigurationObject.java:0)
+Class <org.gradle.plugins.ide.internal.generator.generator.Generator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Generator.java:0)
+Class <org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistableConfigurationObject.java:0)
+Class <org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObjectGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistableConfigurationObjectGenerator.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.DefaultGradleApiSourcesResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleApiSourcesResolver.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.DefaultIdeDependencyResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeDependencyResolver.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.DefaultIdeDependencyResolver$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeDependencyResolver.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.DefaultIdeDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeDependencyResolver.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.GradleApiSourcesResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleApiSourcesResolver.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.IdeDependencySet$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeDependencySet.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.IdeDependencySet$IdeDependencyResult$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeDependencySet.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.IdeDependencySet$IdeDependencyResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeDependencySet.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.IdeDependencySet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeDependencySet.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.IdeDependencyVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeDependencyVisitor.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.NullGradleApiSourcesResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NullGradleApiSourcesResolver.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.UnresolvedIdeDependencyHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnresolvedIdeDependencyHandler.java:0)
+Class <org.gradle.plugins.ide.internal.resolver.model.IdeExtendedRepoFileDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeExtendedRepoFileDependency.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.BasicIdeaModelBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BasicIdeaModelBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.BuildEnvironmentBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildEnvironmentBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.BuildInvocationsBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildInvocationsBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.EclipseModelBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseModelBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.EclipseModelBuilder$ClasspathElements> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseModelBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.EclipseModelBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseModelBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.GradleBuildBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleBuildBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.GradleProjectBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProjectBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.IdeaModelBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaModelBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.PublicationsBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicationsBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.RunEclipseTasksBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RunEclipseTasksBuilder.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.TasksFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TasksFactory.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.ToolingModelBuilderSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelBuilderSupport.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.ToolingModelServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelServices.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.ToolingModelServices$BuildScopeToolingServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelServices.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.ToolingModelServices$BuildScopeToolingServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelServices.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.ToolingModelServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelServices.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultAccessRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAccessRule.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultClasspathAttribute> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClasspathAttribute.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseBuildCommand> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseBuildCommand.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseClasspathContainer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseClasspathContainer.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseClasspathEntry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseClasspathEntry.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseComponentSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseComponentSelector.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseDependency.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseExternalDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseExternalDependency.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseJavaSourceSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseJavaSourceSettings.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseLinkedResource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseLinkedResource.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseOutputLocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseOutputLocation.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseProject.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseProjectDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseProjectDependency.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseProjectNature> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseProjectNature.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseSourceDirectory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseSourceDirectory.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultEclipseTask> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultEclipseTask.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.eclipse.DefaultRunClosedProjectBuildDependencies> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultRunClosedProjectBuildDependencies.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaCompilerOutput> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaCompilerOutput.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaContentRoot> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaContentRoot.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaDependency.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaDependencyScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaDependencyScope.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaJavaLanguageSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaJavaLanguageSettings.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaLanguageLevel> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaLanguageLevel.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaModule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaModule.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaModuleDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaModuleDependency.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaProject.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaSingleEntryLibraryDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaSingleEntryLibraryDependency.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.idea.DefaultIdeaSourceDirectory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultIdeaSourceDirectory.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.java.DefaultInstalledJdk> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInstalledJdk.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.BasicGradleProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BasicGradleProject.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.DefaultBuildInvocations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildInvocations.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.DefaultGradleBuild> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleBuild.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.DefaultGradleModuleVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleModuleVersion.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.DefaultGradleProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleProject.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.DefaultGradlePublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradlePublication.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.DefaultGradleScript> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleScript.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.DefaultProjectPublications> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectPublications.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.LaunchableGradleProjectTask> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LaunchableGradleProjectTask.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.LaunchableGradleTask> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LaunchableGradleTask.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.LaunchableGradleTaskSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LaunchableGradleTaskSelector.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.PartialBasicGradleProject> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PartialBasicGradleProject.java:0)
+Class <org.gradle.plugins.ide.internal.tooling.model.TaskNameComparator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskNameComparator.java:0)
+Class <org.gradle.plugins.signing.internal.SignOperationInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SignOperationInternal.java:0)
+Class <org.gradle.plugins.signing.signatory.internal.ConfigurableSignatoryProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableSignatoryProvider.java:0)
+Class <org.gradle.plugins.signing.signatory.internal.ConfigurableSignatoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableSignatoryProvider.java:0)
+Class <org.gradle.plugins.signing.signatory.internal.gnupg.GnupgSignatoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GnupgSignatoryProvider.java:0)
+Class <org.gradle.plugins.signing.signatory.internal.pgp.InMemoryPgpSignatoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InMemoryPgpSignatoryProvider.java:0)
+Class <org.gradle.problems.BaseProblem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseProblem.java:0)
+Class <org.gradle.problems.BaseSolution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSolution.java:0)
+Class <org.gradle.problems.Location> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Location.java:0)
+Class <org.gradle.problems.Problem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Problem.java:0)
+Class <org.gradle.problems.Solution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Solution.java:0)
+Class <org.gradle.problems.StandardSeverity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StandardSeverity.java:0)
+Class <org.gradle.problems.WithDescription> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WithDescription.java:0)
+Class <org.gradle.problems.WithDocumentationLink> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WithDocumentationLink.java:0)
+Class <org.gradle.problems.WithId> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WithId.java:0)
+Class <org.gradle.problems.buildtree.ProblemLocationAnalyzer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProblemLocationAnalyzer.java:0)
+Class <org.gradle.problems.buildtree.ProblemReporter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProblemReporter.java:0)
+Class <org.gradle.problems.internal.RenderingUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RenderingUtils.java:0)
+Class <org.gradle.process.internal.AbstractExecHandleBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractExecHandleBuilder.java:0)
+Class <org.gradle.process.internal.BadExitCodeException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BadExitCodeException.java:0)
+Class <org.gradle.process.internal.CurrentProcess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CurrentProcess.java:0)
+Class <org.gradle.process.internal.DefaultExecAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecAction.java:0)
+Class <org.gradle.process.internal.DefaultExecActionFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecActionFactory.java:0)
+Class <org.gradle.process.internal.DefaultExecActionFactory$BuilderImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecActionFactory.java:0)
+Class <org.gradle.process.internal.DefaultExecActionFactory$DecoratingExecActionFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecActionFactory.java:0)
+Class <org.gradle.process.internal.DefaultExecActionFactory$DecoratingExecActionFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecActionFactory.java:0)
+Class <org.gradle.process.internal.DefaultExecActionFactory$ImmutableJavaForkOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecActionFactory.java:0)
+Class <org.gradle.process.internal.DefaultExecActionFactory$RootExecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecActionFactory.java:0)
+Class <org.gradle.process.internal.DefaultExecActionFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecActionFactory.java:0)
+Class <org.gradle.process.internal.DefaultExecHandle$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecHandle.java:0)
+Class <org.gradle.process.internal.DefaultExecHandle$CompositeStreamsHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecHandle.java:0)
+Class <org.gradle.process.internal.DefaultExecHandle$ExecResultImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecHandle.java:0)
+Class <org.gradle.process.internal.DefaultExecHandle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecHandle.java:0)
+Class <org.gradle.process.internal.DefaultExecHandleBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecHandleBuilder.java:0)
+Class <org.gradle.process.internal.DefaultExecOperations> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecOperations.java:0)
+Class <org.gradle.process.internal.DefaultExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecSpec.java:0)
+Class <org.gradle.process.internal.DefaultExecSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecSpecFactory.java:0)
+Class <org.gradle.process.internal.DefaultJavaDebugOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaDebugOptions.java:0)
+Class <org.gradle.process.internal.DefaultJavaExecAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaExecAction.java:0)
+Class <org.gradle.process.internal.DefaultJavaExecSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaExecSpec.java:0)
+Class <org.gradle.process.internal.DefaultJavaForkOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaForkOptions.java:0)
+Class <org.gradle.process.internal.DefaultProcessForkOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProcessForkOptions.java:0)
+Class <org.gradle.process.internal.ExecAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecAction.java:0)
+Class <org.gradle.process.internal.ExecActionFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecActionFactory.java:0)
+Class <org.gradle.process.internal.ExecException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecException.java:0)
+Class <org.gradle.process.internal.ExecFactory$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecFactory.java:0)
+Class <org.gradle.process.internal.ExecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecFactory.java:0)
+Class <org.gradle.process.internal.ExecHandle> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecHandle.java:0)
+Class <org.gradle.process.internal.ExecHandleBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecHandleBuilder.java:0)
+Class <org.gradle.process.internal.ExecHandleFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecHandleFactory.java:0)
+Class <org.gradle.process.internal.ExecHandleListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecHandleListener.java:0)
+Class <org.gradle.process.internal.ExecHandleRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecHandleRunner.java:0)
+Class <org.gradle.process.internal.ExecHandleShutdownHookAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecHandleShutdownHookAction.java:0)
+Class <org.gradle.process.internal.ExecHandleState> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecHandleState.java:0)
+Class <org.gradle.process.internal.JavaExecAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaExecAction.java:0)
+Class <org.gradle.process.internal.JavaExecHandleBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaExecHandleBuilder.java:0)
+Class <org.gradle.process.internal.JavaExecHandleFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaExecHandleFactory.java:0)
+Class <org.gradle.process.internal.JavaForkOptionsFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaForkOptionsFactory.java:0)
+Class <org.gradle.process.internal.JavaForkOptionsInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JavaForkOptionsInternal.java:0)
+Class <org.gradle.process.internal.JvmOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmOptions.java:0)
+Class <org.gradle.process.internal.ProcessArgumentsSpec$HasExecutable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessArgumentsSpec.java:0)
+Class <org.gradle.process.internal.ProcessArgumentsSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessArgumentsSpec.java:0)
+Class <org.gradle.process.internal.ProcessBuilderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessBuilderFactory.java:0)
+Class <org.gradle.process.internal.ProcessSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessSettings.java:0)
+Class <org.gradle.process.internal.ProcessStreamsSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProcessStreamsSpec.java:0)
+Class <org.gradle.process.internal.StreamsHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StreamsHandler.java:0)
+Class <org.gradle.process.internal.health.memory.CGroupMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CGroupMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.DefaultJvmMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJvmMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.DefaultMemoryManager$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMemoryManager.java:0)
+Class <org.gradle.process.internal.health.memory.DefaultMemoryManager$MemoryCheck> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMemoryManager.java:0)
+Class <org.gradle.process.internal.health.memory.DefaultMemoryManager$OsMemoryListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMemoryManager.java:0)
+Class <org.gradle.process.internal.health.memory.DefaultMemoryManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMemoryManager.java:0)
+Class <org.gradle.process.internal.health.memory.DefaultOsMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultOsMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.DisabledOsMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DisabledOsMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.JvmMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.JvmMemoryStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmMemoryStatus.java:0)
+Class <org.gradle.process.internal.health.memory.JvmMemoryStatusListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmMemoryStatusListener.java:0)
+Class <org.gradle.process.internal.health.memory.JvmMemoryStatusSnapshot> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JvmMemoryStatusSnapshot.java:0)
+Class <org.gradle.process.internal.health.memory.MBeanAttributeProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MBeanAttributeProvider.java:0)
+Class <org.gradle.process.internal.health.memory.MBeanOsMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MBeanOsMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.MaximumHeapHelper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MaximumHeapHelper.java:0)
+Class <org.gradle.process.internal.health.memory.MemInfoOsMemoryInfo$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MemInfoOsMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.MemInfoOsMemoryInfo$Meminfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MemInfoOsMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.MemInfoOsMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MemInfoOsMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.MemoryAmount> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MemoryAmount.java:0)
+Class <org.gradle.process.internal.health.memory.MemoryHolder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MemoryHolder.java:0)
+Class <org.gradle.process.internal.health.memory.MemoryManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MemoryManager.java:0)
+Class <org.gradle.process.internal.health.memory.NativeOsMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeOsMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.OsMemoryInfo> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OsMemoryInfo.java:0)
+Class <org.gradle.process.internal.health.memory.OsMemoryStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OsMemoryStatus.java:0)
+Class <org.gradle.process.internal.health.memory.OsMemoryStatusListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OsMemoryStatusListener.java:0)
+Class <org.gradle.process.internal.health.memory.OsMemoryStatusSnapshot> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OsMemoryStatusSnapshot.java:0)
+Class <org.gradle.process.internal.shutdown.ShutdownHooks> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShutdownHooks.java:0)
+Class <org.gradle.process.internal.streams.EmptyStdInStreamsHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EmptyStdInStreamsHandler.java:0)
+Class <org.gradle.process.internal.streams.ExecOutputHandleRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecOutputHandleRunner.java:0)
+Class <org.gradle.process.internal.streams.ForwardStdinStreamsHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForwardStdinStreamsHandler.java:0)
+Class <org.gradle.process.internal.streams.OutputStreamsForwarder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OutputStreamsForwarder.java:0)
+Class <org.gradle.process.internal.streams.SafeStreams> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SafeStreams.java:0)
+Class <org.gradle.process.internal.util.LongCommandLineDetectionUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LongCommandLineDetectionUtil.java:0)
+Class <org.gradle.process.internal.util.MergeOptionsUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MergeOptionsUtil.java:0)
+Class <org.gradle.process.internal.worker.DefaultMultiRequestWorkerProcessBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMultiRequestWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultMultiRequestWorkerProcessBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultMultiRequestWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultSingleRequestWorkerProcessBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSingleRequestWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultSingleRequestWorkerProcessBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSingleRequestWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerLoggingProtocol> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerLoggingProtocol.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcess$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcess.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcess$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcess.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcess.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcessBuilder$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcessBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcessBuilder$MemoryRequestingWorkerProcess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcessBuilder$WorkerJvmMemoryStatus$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcessBuilder$WorkerJvmMemoryStatus> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcessBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.DefaultWorkerProcessFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerProcessFactory.java:0)
+Class <org.gradle.process.internal.worker.GradleWorkerMain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleWorkerMain.java:0)
+Class <org.gradle.process.internal.worker.MultiRequestClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiRequestClient.java:0)
+Class <org.gradle.process.internal.worker.MultiRequestWorkerProcessBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MultiRequestWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.RequestHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RequestHandler.java:0)
+Class <org.gradle.process.internal.worker.SingleRequestWorkerProcessBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SingleRequestWorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.WorkerControl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerControl.java:0)
+Class <org.gradle.process.internal.worker.WorkerJvmMemoryInfoSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerJvmMemoryInfoSerializer.java:0)
+Class <org.gradle.process.internal.worker.WorkerJvmMemoryInfoSerializer$JvmMemoryStatusSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerJvmMemoryInfoSerializer.java:0)
+Class <org.gradle.process.internal.worker.WorkerJvmMemoryInfoSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerJvmMemoryInfoSerializer.java:0)
+Class <org.gradle.process.internal.worker.WorkerLoggingSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerLoggingSerializer.java:0)
+Class <org.gradle.process.internal.worker.WorkerProcess> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcess.java:0)
+Class <org.gradle.process.internal.worker.WorkerProcessBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessBuilder.java:0)
+Class <org.gradle.process.internal.worker.WorkerProcessContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessContext.java:0)
+Class <org.gradle.process.internal.worker.WorkerProcessException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessException.java:0)
+Class <org.gradle.process.internal.worker.WorkerProcessFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessFactory.java:0)
+Class <org.gradle.process.internal.worker.WorkerProcessSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessSettings.java:0)
+Class <org.gradle.process.internal.worker.child.ActionExecutionWorker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ActionExecutionWorker.java:0)
+Class <org.gradle.process.internal.worker.child.ActionExecutionWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ActionExecutionWorker.java:0)
+Class <org.gradle.process.internal.worker.child.ApplicationClassesInSystemClassLoaderWorkerImplementationFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ApplicationClassesInSystemClassLoaderWorkerImplementationFactory.java:0)
+Class <org.gradle.process.internal.worker.child.BootstrapSecurityManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BootstrapSecurityManager.java:0)
+Class <org.gradle.process.internal.worker.child.DefaultWorkerDirectoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerDirectoryProvider.java:0)
+Class <org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemApplicationClassLoaderWorker.java:0)
+Class <org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker$ContextImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemApplicationClassLoaderWorker.java:0)
+Class <org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker$PrintUnrecoverableErrorToFileHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemApplicationClassLoaderWorker.java:0)
+Class <org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker$WorkerServices$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemApplicationClassLoaderWorker.java:0)
+Class <org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker$WorkerServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemApplicationClassLoaderWorker.java:0)
+Class <org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker$WorkerServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemApplicationClassLoaderWorker.java:0)
+Class <org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemApplicationClassLoaderWorker.java:0)
+Class <org.gradle.process.internal.worker.child.WorkerDirectoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDirectoryProvider.java:0)
+Class <org.gradle.process.internal.worker.child.WorkerJvmMemoryInfoProtocol> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerJvmMemoryInfoProtocol.java:0)
+Class <org.gradle.process.internal.worker.child.WorkerLogEventListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerLogEventListener.java:0)
+Class <org.gradle.process.internal.worker.child.WorkerLoggingProtocol> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerLoggingProtocol.java:0)
+Class <org.gradle.process.internal.worker.child.WorkerProcessClassPathProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessClassPathProvider.java:0)
+Class <org.gradle.process.internal.worker.child.WorkerProcessClassPathProvider$CacheInitializer$WorkerClassRemapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessClassPathProvider.java:0)
+Class <org.gradle.process.internal.worker.child.WorkerProcessClassPathProvider$CacheInitializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessClassPathProvider.java:0)
+Class <org.gradle.process.internal.worker.child.WorkerProcessClassPathProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerProcessClassPathProvider.java:0)
+Class <org.gradle.process.internal.worker.messaging.WorkerConfig> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerConfig.java:0)
+Class <org.gradle.process.internal.worker.messaging.WorkerConfigSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerConfigSerializer.java:0)
+Class <org.gradle.process.internal.worker.request.Receiver$Failure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Receiver.java:0)
+Class <org.gradle.process.internal.worker.request.Receiver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Receiver.java:0)
+Class <org.gradle.process.internal.worker.request.Request> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Request.java:0)
+Class <org.gradle.process.internal.worker.request.RequestArgumentSerializers$JavaObjectSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RequestArgumentSerializers.java:0)
+Class <org.gradle.process.internal.worker.request.RequestArgumentSerializers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RequestArgumentSerializers.java:0)
+Class <org.gradle.process.internal.worker.request.RequestProtocol> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RequestProtocol.java:0)
+Class <org.gradle.process.internal.worker.request.RequestSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RequestSerializer.java:0)
+Class <org.gradle.process.internal.worker.request.RequestSerializerRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RequestSerializerRegistry.java:0)
+Class <org.gradle.process.internal.worker.request.ResponseProtocol> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResponseProtocol.java:0)
+Class <org.gradle.process.internal.worker.request.WorkerAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerAction.java:0)
+Class <org.gradle.process.internal.worker.request.WorkerAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerAction.java:0)
+Class <org.gradle.reporting.CodePanelRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CodePanelRenderer.java:0)
+Class <org.gradle.reporting.HtmlPageBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlPageBuilder.java:0)
+Class <org.gradle.reporting.HtmlReportBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportBuilder.java:0)
+Class <org.gradle.reporting.HtmlReportRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportRenderer.java:0)
+Class <org.gradle.reporting.HtmlReportRenderer$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportRenderer.java:0)
+Class <org.gradle.reporting.HtmlReportRenderer$DefaultHtmlReportContext$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportRenderer.java:0)
+Class <org.gradle.reporting.HtmlReportRenderer$DefaultHtmlReportContext$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportRenderer.java:0)
+Class <org.gradle.reporting.HtmlReportRenderer$DefaultHtmlReportContext$DefaultHtmlPageBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportRenderer.java:0)
+Class <org.gradle.reporting.HtmlReportRenderer$DefaultHtmlReportContext> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportRenderer.java:0)
+Class <org.gradle.reporting.HtmlReportRenderer$Resource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportRenderer.java:0)
+Class <org.gradle.reporting.HtmlReportRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HtmlReportRenderer.java:0)
+Class <org.gradle.reporting.ReportRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ReportRenderer.java:0)
+Class <org.gradle.reporting.TabbedPageRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TabbedPageRenderer.java:0)
+Class <org.gradle.reporting.TabsRenderer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TabsRenderer.java:0)
+Class <org.gradle.reporting.TabsRenderer$TabDefinition> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TabsRenderer.java:0)
+Class <org.gradle.reporting.TabsRenderer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TabsRenderer.java:0)
+Class <org.gradle.scripts.ScriptingLanguage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ScriptingLanguage.java:0)
+Class <org.gradle.security.internal.BaseSignatoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseSignatoryProvider.java:0)
+Class <org.gradle.security.internal.EmptyPublicKeyService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EmptyPublicKeyService.java:0)
+Class <org.gradle.security.internal.Fingerprint> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Fingerprint.java:0)
+Class <org.gradle.security.internal.KeyringFilePublicKeyService$LoadedKeys> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KeyringFilePublicKeyService.java:0)
+Class <org.gradle.security.internal.KeyringFilePublicKeyService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KeyringFilePublicKeyService.java:0)
+Class <org.gradle.security.internal.KeyringStripper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KeyringStripper.java:0)
+Class <org.gradle.security.internal.PGPUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PGPUtils.java:0)
+Class <org.gradle.security.internal.PublicKeyDownloadService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicKeyDownloadService.java:0)
+Class <org.gradle.security.internal.PublicKeyResultBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicKeyResultBuilder.java:0)
+Class <org.gradle.security.internal.PublicKeyService> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicKeyService.java:0)
+Class <org.gradle.security.internal.PublicKeyServiceChain$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicKeyServiceChain.java:0)
+Class <org.gradle.security.internal.PublicKeyServiceChain$FirstMatchBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicKeyServiceChain.java:0)
+Class <org.gradle.security.internal.PublicKeyServiceChain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PublicKeyServiceChain.java:0)
+Class <org.gradle.security.internal.SecuritySupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SecuritySupport.java:0)
+Class <org.gradle.security.internal.gnupg.BaseGnupgSignatoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseGnupgSignatoryProvider.java:0)
+Class <org.gradle.security.internal.gnupg.GnupgSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GnupgSettings.java:0)
+Class <org.gradle.security.internal.gnupg.GnupgSignatory$Services> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GnupgSignatory.java:0)
+Class <org.gradle.security.internal.gnupg.GnupgSignatory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GnupgSignatory.java:0)
+Class <org.gradle.security.internal.gnupg.GnupgSignatoryFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GnupgSignatoryFactory.java:0)
+Class <org.gradle.security.internal.pgp.BaseInMemoryPgpSignatoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseInMemoryPgpSignatoryProvider.java:0)
+Class <org.gradle.security.internal.pgp.BasePgpSignatoryProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BasePgpSignatoryProvider.java:0)
+Class <org.gradle.swiftpm.internal.AbstractProduct> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractProduct.java:0)
+Class <org.gradle.swiftpm.internal.BranchDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BranchDependency.java:0)
+Class <org.gradle.swiftpm.internal.DefaultExecutableProduct> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecutableProduct.java:0)
+Class <org.gradle.swiftpm.internal.DefaultLibraryProduct> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultLibraryProduct.java:0)
+Class <org.gradle.swiftpm.internal.DefaultPackage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPackage.java:0)
+Class <org.gradle.swiftpm.internal.DefaultTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTarget.java:0)
+Class <org.gradle.swiftpm.internal.Dependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Dependency.java:0)
+Class <org.gradle.swiftpm.internal.NativeProjectPublication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NativeProjectPublication.java:0)
+Class <org.gradle.swiftpm.internal.SwiftPmTarget> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SwiftPmTarget.java:0)
+Class <org.gradle.swiftpm.internal.VersionDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionDependency.java:0)
+Class <org.gradle.testing.base.internal.BaseTestSuiteSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseTestSuiteSpec.java:0)
+Class <org.gradle.testing.base.internal.DefaultTestingExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestingExtension.java:0)
+Class <org.gradle.testing.internal.util.GradlewRunner$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlewRunner.java:0)
+Class <org.gradle.testing.internal.util.GradlewRunner$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlewRunner.java:0)
+Class <org.gradle.testing.internal.util.GradlewRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradlewRunner.java:0)
+Class <org.gradle.testing.internal.util.RetryFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RetryFailure.java:0)
+Class <org.gradle.testing.internal.util.RetryUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RetryUtil.groovy:0)
+Class <org.gradle.testkit.runner.internal.BuildOperationParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationParameters.java:0)
+Class <org.gradle.testkit.runner.internal.ConstantTestKitDirProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConstantTestKitDirProvider.java:0)
+Class <org.gradle.testkit.runner.internal.DefaultBuildResult$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildResult.java:0)
+Class <org.gradle.testkit.runner.internal.DefaultBuildResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildResult.java:0)
+Class <org.gradle.testkit.runner.internal.DefaultBuildTask> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildTask.java:0)
+Class <org.gradle.testkit.runner.internal.DefaultGradleRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleRunner.java:0)
+Class <org.gradle.testkit.runner.internal.FeatureCheckBuildResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureCheckBuildResult.java:0)
+Class <org.gradle.testkit.runner.internal.GradleExecutionParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleExecutionParameters.java:0)
+Class <org.gradle.testkit.runner.internal.GradleExecutionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleExecutionResult.java:0)
+Class <org.gradle.testkit.runner.internal.GradleExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleExecutor.java:0)
+Class <org.gradle.testkit.runner.internal.GradleProvider$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProvider.java:0)
+Class <org.gradle.testkit.runner.internal.GradleProvider$EmbeddedGradleProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProvider.java:0)
+Class <org.gradle.testkit.runner.internal.GradleProvider$InstallationGradleProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProvider.java:0)
+Class <org.gradle.testkit.runner.internal.GradleProvider$UriGradleProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProvider.java:0)
+Class <org.gradle.testkit.runner.internal.GradleProvider$VersionGradleProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProvider.java:0)
+Class <org.gradle.testkit.runner.internal.GradleProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProvider.java:0)
+Class <org.gradle.testkit.runner.internal.PluginUnderTestMetadataReading> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginUnderTestMetadataReading.java:0)
+Class <org.gradle.testkit.runner.internal.TestKitDirProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestKitDirProvider.java:0)
+Class <org.gradle.testkit.runner.internal.ToolingApiGradleExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingApiGradleExecutor.java:0)
+Class <org.gradle.testkit.runner.internal.ToolingApiGradleExecutor$TaskExecutionProgressListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingApiGradleExecutor.java:0)
+Class <org.gradle.testkit.runner.internal.ToolingApiGradleExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingApiGradleExecutor.java:0)
+Class <org.gradle.testkit.runner.internal.feature.BuildResultOutputFeatureCheck> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildResultOutputFeatureCheck.java:0)
+Class <org.gradle.testkit.runner.internal.feature.FeatureCheck> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FeatureCheck.java:0)
+Class <org.gradle.testkit.runner.internal.feature.TestKitFeature> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestKitFeature.java:0)
+Class <org.gradle.testkit.runner.internal.io.NoCloseOutputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoCloseOutputStream.java:0)
+Class <org.gradle.testkit.runner.internal.io.SynchronizedOutputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SynchronizedOutputStream.java:0)
+Class <org.gradle.tooling.events.configuration.internal.DefaultPluginApplicationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPluginApplicationResult.java:0)
+Class <org.gradle.tooling.events.configuration.internal.DefaultProjectConfigurationFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationFailureResult.java:0)
+Class <org.gradle.tooling.events.configuration.internal.DefaultProjectConfigurationFinishEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationFinishEvent.java:0)
+Class <org.gradle.tooling.events.configuration.internal.DefaultProjectConfigurationOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.configuration.internal.DefaultProjectConfigurationStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationStartEvent.java:0)
+Class <org.gradle.tooling.events.configuration.internal.DefaultProjectConfigurationSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConfigurationSuccessResult.java:0)
+Class <org.gradle.tooling.events.download.internal.DefaultFileDownloadFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileDownloadFailureResult.java:0)
+Class <org.gradle.tooling.events.download.internal.DefaultFileDownloadFinishEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileDownloadFinishEvent.java:0)
+Class <org.gradle.tooling.events.download.internal.DefaultFileDownloadOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileDownloadOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.download.internal.DefaultFileDownloadStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileDownloadStartEvent.java:0)
+Class <org.gradle.tooling.events.download.internal.DefaultFileDownloadSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFileDownloadSuccessResult.java:0)
+Class <org.gradle.tooling.events.download.internal.NotFoundFileDownloadSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotFoundFileDownloadSuccessResult.java:0)
+Class <org.gradle.tooling.events.internal.AbstractPluginIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPluginIdentifier.java:0)
+Class <org.gradle.tooling.events.internal.BaseProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BaseProgressEvent.java:0)
+Class <org.gradle.tooling.events.internal.ConsumerOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.internal.DefaultBinaryPluginIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBinaryPluginIdentifier.java:0)
+Class <org.gradle.tooling.events.internal.DefaultFinishEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFinishEvent.java:0)
+Class <org.gradle.tooling.events.internal.DefaultOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.internal.DefaultOperationFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultOperationFailureResult.java:0)
+Class <org.gradle.tooling.events.internal.DefaultOperationSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultOperationSuccessResult.java:0)
+Class <org.gradle.tooling.events.internal.DefaultScriptPluginIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScriptPluginIdentifier.java:0)
+Class <org.gradle.tooling.events.internal.DefaultStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStartEvent.java:0)
+Class <org.gradle.tooling.events.internal.DefaultStatusEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultStatusEvent.java:0)
+Class <org.gradle.tooling.events.internal.OperationDescriptorWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperationDescriptorWrapper.java:0)
+Class <org.gradle.tooling.events.lifecycle.internal.DefaultBuildPhaseFinishEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildPhaseFinishEvent.java:0)
+Class <org.gradle.tooling.events.lifecycle.internal.DefaultBuildPhaseOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildPhaseOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.lifecycle.internal.DefaultBuildPhaseStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildPhaseStartEvent.java:0)
+Class <org.gradle.tooling.events.task.internal.DefaultTaskFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskFailureResult.java:0)
+Class <org.gradle.tooling.events.task.internal.DefaultTaskFinishEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskFinishEvent.java:0)
+Class <org.gradle.tooling.events.task.internal.DefaultTaskOperationDescriptor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.task.internal.DefaultTaskOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.task.internal.DefaultTaskSkippedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskSkippedResult.java:0)
+Class <org.gradle.tooling.events.task.internal.DefaultTaskStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskStartEvent.java:0)
+Class <org.gradle.tooling.events.task.internal.DefaultTaskSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskSuccessResult.java:0)
+Class <org.gradle.tooling.events.task.internal.TaskExecutionDetails$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionDetails.java:0)
+Class <org.gradle.tooling.events.task.internal.TaskExecutionDetails$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionDetails.java:0)
+Class <org.gradle.tooling.events.task.internal.TaskExecutionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskExecutionDetails.java:0)
+Class <org.gradle.tooling.events.task.internal.java.DefaultAnnotationProcessorResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAnnotationProcessorResult.java:0)
+Class <org.gradle.tooling.events.task.internal.java.DefaultJavaCompileTaskSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJavaCompileTaskSuccessResult.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultDebugOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultDebugOptions.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultJvmTestOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultJvmTestOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultTestFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFailureResult.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultTestFinishEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFinishEvent.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultTestOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultTestOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestOutputEvent.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultTestOutputOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestOutputOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultTestSkippedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestSkippedResult.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultTestStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestStartEvent.java:0)
+Class <org.gradle.tooling.events.test.internal.DefaultTestSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestSuccessResult.java:0)
+Class <org.gradle.tooling.events.transform.internal.DefaultTransformFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTransformFailureResult.java:0)
+Class <org.gradle.tooling.events.transform.internal.DefaultTransformFinishEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTransformFinishEvent.java:0)
+Class <org.gradle.tooling.events.transform.internal.DefaultTransformOperationDescriptor$DefaultSubjectDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTransformOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.transform.internal.DefaultTransformOperationDescriptor$DefaultTransformerDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTransformOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.transform.internal.DefaultTransformOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTransformOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.transform.internal.DefaultTransformStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTransformStartEvent.java:0)
+Class <org.gradle.tooling.events.transform.internal.DefaultTransformSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTransformSuccessResult.java:0)
+Class <org.gradle.tooling.events.work.internal.DefaultWorkItemFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkItemFailureResult.java:0)
+Class <org.gradle.tooling.events.work.internal.DefaultWorkItemFinishEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkItemFinishEvent.java:0)
+Class <org.gradle.tooling.events.work.internal.DefaultWorkItemOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkItemOperationDescriptor.java:0)
+Class <org.gradle.tooling.events.work.internal.DefaultWorkItemStartEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkItemStartEvent.java:0)
+Class <org.gradle.tooling.events.work.internal.DefaultWorkItemSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkItemSuccessResult.java:0)
+Class <org.gradle.tooling.internal.adapter.CollectionMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionMapper.java:0)
+Class <org.gradle.tooling.internal.adapter.MethodInvocation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodInvocation.java:0)
+Class <org.gradle.tooling.internal.adapter.MethodInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MethodInvoker.java:0)
+Class <org.gradle.tooling.internal.adapter.ObjectGraphAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ObjectGraphAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$AdaptingMethodInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$BeanMixInMethodInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$ChainedMethodInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$ClassMixInMethodInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$DefaultViewBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$InvocationHandlerImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$MethodInvocationCache$MethodInvocationKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$MethodInvocationCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$MixInBeanMappingAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$MixInMappingAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$MixInTypeMappingAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$NoOpDecoration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$PropertyCachingMethodInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$ReflectionMethodInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$SafeMethodInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$SupportedPropertyInvoker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$TypeSpecificMappingAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$ViewDecoration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$ViewGraphDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter$ViewKey> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.ProtocolToModelAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProtocolToModelAdapter.java:0)
+Class <org.gradle.tooling.internal.adapter.TargetTypeProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TargetTypeProvider.java:0)
+Class <org.gradle.tooling.internal.adapter.TypeInspector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TypeInspector.java:0)
+Class <org.gradle.tooling.internal.adapter.ViewBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ViewBuilder.java:0)
+Class <org.gradle.tooling.internal.build.DefaultBuildEnvironment$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEnvironment.java:0)
+Class <org.gradle.tooling.internal.build.DefaultBuildEnvironment$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEnvironment.java:0)
+Class <org.gradle.tooling.internal.build.DefaultBuildEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildEnvironment.java:0)
+Class <org.gradle.tooling.internal.build.VersionOnlyBuildEnvironment$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionOnlyBuildEnvironment.java:0)
+Class <org.gradle.tooling.internal.build.VersionOnlyBuildEnvironment$VersionOnlyGradleEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionOnlyBuildEnvironment.java:0)
+Class <org.gradle.tooling.internal.build.VersionOnlyBuildEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionOnlyBuildEnvironment.java:0)
+Class <org.gradle.tooling.internal.consumer.AbstractLongRunningOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractLongRunningOperation.java:0)
+Class <org.gradle.tooling.internal.consumer.BlockingResultHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BlockingResultHandler.java:0)
+Class <org.gradle.tooling.internal.consumer.CancellationTokenInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CancellationTokenInternal.java:0)
+Class <org.gradle.tooling.internal.consumer.ConnectionFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.ConnectionParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionParameters.java:0)
+Class <org.gradle.tooling.internal.consumer.ConnectorServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectorServices.java:0)
+Class <org.gradle.tooling.internal.consumer.ConnectorServices$ConnectorServiceRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectorServices.java:0)
+Class <org.gradle.tooling.internal.consumer.ConnectorServices$ConnectorServiceRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectorServices.java:0)
+Class <org.gradle.tooling.internal.consumer.ConnectorServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectorServices.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultBuildActionExecuter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultBuildActionExecuter$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultBuildActionExecuter$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultBuildActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultBuildLauncher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultBuildLauncher$ResultHandlerAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultBuildLauncher$ResultHandlerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultBuildLauncher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultCancellationTokenSource$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCancellationTokenSource.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultCancellationTokenSource$CancellationTokenImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCancellationTokenSource.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultCancellationTokenSource> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultCancellationTokenSource.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultConnectionParameters$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConnectionParameters.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultConnectionParameters$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConnectionParameters.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultConnectionParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConnectionParameters.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultExecutorServiceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultExecutorServiceFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultFailure.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultGradleConnector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleConnector.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultInternalJvmTestRequest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultInternalJvmTestRequest.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultModelBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelBuilder.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultModelBuilder$ResultHandlerAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelBuilder.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultModelBuilder$ResultHandlerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelBuilder.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultModelBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultModelBuilder.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultPhasedActionResultListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPhasedActionResultListener.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultPhasedBuildAction$DefaultBuildActionWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPhasedBuildAction.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultPhasedBuildAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPhasedBuildAction.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultPhasedBuildActionExecuter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPhasedBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultPhasedBuildActionExecuter$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPhasedBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultPhasedBuildActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPhasedBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultProjectConnection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultProjectConnection$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultProjectConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTaskSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTaskSpec.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestAssertionFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestAssertionFailure.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestFrameworkFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestFrameworkFailure.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestLauncher$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestLauncher$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestLauncher$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestLauncher$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestLauncher$5> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestLauncher$ResultHandlerAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestLauncher$ResultHandlerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestLauncher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestLauncher.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestSpec.java:0)
+Class <org.gradle.tooling.internal.consumer.DefaultTestSpecs> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultTestSpecs.java:0)
+Class <org.gradle.tooling.internal.consumer.Distribution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Distribution.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionFactory$ClasspathDistribution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionFactory$InstalledDistribution$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionFactory$InstalledDistribution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionFactory$ZippedDistribution$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionFactory$ZippedDistribution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionInstaller$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionInstaller.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionInstaller$AsyncDownload$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionInstaller.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionInstaller$AsyncDownload> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionInstaller.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionInstaller$ForwardingDownloadProgressListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionInstaller.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionInstaller$NoOpListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionInstaller.java:0)
+Class <org.gradle.tooling.internal.consumer.DistributionInstaller> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionInstaller.java:0)
+Class <org.gradle.tooling.internal.consumer.ExceptionTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExceptionTransformer.java:0)
+Class <org.gradle.tooling.internal.consumer.ExecutorServiceFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecutorServiceFactory.java:0)
+Class <org.gradle.tooling.internal.consumer.LoggingProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingProvider.java:0)
+Class <org.gradle.tooling.internal.consumer.PhasedBuildAction$BuildActionWrapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedBuildAction.java:0)
+Class <org.gradle.tooling.internal.consumer.PhasedBuildAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedBuildAction.java:0)
+Class <org.gradle.tooling.internal.consumer.ProjectConnectionCloseListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectConnectionCloseListener.java:0)
+Class <org.gradle.tooling.internal.consumer.ResultHandlerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResultHandlerAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.SynchronizedLogging$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SynchronizedLogging.java:0)
+Class <org.gradle.tooling.internal.consumer.SynchronizedLogging$ThreadLoggingServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SynchronizedLogging.java:0)
+Class <org.gradle.tooling.internal.consumer.SynchronizedLogging> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SynchronizedLogging.java:0)
+Class <org.gradle.tooling.internal.consumer.TestExecutionRequest$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionRequest.java:0)
+Class <org.gradle.tooling.internal.consumer.TestExecutionRequest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionRequest.java:0)
+Class <org.gradle.tooling.internal.consumer.async.AsyncConsumerActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AsyncConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAsyncConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAsyncConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultAsyncConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.AbstractConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.AbstractPost12ConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractPost12ConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ActionRunner.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.BuildControllerWithoutParameterSupport> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildControllerWithoutParameterSupport.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.CancellableActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CancellableActionRunner.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.CancellableConsumerActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CancellableConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.CancellableConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CancellableConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.CancellableModelBuilderBackedModelProducer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CancellableModelBuilderBackedModelProducer.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.CancellationExceptionTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CancellationExceptionTransformer.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ConsumerAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerAction.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ConsumerActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.HasCompatibilityMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HasCompatibilityMapping.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.InternalBuildActionAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildActionAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.InternalPhasedActionAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalPhasedActionAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LazyConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LazyConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LazyConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ModelProducer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelProducer.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedActionAwareBuildControllerAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NestedActionAwareBuildControllerAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.NoToolingApiConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoToolingApiConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.NotifyDaemonsAboutChangedPathsConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NotifyDaemonsAboutChangedPathsConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ParameterAcceptingConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ParameterAcceptingConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ParameterAwareBuildControllerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ParameterAwareBuildControllerAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ParameterValidatingConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ParameterValidatingConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ParameterizedActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ParameterizedActionRunner.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.PhasedActionAwareConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedActionAwareConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.PluginClasspathInjectionSupportedCheckModelProducer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginClasspathInjectionSupportedCheckModelProducer.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ProgressLoggingConsumerActionExecutor$ProgressListenerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLoggingConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ProgressLoggingConsumerActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressLoggingConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.RethrowingErrorsConsumerActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RethrowingErrorsConsumerActionExecutor.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ShutdownAwareConsumerConnection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShutdownAwareConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ShutdownAwareConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShutdownAwareConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.StopWhenIdleConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StopWhenIdleConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.TestExecutionConsumerConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionConsumerConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.ToolingParameterProxy> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingParameterProxy.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.UnparameterizedBuildController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnparameterizedBuildController.java:0)
+Class <org.gradle.tooling.internal.consumer.connection.UnsupportedOlderVersionConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (UnsupportedOlderVersionConnection.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.BackwardsCompatibleIdeaModuleDependency> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BackwardsCompatibleIdeaModuleDependency.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.BasicGradleProjectIdentifierMixin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BasicGradleProjectIdentifierMixin.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.ConsumerTargetTypeProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerTargetTypeProvider.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.EclipseExternalDependencyUnresolvedMixin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseExternalDependencyUnresolvedMixin.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.EclipseProjectHasAutoBuildMixin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EclipseProjectHasAutoBuildMixin.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.FixedBuildIdentifierProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FixedBuildIdentifierProvider.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.GradleProjectIdentifierMixin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProjectIdentifierMixin.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.IdeaModuleDependencyTargetNameMixin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaModuleDependencyTargetNameMixin.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.IdeaProjectJavaLanguageSettingsMixin$CompatibilityIdeaProjectJavaLanguageSettings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaProjectJavaLanguageSettingsMixin.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.IdeaProjectJavaLanguageSettingsMixin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IdeaProjectJavaLanguageSettingsMixin.java:0)
+Class <org.gradle.tooling.internal.consumer.converters.IncludedBuildsMixin> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncludedBuildsMixin.java:0)
+Class <org.gradle.tooling.internal.consumer.loader.CachingToolingImplementationLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CachingToolingImplementationLoader.java:0)
+Class <org.gradle.tooling.internal.consumer.loader.DefaultToolingImplementationLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingImplementationLoader.java:0)
+Class <org.gradle.tooling.internal.consumer.loader.SynchronizedToolingImplementationLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SynchronizedToolingImplementationLoader.java:0)
+Class <org.gradle.tooling.internal.consumer.loader.ToolingImplementationLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingImplementationLoader.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.BuildCancellationTokenAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildCancellationTokenAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.BuildProgressListenerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildProgressListenerAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerOperationParameters.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters$Builder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerOperationParameters.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerOperationParameters.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.ConsumerOperationParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConsumerOperationParameters.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.FailsafeBuildProgressListenerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailsafeBuildProgressListenerAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.ProgressListenerAdapter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressListenerAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.parameters.ProgressListenerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressListenerAdapter.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.ModelMapping$DefaultModelIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapping.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.ModelMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelMapping.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.VersionDetails$R26VersionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionDetails.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.VersionDetails$R28VersionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionDetails.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.VersionDetails$R35VersionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionDetails.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.VersionDetails$R44VersionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionDetails.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.VersionDetails$R48VersionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionDetails.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.VersionDetails$R51VersionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionDetails.java:0)
+Class <org.gradle.tooling.internal.consumer.versioning.VersionDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionDetails.java:0)
+Class <org.gradle.tooling.internal.gradle.DefaultBuildIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildIdentifier.java:0)
+Class <org.gradle.tooling.internal.gradle.DefaultProjectIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProjectIdentifier.java:0)
+Class <org.gradle.tooling.internal.gradle.GradleBuildIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleBuildIdentity.java:0)
+Class <org.gradle.tooling.internal.gradle.GradleProjectIdentity> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleProjectIdentity.java:0)
+Class <org.gradle.tooling.internal.gradle.TaskListingLaunchable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskListingLaunchable.java:0)
+Class <org.gradle.tooling.internal.protocol.BuildExceptionVersion1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildExceptionVersion1.java:0)
+Class <org.gradle.tooling.internal.protocol.BuildParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildParameters.java:0)
+Class <org.gradle.tooling.internal.protocol.BuildResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildResult.java:0)
+Class <org.gradle.tooling.internal.protocol.ConfigurableConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigurableConnection.java:0)
+Class <org.gradle.tooling.internal.protocol.ConnectionMetaDataVersion1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionMetaDataVersion1.java:0)
+Class <org.gradle.tooling.internal.protocol.ConnectionParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionParameters.java:0)
+Class <org.gradle.tooling.internal.protocol.ConnectionVersion4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionVersion4.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalActionAwareBuildController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalActionAwareBuildController.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalBuildAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildAction.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalBuildActionFailureException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildActionFailureException.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalBuildActionVersion2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildActionVersion2.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalBuildCancelledException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildCancelledException.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalBuildController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildController.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalBuildControllerVersion2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildControllerVersion2.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalBuildEnvironment> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildEnvironment.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalBuildProgressListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildProgressListener.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalCancellableConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCancellableConnection.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalCancellationToken> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCancellationToken.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalFailure.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalInvalidatableVirtualFileSystemConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalInvalidatableVirtualFileSystemConnection.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalLaunchable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalLaunchable.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalParameterAcceptingConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalParameterAcceptingConnection.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalPhasedAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalPhasedAction.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalPhasedActionConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalPhasedActionConnection.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalProtocolInterface> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalProtocolInterface.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalStopWhenIdleConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalStopWhenIdleConnection.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalTestAssertionFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestAssertionFailure.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalTestFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestFailure.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalTestFrameworkFailure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestFrameworkFailure.java:0)
+Class <org.gradle.tooling.internal.protocol.InternalUnsupportedModelException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalUnsupportedModelException.java:0)
+Class <org.gradle.tooling.internal.protocol.ModelIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelIdentifier.java:0)
+Class <org.gradle.tooling.internal.protocol.PhasedActionResult$Phase> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedActionResult.java:0)
+Class <org.gradle.tooling.internal.protocol.PhasedActionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedActionResult.java:0)
+Class <org.gradle.tooling.internal.protocol.PhasedActionResultListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedActionResultListener.java:0)
+Class <org.gradle.tooling.internal.protocol.ProgressListenerVersion1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressListenerVersion1.java:0)
+Class <org.gradle.tooling.internal.protocol.ProjectVersion3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectVersion3.java:0)
+Class <org.gradle.tooling.internal.protocol.ResultHandlerVersion1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ResultHandlerVersion1.java:0)
+Class <org.gradle.tooling.internal.protocol.ShutdownParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShutdownParameters.java:0)
+Class <org.gradle.tooling.internal.protocol.StoppableConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StoppableConnection.java:0)
+Class <org.gradle.tooling.internal.protocol.cpp.InternalCppApplication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCppApplication.java:0)
+Class <org.gradle.tooling.internal.protocol.cpp.InternalCppExecutable> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCppExecutable.java:0)
+Class <org.gradle.tooling.internal.protocol.cpp.InternalCppLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCppLibrary.java:0)
+Class <org.gradle.tooling.internal.protocol.cpp.InternalCppSharedLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCppSharedLibrary.java:0)
+Class <org.gradle.tooling.internal.protocol.cpp.InternalCppStaticLibrary> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCppStaticLibrary.java:0)
+Class <org.gradle.tooling.internal.protocol.cpp.InternalCppTestSuite> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCppTestSuite.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalBinaryPluginIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBinaryPluginIdentifier.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalBuildPhaseDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalBuildPhaseDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalFailureResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalFileDownloadDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalFileDownloadDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalFileDownloadResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalFileDownloadResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalIncrementalTaskResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalIncrementalTaskResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalJavaCompileTaskOperationResult$InternalAnnotationProcessorResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalJavaCompileTaskOperationResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalJavaCompileTaskOperationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalJavaCompileTaskOperationResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalJvmTestDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalJvmTestDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalNotFoundFileDownloadResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalNotFoundFileDownloadResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalOperationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalOperationDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalOperationFinishedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalOperationFinishedProgressEvent.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalOperationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalOperationResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalOperationStartedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalOperationStartedProgressEvent.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalPluginIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalPluginIdentifier.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalProgressEvent.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalProjectConfigurationDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalProjectConfigurationDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalProjectConfigurationResult$InternalPluginApplicationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalProjectConfigurationResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalProjectConfigurationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalProjectConfigurationResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalScriptPluginIdentifier> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalScriptPluginIdentifier.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalStatusEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalStatusEvent.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalSuccessResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTaskCachedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTaskCachedResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTaskDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTaskDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTaskFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTaskFailureResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTaskResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTaskResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTaskSkippedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTaskSkippedResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTaskSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTaskSuccessResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTaskWithExtraInfoDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTaskWithExtraInfoDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestFailureResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestFailureResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestFinishedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestFinishedProgressEvent.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestOutputDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestOutputDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestOutputEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestOutputEvent.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestOutputResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestOutputResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestProgressEvent.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestSkippedResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestSkippedResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestStartedProgressEvent> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestStartedProgressEvent.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTestSuccessResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestSuccessResult.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalTransformDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTransformDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.events.InternalWorkItemDescriptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalWorkItemDescriptor.java:0)
+Class <org.gradle.tooling.internal.protocol.exceptions.InternalUnsupportedBuildArgumentException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalUnsupportedBuildArgumentException.java:0)
+Class <org.gradle.tooling.internal.protocol.test.InternalDebugOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalDebugOptions.java:0)
+Class <org.gradle.tooling.internal.protocol.test.InternalJvmTestRequest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalJvmTestRequest.java:0)
+Class <org.gradle.tooling.internal.protocol.test.InternalTaskSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTaskSpec.java:0)
+Class <org.gradle.tooling.internal.protocol.test.InternalTestExecutionConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestExecutionConnection.java:0)
+Class <org.gradle.tooling.internal.protocol.test.InternalTestExecutionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestExecutionException.java:0)
+Class <org.gradle.tooling.internal.protocol.test.InternalTestExecutionRequest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestExecutionRequest.java:0)
+Class <org.gradle.tooling.internal.protocol.test.InternalTestSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalTestSpec.java:0)
+Class <org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter$ActionImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionLifecycleBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.BuildSessionLifecycleBuildActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildSessionLifecycleBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.ConnectionOperationParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionOperationParameters.java:0)
+Class <org.gradle.tooling.internal.provider.ConnectionScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConnectionScopeServices.java:0)
+Class <org.gradle.tooling.internal.provider.DaemonBuildActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.DefaultConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConnection.java:0)
+Class <org.gradle.tooling.internal.provider.DefaultConnectionMetaData> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultConnectionMetaData.java:0)
+Class <org.gradle.tooling.internal.provider.ExecuteBuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.FailsafePhasedActionResultListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailsafePhasedActionResultListener.java:0)
+Class <org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileSystemWatchingBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileSystemWatchingBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileSystemWatchingBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.InternalCancellationTokenAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (InternalCancellationTokenAdapter.java:0)
+Class <org.gradle.tooling.internal.provider.LauncherServices$ToolingBuildSessionScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LauncherServices.java:0)
+Class <org.gradle.tooling.internal.provider.LauncherServices$ToolingBuildTreeScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LauncherServices.java:0)
+Class <org.gradle.tooling.internal.provider.LauncherServices$ToolingGlobalScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LauncherServices.java:0)
+Class <org.gradle.tooling.internal.provider.LauncherServices$ToolingGradleUserHomeScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LauncherServices.java:0)
+Class <org.gradle.tooling.internal.provider.LauncherServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LauncherServices.java:0)
+Class <org.gradle.tooling.internal.provider.LoggingBridgingBuildActionExecuter$OutputEventListenerAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingBridgingBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.LoggingBridgingBuildActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LoggingBridgingBuildActionExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.PhasedActionEventConsumer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedActionEventConsumer.java:0)
+Class <org.gradle.tooling.internal.provider.PhasedActionEventConsumer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedActionEventConsumer.java:0)
+Class <org.gradle.tooling.internal.provider.PhasedBuildActionResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PhasedBuildActionResult.java:0)
+Class <org.gradle.tooling.internal.provider.ProviderConnection$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderConnection.java:0)
+Class <org.gradle.tooling.internal.provider.ProviderConnection$BuildProgressListenerInvokingBuildEventConsumer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderConnection.java:0)
+Class <org.gradle.tooling.internal.provider.ProviderConnection$Parameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderConnection.java:0)
+Class <org.gradle.tooling.internal.provider.ProviderConnection$ProgressListenerConfiguration$SynchronizedConsumer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderConnection.java:0)
+Class <org.gradle.tooling.internal.provider.ProviderConnection$ProgressListenerConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderConnection.java:0)
+Class <org.gradle.tooling.internal.provider.ProviderConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderConnection.java:0)
+Class <org.gradle.tooling.internal.provider.ProviderStartParameterConverter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderStartParameterConverter.java:0)
+Class <org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SessionFailureReportingActionExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.SetupLoggingActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SetupLoggingActionExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.ShutdownCoordinator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ShutdownCoordinator.java:0)
+Class <org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StartParamsValidatingActionExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.StdInSwapExecuter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StdInSwapExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.StdInSwapExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StdInSwapExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.SubscribableBuildActionExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SubscribableBuildActionExecutor.java:0)
+Class <org.gradle.tooling.internal.provider.SystemPropertySetterExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPropertySetterExecuter.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$BuildEventSubscriptionsSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$BuildModelActionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$ClientProvidedBuildActionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$ClientProvidedPhasedActionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$ExecuteBuildActionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$InternalTaskSpecSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$NullableFileSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$StartParameterSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$TestExecutionRequestActionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$TestExecutionRequestPayload> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer$ValueSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildActionSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildActionSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.action.BuildModelAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildModelAction.java:0)
+Class <org.gradle.tooling.internal.provider.action.ClientProvidedBuildAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientProvidedBuildAction.java:0)
+Class <org.gradle.tooling.internal.provider.action.ClientProvidedPhasedAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientProvidedPhasedAction.java:0)
+Class <org.gradle.tooling.internal.provider.action.ExecuteBuildAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteBuildAction.java:0)
+Class <org.gradle.tooling.internal.provider.action.SubscribableBuildAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SubscribableBuildAction.java:0)
+Class <org.gradle.tooling.internal.provider.action.TestExecutionRequestAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionRequestAction.java:0)
+Class <org.gradle.tooling.internal.provider.connection.BuildLogLevelMixIn> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildLogLevelMixIn.java:0)
+Class <org.gradle.tooling.internal.provider.connection.ProviderBuildResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderBuildResult.java:0)
+Class <org.gradle.tooling.internal.provider.connection.ProviderConnectionParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderConnectionParameters.java:0)
+Class <org.gradle.tooling.internal.provider.connection.ProviderOperationParameters> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderOperationParameters.java:0)
+Class <org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClientProvidedBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionResults> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClientProvidedBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ClientAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClientProvidedBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClientProvidedBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.BuildControllerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildControllerFactory.java:0)
+Class <org.gradle.tooling.internal.provider.runner.BuildModelActionRunner$ModelCreateAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildModelActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.BuildModelActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildModelActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.BuildOperationMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.runner.BuildOperationTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.BuildPhaseOperationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildPhaseOperationListener.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientBuildEventGenerator$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientBuildEventGenerator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientBuildEventGenerator$Disabled> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientBuildEventGenerator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientBuildEventGenerator$Enabled> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientBuildEventGenerator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientBuildEventGenerator$EnabledOperation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientBuildEventGenerator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientBuildEventGenerator$Mapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientBuildEventGenerator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientBuildEventGenerator$Operation> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientBuildEventGenerator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientBuildEventGenerator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientBuildEventGenerator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientForwardingBuildOperationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientForwardingBuildOperationListener.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientForwardingTestOutputOperationListener$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientForwardingTestOutputOperationListener.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientForwardingTestOutputOperationListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientForwardingTestOutputOperationListener.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientProvidedBuildActionRunner$ClientActionImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientProvidedBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientProvidedBuildActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientProvidedBuildActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner$ClientActionImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientProvidedPhasedActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientProvidedPhasedActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.DefaultBuildController$NestedAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildController.java:0)
+Class <org.gradle.tooling.internal.provider.runner.DefaultBuildController> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultBuildController.java:0)
+Class <org.gradle.tooling.internal.provider.runner.FileDownloadOperationMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FileDownloadOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.runner.OperationDependenciesResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperationDependenciesResolver.java:0)
+Class <org.gradle.tooling.internal.provider.runner.OperationDependencyLookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OperationDependencyLookup.java:0)
+Class <org.gradle.tooling.internal.provider.runner.PluginApplicationTracker$PluginApplication> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginApplicationTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.PluginApplicationTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PluginApplicationTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ProgressEventConsumer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProgressEventConsumer.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ProjectConfigurationOperationMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectConfigurationOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ProjectConfigurationTracker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectConfigurationTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ProjectConfigurationTracker$PluginApplicationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectConfigurationTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ProjectConfigurationTracker$ProjectConfigurationResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectConfigurationTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ProjectConfigurationTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProjectConfigurationTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TaskOperationMapper$PostProcessors> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TaskOperationMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TaskOriginTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TaskOriginTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TestExecutionBuildConfigurationAction$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionBuildConfigurationAction.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TestExecutionBuildConfigurationAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionBuildConfigurationAction.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TestExecutionRequestActionRunner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionRequestActionRunner.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TestExecutionResultEvaluator$FailedTest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionResultEvaluator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TestExecutionResultEvaluator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestExecutionResultEvaluator.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TestOperationMapper$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TestOperationMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TestTaskExecutionTracker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TestTaskExecutionTracker.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ToolingApiBuildEventListenerFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingApiBuildEventListenerFactory.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ToolingApiBuildEventListenerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingApiBuildEventListenerFactory.java:0)
+Class <org.gradle.tooling.internal.provider.runner.ToolingBuilderServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingBuilderServices.java:0)
+Class <org.gradle.tooling.internal.provider.runner.TransformOperationMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransformOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.runner.WorkItemOperationMapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkItemOperationMapper.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClassLoaderCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassLoaderCache.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClassLoaderDetails> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassLoaderDetails.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClasspathInferer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClasspathInferer.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientOwnedClassLoaderSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientOwnedClassLoaderSpec.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderFactory$MixInClassLoader$AnnotationDetector$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderFactory$MixInClassLoader$AnnotationDetector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderFactory$MixInClassLoader$TransformingAdapter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderFactory$MixInClassLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderRegistry$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderRegistry$LocalClassLoaderMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ClientSidePayloadClassLoaderRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClientSidePayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.DaemonSidePayloadClassLoaderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonSidePayloadClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.DefaultPayloadClassLoaderRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.DefaultPayloadClassLoaderRegistry$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.DefaultPayloadClassLoaderRegistry$ClassLoaderSpecVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.DefaultPayloadClassLoaderRegistry$ClassLoaderToDetailsTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.DefaultPayloadClassLoaderRegistry$DetailsToClassLoaderTransformer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.DefaultPayloadClassLoaderRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultPayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.DeserializeMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DeserializeMap.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.ModelClassLoaderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ModelClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.PayloadClassLoaderFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PayloadClassLoaderFactory.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.PayloadClassLoaderRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PayloadClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.PayloadSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PayloadSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.PayloadSerializerObjectInputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PayloadSerializerObjectInputStream.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.PayloadSerializerObjectOutputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PayloadSerializerObjectOutputStream.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.SerializeMap> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SerializeMap.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.SerializedPayload> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SerializedPayload.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.SerializedPayloadSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SerializedPayloadSerializer.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.WellKnownClassLoaderRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WellKnownClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.WellKnownClassLoaderRegistry$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WellKnownClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.WellKnownClassLoaderRegistry$KnownClassLoaderSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WellKnownClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.serialization.WellKnownClassLoaderRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WellKnownClassLoaderRegistry.java:0)
+Class <org.gradle.tooling.internal.provider.test.ProviderInternalJvmTestRequest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderInternalJvmTestRequest.java:0)
+Class <org.gradle.tooling.internal.provider.test.ProviderInternalTestExecutionRequest> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProviderInternalTestExecutionRequest.java:0)
+Class <org.gradle.tooling.model.internal.Exceptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Exceptions.java:0)
+Class <org.gradle.tooling.model.internal.ImmutableDomainObjectSet> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ImmutableDomainObjectSet.java:0)
+Class <org.gradle.tooling.provider.model.internal.BuildScopeModelBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScopeModelBuilder.java:0)
+Class <org.gradle.tooling.provider.model.internal.BuildScopeToolingModelBuilderRegistryAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildScopeToolingModelBuilderRegistryAction.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildScopedBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuilderWithNoParameter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuilderWithParameter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$DelegatingBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$LenientToolingModelBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$LockSingleProjectBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$RegistrationImpl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$UserCodeAssigningBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$VoidToolingModelBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultToolingModelBuilderRegistry.java:0)
+Class <org.gradle.tooling.provider.model.internal.LegacyConsumerInterface> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LegacyConsumerInterface.java:0)
+Class <org.gradle.tooling.provider.model.internal.QueryToolingModelBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (QueryToolingModelBuildOperationType.java:0)
+Class <org.gradle.tooling.provider.model.internal.QueryToolingModelBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (QueryToolingModelBuildOperationType.java:0)
+Class <org.gradle.tooling.provider.model.internal.QueryToolingModelBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (QueryToolingModelBuildOperationType.java:0)
+Class <org.gradle.tooling.provider.model.internal.ToolingModelBuilderLookup$Builder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelBuilderLookup.java:0)
+Class <org.gradle.tooling.provider.model.internal.ToolingModelBuilderLookup$Registration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelBuilderLookup.java:0)
+Class <org.gradle.tooling.provider.model.internal.ToolingModelBuilderLookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelBuilderLookup.java:0)
+Class <org.gradle.tooling.provider.model.internal.ToolingModelScope> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToolingModelScope.java:0)
+Class <org.gradle.util.internal.AlwaysTrue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AlwaysTrue.java:0)
+Class <org.gradle.util.internal.AntUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AntUtil.java:0)
+Class <org.gradle.util.internal.ArgumentsSplitter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ArgumentsSplitter.java:0)
+Class <org.gradle.util.internal.BuildCommencedTimeProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildCommencedTimeProvider.java:0)
+Class <org.gradle.util.internal.BulkReadInputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BulkReadInputStream.java:0)
+Class <org.gradle.util.internal.ClosureBackedAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClosureBackedAction.java:0)
+Class <org.gradle.util.internal.CollectionUtils$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionUtils.java:0)
+Class <org.gradle.util.internal.CollectionUtils$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionUtils.java:0)
+Class <org.gradle.util.internal.CollectionUtils$InjectionStep> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionUtils.java:0)
+Class <org.gradle.util.internal.CollectionUtils$SetDiff> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionUtils.java:0)
+Class <org.gradle.util.internal.CollectionUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (CollectionUtils.java:0)
+Class <org.gradle.util.internal.ConfigureUtil$IncompleteInputException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureUtil.java:0)
+Class <org.gradle.util.internal.ConfigureUtil$WrappedConfigureAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureUtil.java:0)
+Class <org.gradle.util.internal.ConfigureUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ConfigureUtil.java:0)
+Class <org.gradle.util.internal.DefaultGradleVersion$Stage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleVersion.java:0)
+Class <org.gradle.util.internal.DefaultGradleVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGradleVersion.java:0)
+Class <org.gradle.util.internal.DeferredUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DeferredUtil.java:0)
+Class <org.gradle.util.internal.DisconnectableInputStream$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DisconnectableInputStream.java:0)
+Class <org.gradle.util.internal.DisconnectableInputStream$ThreadExecuter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DisconnectableInputStream.java:0)
+Class <org.gradle.util.internal.DisconnectableInputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DisconnectableInputStream.java:0)
+Class <org.gradle.util.internal.DistributionLocator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DistributionLocator.java:0)
+Class <org.gradle.util.internal.EncryptionAlgorithm$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EncryptionAlgorithm.java:0)
+Class <org.gradle.util.internal.EncryptionAlgorithm$EncryptionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EncryptionAlgorithm.java:0)
+Class <org.gradle.util.internal.EncryptionAlgorithm$IVCollector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EncryptionAlgorithm.java:0)
+Class <org.gradle.util.internal.EncryptionAlgorithm$IVLoader> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EncryptionAlgorithm.java:0)
+Class <org.gradle.util.internal.EncryptionAlgorithm$Session> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EncryptionAlgorithm.java:0)
+Class <org.gradle.util.internal.EncryptionAlgorithm> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (EncryptionAlgorithm.java:0)
+Class <org.gradle.util.internal.FailsWithMessage> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailsWithMessage.java:0)
+Class <org.gradle.util.internal.FailsWithMessageExtension$FailsWithMessageInterceptor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailsWithMessageExtension.java:0)
+Class <org.gradle.util.internal.FailsWithMessageExtension> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FailsWithMessageExtension.java:0)
+Class <org.gradle.util.internal.GFileUtils$TailReadingException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GFileUtils.java:0)
+Class <org.gradle.util.internal.GFileUtils> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GFileUtils.java:0)
+Class <org.gradle.util.internal.GUtil$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GUtil.java:0)
+Class <org.gradle.util.internal.GUtil$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GUtil.java:0)
+Class <org.gradle.util.internal.GUtil$3> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GUtil.java:0)
+Class <org.gradle.util.internal.GUtil$4> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GUtil.java:0)
+Class <org.gradle.util.internal.GUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GUtil.java:0)
+Class <org.gradle.util.internal.GroovyDependencyUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GroovyDependencyUtil.java:0)
+Class <org.gradle.util.internal.IncubationLogger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IncubationLogger.java:0)
+Class <org.gradle.util.internal.JarUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (JarUtil.java:0)
+Class <org.gradle.util.internal.LimitedDescription> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (LimitedDescription.java:0)
+Class <org.gradle.util.internal.MavenUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (MavenUtil.java:0)
+Class <org.gradle.util.internal.NameMatcher> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NameMatcher.java:0)
+Class <org.gradle.util.internal.NameValidator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NameValidator.java:0)
+Class <org.gradle.util.internal.RedirectStdOutAndErr$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RedirectStdOutAndErr.java:0)
+Class <org.gradle.util.internal.RedirectStdOutAndErr$RedirectingOutputStream> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RedirectStdOutAndErr.java:0)
+Class <org.gradle.util.internal.RedirectStdOutAndErr> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RedirectStdOutAndErr.java:0)
+Class <org.gradle.util.internal.RelativePathUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (RelativePathUtil.java:0)
+Class <org.gradle.util.internal.Resources> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Resources.java:0)
+Class <org.gradle.util.internal.SimpleMapInterner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleMapInterner.java:0)
+Class <org.gradle.util.internal.StdinSwapper$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StdinSwapper.java:0)
+Class <org.gradle.util.internal.StdinSwapper$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StdinSwapper.java:0)
+Class <org.gradle.util.internal.StdinSwapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (StdinSwapper.java:0)
+Class <org.gradle.util.internal.SupportedEncryptionAlgorithm$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SupportedEncryptionAlgorithm.java:0)
+Class <org.gradle.util.internal.SupportedEncryptionAlgorithm> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SupportedEncryptionAlgorithm.java:0)
+Class <org.gradle.util.internal.Swapper> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Swapper.java:0)
+Class <org.gradle.util.internal.TextUtil$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextUtil.java:0)
+Class <org.gradle.util.internal.TextUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TextUtil.java:0)
+Class <org.gradle.util.internal.ToBeImplemented> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ToBeImplemented.java:0)
+Class <org.gradle.util.internal.TreeVisitor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TreeVisitor.java:0)
+Class <org.gradle.util.internal.VersionNumber$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionNumber.java:0)
+Class <org.gradle.util.internal.VersionNumber$AbstractScheme$Scanner> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionNumber.java:0)
+Class <org.gradle.util.internal.VersionNumber$AbstractScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionNumber.java:0)
+Class <org.gradle.util.internal.VersionNumber$DefaultScheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionNumber.java:0)
+Class <org.gradle.util.internal.VersionNumber$Scheme> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionNumber.java:0)
+Class <org.gradle.util.internal.VersionNumber$SchemeWithPatchVersion> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionNumber.java:0)
+Class <org.gradle.util.internal.VersionNumber> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionNumber.java:0)
+Class <org.gradle.util.internal.WrapUtil> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WrapUtil.java:0)
+Class <org.gradle.util.internal.ZipSlip> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ZipSlip.java:0)
+Class <org.gradle.vcs.git.internal.DefaultGitVersionControlSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultGitVersionControlSpec.java:0)
+Class <org.gradle.vcs.git.internal.GitVersionControlSystem$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GitVersionControlSystem.java:0)
+Class <org.gradle.vcs.git.internal.GitVersionControlSystem$DefaultTransportConfigCallback$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GitVersionControlSystem.java:0)
+Class <org.gradle.vcs.git.internal.GitVersionControlSystem$DefaultTransportConfigCallback> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GitVersionControlSystem.java:0)
+Class <org.gradle.vcs.git.internal.GitVersionControlSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GitVersionControlSystem.java:0)
+Class <org.gradle.vcs.git.internal.GitVersionRef> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GitVersionRef.java:0)
+Class <org.gradle.vcs.internal.DefaultSourceControl> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultSourceControl.java:0)
+Class <org.gradle.vcs.internal.DefaultVcsMapping> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVcsMapping.java:0)
+Class <org.gradle.vcs.internal.DefaultVcsMappingFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVcsMappingFactory.java:0)
+Class <org.gradle.vcs.internal.DefaultVcsMappings$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVcsMappings.java:0)
+Class <org.gradle.vcs.internal.DefaultVcsMappings$DslAccessRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVcsMappings.java:0)
+Class <org.gradle.vcs.internal.DefaultVcsMappings$ModuleFilteredRule> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVcsMappings.java:0)
+Class <org.gradle.vcs.internal.DefaultVcsMappings> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVcsMappings.java:0)
+Class <org.gradle.vcs.internal.DefaultVcsMappingsStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVcsMappingsStore.java:0)
+Class <org.gradle.vcs.internal.DefaultVersionControlRepository> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionControlRepository.java:0)
+Class <org.gradle.vcs.internal.VcsDirectoryLayout> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsDirectoryLayout.java:0)
+Class <org.gradle.vcs.internal.VcsMappingFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsMappingFactory.java:0)
+Class <org.gradle.vcs.internal.VcsMappingInternal> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsMappingInternal.java:0)
+Class <org.gradle.vcs.internal.VcsMappingsStore$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsMappingsStore.java:0)
+Class <org.gradle.vcs.internal.VcsMappingsStore> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsMappingsStore.java:0)
+Class <org.gradle.vcs.internal.VcsResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsResolver.java:0)
+Class <org.gradle.vcs.internal.VersionControlRepositoryConnection> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlRepositoryConnection.java:0)
+Class <org.gradle.vcs.internal.VersionControlRepositoryConnectionFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlRepositoryConnectionFactory.java:0)
+Class <org.gradle.vcs.internal.VersionControlSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlSpecFactory.java:0)
+Class <org.gradle.vcs.internal.VersionControlSystem> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlSystem.java:0)
+Class <org.gradle.vcs.internal.VersionRef> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionRef.java:0)
+Class <org.gradle.vcs.internal.resolver.DefaultVcsVersionWorkingDirResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVcsVersionWorkingDirResolver.java:0)
+Class <org.gradle.vcs.internal.resolver.OfflineVcsVersionWorkingDirResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OfflineVcsVersionWorkingDirResolver.java:0)
+Class <org.gradle.vcs.internal.resolver.OncePerBuildInvocationVcsVersionWorkingDirResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OncePerBuildInvocationVcsVersionWorkingDirResolver.java:0)
+Class <org.gradle.vcs.internal.resolver.OncePerBuildInvocationVcsVersionWorkingDirResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (OncePerBuildInvocationVcsVersionWorkingDirResolver.java:0)
+Class <org.gradle.vcs.internal.resolver.PersistentVcsMetadataCache$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentVcsMetadataCache.java:0)
+Class <org.gradle.vcs.internal.resolver.PersistentVcsMetadataCache$VersionRefSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentVcsMetadataCache.java:0)
+Class <org.gradle.vcs.internal.resolver.PersistentVcsMetadataCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PersistentVcsMetadataCache.java:0)
+Class <org.gradle.vcs.internal.resolver.VcsDependencyResolver$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsDependencyResolver.java:0)
+Class <org.gradle.vcs.internal.resolver.VcsDependencyResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsDependencyResolver.java:0)
+Class <org.gradle.vcs.internal.resolver.VcsVersionSelectionCache> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsVersionSelectionCache.java:0)
+Class <org.gradle.vcs.internal.resolver.VcsVersionWorkingDirResolver> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VcsVersionWorkingDirResolver.java:0)
+Class <org.gradle.vcs.internal.services.DefaultVersionControlRepositoryFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionControlRepositoryFactory.java:0)
+Class <org.gradle.vcs.internal.services.DefaultVersionControlRepositoryFactory$LockingVersionControlRepository$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionControlRepositoryFactory.java:0)
+Class <org.gradle.vcs.internal.services.DefaultVersionControlRepositoryFactory$LockingVersionControlRepository> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionControlRepositoryFactory.java:0)
+Class <org.gradle.vcs.internal.services.DefaultVersionControlRepositoryFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionControlRepositoryFactory.java:0)
+Class <org.gradle.vcs.internal.services.DefaultVersionControlSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultVersionControlSpecFactory.java:0)
+Class <org.gradle.vcs.internal.services.VersionControlServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlServices.java:0)
+Class <org.gradle.vcs.internal.services.VersionControlServices$VcsResolverFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlServices.java:0)
+Class <org.gradle.vcs.internal.services.VersionControlServices$VersionControlBuildServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlServices.java:0)
+Class <org.gradle.vcs.internal.services.VersionControlServices$VersionControlBuildSessionServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlServices.java:0)
+Class <org.gradle.vcs.internal.services.VersionControlServices$VersionControlBuildTreeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlServices.java:0)
+Class <org.gradle.vcs.internal.services.VersionControlServices$VersionControlSettingsServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlServices.java:0)
+Class <org.gradle.vcs.internal.services.VersionControlServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VersionControlServices.java:0)
+Class <org.gradle.vcs.internal.spec.AbstractVersionControlSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractVersionControlSpec.java:0)
+Class <org.gradle.workers.internal.AbstractClassLoaderWorker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassLoaderWorker.java:0)
+Class <org.gradle.workers.internal.AbstractClassLoaderWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractClassLoaderWorker.java:0)
+Class <org.gradle.workers.internal.AbstractWorker$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWorker.java:0)
+Class <org.gradle.workers.internal.AbstractWorker$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWorker.java:0)
+Class <org.gradle.workers.internal.AbstractWorker$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWorker.java:0)
+Class <org.gradle.workers.internal.AbstractWorker$Work> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWorker.java:0)
+Class <org.gradle.workers.internal.AbstractWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWorker.java:0)
+Class <org.gradle.workers.internal.AbstractWorkerRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (AbstractWorkerRequirement.java:0)
+Class <org.gradle.workers.internal.ActionExecutionSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ActionExecutionSpecFactory.java:0)
+Class <org.gradle.workers.internal.BuildOperationAwareWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BuildOperationAwareWorker.java:0)
+Class <org.gradle.workers.internal.ClassLoaderStructure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassLoaderStructure.java:0)
+Class <org.gradle.workers.internal.ClassLoaderStructureProvider> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ClassLoaderStructureProvider.java:0)
+Class <org.gradle.workers.internal.DaemonForkOptions> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonForkOptions.java:0)
+Class <org.gradle.workers.internal.DaemonForkOptionsBuilder> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonForkOptionsBuilder.java:0)
+Class <org.gradle.workers.internal.DefaultActionExecutionSpecFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultActionExecutionSpecFactory.java:0)
+Class <org.gradle.workers.internal.DefaultClassLoaderWorkerSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultClassLoaderWorkerSpec.java:0)
+Class <org.gradle.workers.internal.DefaultProcessWorkerSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultProcessWorkerSpec.java:0)
+Class <org.gradle.workers.internal.DefaultWorkResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkResult.java:0)
+Class <org.gradle.workers.internal.DefaultWorkerExecutor$DefaultWorkQueue> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerExecutor.java:0)
+Class <org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerExecutor.java:0)
+Class <org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerExecutor.java:0)
+Class <org.gradle.workers.internal.DefaultWorkerExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerExecutor.java:0)
+Class <org.gradle.workers.internal.DefaultWorkerServer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerServer.java:0)
+Class <org.gradle.workers.internal.DefaultWorkerSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultWorkerSpec.java:0)
+Class <org.gradle.workers.internal.ExecuteWorkItemBuildOperationType$Details> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteWorkItemBuildOperationType.java:0)
+Class <org.gradle.workers.internal.ExecuteWorkItemBuildOperationType$Result> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteWorkItemBuildOperationType.java:0)
+Class <org.gradle.workers.internal.ExecuteWorkItemBuildOperationType> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExecuteWorkItemBuildOperationType.java:0)
+Class <org.gradle.workers.internal.FilteringClassLoaderSpecSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FilteringClassLoaderSpecSerializer.java:0)
+Class <org.gradle.workers.internal.FixedClassLoaderWorkerRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FixedClassLoaderWorkerRequirement.java:0)
+Class <org.gradle.workers.internal.FlatClassLoaderStructure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FlatClassLoaderStructure.java:0)
+Class <org.gradle.workers.internal.FlatClassLoaderWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (FlatClassLoaderWorker.java:0)
+Class <org.gradle.workers.internal.ForkedWorkerRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ForkedWorkerRequirement.java:0)
+Class <org.gradle.workers.internal.HierarchicalClassLoaderStructure> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HierarchicalClassLoaderStructure.java:0)
+Class <org.gradle.workers.internal.HierarchicalClassLoaderStructureSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HierarchicalClassLoaderStructureSerializer.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$AbstractIsolatedMapSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$AttributeDefinitionSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$BooleanValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$FileValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IntegerValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatableSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedArraySerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedEnumValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedImmutableManagedValueSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedJavaSerializedValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedListSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedManagedValueSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedMapSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedPropertiesSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$IsolatedSetSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$LongValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$NullValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$ShortValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry$StringValueSnapshotSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatableSerializerRegistry> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatableSerializerRegistry.java:0)
+Class <org.gradle.workers.internal.IsolatedClassLoaderWorkerRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatedClassLoaderWorkerRequirement.java:0)
+Class <org.gradle.workers.internal.IsolatedClassloaderWorker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatedClassloaderWorker.java:0)
+Class <org.gradle.workers.internal.IsolatedClassloaderWorkerFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatedClassloaderWorkerFactory.java:0)
+Class <org.gradle.workers.internal.IsolatedClassloaderWorkerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatedClassloaderWorkerFactory.java:0)
+Class <org.gradle.workers.internal.IsolatedParametersActionExecutionSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IsolatedParametersActionExecutionSpec.java:0)
+Class <org.gradle.workers.internal.KeepAliveMode> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (KeepAliveMode.java:0)
+Class <org.gradle.workers.internal.NoIsolationWorkerFactory$1$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoIsolationWorkerFactory.java:0)
+Class <org.gradle.workers.internal.NoIsolationWorkerFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoIsolationWorkerFactory.java:0)
+Class <org.gradle.workers.internal.NoIsolationWorkerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (NoIsolationWorkerFactory.java:0)
+Class <org.gradle.workers.internal.ProvidesWorkResult> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ProvidesWorkResult.java:0)
+Class <org.gradle.workers.internal.SimpleActionExecutionSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SimpleActionExecutionSpec.java:0)
+Class <org.gradle.workers.internal.TransportableActionExecutionSpec> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransportableActionExecutionSpec.java:0)
+Class <org.gradle.workers.internal.TransportableActionExecutionSpecSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (TransportableActionExecutionSpecSerializer.java:0)
+Class <org.gradle.workers.internal.VisitableURLClassLoaderSpecSerializer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (VisitableURLClassLoaderSpecSerializer.java:0)
+Class <org.gradle.workers.internal.WorkSerializationException> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkSerializationException.java:0)
+Class <org.gradle.workers.internal.Worker> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Worker.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonClient> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonClient.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonClientsManager$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonClientsManager.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonClientsManager$LogLevelChangeEventListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonClientsManager.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonClientsManager$StopSessionScopedWorkers> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonClientsManager.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonClientsManager$WorkerProcessCleanupAction> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonClientsManager.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonClientsManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonClientsManager.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonExpiration$SimpleMemoryExpirationSelector> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonExpiration.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonExpiration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonExpiration.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonFactory$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonFactory.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonFactory.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonServer$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonServer.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonServer$WorkerDaemonServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonServer.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonServer$WorkerDaemonServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonServer.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonServer$WorkerProjectServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonServer.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonServer$WorkerProjectServices$2> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonServer.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonServer$WorkerProjectServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonServer.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonServer> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonServer.java:0)
+Class <org.gradle.workers.internal.WorkerDaemonStarter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerDaemonStarter.java:0)
+Class <org.gradle.workers.internal.WorkerExecutionQueueFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerExecutionQueueFactory.java:0)
+Class <org.gradle.workers.internal.WorkerFactory> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerFactory.java:0)
+Class <org.gradle.workers.internal.WorkerRequirement> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkerRequirement.java:0)
+Class <org.gradle.workers.internal.WorkersServices$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkersServices.java:0)
+Class <org.gradle.workers.internal.WorkersServices$BuildSessionScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkersServices.java:0)
+Class <org.gradle.workers.internal.WorkersServices$GradleUserHomeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkersServices.java:0)
+Class <org.gradle.workers.internal.WorkersServices$ProjectScopeServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkersServices.java:0)
+Class <org.gradle.workers.internal.WorkersServices> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WorkersServices.java:0)
+Class <org.gradle.wrapper.BootstrapMainStarter$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BootstrapMainStarter.java:0)
+Class <org.gradle.wrapper.BootstrapMainStarter> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (BootstrapMainStarter.java:0)
+Class <org.gradle.wrapper.Download$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Download.java:0)
+Class <org.gradle.wrapper.Download$DefaultDownloadProgressListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Download.java:0)
+Class <org.gradle.wrapper.Download$ProxyAuthenticator> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Download.java:0)
+Class <org.gradle.wrapper.Download> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Download.java:0)
+Class <org.gradle.wrapper.DownloadProgressListener> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DownloadProgressListener.java:0)
+Class <org.gradle.wrapper.ExclusiveFileAccessManager> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (ExclusiveFileAccessManager.java:0)
+Class <org.gradle.wrapper.GradleUserHomeLookup> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleUserHomeLookup.java:0)
+Class <org.gradle.wrapper.GradleWrapperMain> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GradleWrapperMain.java:0)
+Class <org.gradle.wrapper.IDownload> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (IDownload.java:0)
+Class <org.gradle.wrapper.Install$1> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Install.java:0)
+Class <org.gradle.wrapper.Install$InstallCheck> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Install.java:0)
+Class <org.gradle.wrapper.Install> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Install.java:0)
+Class <org.gradle.wrapper.Logger> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (Logger.java:0)
+Class <org.gradle.wrapper.PathAssembler$LocalDistribution> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PathAssembler.java:0)
+Class <org.gradle.wrapper.PathAssembler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (PathAssembler.java:0)
+Class <org.gradle.wrapper.SystemPropertiesHandler> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (SystemPropertiesHandler.java:0)
+Class <org.gradle.wrapper.WrapperConfiguration> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WrapperConfiguration.java:0)
+Class <org.gradle.wrapper.WrapperExecutor> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (WrapperExecutor.java:0)
diff --git a/subprojects/architecture-test/src/changes/archunit_store/provider-task-file-collection.txt b/subprojects/architecture-test/src/changes/archunit_store/provider-task-file-collection.txt
index 422cff7..f67450f 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/provider-task-file-collection.txt
+++ b/subprojects/architecture-test/src/changes/archunit_store/provider-task-file-collection.txt
@@ -1,43 +1,43 @@
-Method <org.gradle.api.plugins.antlr.AntlrTask.getAntlrClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (AntlrTask.java:181)
-Method <org.gradle.api.plugins.antlr.AntlrTask.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (AntlrTask.java:298)
-Method <org.gradle.api.plugins.quality.Checkstyle.getCheckstyleClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Checkstyle.java:241)
-Method <org.gradle.api.plugins.quality.Checkstyle.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Checkstyle.java:256)
-Method <org.gradle.api.plugins.quality.Checkstyle.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Checkstyle.java:233)
-Method <org.gradle.api.plugins.quality.CodeNarc.getCodenarcClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CodeNarc.java:131)
-Method <org.gradle.api.plugins.quality.CodeNarc.getCompilationClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CodeNarc.java:148)
-Method <org.gradle.api.plugins.quality.CodeNarc.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CodeNarc.java:84)
-Method <org.gradle.api.plugins.quality.Pmd.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Pmd.java:432)
-Method <org.gradle.api.plugins.quality.Pmd.getPmdClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Pmd.java:234)
-Method <org.gradle.api.plugins.quality.Pmd.getRuleSetFiles()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Pmd.java:326)
-Method <org.gradle.api.plugins.quality.Pmd.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Pmd.java:226)
-Method <org.gradle.api.tasks.JavaExec.getBootstrapClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaExec.java:269)
-Method <org.gradle.api.tasks.JavaExec.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaExec.java:500)
-Method <org.gradle.api.tasks.SourceTask.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (SourceTask.java:72)
-Method <org.gradle.api.tasks.Upload.getConfiguration()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Upload.java:115)
-Method <org.gradle.api.tasks.bundling.War.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (War.java:129)
-Method <org.gradle.api.tasks.compile.AbstractCompile.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (AbstractCompile.java:52)
-Method <org.gradle.api.tasks.compile.GroovyCompile.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (GroovyCompile.java:113)
-Method <org.gradle.api.tasks.compile.GroovyCompile.getGroovyClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (GroovyCompile.java:336)
-Method <org.gradle.api.tasks.compile.GroovyCompile.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (GroovyCompile.java:305)
-Method <org.gradle.api.tasks.compile.JavaCompile.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaCompile.java:342)
-Method <org.gradle.api.tasks.compile.JavaCompile.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaCompile.java:122)
-Method <org.gradle.api.tasks.diagnostics.DependencyInsightReportTask.getConfiguration()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (DependencyInsightReportTask.java:200)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Groovydoc.java:186)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getGroovyClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Groovydoc.java:169)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Groovydoc.java:142)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Javadoc.java:331)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Javadoc.java:225)
-Method <org.gradle.api.tasks.scala.ScalaCompile.getScalaClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaCompile.java:59)
-Method <org.gradle.api.tasks.scala.ScalaCompile.getScalaCompilerPlugins()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaCompile.java:73)
-Method <org.gradle.api.tasks.scala.ScalaCompile.getZincClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaCompile.java:101)
-Method <org.gradle.api.tasks.scala.ScalaDoc.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaDoc.java:136)
-Method <org.gradle.api.tasks.scala.ScalaDoc.getScalaClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaDoc.java:148)
-Method <org.gradle.api.tasks.scala.ScalaDoc.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaDoc.java:100)
-Method <org.gradle.api.tasks.testing.Test.getBootstrapClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Test.java:343)
-Method <org.gradle.api.tasks.testing.Test.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Test.java:1085)
-Method <org.gradle.api.tasks.testing.Test.getTestClassesDirs()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Test.java:815)
-Method <org.gradle.ide.xcode.tasks.GenerateXcodeWorkspaceFileTask.getXcodeProjectLocations()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (GenerateXcodeWorkspaceFileTask.java:60)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CreateStartScripts.java:320)
-Method <org.gradle.language.scala.tasks.AbstractScalaCompile.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (AbstractScalaCompile.java:241)
-Method <org.gradle.nativeplatform.tasks.InstallExecutable.getLibs()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (InstallExecutable.java:164)
-Method <org.gradle.testing.jacoco.tasks.JacocoBase.getJacocoClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JacocoBase.java:36)
\ No newline at end of file
+Method <org.gradle.api.plugins.antlr.AntlrTask.getAntlrClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.antlr.AntlrTask.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getCheckstyleClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getCodenarcClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getCompilationClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getPmdClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getRuleSetFiles()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Pmd.java:0)
+Method <org.gradle.api.tasks.JavaExec.getBootstrapClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaExec.java:0)
+Method <org.gradle.api.tasks.SourceTask.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (SourceTask.java:0)
+Method <org.gradle.api.tasks.Upload.getConfiguration()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Upload.java:0)
+Method <org.gradle.api.tasks.bundling.War.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (War.java:0)
+Method <org.gradle.api.tasks.compile.AbstractCompile.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (AbstractCompile.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompile.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (GroovyCompile.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompile.getGroovyClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (GroovyCompile.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompile.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (GroovyCompile.java:0)
+Method <org.gradle.api.tasks.compile.JavaCompile.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaCompile.java:0)
+Method <org.gradle.api.tasks.compile.JavaCompile.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaCompile.java:0)
+Method <org.gradle.api.tasks.diagnostics.DependencyInsightReportTask.getConfiguration()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (DependencyInsightReportTask.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getGroovyClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Javadoc.java:0)
+Method <org.gradle.api.tasks.scala.ScalaCompile.getScalaClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaCompile.java:0)
+Method <org.gradle.api.tasks.scala.ScalaCompile.getScalaCompilerPlugins()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaCompile.java:0)
+Method <org.gradle.api.tasks.scala.ScalaCompile.getZincClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaCompile.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDoc.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaDoc.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDoc.getScalaClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaDoc.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDoc.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (ScalaDoc.java:0)
+Method <org.gradle.api.tasks.testing.Test.getBootstrapClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getTestClassesDirs()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (Test.java:0)
+Method <org.gradle.ide.xcode.tasks.GenerateXcodeWorkspaceFileTask.getXcodeProjectLocations()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (GenerateXcodeWorkspaceFileTask.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CreateStartScripts.java:0)
+Method <org.gradle.language.scala.tasks.AbstractScalaCompile.getSource()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (AbstractScalaCompile.java:0)
+Method <org.gradle.nativeplatform.tasks.InstallExecutable.getLibs()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (InstallExecutable.java:0)
+Method <org.gradle.testing.jacoco.tasks.JacocoBase.getJacocoClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JacocoBase.java:0)
\ No newline at end of file
diff --git a/subprojects/architecture-test/src/changes/archunit_store/provider-task-properties.txt b/subprojects/architecture-test/src/changes/archunit_store/provider-task-properties.txt
index eeb9c74..85e85bb 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/provider-task-properties.txt
+++ b/subprojects/architecture-test/src/changes/archunit_store/provider-task-properties.txt
@@ -1,313 +1,313 @@
-Method <org.gradle.api.plugins.antlr.AntlrTask.getArguments()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:152)
-Method <org.gradle.api.plugins.antlr.AntlrTask.getMaxHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:131)
-Method <org.gradle.api.plugins.antlr.AntlrTask.getOutputDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:162)
-Method <org.gradle.api.plugins.antlr.AntlrTask.isTrace()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:83)
-Method <org.gradle.api.plugins.antlr.AntlrTask.isTraceLexer()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:95)
-Method <org.gradle.api.plugins.antlr.AntlrTask.isTraceParser()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:107)
-Method <org.gradle.api.plugins.antlr.AntlrTask.isTraceTreeWalker()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:119)
-Method <org.gradle.api.plugins.quality.Checkstyle.getConfigFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:101)
-Method <org.gradle.api.plugins.quality.Checkstyle.getConfigProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:292)
-Method <org.gradle.api.plugins.quality.Checkstyle.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:333)
-Method <org.gradle.api.plugins.quality.Checkstyle.getMaxErrors()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:363)
-Method <org.gradle.api.plugins.quality.Checkstyle.getMaxWarnings()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:385)
-Method <org.gradle.api.plugins.quality.Checkstyle.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Checkstyle.java:323)
-Method <org.gradle.api.plugins.quality.Checkstyle.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Checkstyle.java:62)
-Method <org.gradle.api.plugins.quality.Checkstyle.isIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:343)
-Method <org.gradle.api.plugins.quality.Checkstyle.isShowViolations()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:405)
-Method <org.gradle.api.plugins.quality.CodeNarc.getConfigFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:75)
-Method <org.gradle.api.plugins.quality.CodeNarc.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:238)
-Method <org.gradle.api.plugins.quality.CodeNarc.getMaxPriority1Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:184)
-Method <org.gradle.api.plugins.quality.CodeNarc.getMaxPriority2Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:199)
-Method <org.gradle.api.plugins.quality.CodeNarc.getMaxPriority3Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:214)
-Method <org.gradle.api.plugins.quality.CodeNarc.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CodeNarc.java:230)
-Method <org.gradle.api.plugins.quality.CodeNarc.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CodeNarc.java:46)
-Method <org.gradle.api.plugins.quality.Pmd.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (Pmd.java:359)
-Method <org.gradle.api.plugins.quality.Pmd.getIncrementalCacheFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Pmd.java:467)
-Method <org.gradle.api.plugins.quality.Pmd.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Pmd.java:347)
-Method <org.gradle.api.plugins.quality.Pmd.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Pmd.java:68)
-Method <org.gradle.api.plugins.quality.Pmd.getRuleSets()> does not have raw return type assignable to org.gradle.api.provider.Property in (Pmd.java:253)
-Method <org.gradle.api.plugins.quality.Pmd.getTargetJdk()> does not have raw return type assignable to org.gradle.api.provider.Property in (Pmd.java:272)
-Method <org.gradle.api.plugins.quality.Pmd.isConsoleOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (Pmd.java:407)
-Method <org.gradle.api.publish.ivy.tasks.GenerateIvyDescriptor.getDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIvyDescriptor.java:62)
-Method <org.gradle.api.publish.ivy.tasks.GenerateIvyDescriptor.getDestination()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIvyDescriptor.java:76)
-Method <org.gradle.api.publish.ivy.tasks.PublishToIvyRepository.getPublication()> does not have raw return type assignable to org.gradle.api.provider.Property in (PublishToIvyRepository.java:78)
-Method <org.gradle.api.publish.ivy.tasks.PublishToIvyRepository.getRepository()> does not have raw return type assignable to org.gradle.api.provider.Property in (PublishToIvyRepository.java:117)
-Method <org.gradle.api.publish.maven.tasks.AbstractPublishToMaven.getPublication()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractPublishToMaven.java:69)
-Method <org.gradle.api.publish.maven.tasks.GenerateMavenPom.getDestination()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateMavenPom.java:95)
-Method <org.gradle.api.publish.maven.tasks.GenerateMavenPom.getPom()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateMavenPom.java:81)
-Method <org.gradle.api.publish.maven.tasks.PublishToMavenRepository.getRepository()> does not have raw return type assignable to org.gradle.api.provider.Property in (PublishToMavenRepository.java:70)
-Method <org.gradle.api.reporting.GenerateBuildDashboard.getInputReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateBuildDashboard.java:72)
-Method <org.gradle.api.reporting.GenerateBuildDashboard.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateBuildDashboard.java:137)
-Method <org.gradle.api.reporting.GenerateBuildDashboard.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateBuildDashboard.java:49)
-Method <org.gradle.api.reporting.dependencies.HtmlDependencyReportTask.getProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (HtmlDependencyReportTask.java:160)
-Method <org.gradle.api.reporting.dependencies.HtmlDependencyReportTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (HtmlDependencyReportTask.java:67)
-Method <org.gradle.api.reporting.dependencies.HtmlDependencyReportTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (HtmlDependencyReportTask.java:94)
-Method <org.gradle.api.reporting.dependents.DependentComponentsReport.getComponents()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentComponentsReport.java:107)
-Method <org.gradle.api.reporting.dependents.DependentComponentsReport.getShowAll()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentComponentsReport.java:87)
-Method <org.gradle.api.reporting.dependents.DependentComponentsReport.isShowNonBuildable()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentComponentsReport.java:61)
-Method <org.gradle.api.reporting.dependents.DependentComponentsReport.isShowTestSuites()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentComponentsReport.java:74)
-Method <org.gradle.api.reporting.model.ModelReport.getFormat()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModelReport.java:76)
-Method <org.gradle.api.reporting.model.ModelReport.isShowHidden()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModelReport.java:66)
-Method <org.gradle.api.tasks.AbstractCopyTask.getDirMode()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:538)
-Method <org.gradle.api.tasks.AbstractCopyTask.getDuplicatesStrategy()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:233)
-Method <org.gradle.api.tasks.AbstractCopyTask.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:440)
-Method <org.gradle.api.tasks.AbstractCopyTask.getFileMode()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:547)
-Method <org.gradle.api.tasks.AbstractCopyTask.getFilteringCharset()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:592)
-Method <org.gradle.api.tasks.AbstractCopyTask.getIncludeEmptyDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:208)
-Method <org.gradle.api.tasks.AbstractCopyTask.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:422)
-Method <org.gradle.api.tasks.AbstractCopyTask.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractCopyTask.java:173)
-Method <org.gradle.api.tasks.AbstractCopyTask.isCaseSensitive()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:191)
-Method <org.gradle.api.tasks.AbstractExecTask.getArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:136)
-Method <org.gradle.api.tasks.AbstractExecTask.getArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractExecTask.java:145)
-Method <org.gradle.api.tasks.AbstractExecTask.getCommandLine()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:154)
-Method <org.gradle.api.tasks.AbstractExecTask.getEnvironment()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:258)
-Method <org.gradle.api.tasks.AbstractExecTask.getErrorOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:347)
-Method <org.gradle.api.tasks.AbstractExecTask.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:189)
-Method <org.gradle.api.tasks.AbstractExecTask.getStandardInput()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:311)
-Method <org.gradle.api.tasks.AbstractExecTask.getStandardOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:329)
-Method <org.gradle.api.tasks.AbstractExecTask.getWorkingDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:224)
-Method <org.gradle.api.tasks.AbstractExecTask.isIgnoreExitValue()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:365)
-Method <org.gradle.api.tasks.Copy.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Copy.java:98)
-Method <org.gradle.api.tasks.Copy.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Copy.java:69)
-Method <org.gradle.api.tasks.Copy.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Copy.java:88)
-Method <org.gradle.api.tasks.Delete.getDelete()> does not have raw return type assignable to org.gradle.api.provider.Property in (Delete.java:76)
-Method <org.gradle.api.tasks.Delete.isFollowSymlinks()> does not have raw return type assignable to org.gradle.api.provider.Property in (Delete.java:105)
-Method <org.gradle.api.tasks.GradleBuild.getBuildFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:106)
-Method <org.gradle.api.tasks.GradleBuild.getBuildName()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:173)
-Method <org.gradle.api.tasks.GradleBuild.getDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:70)
-Method <org.gradle.api.tasks.GradleBuild.getStartParameter()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:51)
-Method <org.gradle.api.tasks.GradleBuild.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:141)
-Method <org.gradle.api.tasks.JavaExec.getAllJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:169)
-Method <org.gradle.api.tasks.JavaExec.getArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:408)
-Method <org.gradle.api.tasks.JavaExec.getArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:474)
-Method <org.gradle.api.tasks.JavaExec.getCommandLine()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:721)
-Method <org.gradle.api.tasks.JavaExec.getDebug()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:358)
-Method <org.gradle.api.tasks.JavaExec.getDebugOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:375)
-Method <org.gradle.api.tasks.JavaExec.getDefaultCharacterEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:310)
-Method <org.gradle.api.tasks.JavaExec.getEnableAssertions()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:342)
-Method <org.gradle.api.tasks.JavaExec.getEnvironment()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:605)
-Method <org.gradle.api.tasks.JavaExec.getErrorOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:694)
-Method <org.gradle.api.tasks.JavaExec.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:537)
-Method <org.gradle.api.tasks.JavaExec.getJavaVersion()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:527)
-Method <org.gradle.api.tasks.JavaExec.getJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:193)
-Method <org.gradle.api.tasks.JavaExec.getJvmArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:729)
-Method <org.gradle.api.tasks.JavaExec.getMaxHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:326)
-Method <org.gradle.api.tasks.JavaExec.getMinHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:294)
-Method <org.gradle.api.tasks.JavaExec.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:508)
-Method <org.gradle.api.tasks.JavaExec.getStandardInput()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:658)
-Method <org.gradle.api.tasks.JavaExec.getStandardOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:676)
-Method <org.gradle.api.tasks.JavaExec.getSystemProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:235)
-Method <org.gradle.api.tasks.JavaExec.getWorkingDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:571)
-Method <org.gradle.api.tasks.JavaExec.isIgnoreExitValue()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:712)
-Method <org.gradle.api.tasks.SourceTask.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceTask.java:201)
-Method <org.gradle.api.tasks.SourceTask.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceTask.java:183)
-Method <org.gradle.api.tasks.Sync.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Sync.java:104)
-Method <org.gradle.api.tasks.Sync.getPreserve()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sync.java:125)
-Method <org.gradle.api.tasks.Sync.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sync.java:67)
-Method <org.gradle.api.tasks.Sync.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sync.java:94)
-Method <org.gradle.api.tasks.Upload.getDescriptorDestination()> does not have raw return type assignable to org.gradle.api.provider.Property in (Upload.java:85)
-Method <org.gradle.api.tasks.Upload.getRepositories()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Upload.java:104)
-Method <org.gradle.api.tasks.Upload.isUploadDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Property in (Upload.java:66)
-Method <org.gradle.api.tasks.WriteProperties.getComment()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:163)
-Method <org.gradle.api.tasks.WriteProperties.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:179)
-Method <org.gradle.api.tasks.WriteProperties.getLineSeparator()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:146)
-Method <org.gradle.api.tasks.WriteProperties.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:195)
-Method <org.gradle.api.tasks.WriteProperties.getProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:72)
-Method <org.gradle.api.tasks.ant.AntTarget.getBaseDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntTarget.java:66)
-Method <org.gradle.api.tasks.ant.AntTarget.getDescription()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntTarget.java:82)
-Method <org.gradle.api.tasks.ant.AntTarget.getTarget()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntTarget.java:51)
-Method <org.gradle.api.tasks.bundling.AbstractArchiveTask.getArchivePath()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractArchiveTask.java:156)
-Method <org.gradle.api.tasks.bundling.AbstractArchiveTask.isPreserveFileTimestamps()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractArchiveTask.java:274)
-Method <org.gradle.api.tasks.bundling.AbstractArchiveTask.isReproducibleFileOrder()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractArchiveTask.java:303)
-Method <org.gradle.api.tasks.bundling.Tar.getCompression()> does not have raw return type assignable to org.gradle.api.provider.Property in (Tar.java:59)
-Method <org.gradle.api.tasks.bundling.War.getWebInf()> does not have raw return type assignable to org.gradle.api.provider.Provider in (War.java:89)
-Method <org.gradle.api.tasks.bundling.War.getWebXml()> does not have raw return type assignable to org.gradle.api.provider.Property in (War.java:171)
-Method <org.gradle.api.tasks.bundling.Zip.getEntryCompression()> does not have raw return type assignable to org.gradle.api.provider.Property in (Zip.java:76)
-Method <org.gradle.api.tasks.bundling.Zip.getMetadataCharset()> does not have raw return type assignable to org.gradle.api.provider.Property in (Zip.java:123)
-Method <org.gradle.api.tasks.bundling.Zip.isZip64()> does not have raw return type assignable to org.gradle.api.provider.Property in (Zip.java:111)
-Method <org.gradle.api.tasks.compile.AbstractCompile.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCompile.java:86)
-Method <org.gradle.api.tasks.compile.AbstractCompile.getSourceCompatibility()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCompile.java:140)
-Method <org.gradle.api.tasks.compile.AbstractCompile.getTargetCompatibility()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCompile.java:159)
-Method <org.gradle.api.tasks.compile.GroovyCompile.getGroovyOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyCompile.java:316)
-Method <org.gradle.api.tasks.compile.GroovyCompile.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyCompile.java:326)
-Method <org.gradle.api.tasks.compile.JavaCompile.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaCompile.java:325)
-Method <org.gradle.api.tasks.compile.JavaCompile.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaCompile.java:335)
-Method <org.gradle.api.tasks.diagnostics.AbstractConfigurationReportTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractConfigurationReportTask.java:51)
-Method <org.gradle.api.tasks.diagnostics.AbstractConfigurationReportTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractConfigurationReportTask.java:73)
-Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.getConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractDependencyReportTask.java:111)
-Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.getRenderer()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractDependencyReportTask.java:54)
+Method <org.gradle.api.plugins.antlr.AntlrTask.getArguments()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.antlr.AntlrTask.getMaxHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.antlr.AntlrTask.getOutputDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.antlr.AntlrTask.isTrace()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.antlr.AntlrTask.isTraceLexer()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.antlr.AntlrTask.isTraceParser()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.antlr.AntlrTask.isTraceTreeWalker()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntlrTask.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getConfigFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getConfigProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getMaxErrors()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getMaxWarnings()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.isIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.Checkstyle.isShowViolations()> does not have raw return type assignable to org.gradle.api.provider.Property in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getConfigFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getMaxPriority1Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getMaxPriority2Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getMaxPriority3Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getIncrementalCacheFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getRuleSets()> does not have raw return type assignable to org.gradle.api.provider.Property in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getTargetJdk()> does not have raw return type assignable to org.gradle.api.provider.Property in (Pmd.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.isConsoleOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (Pmd.java:0)
+Method <org.gradle.api.publish.ivy.tasks.GenerateIvyDescriptor.getDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIvyDescriptor.java:0)
+Method <org.gradle.api.publish.ivy.tasks.GenerateIvyDescriptor.getDestination()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIvyDescriptor.java:0)
+Method <org.gradle.api.publish.ivy.tasks.PublishToIvyRepository.getPublication()> does not have raw return type assignable to org.gradle.api.provider.Property in (PublishToIvyRepository.java:0)
+Method <org.gradle.api.publish.ivy.tasks.PublishToIvyRepository.getRepository()> does not have raw return type assignable to org.gradle.api.provider.Property in (PublishToIvyRepository.java:0)
+Method <org.gradle.api.publish.maven.tasks.AbstractPublishToMaven.getPublication()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractPublishToMaven.java:0)
+Method <org.gradle.api.publish.maven.tasks.GenerateMavenPom.getDestination()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateMavenPom.java:0)
+Method <org.gradle.api.publish.maven.tasks.GenerateMavenPom.getPom()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateMavenPom.java:0)
+Method <org.gradle.api.publish.maven.tasks.PublishToMavenRepository.getRepository()> does not have raw return type assignable to org.gradle.api.provider.Property in (PublishToMavenRepository.java:0)
+Method <org.gradle.api.reporting.GenerateBuildDashboard.getInputReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateBuildDashboard.java:0)
+Method <org.gradle.api.reporting.GenerateBuildDashboard.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateBuildDashboard.java:0)
+Method <org.gradle.api.reporting.GenerateBuildDashboard.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateBuildDashboard.java:0)
+Method <org.gradle.api.reporting.dependencies.HtmlDependencyReportTask.getProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (HtmlDependencyReportTask.java:0)
+Method <org.gradle.api.reporting.dependencies.HtmlDependencyReportTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (HtmlDependencyReportTask.java:0)
+Method <org.gradle.api.reporting.dependencies.HtmlDependencyReportTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (HtmlDependencyReportTask.java:0)
+Method <org.gradle.api.reporting.dependents.DependentComponentsReport.getComponents()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentComponentsReport.java:0)
+Method <org.gradle.api.reporting.dependents.DependentComponentsReport.getShowAll()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentComponentsReport.java:0)
+Method <org.gradle.api.reporting.dependents.DependentComponentsReport.isShowNonBuildable()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentComponentsReport.java:0)
+Method <org.gradle.api.reporting.dependents.DependentComponentsReport.isShowTestSuites()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentComponentsReport.java:0)
+Method <org.gradle.api.reporting.model.ModelReport.getFormat()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModelReport.java:0)
+Method <org.gradle.api.reporting.model.ModelReport.isShowHidden()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModelReport.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getDirMode()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getDuplicatesStrategy()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getFileMode()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getFilteringCharset()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getIncludeEmptyDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.isCaseSensitive()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getCommandLine()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getEnvironment()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getErrorOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getStandardInput()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getStandardOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.getWorkingDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.AbstractExecTask.isIgnoreExitValue()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractExecTask.java:0)
+Method <org.gradle.api.tasks.Copy.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Copy.java:0)
+Method <org.gradle.api.tasks.Copy.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Copy.java:0)
+Method <org.gradle.api.tasks.Copy.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Copy.java:0)
+Method <org.gradle.api.tasks.Delete.getDelete()> does not have raw return type assignable to org.gradle.api.provider.Property in (Delete.java:0)
+Method <org.gradle.api.tasks.Delete.isFollowSymlinks()> does not have raw return type assignable to org.gradle.api.provider.Property in (Delete.java:0)
+Method <org.gradle.api.tasks.GradleBuild.getBuildFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:0)
+Method <org.gradle.api.tasks.GradleBuild.getBuildName()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:0)
+Method <org.gradle.api.tasks.GradleBuild.getDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:0)
+Method <org.gradle.api.tasks.GradleBuild.getStartParameter()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:0)
+Method <org.gradle.api.tasks.GradleBuild.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradleBuild.java:0)
+Method <org.gradle.api.tasks.JavaExec.getAllJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getCommandLine()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getDebug()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getDebugOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getDefaultCharacterEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getEnableAssertions()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getEnvironment()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getErrorOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getJavaVersion()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getJvmArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getMaxHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getMinHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getStandardInput()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getStandardOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getSystemProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.getWorkingDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.JavaExec.isIgnoreExitValue()> does not have raw return type assignable to org.gradle.api.provider.Property in (JavaExec.java:0)
+Method <org.gradle.api.tasks.SourceTask.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceTask.java:0)
+Method <org.gradle.api.tasks.SourceTask.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceTask.java:0)
+Method <org.gradle.api.tasks.Sync.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Sync.java:0)
+Method <org.gradle.api.tasks.Sync.getPreserve()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sync.java:0)
+Method <org.gradle.api.tasks.Sync.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sync.java:0)
+Method <org.gradle.api.tasks.Sync.getRootSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sync.java:0)
+Method <org.gradle.api.tasks.Upload.getDescriptorDestination()> does not have raw return type assignable to org.gradle.api.provider.Property in (Upload.java:0)
+Method <org.gradle.api.tasks.Upload.getRepositories()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Upload.java:0)
+Method <org.gradle.api.tasks.Upload.isUploadDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Property in (Upload.java:0)
+Method <org.gradle.api.tasks.WriteProperties.getComment()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:0)
+Method <org.gradle.api.tasks.WriteProperties.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:0)
+Method <org.gradle.api.tasks.WriteProperties.getLineSeparator()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:0)
+Method <org.gradle.api.tasks.WriteProperties.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:0)
+Method <org.gradle.api.tasks.WriteProperties.getProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (WriteProperties.java:0)
+Method <org.gradle.api.tasks.ant.AntTarget.getBaseDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntTarget.java:0)
+Method <org.gradle.api.tasks.ant.AntTarget.getDescription()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntTarget.java:0)
+Method <org.gradle.api.tasks.ant.AntTarget.getTarget()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntTarget.java:0)
+Method <org.gradle.api.tasks.bundling.AbstractArchiveTask.getArchivePath()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractArchiveTask.java:0)
+Method <org.gradle.api.tasks.bundling.AbstractArchiveTask.isPreserveFileTimestamps()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractArchiveTask.java:0)
+Method <org.gradle.api.tasks.bundling.AbstractArchiveTask.isReproducibleFileOrder()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractArchiveTask.java:0)
+Method <org.gradle.api.tasks.bundling.Tar.getCompression()> does not have raw return type assignable to org.gradle.api.provider.Property in (Tar.java:0)
+Method <org.gradle.api.tasks.bundling.War.getWebInf()> does not have raw return type assignable to org.gradle.api.provider.Provider in (War.java:0)
+Method <org.gradle.api.tasks.bundling.War.getWebXml()> does not have raw return type assignable to org.gradle.api.provider.Property in (War.java:0)
+Method <org.gradle.api.tasks.bundling.Zip.getEntryCompression()> does not have raw return type assignable to org.gradle.api.provider.Property in (Zip.java:0)
+Method <org.gradle.api.tasks.bundling.Zip.getMetadataCharset()> does not have raw return type assignable to org.gradle.api.provider.Property in (Zip.java:0)
+Method <org.gradle.api.tasks.bundling.Zip.isZip64()> does not have raw return type assignable to org.gradle.api.provider.Property in (Zip.java:0)
+Method <org.gradle.api.tasks.compile.AbstractCompile.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCompile.java:0)
+Method <org.gradle.api.tasks.compile.AbstractCompile.getSourceCompatibility()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCompile.java:0)
+Method <org.gradle.api.tasks.compile.AbstractCompile.getTargetCompatibility()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractCompile.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompile.getGroovyOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyCompile.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompile.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyCompile.java:0)
+Method <org.gradle.api.tasks.compile.JavaCompile.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaCompile.java:0)
+Method <org.gradle.api.tasks.compile.JavaCompile.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavaCompile.java:0)
+Method <org.gradle.api.tasks.diagnostics.AbstractConfigurationReportTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractConfigurationReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.AbstractConfigurationReportTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractConfigurationReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.getConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractDependencyReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.getRenderer()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractDependencyReportTask.java:0)
 Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.getTaskConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractDependencyReportTask.java:0)
-Method <org.gradle.api.tasks.diagnostics.AbstractReportTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractReportTask.java:119)
-Method <org.gradle.api.tasks.diagnostics.AbstractReportTask.getProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractReportTask.java:140)
-Method <org.gradle.api.tasks.diagnostics.ConventionReportTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (ConventionReportTask.java:88)
-Method <org.gradle.api.tasks.diagnostics.ConventionReportTask.getProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (ConventionReportTask.java:109)
-Method <org.gradle.api.tasks.diagnostics.DependencyInsightReportTask.getDependencySpec()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependencyInsightReportTask.java:166)
-Method <org.gradle.api.tasks.diagnostics.DependencyInsightReportTask.isShowSinglePathToDependency()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependencyInsightReportTask.java:232)
-Method <org.gradle.api.tasks.diagnostics.DependencyReportTask.getTaskConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Provider in (DependencyReportTask.java:30)
-Method <org.gradle.api.tasks.diagnostics.PropertyReportTask.getRenderer()> does not have raw return type assignable to org.gradle.api.provider.Property in (PropertyReportTask.java:63)
-Method <org.gradle.api.tasks.diagnostics.TaskReportTask.getDisplayGroup()> does not have raw return type assignable to org.gradle.api.provider.Property in (TaskReportTask.java:114)
-Method <org.gradle.api.tasks.diagnostics.TaskReportTask.getDisplayGroups()> does not have raw return type assignable to org.gradle.api.provider.Property in (TaskReportTask.java:142)
-Method <org.gradle.api.tasks.diagnostics.TaskReportTask.getRenderer()> does not have raw return type assignable to org.gradle.api.provider.Property in (TaskReportTask.java:69)
-Method <org.gradle.api.tasks.diagnostics.TaskReportTask.isDetail()> does not have raw return type assignable to org.gradle.api.provider.Provider in (TaskReportTask.java:92)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getAntGroovydoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:198)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:152)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getDocTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:281)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getFooter()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:319)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:300)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getLinks()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:400)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getWindowTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:262)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.isNoTimestamp()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:230)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.isNoVersionStamp()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:245)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.isUse()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:215)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:246)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:411)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getMaxMemory()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:271)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Javadoc.java:350)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Javadoc.java:360)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getOptionsFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Javadoc.java:397)
-Method <org.gradle.api.tasks.javadoc.Javadoc.getTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:292)
-Method <org.gradle.api.tasks.javadoc.Javadoc.isFailOnError()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:388)
-Method <org.gradle.api.tasks.javadoc.Javadoc.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:309)
-Method <org.gradle.api.tasks.scala.ScalaCompile.getScalaCompileOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ScalaCompile.java:40)
-Method <org.gradle.api.tasks.scala.ScalaCompile.getScalaCompileOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ScalaCompile.java:51)
-Method <org.gradle.api.tasks.scala.ScalaDoc.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDoc.java:81)
-Method <org.gradle.api.tasks.scala.ScalaDoc.getScalaDocOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDoc.java:160)
-Method <org.gradle.api.tasks.scala.ScalaDoc.getTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDoc.java:174)
-Method <org.gradle.api.tasks.testing.AbstractTestTask.getFilter()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractTestTask.java:633)
-Method <org.gradle.api.tasks.testing.AbstractTestTask.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractTestTask.java:271)
-Method <org.gradle.api.tasks.testing.AbstractTestTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractTestTask.java:102)
-Method <org.gradle.api.tasks.testing.AbstractTestTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractTestTask.java:577)
-Method <org.gradle.api.tasks.testing.AbstractTestTask.getTestLogging()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractTestTask.java:374)
-Method <org.gradle.api.tasks.testing.Test.getAllJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:535)
-Method <org.gradle.api.tasks.testing.Test.getDebug()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:482)
-Method <org.gradle.api.tasks.testing.Test.getDebugOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:500)
-Method <org.gradle.api.tasks.testing.Test.getDefaultCharacterEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:376)
-Method <org.gradle.api.tasks.testing.Test.getEnableAssertions()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:466)
-Method <org.gradle.api.tasks.testing.Test.getEnvironment()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:560)
-Method <org.gradle.api.tasks.testing.Test.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:879)
-Method <org.gradle.api.tasks.testing.Test.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:276)
-Method <org.gradle.api.tasks.testing.Test.getFailFast()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:527)
-Method <org.gradle.api.tasks.testing.Test.getForkEvery()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:1121)
-Method <org.gradle.api.tasks.testing.Test.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:856)
-Method <org.gradle.api.tasks.testing.Test.getJavaVersion()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:267)
-Method <org.gradle.api.tasks.testing.Test.getJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:416)
-Method <org.gradle.api.tasks.testing.Test.getJvmArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:424)
-Method <org.gradle.api.tasks.testing.Test.getMaxHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:400)
-Method <org.gradle.api.tasks.testing.Test.getMaxParallelForks()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:1155)
-Method <org.gradle.api.tasks.testing.Test.getMinHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:368)
-Method <org.gradle.api.tasks.testing.Test.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:621)
-Method <org.gradle.api.tasks.testing.Test.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:923)
-Method <org.gradle.api.tasks.testing.Test.getSystemProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:309)
-Method <org.gradle.api.tasks.testing.Test.getTestFramework()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:907)
-Method <org.gradle.api.tasks.testing.Test.getWorkingDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:231)
-Method <org.gradle.api.tasks.testing.Test.isScanForTestClasses()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:1098)
-Method <org.gradle.api.tasks.testing.TestReport.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestReport.java:74)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getArchiveBase()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:476)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getArchivePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:459)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getAvailableDistributionTypes()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Wrapper.java:359)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getBatchScript()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Wrapper.java:237)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionBase()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:442)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:283)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionSha256Sum()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:417)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:340)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionUrl()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:376)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getGradleVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:319)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getJarFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:246)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getPropertiesFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Wrapper.java:270)
-Method <org.gradle.api.tasks.wrapper.Wrapper.getScriptFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:213)
-Method <org.gradle.buildinit.tasks.InitBuild.getAvailableBuildTypes()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InitBuild.java:311)
-Method <org.gradle.buildinit.tasks.InitBuild.getAvailableDSLs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InitBuild.java:331)
-Method <org.gradle.buildinit.tasks.InitBuild.getAvailableTestFrameworks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InitBuild.java:347)
-Method <org.gradle.buildinit.tasks.InitBuild.getDsl()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:103)
-Method <org.gradle.buildinit.tasks.InitBuild.getPackageName()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:145)
-Method <org.gradle.buildinit.tasks.InitBuild.getProjectLayoutRegistry()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:174)
-Method <org.gradle.buildinit.tasks.InitBuild.getProjectName()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:133)
-Method <org.gradle.buildinit.tasks.InitBuild.getTestFramework()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:157)
-Method <org.gradle.buildinit.tasks.InitBuild.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:76)
-Method <org.gradle.ide.visualstudio.tasks.GenerateFiltersFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateFiltersFileTask.java:56)
-Method <org.gradle.ide.visualstudio.tasks.GenerateFiltersFileTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateFiltersFileTask.java:61)
-Method <org.gradle.ide.visualstudio.tasks.GenerateFiltersFileTask.getVisualStudioProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateFiltersFileTask.java:51)
-Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getGradleArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:163)
-Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getGradleExe()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:152)
-Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:93)
-Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:99)
-Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateProjectFileTask.java:78)
-Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getVisualStudioProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:87)
-Method <org.gradle.ide.visualstudio.tasks.GenerateSolutionFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateSolutionFileTask.java:63)
-Method <org.gradle.ide.visualstudio.tasks.GenerateSolutionFileTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateSolutionFileTask.java:69)
-Method <org.gradle.ide.visualstudio.tasks.GenerateSolutionFileTask.getSolution()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateSolutionFileTask.java:57)
-Method <org.gradle.ide.xcode.tasks.GenerateSchemeFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateSchemeFileTask.java:128)
-Method <org.gradle.ide.xcode.tasks.GenerateSchemeFileTask.getXcodeProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateSchemeFileTask.java:51)
-Method <org.gradle.ide.xcode.tasks.GenerateXcodeProjectFileTask.getXcodeProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateXcodeProjectFileTask.java:319)
-Method <org.gradle.ide.xcode.tasks.GenerateXcodeWorkspaceFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateXcodeWorkspaceFileTask.java:69)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getApplicationName()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:298)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getDefaultJvmOpts()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:285)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getExecutableDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:217)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getExitEnvironmentVar()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:171)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getMainClassName()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:258)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CreateStartScripts.java:330)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getOptsEnvironmentVar()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:153)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:204)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getUnixScript()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CreateStartScripts.java:187)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getUnixStartScriptGenerator()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:344)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getWindowsScript()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CreateStartScripts.java:195)
-Method <org.gradle.jvm.application.tasks.CreateStartScripts.getWindowsStartScriptGenerator()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:358)
-Method <org.gradle.jvm.tasks.Jar.getManifest()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jar.java:165)
-Method <org.gradle.jvm.tasks.Jar.getManifestContentCharset()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jar.java:138)
-Method <org.gradle.jvm.tasks.Jar.getMetaInf()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Jar.java:213)
-Method <org.gradle.jvm.tasks.Jar.getMetadataCharset()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jar.java:112)
-Method <org.gradle.language.assembler.tasks.Assemble.getAssemblerArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Assemble.java:139)
-Method <org.gradle.language.assembler.tasks.Assemble.getObjectFileDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Assemble.java:171)
-Method <org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.getMacros()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeCompileTask.java:286)
-Method <org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.isDebuggable()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeCompileTask.java:197)
-Method <org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.isOptimized()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeCompileTask.java:216)
-Method <org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.isPositionIndependentCode()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeCompileTask.java:183)
-Method <org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask.getPreCompiledHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeSourceCompileTask.java:81)
-Method <org.gradle.language.rc.tasks.WindowsResourceCompile.getMacros()> does not have raw return type assignable to org.gradle.api.provider.Property in (WindowsResourceCompile.java:206)
-Method <org.gradle.language.rc.tasks.WindowsResourceCompile.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (WindowsResourceCompile.java:159)
-Method <org.gradle.language.scala.tasks.AbstractScalaCompile.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractScalaCompile.java:116)
-Method <org.gradle.language.scala.tasks.AbstractScalaCompile.getScalaCompileOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractScalaCompile.java:108)
-Method <org.gradle.language.swift.tasks.SwiftCompile.isDebuggable()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SwiftCompile.java:170)
-Method <org.gradle.language.swift.tasks.SwiftCompile.isOptimized()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SwiftCompile.java:190)
-Method <org.gradle.nativeplatform.tasks.AbstractLinkTask.isDebuggable()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractLinkTask.java:147)
-Method <org.gradle.nativeplatform.tasks.PrefixHeaderFileGenerateTask.getHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (PrefixHeaderFileGenerateTask.java:74)
-Method <org.gradle.nativeplatform.tasks.PrefixHeaderFileGenerateTask.getPrefixHeaderFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (PrefixHeaderFileGenerateTask.java:83)
-Method <org.gradle.nativeplatform.test.cunit.tasks.GenerateCUnitLauncher.getHeaderDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateCUnitLauncher.java:57)
-Method <org.gradle.nativeplatform.test.cunit.tasks.GenerateCUnitLauncher.getSourceDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateCUnitLauncher.java:48)
-Method <org.gradle.nativeplatform.test.tasks.RunTestExecutable.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (RunTestExecutable.java:91)
-Method <org.gradle.nativeplatform.test.tasks.RunTestExecutable.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (RunTestExecutable.java:78)
-Method <org.gradle.nativeplatform.test.tasks.RunTestExecutable.isIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (RunTestExecutable.java:96)
-Method <org.gradle.plugins.ear.Ear.getDeploymentDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Property in (Ear.java:273)
-Method <org.gradle.plugins.ear.Ear.getLib()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Ear.java:214)
-Method <org.gradle.plugins.ear.Ear.getLibDirName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Ear.java:251)
-Method <org.gradle.plugins.ide.api.GeneratorTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GeneratorTask.java:117)
-Method <org.gradle.plugins.ide.api.GeneratorTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GeneratorTask.java:149)
-Method <org.gradle.plugins.ide.api.PropertyListGeneratorTask.getPropertyListTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PropertyListGeneratorTask.java:55)
-Method <org.gradle.plugins.ide.api.XmlGeneratorTask.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (XmlGeneratorTask.java:49)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.getClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseClasspath.java:69)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateEclipseClasspath.java:58)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseJdt.getJdt()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseJdt.java:75)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseProject.getProjectModel()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseProject.java:71)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseProject.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateEclipseProject.java:60)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseWtpComponent.java:72)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateEclipseWtpComponent.java:61)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.getFacet()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseWtpFacet.java:72)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateEclipseWtpFacet.java:61)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaModule.getModule()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaModule.java:88)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaModule.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaModule.java:72)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaModule.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateIdeaModule.java:59)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.getIdeaProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaProject.java:83)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaProject.java:67)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateIdeaProject.java:56)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.getWorkspace()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaWorkspace.java:65)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateIdeaWorkspace.java:54)
-Method <org.gradle.plugins.signing.Sign.getGeneratorsByKey()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sign.java:253)
-Method <org.gradle.plugins.signing.Sign.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Property in (Sign.java:361)
-Method <org.gradle.plugins.signing.Sign.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Sign.java:344)
-Method <org.gradle.plugins.signing.Sign.getSignatures()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sign.java:242)
-Method <org.gradle.plugins.signing.Sign.getSignaturesByKey()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sign.java:263)
-Method <org.gradle.plugins.signing.Sign.getSingleSignature()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sign.java:297)
-Method <org.gradle.plugins.signing.Sign.isRequired()> does not have raw return type assignable to org.gradle.api.provider.Property in (Sign.java:377)
-Method <org.gradle.testing.jacoco.tasks.JacocoCoverageVerification.getViolationRules()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoCoverageVerification.java:59)
-Method <org.gradle.testing.jacoco.tasks.JacocoReport.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoReport.java:35)
-Method <org.gradle.testing.jacoco.tasks.JacocoReport.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoReport.java:64)
+Method <org.gradle.api.tasks.diagnostics.AbstractReportTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.AbstractReportTask.getProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.ConventionReportTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (ConventionReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.ConventionReportTask.getProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (ConventionReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.DependencyInsightReportTask.getDependencySpec()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependencyInsightReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.DependencyInsightReportTask.isShowSinglePathToDependency()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependencyInsightReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.DependencyReportTask.getTaskConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Provider in (DependencyReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.PropertyReportTask.getRenderer()> does not have raw return type assignable to org.gradle.api.provider.Property in (PropertyReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.TaskReportTask.getDisplayGroup()> does not have raw return type assignable to org.gradle.api.provider.Property in (TaskReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.TaskReportTask.getDisplayGroups()> does not have raw return type assignable to org.gradle.api.provider.Property in (TaskReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.TaskReportTask.getRenderer()> does not have raw return type assignable to org.gradle.api.provider.Property in (TaskReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.TaskReportTask.isDetail()> does not have raw return type assignable to org.gradle.api.provider.Provider in (TaskReportTask.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getAntGroovydoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getDocTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getFooter()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getLinks()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getWindowTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.isNoTimestamp()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.isNoVersionStamp()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.isUse()> does not have raw return type assignable to org.gradle.api.provider.Property in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getMaxMemory()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getOptionsFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.getTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.isFailOnError()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Javadoc.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Property in (Javadoc.java:0)
+Method <org.gradle.api.tasks.scala.ScalaCompile.getScalaCompileOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ScalaCompile.java:0)
+Method <org.gradle.api.tasks.scala.ScalaCompile.getScalaCompileOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ScalaCompile.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDoc.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDoc.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDoc.getScalaDocOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDoc.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDoc.getTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDoc.java:0)
+Method <org.gradle.api.tasks.testing.AbstractTestTask.getFilter()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractTestTask.java:0)
+Method <org.gradle.api.tasks.testing.AbstractTestTask.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractTestTask.java:0)
+Method <org.gradle.api.tasks.testing.AbstractTestTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractTestTask.java:0)
+Method <org.gradle.api.tasks.testing.AbstractTestTask.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractTestTask.java:0)
+Method <org.gradle.api.tasks.testing.AbstractTestTask.getTestLogging()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractTestTask.java:0)
+Method <org.gradle.api.tasks.testing.Test.getAllJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getDebug()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getDebugOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getDefaultCharacterEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getEnableAssertions()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getEnvironment()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getFailFast()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getForkEvery()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getJavaVersion()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getJvmArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getMaxHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getMaxParallelForks()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getMinHeapSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getSystemProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getTestFramework()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getWorkingDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.isScanForTestClasses()> does not have raw return type assignable to org.gradle.api.provider.Property in (Test.java:0)
+Method <org.gradle.api.tasks.testing.TestReport.getDestinationDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestReport.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getArchiveBase()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getArchivePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getAvailableDistributionTypes()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getBatchScript()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionBase()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionSha256Sum()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getDistributionUrl()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getGradleVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getJarFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getPropertiesFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Wrapper.java:0)
+Method <org.gradle.api.tasks.wrapper.Wrapper.getScriptFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (Wrapper.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getAvailableBuildTypes()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InitBuild.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getAvailableDSLs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InitBuild.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getAvailableTestFrameworks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InitBuild.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getDsl()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getPackageName()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getProjectLayoutRegistry()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getProjectName()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getTestFramework()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (InitBuild.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateFiltersFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateFiltersFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateFiltersFileTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateFiltersFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateFiltersFileTask.getVisualStudioProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateFiltersFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getGradleArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getGradleExe()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateProjectFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateProjectFileTask.getVisualStudioProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateProjectFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateSolutionFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateSolutionFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateSolutionFileTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateSolutionFileTask.java:0)
+Method <org.gradle.ide.visualstudio.tasks.GenerateSolutionFileTask.getSolution()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateSolutionFileTask.java:0)
+Method <org.gradle.ide.xcode.tasks.GenerateSchemeFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateSchemeFileTask.java:0)
+Method <org.gradle.ide.xcode.tasks.GenerateSchemeFileTask.getXcodeProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateSchemeFileTask.java:0)
+Method <org.gradle.ide.xcode.tasks.GenerateXcodeProjectFileTask.getXcodeProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateXcodeProjectFileTask.java:0)
+Method <org.gradle.ide.xcode.tasks.GenerateXcodeWorkspaceFileTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateXcodeWorkspaceFileTask.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getApplicationName()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getDefaultJvmOpts()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getExecutableDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getExitEnvironmentVar()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getMainClassName()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getModularity()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getOptsEnvironmentVar()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getUnixScript()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getUnixStartScriptGenerator()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getWindowsScript()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.application.tasks.CreateStartScripts.getWindowsStartScriptGenerator()> does not have raw return type assignable to org.gradle.api.provider.Property in (CreateStartScripts.java:0)
+Method <org.gradle.jvm.tasks.Jar.getManifest()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jar.java:0)
+Method <org.gradle.jvm.tasks.Jar.getManifestContentCharset()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jar.java:0)
+Method <org.gradle.jvm.tasks.Jar.getMetaInf()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Jar.java:0)
+Method <org.gradle.jvm.tasks.Jar.getMetadataCharset()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jar.java:0)
+Method <org.gradle.language.assembler.tasks.Assemble.getAssemblerArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Assemble.java:0)
+Method <org.gradle.language.assembler.tasks.Assemble.getObjectFileDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Assemble.java:0)
+Method <org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.getMacros()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeCompileTask.java:0)
+Method <org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.isDebuggable()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeCompileTask.java:0)
+Method <org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.isOptimized()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeCompileTask.java:0)
+Method <org.gradle.language.nativeplatform.tasks.AbstractNativeCompileTask.isPositionIndependentCode()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeCompileTask.java:0)
+Method <org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask.getPreCompiledHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractNativeSourceCompileTask.java:0)
+Method <org.gradle.language.rc.tasks.WindowsResourceCompile.getMacros()> does not have raw return type assignable to org.gradle.api.provider.Property in (WindowsResourceCompile.java:0)
+Method <org.gradle.language.rc.tasks.WindowsResourceCompile.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (WindowsResourceCompile.java:0)
+Method <org.gradle.language.scala.tasks.AbstractScalaCompile.getOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractScalaCompile.java:0)
+Method <org.gradle.language.scala.tasks.AbstractScalaCompile.getScalaCompileOptions()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractScalaCompile.java:0)
+Method <org.gradle.language.swift.tasks.SwiftCompile.isDebuggable()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SwiftCompile.java:0)
+Method <org.gradle.language.swift.tasks.SwiftCompile.isOptimized()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SwiftCompile.java:0)
+Method <org.gradle.nativeplatform.tasks.AbstractLinkTask.isDebuggable()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractLinkTask.java:0)
+Method <org.gradle.nativeplatform.tasks.PrefixHeaderFileGenerateTask.getHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (PrefixHeaderFileGenerateTask.java:0)
+Method <org.gradle.nativeplatform.tasks.PrefixHeaderFileGenerateTask.getPrefixHeaderFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (PrefixHeaderFileGenerateTask.java:0)
+Method <org.gradle.nativeplatform.test.cunit.tasks.GenerateCUnitLauncher.getHeaderDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateCUnitLauncher.java:0)
+Method <org.gradle.nativeplatform.test.cunit.tasks.GenerateCUnitLauncher.getSourceDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateCUnitLauncher.java:0)
+Method <org.gradle.nativeplatform.test.tasks.RunTestExecutable.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (RunTestExecutable.java:0)
+Method <org.gradle.nativeplatform.test.tasks.RunTestExecutable.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (RunTestExecutable.java:0)
+Method <org.gradle.nativeplatform.test.tasks.RunTestExecutable.isIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (RunTestExecutable.java:0)
+Method <org.gradle.plugins.ear.Ear.getDeploymentDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Property in (Ear.java:0)
+Method <org.gradle.plugins.ear.Ear.getLib()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Ear.java:0)
+Method <org.gradle.plugins.ear.Ear.getLibDirName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Ear.java:0)
+Method <org.gradle.plugins.ide.api.GeneratorTask.getInputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GeneratorTask.java:0)
+Method <org.gradle.plugins.ide.api.GeneratorTask.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GeneratorTask.java:0)
+Method <org.gradle.plugins.ide.api.PropertyListGeneratorTask.getPropertyListTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PropertyListGeneratorTask.java:0)
+Method <org.gradle.plugins.ide.api.XmlGeneratorTask.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (XmlGeneratorTask.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.getClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateEclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseJdt.getJdt()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseJdt.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseProject.getProjectModel()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseProject.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateEclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateEclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.getFacet()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateEclipseWtpFacet.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateEclipseWtpFacet.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaModule.getModule()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaModule.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaModule.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateIdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.getIdeaProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateIdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.getWorkspace()> does not have raw return type assignable to org.gradle.api.provider.Property in (GenerateIdeaWorkspace.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GenerateIdeaWorkspace.java:0)
+Method <org.gradle.plugins.signing.Sign.getGeneratorsByKey()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sign.java:0)
+Method <org.gradle.plugins.signing.Sign.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Property in (Sign.java:0)
+Method <org.gradle.plugins.signing.Sign.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Sign.java:0)
+Method <org.gradle.plugins.signing.Sign.getSignatures()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sign.java:0)
+Method <org.gradle.plugins.signing.Sign.getSignaturesByKey()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sign.java:0)
+Method <org.gradle.plugins.signing.Sign.getSingleSignature()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Sign.java:0)
+Method <org.gradle.plugins.signing.Sign.isRequired()> does not have raw return type assignable to org.gradle.api.provider.Property in (Sign.java:0)
+Method <org.gradle.testing.jacoco.tasks.JacocoCoverageVerification.getViolationRules()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoCoverageVerification.java:0)
+Method <org.gradle.testing.jacoco.tasks.JacocoReport.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoReport.java:0)
+Method <org.gradle.testing.jacoco.tasks.JacocoReport.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoReport.java:0)
\ No newline at end of file
diff --git a/subprojects/architecture-test/src/changes/archunit_store/provider-text-resource.txt b/subprojects/architecture-test/src/changes/archunit_store/provider-text-resource.txt
index 07ee85c..2fb56b7 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/provider-text-resource.txt
+++ b/subprojects/architecture-test/src/changes/archunit_store/provider-text-resource.txt
@@ -1,4 +1,4 @@
-Method <org.gradle.api.plugins.quality.Checkstyle.getConfig()> has raw return type org.gradle.api.resources.TextResource in (Checkstyle.java:273)
-Method <org.gradle.api.plugins.quality.CodeNarc.getConfig()> has raw return type org.gradle.api.resources.TextResource in (CodeNarc.java:167)
-Method <org.gradle.api.plugins.quality.Pmd.getRuleSetConfig()> has raw return type org.gradle.api.resources.TextResource in (Pmd.java:297)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getOverviewText()> has raw return type org.gradle.api.resources.TextResource in (Groovydoc.java:338)
\ No newline at end of file
+Method <org.gradle.api.plugins.quality.Checkstyle.getConfig()> has raw return type org.gradle.api.resources.TextResource in (Checkstyle.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarc.getConfig()> has raw return type org.gradle.api.resources.TextResource in (CodeNarc.java:0)
+Method <org.gradle.api.plugins.quality.Pmd.getRuleSetConfig()> has raw return type org.gradle.api.resources.TextResource in (Pmd.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getOverviewText()> has raw return type org.gradle.api.resources.TextResource in (Groovydoc.java:0)
\ No newline at end of file
diff --git a/subprojects/architecture-test/src/changes/archunit_store/public-api-methods-return-allowed-types.txt b/subprojects/architecture-test/src/changes/archunit_store/public-api-methods-return-allowed-types.txt
index 7538fc0..9c9cd52 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/public-api-methods-return-allowed-types.txt
+++ b/subprojects/architecture-test/src/changes/archunit_store/public-api-methods-return-allowed-types.txt
@@ -1,84 +1,84 @@
-Method <org.gradle.api.DefaultTask.getInputs()> has arguments/return type org.gradle.api.internal.TaskInputsInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (DefaultTask.java:299)
-Method <org.gradle.api.DefaultTask.getOutputs()> has arguments/return type org.gradle.api.internal.TaskOutputsInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (DefaultTask.java:304)
-Method <org.gradle.api.DefaultTask.getState()> has arguments/return type org.gradle.api.internal.tasks.TaskStateInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (DefaultTask.java:274)
-Method <org.gradle.api.DefaultTask.getTaskDependencies()> has arguments/return type org.gradle.api.internal.tasks.TaskDependencyInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (DefaultTask.java:279)
+Method <org.gradle.api.DefaultTask.getInputs()> has arguments/return type org.gradle.api.internal.TaskInputsInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (DefaultTask.java:0)
+Method <org.gradle.api.DefaultTask.getOutputs()> has arguments/return type org.gradle.api.internal.TaskOutputsInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (DefaultTask.java:0)
+Method <org.gradle.api.DefaultTask.getState()> has arguments/return type org.gradle.api.internal.tasks.TaskStateInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (DefaultTask.java:0)
+Method <org.gradle.api.DefaultTask.getTaskDependencies()> has arguments/return type org.gradle.api.internal.tasks.TaskDependencyInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (DefaultTask.java:0)
 Method <org.gradle.api.artifacts.repositories.ExclusiveContentRepository.forRepository(org.gradle.internal.Factory)> has arguments/return type org.gradle.internal.Factory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ExclusiveContentRepository.java:0)
 Method <org.gradle.api.plugins.Convention.getExtensionsAsDynamicObject()> has arguments/return type org.gradle.internal.metaobject.DynamicObject that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Convention.java:0)
 Method <org.gradle.api.plugins.JavaPluginConvention.getProject()> has arguments/return type org.gradle.api.internal.project.ProjectInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (JavaPluginConvention.java:0)
-Method <org.gradle.api.publish.maven.tasks.GenerateMavenPom.withCompileScopeAttributes(org.gradle.api.internal.attributes.ImmutableAttributes)> has arguments/return type org.gradle.api.internal.attributes.ImmutableAttributes that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateMavenPom.java:65)
-Method <org.gradle.api.publish.maven.tasks.GenerateMavenPom.withRuntimeScopeAttributes(org.gradle.api.internal.attributes.ImmutableAttributes)> has arguments/return type org.gradle.api.internal.attributes.ImmutableAttributes that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateMavenPom.java:70)
-Method <org.gradle.api.reporting.GenerateBuildDashboard.getInputReports()> has arguments/return type org.gradle.api.reporting.GenerateBuildDashboard$ReportState that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateBuildDashboard.java:72)
-Method <org.gradle.api.tasks.AbstractCopyTask.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.CopySpecInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractCopyTask.java:173)
-Method <org.gradle.api.tasks.Copy.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.CopySpecInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Copy.java:69)
-Method <org.gradle.api.tasks.Copy.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.DestinationRootCopySpec that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Copy.java:88)
-Method <org.gradle.api.tasks.Sync.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.CopySpecInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Sync.java:67)
-Method <org.gradle.api.tasks.Sync.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.DestinationRootCopySpec that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Sync.java:94)
-Method <org.gradle.api.tasks.ant.AntTarget.getTarget()> has arguments/return type org.apache.tools.ant.Target that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AntTarget.java:51)
-Method <org.gradle.api.tasks.ant.AntTarget.setTarget(org.apache.tools.ant.Target)> has arguments/return type org.apache.tools.ant.Target that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AntTarget.java:58)
-Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.getRenderer()> has arguments/return type org.gradle.api.tasks.diagnostics.internal.ReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractDependencyReportTask.java:54)
-Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.setRenderer(org.gradle.api.tasks.diagnostics.internal.DependencyReportRenderer)> has arguments/return type org.gradle.api.tasks.diagnostics.internal.DependencyReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractDependencyReportTask.java:61)
-Method <org.gradle.api.tasks.diagnostics.ProjectReportTask.getBuildStateRegistry()> has arguments/return type org.gradle.internal.build.BuildStateRegistry that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ProjectReportTask.java:59)
-Method <org.gradle.api.tasks.diagnostics.PropertyReportTask.getRenderer()> has arguments/return type org.gradle.api.tasks.diagnostics.internal.ReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertyReportTask.java:63)
-Method <org.gradle.api.tasks.diagnostics.PropertyReportTask.setRenderer(org.gradle.api.tasks.diagnostics.internal.PropertyReportRenderer)> has arguments/return type org.gradle.api.tasks.diagnostics.internal.PropertyReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertyReportTask.java:67)
-Method <org.gradle.api.tasks.diagnostics.TaskReportTask.getRenderer()> has arguments/return type org.gradle.api.tasks.diagnostics.internal.ReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (TaskReportTask.java:69)
-Method <org.gradle.api.tasks.diagnostics.TaskReportTask.setRenderer(org.gradle.api.tasks.diagnostics.internal.TaskReportRenderer)> has arguments/return type org.gradle.api.tasks.diagnostics.internal.TaskReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (TaskReportTask.java:76)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.getAntGroovydoc()> has arguments/return type org.gradle.api.internal.tasks.AntGroovydoc that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Groovydoc.java:198)
-Method <org.gradle.api.tasks.javadoc.Groovydoc.setAntGroovydoc(org.gradle.api.internal.tasks.AntGroovydoc)> has arguments/return type org.gradle.api.internal.tasks.AntGroovydoc that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Groovydoc.java:207)
-Method <org.gradle.api.tasks.scala.ScalaCompile.setCompiler(org.gradle.language.base.internal.compile.Compiler)> has arguments/return type org.gradle.language.base.internal.compile.Compiler that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ScalaCompile.java:112)
-Method <org.gradle.api.tasks.testing.Test.getTestFramework()> has arguments/return type org.gradle.api.internal.tasks.testing.TestFramework that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Test.java:907)
-Method <org.gradle.api.tasks.testing.Test.getTestFrameworkProperty()> has arguments/return type org.gradle.api.internal.tasks.testing.TestFramework that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Test.java:901)
-Method <org.gradle.api.tasks.testing.Test.testFramework(groovy.lang.Closure)> has arguments/return type org.gradle.api.internal.tasks.testing.TestFramework that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Test.java:912)
-Method <org.gradle.buildinit.tasks.InitBuild.getProjectLayoutRegistry()> has arguments/return type org.gradle.buildinit.plugins.internal.ProjectLayoutSetupRegistry that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (InitBuild.java:174)
+Method <org.gradle.api.publish.maven.tasks.GenerateMavenPom.withCompileScopeAttributes(org.gradle.api.internal.attributes.ImmutableAttributes)> has arguments/return type org.gradle.api.internal.attributes.ImmutableAttributes that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateMavenPom.java:0)
+Method <org.gradle.api.publish.maven.tasks.GenerateMavenPom.withRuntimeScopeAttributes(org.gradle.api.internal.attributes.ImmutableAttributes)> has arguments/return type org.gradle.api.internal.attributes.ImmutableAttributes that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateMavenPom.java:0)
+Method <org.gradle.api.reporting.GenerateBuildDashboard.getInputReports()> has arguments/return type org.gradle.api.reporting.GenerateBuildDashboard$ReportState that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateBuildDashboard.java:0)
+Method <org.gradle.api.tasks.AbstractCopyTask.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.CopySpecInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractCopyTask.java:0)
+Method <org.gradle.api.tasks.Copy.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.CopySpecInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Copy.java:0)
+Method <org.gradle.api.tasks.Copy.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.DestinationRootCopySpec that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Copy.java:0)
+Method <org.gradle.api.tasks.Sync.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.CopySpecInternal that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Sync.java:0)
+Method <org.gradle.api.tasks.Sync.getRootSpec()> has arguments/return type org.gradle.api.internal.file.copy.DestinationRootCopySpec that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Sync.java:0)
+Method <org.gradle.api.tasks.ant.AntTarget.getTarget()> has arguments/return type org.apache.tools.ant.Target that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AntTarget.java:0)
+Method <org.gradle.api.tasks.ant.AntTarget.setTarget(org.apache.tools.ant.Target)> has arguments/return type org.apache.tools.ant.Target that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AntTarget.java:0)
+Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.getRenderer()> has arguments/return type org.gradle.api.tasks.diagnostics.internal.ReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractDependencyReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.AbstractDependencyReportTask.setRenderer(org.gradle.api.tasks.diagnostics.internal.DependencyReportRenderer)> has arguments/return type org.gradle.api.tasks.diagnostics.internal.DependencyReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractDependencyReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.ProjectReportTask.getBuildStateRegistry()> has arguments/return type org.gradle.internal.build.BuildStateRegistry that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ProjectReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.PropertyReportTask.getRenderer()> has arguments/return type org.gradle.api.tasks.diagnostics.internal.ReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertyReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.PropertyReportTask.setRenderer(org.gradle.api.tasks.diagnostics.internal.PropertyReportRenderer)> has arguments/return type org.gradle.api.tasks.diagnostics.internal.PropertyReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertyReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.TaskReportTask.getRenderer()> has arguments/return type org.gradle.api.tasks.diagnostics.internal.ReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (TaskReportTask.java:0)
+Method <org.gradle.api.tasks.diagnostics.TaskReportTask.setRenderer(org.gradle.api.tasks.diagnostics.internal.TaskReportRenderer)> has arguments/return type org.gradle.api.tasks.diagnostics.internal.TaskReportRenderer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (TaskReportTask.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.getAntGroovydoc()> has arguments/return type org.gradle.api.internal.tasks.AntGroovydoc that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.javadoc.Groovydoc.setAntGroovydoc(org.gradle.api.internal.tasks.AntGroovydoc)> has arguments/return type org.gradle.api.internal.tasks.AntGroovydoc that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Groovydoc.java:0)
+Method <org.gradle.api.tasks.scala.ScalaCompile.setCompiler(org.gradle.language.base.internal.compile.Compiler)> has arguments/return type org.gradle.language.base.internal.compile.Compiler that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ScalaCompile.java:0)
+Method <org.gradle.api.tasks.testing.Test.getTestFramework()> has arguments/return type org.gradle.api.internal.tasks.testing.TestFramework that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.getTestFrameworkProperty()> has arguments/return type org.gradle.api.internal.tasks.testing.TestFramework that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Test.java:0)
+Method <org.gradle.api.tasks.testing.Test.testFramework(groovy.lang.Closure)> has arguments/return type org.gradle.api.internal.tasks.testing.TestFramework that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Test.java:0)
+Method <org.gradle.buildinit.tasks.InitBuild.getProjectLayoutRegistry()> has arguments/return type org.gradle.buildinit.plugins.internal.ProjectLayoutSetupRegistry that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (InitBuild.java:0)
 Method <org.gradle.external.javadoc.OptionLessJavadocOptionFileOption.write(org.gradle.external.javadoc.internal.JavadocOptionFileWriterContext)> has arguments/return type org.gradle.external.javadoc.internal.JavadocOptionFileWriterContext that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (OptionLessJavadocOptionFileOption.java:0)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.exclude(org.gradle.api.NamedDomainObjectProvider, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ConfigurationDeprecatedExtensions.kt:358)
-Method <org.gradle.kotlin.dsl.ContentFilterableExtensionsKt.filter(org.gradle.api.file.ContentFilterable, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ContentFilterableExtensions.kt:44)
-Method <org.gradle.kotlin.dsl.ContentFilterableExtensionsKt.filter(org.gradle.api.file.ContentFilterable, kotlin.reflect.KClass, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ContentFilterableExtensions.kt:90)
-Method <org.gradle.kotlin.dsl.CopySpecExtensionsKt.filter(org.gradle.api.file.CopySpec, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (CopySpecExtensions.kt:30)
-Method <org.gradle.kotlin.dsl.ExtraPropertiesExtensionsKt.invoke(org.gradle.api.plugins.ExtraPropertiesExtension, kotlin.jvm.functions.Function0)> has arguments/return type kotlin.jvm.functions.Function0 that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ExtraPropertiesExtensions.kt:86)
-Method <org.gradle.kotlin.dsl.GroovyBuilderScope$DefaultImpls.invoke(org.gradle.kotlin.dsl.GroovyBuilderScope, java.lang.String, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:248)
-Method <org.gradle.kotlin.dsl.GroovyBuilderScope$DefaultImpls.invoke(org.gradle.kotlin.dsl.GroovyBuilderScope, java.lang.String, [Lkotlin.Pair;, kotlin.jvm.functions.Function1)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:242)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.exclude(org.gradle.api.NamedDomainObjectProvider, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ContentFilterableExtensionsKt.filter(org.gradle.api.file.ContentFilterable, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ContentFilterableExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ContentFilterableExtensionsKt.filter(org.gradle.api.file.ContentFilterable, kotlin.reflect.KClass, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ContentFilterableExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.CopySpecExtensionsKt.filter(org.gradle.api.file.CopySpec, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (CopySpecExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ExtraPropertiesExtensionsKt.invoke(org.gradle.api.plugins.ExtraPropertiesExtension, kotlin.jvm.functions.Function0)> has arguments/return type kotlin.jvm.functions.Function0 that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ExtraPropertiesExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.GroovyBuilderScope$DefaultImpls.invoke(org.gradle.kotlin.dsl.GroovyBuilderScope, java.lang.String, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.GroovyBuilderScope$DefaultImpls.invoke(org.gradle.kotlin.dsl.GroovyBuilderScope, java.lang.String, [Lkotlin.Pair;, kotlin.jvm.functions.Function1)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:0)
 Method <org.gradle.kotlin.dsl.GroovyBuilderScope.invoke(java.lang.String, [Lkotlin.Pair;)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:0)
 Method <org.gradle.kotlin.dsl.GroovyBuilderScope.invoke(java.lang.String, [Lkotlin.Pair;, kotlin.jvm.functions.Function1)> has arguments/return type [Lkotlin.Pair; that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:0)
-Method <org.gradle.kotlin.dsl.GroovyInteroperabilityKt.delegateClosureOf(java.lang.Object, kotlin.jvm.functions.Function1)> has arguments/return type kotlin.Unit that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:49)
-Method <org.gradle.kotlin.dsl.KotlinClosure0.getFunction()> has arguments/return type kotlin.jvm.functions.Function0 that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:66)
-Method <org.gradle.kotlin.dsl.KotlinClosure2.getFunction()> has arguments/return type kotlin.jvm.functions.Function2 that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:111)
-Method <org.gradle.kotlin.dsl.KotlinClosure3.getFunction()> has arguments/return type kotlin.jvm.functions.Function3 that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:135)
-Method <org.gradle.kotlin.dsl.precompile.PrecompiledScriptDependenciesResolver.resolve(kotlin.script.dependencies.ScriptContents, java.util.Map, kotlin.jvm.functions.Function3, kotlin.script.dependencies.KotlinScriptExternalDependencies)> has arguments/return type kotlin.script.dependencies.KotlinScriptExternalDependencies, kotlin.script.dependencies.ScriptContents, kotlin.jvm.functions.Function3 that are not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PrecompiledScriptDependenciesResolver.kt:78)
-Method <org.gradle.language.assembler.tasks.Assemble.getOperationLoggerFactory()> has arguments/return type org.gradle.internal.operations.logging.BuildOperationLoggerFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Assemble.java:85)
-Method <org.gradle.language.base.sources.BaseLanguageSourceSet.create(java.lang.Class, java.lang.Class, org.gradle.platform.base.internal.ComponentSpecIdentifier, org.gradle.api.model.ObjectFactory)> has arguments/return type org.gradle.platform.base.internal.ComponentSpecIdentifier that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseLanguageSourceSet.java:48)
-Method <org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask.getPreCompiledHeader()> has arguments/return type org.gradle.nativeplatform.toolchain.internal.PreCompiledHeader that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractNativeSourceCompileTask.java:81)
-Method <org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask.setPreCompiledHeader(org.gradle.nativeplatform.toolchain.internal.PreCompiledHeader)> has arguments/return type org.gradle.nativeplatform.toolchain.internal.PreCompiledHeader that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractNativeSourceCompileTask.java:86)
-Method <org.gradle.language.rc.tasks.WindowsResourceCompile.getIncrementalCompilerBuilder()> has arguments/return type org.gradle.language.nativeplatform.internal.incremental.IncrementalCompilerBuilder that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (WindowsResourceCompile.java:97)
-Method <org.gradle.language.rc.tasks.WindowsResourceCompile.getOperationLoggerFactory()> has arguments/return type org.gradle.internal.operations.logging.BuildOperationLoggerFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (WindowsResourceCompile.java:102)
-Method <org.gradle.nativeplatform.tasks.CreateStaticLibrary.getOperationLoggerFactory()> has arguments/return type org.gradle.internal.operations.logging.BuildOperationLoggerFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (CreateStaticLibrary.java:94)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.create(java.lang.Class, java.lang.Class, org.gradle.platform.base.internal.ComponentSpecIdentifier, org.gradle.model.internal.core.MutableModelNode, org.gradle.model.internal.core.MutableModelNode, org.gradle.internal.reflect.Instantiator, org.gradle.model.internal.core.NamedEntityInstantiator, org.gradle.api.internal.CollectionCallbackActionDecorator, org.gradle.api.internal.collections.DomainObjectCollectionFactory)> has arguments/return type org.gradle.platform.base.internal.ComponentSpecIdentifier, org.gradle.model.internal.core.MutableModelNode, org.gradle.internal.reflect.Instantiator, org.gradle.model.internal.core.NamedEntityInstantiator, org.gradle.api.internal.CollectionCallbackActionDecorator, org.gradle.api.internal.collections.DomainObjectCollectionFactory that are not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseBinarySpec.java:83)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getBuildAbility()> has arguments/return type org.gradle.platform.base.internal.BinaryBuildAbility that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseBinarySpec.java:242)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getNamingScheme()> has arguments/return type org.gradle.platform.base.internal.BinaryNamingScheme that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseBinarySpec.java:205)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.setNamingScheme(org.gradle.platform.base.internal.BinaryNamingScheme)> has arguments/return type org.gradle.platform.base.internal.BinaryNamingScheme that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseBinarySpec.java:210)
-Method <org.gradle.plugins.ide.api.FileContentMerger.getBeforeMerged()> has arguments/return type org.gradle.internal.MutableActionSet that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (FileContentMerger.java:43)
-Method <org.gradle.plugins.ide.api.FileContentMerger.getWhenMerged()> has arguments/return type org.gradle.internal.MutableActionSet that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (FileContentMerger.java:35)
-Method <org.gradle.plugins.ide.api.FileContentMerger.setBeforeMerged(org.gradle.internal.MutableActionSet)> has arguments/return type org.gradle.internal.MutableActionSet that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (FileContentMerger.java:47)
-Method <org.gradle.plugins.ide.api.FileContentMerger.setWhenMerged(org.gradle.internal.MutableActionSet)> has arguments/return type org.gradle.internal.MutableActionSet that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (FileContentMerger.java:39)
-Method <org.gradle.plugins.ide.api.PropertiesFileContentMerger.getTransformer()> has arguments/return type org.gradle.api.internal.PropertiesTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertiesFileContentMerger.java:40)
-Method <org.gradle.plugins.ide.api.PropertiesFileContentMerger.setTransformer(org.gradle.api.internal.PropertiesTransformer)> has arguments/return type org.gradle.api.internal.PropertiesTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertiesFileContentMerger.java:44)
-Method <org.gradle.plugins.ide.api.PropertyListGeneratorTask.getPropertyListTransformer()> has arguments/return type org.gradle.api.internal.PropertyListTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertyListGeneratorTask.java:55)
-Method <org.gradle.plugins.ide.api.XmlFileContentMerger.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (XmlFileContentMerger.java:40)
-Method <org.gradle.plugins.ide.api.XmlFileContentMerger.setXmlTransformer(org.gradle.internal.xml.XmlTransformer)> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (XmlFileContentMerger.java:44)
-Method <org.gradle.plugins.ide.api.XmlGeneratorTask.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (XmlGeneratorTask.java:49)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateEclipseClasspath.java:58)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseProject.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateEclipseProject.java:60)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateEclipseWtpComponent.java:61)
-Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateEclipseWtpFacet.java:61)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getFileReferenceFactory()> has arguments/return type org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (EclipseClasspath.java:379)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getFileReferenceFactory()> has arguments/return type org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (EclipseWtpComponent.java:385)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaModule.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateIdeaModule.java:59)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.create()> has arguments/return type org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateIdeaProject.java:31)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateIdeaProject.java:56)
-Method <org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateIdeaWorkspace.java:54)
-Method <org.gradle.plugins.signing.signatory.pgp.PgpSignatory.createSignatureGenerator()> has arguments/return type org.bouncycastle.openpgp.PGPSignatureGenerator that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PgpSignatory.java:106)
-Method <org.gradle.plugins.signing.signatory.pgp.PgpSignatoryFactory.createSignatory(java.lang.String, org.bouncycastle.openpgp.PGPSecretKey, java.lang.String)> has arguments/return type org.bouncycastle.openpgp.PGPSecretKey that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PgpSignatoryFactory.java:73)
-Method <org.gradle.plugins.signing.signatory.pgp.PgpSignatoryFactory.readSecretKey(java.lang.String, java.io.File)> has arguments/return type org.bouncycastle.openpgp.PGPSecretKey that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PgpSignatoryFactory.java:121)
-Method <org.gradle.util.CollectionUtils.partition(java.lang.Iterable, org.gradle.api.specs.Spec)> has arguments/return type org.gradle.internal.Pair that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (CollectionUtils.java:656)
-Method <org.gradle.util.ConfigureUtil.configureSelf(groovy.lang.Closure, java.lang.Object, org.gradle.internal.metaobject.ConfigureDelegate)> has arguments/return type org.gradle.internal.metaobject.ConfigureDelegate that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ConfigureUtil.java:192)
-Method <org.gradle.util.GFileUtils.checksum(java.io.File, java.util.zip.Checksum)> has arguments/return type java.util.zip.Checksum that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GFileUtils.java:329)
-Method <org.gradle.util.GUtil.getOrDefault(java.lang.Object, org.gradle.internal.Factory)> has arguments/return type org.gradle.internal.Factory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GUtil.java:195)
+Method <org.gradle.kotlin.dsl.GroovyInteroperabilityKt.delegateClosureOf(java.lang.Object, kotlin.jvm.functions.Function1)> has arguments/return type kotlin.Unit that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.KotlinClosure0.getFunction()> has arguments/return type kotlin.jvm.functions.Function0 that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.KotlinClosure2.getFunction()> has arguments/return type kotlin.jvm.functions.Function2 that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.KotlinClosure3.getFunction()> has arguments/return type kotlin.jvm.functions.Function3 that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.precompile.PrecompiledScriptDependenciesResolver.resolve(kotlin.script.dependencies.ScriptContents, java.util.Map, kotlin.jvm.functions.Function3, kotlin.script.dependencies.KotlinScriptExternalDependencies)> has arguments/return type kotlin.script.dependencies.KotlinScriptExternalDependencies, kotlin.script.dependencies.ScriptContents, kotlin.jvm.functions.Function3 that are not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PrecompiledScriptDependenciesResolver.kt:0)
+Method <org.gradle.language.assembler.tasks.Assemble.getOperationLoggerFactory()> has arguments/return type org.gradle.internal.operations.logging.BuildOperationLoggerFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (Assemble.java:0)
+Method <org.gradle.language.base.sources.BaseLanguageSourceSet.create(java.lang.Class, java.lang.Class, org.gradle.platform.base.internal.ComponentSpecIdentifier, org.gradle.api.model.ObjectFactory)> has arguments/return type org.gradle.platform.base.internal.ComponentSpecIdentifier that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseLanguageSourceSet.java:0)
+Method <org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask.getPreCompiledHeader()> has arguments/return type org.gradle.nativeplatform.toolchain.internal.PreCompiledHeader that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractNativeSourceCompileTask.java:0)
+Method <org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask.setPreCompiledHeader(org.gradle.nativeplatform.toolchain.internal.PreCompiledHeader)> has arguments/return type org.gradle.nativeplatform.toolchain.internal.PreCompiledHeader that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (AbstractNativeSourceCompileTask.java:0)
+Method <org.gradle.language.rc.tasks.WindowsResourceCompile.getIncrementalCompilerBuilder()> has arguments/return type org.gradle.language.nativeplatform.internal.incremental.IncrementalCompilerBuilder that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (WindowsResourceCompile.java:0)
+Method <org.gradle.language.rc.tasks.WindowsResourceCompile.getOperationLoggerFactory()> has arguments/return type org.gradle.internal.operations.logging.BuildOperationLoggerFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (WindowsResourceCompile.java:0)
+Method <org.gradle.nativeplatform.tasks.CreateStaticLibrary.getOperationLoggerFactory()> has arguments/return type org.gradle.internal.operations.logging.BuildOperationLoggerFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (CreateStaticLibrary.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.create(java.lang.Class, java.lang.Class, org.gradle.platform.base.internal.ComponentSpecIdentifier, org.gradle.model.internal.core.MutableModelNode, org.gradle.model.internal.core.MutableModelNode, org.gradle.internal.reflect.Instantiator, org.gradle.model.internal.core.NamedEntityInstantiator, org.gradle.api.internal.CollectionCallbackActionDecorator, org.gradle.api.internal.collections.DomainObjectCollectionFactory)> has arguments/return type org.gradle.platform.base.internal.ComponentSpecIdentifier, org.gradle.model.internal.core.MutableModelNode, org.gradle.internal.reflect.Instantiator, org.gradle.model.internal.core.NamedEntityInstantiator, org.gradle.api.internal.CollectionCallbackActionDecorator, org.gradle.api.internal.collections.DomainObjectCollectionFactory that are not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getBuildAbility()> has arguments/return type org.gradle.platform.base.internal.BinaryBuildAbility that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getNamingScheme()> has arguments/return type org.gradle.platform.base.internal.BinaryNamingScheme that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.setNamingScheme(org.gradle.platform.base.internal.BinaryNamingScheme)> has arguments/return type org.gradle.platform.base.internal.BinaryNamingScheme that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (BaseBinarySpec.java:0)
+Method <org.gradle.plugins.ide.api.FileContentMerger.getBeforeMerged()> has arguments/return type org.gradle.internal.MutableActionSet that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (FileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.FileContentMerger.getWhenMerged()> has arguments/return type org.gradle.internal.MutableActionSet that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (FileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.FileContentMerger.setBeforeMerged(org.gradle.internal.MutableActionSet)> has arguments/return type org.gradle.internal.MutableActionSet that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (FileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.FileContentMerger.setWhenMerged(org.gradle.internal.MutableActionSet)> has arguments/return type org.gradle.internal.MutableActionSet that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (FileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.PropertiesFileContentMerger.getTransformer()> has arguments/return type org.gradle.api.internal.PropertiesTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertiesFileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.PropertiesFileContentMerger.setTransformer(org.gradle.api.internal.PropertiesTransformer)> has arguments/return type org.gradle.api.internal.PropertiesTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertiesFileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.PropertyListGeneratorTask.getPropertyListTransformer()> has arguments/return type org.gradle.api.internal.PropertyListTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PropertyListGeneratorTask.java:0)
+Method <org.gradle.plugins.ide.api.XmlFileContentMerger.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (XmlFileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.XmlFileContentMerger.setXmlTransformer(org.gradle.internal.xml.XmlTransformer)> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (XmlFileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.XmlGeneratorTask.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (XmlGeneratorTask.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseClasspath.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateEclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseProject.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateEclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpComponent.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateEclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.GenerateEclipseWtpFacet.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateEclipseWtpFacet.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getFileReferenceFactory()> has arguments/return type org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getFileReferenceFactory()> has arguments/return type org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaModule.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateIdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.create()> has arguments/return type org.gradle.plugins.ide.internal.generator.generator.PersistableConfigurationObject that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateIdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaProject.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateIdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.GenerateIdeaWorkspace.getXmlTransformer()> has arguments/return type org.gradle.internal.xml.XmlTransformer that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GenerateIdeaWorkspace.java:0)
+Method <org.gradle.plugins.signing.signatory.pgp.PgpSignatory.createSignatureGenerator()> has arguments/return type org.bouncycastle.openpgp.PGPSignatureGenerator that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PgpSignatory.java:0)
+Method <org.gradle.plugins.signing.signatory.pgp.PgpSignatoryFactory.createSignatory(java.lang.String, org.bouncycastle.openpgp.PGPSecretKey, java.lang.String)> has arguments/return type org.bouncycastle.openpgp.PGPSecretKey that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PgpSignatoryFactory.java:0)
+Method <org.gradle.plugins.signing.signatory.pgp.PgpSignatoryFactory.readSecretKey(java.lang.String, java.io.File)> has arguments/return type org.bouncycastle.openpgp.PGPSecretKey that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (PgpSignatoryFactory.java:0)
+Method <org.gradle.util.CollectionUtils.partition(java.lang.Iterable, org.gradle.api.specs.Spec)> has arguments/return type org.gradle.internal.Pair that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (CollectionUtils.java:0)
+Method <org.gradle.util.ConfigureUtil.configureSelf(groovy.lang.Closure, java.lang.Object, org.gradle.internal.metaobject.ConfigureDelegate)> has arguments/return type org.gradle.internal.metaobject.ConfigureDelegate that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (ConfigureUtil.java:0)
+Method <org.gradle.util.GFileUtils.checksum(java.io.File, java.util.zip.Checksum)> has arguments/return type java.util.zip.Checksum that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GFileUtils.java:0)
+Method <org.gradle.util.GUtil.getOrDefault(java.lang.Object, org.gradle.internal.Factory)> has arguments/return type org.gradle.internal.Factory that is not Gradle public API or primitive or built-in JDK classes or Groovy classes or Kotlin classes in (GUtil.java:0)
\ No newline at end of file
diff --git a/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-file-collection.txt b/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-file-collection.txt
index 8ef1a82..cf8e5a8 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-file-collection.txt
+++ b/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-file-collection.txt
@@ -1,10 +1,10 @@
-Method <org.gradle.api.plugins.quality.PmdExtension.getRuleSetFiles()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (PmdExtension.java:189)
+Method <org.gradle.api.plugins.quality.PmdExtension.getRuleSetFiles()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (PmdExtension.java:0)
 Method <org.gradle.api.tasks.SourceSet.getAnnotationProcessorPath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (SourceSet.java:0)
 Method <org.gradle.api.tasks.SourceSet.getCompileClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (SourceSet.java:0)
 Method <org.gradle.api.tasks.SourceSet.getRuntimeClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (SourceSet.java:0)
-Method <org.gradle.api.tasks.compile.CompileOptions.getAnnotationProcessorPath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CompileOptions.java:455)
-Method <org.gradle.api.tasks.compile.CompileOptions.getBootstrapClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CompileOptions.java:279)
-Method <org.gradle.api.tasks.compile.CompileOptions.getSourcepath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CompileOptions.java:433)
-Method <org.gradle.plugins.signing.SigningExtension.getConfiguration()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (SigningExtension.java:281)
+Method <org.gradle.api.tasks.compile.CompileOptions.getAnnotationProcessorPath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getBootstrapClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getSourcepath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (CompileOptions.java:0)
+Method <org.gradle.plugins.signing.SigningExtension.getConfiguration()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (SigningExtension.java:0)
 Method <org.gradle.process.JavaExecSpec.getClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaExecSpec.java:0)
 Method <org.gradle.process.JavaForkOptions.getBootstrapClasspath()> does not have raw return type assignable to org.gradle.api.file.ConfigurableFileCollection in (JavaForkOptions.java:0)
\ No newline at end of file
diff --git a/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-properties.txt b/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-properties.txt
index 63863b2..c0d8f4d 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-properties.txt
+++ b/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-properties.txt
@@ -1,12 +1,12 @@
 Method <org.gradle.api.ActionConfiguration.getParams()> does not have raw return type assignable to org.gradle.api.provider.Property in (ActionConfiguration.java:0)
-Method <org.gradle.api.AntBuilder.getAnt()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AntBuilder.java:99)
+Method <org.gradle.api.AntBuilder.getAnt()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AntBuilder.java:0)
 Method <org.gradle.api.AntBuilder.getLifecycleLogLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (AntBuilder.java:0)
 Method <org.gradle.api.AntBuilder.getProperties()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AntBuilder.java:0)
 Method <org.gradle.api.AntBuilder.getReferences()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AntBuilder.java:0)
 Method <org.gradle.api.BuildableComponentSpec.getBuildTask()> does not have raw return type assignable to org.gradle.api.provider.Property in (BuildableComponentSpec.java:0)
 Method <org.gradle.api.CheckableComponentSpec.getCheckTask()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckableComponentSpec.java:0)
-Method <org.gradle.api.InvalidActionClosureException.getArgument()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InvalidActionClosureException.java:62)
-Method <org.gradle.api.InvalidActionClosureException.getClosure()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InvalidActionClosureException.java:53)
+Method <org.gradle.api.InvalidActionClosureException.getArgument()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InvalidActionClosureException.java:0)
+Method <org.gradle.api.InvalidActionClosureException.getClosure()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InvalidActionClosureException.java:0)
 Method <org.gradle.api.Project.getAllTasks(boolean)> does not have raw return type assignable to org.gradle.api.provider.Provider in (Project.java:0)
 Method <org.gradle.api.Project.getAllprojects()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Project.java:0)
 Method <org.gradle.api.Project.getAnt()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Project.java:0)
@@ -175,23 +175,23 @@
 Method <org.gradle.api.plugins.WarPluginConvention.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (WarPluginConvention.java:0)
 Method <org.gradle.api.plugins.WarPluginConvention.getWebAppDir()> does not have raw return type assignable to org.gradle.api.provider.Provider in (WarPluginConvention.java:0)
 Method <org.gradle.api.plugins.WarPluginConvention.getWebAppDirName()> does not have raw return type assignable to org.gradle.api.provider.Property in (WarPluginConvention.java:0)
-Method <org.gradle.api.plugins.quality.CheckstyleExtension.getConfigFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:57)
-Method <org.gradle.api.plugins.quality.CheckstyleExtension.getConfigProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:89)
-Method <org.gradle.api.plugins.quality.CheckstyleExtension.getMaxErrors()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:122)
-Method <org.gradle.api.plugins.quality.CheckstyleExtension.getMaxWarnings()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:145)
-Method <org.gradle.api.plugins.quality.CheckstyleExtension.isShowViolations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:164)
-Method <org.gradle.api.plugins.quality.CodeNarcExtension.getConfigFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:69)
-Method <org.gradle.api.plugins.quality.CodeNarcExtension.getMaxPriority1Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:83)
-Method <org.gradle.api.plugins.quality.CodeNarcExtension.getMaxPriority2Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:97)
-Method <org.gradle.api.plugins.quality.CodeNarcExtension.getMaxPriority3Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:111)
-Method <org.gradle.api.plugins.quality.CodeNarcExtension.getReportFormat()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:125)
-Method <org.gradle.api.plugins.quality.CodeQualityExtension.getReportsDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeQualityExtension.java:83)
-Method <org.gradle.api.plugins.quality.CodeQualityExtension.getSourceSets()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeQualityExtension.java:51)
-Method <org.gradle.api.plugins.quality.CodeQualityExtension.getToolVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeQualityExtension.java:37)
-Method <org.gradle.api.plugins.quality.CodeQualityExtension.isIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeQualityExtension.java:67)
-Method <org.gradle.api.plugins.quality.PmdExtension.getRuleSets()> does not have raw return type assignable to org.gradle.api.provider.Property in (PmdExtension.java:69)
-Method <org.gradle.api.plugins.quality.PmdExtension.getTargetJdk()> does not have raw return type assignable to org.gradle.api.provider.Property in (PmdExtension.java:100)
-Method <org.gradle.api.plugins.quality.PmdExtension.isConsoleOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (PmdExtension.java:221)
+Method <org.gradle.api.plugins.quality.CheckstyleExtension.getConfigFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:0)
+Method <org.gradle.api.plugins.quality.CheckstyleExtension.getConfigProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:0)
+Method <org.gradle.api.plugins.quality.CheckstyleExtension.getMaxErrors()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:0)
+Method <org.gradle.api.plugins.quality.CheckstyleExtension.getMaxWarnings()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:0)
+Method <org.gradle.api.plugins.quality.CheckstyleExtension.isShowViolations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CheckstyleExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarcExtension.getConfigFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarcExtension.getMaxPriority1Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarcExtension.getMaxPriority2Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarcExtension.getMaxPriority3Violations()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarcExtension.getReportFormat()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeNarcExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeQualityExtension.getReportsDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeQualityExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeQualityExtension.getSourceSets()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeQualityExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeQualityExtension.getToolVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeQualityExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeQualityExtension.isIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (CodeQualityExtension.java:0)
+Method <org.gradle.api.plugins.quality.PmdExtension.getRuleSets()> does not have raw return type assignable to org.gradle.api.provider.Property in (PmdExtension.java:0)
+Method <org.gradle.api.plugins.quality.PmdExtension.getTargetJdk()> does not have raw return type assignable to org.gradle.api.provider.Property in (PmdExtension.java:0)
+Method <org.gradle.api.plugins.quality.PmdExtension.isConsoleOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (PmdExtension.java:0)
 Method <org.gradle.api.publish.ivy.IvyArtifact.getClassifier()> does not have raw return type assignable to org.gradle.api.provider.Property in (IvyArtifact.java:0)
 Method <org.gradle.api.publish.ivy.IvyArtifact.getConf()> does not have raw return type assignable to org.gradle.api.provider.Property in (IvyArtifact.java:0)
 Method <org.gradle.api.publish.ivy.IvyArtifact.getExtension()> does not have raw return type assignable to org.gradle.api.provider.Property in (IvyArtifact.java:0)
@@ -215,10 +215,10 @@
 Method <org.gradle.api.publish.maven.MavenPublication.getGroupId()> does not have raw return type assignable to org.gradle.api.provider.Property in (MavenPublication.java:0)
 Method <org.gradle.api.publish.maven.MavenPublication.getPom()> does not have raw return type assignable to org.gradle.api.provider.Provider in (MavenPublication.java:0)
 Method <org.gradle.api.publish.maven.MavenPublication.getVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (MavenPublication.java:0)
-Method <org.gradle.api.reporting.ReportingExtension.getApiDocTitle()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ReportingExtension.java:129)
-Method <org.gradle.api.reporting.ReportingExtension.getBaseDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (ReportingExtension.java:75)
-Method <org.gradle.api.reporting.ReportingExtension.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ReportingExtension.java:145)
-Method <org.gradle.api.resources.ResourceException.getLocation()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ResourceException.java:63)
+Method <org.gradle.api.reporting.ReportingExtension.getApiDocTitle()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ReportingExtension.java:0)
+Method <org.gradle.api.reporting.ReportingExtension.getBaseDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (ReportingExtension.java:0)
+Method <org.gradle.api.reporting.ReportingExtension.getReports()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ReportingExtension.java:0)
+Method <org.gradle.api.resources.ResourceException.getLocation()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ResourceException.java:0)
 Method <org.gradle.api.tasks.SourceSet.getAnnotationProcessorConfigurationName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceSet.java:0)
 Method <org.gradle.api.tasks.SourceSet.getApiConfigurationName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceSet.java:0)
 Method <org.gradle.api.tasks.SourceSet.getApiElementsConfigurationName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceSet.java:0)
@@ -241,67 +241,67 @@
 Method <org.gradle.api.tasks.SourceSet.getSourcesElementsConfigurationName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceSet.java:0)
 Method <org.gradle.api.tasks.SourceSet.getSourcesJarTaskName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceSet.java:0)
 Method <org.gradle.api.tasks.SourceSet.getTaskName(java.lang.String, java.lang.String)> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceSet.java:0)
-Method <org.gradle.api.tasks.SourceSet.isMain(org.gradle.api.tasks.SourceSet)> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceSet.java:400)
+Method <org.gradle.api.tasks.SourceSet.isMain(org.gradle.api.tasks.SourceSet)> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceSet.java:0)
 Method <org.gradle.api.tasks.SourceSetOutput.getResourcesDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceSetOutput.java:0)
-Method <org.gradle.api.tasks.TaskExecutionException.getTask()> does not have raw return type assignable to org.gradle.api.provider.Provider in (TaskExecutionException.java:35)
+Method <org.gradle.api.tasks.TaskExecutionException.getTask()> does not have raw return type assignable to org.gradle.api.provider.Provider in (TaskExecutionException.java:0)
 Method <org.gradle.api.tasks.VerificationTask.getIgnoreFailures()> does not have raw return type assignable to org.gradle.api.provider.Property in (VerificationTask.java:0)
-Method <org.gradle.api.tasks.compile.BaseForkOptions.getJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseForkOptions.java:83)
-Method <org.gradle.api.tasks.compile.BaseForkOptions.getMemoryInitialSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseForkOptions.java:47)
-Method <org.gradle.api.tasks.compile.BaseForkOptions.getMemoryMaximumSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseForkOptions.java:64)
-Method <org.gradle.api.tasks.compile.CompileOptions.getAllCompilerArgs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CompileOptions.java:331)
-Method <org.gradle.api.tasks.compile.CompileOptions.getAnnotationProcessorGeneratedSourcesDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:528)
-Method <org.gradle.api.tasks.compile.CompileOptions.getCompilerArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:321)
-Method <org.gradle.api.tasks.compile.CompileOptions.getCompilerArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CompileOptions.java:346)
-Method <org.gradle.api.tasks.compile.CompileOptions.getDebugOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:226)
-Method <org.gradle.api.tasks.compile.CompileOptions.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:193)
-Method <org.gradle.api.tasks.compile.CompileOptions.getExtensionDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:298)
-Method <org.gradle.api.tasks.compile.CompileOptions.getForkOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:260)
-Method <org.gradle.api.tasks.compile.CompileOptions.isDebug()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:210)
-Method <org.gradle.api.tasks.compile.CompileOptions.isDeprecation()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:160)
-Method <org.gradle.api.tasks.compile.CompileOptions.isFailOnError()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:115)
-Method <org.gradle.api.tasks.compile.CompileOptions.isFork()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:243)
-Method <org.gradle.api.tasks.compile.CompileOptions.isIncremental()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:390)
-Method <org.gradle.api.tasks.compile.CompileOptions.isListFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:145)
-Method <org.gradle.api.tasks.compile.CompileOptions.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:130)
-Method <org.gradle.api.tasks.compile.CompileOptions.isWarnings()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:175)
-Method <org.gradle.api.tasks.compile.DebugOptions.getDebugLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (DebugOptions.java:52)
-Method <org.gradle.api.tasks.compile.ForkOptions.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (ForkOptions.java:49)
-Method <org.gradle.api.tasks.compile.ForkOptions.getJavaHome()> does not have raw return type assignable to org.gradle.api.provider.Property in (ForkOptions.java:73)
-Method <org.gradle.api.tasks.compile.ForkOptions.getTempDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (ForkOptions.java:95)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getConfigurationScript()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:193)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:126)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getFileExtensions()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:331)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getForkOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:257)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getOptimizationOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:285)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getStubDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:314)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isFailOnError()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:81)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isFork()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:141)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isJavaAnnotationProcessing()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:219)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isKeepStubs()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:349)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isListFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:111)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isParameters()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:238)
-Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:96)
-Method <org.gradle.api.tasks.compile.ProviderAwareCompilerDaemonForkOptions.getAllJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ProviderAwareCompilerDaemonForkOptions.java:60)
-Method <org.gradle.api.tasks.compile.ProviderAwareCompilerDaemonForkOptions.getJvmArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ProviderAwareCompilerDaemonForkOptions.java:49)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.getAdditionalParameters()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:165)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.getBottom()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:149)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.getDocTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:89)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.getFooter()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:119)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.getHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:104)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.getTop()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:134)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.getWindowTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:74)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.isDeprecation()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:44)
-Method <org.gradle.api.tasks.scala.ScalaDocOptions.isUnchecked()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:59)
+Method <org.gradle.api.tasks.compile.BaseForkOptions.getJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseForkOptions.java:0)
+Method <org.gradle.api.tasks.compile.BaseForkOptions.getMemoryInitialSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseForkOptions.java:0)
+Method <org.gradle.api.tasks.compile.BaseForkOptions.getMemoryMaximumSize()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseForkOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getAllCompilerArgs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getAnnotationProcessorGeneratedSourcesDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getCompilerArgs()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getCompilerArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getDebugOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getExtensionDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.getForkOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.isDebug()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.isDeprecation()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.isFailOnError()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.isFork()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.isIncremental()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.isListFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.CompileOptions.isWarnings()> does not have raw return type assignable to org.gradle.api.provider.Property in (CompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.DebugOptions.getDebugLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (DebugOptions.java:0)
+Method <org.gradle.api.tasks.compile.ForkOptions.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (ForkOptions.java:0)
+Method <org.gradle.api.tasks.compile.ForkOptions.getJavaHome()> does not have raw return type assignable to org.gradle.api.provider.Property in (ForkOptions.java:0)
+Method <org.gradle.api.tasks.compile.ForkOptions.getTempDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (ForkOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getConfigurationScript()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getFileExtensions()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getForkOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getOptimizationOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.getStubDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isFailOnError()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isFork()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isJavaAnnotationProcessing()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isKeepStubs()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isListFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isParameters()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.GroovyCompileOptions.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Property in (GroovyCompileOptions.java:0)
+Method <org.gradle.api.tasks.compile.ProviderAwareCompilerDaemonForkOptions.getAllJvmArgs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ProviderAwareCompilerDaemonForkOptions.java:0)
+Method <org.gradle.api.tasks.compile.ProviderAwareCompilerDaemonForkOptions.getJvmArgumentProviders()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ProviderAwareCompilerDaemonForkOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.getAdditionalParameters()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.getBottom()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.getDocTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.getFooter()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.getHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.getTop()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.getWindowTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.isDeprecation()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
+Method <org.gradle.api.tasks.scala.ScalaDocOptions.isUnchecked()> does not have raw return type assignable to org.gradle.api.provider.Property in (ScalaDocOptions.java:0)
 Method <org.gradle.api.tasks.testing.JUnitXmlReport.isOutputPerTestCase()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitXmlReport.java:0)
 Method <org.gradle.api.tasks.testing.TestFilter.getExcludePatterns()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestFilter.java:0)
 Method <org.gradle.api.tasks.testing.TestFilter.getIncludePatterns()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestFilter.java:0)
 Method <org.gradle.api.tasks.testing.TestFilter.isFailOnNoMatchingTests()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestFilter.java:0)
-Method <org.gradle.api.tasks.testing.junit.JUnitOptions.getExcludeCategories()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitOptions.java:78)
-Method <org.gradle.api.tasks.testing.junit.JUnitOptions.getIncludeCategories()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitOptions.java:63)
-Method <org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions.getExcludeEngines()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitPlatformOptions.java:113)
-Method <org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions.getExcludeTags()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitPlatformOptions.java:126)
-Method <org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions.getIncludeEngines()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitPlatformOptions.java:99)
-Method <org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions.getIncludeTags()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitPlatformOptions.java:104)
+Method <org.gradle.api.tasks.testing.junit.JUnitOptions.getExcludeCategories()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitOptions.java:0)
+Method <org.gradle.api.tasks.testing.junit.JUnitOptions.getIncludeCategories()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitOptions.java:0)
+Method <org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions.getExcludeEngines()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitPlatformOptions.java:0)
+Method <org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions.getExcludeTags()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitPlatformOptions.java:0)
+Method <org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions.getIncludeEngines()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitPlatformOptions.java:0)
+Method <org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions.getIncludeTags()> does not have raw return type assignable to org.gradle.api.provider.Property in (JUnitPlatformOptions.java:0)
 Method <org.gradle.api.tasks.testing.logging.TestLogging.getDisplayGranularity()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestLogging.java:0)
 Method <org.gradle.api.tasks.testing.logging.TestLogging.getEvents()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestLogging.java:0)
 Method <org.gradle.api.tasks.testing.logging.TestLogging.getExceptionFormat()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestLogging.java:0)
@@ -318,68 +318,68 @@
 Method <org.gradle.api.tasks.testing.logging.TestLoggingContainer.getLifecycle()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestLoggingContainer.java:0)
 Method <org.gradle.api.tasks.testing.logging.TestLoggingContainer.getQuiet()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestLoggingContainer.java:0)
 Method <org.gradle.api.tasks.testing.logging.TestLoggingContainer.getWarn()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestLoggingContainer.java:0)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getConfigFailurePolicy()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:254)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getExcludeGroups()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:242)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getGroupByInstances()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:419)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getIncludeGroups()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:230)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getListeners()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:281)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getOutputDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:218)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getParallel()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:298)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getPreserveOrder()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:397)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuiteName()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:361)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuiteXmlBuilder()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:461)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuiteXmlFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:388)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuiteXmlWriter()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:452)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuites(java.io.File)> does not have raw return type assignable to org.gradle.api.provider.Provider in (TestNGOptions.java:147)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getTestName()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:373)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getThreadCount()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:310)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getUseDefaultListeners()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:319)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.isGroupByInstances()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:432)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.isPreserveOrder()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:410)
-Method <org.gradle.api.tasks.testing.testng.TestNGOptions.isUseDefaultListeners()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:349)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getConfigFailurePolicy()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getExcludeGroups()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getGroupByInstances()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getIncludeGroups()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getListeners()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getOutputDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getParallel()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getPreserveOrder()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuiteName()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuiteXmlBuilder()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuiteXmlFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuiteXmlWriter()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getSuites(java.io.File)> does not have raw return type assignable to org.gradle.api.provider.Provider in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getTestName()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getThreadCount()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.getUseDefaultListeners()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.isGroupByInstances()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.isPreserveOrder()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
+Method <org.gradle.api.tasks.testing.testng.TestNGOptions.isUseDefaultListeners()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestNGOptions.java:0)
 Method <org.gradle.api.tasks.util.PatternFilterable.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (PatternFilterable.java:0)
 Method <org.gradle.api.tasks.util.PatternFilterable.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (PatternFilterable.java:0)
-Method <org.gradle.api.tasks.util.PatternSet.getAsExcludeSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:173)
-Method <org.gradle.api.tasks.util.PatternSet.getAsIncludeSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:169)
-Method <org.gradle.api.tasks.util.PatternSet.getAsSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:165)
-Method <org.gradle.api.tasks.util.PatternSet.getExcludeSpecs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:226)
-Method <org.gradle.api.tasks.util.PatternSet.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (PatternSet.java:219)
-Method <org.gradle.api.tasks.util.PatternSet.getIncludeSpecs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:185)
-Method <org.gradle.api.tasks.util.PatternSet.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (PatternSet.java:178)
-Method <org.gradle.api.tasks.util.PatternSet.isCaseSensitive()> does not have raw return type assignable to org.gradle.api.provider.Property in (PatternSet.java:240)
-Method <org.gradle.api.tasks.util.PatternSet.isEmpty()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:158)
-Method <org.gradle.caching.configuration.AbstractBuildCache.isEnabled()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractBuildCache.java:33)
-Method <org.gradle.caching.configuration.AbstractBuildCache.isPush()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractBuildCache.java:49)
+Method <org.gradle.api.tasks.util.PatternSet.getAsExcludeSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:0)
+Method <org.gradle.api.tasks.util.PatternSet.getAsIncludeSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:0)
+Method <org.gradle.api.tasks.util.PatternSet.getAsSpec()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:0)
+Method <org.gradle.api.tasks.util.PatternSet.getExcludeSpecs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:0)
+Method <org.gradle.api.tasks.util.PatternSet.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (PatternSet.java:0)
+Method <org.gradle.api.tasks.util.PatternSet.getIncludeSpecs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:0)
+Method <org.gradle.api.tasks.util.PatternSet.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (PatternSet.java:0)
+Method <org.gradle.api.tasks.util.PatternSet.isCaseSensitive()> does not have raw return type assignable to org.gradle.api.provider.Property in (PatternSet.java:0)
+Method <org.gradle.api.tasks.util.PatternSet.isEmpty()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PatternSet.java:0)
+Method <org.gradle.caching.configuration.AbstractBuildCache.isEnabled()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractBuildCache.java:0)
+Method <org.gradle.caching.configuration.AbstractBuildCache.isPush()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractBuildCache.java:0)
 Method <org.gradle.caching.configuration.BuildCache.isEnabled()> does not have raw return type assignable to org.gradle.api.provider.Property in (BuildCache.java:0)
 Method <org.gradle.caching.configuration.BuildCache.isPush()> does not have raw return type assignable to org.gradle.api.provider.Property in (BuildCache.java:0)
-Method <org.gradle.caching.http.HttpBuildCache.getCredentials()> does not have raw return type assignable to org.gradle.api.provider.Provider in (HttpBuildCache.java:97)
-Method <org.gradle.caching.http.HttpBuildCache.getUrl()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCache.java:69)
-Method <org.gradle.caching.http.HttpBuildCache.isAllowInsecureProtocol()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCache.java:154)
-Method <org.gradle.caching.http.HttpBuildCache.isAllowUntrustedServer()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCache.java:123)
-Method <org.gradle.caching.http.HttpBuildCache.isUseExpectContinue()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCache.java:193)
-Method <org.gradle.caching.http.HttpBuildCacheCredentials.getPassword()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCacheCredentials.java:61)
-Method <org.gradle.caching.http.HttpBuildCacheCredentials.getUsername()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCacheCredentials.java:40)
-Method <org.gradle.caching.local.DirectoryBuildCache.getDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (DirectoryBuildCache.java:38)
-Method <org.gradle.caching.local.DirectoryBuildCache.getRemoveUnusedEntriesAfterDays()> does not have raw return type assignable to org.gradle.api.provider.Property in (DirectoryBuildCache.java:56)
+Method <org.gradle.caching.http.HttpBuildCache.getCredentials()> does not have raw return type assignable to org.gradle.api.provider.Provider in (HttpBuildCache.java:0)
+Method <org.gradle.caching.http.HttpBuildCache.getUrl()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCache.java:0)
+Method <org.gradle.caching.http.HttpBuildCache.isAllowInsecureProtocol()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCache.java:0)
+Method <org.gradle.caching.http.HttpBuildCache.isAllowUntrustedServer()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCache.java:0)
+Method <org.gradle.caching.http.HttpBuildCache.isUseExpectContinue()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCache.java:0)
+Method <org.gradle.caching.http.HttpBuildCacheCredentials.getPassword()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCacheCredentials.java:0)
+Method <org.gradle.caching.http.HttpBuildCacheCredentials.getUsername()> does not have raw return type assignable to org.gradle.api.provider.Property in (HttpBuildCacheCredentials.java:0)
+Method <org.gradle.caching.local.DirectoryBuildCache.getDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (DirectoryBuildCache.java:0)
+Method <org.gradle.caching.local.DirectoryBuildCache.getRemoveUnusedEntriesAfterDays()> does not have raw return type assignable to org.gradle.api.provider.Property in (DirectoryBuildCache.java:0)
 Method <org.gradle.concurrent.ParallelismConfiguration.getMaxWorkerCount()> does not have raw return type assignable to org.gradle.api.provider.Property in (ParallelismConfiguration.java:0)
 Method <org.gradle.concurrent.ParallelismConfiguration.isParallelProjectExecutionEnabled()> does not have raw return type assignable to org.gradle.api.provider.Property in (ParallelismConfiguration.java:0)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getBootClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:392)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:348)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getDoclet()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:245)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getDocletpath()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:280)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:554)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getExtDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:416)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getJFlags()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:605)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getLocale()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:531)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getMemberLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:191)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getModulePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:358)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getOptionFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:628)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getOutputLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:435)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getOverview()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:167)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getSource()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:308)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.getSourceNames()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:570)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.isBreakIterator()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:491)
-Method <org.gradle.external.javadoc.CoreJavadocOptions.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CoreJavadocOptions.java:451)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getBootClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getDoclet()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getDocletpath()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getExtDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getJFlags()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getLocale()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getMemberLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getModulePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getOptionFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getOutputLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getOverview()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getSource()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.getSourceNames()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.isBreakIterator()> does not have raw return type assignable to org.gradle.api.provider.Property in (CoreJavadocOptions.java:0)
+Method <org.gradle.external.javadoc.CoreJavadocOptions.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CoreJavadocOptions.java:0)
 Method <org.gradle.external.javadoc.JavadocOptionFileOption.getOption()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JavadocOptionFileOption.java:0)
 Method <org.gradle.external.javadoc.MinimalJavadocOptions.getBootClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (MinimalJavadocOptions.java:0)
 Method <org.gradle.external.javadoc.MinimalJavadocOptions.getClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (MinimalJavadocOptions.java:0)
@@ -402,97 +402,97 @@
 Method <org.gradle.external.javadoc.MinimalJavadocOptions.isBreakIterator()> does not have raw return type assignable to org.gradle.api.provider.Property in (MinimalJavadocOptions.java:0)
 Method <org.gradle.external.javadoc.MinimalJavadocOptions.isVerbose()> does not have raw return type assignable to org.gradle.api.provider.Provider in (MinimalJavadocOptions.java:0)
 Method <org.gradle.external.javadoc.OptionLessJavadocOptionFileOption.getValue()> does not have raw return type assignable to org.gradle.api.provider.Property in (OptionLessJavadocOptionFileOption.java:0)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getBottom()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:500)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getCharSet()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:948)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getDestinationDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:275)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getDocEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:971)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getDocTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:457)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getExcludeDocFilesSubDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:1097)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getFooter()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:478)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getGroups()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:668)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:432)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getHelpFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:873)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getLinks()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:533)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getLinksOffline()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:576)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getNoQualifiers()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:1118)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getStylesheetFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:894)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getTagletPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:1055)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getTaglets()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:1034)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getTags()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:1009)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getWindowTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:408)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isAuthor()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:356)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isDocFilesSubDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:1076)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isKeyWords()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:988)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isLinkSource()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:608)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoComment()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:1157)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoDeprecated()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:702)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoDeprecatedList()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:728)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoHelp()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:822)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoIndex()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:799)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoNavBar()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:848)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoSince()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:751)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoTimestamp()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:1136)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoTree()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:776)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isSerialWarn()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:916)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isSplitIndex()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:380)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isUse()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:309)
-Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:333)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getBottom()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getCharSet()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getDestinationDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getDocEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getDocTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getExcludeDocFilesSubDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getFooter()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getGroups()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getHelpFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getLinks()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getLinksOffline()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getNoQualifiers()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getStylesheetFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getTagletPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getTaglets()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getTags()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.getWindowTitle()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isAuthor()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isDocFilesSubDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isKeyWords()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isLinkSource()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoComment()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoDeprecated()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoDeprecatedList()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoHelp()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoIndex()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoNavBar()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoSince()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoTimestamp()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isNoTree()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isSerialWarn()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isSplitIndex()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isUse()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
+Method <org.gradle.external.javadoc.StandardJavadocDocletOptions.isVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (StandardJavadocDocletOptions.java:0)
 Method <org.gradle.ide.visualstudio.ConfigFile.getLocation()> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigFile.java:0)
 Method <org.gradle.ide.visualstudio.TextProvider.getText()> does not have raw return type assignable to org.gradle.api.provider.Property in (TextProvider.java:0)
-Method <org.gradle.kotlin.dsl.ClientModuleScope.getClientModule()> does not have raw return type assignable to org.gradle.api.provider.Provider in (DependencyHandlerExtensions.kt:188)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAll(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:382)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAllArtifacts(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:334)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAllDependencies(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:302)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAllDependencyConstraints(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:318)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getArtifacts(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:326)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAsPath(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:75)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAttributes(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:468)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getBuildDependencies(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:43)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getDependencies(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:294)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getDependencyConstraints(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:310)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getDescription(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:214)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getExcludeRules(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:342)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getExtendsFrom(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:174)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getFiles(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:59)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getHierarchy(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:230)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getIncoming(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:390)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getOutgoing(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:398)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getResolutionStrategy(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:139)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getResolvedConfiguration(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:278)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getSingleFile(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:51)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getState(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:155)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getTaskDependencyFromProjectDependency(org.gradle.api.NamedDomainObjectProvider, boolean, java.lang.String)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:286)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isCanBeConsumed(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:446)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isCanBeResolved(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:457)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isEmpty(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:123)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isTransitive(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:198)
-Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isVisible(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:163)
+Method <org.gradle.kotlin.dsl.ClientModuleScope.getClientModule()> does not have raw return type assignable to org.gradle.api.provider.Provider in (DependencyHandlerExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAll(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAllArtifacts(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAllDependencies(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAllDependencyConstraints(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getArtifacts(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAsPath(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getAttributes(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getBuildDependencies(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getDependencies(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getDependencyConstraints(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getDescription(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getExcludeRules(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getExtendsFrom(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getFiles(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getHierarchy(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getIncoming(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getOutgoing(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getResolutionStrategy(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getResolvedConfiguration(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getSingleFile(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getState(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.getTaskDependencyFromProjectDependency(org.gradle.api.NamedDomainObjectProvider, boolean, java.lang.String)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isCanBeConsumed(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isCanBeResolved(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isEmpty(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isTransitive(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.ConfigurationDeprecatedExtensionsKt.isVisible(org.gradle.api.NamedDomainObjectProvider)> does not have raw return type assignable to org.gradle.api.provider.Property in (ConfigurationDeprecatedExtensions.kt:0)
 Method <org.gradle.kotlin.dsl.GroovyBuilderScope.getDelegate()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:0)
-Method <org.gradle.kotlin.dsl.InitialValueExtraPropertyDelegate.getValue(java.lang.Object, kotlin.reflect.KProperty)> does not have raw return type assignable to org.gradle.api.provider.Property in (ExtraPropertiesExtensions.kt:135)
-Method <org.gradle.kotlin.dsl.KotlinBuildScript.getBuildscript()> does not have raw return type assignable to org.gradle.api.provider.Provider in (KotlinBuildScript.kt:63)
-Method <org.gradle.kotlin.dsl.KotlinClosure0.getFunction()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:66)
-Method <org.gradle.kotlin.dsl.KotlinClosure1.getFunction()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:88)
-Method <org.gradle.kotlin.dsl.KotlinClosure2.getFunction()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:111)
-Method <org.gradle.kotlin.dsl.KotlinClosure3.getFunction()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:135)
-Method <org.gradle.kotlin.dsl.PropertyStateExtensionsKt.getValue(org.gradle.api.provider.Property, java.lang.Object, kotlin.reflect.KProperty)> does not have raw return type assignable to org.gradle.api.provider.Property in (PropertyStateExtensions.kt:29)
+Method <org.gradle.kotlin.dsl.InitialValueExtraPropertyDelegate.getValue(java.lang.Object, kotlin.reflect.KProperty)> does not have raw return type assignable to org.gradle.api.provider.Property in (ExtraPropertiesExtensions.kt:0)
+Method <org.gradle.kotlin.dsl.KotlinBuildScript.getBuildscript()> does not have raw return type assignable to org.gradle.api.provider.Provider in (KotlinBuildScript.kt:0)
+Method <org.gradle.kotlin.dsl.KotlinClosure0.getFunction()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.KotlinClosure1.getFunction()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.KotlinClosure2.getFunction()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.KotlinClosure3.getFunction()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GroovyInteroperability.kt:0)
+Method <org.gradle.kotlin.dsl.PropertyStateExtensionsKt.getValue(org.gradle.api.provider.Property, java.lang.Object, kotlin.reflect.KProperty)> does not have raw return type assignable to org.gradle.api.provider.Property in (PropertyStateExtensions.kt:0)
 Method <org.gradle.language.base.DependentSourceSet.getDependencies()> does not have raw return type assignable to org.gradle.api.provider.Provider in (DependentSourceSet.java:0)
 Method <org.gradle.language.base.LanguageSourceSet.getParentName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (LanguageSourceSet.java:0)
 Method <org.gradle.language.nativeplatform.DependentSourceSet.getLibs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (DependentSourceSet.java:0)
 Method <org.gradle.language.nativeplatform.DependentSourceSet.getPreCompiledHeader()> does not have raw return type assignable to org.gradle.api.provider.Property in (DependentSourceSet.java:0)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getAdditionalParameters()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:175)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getDebugLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:119)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:143)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getForkOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:226)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getIncrementalOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:238)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getLoggingLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:200)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getLoggingPhases()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:214)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isDeprecation()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:92)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isFailOnError()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:80)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isForce()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:158)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isListFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:187)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isOptimize()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:131)
-Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isUnchecked()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:104)
-Method <org.gradle.model.InvalidModelRuleException.getDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InvalidModelRuleException.java:52)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getAdditionalParameters()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getDebugLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getEncoding()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getForkOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getIncrementalOptions()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getLoggingLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.getLoggingPhases()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isDeprecation()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isFailOnError()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isForce()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isListFiles()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isOptimize()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.language.scala.tasks.BaseScalaCompileOptions.isUnchecked()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseScalaCompileOptions.java:0)
+Method <org.gradle.model.InvalidModelRuleException.getDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Provider in (InvalidModelRuleException.java:0)
 Method <org.gradle.nativeplatform.NativeBinarySpec.getAssembler()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeBinarySpec.getBuildType()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeBinarySpec.java:0)
@@ -509,55 +509,55 @@
 Method <org.gradle.nativeplatform.NativeBinarySpec.getcCompiler()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeComponentSpec.getBaseName()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeComponentSpec.java:0)
 Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getApplication()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:0)
-Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getApplication()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:27)
+Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getApplication()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:0)
-Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:27)
+Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getInstallation()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:0)
-Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:27)
-Method <org.gradle.nativeplatform.NativeExecutableFileSpec.getFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeExecutableFileSpec.java:37)
-Method <org.gradle.nativeplatform.NativeExecutableFileSpec.getToolChain()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeExecutableFileSpec.java:49)
-Method <org.gradle.nativeplatform.NativeInstallationSpec.getDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeInstallationSpec.java:32)
+Method <org.gradle.nativeplatform.NativeExecutableBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeExecutableBinarySpec.java:0)
+Method <org.gradle.nativeplatform.NativeExecutableFileSpec.getFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeExecutableFileSpec.java:0)
+Method <org.gradle.nativeplatform.NativeExecutableFileSpec.getToolChain()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeExecutableFileSpec.java:0)
+Method <org.gradle.nativeplatform.NativeInstallationSpec.getDirectory()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeInstallationSpec.java:0)
 Method <org.gradle.nativeplatform.NativeLibraryBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibraryBinarySpec.java:0)
-Method <org.gradle.nativeplatform.NativeLibraryBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibraryBinarySpec.java:25)
+Method <org.gradle.nativeplatform.NativeLibraryBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibraryBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeLibraryBinarySpec.getLibrary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibraryBinarySpec.java:0)
-Method <org.gradle.nativeplatform.NativeLibraryBinarySpec.getLibrary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibraryBinarySpec.java:25)
+Method <org.gradle.nativeplatform.NativeLibraryBinarySpec.getLibrary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibraryBinarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeLibrarySpec.getApi()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibrarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeLibrarySpec.getShared()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibrarySpec.java:0)
 Method <org.gradle.nativeplatform.NativeLibrarySpec.getStatic()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeLibrarySpec.java:0)
 Method <org.gradle.nativeplatform.SharedLibraryBinarySpec.getSharedLibraryFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (SharedLibraryBinarySpec.java:0)
 Method <org.gradle.nativeplatform.SharedLibraryBinarySpec.getSharedLibraryLinkFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (SharedLibraryBinarySpec.java:0)
 Method <org.gradle.nativeplatform.SharedLibraryBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SharedLibraryBinarySpec.java:0)
-Method <org.gradle.nativeplatform.SharedLibraryBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SharedLibraryBinarySpec.java:28)
+Method <org.gradle.nativeplatform.SharedLibraryBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SharedLibraryBinarySpec.java:0)
 Method <org.gradle.nativeplatform.StaticLibraryBinarySpec.getStaticLibraryFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (StaticLibraryBinarySpec.java:0)
 Method <org.gradle.nativeplatform.StaticLibraryBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (StaticLibraryBinarySpec.java:0)
-Method <org.gradle.nativeplatform.StaticLibraryBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (StaticLibraryBinarySpec.java:29)
+Method <org.gradle.nativeplatform.StaticLibraryBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (StaticLibraryBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
-Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:34)
+Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getExecutableFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getInstallation()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
-Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:34)
+Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
-Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:34)
+Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTestedBinary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
-Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTestedBinary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:34)
+Method <org.gradle.nativeplatform.test.NativeTestSuiteBinarySpec.getTestedBinary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (NativeTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.NativeTestSuiteSpec.getTestedComponent()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeTestSuiteSpec.java:0)
-Method <org.gradle.nativeplatform.test.NativeTestSuiteSpec.getTestedComponent()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeTestSuiteSpec.java:27)
+Method <org.gradle.nativeplatform.test.NativeTestSuiteSpec.getTestedComponent()> does not have raw return type assignable to org.gradle.api.provider.Property in (NativeTestSuiteSpec.java:0)
 Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:0)
-Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:25)
-Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:25)
+Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:0)
+Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:0)
-Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:25)
-Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:25)
+Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:0)
+Method <org.gradle.nativeplatform.test.cunit.CUnitTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (CUnitTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:0)
-Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:25)
-Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:25)
+Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:0)
+Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:0)
-Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:25)
-Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:25)
+Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:0)
+Method <org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GoogleTestTestSuiteBinarySpec.java:0)
 Method <org.gradle.nativeplatform.toolchain.GccCommandLineToolConfiguration.getExecutable()> does not have raw return type assignable to org.gradle.api.provider.Property in (GccCommandLineToolConfiguration.java:0)
 Method <org.gradle.nativeplatform.toolchain.VisualCpp.getInstallDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (VisualCpp.java:0)
 Method <org.gradle.nativeplatform.toolchain.VisualCpp.getWindowsSdkDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (VisualCpp.java:0)
@@ -567,26 +567,26 @@
 Method <org.gradle.platform.base.BinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BinarySpec.java:0)
 Method <org.gradle.platform.base.BinarySpec.isBuildable()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BinarySpec.java:0)
 Method <org.gradle.platform.base.LibraryBinarySpec.getLibrary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (LibraryBinarySpec.java:0)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getBuildAbility()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:242)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:154)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getId()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:142)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getInputs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:185)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getNamingScheme()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseBinarySpec.java:205)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getProjectScopedName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:170)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getPublicType()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:148)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getSources()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:190)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:195)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.isBuildable()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseBinarySpec.java:180)
-Method <org.gradle.platform.base.binary.BaseBinarySpec.isLegacyBinary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:200)
-Method <org.gradle.plugin.devel.GradlePluginDevelopmentExtension.getPluginSourceSet()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GradlePluginDevelopmentExtension.java:130)
-Method <org.gradle.plugin.devel.GradlePluginDevelopmentExtension.getPlugins()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GradlePluginDevelopmentExtension.java:168)
-Method <org.gradle.plugin.devel.GradlePluginDevelopmentExtension.getTestSourceSets()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GradlePluginDevelopmentExtension.java:139)
-Method <org.gradle.plugin.devel.GradlePluginDevelopmentExtension.isAutomatedPublishing()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradlePluginDevelopmentExtension.java:185)
-Method <org.gradle.plugin.devel.PluginDeclaration.getDescription()> does not have raw return type assignable to org.gradle.api.provider.Property in (PluginDeclaration.java:94)
-Method <org.gradle.plugin.devel.PluginDeclaration.getDisplayName()> does not have raw return type assignable to org.gradle.api.provider.Property in (PluginDeclaration.java:70)
-Method <org.gradle.plugin.devel.PluginDeclaration.getId()> does not have raw return type assignable to org.gradle.api.provider.Property in (PluginDeclaration.java:46)
-Method <org.gradle.plugin.devel.PluginDeclaration.getImplementationClass()> does not have raw return type assignable to org.gradle.api.provider.Property in (PluginDeclaration.java:54)
-Method <org.gradle.plugin.devel.PluginDeclaration.getName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PluginDeclaration.java:42)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getBuildAbility()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getId()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getInputs()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getNamingScheme()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getProjectScopedName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getPublicType()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getSources()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.getTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.isBuildable()> does not have raw return type assignable to org.gradle.api.provider.Property in (BaseBinarySpec.java:0)
+Method <org.gradle.platform.base.binary.BaseBinarySpec.isLegacyBinary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseBinarySpec.java:0)
+Method <org.gradle.plugin.devel.GradlePluginDevelopmentExtension.getPluginSourceSet()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GradlePluginDevelopmentExtension.java:0)
+Method <org.gradle.plugin.devel.GradlePluginDevelopmentExtension.getPlugins()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GradlePluginDevelopmentExtension.java:0)
+Method <org.gradle.plugin.devel.GradlePluginDevelopmentExtension.getTestSourceSets()> does not have raw return type assignable to org.gradle.api.provider.Provider in (GradlePluginDevelopmentExtension.java:0)
+Method <org.gradle.plugin.devel.GradlePluginDevelopmentExtension.isAutomatedPublishing()> does not have raw return type assignable to org.gradle.api.provider.Property in (GradlePluginDevelopmentExtension.java:0)
+Method <org.gradle.plugin.devel.PluginDeclaration.getDescription()> does not have raw return type assignable to org.gradle.api.provider.Property in (PluginDeclaration.java:0)
+Method <org.gradle.plugin.devel.PluginDeclaration.getDisplayName()> does not have raw return type assignable to org.gradle.api.provider.Property in (PluginDeclaration.java:0)
+Method <org.gradle.plugin.devel.PluginDeclaration.getId()> does not have raw return type assignable to org.gradle.api.provider.Property in (PluginDeclaration.java:0)
+Method <org.gradle.plugin.devel.PluginDeclaration.getImplementationClass()> does not have raw return type assignable to org.gradle.api.provider.Property in (PluginDeclaration.java:0)
+Method <org.gradle.plugin.devel.PluginDeclaration.getName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (PluginDeclaration.java:0)
 Method <org.gradle.plugins.ear.EarPluginConvention.getAppDirName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EarPluginConvention.java:0)
 Method <org.gradle.plugins.ear.EarPluginConvention.getDeploymentDescriptor()> does not have raw return type assignable to org.gradle.api.provider.Property in (EarPluginConvention.java:0)
 Method <org.gradle.plugins.ear.EarPluginConvention.getLibDirName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EarPluginConvention.java:0)
@@ -605,97 +605,97 @@
 Method <org.gradle.plugins.ear.descriptor.EarSecurityRole.getDescription()> does not have raw return type assignable to org.gradle.api.provider.Property in (EarSecurityRole.java:0)
 Method <org.gradle.plugins.ear.descriptor.EarSecurityRole.getRoleName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EarSecurityRole.java:0)
 Method <org.gradle.plugins.ear.descriptor.EarWebModule.getContextRoot()> does not have raw return type assignable to org.gradle.api.provider.Property in (EarWebModule.java:0)
-Method <org.gradle.plugins.ide.api.FileContentMerger.getBeforeMerged()> does not have raw return type assignable to org.gradle.api.provider.Property in (FileContentMerger.java:43)
-Method <org.gradle.plugins.ide.api.FileContentMerger.getWhenMerged()> does not have raw return type assignable to org.gradle.api.provider.Property in (FileContentMerger.java:35)
-Method <org.gradle.plugins.ide.api.PropertiesFileContentMerger.getTransformer()> does not have raw return type assignable to org.gradle.api.provider.Property in (PropertiesFileContentMerger.java:40)
-Method <org.gradle.plugins.ide.api.XmlFileContentMerger.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Property in (XmlFileContentMerger.java:40)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.getAccessRules()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractClasspathEntry.java:90)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.getEntryAttributes()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractClasspathEntry.java:98)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.getNativeLibraryLocation()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractClasspathEntry.java:102)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.getPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractClasspathEntry.java:74)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.isExported()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractClasspathEntry.java:82)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractLibrary.getJavadocPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractLibrary.java:59)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractLibrary.getLibrary()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractLibrary.java:73)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractLibrary.getModuleVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractLibrary.java:83)
-Method <org.gradle.plugins.ide.eclipse.model.AbstractLibrary.getSourcePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractLibrary.java:51)
-Method <org.gradle.plugins.ide.eclipse.model.AccessRule.getKind()> does not have raw return type assignable to org.gradle.api.provider.Property in (AccessRule.java:35)
-Method <org.gradle.plugins.ide.eclipse.model.AccessRule.getPattern()> does not have raw return type assignable to org.gradle.api.provider.Property in (AccessRule.java:43)
-Method <org.gradle.plugins.ide.eclipse.model.BuildCommand.getArguments()> does not have raw return type assignable to org.gradle.api.provider.Property in (BuildCommand.java:51)
-Method <org.gradle.plugins.ide.eclipse.model.BuildCommand.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (BuildCommand.java:43)
-Method <org.gradle.plugins.ide.eclipse.model.Classpath.getEntries()> does not have raw return type assignable to org.gradle.api.provider.Property in (Classpath.java:55)
-Method <org.gradle.plugins.ide.eclipse.model.Container.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Container.java:36)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getClassFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:302)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getContainers()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:228)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getDefaultOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:241)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:278)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getFileReferenceFactory()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseClasspath.java:379)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getMinusConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:215)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getPathVariables()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:286)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getPlusConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:202)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseClasspath.java:310)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getSourceSets()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:189)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.isDownloadJavadoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:267)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.isDownloadSources()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:254)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.isProjectDependenciesOnly()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:294)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseJdt.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseJdt.java:147)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseJdt.getJavaRuntimeName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseJdt.java:136)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseJdt.getSourceCompatibility()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseJdt.java:86)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseJdt.getTargetCompatibility()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseJdt.java:111)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getAutoBuildTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseModel.java:284)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseModel.java:133)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getJdt()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseModel.java:146)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseModel.java:115)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getSynchronizationTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseModel.java:260)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getWtp()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseModel.java:159)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getBuildCommands()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:249)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getComment()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:185)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseProject.java:371)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getLinkedResources()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:285)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:157)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getNatures()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:227)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getReferencedProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:199)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getResourceFilters()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseProject.java:316)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtp.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtp.java:80)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtp.getFacet()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtp.java:113)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getClassesDeployPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:351)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getContextPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:338)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getDeployName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:272)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseWtpComponent.java:170)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getFileReferenceFactory()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseWtpComponent.java:385)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getLibConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:232)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getLibDeployPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:364)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getMinusConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:259)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getPathVariables()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:377)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getPlusConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:243)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseWtpComponent.java:163)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:314)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getResources()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:290)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getRootConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:219)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getSourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:206)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet.getFacets()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpFacet.java:131)
-Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseWtpFacet.java:96)
-Method <org.gradle.plugins.ide.eclipse.model.Facet.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Facet.java:77)
-Method <org.gradle.plugins.ide.eclipse.model.Facet.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Facet.java:69)
-Method <org.gradle.plugins.ide.eclipse.model.Facet.getVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (Facet.java:85)
-Method <org.gradle.plugins.ide.eclipse.model.Library.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Library.java:37)
-Method <org.gradle.plugins.ide.eclipse.model.Link.getLocation()> does not have raw return type assignable to org.gradle.api.provider.Property in (Link.java:63)
-Method <org.gradle.plugins.ide.eclipse.model.Link.getLocationUri()> does not have raw return type assignable to org.gradle.api.provider.Property in (Link.java:71)
-Method <org.gradle.plugins.ide.eclipse.model.Link.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Link.java:47)
-Method <org.gradle.plugins.ide.eclipse.model.Link.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Link.java:55)
-Method <org.gradle.plugins.ide.eclipse.model.Output.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Output.java:53)
-Method <org.gradle.plugins.ide.eclipse.model.Output.getPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (Output.java:44)
-Method <org.gradle.plugins.ide.eclipse.model.Project.getBuildCommands()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:114)
-Method <org.gradle.plugins.ide.eclipse.model.Project.getComment()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:81)
-Method <org.gradle.plugins.ide.eclipse.model.Project.getLinkedResources()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:125)
-Method <org.gradle.plugins.ide.eclipse.model.Project.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:70)
-Method <org.gradle.plugins.ide.eclipse.model.Project.getNatures()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:103)
-Method <org.gradle.plugins.ide.eclipse.model.Project.getReferencedProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:92)
-Method <org.gradle.plugins.ide.eclipse.model.Project.getResourceFilters()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:138)
-Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getBuildDependencies()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ProjectDependency.java:119)
-Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ProjectDependency.java:160)
-Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getPublication()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectDependency.java:57)
-Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getPublicationJavadocPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectDependency.java:96)
-Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getPublicationSourcePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectDependency.java:76)
+Method <org.gradle.plugins.ide.api.FileContentMerger.getBeforeMerged()> does not have raw return type assignable to org.gradle.api.provider.Property in (FileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.FileContentMerger.getWhenMerged()> does not have raw return type assignable to org.gradle.api.provider.Property in (FileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.PropertiesFileContentMerger.getTransformer()> does not have raw return type assignable to org.gradle.api.provider.Property in (PropertiesFileContentMerger.java:0)
+Method <org.gradle.plugins.ide.api.XmlFileContentMerger.getXmlTransformer()> does not have raw return type assignable to org.gradle.api.provider.Property in (XmlFileContentMerger.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.getAccessRules()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractClasspathEntry.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.getEntryAttributes()> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractClasspathEntry.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.getNativeLibraryLocation()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractClasspathEntry.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.getPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractClasspathEntry.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractClasspathEntry.isExported()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractClasspathEntry.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractLibrary.getJavadocPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractLibrary.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractLibrary.getLibrary()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractLibrary.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractLibrary.getModuleVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractLibrary.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AbstractLibrary.getSourcePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractLibrary.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AccessRule.getKind()> does not have raw return type assignable to org.gradle.api.provider.Property in (AccessRule.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.AccessRule.getPattern()> does not have raw return type assignable to org.gradle.api.provider.Property in (AccessRule.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.BuildCommand.getArguments()> does not have raw return type assignable to org.gradle.api.provider.Property in (BuildCommand.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.BuildCommand.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (BuildCommand.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Classpath.getEntries()> does not have raw return type assignable to org.gradle.api.provider.Property in (Classpath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Container.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Container.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getClassFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getContainers()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getDefaultOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getFileReferenceFactory()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getMinusConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getPathVariables()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getPlusConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.getSourceSets()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.isDownloadJavadoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.isDownloadSources()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseClasspath.isProjectDependenciesOnly()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseClasspath.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseJdt.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseJdt.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseJdt.getJavaRuntimeName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseJdt.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseJdt.getSourceCompatibility()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseJdt.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseJdt.getTargetCompatibility()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseJdt.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getAutoBuildTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseModel.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseModel.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getJdt()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseModel.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseModel.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getSynchronizationTasks()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseModel.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseModel.getWtp()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseModel.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getBuildCommands()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getComment()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getLinkedResources()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getNatures()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getReferencedProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseProject.getResourceFilters()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseProject.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtp.getComponent()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtp.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtp.getFacet()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtp.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getClassesDeployPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getContextPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getDeployName()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getFileReferenceFactory()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getLibConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getLibDeployPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getMinusConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getPathVariables()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getPlusConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getProperties()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getResources()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getRootConfigurations()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent.getSourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet.getFacets()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseWtpFacet.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.EclipseWtpFacet.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (EclipseWtpFacet.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Facet.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Facet.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Facet.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Facet.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Facet.getVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (Facet.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Library.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Library.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Link.getLocation()> does not have raw return type assignable to org.gradle.api.provider.Property in (Link.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Link.getLocationUri()> does not have raw return type assignable to org.gradle.api.provider.Property in (Link.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Link.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Link.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Link.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Link.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Output.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Output.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Output.getPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (Output.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Project.getBuildCommands()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Project.getComment()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Project.getLinkedResources()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Project.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Project.getNatures()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Project.getReferencedProjects()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Project.getResourceFilters()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getBuildDependencies()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ProjectDependency.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ProjectDependency.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getPublication()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectDependency.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getPublicationJavadocPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectDependency.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.ProjectDependency.getPublicationSourcePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectDependency.java:0)
 Method <org.gradle.plugins.ide.eclipse.model.ResourceFilter.getAppliesTo()> does not have raw return type assignable to org.gradle.api.provider.Property in (ResourceFilter.java:0)
 Method <org.gradle.plugins.ide.eclipse.model.ResourceFilter.getMatcher()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ResourceFilter.java:0)
 Method <org.gradle.plugins.ide.eclipse.model.ResourceFilter.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (ResourceFilter.java:0)
@@ -703,141 +703,141 @@
 Method <org.gradle.plugins.ide.eclipse.model.ResourceFilterMatcher.getArguments()> does not have raw return type assignable to org.gradle.api.provider.Property in (ResourceFilterMatcher.java:0)
 Method <org.gradle.plugins.ide.eclipse.model.ResourceFilterMatcher.getChildren()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ResourceFilterMatcher.java:0)
 Method <org.gradle.plugins.ide.eclipse.model.ResourceFilterMatcher.getId()> does not have raw return type assignable to org.gradle.api.provider.Property in (ResourceFilterMatcher.java:0)
-Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getAbsolutePath()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceFolder.java:111)
-Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:90)
-Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:82)
-Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:74)
-Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceFolder.java:107)
-Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:98)
-Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:66)
-Method <org.gradle.plugins.ide.eclipse.model.UnresolvedLibrary.getAttemptedSelector()> does not have raw return type assignable to org.gradle.api.provider.Property in (UnresolvedLibrary.java:38)
-Method <org.gradle.plugins.ide.eclipse.model.Variable.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Variable.java:37)
-Method <org.gradle.plugins.ide.eclipse.model.WbDependentModule.getDeployPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbDependentModule.java:45)
-Method <org.gradle.plugins.ide.eclipse.model.WbDependentModule.getArchiveName()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbDependentModule.java:45)
-Method <org.gradle.plugins.ide.eclipse.model.WbDependentModule.getHandle()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbDependentModule.java:53)
-Method <org.gradle.plugins.ide.eclipse.model.WbProperty.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbProperty.java:43)
-Method <org.gradle.plugins.ide.eclipse.model.WbProperty.getValue()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbProperty.java:51)
-Method <org.gradle.plugins.ide.eclipse.model.WbResource.getDeployPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbResource.java:46)
-Method <org.gradle.plugins.ide.eclipse.model.WbResource.getSourcePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbResource.java:54)
-Method <org.gradle.plugins.ide.eclipse.model.WtpComponent.getContextPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (WtpComponent.java:65)
-Method <org.gradle.plugins.ide.eclipse.model.WtpComponent.getDeployName()> does not have raw return type assignable to org.gradle.api.provider.Property in (WtpComponent.java:57)
-Method <org.gradle.plugins.ide.eclipse.model.WtpComponent.getWbModuleEntries()> does not have raw return type assignable to org.gradle.api.provider.Property in (WtpComponent.java:73)
-Method <org.gradle.plugins.ide.eclipse.model.WtpFacet.getFacets()> does not have raw return type assignable to org.gradle.api.provider.Property in (WtpFacet.java:41)
+Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getAbsolutePath()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceFolder.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SourceFolder.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.SourceFolder.getOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (SourceFolder.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.UnresolvedLibrary.getAttemptedSelector()> does not have raw return type assignable to org.gradle.api.provider.Property in (UnresolvedLibrary.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.Variable.getKind()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Variable.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WbDependentModule.getArchiveName()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbDependentModule.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WbDependentModule.getDeployPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbDependentModule.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WbDependentModule.getHandle()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbDependentModule.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WbProperty.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbProperty.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WbProperty.getValue()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbProperty.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WbResource.getDeployPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbResource.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WbResource.getSourcePath()> does not have raw return type assignable to org.gradle.api.provider.Property in (WbResource.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WtpComponent.getContextPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (WtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WtpComponent.getDeployName()> does not have raw return type assignable to org.gradle.api.provider.Property in (WtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WtpComponent.getWbModuleEntries()> does not have raw return type assignable to org.gradle.api.provider.Property in (WtpComponent.java:0)
+Method <org.gradle.plugins.ide.eclipse.model.WtpFacet.getFacets()> does not have raw return type assignable to org.gradle.api.provider.Property in (WtpFacet.java:0)
 Method <org.gradle.plugins.ide.idea.model.Dependency.getScope()> does not have raw return type assignable to org.gradle.api.provider.Property in (Dependency.java:0)
-Method <org.gradle.plugins.ide.idea.model.IdeaLanguageLevel.getLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaLanguageLevel.java:37)
-Method <org.gradle.plugins.ide.idea.model.IdeaModel.getModule()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModel.java:45)
-Method <org.gradle.plugins.ide.idea.model.IdeaModel.getProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModel.java:56)
-Method <org.gradle.plugins.ide.idea.model.IdeaModel.getTargetVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModel.java:80)
-Method <org.gradle.plugins.ide.idea.model.IdeaModel.getWorkspace()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModel.java:69)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getContentRoot()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:331)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getExcludeDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:430)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getGeneratedSourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:262)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getIml()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaModule.java:541)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getInheritOutputDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:444)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getJdkName()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:502)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getLanguageLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:516)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:236)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:458)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:610)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getPathFactory()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:555)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getPathVariables()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:486)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaModule.java:551)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getResourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:378)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getScopes()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:298)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getSingleEntryLibraries()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:574)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getSourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:249)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getTargetBytecodeVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:530)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getTestOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:472)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getTestResourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:399)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.getTestSourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:349)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.isDownloadJavadoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:320)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.isDownloadSources()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:309)
-Method <org.gradle.plugins.ide.idea.model.IdeaModule.isOffline()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:566)
-Method <org.gradle.plugins.ide.idea.model.IdeaModuleIml.getGenerateTo()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModuleIml.java:43)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getDisplayName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaProject.java:149)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getIpr()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaProject.java:170)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getJdkName()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:222)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getLanguageLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:239)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getModules()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:209)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaProject.java:200)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:318)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getPathFactory()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:337)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaProject.java:163)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getProjectLibraries()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:329)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getTargetBytecodeVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:277)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getVcs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:292)
-Method <org.gradle.plugins.ide.idea.model.IdeaProject.getWildcards()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:305)
-Method <org.gradle.plugins.ide.idea.model.IdeaWorkspace.getIws()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaWorkspace.java:51)
-Method <org.gradle.plugins.ide.idea.model.JarDirectory.getPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (JarDirectory.java:38)
-Method <org.gradle.plugins.ide.idea.model.JarDirectory.isRecursive()> does not have raw return type assignable to org.gradle.api.provider.Property in (JarDirectory.java:49)
-Method <org.gradle.plugins.ide.idea.model.Jdk.getLanguageLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jdk.java:69)
-Method <org.gradle.plugins.ide.idea.model.Jdk.getProjectJdkName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jdk.java:77)
-Method <org.gradle.plugins.ide.idea.model.Jdk.isAssertKeyword()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jdk.java:53)
-Method <org.gradle.plugins.ide.idea.model.Jdk.isJdk15()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jdk.java:61)
-Method <org.gradle.plugins.ide.idea.model.Module.getContentPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:70)
-Method <org.gradle.plugins.ide.idea.model.Module.getDependencies()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:199)
-Method <org.gradle.plugins.ide.idea.model.Module.getExcludeFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:151)
-Method <org.gradle.plugins.ide.idea.model.Module.getGeneratedSourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:139)
-Method <org.gradle.plugins.ide.idea.model.Module.getJdkName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:207)
-Method <org.gradle.plugins.ide.idea.model.Module.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:175)
-Method <org.gradle.plugins.ide.idea.model.Module.getResourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:107)
-Method <org.gradle.plugins.ide.idea.model.Module.getSourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:82)
-Method <org.gradle.plugins.ide.idea.model.Module.getTestOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:187)
-Method <org.gradle.plugins.ide.idea.model.Module.getTestResourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:124)
-Method <org.gradle.plugins.ide.idea.model.Module.getTestSourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:94)
-Method <org.gradle.plugins.ide.idea.model.Module.isInheritOutputDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:163)
-Method <org.gradle.plugins.ide.idea.model.ModuleDependency.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleDependency.java:45)
-Method <org.gradle.plugins.ide.idea.model.ModuleDependency.getScope()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleDependency.java:57)
-Method <org.gradle.plugins.ide.idea.model.ModuleDependency.isExported()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleDependency.java:66)
-Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getClasses()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:55)
-Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getJarDirectories()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:66)
-Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getJavadoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:77)
-Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getScope()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:100)
-Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getSources()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:88)
-Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.isExported()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:112)
-Method <org.gradle.plugins.ide.idea.model.Project.getJdk()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:94)
-Method <org.gradle.plugins.ide.idea.model.Project.getModulePaths()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:61)
-Method <org.gradle.plugins.ide.idea.model.Project.getProjectLibraries()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:116)
-Method <org.gradle.plugins.ide.idea.model.Project.getVcs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:105)
-Method <org.gradle.plugins.ide.idea.model.Project.getWildcards()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:83)
-Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getClasses()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:76)
-Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getCompilerClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:65)
-Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getJavadoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:90)
-Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:43)
-Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getSources()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:101)
-Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:54)
-Method <org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary.getJavadocFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SingleEntryModuleLibrary.java:97)
-Method <org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary.getLibraryFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SingleEntryModuleLibrary.java:90)
-Method <org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary.getModuleVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (SingleEntryModuleLibrary.java:79)
-Method <org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary.getSourceFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SingleEntryModuleLibrary.java:108)
-Method <org.gradle.plugins.signing.SignOperation.getDisplayName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SignOperation.java:57)
-Method <org.gradle.plugins.signing.SignOperation.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignOperation.java:82)
-Method <org.gradle.plugins.signing.SignOperation.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignOperation.java:72)
-Method <org.gradle.plugins.signing.SignOperation.getSignatures()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SignOperation.java:178)
-Method <org.gradle.plugins.signing.SignOperation.getSingleSignature()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SignOperation.java:188)
-Method <org.gradle.plugins.signing.SignOperation.isRequired()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignOperation.java:92)
-Method <org.gradle.plugins.signing.Signature.getBuildDependencies()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:398)
-Method <org.gradle.plugins.signing.Signature.getClassifier()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:300)
-Method <org.gradle.plugins.signing.Signature.getDate()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:321)
-Method <org.gradle.plugins.signing.Signature.getExtension()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:248)
-Method <org.gradle.plugins.signing.Signature.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:352)
-Method <org.gradle.plugins.signing.Signature.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:218)
-Method <org.gradle.plugins.signing.Signature.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:366)
-Method <org.gradle.plugins.signing.Signature.getSignatureSpec()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:387)
-Method <org.gradle.plugins.signing.Signature.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:376)
-Method <org.gradle.plugins.signing.Signature.getToSign()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:199)
-Method <org.gradle.plugins.signing.Signature.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:274)
+Method <org.gradle.plugins.ide.idea.model.IdeaLanguageLevel.getLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaLanguageLevel.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModel.getModule()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModel.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModel.getProject()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModel.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModel.getTargetVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModel.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModel.getWorkspace()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModel.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getContentRoot()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getExcludeDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getGeneratedSourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getIml()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getInheritOutputDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getJdkName()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getLanguageLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getPathFactory()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getPathVariables()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getResourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getScopes()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getSingleEntryLibraries()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getSourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getTargetBytecodeVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getTestOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getTestResourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.getTestSourceDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.isDownloadJavadoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.isDownloadSources()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModule.isOffline()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModule.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaModuleIml.getGenerateTo()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaModuleIml.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getDisplayName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getIpr()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getJdkName()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getLanguageLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getModules()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getOutputFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getPathFactory()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getProjectLibraries()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getTargetBytecodeVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getVcs()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaProject.getWildcards()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaProject.java:0)
+Method <org.gradle.plugins.ide.idea.model.IdeaWorkspace.getIws()> does not have raw return type assignable to org.gradle.api.provider.Property in (IdeaWorkspace.java:0)
+Method <org.gradle.plugins.ide.idea.model.JarDirectory.getPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (JarDirectory.java:0)
+Method <org.gradle.plugins.ide.idea.model.JarDirectory.isRecursive()> does not have raw return type assignable to org.gradle.api.provider.Property in (JarDirectory.java:0)
+Method <org.gradle.plugins.ide.idea.model.Jdk.getLanguageLevel()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jdk.java:0)
+Method <org.gradle.plugins.ide.idea.model.Jdk.getProjectJdkName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jdk.java:0)
+Method <org.gradle.plugins.ide.idea.model.Jdk.isAssertKeyword()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jdk.java:0)
+Method <org.gradle.plugins.ide.idea.model.Jdk.isJdk15()> does not have raw return type assignable to org.gradle.api.provider.Property in (Jdk.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getContentPath()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getDependencies()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getExcludeFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getGeneratedSourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getJdkName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getResourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getSourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getTestOutputDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getTestResourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.getTestSourceFolders()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.Module.isInheritOutputDirs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Module.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleDependency.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleDependency.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleDependency.getScope()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleDependency.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleDependency.isExported()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleDependency.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getClasses()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getJarDirectories()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getJavadoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getScope()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.getSources()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ModuleLibrary.isExported()> does not have raw return type assignable to org.gradle.api.provider.Property in (ModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.Project.getJdk()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.idea.model.Project.getModulePaths()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.idea.model.Project.getProjectLibraries()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.idea.model.Project.getVcs()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.idea.model.Project.getWildcards()> does not have raw return type assignable to org.gradle.api.provider.Property in (Project.java:0)
+Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getClasses()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getCompilerClasspath()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getJavadoc()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getSources()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.ProjectLibrary.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (ProjectLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary.getJavadocFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SingleEntryModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary.getLibraryFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SingleEntryModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary.getModuleVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (SingleEntryModuleLibrary.java:0)
+Method <org.gradle.plugins.ide.idea.model.SingleEntryModuleLibrary.getSourceFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SingleEntryModuleLibrary.java:0)
+Method <org.gradle.plugins.signing.SignOperation.getDisplayName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SignOperation.java:0)
+Method <org.gradle.plugins.signing.SignOperation.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignOperation.java:0)
+Method <org.gradle.plugins.signing.SignOperation.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignOperation.java:0)
+Method <org.gradle.plugins.signing.SignOperation.getSignatures()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SignOperation.java:0)
+Method <org.gradle.plugins.signing.SignOperation.getSingleSignature()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SignOperation.java:0)
+Method <org.gradle.plugins.signing.SignOperation.isRequired()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignOperation.java:0)
+Method <org.gradle.plugins.signing.Signature.getBuildDependencies()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getClassifier()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getDate()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getExtension()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getFile()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getName()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getSignatureSpec()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getToSign()> does not have raw return type assignable to org.gradle.api.provider.Provider in (Signature.java:0)
+Method <org.gradle.plugins.signing.Signature.getType()> does not have raw return type assignable to org.gradle.api.provider.Property in (Signature.java:0)
 Method <org.gradle.plugins.signing.SignatureSpec.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignatureSpec.java:0)
 Method <org.gradle.plugins.signing.SignatureSpec.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignatureSpec.java:0)
 Method <org.gradle.plugins.signing.SignatureSpec.isRequired()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignatureSpec.java:0)
-Method <org.gradle.plugins.signing.SigningExtension.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SigningExtension.java:105)
-Method <org.gradle.plugins.signing.SigningExtension.getSignatories()> does not have raw return type assignable to org.gradle.api.provider.Property in (SigningExtension.java:525)
-Method <org.gradle.plugins.signing.SigningExtension.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SigningExtension.java:197)
-Method <org.gradle.plugins.signing.SigningExtension.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SigningExtension.java:206)
-Method <org.gradle.plugins.signing.SigningExtension.getSignatureTypes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SigningExtension.java:216)
-Method <org.gradle.plugins.signing.SigningExtension.isRequired()> does not have raw return type assignable to org.gradle.api.provider.Property in (SigningExtension.java:151)
-Method <org.gradle.plugins.signing.type.AbstractSignatureTypeProvider.getDefaultType()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractSignatureTypeProvider.java:33)
-Method <org.gradle.plugins.signing.type.AbstractSignatureTypeProvider.getTypeForExtension(java.lang.String)> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractSignatureTypeProvider.java:49)
+Method <org.gradle.plugins.signing.SigningExtension.getProject()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SigningExtension.java:0)
+Method <org.gradle.plugins.signing.SigningExtension.getSignatories()> does not have raw return type assignable to org.gradle.api.provider.Property in (SigningExtension.java:0)
+Method <org.gradle.plugins.signing.SigningExtension.getSignatory()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SigningExtension.java:0)
+Method <org.gradle.plugins.signing.SigningExtension.getSignatureType()> does not have raw return type assignable to org.gradle.api.provider.Provider in (SigningExtension.java:0)
+Method <org.gradle.plugins.signing.SigningExtension.getSignatureTypes()> does not have raw return type assignable to org.gradle.api.provider.Property in (SigningExtension.java:0)
+Method <org.gradle.plugins.signing.SigningExtension.isRequired()> does not have raw return type assignable to org.gradle.api.provider.Property in (SigningExtension.java:0)
+Method <org.gradle.plugins.signing.type.AbstractSignatureTypeProvider.getDefaultType()> does not have raw return type assignable to org.gradle.api.provider.Property in (AbstractSignatureTypeProvider.java:0)
+Method <org.gradle.plugins.signing.type.AbstractSignatureTypeProvider.getTypeForExtension(java.lang.String)> does not have raw return type assignable to org.gradle.api.provider.Provider in (AbstractSignatureTypeProvider.java:0)
 Method <org.gradle.plugins.signing.type.SignatureTypeProvider.getDefaultType()> does not have raw return type assignable to org.gradle.api.provider.Property in (SignatureTypeProvider.java:0)
 Method <org.gradle.plugins.signing.type.SignatureTypeProvider.getTypeForExtension(java.lang.String)> does not have raw return type assignable to org.gradle.api.provider.Provider in (SignatureTypeProvider.java:0)
 Method <org.gradle.process.BaseExecSpec.getCommandLine()> does not have raw return type assignable to org.gradle.api.provider.Provider in (BaseExecSpec.java:0)
@@ -866,21 +866,21 @@
 Method <org.gradle.testing.base.TestSuiteBinarySpec.getTestSuite()> does not have raw return type assignable to org.gradle.api.provider.Provider in (TestSuiteBinarySpec.java:0)
 Method <org.gradle.testing.base.TestSuiteBinarySpec.getTestedBinary()> does not have raw return type assignable to org.gradle.api.provider.Provider in (TestSuiteBinarySpec.java:0)
 Method <org.gradle.testing.base.TestSuiteSpec.getTestedComponent()> does not have raw return type assignable to org.gradle.api.provider.Property in (TestSuiteSpec.java:0)
-Method <org.gradle.testing.jacoco.plugins.JacocoPluginExtension.getToolVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoPluginExtension.java:87)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getAddress()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:233)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getAsJvmArg()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoTaskExtension.java:306)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getClassDumpDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:261)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getDestinationFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:115)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getExcludeClassLoaders()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:167)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:153)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:139)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:219)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getPort()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:245)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getSessionId()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:195)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.isDumpOnExit()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:207)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.isEnabled()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:101)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.isIncludeNoLocationClasses()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:181)
-Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.isJmx()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:280)
+Method <org.gradle.testing.jacoco.plugins.JacocoPluginExtension.getToolVersion()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoPluginExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getAddress()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getAsJvmArg()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getClassDumpDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getDestinationFile()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getExcludeClassLoaders()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getExcludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getIncludes()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getOutput()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getPort()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.getSessionId()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.isDumpOnExit()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.isEnabled()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.isIncludeNoLocationClasses()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
+Method <org.gradle.testing.jacoco.plugins.JacocoTaskExtension.isJmx()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoTaskExtension.java:0)
 Method <org.gradle.testing.jacoco.tasks.rules.JacocoLimit.getCounter()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoLimit.java:0)
 Method <org.gradle.testing.jacoco.tasks.rules.JacocoLimit.getMaximum()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoLimit.java:0)
 Method <org.gradle.testing.jacoco.tasks.rules.JacocoLimit.getMinimum()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoLimit.java:0)
@@ -892,12 +892,12 @@
 Method <org.gradle.testing.jacoco.tasks.rules.JacocoViolationRule.isEnabled()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoViolationRule.java:0)
 Method <org.gradle.testing.jacoco.tasks.rules.JacocoViolationRulesContainer.getRules()> does not have raw return type assignable to org.gradle.api.provider.Provider in (JacocoViolationRulesContainer.java:0)
 Method <org.gradle.testing.jacoco.tasks.rules.JacocoViolationRulesContainer.isFailOnViolation()> does not have raw return type assignable to org.gradle.api.provider.Property in (JacocoViolationRulesContainer.java:0)
-Method <org.gradle.testkit.runner.UnexpectedBuildResultException.getBuildResult()> does not have raw return type assignable to org.gradle.api.provider.Provider in (UnexpectedBuildResultException.java:40)
-Method <org.gradle.tooling.ListenerFailedException.getCauses()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ListenerFailedException.java:39)
+Method <org.gradle.testkit.runner.UnexpectedBuildResultException.getBuildResult()> does not have raw return type assignable to org.gradle.api.provider.Provider in (UnexpectedBuildResultException.java:0)
+Method <org.gradle.tooling.ListenerFailedException.getCauses()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ListenerFailedException.java:0)
 Method <org.gradle.tooling.model.eclipse.EclipseRuntime.getWorkspace()> does not have raw return type assignable to org.gradle.api.provider.Property in (EclipseRuntime.java:0)
-Method <org.gradle.util.ConfigureUtil$IncompleteInputException.getMissingKeys()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigureUtil.java:130)
+Method <org.gradle.util.ConfigureUtil$IncompleteInputException.getMissingKeys()> does not have raw return type assignable to org.gradle.api.provider.Provider in (ConfigureUtil.java:0)
 Method <org.gradle.vcs.VersionControlRepository.getRootDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (VersionControlRepository.java:0)
 Method <org.gradle.vcs.VersionControlSpec.getRepoName()> does not have raw return type assignable to org.gradle.api.provider.Provider in (VersionControlSpec.java:0)
 Method <org.gradle.vcs.VersionControlSpec.getRootDir()> does not have raw return type assignable to org.gradle.api.provider.Property in (VersionControlSpec.java:0)
 Method <org.gradle.vcs.VersionControlSpec.getUniqueId()> does not have raw return type assignable to org.gradle.api.provider.Provider in (VersionControlSpec.java:0)
-Method <org.gradle.vcs.git.GitVersionControlSpec.getUrl()> does not have raw return type assignable to org.gradle.api.provider.Property in (GitVersionControlSpec.java:0)
+Method <org.gradle.vcs.git.GitVersionControlSpec.getUrl()> does not have raw return type assignable to org.gradle.api.provider.Property in (GitVersionControlSpec.java:0)
\ No newline at end of file
diff --git a/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-text-resource.txt b/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-text-resource.txt
index f35c8b7..ed65ee6 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-text-resource.txt
+++ b/subprojects/architecture-test/src/changes/archunit_store/public-api-mutable-text-resource.txt
@@ -1,5 +1,5 @@
-Method <org.gradle.api.plugins.quality.CheckstyleExtension.getConfig()> has raw return type org.gradle.api.resources.TextResource in (CheckstyleExtension.java:73)
-Method <org.gradle.api.plugins.quality.CodeNarcExtension.getConfig()> has raw return type org.gradle.api.resources.TextResource in (CodeNarcExtension.java:53)
-Method <org.gradle.api.plugins.quality.PmdExtension.getRuleSetConfig()> has raw return type org.gradle.api.resources.TextResource in (PmdExtension.java:163)
+Method <org.gradle.api.plugins.quality.CheckstyleExtension.getConfig()> has raw return type org.gradle.api.resources.TextResource in (CheckstyleExtension.java:0)
+Method <org.gradle.api.plugins.quality.CodeNarcExtension.getConfig()> has raw return type org.gradle.api.resources.TextResource in (CodeNarcExtension.java:0)
+Method <org.gradle.api.plugins.quality.PmdExtension.getRuleSetConfig()> has raw return type org.gradle.api.resources.TextResource in (PmdExtension.java:0)
 Method <org.gradle.api.reporting.CustomizableHtmlReport.getStylesheet()> has raw return type org.gradle.api.resources.TextResource in (CustomizableHtmlReport.java:0)
 Method <org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator.getTemplate()> has raw return type org.gradle.api.resources.TextResource in (TemplateBasedScriptGenerator.java:0)
\ No newline at end of file
diff --git a/subprojects/architecture-test/src/changes/archunit_store/public-api-not-extends-internal-types.txt b/subprojects/architecture-test/src/changes/archunit_store/public-api-not-extends-internal-types.txt
index 333b750..3faa81c 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/public-api-not-extends-internal-types.txt
+++ b/subprojects/architecture-test/src/changes/archunit_store/public-api-not-extends-internal-types.txt
@@ -33,6 +33,8 @@
 Class <org.gradle.kotlin.dsl.KotlinProjectScriptTemplate> extends/implements org.gradle.kotlin.dsl.support.DefaultKotlinScript that is Gradle Internal API in (KotlinProjectScriptTemplate.kt:0)
 Class <org.gradle.kotlin.dsl.KotlinSettingsScriptTemplate> extends/implements org.gradle.kotlin.dsl.support.DefaultKotlinScript that is Gradle Internal API in (KotlinSettingsScriptTemplate.kt:0)
 Class <org.gradle.kotlin.dsl.NamedDomainObjectContainerScope> extends/implements org.gradle.kotlin.dsl.support.delegates.NamedDomainObjectContainerDelegate that is Gradle Internal API in (NamedDomainObjectContainerExtensions.kt:0)
+Class <org.gradle.kotlin.dsl.ProjectExtensionsKt$configure$1$c$1> extends/implements org.gradle.internal.Factory that is Gradle Internal API in (ProjectExtensions.kt:0)
+Class <org.gradle.kotlin.dsl.ProjectExtensionsKt$the$1$c$1> extends/implements org.gradle.internal.Factory that is Gradle Internal API in (ProjectExtensions.kt:0)
 Class <org.gradle.kotlin.dsl.SettingsScriptApi> extends/implements org.gradle.kotlin.dsl.support.delegates.SettingsDelegate that is Gradle Internal API in (SettingsScriptApi.kt:0)
 Class <org.gradle.kotlin.dsl.TaskContainerScope> extends/implements org.gradle.kotlin.dsl.support.delegates.TaskContainerDelegate that is Gradle Internal API in (TaskContainerExtensions.kt:0)
 Class <org.gradle.kotlin.dsl.precompile.PrecompiledProjectScript> extends/implements org.gradle.kotlin.dsl.support.delegates.ProjectDelegate that is Gradle Internal API in (PrecompiledProjectScript.kt:0)
diff --git a/subprojects/architecture-test/src/changes/archunit_store/stored.rules b/subprojects/architecture-test/src/changes/archunit_store/stored.rules
index f35a8c3..63fe7b9 100644
--- a/subprojects/architecture-test/src/changes/archunit_store/stored.rules
+++ b/subprojects/architecture-test/src/changes/archunit_store/stored.rules
@@ -1,5 +1,5 @@
 #
-#Sat Jan 07 15:55:36 CET 2023
+#Mon Apr 03 14:56:49 CEST 2023
 methods\ that\ are\ mutable\ public\ API\ properties\ and\ do\ not\ have\ raw\ return\ type\ org.gradle.api.resources.TextResource\ and\ do\ not\ have\ raw\ return\ type\ assignable\ to\ org.gradle.api.file.FileCollection\ should\ have\ return\ type\ Provider=public-api-mutable-properties.txt
 classes\ that\ are\ Gradle\ public\ API\ should\ not\ have\ direct\ super-class\ or\ interface\ that\ are\ Gradle\ Internal\ API=public-api-not-extends-internal-types.txt
 methods\ that\ are\ public\ API\ methods\ should\ have\ only\ arguments\ or\ return\ types\ that\ are\ Gradle\ public\ API\ or\ primitive\ or\ built-in\ JDK\ classes\ or\ Groovy\ classes\ or\ Kotlin\ classes=public-api-methods-return-allowed-types.txt
@@ -8,5 +8,6 @@
 methods\ that\ are\ task\ properties\ and\ do\ not\ have\ raw\ return\ type\ org.gradle.api.resources.TextResource\ and\ do\ not\ have\ raw\ return\ type\ assignable\ to\ org.gradle.api.file.FileCollection\ should\ have\ return\ type\ Provider=provider-task-properties.txt
 classes\ that\ are\ not\ Gradle\ public\ API\ should\ have\ accessors\ with\ symmetrical\ @Nullable\ annotations=internal-api-symmetrical-accessors-nullability.txt
 methods\ that\ are\ task\ properties\ and\ have\ raw\ return\ type\ assignable\ to\ org.gradle.api.file.FileCollection\ should\ have\ return\ type\ FileCollection=provider-task-file-collection.txt
+classes\ that\ are\ in\ Gradle\ internal\ API\ packages\ and\ classes\ written\ in\ Java\ or\ Groovy\ should\ be\ annotated\ (directly\ or\ via\ its\ package)\ with\ @org.gradle.api.NonNullApi=internal-api-nullability.txt
 methods\ that\ are\ task\ properties\ should\ not\ have\ raw\ return\ type\ org.gradle.api.resources.TextResource=provider-text-resource.txt
 classes\ that\ are\ Gradle\ public\ API\ should\ have\ accessors\ with\ symmetrical\ @Nullable\ annotations=public-api-symmetrical-accessors-nullability.txt
diff --git a/subprojects/architecture-test/src/test/java/com/tngtech/archunit/library/freeze/GradleViolationStoreFactory.java b/subprojects/architecture-test/src/test/java/com/tngtech/archunit/library/freeze/GradleViolationStoreFactory.java
new file mode 100644
index 0000000..51a4a38
--- /dev/null
+++ b/subprojects/architecture-test/src/test/java/com/tngtech/archunit/library/freeze/GradleViolationStoreFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 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 com.tngtech.archunit.library.freeze;
+
+/**
+ * Remove this class once ArchUnit 1.0.2 is released and use TextFileBasedViolationStore directly once it becomes public.
+ * See: https://github.com/TNG/ArchUnit/pull/1046.
+ */
+@SuppressWarnings("JavadocLinkAsPlainText")
+public class GradleViolationStoreFactory {
+    public static ViolationStore create() {
+        return new ViolationStoreFactory.TextFileBasedViolationStore();
+    }
+}
diff --git a/subprojects/architecture-test/src/test/java/org/gradle/architecture/library/freeze/LineNumberIgnoringViolationStore.java b/subprojects/architecture-test/src/test/java/org/gradle/architecture/library/freeze/LineNumberIgnoringViolationStore.java
new file mode 100644
index 0000000..1a16cea
--- /dev/null
+++ b/subprojects/architecture-test/src/test/java/org/gradle/architecture/library/freeze/LineNumberIgnoringViolationStore.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 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.architecture.library.freeze;
+
+import com.tngtech.archunit.lang.ArchRule;
+import com.tngtech.archunit.library.freeze.GradleViolationStoreFactory;
+import com.tngtech.archunit.library.freeze.ViolationStore;
+
+import java.util.List;
+import java.util.Properties;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * Changes the line numbers in all sources to 0. That way line changes are not captured when
+ * refreezing the ArchUnit tests. With that there is less noise on refreeze and changes are easier to review.
+ */
+public class LineNumberIgnoringViolationStore implements ViolationStore {
+
+    private final ViolationStore delegate;
+
+    public LineNumberIgnoringViolationStore() {
+        // We can instantiate TextFileBasedViolationStore directly once ArchUnit 1.0.2 is released,
+        // since TextFileBasedViolationStore became public type in https://github.com/TNG/ArchUnit/pull/1046.
+        delegate = GradleViolationStoreFactory.create();
+    }
+
+    @Override
+    public void initialize(Properties properties) {
+        delegate.initialize(properties);
+    }
+
+    @Override
+    public boolean contains(ArchRule rule) {
+        return delegate.contains(rule);
+    }
+
+    @Override
+    public void save(ArchRule rule, List<String> violations) {
+        List<String> violationsWithoutLineNumbers = violations.stream()
+            .map(it -> it.replaceAll("\\.(java|kt):\\d+", ".$1:0"))
+            .collect(toList());
+        delegate.save(rule, violationsWithoutLineNumbers);
+    }
+
+    @Override
+    public List<String> getViolations(ArchRule rule) {
+        return delegate.getViolations(rule);
+    }
+}
diff --git a/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ArchUnitFixture.java b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ArchUnitFixture.java
index e5c23f2..0e2184a 100644
--- a/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ArchUnitFixture.java
+++ b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ArchUnitFixture.java
@@ -36,15 +36,14 @@
 import com.tngtech.archunit.lang.ArchRule;
 import com.tngtech.archunit.lang.ConditionEvents;
 import com.tngtech.archunit.lang.SimpleConditionEvent;
+import com.tngtech.archunit.lang.conditions.ArchConditions;
 import com.tngtech.archunit.library.freeze.FreezingArchRule;
+import org.gradle.test.precondition.Requires;
+import org.gradle.test.precondition.TestPrecondition;
 import org.gradle.util.EmptyStatement;
 import org.gradle.util.Matchers;
-import org.gradle.util.PreconditionVerifier;
-import org.gradle.util.Requires;
 import org.gradle.util.SetSystemProperties;
 import org.gradle.util.TestClassLoader;
-import org.gradle.util.TestPrecondition;
-import org.gradle.util.TestPreconditionExtension;
 import org.gradle.util.UsesNativeServices;
 import org.gradle.util.UsesNativeServicesExtension;
 
@@ -74,9 +73,11 @@
 import static java.util.stream.Collectors.toSet;
 
 public interface ArchUnitFixture {
-    DescribedPredicate<JavaMember> not_written_in_kotlin = declaredIn(
-            resideOutsideOfPackages("org.gradle.configurationcache..", "org.gradle.kotlin.."))
-            .as("API written in Java");
+    DescribedPredicate<JavaClass> classes_not_written_in_kotlin = resideOutsideOfPackages("org.gradle.configurationcache..", "org.gradle.kotlin..")
+        .as("classes written in Java or Groovy");
+
+    DescribedPredicate<JavaMember> not_written_in_kotlin = declaredIn(classes_not_written_in_kotlin)
+        .as("written in Java or Groovy");
 
     DescribedPredicate<JavaMember> kotlin_internal_methods = declaredIn(gradlePublicApi())
         .and(not(not_written_in_kotlin))
@@ -103,6 +104,22 @@ static DescribedPredicate<JavaClass> gradleInternalApi() {
             .as("Gradle Internal API");
     }
 
+    static DescribedPredicate<JavaClass> inGradlePublicApiPackages() {
+        return new InGradlePublicApiPackages();
+    }
+
+    static DescribedPredicate<JavaClass> inTestFixturePackages() {
+        return resideInAnyPackage("org.gradle.test.fixtures..", "org.gradle.integtests.fixtures..", "org.gradle.architecture.test..")
+            .as("in test fixture packages");
+    }
+
+    static DescribedPredicate<JavaClass> inGradleInternalApiPackages() {
+        return resideInAnyPackage("org.gradle..")
+            .and(not(inGradlePublicApiPackages()))
+            .and(not(inTestFixturePackages()))
+            .as("in Gradle internal API packages");
+    }
+
     DescribedPredicate<JavaClass> primitive = new DescribedPredicate<JavaClass>("primitive") {
         @Override
         public boolean test(JavaClass input) {
@@ -188,6 +205,17 @@ static DescribedPredicate<JavaMember> annotatedMaybeInSupertypeWith(final Descri
         return new AnnotatedMaybeInSupertypePredicate(predicate);
     }
 
+    static ArchCondition<JavaClass> beAnnotatedOrInPackageAnnotatedWith(Class<? extends Annotation> annotationType) {
+        return ArchConditions.be(annotatedOrInPackageAnnotatedWith(annotationType));
+    }
+
+    /**
+     * Either the class is directly annotated with the given annotation type or the class is in a package that is annotated with the given annotation type.
+     */
+    static DescribedPredicate<JavaClass> annotatedOrInPackageAnnotatedWith(Class<? extends Annotation> annotationType) {
+        return new AnnotatedOrInPackageAnnotatedPredicate(annotationType);
+    }
+
     class HaveOnlyArgumentsOrReturnTypesThatAre extends ArchCondition<JavaMethod> {
         private final DescribedPredicate<JavaClass> types;
 
@@ -250,10 +278,30 @@ private void unpackJavaType(JavaType type, Set<JavaClass> referencedTypes, Set<J
         }
     }
 
-    class GradlePublicApi extends DescribedPredicate<JavaClass> {
+    class InGradlePublicApiPackages extends DescribedPredicate<JavaClass> {
         private static final PackageMatchers INCLUDES = PackageMatchers.of(parsePackageMatcher(System.getProperty("org.gradle.public.api.includes")));
         private static final PackageMatchers EXCLUDES = PackageMatchers.of(parsePackageMatcher(System.getProperty("org.gradle.public.api.excludes")));
-        private static final DescribedPredicate<JavaClass> TEST_FIXTURES = JavaClass.Predicates.belongToAnyOf(EmptyStatement.class, Matchers.class, PreconditionVerifier.class, Requires.class, SetSystemProperties.class, TestClassLoader.class, TestPrecondition.class, TestPreconditionExtension.class, UsesNativeServices.class, UsesNativeServicesExtension.class);
+
+        public InGradlePublicApiPackages() {
+            super("in Gradle public API packages");
+        }
+
+        @Override
+        public boolean test(JavaClass input) {
+            return INCLUDES.test(input.getPackageName()) && !EXCLUDES.test(input.getPackageName());
+        }
+
+        private static Set<String> parsePackageMatcher(String packageList) {
+            return Arrays.stream(packageList.split(":"))
+                .map(include -> include.replace("**/", "..").replace("/**", "..").replace("/*", "").replace("/", "."))
+                .collect(toSet());
+        }
+    }
+
+    class GradlePublicApi extends DescribedPredicate<JavaClass> {
+        private static final DescribedPredicate<JavaClass> TEST_FIXTURES = JavaClass.Predicates.belongToAnyOf(EmptyStatement.class, Matchers.class, Requires.class, SetSystemProperties.class, TestClassLoader.class, TestPrecondition.class, UsesNativeServices.class, UsesNativeServicesExtension.class);
+
+        private final InGradlePublicApiPackages packages = new InGradlePublicApiPackages();
 
         public GradlePublicApi() {
             super("Gradle public API");
@@ -261,13 +309,7 @@ public GradlePublicApi() {
 
         @Override
         public boolean test(JavaClass input) {
-            return INCLUDES.test(input.getPackageName()) && !EXCLUDES.test(input.getPackageName()) && !TEST_FIXTURES.test(input) && input.getModifiers().contains(JavaModifier.PUBLIC);
-        }
-
-        private static Set<String> parsePackageMatcher(String packageList) {
-            return Arrays.stream(packageList.split(":"))
-                .map(include -> include.replace("**/", "..").replace("/**", "..").replace("/*", "").replace("/", "."))
-                .collect(toSet());
+            return packages.test(input) && !TEST_FIXTURES.test(input) && input.getModifiers().contains(JavaModifier.PUBLIC);
         }
     }
 
@@ -341,4 +383,18 @@ private Optional<? extends JavaMember> findMatchingCallableMember(JavaClass owne
             }
         }
     }
+
+    class AnnotatedOrInPackageAnnotatedPredicate extends DescribedPredicate<JavaClass> {
+        private final Class<? extends Annotation> annotationType;
+
+        AnnotatedOrInPackageAnnotatedPredicate(Class<? extends Annotation> annotationType) {
+            super("annotated (directly or via its package) with @" + annotationType.getName());
+            this.annotationType = annotationType;
+        }
+
+        @Override
+        public boolean test(JavaClass input) {
+            return input.isAnnotatedWith(annotationType) || input.getPackage().isAnnotatedWith(annotationType);
+        }
+    }
 }
diff --git a/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ArchUnitFixtureTest.java b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ArchUnitFixtureTest.java
index 6c45658..9a7a10d 100644
--- a/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ArchUnitFixtureTest.java
+++ b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ArchUnitFixtureTest.java
@@ -23,6 +23,7 @@
 import com.tngtech.archunit.lang.ArchCondition;
 import com.tngtech.archunit.lang.ConditionEvent;
 import com.tngtech.archunit.lang.ConditionEvents;
+import org.gradle.api.NonNullApi;
 import org.gradlebuild.AbstractClass;
 import org.gradlebuild.AllowedMethodTypesClass;
 import org.gradlebuild.ConcreteClass;
@@ -135,6 +136,16 @@ public void checks_for_nullable_annotation() {
         );
     }
 
+    @Test
+    public void checks_for_annotation_presence() {
+        ArchCondition<JavaClass> condition = ArchUnitFixture.beAnnotatedOrInPackageAnnotatedWith(NonNullApi.class);
+        assertNoViolation(checkClassCondition(condition, org.gradlebuild.nonnullapi.notinpackage.OwnNonNullApi.class));
+        ConditionEvent event = checkClassCondition(condition, org.gradlebuild.nonnullapi.notinpackage.NoOwnNonNullApi.class);
+        assertTrue(event.isViolation());
+        assertThat(eventDescription(event)).startsWith("Class <org.gradlebuild.nonnullapi.notinpackage.NoOwnNonNullApi> is not annotated (directly or via its package) with @org.gradle.api.NonNullApi");
+        // Cannot test on-package (not on the class) annotation, due to `ClasFileImporter` limitations
+    }
+
     private static String eventDescription(ConditionEvent event) {
         return String.join(" ", event.getDescriptionLines());
     }
diff --git a/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/BuildOperationsApiTest.java b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/BuildOperationsApiTest.java
new file mode 100644
index 0000000..767dad5
--- /dev/null
+++ b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/BuildOperationsApiTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 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.architecture.test;
+
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import org.gradle.api.NonNullApi;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import static org.gradle.architecture.test.ArchUnitFixture.beAnnotatedOrInPackageAnnotatedWith;
+
+@AnalyzeClasses(packages = "org.gradle.operations")
+public class BuildOperationsApiTest {
+
+    @ArchTest
+    public static final ArchRule classes_in_operations_package_are_annotated_with_non_null_api =
+        classes().should(beAnnotatedOrInPackageAnnotatedWith(NonNullApi.class));
+}
diff --git a/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ConfigurationCreationTest.java b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ConfigurationCreationTest.java
new file mode 100644
index 0000000..684c9bc
--- /dev/null
+++ b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/ConfigurationCreationTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 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.architecture.test;
+
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+
+/**
+ * This test ensures that <strong>ONLY</strong> the role-based API is used to create configurations in Gradle core plugins.
+ * <p>
+ * It is important to maintain this condition, because the role-based API will allow us to (eventually) split
+ * the implementation of configurations into separate classes per role.  If new configurations are created using
+ * the existing public API, they may be used in non-idiomatic ways that will make this difficult.
+ * <p>
+ * This test will ensure that calls into {@code DefaultConfgurationContainer#doCreate(String)} in non-test code
+ * begin with a call from a method in {@code RoleBasedConfigurationContainerInternal}.
+ * (The banned alternate {@code create} methods are defined in {@code ConfigurationContainer} and
+ * implemented in {@code AbstractNamedDomainObjectContainer}.)
+ */
+@AnalyzeClasses(packages = "org.gradle")
+public final class ConfigurationCreationTest {
+    @ArchTest
+    public static final ArchRule configurations_are_created_with_role_based_api =
+            ArchRuleDefinition.noClasses()
+                    .should().callMethod("org.gradle.api.artifacts.ConfigurationContainer", "create", String.class.getName())
+                    .orShould().callMethod("org.gradle.api.artifacts.ConfigurationContainer", "maybeCreate", String.class.getName())
+                    .orShould().callMethod("org.gradle.api.artifacts.ConfigurationContainer", "create", String.class.getName(), Closure.class.getName())
+                    .orShould().callMethod("org.gradle.api.artifacts.ConfigurationContainer", "create", String.class.getName(), Action.class.getName())
+                    .because("Configurations should be created with the role-based API in org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal");
+}
diff --git a/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/InternalNullabilityTest.java b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/InternalNullabilityTest.java
new file mode 100644
index 0000000..990a91b
--- /dev/null
+++ b/subprojects/architecture-test/src/test/java/org/gradle/architecture/test/InternalNullabilityTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 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.architecture.test;
+
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+import org.gradle.api.NonNullApi;
+
+import static com.tngtech.archunit.lang.conditions.ArchPredicates.are;
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import static org.gradle.architecture.test.ArchUnitFixture.beAnnotatedOrInPackageAnnotatedWith;
+import static org.gradle.architecture.test.ArchUnitFixture.classes_not_written_in_kotlin;
+import static org.gradle.architecture.test.ArchUnitFixture.freeze;
+import static org.gradle.architecture.test.ArchUnitFixture.inGradleInternalApiPackages;
+
+@AnalyzeClasses(packages = "org.gradle")
+public class InternalNullabilityTest {
+
+    /**
+     * This test validates that all internal classes are annotated with {@link NonNullApi}.
+     * <p>
+     * The annotation can be applied on the class directly, but the preferred way is to annotate the package by adding or updating the {@code package-info.java} file.
+     * See {@code subprojects/core-api/src/main/java/org/gradle/package-info.java} for an example.
+     * <p>
+     * Note that adding the annotation for a package in one subproject will automatically apply it for the same package in all other subprojects.
+     * Therefore, it's advised to add the annotation to the package in the most appropriate subproject.
+     * For instance, if the package exists in both {@code :core} and {@code :base-services}, it should be annotated in {@code :base-services}.
+     */
+    @ArchTest
+    public static final ArchRule internal_classes_are_annotated_with_non_null_api = freeze(classes()
+        .that(are(inGradleInternalApiPackages())).and(classes_not_written_in_kotlin)
+        .should(beAnnotatedOrInPackageAnnotatedWith(NonNullApi.class)));
+
+}
diff --git a/subprojects/architecture-test/src/test/java/org/gradlebuild/nonnullapi/notinpackage/NoOwnNonNullApi.java b/subprojects/architecture-test/src/test/java/org/gradlebuild/nonnullapi/notinpackage/NoOwnNonNullApi.java
new file mode 100644
index 0000000..ef38dee
--- /dev/null
+++ b/subprojects/architecture-test/src/test/java/org/gradlebuild/nonnullapi/notinpackage/NoOwnNonNullApi.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 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.gradlebuild.nonnullapi.notinpackage;
+
+public interface NoOwnNonNullApi {
+}
diff --git a/subprojects/architecture-test/src/test/java/org/gradlebuild/nonnullapi/notinpackage/OwnNonNullApi.java b/subprojects/architecture-test/src/test/java/org/gradlebuild/nonnullapi/notinpackage/OwnNonNullApi.java
new file mode 100644
index 0000000..96e3f1e
--- /dev/null
+++ b/subprojects/architecture-test/src/test/java/org/gradlebuild/nonnullapi/notinpackage/OwnNonNullApi.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 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.gradlebuild.nonnullapi.notinpackage;
+
+import org.gradle.api.NonNullApi;
+
+@NonNullApi
+public interface OwnNonNullApi {
+}
diff --git a/subprojects/architecture-test/src/test/resources/archunit.properties b/subprojects/architecture-test/src/test/resources/archunit.properties
new file mode 100644
index 0000000..785a18f
--- /dev/null
+++ b/subprojects/architecture-test/src/test/resources/archunit.properties
@@ -0,0 +1,18 @@
+#
+# Copyright 2023 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.
+#
+
+# Changes the line number in all sources to 0, so line numbers are ignored and changes are easier to review
+freeze.store=org.gradle.architecture.library.freeze.LineNumberIgnoringViolationStore
diff --git a/subprojects/base-services-groovy/src/main/java/org/gradle/groovy/scripts/internal/AstUtils.java b/subprojects/base-services-groovy/src/main/java/org/gradle/groovy/scripts/internal/AstUtils.java
index 58e5159..5805008 100644
--- a/subprojects/base-services-groovy/src/main/java/org/gradle/groovy/scripts/internal/AstUtils.java
+++ b/subprojects/base-services-groovy/src/main/java/org/gradle/groovy/scripts/internal/AstUtils.java
@@ -220,13 +220,16 @@ public static ConstantExpression hasSingleConstantStringArg(MethodCallExpression
 
     @Nullable
     public static ConstantExpression hasSingleConstantArgOfType(MethodCallExpression call, Class<?> type) {
-        ArgumentListExpression argumentList = (ArgumentListExpression) call.getArguments();
-        if (argumentList.getExpressions().size() == 1) {
-            Expression argumentExpression = argumentList.getExpressions().get(0);
-            if (argumentExpression instanceof ConstantExpression) {
-                ConstantExpression constantArgumentExpression = (ConstantExpression) argumentExpression;
-                if (isOfType(constantArgumentExpression, type)) {
-                    return constantArgumentExpression;
+        Expression arguments = call.getArguments();
+        if (arguments instanceof ArgumentListExpression) {
+            ArgumentListExpression argumentList = (ArgumentListExpression) arguments;
+            if (argumentList.getExpressions().size() == 1) {
+                Expression argumentExpression = argumentList.getExpressions().get(0);
+                if (argumentExpression instanceof ConstantExpression) {
+                    ConstantExpression constantArgumentExpression = (ConstantExpression) argumentExpression;
+                    if (isOfType(constantArgumentExpression, type)) {
+                        return constantArgumentExpression;
+                    }
                 }
             }
         }
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java b/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java
index 5718233..46b5a96 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/JavaVersion.java
@@ -94,11 +94,9 @@ public enum JavaVersion {
 
     /**
      * Java 20 major version.
-     * Not officially supported by Gradle. Use at your own risk.
      *
      * @since 7.0
      */
-    @Incubating
     VERSION_20,
 
     /**
diff --git a/subprojects/base-services/src/main/java/org/gradle/api/specs/package-info.java b/subprojects/base-services/src/main/java/org/gradle/api/specs/package-info.java
index a5cc802..07fa082 100644
--- a/subprojects/base-services/src/main/java/org/gradle/api/specs/package-info.java
+++ b/subprojects/base-services/src/main/java/org/gradle/api/specs/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Classes for defining general purpose criteria.
  */
-package org.gradle.api.specs;
\ No newline at end of file
+package org.gradle.api.specs;
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/agents/AgentControl.java b/subprojects/base-services/src/main/java/org/gradle/internal/agents/AgentControl.java
deleted file mode 100644
index 54b7718..0000000
--- a/subprojects/base-services/src/main/java/org/gradle/internal/agents/AgentControl.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2022 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.agents;
-
-import org.gradle.internal.UncheckedException;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.Nullable;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Provides methods to interact with the Java agent shipped with Gradle. Because of the different class loaders, it is hard to query the Agent class directly.
- * <p>
- * The agent class must follow a special protocol to be recognized properly: the class should have a method:
- * <pre>
- *     public static boolean isApplied()
- * </pre>
- * that returns if the agent is applied, i.e. if one of its {@code premain} or {@code agentmain} entry methods was called.
- * <p>
- * It is possible to have an agent in the classpath without actually applying it, so checking for the availability of the class is not enough.
- */
-public class AgentControl {
-    private static final String INSTRUMENTATION_AGENT_CLASS_NAME = "org.gradle.instrumentation.agent.Agent";
-
-    /**
-     * Checks if the instrumentation agent class is applied to the current JVM.
-     *
-     * @return {@code true} if the agent was applied
-     */
-    public static boolean isInstrumentationAgentApplied() {
-        Method isAppliedMethod = findAgentMethod(INSTRUMENTATION_AGENT_CLASS_NAME, "isApplied");
-        if (isAppliedMethod == null) {
-            return false;
-        }
-        return callStaticAgentMethod(isAppliedMethod);
-    }
-
-    public static boolean installTransformer(ClassFileTransformer transformer) {
-        Method installTransformer = findAgentMethod(INSTRUMENTATION_AGENT_CLASS_NAME, "installTransformer", ClassFileTransformer.class);
-        if (installTransformer == null) {
-            return false;
-        }
-        return callStaticAgentMethod(installTransformer, transformer);
-    }
-
-    @Nullable
-    private static Class<?> tryLoadAgentClass(String agentClassName) {
-        try {
-            return ClassLoader.getSystemClassLoader().loadClass(agentClassName);
-        } catch (ClassNotFoundException e) {
-            // This typically means that the agent is not loaded at all.
-            // For now, this happens when running in a no-daemon mode, or when the Gradle distribution is not available.
-            LoggerFactory.getLogger(AgentControl.class).debug("Agent {} is not loaded", agentClassName);
-        }
-        return null;
-    }
-
-    @Nullable
-    private static Method findAgentMethod(String agentClassName, String methodName, Class<?>... args) {
-        Class<?> agentClass = tryLoadAgentClass(agentClassName);
-        if (agentClass == null) {
-            return null;
-        }
-        try {
-            Method method = agentClass.getMethod(methodName, args);
-            method.setAccessible(true);
-            return method;
-        } catch (NoSuchMethodException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> T callStaticAgentMethod(Method method, Object... args) {
-        try {
-            return (T) method.invoke(null, args);
-        } catch (IllegalAccessException e) {
-            throw new IllegalArgumentException(e);
-        } catch (InvocationTargetException e) {
-            throw UncheckedException.unwrapAndRethrow(e);
-        }
-    }
-}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/agents/DefaultClassFileTransformer.java b/subprojects/base-services/src/main/java/org/gradle/internal/agents/DefaultClassFileTransformer.java
deleted file mode 100644
index 3ce6d47..0000000
--- a/subprojects/base-services/src/main/java/org/gradle/internal/agents/DefaultClassFileTransformer.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2022 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.agents;
-
-import org.gradle.internal.UncheckedException;
-
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.reflect.Method;
-import java.security.ProtectionDomain;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class DefaultClassFileTransformer implements ClassFileTransformer {
-    private static final AtomicBoolean INSTALLED = new AtomicBoolean();
-
-    @Override
-    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
-        if (!(loader instanceof InstrumentingClassLoader)) {
-            return null;
-        }
-        InstrumentingClassLoader instrumentingLoader = (InstrumentingClassLoader) loader;
-        try {
-            return instrumentingLoader.instrumentClass(className, protectionDomain, classfileBuffer);
-        } catch (Throwable th) {
-            // Throwing exception from the ClassFileTransformer has no effect - if it happens, the class is loaded unchanged silently.
-            // This is not something we want, so we notify the class loader about this.
-            instrumentingLoader.transformFailed(className, th);
-            return null;
-        }
-    }
-
-    @SuppressWarnings("unused")  // Used reflectively
-    public static boolean tryInstall() {
-        // Installing the same transformer multiple times is very problematic, so additional correctness check is worth it.
-        if (!INSTALLED.compareAndSet(false, true)) {
-            throw new IllegalStateException("The transformer is already installed in " + DefaultClassFileTransformer.class.getClassLoader());
-        }
-        return AgentControl.installTransformer(new DefaultClassFileTransformer());
-    }
-
-    public static boolean tryInstallInClassLoader(ClassLoader classLoader) {
-        // Potentially reload this class in the provided class loader.
-        try {
-            Class<?> transformer = classLoader.loadClass(DefaultClassFileTransformer.class.getName());
-            Method tryInstall = transformer.getMethod("tryInstall");
-            tryInstall.setAccessible(true);
-            return (Boolean) tryInstall.invoke(null);
-        } catch (Exception e) {
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-    }
-}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClasspathUtil.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClasspathUtil.java
index 5bd72e0..d8fc3bc 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClasspathUtil.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/ClasspathUtil.java
@@ -133,7 +133,7 @@ public static File getClasspathForResource(URL resource, String name) {
                 String schemeSpecificPart = location.getRawSchemeSpecificPart();
                 int pos = schemeSpecificPart.indexOf("!");
                 if (pos > 0) {
-                    assert schemeSpecificPart.substring(pos + 1).equals("/" + name);
+                    assert schemeSpecificPart.substring(pos + 1).endsWith("/" + name);
                     URI jarFile = new URI(schemeSpecificPart.substring(0, pos));
                     if (jarFile.getScheme().equals("file")) {
                         return new File(jarFile.getPath());
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/TransformReplacer.java b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/TransformReplacer.java
index bf8fe82..1ec4458 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/classloader/TransformReplacer.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/classloader/TransformReplacer.java
@@ -186,10 +186,17 @@ public synchronized void close() {
             IoActions.closeQuietly(jarFile);
         }
 
+        @SuppressWarnings("Since15")
         private JarFile getJarFileLocked() throws IOException {
             ensureOpened();
             if (jarFile == null) {
-                jarFile = new JarFile(jarFilePath);
+                try {
+                    // Set up the MR-JAR properly when running on Java 9+.
+                    jarFile = new JarFile(jarFilePath, true, JarFile.OPEN_READ, JarFile.runtimeVersion());
+                } catch (NoSuchMethodError e) {
+                    // Running on Java 8, fall back to the old ways.
+                    jarFile = new JarFile(jarFilePath);
+                }
             }
             return jarFile;
         }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/AbstractDelegatingExecutorService.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/AbstractDelegatingExecutorService.java
index 47ec1d9..ad2a08f 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/AbstractDelegatingExecutorService.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/AbstractDelegatingExecutorService.java
@@ -21,10 +21,10 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 
-public class AbstractDelegatingExecutorService extends AbstractExecutorService {
-    private final ExecutorService delegate;
+public class AbstractDelegatingExecutorService<S extends ExecutorService> extends AbstractExecutorService {
+    protected final S delegate;
 
-    public AbstractDelegatingExecutorService(ExecutorService delegate) {
+    public AbstractDelegatingExecutorService(S delegate) {
         this.delegate = delegate;
     }
 
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/AbstractManagedExecutor.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/AbstractManagedExecutor.java
new file mode 100644
index 0000000..2754898
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/AbstractManagedExecutor.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2015 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.concurrent;
+
+import org.gradle.internal.UncheckedException;
+
+import javax.annotation.Nonnull;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public abstract class AbstractManagedExecutor<S extends ExecutorService> extends AbstractDelegatingExecutorService<S> implements ManagedExecutor {
+    private final ThreadLocal<Object> executing = new ThreadLocal<Object>();
+    private final ExecutorPolicy executorPolicy;
+
+    public AbstractManagedExecutor(S delegate, ExecutorPolicy executorPolicy) {
+        super(delegate);
+        this.executorPolicy = executorPolicy;
+    }
+
+    @Override
+    public void execute(@Nonnull final Runnable command) {
+        delegate.execute(trackedCommand(command));
+    }
+
+    protected Runnable trackedCommand(final Runnable command) {
+        return new Runnable() {
+            @Override
+            public void run() {
+                executing.set(command);
+                try {
+                    executorPolicy.onExecute(command);
+                } finally {
+                    executing.remove();
+                }
+            }
+        };
+    }
+
+    protected <V> Callable<V> trackedCommand(final Callable<V> command) {
+        return new Callable<V>() {
+            @Override
+            public V call() throws Exception {
+                executing.set(command);
+                try {
+                    return executorPolicy.onExecute(command);
+                } finally {
+                    executing.remove();
+                }
+            }
+        };
+    }
+
+    @Override
+    public void requestStop() {
+        delegate.shutdown();
+    }
+
+    @Override
+    public void stop() {
+        stop(Integer.MAX_VALUE, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public void stop(int timeoutValue, TimeUnit timeoutUnits) throws IllegalStateException {
+        requestStop();
+        if (executing.get() != null) {
+            throw new IllegalStateException("Cannot stop this executor from an executor thread.");
+        }
+        try {
+            if (!delegate.awaitTermination(timeoutValue, timeoutUnits)) {
+                delegate.shutdownNow();
+                throw new IllegalStateException("Timeout waiting for concurrent jobs to complete.");
+            }
+        } catch (InterruptedException e) {
+            delegate.shutdownNow();
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+        executorPolicy.onStop();
+    }
+
+    @Override
+    public void setKeepAlive(int timeout, TimeUnit timeUnit) {
+        if (delegate instanceof ThreadPoolExecutor) {
+            ((ThreadPoolExecutor) delegate).setKeepAliveTime(timeout, timeUnit);
+        } else {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/DefaultExecutorFactory.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/DefaultExecutorFactory.java
index b3ac3a7..15e6dc5 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/DefaultExecutorFactory.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/DefaultExecutorFactory.java
@@ -21,9 +21,11 @@
 import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
 public class DefaultExecutorFactory implements ExecutorFactory, Stoppable {
@@ -66,6 +68,24 @@ public ManagedExecutor create(String displayName, int fixedSize) {
         return executor;
     }
 
+    @Override
+    public ManagedThreadPoolExecutor createThreadPool(String displayName, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit timeUnit) {
+        ThreadPoolExecutor executorService = createThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, newThreadFactory(displayName));
+        TrackedThreadPoolManagedExecutor executor = new TrackedThreadPoolManagedExecutor(executorService, new ExecutorPolicy.CatchAndRecordFailures());
+        executors.add(executor);
+        return executor;
+    }
+
+    private static ThreadPoolExecutor createThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit timeUnit, ThreadFactory threadFactory) {
+        return new ThreadPoolExecutor(
+            corePoolSize,
+            maximumPoolSize,
+            keepAliveTime,
+            timeUnit,
+            new LinkedBlockingQueue<Runnable>(),
+            threadFactory);
+    }
+
     protected ExecutorService createExecutor(String displayName, int fixedSize) {
         return Executors.newFixedThreadPool(fixedSize, newThreadFactory(displayName));
     }
@@ -114,4 +134,19 @@ public void stop(int timeoutValue, TimeUnit timeoutUnits) throws IllegalStateExc
             }
         }
     }
+
+    private class TrackedThreadPoolManagedExecutor extends ManagedThreadPoolExecutorImpl {
+        public TrackedThreadPoolManagedExecutor(ThreadPoolExecutor delegate, ExecutorPolicy executorPolicy) {
+            super(delegate, executorPolicy);
+        }
+
+        @Override
+        public void stop(int timeoutValue, TimeUnit timeoutUnits) throws IllegalStateException {
+            try {
+                super.stop(timeoutValue, timeoutUnits);
+            } finally {
+                executors.remove(this);
+            }
+        }
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorFactory.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorFactory.java
index 502c7b1..07f17af 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorFactory.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ExecutorFactory.java
@@ -16,6 +16,8 @@
 
 package org.gradle.internal.concurrent;
 
+import java.util.concurrent.TimeUnit;
+
 public interface ExecutorFactory {
     /**
      * Creates an executor which can run multiple actions concurrently. It is the caller's responsibility to stop the executor.
@@ -39,6 +41,22 @@ public interface ExecutorFactory {
     ManagedExecutor create(String displayName, int fixedSize);
 
     /**
+     * Creates an executor which can run multiple tasks concurrently. It is the caller's responsibility to stop the executor.
+     *
+     * The executor will collect failures thrown by actions and rethrow when the executor is stopped.
+     *
+     * @param displayName The display name for this executor. Used for thread names, logging and error message.
+     * @param corePoolSize The number of threads to keep in the pool
+     * @param maximumPoolSize The maximum number of threads allowed
+     * @param keepAliveTime  when the number of threads is greater than
+     *        the core, this is the maximum time that excess idle threads
+     *        will wait for new tasks before terminating.
+     * @param timeUnit the time unit for the {@code keepAliveTime} argument
+     * @return The executor.
+     */
+    ManagedThreadPoolExecutor createThreadPool(String displayName, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit timeUnit);
+
+    /**
      * Creates a scheduled executor which can run tasks periodically. It is the caller's responsibility to stop the executor.
      *
      * The created scheduled executor has a fixed pool size of {@literal fixedSize}.
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedExecutorImpl.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedExecutorImpl.java
index 544f3df..b9a6aa0 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedExecutorImpl.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedExecutorImpl.java
@@ -16,92 +16,10 @@
 
 package org.gradle.internal.concurrent;
 
-import org.gradle.internal.UncheckedException;
-
-import javax.annotation.Nonnull;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 
-public class ManagedExecutorImpl extends AbstractDelegatingExecutorService implements ManagedExecutor {
-    private final ExecutorService executor;
-    private final ThreadLocal<Object> executing = new ThreadLocal<Object>();
-    private final ExecutorPolicy executorPolicy;
-
-    public ManagedExecutorImpl(ExecutorService executor, ExecutorPolicy executorPolicy) {
-        super(executor);
-        this.executor = executor;
-        this.executorPolicy = executorPolicy;
-    }
-
-    @Override
-    public void execute(@Nonnull final Runnable command) {
-        executor.execute(trackedCommand(command));
-    }
-
-    protected Runnable trackedCommand(final Runnable command) {
-        return new Runnable() {
-            @Override
-            public void run() {
-                executing.set(command);
-                try {
-                    executorPolicy.onExecute(command);
-                } finally {
-                    executing.remove();
-                }
-            }
-        };
-    }
-
-    protected <V> Callable<V> trackedCommand(final Callable<V> command) {
-        return new Callable<V>() {
-            @Override
-            public V call() throws Exception {
-                executing.set(command);
-                try {
-                    return executorPolicy.onExecute(command);
-                } finally {
-                    executing.remove();
-                }
-            }
-        };
-    }
-
-    @Override
-    public void requestStop() {
-        executor.shutdown();
-    }
-
-    @Override
-    public void stop() {
-        stop(Integer.MAX_VALUE, TimeUnit.SECONDS);
-    }
-
-    @Override
-    public void stop(int timeoutValue, TimeUnit timeoutUnits) throws IllegalStateException {
-        requestStop();
-        if (executing.get() != null) {
-            throw new IllegalStateException("Cannot stop this executor from an executor thread.");
-        }
-        try {
-            if (!executor.awaitTermination(timeoutValue, timeoutUnits)) {
-                executor.shutdownNow();
-                throw new IllegalStateException("Timeout waiting for concurrent jobs to complete.");
-            }
-        } catch (InterruptedException e) {
-            executor.shutdownNow();
-            throw UncheckedException.throwAsUncheckedException(e);
-        }
-        executorPolicy.onStop();
-    }
-
-    @Override
-    public void setKeepAlive(int timeout, TimeUnit timeUnit) {
-        if (executor instanceof ThreadPoolExecutor) {
-            ((ThreadPoolExecutor) executor).setKeepAliveTime(timeout, timeUnit);
-        } else {
-            throw new UnsupportedOperationException();
-        }
+public class ManagedExecutorImpl extends AbstractManagedExecutor<ExecutorService> implements ManagedExecutor {
+    public ManagedExecutorImpl(ExecutorService delegate, ExecutorPolicy executorPolicy) {
+        super(delegate, executorPolicy);
     }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedScheduledExecutorImpl.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedScheduledExecutorImpl.java
index 5fbeccd..7b49d84 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedScheduledExecutorImpl.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedScheduledExecutorImpl.java
@@ -21,31 +21,28 @@
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
-public class ManagedScheduledExecutorImpl extends ManagedExecutorImpl implements ManagedScheduledExecutor {
-    private final ScheduledExecutorService executor;
-
-    ManagedScheduledExecutorImpl(ScheduledExecutorService executor, ExecutorPolicy executorPolicy) {
-        super(executor, executorPolicy);
-        this.executor = executor;
+public class ManagedScheduledExecutorImpl extends AbstractManagedExecutor<ScheduledExecutorService> implements ManagedScheduledExecutor {
+    ManagedScheduledExecutorImpl(ScheduledExecutorService delegate, ExecutorPolicy executorPolicy) {
+        super(delegate, executorPolicy);
     }
 
     @Override
     public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
-        return executor.schedule(trackedCommand(command), delay, unit);
+        return delegate.schedule(trackedCommand(command), delay, unit);
     }
 
     @Override
     public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
-        return executor.schedule(trackedCommand(callable), delay, unit);
+        return delegate.schedule(trackedCommand(callable), delay, unit);
     }
 
     @Override
     public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
-        return executor.scheduleAtFixedRate(trackedCommand(command), initialDelay, period, unit);
+        return delegate.scheduleAtFixedRate(trackedCommand(command), initialDelay, period, unit);
     }
 
     @Override
     public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
-        return executor.scheduleWithFixedDelay(trackedCommand(command), initialDelay, delay, unit);
+        return delegate.scheduleWithFixedDelay(trackedCommand(command), initialDelay, delay, unit);
     }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedThreadPoolExecutor.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedThreadPoolExecutor.java
new file mode 100644
index 0000000..95f2110
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedThreadPoolExecutor.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 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.concurrent;
+
+import javax.annotation.Nonnull;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+public interface ManagedThreadPoolExecutor extends ManagedExecutor {
+    void setThreadFactory(@Nonnull ThreadFactory threadFactory);
+
+    ThreadFactory getThreadFactory();
+
+    void setRejectedExecutionHandler(@Nonnull RejectedExecutionHandler handler);
+
+    RejectedExecutionHandler getRejectedExecutionHandler();
+
+    void setCorePoolSize(int corePoolSize);
+
+    int getCorePoolSize();
+
+    boolean prestartCoreThread();
+
+    int prestartAllCoreThreads();
+
+    boolean allowsCoreThreadTimeOut();
+
+    void allowCoreThreadTimeOut(boolean value);
+
+    void setMaximumPoolSize(int maximumPoolSize);
+
+    int getMaximumPoolSize();
+
+    void setKeepAliveTime(long time, TimeUnit unit);
+
+    long getKeepAliveTime(TimeUnit unit);
+
+    BlockingQueue<Runnable> getQueue();
+
+    boolean remove(Runnable task);
+
+    void purge();
+
+    int getPoolSize();
+
+    int getActiveCount();
+
+    int getLargestPoolSize();
+
+    long getTaskCount();
+
+    long getCompletedTaskCount();
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedThreadPoolExecutorImpl.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedThreadPoolExecutorImpl.java
new file mode 100644
index 0000000..3cab43d
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/ManagedThreadPoolExecutorImpl.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2015 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.concurrent;
+
+import javax.annotation.Nonnull;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class ManagedThreadPoolExecutorImpl extends AbstractManagedExecutor<ThreadPoolExecutor> implements ManagedThreadPoolExecutor {
+    public ManagedThreadPoolExecutorImpl(ThreadPoolExecutor delegate, ExecutorPolicy executorPolicy) {
+        super(delegate, executorPolicy);
+    }
+
+    @Override
+    public void setThreadFactory(@Nonnull ThreadFactory threadFactory) {
+        delegate.setThreadFactory(threadFactory);
+    }
+
+    @Override
+    public ThreadFactory getThreadFactory() {
+        return delegate.getThreadFactory();
+    }
+
+    @Override
+    public void setRejectedExecutionHandler(@Nonnull RejectedExecutionHandler handler) {
+        delegate.setRejectedExecutionHandler(handler);
+    }
+
+    @Override
+    public RejectedExecutionHandler getRejectedExecutionHandler() {
+        return delegate.getRejectedExecutionHandler();
+    }
+
+    @Override
+    public void setCorePoolSize(int corePoolSize) {
+        delegate.setCorePoolSize(corePoolSize);
+    }
+
+    @Override
+    public int getCorePoolSize() {
+        return delegate.getCorePoolSize();
+    }
+
+    @Override
+    public boolean prestartCoreThread() {
+        return delegate.prestartCoreThread();
+    }
+
+    @Override
+    public int prestartAllCoreThreads() {
+        return delegate.prestartAllCoreThreads();
+    }
+
+    @Override
+    public boolean allowsCoreThreadTimeOut() {
+        return delegate.allowsCoreThreadTimeOut();
+    }
+
+    @Override
+    public void allowCoreThreadTimeOut(boolean value) {
+        delegate.allowCoreThreadTimeOut(value);
+    }
+
+    @Override
+    public void setMaximumPoolSize(int maximumPoolSize) {
+        delegate.setMaximumPoolSize(maximumPoolSize);
+    }
+
+    @Override
+    public int getMaximumPoolSize() {
+        return delegate.getMaximumPoolSize();
+    }
+
+    @Override
+    public void setKeepAliveTime(long time, TimeUnit unit) {
+        delegate.setKeepAliveTime(time, unit);
+    }
+
+    @Override
+    public long getKeepAliveTime(TimeUnit unit) {
+        return delegate.getKeepAliveTime(unit);
+    }
+
+    @Override
+    public BlockingQueue<Runnable> getQueue() {
+        return delegate.getQueue();
+    }
+
+    @Override
+    public boolean remove(Runnable task) {
+        return delegate.remove(task);
+    }
+
+    @Override
+    public void purge() {
+        delegate.purge();
+    }
+
+    @Override
+    public int getPoolSize() {
+        return delegate.getPoolSize();
+    }
+
+    @Override
+    public int getActiveCount() {
+        return delegate.getActiveCount();
+    }
+
+    @Override
+    public int getLargestPoolSize() {
+        return delegate.getLargestPoolSize();
+    }
+
+    @Override
+    public long getTaskCount() {
+        return delegate.getTaskCount();
+    }
+
+    @Override
+    public long getCompletedTaskCount() {
+        return delegate.getCompletedTaskCount();
+    }
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/Synchronizer.java b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/Synchronizer.java
index 4d2c419..27d40c9 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/Synchronizer.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/concurrent/Synchronizer.java
@@ -42,4 +42,4 @@ public void synchronize(Runnable operation) {
             lock.unlock();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/CompilationFailedIndicator.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/CompilationFailedIndicator.java
new file mode 100644
index 0000000..7953263
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/CompilationFailedIndicator.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 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.exceptions;
+
+public interface CompilationFailedIndicator extends NonGradleCause {
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DefaultMultiCauseException.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DefaultMultiCauseException.java
index b97172e..4d9541f 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DefaultMultiCauseException.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/DefaultMultiCauseException.java
@@ -26,7 +26,7 @@
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
-public class DefaultMultiCauseException extends GradleException implements MultiCauseException {
+public class DefaultMultiCauseException extends GradleException implements MultiCauseException, NonGradleCauseExceptionsHolder {
     private final List<Throwable> causes = new CopyOnWriteArrayList<Throwable>();
     private transient ThreadLocal<Boolean> hideCause = threadLocal();
     private transient Factory<String> messageFactory;
@@ -162,4 +162,19 @@ public String getMessage() {
         }
         return message;
     }
+
+    @Override
+    public boolean hasCause(Class<?> type) {
+        for (Throwable cause : getCauses()) {
+            if (cause instanceof NonGradleCauseExceptionsHolder) {
+                boolean hasCauseOfType = ((NonGradleCauseExceptionsHolder) cause).hasCause(type);
+                if (hasCauseOfType) {
+                    return true;
+                }
+            } else if (type.isInstance(cause)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/NonGradleCause.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/NonGradleCause.java
new file mode 100644
index 0000000..b9c7c399
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/NonGradleCause.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.exceptions;
+
+/**
+ * An exception that is not caused by a Gradle specific problem. e.g. a problem caused by a compilation error.
+ * This serves as a marker interface.
+ */
+public interface NonGradleCause {
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/NonGradleCauseExceptionsHolder.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/NonGradleCauseExceptionsHolder.java
new file mode 100644
index 0000000..224cf4e
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/NonGradleCauseExceptionsHolder.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.exceptions;
+
+public interface NonGradleCauseExceptionsHolder {
+    boolean hasCause(Class<?> type);
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/ResolutionProvider.java b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/ResolutionProvider.java
new file mode 100644
index 0000000..1f90b42
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/exceptions/ResolutionProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.exceptions;
+
+import org.gradle.api.NonNullApi;
+
+import java.util.List;
+
+/**
+ * A provider of resolutions for an exception.
+ * Exceptions can be derived from this interface to provide a list of resolutions that are then displayed in the suggestion section of the error message.
+ */
+
+@NonNullApi
+public interface ResolutionProvider {
+    List<String> getResolutions();
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/id/package-info.java b/subprojects/base-services/src/main/java/org/gradle/internal/id/package-info.java
new file mode 100644
index 0000000..0676a87
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/id/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * Internal classes for generating identifiers.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.internal.id;
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationCategory.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationCategory.java
index 80d24a9..d43091d 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationCategory.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/BuildOperationCategory.java
@@ -83,4 +83,14 @@ public boolean isTopLevelWorkItem() {
     public boolean isShowHeader() {
         return showHeader;
     }
+
+    /**
+     * If the metadata corresponds to some {@link BuildOperationCategory}, then returns this category. Returns {@link #UNCATEGORIZED} if there is no defined corresponding category.
+     *
+     * @param metadata the metadata to find category for
+     * @return the category
+     */
+    public static BuildOperationCategory toCategory(BuildOperationMetadata metadata) {
+        return (metadata instanceof BuildOperationCategory) ? (BuildOperationCategory) metadata : UNCATEGORIZED;
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/operations/UncategorizedBuildOperations.java b/subprojects/base-services/src/main/java/org/gradle/internal/operations/UncategorizedBuildOperations.java
new file mode 100644
index 0000000..450c94a
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/operations/UncategorizedBuildOperations.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.operations;
+
+/**
+ * Metadata markers for build operations that are uncategorized for the progress logging purposes.
+ */
+public enum UncategorizedBuildOperations implements BuildOperationMetadata {
+    /**
+     * Creates and executes the transform actions. {@link BuildOperationCategory#TRANSFORM} can include this operation.
+     */
+    TRANSFORM_ACTION
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaReflectionUtil.java b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaReflectionUtil.java
index bef057e..42ae5bc 100644
--- a/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaReflectionUtil.java
+++ b/subprojects/base-services/src/main/java/org/gradle/internal/reflect/JavaReflectionUtil.java
@@ -16,9 +16,11 @@
 
 package org.gradle.internal.reflect;
 
+import com.google.common.reflect.TypeToken;
 import org.gradle.internal.UncheckedException;
 
 import java.lang.reflect.Constructor;
+import java.lang.reflect.ParameterizedType;
 
 public class JavaReflectionUtil {
 
@@ -55,4 +57,17 @@ public static <T> T newInstance(Class<T> c) {
             throw UncheckedException.throwAsUncheckedException(e);
         }
     }
+
+    /**
+     * Returns the {@link TypeToken} of a parameter specified by index.
+     *
+     * @param beanType the TypeToken of the bean
+     * @param parameterizedClass the parameterized class
+     * @param typeParameterIndex the index of the parameter
+     * @return a TypeToken
+     */
+    public static <T> TypeToken<?> extractNestedType(TypeToken<T> beanType, Class<? super T> parameterizedClass, int typeParameterIndex) {
+        ParameterizedType type = (ParameterizedType) beanType.getSupertype(parameterizedClass).getType();
+        return TypeToken.of(type.getActualTypeArguments()[typeParameterIndex]);
+    }
 }
diff --git a/subprojects/base-services/src/main/java/org/gradle/util/internal/EncryptionAlgorithm.java b/subprojects/base-services/src/main/java/org/gradle/util/internal/EncryptionAlgorithm.java
new file mode 100644
index 0000000..26ae6ded
--- /dev/null
+++ b/subprojects/base-services/src/main/java/org/gradle/util/internal/EncryptionAlgorithm.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 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.util.internal;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import java.io.IOException;
+
+/**
+ * A protocol for encryption algorithms.
+ */
+public interface EncryptionAlgorithm {
+    String getTransformation();
+
+    String getAlgorithm();
+
+    /**
+     * Creates an encryption/decryption session with this algorithm and the given key.
+     *
+     * @param key the key to use
+     * @return a new encryption/decryption session
+     */
+    Session newSession(SecretKey key);
+
+    interface IVLoader {
+        void load(byte[] toLoad) throws IOException;
+    }
+
+    interface IVCollector {
+        void collect(byte[] toCollect) throws IOException;
+    }
+
+    /**
+     * Combines an algorithm and a key, and allows obtaining encryption/decryption ciphers according to those.
+     */
+    interface Session {
+        SecretKey getKey();
+        EncryptionAlgorithm getAlgorithm();
+        Cipher encryptingCipher(IVCollector collector);
+        Cipher decryptingCipher(IVLoader loader);
+    }
+
+    class EncryptionException extends RuntimeException {
+        public EncryptionException(String message, Throwable cause) {
+            super(message, cause);
+        }
+        public EncryptionException(Throwable cause) {
+            super(null, cause);
+        }
+    }
+
+    EncryptionAlgorithm NONE = new EncryptionAlgorithm() {
+        @Override
+        public String getTransformation() {
+            return "none";
+        }
+
+        @Override
+        public String getAlgorithm() {
+            return "none";
+        }
+
+        @Override
+        public Session newSession(SecretKey key) {
+            throw new IllegalStateException("Encryption not enabled");
+        }
+    };
+}
diff --git a/subprojects/base-services/src/main/java/org/gradle/util/internal/TextUtil.java b/subprojects/base-services/src/main/java/org/gradle/util/internal/TextUtil.java
index 90a0b08..0a118fa 100644
--- a/subprojects/base-services/src/main/java/org/gradle/util/internal/TextUtil.java
+++ b/subprojects/base-services/src/main/java/org/gradle/util/internal/TextUtil.java
@@ -28,6 +28,7 @@
 import java.io.StringWriter;
 import java.io.Writer;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Locale;
 import java.util.regex.Pattern;
 
@@ -350,4 +351,21 @@ public static String camelToKebabCase(String camelCase) {
     public static String toLowerCaseLocaleSafe(String s) {
         return s.toLowerCase(Locale.ENGLISH);
     }
+
+    /**
+     * This method returns the plural ending for an english word for trivial cases depending on the number of elements a list has.
+     *
+     * @param collection which size is used to determine the plural ending
+     * @return "s" if the collection has more than one element, an empty string otherwise
+     */
+    public static String getPluralEnding(Collection<?> collection) {
+        return collection.size() > 1 ? "s" : "";
+    }
+
+    public static String endLineWithDot(String txt) {
+        if (txt.endsWith(".") || txt.endsWith("\n")) {
+            return txt;
+        }
+        return txt + ".";
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/util/internal/VersionNumber.java b/subprojects/base-services/src/main/java/org/gradle/util/internal/VersionNumber.java
similarity index 100%
rename from subprojects/core/src/main/java/org/gradle/util/internal/VersionNumber.java
rename to subprojects/base-services/src/main/java/org/gradle/util/internal/VersionNumber.java
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/ClasspathUtilTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/ClasspathUtilTest.groovy
index b582da2..b42c6f0 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/ClasspathUtilTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/ClasspathUtilTest.groovy
@@ -16,12 +16,14 @@
 package org.gradle.internal.classloader
 
 import org.gradle.internal.classpath.DefaultClassPath
+import spock.lang.Issue
 import spock.lang.Specification
 
 class ClasspathUtilTest extends Specification {
     private static final URL JAR_URL = new URL('jar:file:home/duke/duke.jar!/')
     private static final URL HTTP_URL = new URL('http://www.foo.com/bar/')
-    private static final URL FILE_URL = new File('home/duke/duke.jar').toURI().toURL()
+    private static final File FILE = new File('/home/duke/duke.jar')
+    private static final URL FILE_URL = FILE.toURI().toURL()
     def factory = new DefaultClassLoaderFactory()
 
     def "filters non-file URLs from classpath"() {
@@ -36,4 +38,27 @@
         !classpath.asURLs.any { it == JAR_URL || it == HTTP_URL }
         classpath.asURLs.any { it == FILE_URL }
     }
+
+    def "getClasspathForResource for jar scheme with resource name"() {
+        given:
+        def jarUrlWithResourceName = new URL('jar:file:/home/duke/duke.jar!/Test.class')
+
+        when:
+        def file = ClasspathUtil.getClasspathForResource(jarUrlWithResourceName, "Test.class")
+
+        then:
+        FILE.getPath() == file.getPath()
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/23625")
+    def "getClasspathForResource for jar scheme with prefix resource name"() {
+        given:
+        def jarUrlWithResourceName = new URL('jar:file:/home/duke/duke.jar!/META-INF/versions/9/Test.class')
+
+        when:
+        def file = ClasspathUtil.getClasspathForResource(jarUrlWithResourceName, "Test.class")
+
+        then:
+        FILE.getPath() == file.getPath()
+    }
 }
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/FilteringClassLoaderTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/FilteringClassLoaderTest.groovy
index a474877..0f013b5 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/FilteringClassLoaderTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/FilteringClassLoaderTest.groovy
@@ -15,8 +15,8 @@
  */
 package org.gradle.internal.classloader
 
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Before
 import org.junit.Test
 import org.junit.runners.BlockJUnit4ClassRunner
@@ -75,7 +75,7 @@
         cannotSeePackage('org.junit')
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     void filtersPackagesByDefaultPostJdk8() {
         given:
         assert classLoader.parent.getDefinedPackage('org.junit') != null
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MultiParentClassLoaderTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MultiParentClassLoaderTest.groovy
index 787fbaa..a2dd111 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MultiParentClassLoaderTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/classloader/MultiParentClassLoaderTest.groovy
@@ -15,8 +15,8 @@
  */
 package org.gradle.internal.classloader
 
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Specification
 
 class MultiParentClassLoaderTest extends Specification {
@@ -55,7 +55,7 @@
         e.message == 'string not found.'
     }
 
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     // todo: find a way to mock this in JDK 9+, where `getDefinedPackage` is final
     public void loadsPackageFromParentsInOrderSpecified() {
         def stringPackage = String.class.getPackage()
@@ -71,7 +71,7 @@
         loader.getPackage('list') == listPackage
     }
 
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     // todo: find a way to mock this in JDK 9+, where `getDefinedPackages` is final
     public void containsUnionOfPackagesFromAllParents() {
         def package1 = Stub(Package)
diff --git a/subprojects/base-services/src/test/groovy/org/gradle/internal/io/StreamByteBufferTest.groovy b/subprojects/base-services/src/test/groovy/org/gradle/internal/io/StreamByteBufferTest.groovy
index 3a95323..d440e93 100644
--- a/subprojects/base-services/src/test/groovy/org/gradle/internal/io/StreamByteBufferTest.groovy
+++ b/subprojects/base-services/src/test/groovy/org/gradle/internal/io/StreamByteBufferTest.groovy
@@ -418,4 +418,4 @@
         expect:
         StreamByteBuffer.of(byteArrayList).readAsByteArray() == (byteArrayList.flatten() as byte[])
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/NextGenBuildCacheHttpIntegrationTest.groovy b/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/NextGenBuildCacheHttpIntegrationTest.groovy
new file mode 100644
index 0000000..00cb27c
--- /dev/null
+++ b/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/NextGenBuildCacheHttpIntegrationTest.groovy
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2023 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.caching
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture
+import org.gradle.integtests.fixtures.TestBuildCache
+import org.gradle.test.fixtures.server.http.HttpBuildCacheServer
+import org.junit.Rule
+
+class NextGenBuildCacheHttpIntegrationTest extends AbstractIntegrationSpec implements DirectoryBuildCacheFixture {
+
+    @Rule
+    HttpBuildCacheServer httpBuildCacheServer = new HttpBuildCacheServer(temporaryFolder)
+
+    def setup() {
+        httpBuildCacheServer.start()
+        settingsFile << """
+            buildCache {
+                remote(HttpBuildCache) {
+                    url = "${httpBuildCacheServer.uri}"
+                    push = true
+                }
+            }
+        """
+    }
+
+    def "compile task is loaded from cache"() {
+        buildFile << """
+            apply plugin: "java"
+        """
+
+        file("src/main/java/Main.java") << """
+            public class Main {}
+        """
+
+        when:
+        runWithBuildCacheNG "compileJava"
+        then:
+        executedAndNotSkipped ":compileJava"
+
+        def alternativeBuildCache = new TestBuildCache(temporaryFolder.file("cache-dir-2").deleteDir().createDir())
+        settingsFile << alternativeBuildCache.localCacheConfiguration()
+
+        when:
+        runWithBuildCacheNG "clean", "compileJava"
+        then:
+        skipped ":compileJava"
+    }
+
+    def "should use different hashes than production build cache for same artifacts"() {
+        buildFile << """
+            plugins {
+                id("base")
+            }
+
+            @CacheableTask
+            abstract class BuildCacheTask extends DefaultTask {
+                @Input
+                abstract Property<String> getInput();
+                @OutputFile
+                abstract RegularFileProperty getOutput();
+
+                @TaskAction
+                void run() {
+                    def file = output.asFile.get()
+                    file.text = input.get()
+                }
+            }
+
+            tasks.register("testBuildCache", BuildCacheTask) {
+                input.set("Hello world")
+                output.set(file("build/build-cache/output.txt"))
+            }
+        """
+
+        when:
+        runWithBuildCache "testBuildCache"
+
+        then:
+        executedAndNotSkipped ":testBuildCache"
+        def productionHashes = httpBuildCacheServer.listCacheFiles()
+        !productionHashes.empty
+
+        when:
+        httpBuildCacheServer.deleteCacheFiles()
+        def alternativeBuildCache = new TestBuildCache(temporaryFolder.file("cache-dir-ng").deleteDir().createDir())
+        settingsFile << alternativeBuildCache.localCacheConfiguration()
+        runWithBuildCacheNG "clean", "testBuildCache"
+
+        then:
+        executedAndNotSkipped ":testBuildCache"
+        def ngHashes = httpBuildCacheServer.listCacheFiles()
+        !ngHashes.empty
+        ngHashes.intersect(productionHashes).empty
+    }
+
+    private runWithBuildCache(String... tasks) {
+        withBuildCache().run(tasks)
+    }
+
+    private runWithBuildCacheNG(String... tasks) {
+        withBuildCacheNg().run(tasks)
+    }
+}
diff --git a/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/http/internal/HttpBuildCacheServiceIntegrationTest.groovy b/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/http/internal/HttpBuildCacheServiceIntegrationTest.groovy
index a676fee..dbb3b6f 100644
--- a/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/http/internal/HttpBuildCacheServiceIntegrationTest.groovy
+++ b/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/http/internal/HttpBuildCacheServiceIntegrationTest.groovy
@@ -273,12 +273,10 @@
         settingsFile.text = useHttpBuildCache(httpBuildCacheServer.uri)
 
         then:
-        def failure = withBuildCache().fails "jar"
-        failure.assertHasCause(
-            "Using insecure protocols with remote build cache, without explicit opt-in, is unsupported. " +
-                "Switch remote build cache to a secure protocol (like HTTPS) or allow insecure protocols. " +
-                Documentation.dslReference(HttpBuildCache, "allowInsecureProtocol").consultDocumentationMessage()
-        )
+        withBuildCache().fails("jar")
+            .assertHasCause("Using insecure protocols with remote build cache, without explicit opt-in, is unsupported.")
+            .assertHasResolution("Switch remote build cache to a secure protocol (like HTTPS) or allow insecure protocols.")
+            .assertHasResolution(Documentation.dslReference(HttpBuildCache, "allowInsecureProtocol").consultDocumentationMessage())
     }
 
     def "ssl certificate is validated"() {
diff --git a/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/http/internal/HttpBuildCacheServiceTest.groovy b/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/http/internal/HttpBuildCacheServiceTest.groovy
index b37a284..350447f 100644
--- a/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/http/internal/HttpBuildCacheServiceTest.groovy
+++ b/subprojects/build-cache-http/src/integTest/groovy/org/gradle/caching/http/internal/HttpBuildCacheServiceTest.groovy
@@ -20,11 +20,11 @@
 import org.apache.http.HttpStatus
 import org.gradle.api.UncheckedIOException
 import org.gradle.api.internal.DocumentationRegistry
-import org.gradle.caching.BuildCacheEntryWriter
 import org.gradle.caching.BuildCacheException
-import org.gradle.caching.BuildCacheKey
 import org.gradle.caching.BuildCacheServiceFactory
 import org.gradle.caching.http.HttpBuildCache
+import org.gradle.caching.internal.DefaultBuildCacheKey
+import org.gradle.caching.internal.NextGenBuildCacheService
 import org.gradle.internal.hash.HashCode
 import org.gradle.internal.resource.transport.http.DefaultSslContextFactory
 import org.gradle.internal.resource.transport.http.HttpClientHelper
@@ -59,29 +59,7 @@
     BuildCacheServiceFactory.Describer buildCacheDescriber
     HttpClientHelper.Factory httpClientHelperFactory = HttpClientHelper.Factory.createFactory(new DocumentationRegistry())
 
-    def key = new BuildCacheKey() {
-        def hashCode = HashCode.fromString("01234567abcdef")
-
-        @Override
-        String getHashCode() {
-            return hashCode.toString()
-        }
-
-        @Override
-        String toString() {
-            return getHashCode()
-        }
-
-        @Override
-        byte[] toByteArray() {
-            return hashCode.toByteArray()
-        }
-
-        @Override
-        String getDisplayName() {
-            return getHashCode()
-        }
-    }
+    def key = new DefaultBuildCacheKey(HashCode.fromString("01234567abcdef"))
     private config = TestUtil.newInstance(HttpBuildCache.class)
 
     HttpBuildCacheService cacheRef
@@ -378,7 +356,7 @@
 
     }
 
-    static class Writer implements BuildCacheEntryWriter {
+    static class Writer implements NextGenBuildCacheService.NextGenWriter {
         private final byte[] content
         private int writeCount = 0
 
@@ -387,6 +365,12 @@
         }
 
         @Override
+        InputStream openStream() throws IOException {
+            ++writeCount
+            return new ByteArrayInputStream(content)
+        }
+
+        @Override
         void writeTo(OutputStream output) throws IOException {
             ++writeCount
             output << content
diff --git a/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/DefaultHttpBuildCacheServiceFactory.java b/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/DefaultHttpBuildCacheServiceFactory.java
index 2160a81..961d1e2 100644
--- a/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/DefaultHttpBuildCacheServiceFactory.java
+++ b/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/DefaultHttpBuildCacheServiceFactory.java
@@ -18,7 +18,6 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import org.gradle.api.GradleException;
-import org.gradle.api.InvalidUserCodeException;
 import org.gradle.authentication.Authentication;
 import org.gradle.caching.BuildCacheService;
 import org.gradle.caching.BuildCacheServiceFactory;
@@ -114,11 +113,11 @@ private HttpRedirectVerifier createRedirectVerifier(URI url, boolean allowInsecu
                 url,
                 allowInsecureProtocol,
                 () -> {
-                    String message =
-                        "Using insecure protocols with remote build cache, without explicit opt-in, is unsupported. " +
-                            "Switch remote build cache to a secure protocol (like HTTPS) or allow insecure protocols. " +
-                            Documentation.dslReference(HttpBuildCache.class, "allowInsecureProtocol").consultDocumentationMessage();
-                    throw new InvalidUserCodeException(message);
+                    throw new InsecureProtocolException(
+                        "Using insecure protocols with remote build cache, without explicit opt-in, is unsupported.",
+                        "Switch remote build cache to a secure protocol (like HTTPS) or allow insecure protocols.",
+                        Documentation.dslReference(HttpBuildCache.class, "allowInsecureProtocol").consultDocumentationMessage()
+                    );
                 },
                 redirect -> {
                     throw new IllegalStateException("Redirects are unsupported by the build cache.");
diff --git a/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/HttpBuildCacheService.java b/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/HttpBuildCacheService.java
index 552f531..4a42a9b 100644
--- a/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/HttpBuildCacheService.java
+++ b/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/HttpBuildCacheService.java
@@ -22,16 +22,16 @@
 import org.apache.http.StatusLine;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
 import org.apache.http.client.methods.HttpPut;
 import org.apache.http.client.utils.URIBuilder;
 import org.apache.http.entity.AbstractHttpEntity;
 import org.apache.http.protocol.HTTP;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.caching.BuildCacheEntryReader;
-import org.gradle.caching.BuildCacheEntryWriter;
 import org.gradle.caching.BuildCacheException;
 import org.gradle.caching.BuildCacheKey;
-import org.gradle.caching.BuildCacheService;
+import org.gradle.caching.internal.NextGenBuildCacheService;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.resource.transport.http.HttpClientHelper;
 import org.gradle.internal.resource.transport.http.HttpClientResponse;
@@ -48,7 +48,7 @@
 /**
  * Build cache implementation that delegates to a service accessible via HTTP.
  */
-public class HttpBuildCacheService implements BuildCacheService {
+public class HttpBuildCacheService implements NextGenBuildCacheService {
     private static final Logger LOGGER = LoggerFactory.getLogger(HttpBuildCacheService.class);
     static final String BUILD_CACHE_CONTENT_TYPE = "application/vnd.gradle.build-cache-artifact.v1";
 
@@ -76,6 +76,31 @@ public HttpBuildCacheService(HttpClientHelper httpClientHelper, URI url, HttpBui
     }
 
     @Override
+    public boolean contains(BuildCacheKey key) {
+        final URI uri = root.resolve("./" + key.getHashCode());
+        HttpHead httpHead = new HttpHead(uri);
+        requestCustomizer.customize(httpHead);
+
+        try (HttpClientResponse response = httpClientHelper.performHttpRequest(httpHead)) {
+            StatusLine statusLine = response.getStatusLine();
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Response for HEAD {}: {}", safeUri(uri), statusLine);
+            }
+            int statusCode = statusLine.getStatusCode();
+            if (isHttpSuccess(statusCode)) {
+                return true;
+            } else if (statusCode == HttpStatus.SC_NOT_FOUND) {
+                return false;
+            } else {
+                String defaultMessage = String.format("Checking entry from '%s' response status %d: %s", safeUri(uri), statusCode, statusLine.getReasonPhrase());
+                return throwHttpStatusCodeException(statusCode, defaultMessage);
+            }
+        } catch (IOException e) {
+            throw wrap(e);
+        }
+    }
+
+    @Override
     public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
         final URI uri = root.resolve("./" + key.getHashCode());
         HttpGet httpGet = new HttpGet(uri);
@@ -103,7 +128,7 @@ public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws Buil
     }
 
     @Override
-    public void store(BuildCacheKey key, final BuildCacheEntryWriter output) throws BuildCacheException {
+    public void store(BuildCacheKey key, NextGenWriter writer) throws BuildCacheException {
         final URI uri = root.resolve(key.getHashCode());
         HttpPut httpPut = new HttpPut(uri);
         if (useExpectContinue) {
@@ -120,7 +145,7 @@ public boolean isRepeatable() {
 
             @Override
             public long getContentLength() {
-                return output.getSize();
+                return writer.getSize();
             }
 
             @Override
@@ -130,7 +155,7 @@ public InputStream getContent() {
 
             @Override
             public void writeTo(OutputStream outstream) throws IOException {
-                output.writeTo(outstream);
+                writer.writeTo(outstream);
             }
 
             @Override
diff --git a/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/InsecureProtocolException.java b/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/InsecureProtocolException.java
new file mode 100644
index 0000000..ef9b692
--- /dev/null
+++ b/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/InsecureProtocolException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.caching.http.internal;
+
+import org.gradle.api.InvalidUserCodeException;
+import org.gradle.internal.exceptions.ResolutionProvider;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class InsecureProtocolException extends InvalidUserCodeException implements ResolutionProvider {
+
+    private final List<String> resolutions;
+
+    public InsecureProtocolException(String message, String... resolutions) {
+        super(message);
+        this.resolutions = Arrays.asList(resolutions);
+    }
+
+    @Override
+    public List<String> getResolutions() {
+        return resolutions;
+    }
+}
diff --git a/subprojects/build-cache-packaging/src/main/java/org/gradle/caching/internal/packaging/impl/TarBuildCacheEntryPacker.java b/subprojects/build-cache-packaging/src/main/java/org/gradle/caching/internal/packaging/impl/TarBuildCacheEntryPacker.java
index 617c8e0..92610f6 100644
--- a/subprojects/build-cache-packaging/src/main/java/org/gradle/caching/internal/packaging/impl/TarBuildCacheEntryPacker.java
+++ b/subprojects/build-cache-packaging/src/main/java/org/gradle/caching/internal/packaging/impl/TarBuildCacheEntryPacker.java
@@ -32,6 +32,7 @@
 import org.gradle.caching.internal.origin.OriginWriter;
 import org.gradle.caching.internal.packaging.BuildCacheEntryPacker;
 import org.gradle.internal.RelativePathSupplier;
+import org.gradle.internal.file.BufferProvider;
 import org.gradle.internal.file.FileType;
 import org.gradle.internal.file.TreeType;
 import org.gradle.internal.file.impl.DefaultFileMetadata;
@@ -90,24 +91,25 @@ private interface UnixPermissions {
     private static final Charset ENCODING = StandardCharsets.UTF_8;
     private static final String METADATA_PATH = "METADATA";
     private static final Pattern TREE_PATH = Pattern.compile("(missing-)?tree-([^/]+)(?:/(.*))?");
-    private static final int BUFFER_SIZE = 64 * 1024;
-    private static final ThreadLocal<byte[]> COPY_BUFFERS = ThreadLocal.withInitial(() -> new byte[BUFFER_SIZE]);
 
     private final TarPackerFileSystemSupport fileSystemSupport;
     private final FilePermissionAccess filePermissionAccess;
     private final StreamHasher streamHasher;
     private final Interner<String> stringInterner;
+    private final BufferProvider bufferProvider;
 
     public TarBuildCacheEntryPacker(
         TarPackerFileSystemSupport fileSystemSupport,
         FilePermissionAccess filePermissionAccess,
         StreamHasher streamHasher,
-        Interner<String> stringInterner
+        Interner<String> stringInterner,
+        BufferProvider bufferProvider
     ) {
         this.fileSystemSupport = fileSystemSupport;
         this.filePermissionAccess = filePermissionAccess;
         this.streamHasher = streamHasher;
         this.stringInterner = stringInterner;
+        this.bufferProvider = bufferProvider;
     }
 
     @Override
@@ -128,7 +130,7 @@ public PackResult pack(CacheableEntity entity, Map<String, ? extends FileSystemS
         }
     }
 
-    private void packMetadata(OriginWriter writeMetadata, TarArchiveOutputStream tarOutput) throws IOException {
+    private static void packMetadata(OriginWriter writeMetadata, TarArchiveOutputStream tarOutput) throws IOException {
         ByteArrayOutputStream output = new ByteArrayOutputStream();
         writeMetadata.execute(output);
         createTarEntry(METADATA_PATH, output.size(), UnixPermissions.FILE_FLAG | UnixPermissions.DEFAULT_FILE_PERM, tarOutput);
@@ -151,7 +153,7 @@ private long pack(CacheableEntity entity, Map<String, ? extends FileSystemSnapsh
     }
 
     private long packTree(String name, TreeType type, FileSystemSnapshot snapshots, TarArchiveOutputStream tarOutput) {
-        PackingVisitor packingVisitor = new PackingVisitor(tarOutput, name, type, filePermissionAccess);
+        PackingVisitor packingVisitor = new PackingVisitor(tarOutput, name, type);
         snapshots.accept(new RelativePathTracker(), packingVisitor);
         return packingVisitor.getPackedEntryCount();
     }
@@ -330,21 +332,19 @@ private static String unescape(String name) {
         }
     }
 
-    private static class PackingVisitor implements RelativePathTrackingFileSystemSnapshotHierarchyVisitor {
+    private class PackingVisitor implements RelativePathTrackingFileSystemSnapshotHierarchyVisitor {
         private final TarArchiveOutputStream tarOutput;
         private final String treePath;
         private final String treeRoot;
-        private final FilePermissionAccess filePermissionAccess;
         private final TreeType type;
 
         private long packedEntryCount;
 
-        public PackingVisitor(TarArchiveOutputStream tarOutput, String treeName, TreeType type, FilePermissionAccess filePermissionAccess) {
+        public PackingVisitor(TarArchiveOutputStream tarOutput, String treeName, TreeType type) {
             this.tarOutput = tarOutput;
             this.treePath = "tree-" + escape(treeName);
             this.treeRoot = treePath + "/";
             this.type = type;
-            this.filePermissionAccess = filePermissionAccess;
         }
 
         @Override
@@ -431,7 +431,7 @@ private void storeFileEntry(File inputFile, String path, long size, int mode, Ta
             try {
                 createTarEntry(path, size, UnixPermissions.FILE_FLAG | mode, tarOutput);
                 try (FileInputStream input = new FileInputStream(inputFile)) {
-                    IOUtils.copyLarge(input, tarOutput, COPY_BUFFERS.get());
+                    IOUtils.copyLarge(input, tarOutput, bufferProvider.getBuffer());
                 }
                 tarOutput.closeArchiveEntry();
             } catch (IOException e) {
diff --git a/subprojects/build-cache-packaging/src/test/groovy/org/gradle/caching/internal/packaging/impl/AbstractTarBuildCacheEntryPackerSpec.groovy b/subprojects/build-cache-packaging/src/test/groovy/org/gradle/caching/internal/packaging/impl/AbstractTarBuildCacheEntryPackerSpec.groovy
index 64aaf0c..391cf09 100644
--- a/subprojects/build-cache-packaging/src/test/groovy/org/gradle/caching/internal/packaging/impl/AbstractTarBuildCacheEntryPackerSpec.groovy
+++ b/subprojects/build-cache-packaging/src/test/groovy/org/gradle/caching/internal/packaging/impl/AbstractTarBuildCacheEntryPackerSpec.groovy
@@ -22,6 +22,7 @@
 import org.gradle.caching.internal.CacheableEntity
 import org.gradle.caching.internal.origin.OriginReader
 import org.gradle.caching.internal.origin.OriginWriter
+import org.gradle.internal.file.BufferProvider
 import org.gradle.internal.file.Deleter
 import org.gradle.internal.file.TreeType
 import org.gradle.internal.hash.DefaultStreamHasher
@@ -43,7 +44,9 @@
     def fileSystemSupport = new DefaultTarPackerFileSystemSupport(deleter)
     def streamHasher = new DefaultStreamHasher()
     def stringInterner = new StringInterner()
-    def packer = new TarBuildCacheEntryPacker(fileSystemSupport, filePermissionAccess, streamHasher, stringInterner)
+    def packer = new TarBuildCacheEntryPacker(fileSystemSupport, filePermissionAccess, streamHasher, stringInterner, Stub(BufferProvider) {
+        getBuffer() >> new byte[4096]
+    })
     def fileSystemAccess = TestFiles.fileSystemAccess()
 
     abstract protected FilePermissionAccess createFilePermissionAccess()
diff --git a/subprojects/build-cache-packaging/src/test/groovy/org/gradle/caching/internal/packaging/impl/TarBuildCacheEntryPackerTest.groovy b/subprojects/build-cache-packaging/src/test/groovy/org/gradle/caching/internal/packaging/impl/TarBuildCacheEntryPackerTest.groovy
index 57a8d0d..8a67a4a 100644
--- a/subprojects/build-cache-packaging/src/test/groovy/org/gradle/caching/internal/packaging/impl/TarBuildCacheEntryPackerTest.groovy
+++ b/subprojects/build-cache-packaging/src/test/groovy/org/gradle/caching/internal/packaging/impl/TarBuildCacheEntryPackerTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.internal.file.Deleter
 import org.gradle.internal.nativeintegration.filesystem.FileSystem
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.internal.file.TreeType.DIRECTORY
 import static org.gradle.internal.file.TreeType.FILE
@@ -121,7 +121,7 @@
         "unicode" | "tree-dezső"
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "can pack tree directory with files having #type characters in name"() {
         def sourceOutputDir = temporaryFolder.file("source").createDir()
         sourceOutputDir.file(fileName) << "output"
diff --git a/subprojects/build-cache/build.gradle.kts b/subprojects/build-cache/build.gradle.kts
index 26733c1..c4bc172 100644
--- a/subprojects/build-cache/build.gradle.kts
+++ b/subprojects/build-cache/build.gradle.kts
@@ -24,6 +24,17 @@
     implementation(libs.slf4jApi)
     implementation(libs.guava)
     implementation(libs.inject)
+    implementation(libs.h2Database) {
+        because("Used in BuildCacheNG")
+    }
+    implementation(libs.hikariCP) {
+        because("Used in BuildCacheNG")
+    }
+    implementation(libs.gson) {
+        because("Used in Build Cache NG: Cache manifest uses JSON format")
+    }
+    implementation(libs.commonsIo)
+
 
     jmhImplementation(platform(project(":distributions-dependencies")))
     jmhImplementation(libs.ant)
diff --git a/subprojects/build-cache/src/integTest/groovy/org/gradle/caching/NextGenBuildCacheIntegrationTest.groovy b/subprojects/build-cache/src/integTest/groovy/org/gradle/caching/NextGenBuildCacheIntegrationTest.groovy
new file mode 100644
index 0000000..a958521
--- /dev/null
+++ b/subprojects/build-cache/src/integTest/groovy/org/gradle/caching/NextGenBuildCacheIntegrationTest.groovy
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2023 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.caching
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.server.http.BlockingHttpServer
+
+class NextGenBuildCacheIntegrationTest extends AbstractIntegrationSpec implements DirectoryBuildCacheFixture {
+
+    def "compile task is loaded from cache"() {
+        buildFile << """
+            apply plugin: "java"
+        """
+
+        file("src/main/java/Main.java") << """
+            public class Main {}
+        """
+
+        when:
+        runWithCacheNG "compileJava"
+        then:
+        executedAndNotSkipped ":compileJava"
+
+        when:
+        runWithCacheNG "clean", "compileJava"
+        then:
+        skipped ":compileJava"
+    }
+
+    def "empty output directory is cached properly"() {
+        given:
+        buildFile << """
+            task customTask {
+                def outputDir = file("build/empty")
+                outputs.dir outputDir withPropertyName "empty"
+                outputs.cacheIf { true }
+                doLast {
+                    outputDir.mkdirs()
+                }
+            }
+        """
+
+        when:
+        runWithCacheNG "customTask"
+        then:
+        executedAndNotSkipped ":customTask"
+        file("build/empty").assertIsEmptyDir()
+
+        when:
+        cleanBuildDir()
+        runWithCacheNG "customTask"
+        then:
+        skipped ":customTask"
+        file("build/empty").assertIsEmptyDir()
+    }
+
+    def "missing output is cached properly"() {
+        given:
+        buildFile << """
+            task customTask {
+                def existingFile = file("build/existing.txt")
+                def missingFile = file("build/missing.txt")
+                def missingDir = file("build/missing")
+                outputs.file existingFile withPropertyName "existing"
+                outputs.file missingFile optional() withPropertyName "missingFile"
+                outputs.dir missingDir optional() withPropertyName "missingDir"
+                outputs.cacheIf { true }
+                doLast {
+                    existingFile.text = "data"
+                    missingFile.delete()
+                    missingDir.delete()
+                }
+            }
+        """
+
+        when:
+        runWithCacheNG "customTask"
+        then:
+        executedAndNotSkipped ":customTask"
+        file("build/missing.txt").assertDoesNotExist()
+        file("build/missing").assertDoesNotExist()
+
+        when:
+        cleanBuildDir()
+        runWithCacheNG "customTask"
+        then:
+        skipped ":customTask"
+        file("build/missing").assertDoesNotExist()
+    }
+
+    def "non-cacheable task with cache enabled gets cached"() {
+        file("input.txt") << "data"
+        buildFile << """
+            class NonCacheableTask extends DefaultTask {
+                @InputFile inputFile
+                @OutputFile outputFile
+
+                @TaskAction copy() {
+                    outputFile.parentFile.mkdirs()
+                    outputFile.text = inputFile.text
+                }
+            }
+            task customTask(type: NonCacheableTask) {
+                inputFile = file("input.txt")
+                outputFile = file("\$buildDir/output.txt")
+                outputs.cacheIf { true }
+            }
+        """
+
+        when:
+        runWithCacheNG "customTask"
+        then:
+        executedAndNotSkipped ":customTask"
+
+        when:
+        cleanBuildDir()
+        runWithCacheNG "customTask"
+        then:
+        skipped ":customTask"
+    }
+
+    def "cacheable task with multiple outputs declared via runtime API with matching cardinality get cached"() {
+        buildFile << """
+            task customTask {
+                outputs.cacheIf { true }
+                def output1 = file("build/output1.txt")
+                def output2 = file("build/output2.txt")
+                outputs.files files(output1, output2) withPropertyName("out")
+                doLast {
+                    output1 << "data"
+                    output2 << "data"
+                }
+            }
+        """
+
+        when:
+        runWithCacheNG "customTask"
+        then:
+        executedAndNotSkipped ":customTask"
+
+        when:
+        cleanBuildDir()
+        runWithCacheNG "customTask"
+        then:
+        skipped ":customTask"
+    }
+
+    def "cacheable task with multiple output properties with matching cardinality get cached"() {
+        buildFile << """
+            @CacheableTask
+            class CustomTask extends DefaultTask {
+                @OutputFiles Iterable<File> out
+
+                @TaskAction
+                void execute() {
+                    out.eachWithIndex { file, index ->
+                        file.text = "data\${index + 1}"
+                    }
+                }
+            }
+
+            task customTask(type: CustomTask) {
+                out = files("build/output1.txt", "build/output2.txt")
+            }
+        """
+
+        when:
+        runWithCacheNG "customTask"
+        then:
+        executedAndNotSkipped ":customTask"
+
+        when:
+        cleanBuildDir()
+        runWithCacheNG "customTask"
+        then:
+        skipped ":customTask"
+        file("build/output1.txt").text == "data1"
+        file("build/output2.txt").text == "data2"
+    }
+
+    def "cacheable task with identical output files is cached"() {
+        buildFile << """
+            @CacheableTask
+            abstract class CustomTask extends DefaultTask {
+                @OutputDirectory abstract DirectoryProperty getOutputDir()
+
+                @TaskAction
+                void execute() {
+                    outputDir.file("output1.txt").get().asFile.text = "data"
+                    outputDir.file("output2.txt").get().asFile.text = "data"
+                    outputDir.file("output3.txt").get().asFile.text = "data"
+                }
+            }
+
+            task customTask(type: CustomTask) {
+                outputDir = layout.buildDir
+            }
+        """
+
+        when:
+        runWithCacheNG "customTask"
+        then:
+        executedAndNotSkipped ":customTask"
+        file("build/output1.txt").text == "data"
+        file("build/output2.txt").text == "data"
+        file("build/output3.txt").text == "data"
+
+        when:
+        cleanBuildDir()
+        runWithCacheNG "customTask"
+        then:
+        skipped ":customTask"
+        file("build/output1.txt").text == "data"
+        file("build/output2.txt").text == "data"
+        file("build/output3.txt").text == "data"
+    }
+
+    def "ad hoc tasks are not cacheable by default"() {
+        given:
+        file("input.txt") << "data"
+        buildFile << adHocTaskWithInputs()
+
+        expect:
+        runWithCacheNG ":adHocTask"
+        executedAndNotSkipped ":adHocTask"
+        cleanBuildDir()
+
+        runWithCacheNG ":adHocTask"
+        executedAndNotSkipped ":adHocTask"
+    }
+
+    def "ad hoc tasks are cached when explicitly requested"() {
+        given:
+        file("input.txt") << "data"
+        buildFile << adHocTaskWithInputs()
+        buildFile << 'adHocTask { outputs.cacheIf { true } }'
+
+        expect:
+        runWithCacheNG ":adHocTask"
+        executedAndNotSkipped ":adHocTask"
+        cleanBuildDir()
+
+        runWithCacheNG ":adHocTask"
+        skipped ":adHocTask"
+    }
+
+    def "cache can be accessed concurrently"() {
+        given:
+        BlockingHttpServer server = new BlockingHttpServer()
+        server.start()
+
+        buildScript("""
+            @CacheableTask
+            abstract class CustomTask extends DefaultTask {
+                @OutputFile abstract RegularFileProperty getOutputFile()
+
+                @TaskAction
+                void execute() {
+                    outputFile.asFile.get().text = "Some text"
+                }
+            }
+
+            tasks.register("block") {
+                doLast {
+                    ${server.callFromTaskAction("block")}
+                }
+            }
+
+            tasks.register("a", CustomTask) {
+                finalizedBy("block")
+                outputFile = layout.buildDirectory.file("outputA.txt")
+            }
+
+            tasks.register("b", CustomTask) {
+                outputFile = layout.buildDirectory.file("outputB.txt")
+            }
+        """)
+
+        when:
+        def block = server.expectAndBlock("block")
+        withBuildCacheNg()
+        executer.withTasks("a").start()
+        block.waitForAllPendingCalls()
+
+        runWithCacheNG("b")
+        block.releaseAll()
+
+        then:
+        skipped(":b")
+
+        cleanup:
+        server.stop()
+    }
+
+    private runWithCacheNG(String... tasks) {
+        withBuildCacheNg().run(tasks)
+    }
+
+    private TestFile cleanBuildDir() {
+        file("build").assertIsDir().deleteDir()
+    }
+
+    private static String adHocTaskWithInputs() {
+        """
+        task adHocTask {
+            def inputFile = file("input.txt")
+            def outputFile = file("\$buildDir/output.txt")
+            inputs.file(inputFile)
+            outputs.file(outputFile)
+            doLast {
+                outputFile.parentFile.mkdirs()
+                outputFile.text = inputFile.text
+            }
+        }
+        """.stripIndent()
+    }
+
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/DefaultBuildCacheKey.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/DefaultBuildCacheKey.java
new file mode 100644
index 0000000..e70a05b
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/DefaultBuildCacheKey.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 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.caching.internal;
+
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.internal.hash.HashCode;
+
+public class DefaultBuildCacheKey implements BuildCacheKey {
+    private final HashCode hashCode;
+
+    public DefaultBuildCacheKey(HashCode hashCode) {
+        this.hashCode = hashCode;
+    }
+
+    @Override
+    public String getHashCode() {
+        return hashCode.toString();
+    }
+
+    @Override
+    public byte[] toByteArray() {
+        return hashCode.toByteArray();
+    }
+
+    @Override
+    public String getDisplayName() {
+        return getHashCode();
+    }
+
+    @Override
+    public String toString() {
+        return getHashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DefaultBuildCacheKey that = (DefaultBuildCacheKey) o;
+
+        return hashCode.equals(that.hashCode);
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode.hashCode();
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/NextGenBuildCacheService.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/NextGenBuildCacheService.java
new file mode 100644
index 0000000..0b794c0
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/NextGenBuildCacheService.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 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.caching.internal;
+
+import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
+import org.gradle.api.Incubating;
+import org.gradle.caching.BuildCacheEntryWriter;
+import org.gradle.caching.BuildCacheException;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.BuildCacheService;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Build cache service with additional features for next-generation build cache implementation.
+ */
+@Incubating
+public interface NextGenBuildCacheService extends BuildCacheService  {
+    /**
+     * Returns whether the given entry exists in the cache.
+     *
+     * @param key the cache key.
+     * @return {code true} if the entry exists in the cache.
+     */
+    boolean contains(BuildCacheKey key);
+
+    @Override
+    default void store(BuildCacheKey key, BuildCacheEntryWriter legacyWriter) throws BuildCacheException {
+        NextGenWriter writer;
+        if (legacyWriter instanceof NextGenWriter) {
+            writer = (NextGenWriter) legacyWriter;
+        } else {
+            writer = new NextGenWriter() {
+                @Override
+                public InputStream openStream() throws IOException {
+                    UnsynchronizedByteArrayOutputStream data = new UnsynchronizedByteArrayOutputStream();
+                    writeTo(data);
+                    return data.toInputStream();
+                }
+
+                @Override
+                public void writeTo(OutputStream output) throws IOException {
+                    legacyWriter.writeTo(output);
+                }
+
+                @Override
+                public long getSize() {
+                    return legacyWriter.getSize();
+                }
+            };
+        }
+        store(key, writer);
+    }
+
+    void store(BuildCacheKey key, NextGenWriter writer) throws BuildCacheException;
+
+    /**
+     * A {@link BuildCacheEntryWriter} that can open an {@link InputStream} to the data instead of writing it to an {@link OutputStream}.
+     *
+     * In some backend implementations this results in better performance.
+     */
+    interface NextGenWriter extends BuildCacheEntryWriter {
+        InputStream openStream() throws IOException;
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/NoOpBuildCacheService.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/NoOpBuildCacheService.java
new file mode 100644
index 0000000..c5beaf1
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/NoOpBuildCacheService.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 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.caching.internal;
+
+import org.gradle.caching.BuildCacheEntryReader;
+import org.gradle.caching.BuildCacheEntryWriter;
+import org.gradle.caching.BuildCacheException;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.BuildCacheService;
+
+import java.io.IOException;
+
+public class NoOpBuildCacheService implements NextGenBuildCacheService {
+    public static final BuildCacheService INSTANCE = new NoOpBuildCacheService();
+
+    private NoOpBuildCacheService() {
+    }
+
+    @Override
+    public boolean contains(BuildCacheKey key) {
+        return false;
+    }
+
+    @Override
+    public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+        return false;
+    }
+
+    @Override
+    public void store(BuildCacheKey key, BuildCacheEntryWriter legacyWriter) throws BuildCacheException {
+    }
+
+    @Override
+    public void store(BuildCacheKey key, NextGenWriter writer) throws BuildCacheException {
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/StatefulNextGenBuildCacheService.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/StatefulNextGenBuildCacheService.java
new file mode 100644
index 0000000..983e642
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/StatefulNextGenBuildCacheService.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.caching.internal;
+
+import java.io.Closeable;
+
+public interface StatefulNextGenBuildCacheService extends NextGenBuildCacheService, Closeable {
+    void open();
+
+    @Override
+    void close();
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/CacheManifest.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/CacheManifest.java
new file mode 100644
index 0000000..50a9582
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/CacheManifest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023 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.caching.internal.controller;
+
+import org.gradle.caching.internal.origin.OriginMetadata;
+import org.gradle.internal.file.FileType;
+import org.gradle.internal.hash.HashCode;
+
+import java.util.List;
+import java.util.Map;
+
+public class CacheManifest {
+    private final OriginMetadata originMetadata;
+    private final Map<String, List<ManifestEntry>> propertyManifests;
+
+    public CacheManifest(OriginMetadata originMetadata, Map<String, List<ManifestEntry>> propertyManifests) {
+        this.originMetadata = originMetadata;
+        this.propertyManifests = propertyManifests;
+    }
+
+    public OriginMetadata getOriginMetadata() {
+        return originMetadata;
+    }
+
+    public Map<String, List<ManifestEntry>> getPropertyManifests() {
+        return propertyManifests;
+    }
+
+    public static class ManifestEntry {
+        private final FileType type;
+        private final String relativePath;
+        private final HashCode contentHash;
+        private final long length;
+
+        public ManifestEntry(FileType type, String relativePath, HashCode contentHash, long length) {
+            this.type = type;
+            this.relativePath = relativePath;
+            this.contentHash = contentHash;
+            this.length = length;
+        }
+
+        public FileType getType() {
+            return type;
+        }
+
+        public String getRelativePath() {
+            return relativePath;
+        }
+
+        public HashCode getContentHash() {
+            return contentHash;
+        }
+
+        public long getLength() {
+            return length;
+        }
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/DefaultNextGenBuildCacheAccess.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/DefaultNextGenBuildCacheAccess.java
new file mode 100644
index 0000000..f6dfea8
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/DefaultNextGenBuildCacheAccess.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2023 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.caching.internal.controller;
+
+import com.google.common.io.Closer;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.internal.NextGenBuildCacheService;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.concurrent.ManagedThreadPoolExecutor;
+import org.gradle.internal.file.BufferProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Stream;
+
+public class DefaultNextGenBuildCacheAccess implements NextGenBuildCacheAccess {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultNextGenBuildCacheAccess.class);
+
+    private final NextGenBuildCacheHandler local;
+    private final NextGenBuildCacheHandler remote;
+    private final BufferProvider bufferProvider;
+    private final ManagedThreadPoolExecutor remoteProcessor;
+    private final ConcurrencyCounter counter;
+
+    public DefaultNextGenBuildCacheAccess(
+        NextGenBuildCacheHandler local,
+        NextGenBuildCacheHandler remote,
+        BufferProvider bufferProvider,
+        ExecutorFactory executorFactory
+    ) {
+        this.local = local;
+        this.remote = remote;
+        this.bufferProvider = bufferProvider;
+        // TODO Configure this properly
+        this.remoteProcessor = executorFactory.createThreadPool("Build cache access", 256, 256, 10, TimeUnit.SECONDS);
+        this.counter = new ConcurrencyCounter(remoteProcessor);
+    }
+
+    @Override
+    public <T> void load(Map<BuildCacheKey, T> entries, LoadHandler<T> handler) {
+        CompletableFuture<?>[] asyncLoads = entries.entrySet().stream()
+            .flatMap(entry -> {
+                BuildCacheKey key = entry.getKey();
+                T payload = entry.getValue();
+                boolean foundLocally = local.canLoad() && local.load(key, input -> handler.handle(input, payload));
+                if (!foundLocally && remote.canLoad()) {
+                    // TODO Improve error handling
+                    return Stream.of(CompletableFuture.runAsync(counter.wrap(new RemoteDownload<>(key, payload, handler)), remoteProcessor));
+                } else {
+                    return Stream.empty();
+                }
+            })
+            .toArray(CompletableFuture[]::new);
+        CompletableFuture.allOf(asyncLoads)
+            .join();
+    }
+
+    @Override
+    public <T> void store(Map<BuildCacheKey, T> entries, StoreHandler<T> handler) {
+        CompletableFuture<?>[] asyncStores = entries.entrySet().stream()
+            .flatMap(entry -> {
+                BuildCacheKey key = entry.getKey();
+                T payload = entry.getValue();
+                if (!local.canStore()) {
+                    // TODO Handle the case when local store is disabled but the remote is not?
+                    return Stream.empty();
+                }
+                if (!local.contains(key)) {
+                    local.store(key, handler.handle(payload));
+                }
+                // TODO Improve error handling
+                if (remote.canStore()) {
+                    return Stream.of(CompletableFuture.runAsync(counter.wrap(new RemoteUpload(key)), remoteProcessor));
+                } else {
+                    return Stream.empty();
+                }
+            })
+            .toArray(CompletableFuture[]::new);
+        CompletableFuture.allOf(asyncStores)
+            .join();
+    }
+
+    @Override
+    public void close() throws IOException {
+        Closer closer = Closer.create();
+        closer.register(() -> {
+            if (!remoteProcessor.getQueue().isEmpty() || remoteProcessor.getActiveCount() > 0) {
+                LOGGER.warn("Waiting for remote cache uploads to finish");
+            }
+            try {
+                remoteProcessor.stop(5, TimeUnit.MINUTES);
+            } catch (RuntimeException e) {
+                throw new RuntimeException("Couldn't finish uploading all remote entries", e);
+            }
+        });
+        closer.register(local);
+        closer.register(remote);
+        closer.register(counter);
+        closer.close();
+    }
+
+    private static class ConcurrencyCounter implements Closeable {
+        private final AtomicInteger maxQueueLength = new AtomicInteger(0);
+        private final AtomicInteger maxActiveCount = new AtomicInteger(0);
+
+        private final ManagedThreadPoolExecutor processor;
+
+        public ConcurrencyCounter(ManagedThreadPoolExecutor processor) {
+            this.processor = processor;
+        }
+
+        public Runnable wrap(Runnable delegate) {
+            return () -> {
+                maxQueueLength.updateAndGet(max -> Integer.max(max, processor.getQueue().size()));
+                maxActiveCount.updateAndGet(max -> Integer.max(max, processor.getActiveCount()));
+                delegate.run();
+            };
+        }
+
+        @Override
+        public void close() {
+            LOGGER.warn("Max concurrency encountered while processing remote cache entries: {}, max queue length: {}",
+                maxActiveCount.get(), maxQueueLength.get());
+        }
+    }
+
+    private class RemoteDownload<T> implements Runnable {
+        private final BuildCacheKey key;
+        private final T payload;
+        private final LoadHandler<T> handler;
+
+        public RemoteDownload(BuildCacheKey key, T payload, LoadHandler<T> handler) {
+            this.key = key;
+            this.payload = payload;
+            this.handler = handler;
+        }
+
+        @Override
+        public void run() {
+            LOGGER.warn("Loading {} from remote", key);
+            remote.load(key, input -> {
+                // TODO Make this work for large pieces of content, too
+                UnsynchronizedByteArrayOutputStream data = new UnsynchronizedByteArrayOutputStream();
+                byte[] buffer = bufferProvider.getBuffer();
+                IOUtils.copyLarge(input, data, buffer);
+                LOGGER.warn("Found {} in remote (size: {})", key, data.size());
+
+                // Mirror data in local cache
+                // TODO Do we need to support local push = false?
+                if (local.canStore()) {
+                    local.store(key, new NextGenBuildCacheService.NextGenWriter() {
+                        @Override
+                        public InputStream openStream() {
+                            return data.toInputStream();
+                        }
+
+                        @Override
+                        public void writeTo(OutputStream output) throws IOException {
+                            data.writeTo(output);
+                        }
+
+                        @Override
+                        public long getSize() {
+                            return data.size();
+                        }
+                    });
+                    handler.handle(data.toInputStream(), payload);
+                } else {
+                    handler.handle(input, payload);
+                }
+            });
+        }
+    }
+
+    private class RemoteUpload implements Runnable {
+        private final BuildCacheKey key;
+
+        public RemoteUpload(BuildCacheKey key) {
+            this.key = key;
+        }
+
+        @Override
+        public void run() {
+            // TODO Check contains only above a threshold
+            if (remote.contains(key)) {
+                LOGGER.warn("Not storing {} in remote", key);
+                return;
+            }
+
+            // TODO Use a buffer fit for large files
+            @SuppressWarnings("resource")
+            UnsynchronizedByteArrayOutputStream data = new UnsynchronizedByteArrayOutputStream();
+            boolean foundLocally = local.load(key, input -> IOUtils.copyLarge(input, data, bufferProvider.getBuffer()));
+            // TODO Handle this better? Do we need to hadnle it at all?
+            if (!foundLocally) {
+                throw new IllegalStateException("Couldn't store " + key + " in remote cache because it was missing from local cache");
+            }
+
+            LOGGER.warn("Storing {} in remote (size: {})", key, data.size());
+            remote.store(key, new NextGenBuildCacheService.NextGenWriter() {
+                @Override
+                public InputStream openStream() {
+                    return data.toInputStream();
+                }
+
+                @Override
+                public void writeTo(OutputStream output) throws IOException {
+                    data.writeTo(output);
+                }
+
+                @Override
+                public long getSize() {
+                    return data.size();
+                }
+            });
+        }
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/GZipNextGenBuildCacheAccess.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/GZipNextGenBuildCacheAccess.java
new file mode 100644
index 0000000..056fd34
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/GZipNextGenBuildCacheAccess.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023 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.caching.internal.controller;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.internal.NextGenBuildCacheService;
+import org.gradle.internal.file.BufferProvider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class GZipNextGenBuildCacheAccess implements NextGenBuildCacheAccess {
+    private final NextGenBuildCacheAccess delegate;
+    private final BufferProvider bufferProvider;
+
+    public GZipNextGenBuildCacheAccess(NextGenBuildCacheAccess delegate, BufferProvider bufferProvider) {
+        this.delegate = delegate;
+        this.bufferProvider = bufferProvider;
+    }
+
+    @Override
+    public <T> void load(Map<BuildCacheKey, T> entries, LoadHandler<T> handler) {
+        delegate.load(entries, (inputStream, payload) -> {
+            try (GZIPInputStream zipInput = new GZIPInputStream(inputStream)) {
+                handler.handle(zipInput, payload);
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        });
+    }
+
+    @Override
+    public <T> void store(Map<BuildCacheKey, T> entries, StoreHandler<T> handler) {
+        delegate.store(entries, payload -> {
+            NextGenBuildCacheService.NextGenWriter delegateWriter = handler.handle(payload);
+            // TODO Make this more performant for large files
+            UnsynchronizedByteArrayOutputStream compressed = new UnsynchronizedByteArrayOutputStream((int) (delegateWriter.getSize() * 1.2));
+            try (GZIPOutputStream zipOutput = new GZIPOutputStream(compressed)) {
+                try (InputStream delegateInput = delegateWriter.openStream()) {
+                    IOUtils.copyLarge(delegateInput, zipOutput, bufferProvider.getBuffer());
+                }
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+
+            return new NextGenBuildCacheService.NextGenWriter() {
+                @Override
+                public InputStream openStream() {
+                    return compressed.toInputStream();
+                }
+
+                @Override
+                public void writeTo(OutputStream output) throws IOException {
+                    compressed.writeTo(output);
+                }
+
+                @Override
+                public long getSize() {
+                    return compressed.size();
+                }
+            };
+        });
+    }
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheAccess.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheAccess.java
new file mode 100644
index 0000000..330460e
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheAccess.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.caching.internal.controller;
+
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.internal.NextGenBuildCacheService;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.util.Map;
+
+public interface NextGenBuildCacheAccess extends Closeable {
+    <T> void load(Map<BuildCacheKey, T> entries, LoadHandler<T> handler);
+
+    <T> void store(Map<BuildCacheKey, T> entries, StoreHandler<T> handler);
+
+    interface LoadHandler<T> {
+        void handle(InputStream input, T payload);
+    }
+
+    interface StoreHandler<T> {
+        NextGenBuildCacheService.NextGenWriter handle(T payload);
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheController.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheController.java
new file mode 100644
index 0000000..3d9c31d
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheController.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2023 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.caching.internal.controller;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Closer;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
+import org.apache.commons.io.output.NullOutputStream;
+import org.apache.commons.io.output.TeeOutputStream;
+import org.gradle.api.internal.cache.StringInterner;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.internal.CacheableEntity;
+import org.gradle.caching.internal.DefaultBuildCacheKey;
+import org.gradle.caching.internal.NextGenBuildCacheService;
+import org.gradle.caching.internal.controller.CacheManifest.ManifestEntry;
+import org.gradle.caching.internal.controller.service.BuildCacheLoadResult;
+import org.gradle.caching.internal.origin.OriginMetadata;
+import org.gradle.caching.internal.packaging.impl.RelativePathParser;
+import org.gradle.internal.file.BufferProvider;
+import org.gradle.internal.file.Deleter;
+import org.gradle.internal.file.FileType;
+import org.gradle.internal.file.TreeType;
+import org.gradle.internal.file.impl.DefaultFileMetadata;
+import org.gradle.internal.hash.HashCode;
+import org.gradle.internal.snapshot.DirectorySnapshotBuilder;
+import org.gradle.internal.snapshot.FileSystemLocationSnapshot;
+import org.gradle.internal.snapshot.FileSystemSnapshot;
+import org.gradle.internal.snapshot.MerkleDirectorySnapshotBuilder;
+import org.gradle.internal.snapshot.RegularFileSnapshot;
+import org.gradle.internal.snapshot.RelativePathTracker;
+import org.gradle.internal.snapshot.SnapshotUtil;
+import org.gradle.internal.snapshot.SnapshotVisitResult;
+import org.gradle.internal.vfs.FileSystemAccess;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.gradle.internal.file.FileMetadata.AccessType.DIRECT;
+import static org.gradle.internal.file.FileType.Directory;
+import static org.gradle.internal.snapshot.DirectorySnapshotBuilder.EmptyDirectoryHandlingStrategy.INCLUDE_EMPTY_DIRS;
+
+public class NextGenBuildCacheController implements BuildCacheController {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(NextGenBuildCacheController.class);
+
+    public static final String NEXT_GEN_CACHE_SYSTEM_PROPERTY = "org.gradle.unsafe.cache.ng";
+
+    private final BufferProvider bufferProvider;
+    private final NextGenBuildCacheAccess cacheAccess;
+    private final FileSystemAccess fileSystemAccess;
+    private final String buildInvocationId;
+    private final Deleter deleter;
+    private final StringInterner stringInterner;
+    private final Gson gson;
+
+    public NextGenBuildCacheController(
+        String buildInvocationId,
+        Deleter deleter,
+        FileSystemAccess fileSystemAccess,
+        BufferProvider bufferProvider,
+        StringInterner stringInterner,
+        NextGenBuildCacheAccess cacheAccess
+    ) {
+        this.buildInvocationId = buildInvocationId;
+        this.deleter = deleter;
+        this.fileSystemAccess = fileSystemAccess;
+        this.bufferProvider = bufferProvider;
+        this.cacheAccess = cacheAccess;
+        this.stringInterner = stringInterner;
+        this.gson = new GsonBuilder()
+            .registerTypeAdapter(Duration.class, new TypeAdapter<Duration>() {
+                @Override
+                public void write(JsonWriter out, Duration value) throws IOException {
+                    out.value(value.toMillis());
+                }
+
+                @Override
+                public Duration read(JsonReader in) throws IOException {
+                    return Duration.ofMillis(in.nextLong());
+                }
+            })
+            .registerTypeAdapter(HashCode.class, new TypeAdapter<HashCode>() {
+                @Override
+                public void write(JsonWriter out, HashCode value) throws IOException {
+                    out.value(value.toString());
+                }
+
+                @Override
+                public HashCode read(JsonReader in) throws IOException {
+                    return HashCode.fromString(in.nextString());
+                }
+            })
+            .create();
+
+        LOGGER.warn("Creating next-generation build cache controller");
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean isEmitDebugLogging() {
+        return false;
+    }
+
+    @Override
+    public Optional<BuildCacheLoadResult> load(BuildCacheKey manifestCacheKey, CacheableEntity cacheableEntity) {
+        // TODO Make load() return T
+        AtomicReference<CacheManifest> manifestRef = new AtomicReference<>();
+        cacheAccess.load(Collections.singletonMap(manifestCacheKey, null), (manifestStream, __) ->
+            manifestRef.set(gson.fromJson(new InputStreamReader(manifestStream), CacheManifest.class)));
+        CacheManifest manifest = manifestRef.get();
+        if (manifest == null) {
+            return Optional.empty();
+        }
+
+        AtomicLong loadedEntryCount = new AtomicLong(0);
+        ImmutableSortedMap.Builder<String, FileSystemSnapshot> snapshots = ImmutableSortedMap.naturalOrder();
+
+        cacheableEntity.visitOutputTrees((propertyName, type, root) -> {
+            // Invalidate VFS
+            fileSystemAccess.write(Collections.singleton(root.getAbsolutePath()), () -> {});
+
+            // TODO Apply diff to outputs instead of clearing them here and loading everything
+            try {
+                cleanOutputDirectory(type, root);
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+
+            // Note that there can be multiple output files with the same content
+            ImmutableListMultimap.Builder<BuildCacheKey, File> filesBuilder = ImmutableListMultimap.builder();
+            List<ManifestEntry> manifestEntries = manifest.getPropertyManifests().get(propertyName);
+            manifestEntries.forEach(entry -> {
+                File file = new File(root, entry.getRelativePath());
+                switch (entry.getType()) {
+                    case Directory:
+                        // TODO set correct file permissions
+                        // TODO Handle this
+                        //noinspection ResultOfMethodCallIgnored
+                        file.mkdirs();
+                        break;
+                    case RegularFile:
+                        // TODO set correct file permissions
+                        filesBuilder.put(new DefaultBuildCacheKey(entry.getContentHash()), file);
+                        break;
+                    case Missing:
+                        FileUtils.deleteQuietly(file);
+                        break;
+                }
+            });
+
+            // TODO Filter out entries that are already in the right place in the output directory
+            cacheAccess.load(filesBuilder.build().asMap(), (input, filesForHash) -> {
+                loadedEntryCount.addAndGet(filesForHash.size());
+
+                try (Closer closer = Closer.create()) {
+                    OutputStream output = filesForHash.stream()
+                        .map(file -> {
+                            try {
+                                return closer.register(new FileOutputStream(file));
+                            } catch (FileNotFoundException e) {
+                                throw new UncheckedIOException("Couldn't create " + file.getAbsolutePath(), e);
+                            }
+                        })
+                        .map(OutputStream.class::cast)
+                        .reduce(TeeOutputStream::new)
+                        .orElse(NullOutputStream.NULL_OUTPUT_STREAM);
+
+                    IOUtils.copyLarge(input, output, bufferProvider.getBuffer());
+                } catch (IOException ex) {
+                    throw new UncheckedIOException(ex);
+                }
+            });
+
+            createSnapshot(type, root, manifestEntries)
+                .ifPresent(snapshot -> {
+                    snapshots.put(propertyName, snapshot);
+                    fileSystemAccess.record(snapshot);
+                });
+        });
+
+        ImmutableSortedMap<String, FileSystemSnapshot> resultingSnapshots = snapshots.build();
+        return Optional.of(new BuildCacheLoadResult() {
+            @Override
+            public long getArtifactEntryCount() {
+                return loadedEntryCount.get();
+            }
+
+            @Override
+            public OriginMetadata getOriginMetadata() {
+                return manifest.getOriginMetadata();
+            }
+
+            @Override
+            public ImmutableSortedMap<String, FileSystemSnapshot> getResultingSnapshots() {
+                return resultingSnapshots;
+            }
+        });
+    }
+
+    // TODO Extract snapshotting part to it's own class
+    @VisibleForTesting
+    Optional<FileSystemLocationSnapshot> createSnapshot(TreeType type, File root, List<ManifestEntry> entries) {
+        switch (type) {
+            case DIRECTORY:
+                return Optional.of(createDirectorySnapshot(root, entries));
+            case FILE:
+                if (entries.size() != 1) {
+                    throw new IllegalStateException("Expected a single manifest entry, found " + entries.size());
+                }
+                ManifestEntry rootEntry = entries.get(0);
+                switch (rootEntry.getType()) {
+                    case Directory:
+                        throw new IllegalStateException("Directory manifest entry found for a file output");
+                    case RegularFile:
+                        return Optional.of(createFileSnapshot(rootEntry, root));
+                    case Missing:
+                        // No need to create a
+                        return Optional.empty();
+                    default:
+                        throw new AssertionError("Unknown manifest entry type " + rootEntry.getType());
+                }
+            default:
+                throw new AssertionError("Unknown output type " + type);
+        }
+    }
+
+    // TODO We should not capture any snapshot for a missing directory output
+    private FileSystemLocationSnapshot createDirectorySnapshot(File root, List<ManifestEntry> entries) {
+        String rootPath = root.getName() + "/";
+        RelativePathParser parser = new RelativePathParser(rootPath);
+        DirectorySnapshotBuilder builder = MerkleDirectorySnapshotBuilder.noSortingRequired();
+        builder.enterDirectory(DIRECT, stringInterner.intern(root.getAbsolutePath()), stringInterner.intern(root.getName()), INCLUDE_EMPTY_DIRS);
+
+        for (ManifestEntry entry : Iterables.skip(entries, 1)) {
+            File file = new File(root, entry.getRelativePath());
+
+            boolean isDirectory = entry.getType() == Directory;
+            String relativePath = isDirectory
+                ? rootPath + entry.getRelativePath() + "/"
+                : rootPath + entry.getRelativePath();
+            boolean outsideOfRoot = parser.nextPath(relativePath, isDirectory, builder::leaveDirectory);
+            if (outsideOfRoot) {
+                break;
+            }
+
+            switch (entry.getType()) {
+                case Directory:
+                    String internedAbsolutePath = stringInterner.intern(file.getAbsolutePath());
+                    String internedName = stringInterner.intern(parser.getName());
+                    builder.enterDirectory(DIRECT, internedAbsolutePath, internedName, INCLUDE_EMPTY_DIRS);
+                    break;
+                case RegularFile:
+                    RegularFileSnapshot fileSnapshot = createFileSnapshot(entry, file);
+                    builder.visitLeafElement(fileSnapshot);
+                    break;
+                case Missing:
+                    // No need to store a snapshot for a missing file
+                    break;
+            }
+        }
+
+        parser.exitToRoot(builder::leaveDirectory);
+        builder.leaveDirectory();
+        return checkNotNull(builder.getResult());
+    }
+
+    private RegularFileSnapshot createFileSnapshot(CacheManifest.ManifestEntry entry, File file) {
+        return new RegularFileSnapshot(
+            stringInterner.intern(file.getAbsolutePath()),
+            stringInterner.intern(file.getName()),
+            entry.getContentHash(),
+            DefaultFileMetadata.file(file.lastModified(), entry.getLength(), DIRECT)
+        );
+    }
+
+    @Override
+    public void store(BuildCacheKey manifestCacheKey, CacheableEntity entity, Map<String, FileSystemSnapshot> snapshots, Duration executionTime) {
+        ImmutableMap.Builder<String, List<ManifestEntry>> propertyManifests = ImmutableMap.builder();
+
+        entity.visitOutputTrees((propertyName, type, root) -> {
+            ImmutableList.Builder<ManifestEntry> manifestEntries = ImmutableList.builder();
+            FileSystemSnapshot rootSnapshot = snapshots.get(propertyName);
+            rootSnapshot.accept(new RelativePathTracker(), (snapshot, relativePath) -> {
+                if (relativePath.isRoot()) {
+                    assertCorrectType(type, snapshot);
+                }
+                manifestEntries.add(new ManifestEntry(
+                    snapshot.getType(),
+                    relativePath.toRelativePath(),
+                    snapshot.getHash(),
+                    SnapshotUtil.getLength(snapshot)));
+                return SnapshotVisitResult.CONTINUE;
+            });
+            propertyManifests.put(propertyName, manifestEntries.build());
+        });
+
+        CacheManifest manifest = new CacheManifest(
+            new OriginMetadata(buildInvocationId, executionTime),
+            propertyManifests.build());
+
+        entity.visitOutputTrees((propertyName, type, root) -> {
+            Map<BuildCacheKey, ManifestEntry> manifestIndex = manifest.getPropertyManifests().get(propertyName).stream()
+                .filter(entry -> entry.getType() == FileType.RegularFile)
+                .collect(ImmutableMap.toImmutableMap(
+                    manifestEntry -> new DefaultBuildCacheKey(manifestEntry.getContentHash()),
+                    Function.identity(),
+                    // When there are multiple identical files to store, it doesn't matter which one we read
+                    (a, b) -> a)
+                );
+
+            cacheAccess.store(manifestIndex, manifestEntry -> new NextGenBuildCacheService.NextGenWriter() {
+                @Override
+                public InputStream openStream() throws IOException {
+                    // TODO Replace with "Files.newInputStream()" as it seems to be more efficient
+                    //      Might be a good idea to pass `root` as `Path` instead of `File` then
+                    //noinspection IOStreamConstructor
+                    return new FileInputStream(new File(root, manifestEntry.getRelativePath()));
+                }
+
+                @Override
+                public void writeTo(OutputStream output) throws IOException {
+                    try (InputStream input = openStream()) {
+                        IOUtils.copyLarge(input, output, bufferProvider.getBuffer());
+                    }
+                }
+
+                @Override
+                public long getSize() {
+                    return manifestEntry.getLength();
+                }
+            });
+        });
+
+        cacheAccess.store(Collections.singletonMap(manifestCacheKey, manifest), __ -> {
+            String manifestJson = gson.toJson(manifest);
+            byte[] bytes = manifestJson.getBytes(StandardCharsets.UTF_8);
+
+            return new NextGenBuildCacheService.NextGenWriter() {
+                @Override
+                public InputStream openStream() {
+                    return new UnsynchronizedByteArrayInputStream(bytes);
+                }
+
+                @Override
+                public void writeTo(OutputStream output) throws IOException {
+                    output.write(bytes);
+                }
+
+                @Override
+                public long getSize() {
+                    return bytes.length;
+                }
+            };
+        });
+    }
+
+    private static void assertCorrectType(TreeType type, FileSystemLocationSnapshot snapshot) {
+        if (snapshot.getType() == FileType.Missing) {
+            return;
+        }
+        switch (type) {
+            case DIRECTORY:
+                if (snapshot.getType() != Directory) {
+                    throw new IllegalArgumentException(String.format("Expected '%s' to be a directory", snapshot.getAbsolutePath()));
+                }
+                break;
+            case FILE:
+                if (snapshot.getType() != FileType.RegularFile) {
+                    throw new IllegalArgumentException(String.format("Expected '%s' to be a file", snapshot.getAbsolutePath()));
+                }
+                break;
+            default:
+                throw new AssertionError();
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        LOGGER.warn("Closing next-generation build cache controller");
+        cacheAccess.close();
+    }
+
+    // FIXME code duplicate
+    private void cleanOutputDirectory(TreeType type, File root) throws IOException {
+        switch (type) {
+            case DIRECTORY:
+                deleter.ensureEmptyDirectory(root);
+                break;
+            case FILE:
+                if (!makeDirectory(root.getParentFile())) {
+                    if (root.exists()) {
+                        deleter.deleteRecursively(root);
+                    }
+                }
+                break;
+            default:
+                throw new AssertionError();
+        }
+    }
+
+    // FIXME code duplicate
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    private boolean makeDirectory(File target) throws IOException {
+        if (target.isDirectory()) {
+            return false;
+        } else if (target.isFile()) {
+            deleter.delete(target);
+        }
+        FileUtils.forceMkdir(target);
+        return true;
+    }
+
+    public static boolean isNextGenCachingEnabled() {
+        return Boolean.getBoolean(NEXT_GEN_CACHE_SYSTEM_PROPERTY) == Boolean.TRUE;
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheHandler.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheHandler.java
new file mode 100644
index 0000000..dcc377b
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/NextGenBuildCacheHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 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.caching.internal.controller;
+
+import org.gradle.caching.BuildCacheEntryReader;
+import org.gradle.caching.BuildCacheException;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.internal.NextGenBuildCacheService;
+
+import java.io.Closeable;
+
+public interface NextGenBuildCacheHandler extends Closeable {
+    boolean canLoad();
+
+    boolean canStore();
+
+    boolean contains(BuildCacheKey key);
+
+    boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException;
+
+    void store(BuildCacheKey key, NextGenBuildCacheService.NextGenWriter writer) throws BuildCacheException;
+
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/H2BuildCacheService.java b/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/H2BuildCacheService.java
new file mode 100644
index 0000000..0f00c71
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/H2BuildCacheService.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2023 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.caching.local.internal;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import org.gradle.caching.BuildCacheEntryReader;
+import org.gradle.caching.BuildCacheException;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.internal.NextGenBuildCacheService;
+import org.gradle.caching.internal.StatefulNextGenBuildCacheService;
+import org.h2.Driver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class H2BuildCacheService implements NextGenBuildCacheService, StatefulNextGenBuildCacheService {
+
+    private HikariDataSource dataSource;
+    private final Path dbPath;
+    private final int maxPoolSize;
+
+    public H2BuildCacheService(Path dbPath, int maxPoolSize) {
+        this.dbPath = dbPath;
+        this.maxPoolSize = maxPoolSize;
+    }
+
+    @Override
+    public void open() {
+        HikariConfig hikariConfig = new HikariConfig();
+        // RETENTION_TIME=0 prevents uncontrolled DB growth with old pages retention
+        // AUTO_COMPACT_FILL_RATE=0 disables compacting, we will compact on cleanup
+        // COMPRESS=false disables compression, we already do gzip compression
+        // We use MODE=MySQL so we can use INSERT IGNORE
+        String h2JdbcUrl = String.format("jdbc:h2:file:%s;RETENTION_TIME=0;AUTO_COMPACT_FILL_RATE=0;COMPRESS=false;MODE=MySQL;INIT=runscript from 'classpath:/h2/schemas/org.gradle.caching.local.internal.H2BuildCacheService.sql'", dbPath.resolve("filestore"));
+        hikariConfig.setJdbcUrl(h2JdbcUrl);
+        hikariConfig.setDriverClassName(Driver.class.getName());
+        hikariConfig.setUsername("sa");
+        hikariConfig.setPassword("");
+        hikariConfig.setCatalog("filestore");
+        hikariConfig.setPoolName("filestore-pool");
+        hikariConfig.setMaximumPoolSize(maxPoolSize);
+        hikariConfig.setConnectionInitSql("select 1;");
+        this.dataSource = new HikariDataSource(hikariConfig);
+    }
+
+    @Override
+    public boolean contains(BuildCacheKey key) {
+        try (Connection conn = dataSource.getConnection()) {
+            try (PreparedStatement stmt = conn.prepareStatement("select entry_key from filestore.catalog where entry_key = ?")) {
+                stmt.setString(1, key.getHashCode());
+                try (ResultSet rs = stmt.executeQuery()) {
+                    return rs.next();
+                }
+            }
+        } catch (SQLException e) {
+            throw new BuildCacheException("contains " + key, e);
+        }
+    }
+
+    @Override
+    public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+        try (Connection conn = dataSource.getConnection()) {
+            try (PreparedStatement stmt = conn.prepareStatement("select entry_content from filestore.catalog where entry_key = ?")) {
+                stmt.setString(1, key.getHashCode());
+                try (ResultSet rs = stmt.executeQuery()) {
+                    if (rs.next()) {
+                        Blob content = rs.getBlob(1);
+                        try (InputStream binaryStream = content.getBinaryStream()) {
+                            reader.readFrom(binaryStream);
+                        }
+                        return true;
+                    }
+                    return false;
+                }
+            }
+        } catch (SQLException | IOException e) {
+            throw new BuildCacheException("loading " + key, e);
+        }
+    }
+
+    @Override
+    public void store(BuildCacheKey key, NextGenWriter writer) throws BuildCacheException {
+        try (Connection conn = dataSource.getConnection()) {
+            try (PreparedStatement stmt = conn.prepareStatement("insert ignore into filestore.catalog(entry_key, entry_size, entry_content) values (?, ?, ?)")) {
+                try (InputStream input = writer.openStream()) {
+                    stmt.setString(1, key.getHashCode());
+                    stmt.setLong(2, writer.getSize());
+                    stmt.setBinaryStream(3, input);
+                    stmt.executeUpdate();
+                }
+            }
+        } catch (SQLException | IOException e) {
+            throw new BuildCacheException("storing " + key, e);
+        }
+    }
+
+    @Override
+    public void close() {
+        dataSource.close();
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/H2BuildCacheServiceFactory.java b/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/H2BuildCacheServiceFactory.java
new file mode 100644
index 0000000..2b067c5
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/H2BuildCacheServiceFactory.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017 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.caching.local.internal;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.cache.FileLockManager;
+import org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory;
+import org.gradle.caching.BuildCacheService;
+import org.gradle.caching.BuildCacheServiceFactory;
+import org.gradle.caching.local.DirectoryBuildCache;
+import org.gradle.concurrent.ParallelismConfiguration;
+import org.gradle.internal.file.PathToFileResolver;
+
+import javax.inject.Inject;
+import java.io.File;
+
+public class H2BuildCacheServiceFactory implements BuildCacheServiceFactory<DirectoryBuildCache> {
+    private static final String BUILD_CACHE_VERSION = "2";
+    private static final String BUILD_CACHE_KEY = "build-cache-" + BUILD_CACHE_VERSION;
+    private static final String H2_BUILD_CACHE_TYPE = "h2";
+
+    private final FileLockManager lockManager;
+    private final GlobalScopedCacheBuilderFactory cacheBuilderFactory;
+    private final ParallelismConfiguration parallelismConfiguration;
+    private final PathToFileResolver resolver;
+
+    @Inject
+    public H2BuildCacheServiceFactory(
+        FileLockManager lockManager,
+        GlobalScopedCacheBuilderFactory cacheBuilderFactory,
+        ParallelismConfiguration parallelismConfiguration,
+        PathToFileResolver resolver
+    ) {
+        this.lockManager = lockManager;
+        this.cacheBuilderFactory = cacheBuilderFactory;
+        this.parallelismConfiguration = parallelismConfiguration;
+        this.resolver = resolver;
+    }
+
+    @Override
+    public BuildCacheService createBuildCacheService(DirectoryBuildCache configuration, Describer describer) {
+        Object cacheDirectory = configuration.getDirectory();
+        File target;
+        if (cacheDirectory != null) {
+            target = resolver.resolve(cacheDirectory);
+        } else {
+            target = cacheBuilderFactory.baseDirForCrossVersionCache(BUILD_CACHE_KEY);
+        }
+        checkDirectory(target);
+
+        int removeUnusedEntriesAfterDays = configuration.getRemoveUnusedEntriesAfterDays();
+        describer.type(H2_BUILD_CACHE_TYPE).
+            config("location", target.getAbsolutePath());
+
+        H2BuildCacheService h2Service = new H2BuildCacheService(target.toPath(), parallelismConfiguration.getMaxWorkerCount());
+        return new LockOnDemandCrossProcessBuildCacheService("build-cache-2", target, lockManager, h2Service);
+    }
+
+    private static void checkDirectory(File directory) {
+        if (directory.exists()) {
+            if (!directory.isDirectory()) {
+                throw new IllegalArgumentException(String.format("Cache directory %s must be a directory", directory));
+            }
+            if (!directory.canRead()) {
+                throw new IllegalArgumentException(String.format("Cache directory %s must be readable", directory));
+            }
+            if (!directory.canWrite()) {
+                throw new IllegalArgumentException(String.format("Cache directory %s must be writable", directory));
+            }
+        } else {
+            if (!directory.mkdirs()) {
+                throw new UncheckedIOException(String.format("Could not create cache directory: %s", directory));
+            }
+        }
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/LockOnDemandCrossProcessBuildCacheService.java b/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/LockOnDemandCrossProcessBuildCacheService.java
new file mode 100644
index 0000000..9c0ed5f
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/local/internal/LockOnDemandCrossProcessBuildCacheService.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2023 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.caching.local.internal;
+
+import org.gradle.api.Action;
+import org.gradle.cache.FileLock;
+import org.gradle.cache.FileLockManager;
+import org.gradle.cache.internal.AbstractCrossProcessCacheAccess;
+import org.gradle.cache.internal.LockOnDemandCrossProcessCacheAccess;
+import org.gradle.cache.internal.cacheops.CacheAccessOperationsStack;
+import org.gradle.cache.internal.filelock.LockOptionsBuilder;
+import org.gradle.caching.BuildCacheEntryReader;
+import org.gradle.caching.BuildCacheEntryWriter;
+import org.gradle.caching.BuildCacheException;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.internal.NextGenBuildCacheService;
+import org.gradle.caching.internal.StatefulNextGenBuildCacheService;
+import org.gradle.internal.UncheckedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static org.gradle.cache.FileLockManager.LockMode.Exclusive;
+import static org.gradle.cache.internal.CacheInitializationAction.NO_INIT_REQUIRED;
+
+public class LockOnDemandCrossProcessBuildCacheService implements NextGenBuildCacheService {
+    private final static Logger LOG = LoggerFactory.getLogger(LockOnDemandCrossProcessBuildCacheService.class);
+
+    private final String cacheDisplayName;
+    private final StatefulNextGenBuildCacheService delegate;
+    private final AbstractCrossProcessCacheAccess crossProcessCacheAccess;
+    private final CacheAccessOperationsStack operations;
+
+    private final Lock stateLock = new ReentrantLock(); // protects the following state
+    private final Condition condition = stateLock.newCondition();
+
+    private Thread owner;
+    private FileLock fileLock;
+    private Runnable fileLockHeldByOwner;
+    private int cacheClosedCount;
+
+    public LockOnDemandCrossProcessBuildCacheService(
+        String cacheDisplayName,
+        File lockTarget,
+        FileLockManager lockManager,
+        StatefulNextGenBuildCacheService delegate
+    ) {
+        this.cacheDisplayName = cacheDisplayName;
+        this.delegate = delegate;
+        this.operations = new CacheAccessOperationsStack();
+
+        Action<FileLock> onFileLockAcquireAction = this::afterLockAcquire;
+        Action<FileLock> onFileLockReleaseAction = this::beforeLockRelease;
+
+        LockOptionsBuilder lockOptions = LockOptionsBuilder
+            .mode(Exclusive)
+            // TODO Is cross-version something we need?
+            .useCrossVersionImplementation();
+        this.crossProcessCacheAccess = new LockOnDemandCrossProcessCacheAccess(
+            cacheDisplayName,
+            lockTarget,
+            lockOptions,
+            lockManager,
+            stateLock,
+            NO_INIT_REQUIRED,
+            onFileLockAcquireAction,
+            onFileLockReleaseAction);
+
+        withOwnershipNow(() -> {
+            try {
+                crossProcessCacheAccess.open();
+            } catch (Throwable throwable) {
+                crossProcessCacheAccess.close();
+                throw UncheckedException.throwAsUncheckedException(throwable);
+            }
+        });
+    }
+
+    @Override
+    public boolean contains(BuildCacheKey key) {
+        return crossProcessCacheAccess.withFileLock(() -> delegate.contains(key));
+    }
+
+    @Override
+    public void store(BuildCacheKey key, BuildCacheEntryWriter legacyWriter) throws BuildCacheException {
+        crossProcessCacheAccess.withFileLock(() -> {
+            delegate.store(key, legacyWriter);
+            return null;
+        });
+    }
+
+    @Override
+    public void store(BuildCacheKey key, NextGenWriter writer) throws BuildCacheException {
+        crossProcessCacheAccess.withFileLock(() -> {
+            delegate.store(key, writer);
+            return null;
+        });
+    }
+
+    @Override
+    public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+        return crossProcessCacheAccess.withFileLock(() -> delegate.load(key, reader));
+    }
+
+    @Override
+    public synchronized void close() {
+        withOwnershipNow(() -> {
+            try {
+                if (fileLockHeldByOwner != null) {
+                    fileLockHeldByOwner.run();
+                }
+                crossProcessCacheAccess.close();
+
+                if (cacheClosedCount != 1) {
+                    LOG.debug("Cache {} was closed {} times.", cacheDisplayName, cacheClosedCount);
+                }
+            } finally {
+                owner = null;
+                fileLockHeldByOwner = null;
+            }
+        });
+    }
+
+    private void withOwnershipNow(Runnable action) {
+        stateLock.lock();
+        try {
+            // Take ownership
+            takeOwnershipNow();
+            try {
+                action.run();
+            } finally {
+                releaseOwnership();
+            }
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    /**
+     * Takes ownership of the cache, asserting that this can be done without waiting.
+     * Must be called while holding the lock.
+     */
+    private void takeOwnershipNow() {
+        if (owner != null && owner != Thread.currentThread()) {
+            throw new IllegalStateException(String.format("Cannot take ownership of %s as it is currently being used by another thread.", cacheDisplayName));
+        }
+        owner = Thread.currentThread();
+        operations.pushCacheAction();
+    }
+
+    /**
+     * Releases ownership of the cache.
+     * Must be called while holding the lock.
+     */
+    private void releaseOwnership() {
+        operations.popCacheAction();
+        if (!operations.isInCacheAction()) {
+            owner = null;
+            condition.signalAll();
+        }
+    }
+
+    /**
+     * Called just after the file lock has been acquired.
+     */
+    private void afterLockAcquire(FileLock fileLock) {
+        assert this.fileLock == null;
+        this.fileLock = fileLock;
+
+        withOwnershipNow(delegate::open);
+    }
+
+    /**
+     * Called just before the file lock is about to be released.
+     */
+    private void beforeLockRelease(FileLock fileLock) {
+        assert this.fileLock == fileLock;
+        try {
+            cacheClosedCount++;
+            withOwnershipNow(delegate::close);
+        } finally {
+            this.fileLock = null;
+        }
+    }
+}
diff --git a/subprojects/build-cache/src/main/resources/h2/schemas/org.gradle.caching.local.internal.H2BuildCacheService.sql b/subprojects/build-cache/src/main/resources/h2/schemas/org.gradle.caching.local.internal.H2BuildCacheService.sql
new file mode 100644
index 0000000..642d0a3
--- /dev/null
+++ b/subprojects/build-cache/src/main/resources/h2/schemas/org.gradle.caching.local.internal.H2BuildCacheService.sql
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.
+ */
+
+CREATE SCHEMA IF NOT EXISTS filestore;
+
+CREATE TABLE IF NOT EXISTS filestore.catalog
+(
+    entry_key     VARCHAR(32)         NOT NULL PRIMARY KEY,
+    entry_size    BIGINT              NOT NULL,
+    entry_content BINARY LARGE OBJECT NOT NULL
+);
diff --git a/subprojects/build-cache/src/test/groovy/org/gradle/caching/internal/controller/NextGenBuildCacheControllerTest.groovy b/subprojects/build-cache/src/test/groovy/org/gradle/caching/internal/controller/NextGenBuildCacheControllerTest.groovy
new file mode 100644
index 0000000..321ff79
--- /dev/null
+++ b/subprojects/build-cache/src/test/groovy/org/gradle/caching/internal/controller/NextGenBuildCacheControllerTest.groovy
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2023 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.caching.internal.controller
+
+import com.google.common.collect.ImmutableList
+import org.gradle.api.internal.cache.StringInterner
+import org.gradle.api.internal.file.TestFiles
+import org.gradle.internal.RelativePathSupplier
+import org.gradle.internal.file.FileType
+import org.gradle.internal.file.ThreadLocalBufferProvider
+import org.gradle.internal.file.TreeType
+import org.gradle.internal.hash.TestHashCodes
+import org.gradle.internal.snapshot.DirectorySnapshot
+import org.gradle.internal.snapshot.FileSystemLocationSnapshot
+import org.gradle.internal.snapshot.FileSystemSnapshotHierarchyVisitor
+import org.gradle.internal.snapshot.RelativePathTracker
+import org.gradle.internal.snapshot.RelativePathTrackingFileSystemSnapshotHierarchyVisitor
+import org.gradle.internal.snapshot.SnapshotUtil
+import org.gradle.internal.snapshot.SnapshotVisitResult
+import org.gradle.internal.vfs.FileSystemAccess
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class NextGenBuildCacheControllerTest extends Specification {
+
+    @Rule
+    final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass())
+
+    NextGenBuildCacheController controller
+    FileSystemAccess fileSystemAccess
+
+    def setup() {
+        fileSystemAccess = TestFiles.fileSystemAccess()
+        controller = new NextGenBuildCacheController(
+            "id",
+            TestFiles.deleter(),
+            fileSystemAccess,
+            new ThreadLocalBufferProvider(64 * 1024),
+            new StringInterner(),
+            Mock(NextGenBuildCacheAccess)
+        )
+    }
+
+    def "should use snapshots from cache data for #description output"() {
+        given:
+        def root = tmpDir.file("root")
+        createOuput(root)
+        def manifestEntriesBuilder = ImmutableList.<CacheManifest.ManifestEntry> builder()
+        def rootSnapshot = fileSystemAccess.read(root.absolutePath)
+        rootSnapshot.accept(new RelativePathTracker(), new RelativePathTrackingFileSystemSnapshotHierarchyVisitor() {
+            @Override
+            SnapshotVisitResult visitEntry(FileSystemLocationSnapshot snapshot, RelativePathSupplier relativePath) {
+                manifestEntriesBuilder.add(new CacheManifest.ManifestEntry(
+                    snapshot.getType(),
+                    relativePath.toRelativePath(),
+                    snapshot.getHash(),
+                    SnapshotUtil.getLength(snapshot)))
+                return SnapshotVisitResult.CONTINUE
+            }
+        })
+        def manifestEntries = manifestEntriesBuilder.build()
+
+        when:
+        def actualSnapshot = controller.createSnapshot(type, root, manifestEntries)
+            .get()
+
+        then:
+        actualSnapshot == rootSnapshot
+
+        where:
+        description | type               | createOuput
+        "file"      | TreeType.FILE      | { TestFile location -> createFileOutput(location) }
+        "directory" | TreeType.DIRECTORY | { TestFile location -> createDirectoryOutput(location) }
+    }
+
+    def "can handle missing file output"() {
+        given:
+        def root = tmpDir.file("root")
+        def manifestEntries = [new CacheManifest.ManifestEntry(FileType.Missing, "", TestHashCodes.hashCodeFrom(12345678L), 0)]
+
+        when:
+        def actualSnapshot = controller.createSnapshot(TreeType.FILE, root, manifestEntries)
+
+        then:
+        !actualSnapshot.present
+    }
+
+    def "can handle missing directory output"() {
+        given:
+        def root = tmpDir.file("root")
+        def manifestEntries = [new CacheManifest.ManifestEntry(FileType.Missing, "", TestHashCodes.hashCodeFrom(12345678L), 0)]
+
+        when:
+        def actualSnapshot = controller.createSnapshot(TreeType.DIRECTORY, root, manifestEntries)
+            .get()
+
+        then:
+        actualSnapshot instanceof DirectorySnapshot
+        actualSnapshot.absolutePath == root.absolutePath
+        actualSnapshot.accept(new FileSystemSnapshotHierarchyVisitor() {
+            @Override
+            SnapshotVisitResult visitEntry(FileSystemLocationSnapshot entry) {
+                // Make sure we don't have entries in the directory snapshot apart from itself
+                assert entry == actualSnapshot
+                return SnapshotVisitResult.CONTINUE
+            }
+        })
+    }
+
+    void createFileOutput(TestFile location) {
+        location.createFile() << "Hello world"
+    }
+
+    void createDirectoryOutput(TestFile location) {
+        location.createDir()
+        location.createFile("a.txt") << "Hello world: 'a'"
+        location.createDir("b")
+        location.createFile("b/b.txt") << "Hello world: 'b'"
+        location.createDir("c")
+        location.createFile("c/c.txt") << "Hello world: 'c'"
+        location.createDir("c/d")
+        location.createFile("c/d/d.txt") << "Hello world: 'd'"
+    }
+}
diff --git a/subprojects/build-cache/src/test/groovy/org/gradle/caching/local/internal/H2BuildCacheServiceTest.groovy b/subprojects/build-cache/src/test/groovy/org/gradle/caching/local/internal/H2BuildCacheServiceTest.groovy
new file mode 100644
index 0000000..ba6f38f
--- /dev/null
+++ b/subprojects/build-cache/src/test/groovy/org/gradle/caching/local/internal/H2BuildCacheServiceTest.groovy
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2023 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.caching.local.internal
+
+import org.gradle.caching.BuildCacheEntryReader
+import org.gradle.caching.BuildCacheKey
+import org.gradle.caching.internal.controller.service.StoreTarget
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class H2BuildCacheServiceTest extends Specification {
+
+    @Rule
+    TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider(getClass())
+
+    def dbDir = temporaryFolder.createDir("h2db")
+    H2BuildCacheService service
+
+    def setup() {
+        service = new H2BuildCacheService(dbDir.toPath(), 20)
+        service.open()
+    }
+
+    def cleanup() {
+        service.close()
+    }
+
+    BuildCacheKey key = Mock(BuildCacheKey) {
+        getHashCode() >> "1234abcd"
+    }
+
+    def "can write to h2"() {
+        given:
+        def tmpFile = temporaryFolder.createFile("test")
+        tmpFile << "Hello world"
+
+        expect:
+        service.store(key, new StoreTarget(tmpFile))
+    }
+
+    def "can write and read from h2"() {
+        given:
+        def tmpFile = temporaryFolder.createFile("test")
+        tmpFile << "Hello world"
+
+        service.store(key, new StoreTarget(tmpFile))
+
+        expect:
+        service.load(key, new BuildCacheEntryReader() {
+            @Override
+            void readFrom(InputStream input) throws IOException {
+                assert input.text == "Hello world"
+            }
+        })
+    }
+
+    def "can write to h2 and stop the service and read from h2 with a new service"() {
+        given:
+        def tmpFile = temporaryFolder.createFile("test")
+        tmpFile << "Hello world"
+
+        service.store(key, new StoreTarget(tmpFile))
+        service.close()
+
+        expect:
+        def newService = new H2BuildCacheService(dbDir.toPath(), 20)
+        newService.open()
+        newService.load(key, new BuildCacheEntryReader() {
+            @Override
+            void readFrom(InputStream input) throws IOException {
+                assert input.text == "Hello world"
+            }
+        })
+
+        cleanup:
+        newService.close()
+    }
+
+    def "doesn't write to h2 if the entry with the same key already exists"() {
+        given:
+        def firstFile = temporaryFolder.createFile("first")
+        firstFile << "Hello world"
+        def secondFile = temporaryFolder.createFile("second")
+        secondFile << "Hello Bob"
+
+        when:
+        service.store(key, new StoreTarget(firstFile))
+        service.store(key, new StoreTarget(secondFile))
+
+        then:
+        service.load(key, new BuildCacheEntryReader() {
+            @Override
+            void readFrom(InputStream input) throws IOException {
+                assert input.text == "Hello world"
+            }
+        })
+    }
+}
diff --git a/subprojects/build-events/src/main/java/org/gradle/internal/build/event/OperationResultPostProcessor.java b/subprojects/build-events/src/main/java/org/gradle/internal/build/event/OperationResultPostProcessor.java
index a9908c5..52b9d4f 100644
--- a/subprojects/build-events/src/main/java/org/gradle/internal/build/event/OperationResultPostProcessor.java
+++ b/subprojects/build-events/src/main/java/org/gradle/internal/build/event/OperationResultPostProcessor.java
@@ -16,10 +16,10 @@
 
 package org.gradle.internal.build.event;
 
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.internal.build.event.types.AbstractTaskResult;
 import org.gradle.internal.operations.BuildOperationDescriptor;
 import org.gradle.internal.operations.OperationFinishEvent;
-import org.gradle.internal.operations.OperationIdentifier;
 import org.gradle.internal.operations.OperationStartEvent;
 
 /**
@@ -33,5 +33,5 @@ public interface OperationResultPostProcessor {
 
     void finished(BuildOperationDescriptor buildOperation, OperationFinishEvent finishEvent);
 
-    AbstractTaskResult process(AbstractTaskResult taskResult, OperationIdentifier taskBuildOperationId);
+    AbstractTaskResult process(AbstractTaskResult taskResult, TaskInternal taskInternal);
 }
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/AbstractInitIntegrationSpec.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/AbstractInitIntegrationSpec.groovy
index 13e072b..3607336 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/AbstractInitIntegrationSpec.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/AbstractInitIntegrationSpec.groovy
@@ -83,6 +83,10 @@
         subprojectDir.file("src/test/resources").assertIsDir()
     }
 
+    protected void gradlePropertiesGenerated() {
+        targetDir.file("gradle.properties").assertIsFile()
+    }
+
     protected ScriptDslFixture dslFixtureFor(BuildInitDsl dsl) {
         ScriptDslFixture.of(dsl, targetDir, subprojectName())
     }
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BasicTypeInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BasicTypeInitIntegrationTest.groovy
index 8bcf6d7..99f0845 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BasicTypeInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BasicTypeInitIntegrationTest.groovy
@@ -18,7 +18,7 @@
 
 import org.gradle.buildinit.plugins.fixtures.ScriptDslFixture
 
-import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.GROOVY
+import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.KOTLIN
 import static org.gradle.util.internal.TextUtil.toPlatformLineSeparators
 
 
@@ -27,12 +27,12 @@
     @Override
     String subprojectName() { null }
 
-    def "defaults to groovy build scripts"() {
+    def "defaults to kotlin build scripts"() {
         when:
         run 'init'
 
         then:
-        dslFixtureFor(GROOVY).assertGradleFilesGenerated()
+        dslFixtureFor(KOTLIN).assertGradleFilesGenerated()
     }
 
     def "incubating does not break basic #scriptDsl build scripts"() {
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitInteractiveIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitInteractiveIntegrationTest.groovy
index def0d9b..06a1750 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitInteractiveIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitInteractiveIntegrationTest.groovy
@@ -55,10 +55,10 @@
         // Select 'kotlin'
         ConcurrentTestUtil.poll(60) {
             assert handle.standardOutput.contains(dslPrompt)
-            assert handle.standardOutput.contains("1: Groovy")
-            assert handle.standardOutput.contains("2: Kotlin")
+            assert handle.standardOutput.contains("1: Kotlin")
+            assert handle.standardOutput.contains("2: Groovy")
         }
-        handle.stdinPipe.write(("2" + TextUtil.platformLineSeparator).bytes)
+        handle.stdinPipe.write(("1" + TextUtil.platformLineSeparator).bytes)
 
         // Select default project name
         ConcurrentTestUtil.poll(60) {
@@ -74,7 +74,7 @@
 
         // after generating the project, we suggest the user reads some documentation
         ConcurrentTestUtil.poll(60) {
-            assert handle.standardOutput.contains("Get more help with your project")
+            assert handle.standardOutput.contains(documentationRegistry.getSampleForMessage())
         }
         handle.stdinPipe.close()
         handle.waitForFinish()
@@ -97,8 +97,9 @@
         handle.stdinPipe.write(TextUtil.platformLineSeparator.bytes)
 
         // after generating the project, we suggest the user reads some documentation
+        def msg = documentationRegistry.getSampleForMessage()
         ConcurrentTestUtil.poll(60) {
-            assert handle.standardOutput.contains("Get more help with your project")
+            assert handle.standardOutput.contains(msg)
         }
         handle.stdinPipe.close()
         handle.waitForFinish()
@@ -122,13 +123,9 @@
 
         // Select 'java'
         ConcurrentTestUtil.poll(60) {
-            assert handle.standardOutput.contains("Select implementation language:")
-            assert handle.standardOutput.contains("1: C++")
-            assert handle.standardOutput.contains("2: Groovy")
-            assert handle.standardOutput.contains("3: Java")
-            assert handle.standardOutput.contains("4: Kotlin")
-            assert handle.standardOutput.contains("5: Scala")
-            assert handle.standardOutput.contains("6: Swift")
+            ["Select implementation language:","1: C++","2: Groovy","3: Java","4: Kotlin","5: Scala","6: Swift"].each {
+                assert handle.standardOutput.contains(it)
+            }
         }
         handle.stdinPipe.write(("3" + TextUtil.platformLineSeparator).bytes)
 
@@ -142,7 +139,7 @@
         ConcurrentTestUtil.poll(60) {
             assert handle.standardOutput.contains(dslPrompt)
         }
-        handle.stdinPipe.write(("2" + TextUtil.platformLineSeparator).bytes)
+        handle.stdinPipe.write(("1" + TextUtil.platformLineSeparator).bytes)
 
         // Select 'junit'
         ConcurrentTestUtil.poll(60) {
@@ -177,7 +174,7 @@
 
         // after generating the project, we suggest the user reads some documentation
         ConcurrentTestUtil.poll(60) {
-            assert handle.standardOutput.contains("Get more help with your project")
+            assert handle.standardOutput.contains(documentationRegistry.getSampleForMessage())
         }
         handle.stdinPipe.close()
         handle.waitForFinish()
@@ -205,7 +202,7 @@
         ConcurrentTestUtil.poll(60) {
             assert handle.standardOutput.contains(dslPrompt)
         }
-        handle.stdinPipe.write(("1" + TextUtil.platformLineSeparator).bytes)
+        handle.stdinPipe.write(("2" + TextUtil.platformLineSeparator).bytes)
 
         // Select 'no' for incubating APIs
         ConcurrentTestUtil.poll(60) {
@@ -215,7 +212,7 @@
 
         // after generating the project, we suggest the user reads some documentation
         ConcurrentTestUtil.poll(60) {
-            assert handle.standardOutput.contains("Get more help with your project")
+            assert handle.standardOutput.contains(documentationRegistry.getDocumentationRecommendationFor("information", "migrating_from_maven"))
         }
         handle.stdinPipe.close()
         handle.waitForFinish()
@@ -255,7 +252,7 @@
         ConcurrentTestUtil.poll(60) {
             assert handle.standardOutput.contains(dslPrompt)
         }
-        handle.stdinPipe.write(("2" + TextUtil.platformLineSeparator).bytes)
+        handle.stdinPipe.write(("1" + TextUtil.platformLineSeparator).bytes)
 
         // Select default project name
         ConcurrentTestUtil.poll(60) {
@@ -271,7 +268,7 @@
 
         // after generating the project, we suggest the user reads some documentation
         ConcurrentTestUtil.poll(60) {
-            assert handle.standardOutput.contains("Get more help with your project")
+            assert handle.standardOutput.contains(documentationRegistry.getSampleForMessage())
         }
         handle.stdinPipe.close()
         handle.waitForFinish()
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitPluginIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitPluginIntegrationTest.groovy
index f7ea310..91f3cd0 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitPluginIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/BuildInitPluginIntegrationTest.groovy
@@ -21,7 +21,7 @@
 import org.gradle.integtests.fixtures.executer.ExecutionResult
 import org.hamcrest.Matcher
 
-import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.GROOVY
+import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.KOTLIN
 import static org.hamcrest.CoreMatchers.allOf
 import static org.hamcrest.CoreMatchers.containsString
 import static org.hamcrest.CoreMatchers.not
@@ -57,7 +57,9 @@
         dslFixture.buildFile.assertContents(
             allOf(
                 containsString("This is a general purpose Gradle build"),
-                containsString("Learn more about Gradle by exploring our samples at")))
+                containsString(documentationRegistry.getSampleForMessage())
+            )
+        )
 
         expect:
         succeeds 'properties'
@@ -81,7 +83,7 @@
         dslFixture.buildFile.assertContents(
             allOf(
                 containsString("This is a general purpose Gradle build"),
-                containsString("Learn more about Gradle by exploring our samples at"),
+                containsString(documentationRegistry.getSampleForMessage()),
                 containsString(BuildScriptBuilder.getIncubatingApisWarning())))
 
         expect:
@@ -174,7 +176,8 @@
 
         when:
         executer.usingSettingsFile(customSettings)
-        executer.expectDocumentedDeprecationWarning("Specifying custom settings file location has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_7.html#configuring_custom_build_layout")
+        executer.expectDocumentedDeprecationWarning("Specifying custom settings file location has been deprecated. This is scheduled to be removed in Gradle 9.0. " +
+            "Consult the upgrading guide for further information: ${documentationRegistry.getDocumentationFor("upgrading_version_7", "configuring_custom_build_layout")}")
         runInitWith targetScriptDsl as BuildInitDsl
 
         then:
@@ -190,7 +193,7 @@
         [existingScriptDsl, targetScriptDsl] << ScriptDslFixture.scriptDslCombinationsFor(2)
     }
 
-    def "pom conversion to groovy build scripts is triggered when pom and no gradle file found"() {
+    def "pom conversion to kotlin build scripts is triggered when pom and no gradle file found"() {
         given:
         pom()
 
@@ -198,7 +201,7 @@
         run('init')
 
         then:
-        pomValuesUsed(rootProjectDslFixtureFor(GROOVY))
+        pomValuesUsed(rootProjectDslFixtureFor(KOTLIN))
     }
 
     def "pom conversion to #scriptDsl build scripts not triggered when build type is specified"() {
@@ -244,8 +247,8 @@
 
         then:
         failure.assertHasCause("""The requested build script DSL 'some-unknown-dsl' is not supported. Supported DSLs:
-  - 'groovy'
-  - 'kotlin'""")
+  - 'kotlin'
+  - 'groovy'""")
     }
 
     def "gives decent error message when using unknown test framework"() {
@@ -303,6 +306,10 @@
                                   UPGRADE
                                   WARN
 
+     --no-incubating     Disables option --incubating
+
+     --no-split-project     Disables option --split-project
+
      --package     Set the package for source files.
 
      --project-name     Set the project name.
@@ -343,8 +350,8 @@
 
         then:
         succeeds "init"
-        targetDir.file("settings.gradle").assertIsFile()
-        targetDir.file("build.gradle").assertIsFile()
+        targetDir.file("settings.gradle.kts").assertIsFile()
+        targetDir.file("build.gradle.kts").assertIsFile()
     }
 
     def "fails when initializing in a project directory of another build that contains a build script"() {
@@ -405,8 +412,8 @@
         then:
         succeeds "init"
         targetDir.file("gradlew").assertIsFile()
-        targetDir.file("settings.gradle").assertIsFile()
-        targetDir.file("build.gradle").assertIsFile()
+        targetDir.file("settings.gradle.kts").assertIsFile()
+        targetDir.file("build.gradle.kts").assertIsFile()
         targetDir.file(".gradle/gradle.properties").assertHasNotChangedSince(snapshot)
     }
 
@@ -420,8 +427,8 @@
         then:
         succeeds "init"
         targetDir.file("gradlew").assertIsFile()
-        targetDir.file("settings.gradle").assertIsFile()
-        targetDir.file("build.gradle").assertIsFile()
+        targetDir.file("settings.gradle.kts").assertIsFile()
+        targetDir.file("build.gradle.kts").assertIsFile()
     }
 
     private ExecutionResult runInitWith(BuildInitDsl dsl, String... initOptions) {
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/CppLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/CppLibraryInitIntegrationTest.groovy
index 52da9cc..2efbb37 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/CppLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/CppLibraryInitIntegrationTest.groovy
@@ -20,10 +20,10 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.AvailableToolChains
 import org.gradle.nativeplatform.fixtures.SharedLibraryFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.NOT_MAC_OS_X)
+@Requires(UnitTestPreconditions.NotMacOs)
 class CppLibraryInitIntegrationTest extends AbstractInitIntegrationSpec {
 
     public static final String SAMPLE_LIB_CLASS = "hello.cpp"
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyApplicationInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyApplicationInitIntegrationTest.groovy
index 0c47ec0..48fd6b0 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyApplicationInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyApplicationInitIntegrationTest.groovy
@@ -143,6 +143,29 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run('init', '--type', 'groovy-application', '--package', 'my.app', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        run("build")
+
+        then:
+        assertTestPassed("my.app.AppTest", "application has a greeting")
+
+        when:
+        run("run")
+
+        then:
+        outputContains("Hello World!")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     def "source generation is skipped when groovy sources detected with #scriptDsl build scripts"() {
         setup:
         subprojectDir.file("src/main/groovy/org/acme/SampleMain.groovy") << """
@@ -155,6 +178,9 @@
                 package org.acme;
 
                 class SampleMainTest {
+
+                    @org.junit.jupiter.api.Test
+                    public void sampleTest() { }
                 }
         """
         when:
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyGradlePluginInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyGradlePluginInitIntegrationTest.groovy
index 5a43dbf..fa67a42 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyGradlePluginInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyGradlePluginInitIntegrationTest.groovy
@@ -93,6 +93,25 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    @IgnoreIf({ GradleContextualExecuter.embedded }) // This test runs a build that itself runs a build in a test worker with 'gradleApi()' dependency, which needs to pick up Gradle modules from a real distribution
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run('init', '--type', 'groovy-gradle-plugin', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        run("build")
+
+        then:
+        assertTestPassed("some.thing.SomeThingPluginTest", "plugin registers task")
+        assertFunctionalTestPassed("some.thing.SomeThingPluginFunctionalTest", "can run task")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     @Issue("https://github.com/gradle/gradle/issues/18206")
     @IgnoreIf({ GradleContextualExecuter.embedded }) // This test runs a build that itself runs builds in a test worker with 'gradleApi()' dependency, which needs to pick up Gradle modules from a real distribution
     def "re-running check succeeds"() {
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyLibraryInitIntegrationTest.groovy
index 0773943..e6daba3 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/GroovyLibraryInitIntegrationTest.groovy
@@ -110,6 +110,23 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run('init', '--type', 'groovy-library', '--package', 'my.lib', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        run("build")
+
+        then:
+        assertTestPassed("my.lib.LibraryTest", "someLibraryMethod returns true")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     def "source generation is skipped when groovy sources detected with #scriptDsl build scripts"() {
         setup:
         subprojectDir.file("src/main/groovy/org/acme/SampleMain.groovy") << """
@@ -122,6 +139,9 @@
                     package org.acme;
 
                     class SampleMainTest {
+
+                        @org.junit.jupiter.api.Test
+                        public void sampleTest() { }
                     }
             """
         when:
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaApplicationInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaApplicationInitIntegrationTest.groovy
index e056425..38cf56f 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaApplicationInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaApplicationInitIntegrationTest.groovy
@@ -22,7 +22,7 @@
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 
-import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.GROOVY
+import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.KOTLIN
 
 class JavaApplicationInitIntegrationTest extends AbstractJvmLibraryInitIntegrationSpec {
 
@@ -33,12 +33,12 @@
     @Override
     String subprojectName() { 'app' }
 
-    def "defaults to Groovy build scripts"() {
+    def "defaults to Kotlin build scripts"() {
         when:
         run ('init', '--type', 'java-application')
 
         then:
-        dslFixtureFor(GROOVY).assertGradleFilesGenerated()
+        dslFixtureFor(KOTLIN).assertGradleFilesGenerated()
     }
 
     def "creates sample source if no source present with #scriptDsl build scripts"() {
@@ -76,9 +76,11 @@
 
         when:
         run ('init', '--type', 'java-application', '--dsl', scriptDsl.id, '--incubating')
+
         then:
         subprojectDir.file("src/main/java").assertHasDescendants(SAMPLE_APP_CLASS)
         subprojectDir.file("src/test/java").assertHasDescendants(SAMPLE_APP_TEST_CLASS)
+
         and:
         commonJvmFilesGenerated(scriptDsl)
         dslFixture.assertHasTestSuite("test")
@@ -92,6 +94,23 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run ('init', '--type', 'java-application', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        succeeds('test')
+
+        then:
+        assertTestPassed("some.thing.AppTest", "appHasAGreeting")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     def "creates sample source using spock instead of junit with #scriptDsl build scripts"() {
         when:
         run('init', '--type', 'java-application', '--test-framework', 'spock', '--dsl', scriptDsl.id)
@@ -222,6 +241,9 @@
                 package org.acme;
 
                 public class SampleMainTest {
+
+                    @org.junit.jupiter.api.Test
+                    public void sampleTest() { }
                 }
         """
         when:
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaGradlePluginInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaGradlePluginInitIntegrationTest.groovy
index 051d9a9..1fc8bb3 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaGradlePluginInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaGradlePluginInitIntegrationTest.groovy
@@ -21,7 +21,7 @@
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 
-import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.GROOVY
+import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.KOTLIN
 
 
 class JavaGradlePluginInitIntegrationTest extends AbstractInitIntegrationSpec {
@@ -29,12 +29,12 @@
     @Override
     String subprojectName() { 'plugin' }
 
-    def "defaults to Groovy build scripts"() {
+    def "defaults to Kotlin build scripts"() {
         when:
         run ('init', '--type', 'java-gradle-plugin')
 
         then:
-        dslFixtureFor(GROOVY).assertGradleFilesGenerated()
+        dslFixtureFor(KOTLIN).assertGradleFilesGenerated()
     }
 
     @IgnoreIf({ GradleContextualExecuter.embedded }) // This test runs a build that itself runs a build in a test worker with 'gradleApi()' dependency, which needs to pick up Gradle modules from a real distribution
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaLibraryInitIntegrationTest.groovy
index ad8279d..93bf228 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/JavaLibraryInitIntegrationTest.groovy
@@ -19,10 +19,10 @@
 import org.gradle.buildinit.plugins.fixtures.ScriptDslFixture
 import org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl
 import org.gradle.buildinit.plugins.internal.modifiers.BuildInitTestFramework
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.GROOVY
+import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.KOTLIN
 import static org.hamcrest.CoreMatchers.allOf
 
 class JavaLibraryInitIntegrationTest extends AbstractJvmLibraryInitIntegrationSpec {
@@ -31,12 +31,12 @@
     public static final String SAMPLE_LIBRARY_TEST_CLASS = "some/thing/LibraryTest.java"
     public static final String SAMPLE_SPOCK_LIBRARY_TEST_CLASS = "some/thing/LibraryTest.groovy"
 
-    def "defaults to Groovy build scripts"() {
+    def "defaults to Kotlin build scripts"() {
         when:
         run ('init', '--type', 'java-library' )
 
         then:
-        dslFixtureFor(GROOVY).assertGradleFilesGenerated()
+        dslFixtureFor(KOTLIN).assertGradleFilesGenerated()
     }
 
     def "creates sample source if no source present with #scriptDsl build scripts"() {
@@ -68,9 +68,11 @@
 
         when:
         run ('init', '--type', 'java-library', '--incubating', '--dsl', scriptDsl.id)
+
         then:
         subprojectDir.file("src/main/java").assertHasDescendants(SAMPLE_LIBRARY_CLASS)
         subprojectDir.file("src/test/java").assertHasDescendants(SAMPLE_LIBRARY_TEST_CLASS)
+
         and:
         commonJvmFilesGenerated(scriptDsl)
         dslFixture.assertHasTestSuite('test')
@@ -84,6 +86,23 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run ('init', '--type', 'java-library', '--incubating', '--dsl', scriptDsl.id)
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        succeeds('test')
+
+        then:
+        assertTestPassed("some.thing.LibraryTest", "someLibraryMethodReturnsTrue")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     def "creates sample source using spock instead of junit with #scriptDsl build scripts"() {
         when:
         run('init', '--type', 'java-library', '--test-framework', 'spock', '--dsl', scriptDsl.id)
@@ -206,7 +225,7 @@
     }
 
     // Spock pulls an old version of Groovy that doesn't work with Java 18
-    @Requires(TestPrecondition.JDK17_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk17OrEarlier)
     def "creates sample source with package and spock and #scriptDsl build scripts"() {
         when:
         run('init', '--type', 'java-library', '--test-framework', 'spock', '--package', 'my.lib', '--dsl', scriptDsl.id)
@@ -229,7 +248,7 @@
     }
 
     // Spock pulls an old version of Groovy that doesn't work with Java 18
-    @Requires(TestPrecondition.JDK17_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk17OrEarlier)
     def "creates sample source with package and spock and #scriptDsl build scripts with --incubating"() {
         def dslFixture = dslFixtureFor(scriptDsl)
 
@@ -266,6 +285,9 @@
                 package org.acme;
 
                 public class SampleMainTest {
+
+                    @org.junit.jupiter.api.Test
+                    public void sampleTest() { }
                 }
         """
         when:
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinApplicationInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinApplicationInitIntegrationTest.groovy
index 03feb61..dafd3b7 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinApplicationInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinApplicationInitIntegrationTest.groovy
@@ -95,6 +95,29 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run('init', '--type', 'kotlin-application', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        run("build")
+
+        then:
+        assertTestPassed("some.thing.AppTest", "appHasAGreeting")
+
+        when:
+        run("run")
+
+        then:
+        outputContains("Hello World!")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     def "creates sample source with package and #scriptDsl build scripts"() {
         when:
         run('init', '--type', 'kotlin-application', '--package', 'my.app', '--dsl', scriptDsl.id)
@@ -134,6 +157,9 @@
                 package org.acme
 
                 class SampleMainTest {
+
+                    @org.junit.jupiter.api.Test
+                    fun sampleTest() { }
                 }
         """
         when:
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinGradlePluginInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinGradlePluginInitIntegrationTest.groovy
index 9980977..8c192eb 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinGradlePluginInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinGradlePluginInitIntegrationTest.groovy
@@ -93,6 +93,25 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    @IgnoreIf({ GradleContextualExecuter.embedded }) // This test runs a build that itself runs a build in a test worker with 'gradleApi()' dependency, which needs to pick up Gradle modules from a real distribution
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run('init', '--type', 'kotlin-gradle-plugin', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        run("build")
+
+        then:
+        assertTestPassed("some.thing.SomeThingPluginTest", "plugin registers task")
+        assertFunctionalTestPassed("some.thing.SomeThingPluginFunctionalTest", "can run task")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     @Issue("https://github.com/gradle/gradle/issues/18206")
     @IgnoreIf({ GradleContextualExecuter.embedded }) // This test runs a build that itself runs builds in a test worker with 'gradleApi()' dependency, which needs to pick up Gradle modules from a real distribution
     def "re-running check succeeds with #scriptDsl"() {
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinLibraryInitIntegrationTest.groovy
index cb30633..7a2914a 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/KotlinLibraryInitIntegrationTest.groovy
@@ -64,6 +64,7 @@
 
         when:
         run ('init', '--type', 'kotlin-library', '--dsl', scriptDsl.id, '--incubating')
+
         then:
         subprojectDir.file("src/main/kotlin").assertHasDescendants(SAMPLE_LIBRARY_CLASS)
         subprojectDir.file("src/test/kotlin").assertHasDescendants(SAMPLE_LIBRARY_TEST_CLASS)
@@ -81,6 +82,23 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run ('init', '--type', 'kotlin-library', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        succeeds('test')
+
+        then:
+        assertTestPassed("some.thing.LibraryTest", "someLibraryMethodReturnsTrue")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     def "creates sample source with package and #scriptDsl build scripts"() {
         when:
         run('init', '--type', 'kotlin-library', '--package', 'my.lib', '--dsl', scriptDsl.id)
@@ -114,6 +132,9 @@
                     package org.acme
 
                     class SampleMainTest {
+
+                        @org.junit.jupiter.api.Test
+                        fun sampleTest() { }
                     }
             """
         when:
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MavenConversionIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MavenConversionIntegrationTest.groovy
index 37d972f..9d3f625 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MavenConversionIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MavenConversionIntegrationTest.groovy
@@ -17,7 +17,7 @@
 
 package org.gradle.buildinit.plugins
 
-
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.buildinit.InsecureProtocolOption
 import org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
@@ -109,6 +109,61 @@
 """
     }
 
+    /**
+     * This test demonstrates that back-references from the child projects to the parent project are not necessary to convert
+     * a multi-module Maven build to Gradle.
+     */
+    def "multiModuleNoBackReferences"() {
+        def dsl = dslFixtureFor(scriptDsl)
+        def warSubprojectBuildFile = targetDir.file("webinar-war/" + dsl.buildFileName)
+        def implSubprojectBuildFile = targetDir.file("webinar-impl/" + dsl.buildFileName)
+        def conventionPluginScript = targetDir.file("buildSrc/src/main/${scriptDsl.name().toLowerCase()}/${scriptDsl.fileNameFor("com.example.webinar.java-conventions")}")
+
+        when:
+        run 'init', '--dsl', scriptDsl.id as String
+
+        then:
+        targetDir.file(dsl.settingsFileName).exists()
+        !targetDir.file(dsl.buildFileName).exists() // no root build file
+        warSubprojectBuildFile.exists()
+
+        warSubprojectBuildFile.text.contains("id 'com.example.webinar.java-conventions'") || warSubprojectBuildFile.text.contains('id("com.example.webinar.java-conventions")')
+        !warSubprojectBuildFile.text.contains("options.encoding")
+
+        assertContainsPublishingConfig(conventionPluginScript, scriptDsl)
+        assertContainsEncodingConfig(conventionPluginScript, scriptDsl, 'UTF-8')
+        conventionPluginScript.text.contains(TextUtil.toPlatformLineSeparators('''
+java {
+    withSourcesJar()
+}'''))
+
+        implSubprojectBuildFile.text.contains("publishing.publications.maven.artifact(testsJar)") || implSubprojectBuildFile.text.contains('(publishing.publications["maven"] as MavenPublication).artifact(testsJar)')
+        implSubprojectBuildFile.text.contains(TextUtil.toPlatformLineSeparators('''
+java {
+    withJavadocJar()
+}'''))
+        when:
+        run 'clean', 'build'
+
+        then: //smoke test the build artifacts
+        targetDir.file("webinar-api/build/libs/webinar-api-1.0-SNAPSHOT.jar").exists()
+        targetDir.file("webinar-impl/build/libs/webinar-impl-1.0-SNAPSHOT.jar").exists()
+        targetDir.file("webinar-war/build/libs/webinar-war-1.0-SNAPSHOT.war").exists()
+
+        new DefaultTestExecutionResult(targetDir.file("webinar-impl")).assertTestClassesExecuted('webinar.WebinarTest')
+
+        when:
+        run 'projects'
+
+        then:
+        output.contains """
+Root project 'webinar-parent'
++--- Project ':webinar-api' - Webinar APIs
++--- Project ':webinar-impl' - Webinar implementation
+\\--- Project ':webinar-war' - Webinar web application
+"""
+    }
+
     def "multiModuleWithNestedParent"() {
         def dsl = dslFixtureFor(scriptDsl)
 
@@ -570,7 +625,8 @@
     def "insecureProtocolFail"() {
         expect:
         fails 'init', '--dsl', scriptDsl.id as String, '--insecure-protocol', InsecureProtocolOption.FAIL as String
-        result.assertHasErrorOutput("Gradle found an insecure protocol in a repository definition. The current strategy for handling insecure URLs is to fail. For more options, see")
+        result.assertHasErrorOutput("Gradle found an insecure protocol in a repository definition. The current strategy for handling insecure URLs is to fail. " +
+            insecureProtocolsLinks("options"))
     }
 
     @Issue("https://github.com/gradle/gradle/issues/17328")
@@ -582,7 +638,12 @@
 
         then:
         dsl.assertGradleFilesGenerated()
-        outputContains("Gradle found an insecure protocol in a repository definition. You will have to opt into allowing insecure protocols in the generated build file. See ")
+        outputContains("Gradle found an insecure protocol in a repository definition. You will have to opt into allowing insecure protocols in the generated build file. " +
+            insecureProtocolsLinks())
+    }
+
+    private insecureProtocolsLinks(String topic = "information on how to do this") {
+        new DocumentationRegistry().getDocumentationRecommendationFor(topic, "build_init_plugin", "sec:allow_insecure")
     }
 
     @Issue("https://github.com/gradle/gradle/issues/17328")
@@ -594,7 +655,8 @@
 
         then:
         dsl.assertGradleFilesGenerated()
-        outputContains("Gradle found an insecure protocol in a repository definition. You will have to opt into allowing insecure protocols in the generated build file. See ")
+        outputContains("Gradle found an insecure protocol in a repository definition. You will have to opt into allowing insecure protocols in the generated build file. " +
+            insecureProtocolsLinks())
     }
 
     @Issue("https://github.com/gradle/gradle/issues/17328")
@@ -694,6 +756,34 @@
         dsl.getBuildFile().text.readLines().contains(descriptionPropertyAssignment)
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/23963")
+    def "emptySource"() {
+        def dsl = dslFixtureFor(scriptDsl)
+
+        when:
+        run 'init', '--dsl', scriptDsl.id as String
+
+        then:
+        dsl.assertGradleFilesGenerated()
+        dsl.getSettingsFile().text.contains("rootProject.name = 'util'") || dsl.getSettingsFile().text.contains('rootProject.name = "util"')
+        assertContainsPublishingConfig(dsl.getBuildFile(), scriptDsl)
+        succeeds 'clean', 'build'
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/23963")
+    def "emptyTarget"() {
+        def dsl = dslFixtureFor(scriptDsl)
+
+        when:
+        run 'init', '--dsl', scriptDsl.id as String
+
+        then:
+        dsl.assertGradleFilesGenerated()
+        dsl.getSettingsFile().text.contains("rootProject.name = 'util'") || dsl.getSettingsFile().text.contains('rootProject.name = "util"')
+        assertContainsPublishingConfig(dsl.getBuildFile(), scriptDsl)
+        succeeds 'clean', 'build'
+    }
+
     static libRequest(MavenHttpRepository repo, String group, String name, Object version) {
         MavenHttpModule module = repo.module(group, name, version as String)
         module.allowAll()
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MultiProjectApplicationInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MultiProjectApplicationInitIntegrationTest.groovy
index 43a8632..acd3cce 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MultiProjectApplicationInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/MultiProjectApplicationInitIntegrationTest.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.buildinit.plugins
 
-import org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl
+import org.gradle.buildinit.plugins.fixtures.ScriptDslFixture
 
 class MultiProjectApplicationInitIntegrationTest extends AbstractInitIntegrationSpec {
     @Override
@@ -30,6 +30,6 @@
         succeeds('init', '--type', "java-application", '--dsl', dsl.id)
 
         where:
-        dsl << [BuildInitDsl.KOTLIN, BuildInitDsl.GROOVY]
+        dsl << ScriptDslFixture.SCRIPT_DSLS
     }
 }
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaApplicationInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaApplicationInitIntegrationTest.groovy
index 57d8e19..347d49f 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaApplicationInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaApplicationInitIntegrationTest.groovy
@@ -81,6 +81,29 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run('init', '--type', 'scala-application', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        run("build")
+
+        then:
+        assertTestPassed("some.thing.AppSuite", "App has a greeting")
+
+        when:
+        run("run")
+
+        then:
+        outputContains("Hello, world!")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     def "specifying JUnit4 is not supported with #scriptDsl build scripts"() {
         when:
         fails('init', '--type', 'scala-application', '--test-framework', 'junit-4', '--dsl', scriptDsl.id)
@@ -132,6 +155,9 @@
                 package org.acme;
 
                 class SampleMainSuite {
+
+                    @org.junit.Test
+                    def sampleTest : Unit = { }
                 }
         """
         when:
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaLibraryInitIntegrationTest.groovy
index ef1d92b..10b8b39 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/ScalaLibraryInitIntegrationTest.groovy
@@ -87,6 +87,23 @@
         scriptDsl << ScriptDslFixture.SCRIPT_DSLS
     }
 
+    def "creates with gradle.properties when using #scriptDsl build scripts with --incubating"() {
+        when:
+        run('init', '--type', 'scala-library', '--package', 'my.lib', '--dsl', scriptDsl.id, '--incubating')
+
+        then:
+        gradlePropertiesGenerated()
+
+        when:
+        run("build")
+
+        then:
+        assertTestPassed("my.lib.LibrarySuite", "someLibraryMethod is always true")
+
+        where:
+        scriptDsl << ScriptDslFixture.SCRIPT_DSLS
+    }
+
     def "source generation is skipped when scala sources detected with #scriptDsl build scripts"() {
         setup:
         subprojectDir.file("src/main/scala/org/acme/SampleMain.scala") << """
@@ -99,6 +116,9 @@
                     package org.acme;
 
                     class SampleMainTest{
+
+                        @org.junit.Test
+                        def sampleTest : Unit = { }
                     }
             """
 
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/SwiftApplicationInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/SwiftApplicationInitIntegrationTest.groovy
index d3a4c1a..fdf97a3 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/SwiftApplicationInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/SwiftApplicationInitIntegrationTest.groovy
@@ -22,11 +22,11 @@
 import org.gradle.nativeplatform.fixtures.ExecutableFixture
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 @RequiresInstalledToolChain(ToolChainRequirement.SWIFTC)
-@Requires(TestPrecondition.NOT_MAC_OS_X_M1) // M1 Macs need modern Xcode to compile aarch64 binaries
+@Requires(UnitTestPreconditions.NotMacOsM1) // M1 Macs need modern Xcode to compile aarch64 binaries
 class SwiftApplicationInitIntegrationTest extends AbstractInitIntegrationSpec {
 
     public static final String SAMPLE_APPLICATION_CLASS = "main.swift"
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/SwiftLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/SwiftLibraryInitIntegrationTest.groovy
index acac678..64fc51b 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/SwiftLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/SwiftLibraryInitIntegrationTest.groovy
@@ -23,11 +23,11 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.SharedLibraryFixture
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 @RequiresInstalledToolChain(ToolChainRequirement.SWIFTC)
-@Requires(TestPrecondition.NOT_MAC_OS_X_M1) // M1 Macs need modern Xcode to compile aarch64 binaries
+@Requires(UnitTestPreconditions.NotMacOsM1) // M1 Macs need modern Xcode to compile aarch64 binaries
 class SwiftLibraryInitIntegrationTest extends AbstractInitIntegrationSpec {
 
     public static final String SAMPLE_LIBRARY_CLASS = "Hello.swift"
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WindowsSwiftApplicationInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WindowsSwiftApplicationInitIntegrationTest.groovy
index 9f83e4d..892a5a0 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WindowsSwiftApplicationInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WindowsSwiftApplicationInitIntegrationTest.groovy
@@ -18,14 +18,14 @@
 
 
 import org.gradle.buildinit.plugins.fixtures.ScriptDslFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.buildinit.plugins.SwiftApplicationInitIntegrationTest.LINUX_MAIN_DOT_SWIFT
 import static org.gradle.buildinit.plugins.SwiftApplicationInitIntegrationTest.SAMPLE_APPLICATION_CLASS
 import static org.gradle.buildinit.plugins.SwiftApplicationInitIntegrationTest.SAMPLE_APPLICATION_TEST_CLASS
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class WindowsSwiftApplicationInitIntegrationTest extends AbstractInitIntegrationSpec {
 
     @Override
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WindowsSwiftLibraryInitIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WindowsSwiftLibraryInitIntegrationTest.groovy
index 9da5c6f..3aa1e2a 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WindowsSwiftLibraryInitIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WindowsSwiftLibraryInitIntegrationTest.groovy
@@ -18,14 +18,14 @@
 
 
 import org.gradle.buildinit.plugins.fixtures.ScriptDslFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.buildinit.plugins.SwiftApplicationInitIntegrationTest.LINUX_MAIN_DOT_SWIFT
 import static org.gradle.buildinit.plugins.SwiftLibraryInitIntegrationTest.SAMPLE_LIBRARY_CLASS
 import static org.gradle.buildinit.plugins.SwiftLibraryInitIntegrationTest.SAMPLE_LIBRARY_TEST_CLASS
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class WindowsSwiftLibraryInitIntegrationTest extends AbstractInitIntegrationSpec {
 
     @Override
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WrapperPluginIntegrationTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WrapperPluginIntegrationTest.groovy
index 8033856..18d1ed0 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WrapperPluginIntegrationTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/WrapperPluginIntegrationTest.groovy
@@ -32,4 +32,4 @@
         then:
         new WrapperTestFixture(testDirectory).generated()
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/internal/WrapperPluginAutoApplyActionIntegTest.groovy b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/internal/WrapperPluginAutoApplyActionIntegTest.groovy
index 27216f6..7c71d3e 100644
--- a/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/internal/WrapperPluginAutoApplyActionIntegTest.groovy
+++ b/subprojects/build-init/src/integTest/groovy/org/gradle/buildinit/plugins/internal/WrapperPluginAutoApplyActionIntegTest.groovy
@@ -68,7 +68,7 @@
                 gradleVersion = '12.34'
             }
     """
-        run 'wrapper'
+        run 'wrapper', '--no-validate-url'
         then:
         wrapper.generated("12.34")
     }
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/emptySource/some-thing/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/emptySource/some-thing/pom.xml
new file mode 100644
index 0000000..725dea4
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/emptySource/some-thing/pom.xml
@@ -0,0 +1,20 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>util</groupId>
+    <artifactId>util</artifactId>
+    <version>2.5</version>
+    <packaging>jar</packaging>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source/>
+                    <target>1.7</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/emptyTarget/some-thing/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/emptyTarget/some-thing/pom.xml
new file mode 100644
index 0000000..165f95b
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/emptyTarget/some-thing/pom.xml
@@ -0,0 +1,20 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>util</groupId>
+    <artifactId>util</artifactId>
+    <version>2.5</version>
+    <packaging>jar</packaging>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.7</source>
+                    <target/>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/expandProperties/some-thing/src/main/java/Foo.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/expandProperties/some-thing/src/main/java/Foo.java
index c964cbb..832f055 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/expandProperties/some-thing/src/main/java/Foo.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/expandProperties/some-thing/src/main/java/Foo.java
@@ -7,4 +7,4 @@ public class Foo {
   public String toString() {
     return "foo";
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/expandProperties/some-thing/src/test/java/FooTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/expandProperties/some-thing/src/test/java/FooTest.java
index 03a25b0..830937f 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/expandProperties/some-thing/src/test/java/FooTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/expandProperties/some-thing/src/test/java/FooTest.java
@@ -7,4 +7,4 @@ public class FooTest {
   public void test() {
     assertFalse(false);
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-api/src/main/java/webinar/Demoable.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-api/src/main/java/webinar/Demoable.java
index a7fe90f..8e522df 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-api/src/main/java/webinar/Demoable.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-api/src/main/java/webinar/Demoable.java
@@ -2,4 +2,4 @@
 
 public interface Demoable {
   String getDescription();
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-impl/src/main/java/webinar/Webinar.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
index 7678df1..2825bb7 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
@@ -17,4 +17,4 @@ public Webinar(String description) {
   public String getDescription() {
     return StringUtils.normalizeSpace(description);
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
index 0cbc918..8892578 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/flatmultimodule/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
@@ -12,4 +12,4 @@ public class WebinarTest {
     //then
     Assert.assertEquals("nice day", demoable.getDescription());
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/pom.xml
index 63b226d..c92c7bb 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/pom.xml
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/pom.xml
@@ -30,9 +30,9 @@
             <version>2.6</version>
         </dependency>
         <dependency>
-            <groupId>org.testng</groupId>
-            <artifactId>testng</artifactId>
-            <version>6.7</version>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.1</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/src/main/java/Foo.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/src/main/java/Foo.java
index 926c3dc..85c660d 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/src/main/java/Foo.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/src/main/java/Foo.java
@@ -4,4 +4,4 @@ public class Foo {
   public String toString() {
     return StringUtils.normalizeSpace("hi  there!");
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/src/test/java/FooTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/src/test/java/FooTest.java
index dd0a335..bdeb6ee 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/src/test/java/FooTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/javadocJar/some-thing/src/test/java/FooTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import org.testng.annotations.Test;
+import org.junit.Test;
 
 public class FooTest {
   @Test public void test() { }
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-api/src/main/java/webinar/Demoable.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-api/src/main/java/webinar/Demoable.java
index a7fe90f..8e522df 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-api/src/main/java/webinar/Demoable.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-api/src/main/java/webinar/Demoable.java
@@ -2,4 +2,4 @@
 
 public interface Demoable {
   String getDescription();
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-impl/src/main/java/webinar/Webinar.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
index 7678df1..2825bb7 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
@@ -17,4 +17,4 @@ public Webinar(String description) {
   public String getDescription() {
     return StringUtils.normalizeSpace(description);
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
index 0cbc918..8892578 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModule/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
@@ -12,4 +12,4 @@ public class WebinarTest {
     //then
     Assert.assertEquals("nice day", demoable.getDescription());
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/pom.xml
new file mode 100644
index 0000000..e7daab1
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/pom.xml
@@ -0,0 +1,57 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>com.example.webinar</groupId>
+  <artifactId>webinar-parent</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>junit</groupId>
+				<artifactId>junit</artifactId>
+				<version>4.13.1</version>
+				<scope>test</scope>
+			</dependency>
+		</dependencies>
+  </dependencyManagement>
+
+  <modules>
+    <module>webinar-api</module>
+    <module>webinar-impl</module>
+    <module>webinar-war</module>
+  </modules>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>3.0.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar</goal>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-api/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-api/pom.xml
new file mode 100644
index 0000000..4f72c7d
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-api/pom.xml
@@ -0,0 +1,15 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>webinar-api</artifactId>
+  <packaging>jar</packaging>
+  <name>Webinar APIs</name>
+  <groupId>com.example.webinar</groupId>
+  <version>1.0-SNAPSHOT</version>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+</project>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-api/src/main/java/webinar/Demoable.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-api/src/main/java/webinar/Demoable.java
new file mode 100644
index 0000000..8e522df
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-api/src/main/java/webinar/Demoable.java
@@ -0,0 +1,5 @@
+package webinar;
+
+public interface Demoable {
+  String getDescription();
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/pom.xml
new file mode 100644
index 0000000..a0df1c1
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/pom.xml
@@ -0,0 +1,66 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>webinar-impl</artifactId>
+  <packaging>jar</packaging>
+  <name>Webinar implementation</name>
+  <groupId>com.example.webinar</groupId>
+  <version>1.0-SNAPSHOT</version>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>2.6</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.example.webinar</groupId>
+      <artifactId>webinar-api</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.13.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>3.0.1</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>3.0.1</version>
+          <executions>
+            <execution>
+              <id>attach-sources</id>
+              <goals>
+                <goal>jar</goal>
+              </goals>
+            </execution>
+          </executions>
+        </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/src/main/java/webinar/Webinar.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
new file mode 100644
index 0000000..2825bb7
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
@@ -0,0 +1,20 @@
+package webinar;
+
+import org.apache.commons.lang.StringUtils;
+
+public class Webinar implements Demoable {
+  
+  private final String description;
+  
+  public Webinar() {
+    this("I'm happy today!");
+  }
+  
+  public Webinar(String description) {
+    this.description = description;
+  }
+
+  public String getDescription() {
+    return StringUtils.normalizeSpace(description);
+  }
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
new file mode 100644
index 0000000..8892578
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
@@ -0,0 +1,15 @@
+package webinar;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class WebinarTest {
+  
+  @Test public void normalizesDescription() {
+    //when
+    Demoable demoable = new Webinar("nice   day");
+    
+    //then
+    Assert.assertEquals("nice day", demoable.getDescription());
+  }
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/pom.xml
new file mode 100644
index 0000000..4ef3507
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/pom.xml
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>webinar-war</artifactId>
+  <packaging>war</packaging>
+  <name>Webinar web application</name>
+  <groupId>com.example.webinar</groupId>
+  <version>1.0-SNAPSHOT</version>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.13.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.example.webinar</groupId>
+      <artifactId>webinar-impl</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+	    <plugin>
+    	    <groupId>org.mortbay.jetty</groupId>
+        	<artifactId>maven-jetty-plugin</artifactId>
+        	<version>6.1.26</version>
+        </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/src/main/webapp/WEB-INF/web.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..c9ba3b2
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,7 @@
+<!DOCTYPE web-app PUBLIC
+ "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+ "http://java.sun.com/dtd/web-app_2_3.dtd" >
+
+<web-app>
+  <display-name>Webinar war</display-name>
+</web-app>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/src/main/webapp/index.jsp b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/src/main/webapp/index.jsp
new file mode 100644
index 0000000..3a5211f
--- /dev/null
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleNoBackReferences/some-thing/webinar-war/src/main/webapp/index.jsp
@@ -0,0 +1,6 @@
+<%@ page import="webinar.*" %>
+<html>
+<body>
+<h2>The webinar says: <%=new Webinar().getDescription()%></h2>
+</body>
+</html>
\ No newline at end of file
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-api/src/main/java/webinar/Demoable.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-api/src/main/java/webinar/Demoable.java
index a7fe90f..8e522df 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-api/src/main/java/webinar/Demoable.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-api/src/main/java/webinar/Demoable.java
@@ -2,4 +2,4 @@
 
 public interface Demoable {
   String getDescription();
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-impl/src/main/java/webinar/Webinar.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
index 7678df1..2825bb7 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
@@ -17,4 +17,4 @@ public Webinar(String description) {
   public String getDescription() {
     return StringUtils.normalizeSpace(description);
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
index 0cbc918..8892578 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithNestedParent/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
@@ -12,4 +12,4 @@ public class WebinarTest {
     //then
     Assert.assertEquals("nice day", demoable.getDescription());
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-api/src/main/java/webinar/Demoable.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-api/src/main/java/webinar/Demoable.java
index a7fe90f..8e522df 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-api/src/main/java/webinar/Demoable.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-api/src/main/java/webinar/Demoable.java
@@ -2,4 +2,4 @@
 
 public interface Demoable {
   String getDescription();
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-impl/src/main/java/webinar/Webinar.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
index 7678df1..2825bb7 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-impl/src/main/java/webinar/Webinar.java
@@ -17,4 +17,4 @@ public Webinar(String description) {
   public String getDescription() {
     return StringUtils.normalizeSpace(description);
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
index 0cbc918..8892578 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/multiModuleWithRemoteParent/some-thing/webinar-impl/src/test/java/webinar/WebinarTest.java
@@ -12,4 +12,4 @@ public class WebinarTest {
     //then
     Assert.assertEquals("nice day", demoable.getDescription());
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/pom.xml
index a436695..069d16d 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/pom.xml
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/pom.xml
@@ -8,18 +8,18 @@
   <packaging>jar</packaging>
 
 		<dependencies>
-			<dependency>
+            <dependency>
                 <groupId>commons-lang</groupId>
                 <artifactId>commons-lang</artifactId>
                 <version>2.6</version>
-			</dependency>
-			<dependency>
-				<groupId>org.testng</groupId>
-				<artifactId>testng</artifactId>
-				<version>6.7</version>
-				<scope>test</scope>
-			</dependency>
-		</dependencies>
+            </dependency>
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>4.13.1</version>
+                <scope>test</scope>
+            </dependency>
+        </dependencies>
     <build>
         <plugins>
             <plugin>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/src/main/java/Foo.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/src/main/java/Foo.java
index 926c3dc..85c660d 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/src/main/java/Foo.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/src/main/java/Foo.java
@@ -4,4 +4,4 @@ public class Foo {
   public String toString() {
     return StringUtils.normalizeSpace("hi  there!");
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/src/test/java/FooTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/src/test/java/FooTest.java
index 673ba0b..322cc69 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/src/test/java/FooTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/singleModule/some-thing/src/test/java/FooTest.java
@@ -1,7 +1,7 @@
-import org.testng.annotations.Test;
+import org.junit.Test;
 
 public class FooTest {
   @Test public void test() {
     assert false: "test failure";
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/pom.xml b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/pom.xml
index 2278826..2d41993 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/pom.xml
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/pom.xml
@@ -30,9 +30,9 @@
             <version>2.6</version>
         </dependency>
         <dependency>
-            <groupId>org.testng</groupId>
-            <artifactId>testng</artifactId>
-            <version>6.7</version>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.1</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/src/main/java/Foo.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/src/main/java/Foo.java
index 926c3dc..85c660d 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/src/main/java/Foo.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/src/main/java/Foo.java
@@ -4,4 +4,4 @@ public class Foo {
   public String toString() {
     return StringUtils.normalizeSpace("hi  there!");
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/src/test/java/FooTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/src/test/java/FooTest.java
index 869ba65..b204f90 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/src/test/java/FooTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/sourcesJar/some-thing/src/test/java/FooTest.java
@@ -1,4 +1,4 @@
-import org.testng.annotations.Test;
+import org.junit.Test;
 
 public class FooTest {
   @Test public void test() { }
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/testsJar/some-thing/src/main/java/Foo.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/testsJar/some-thing/src/main/java/Foo.java
index 926c3dc..85c660d 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/testsJar/some-thing/src/main/java/Foo.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/testsJar/some-thing/src/main/java/Foo.java
@@ -4,4 +4,4 @@ public class Foo {
   public String toString() {
     return StringUtils.normalizeSpace("hi  there!");
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/testsJar/some-thing/src/test/java/FooTest.java b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/testsJar/some-thing/src/test/java/FooTest.java
index 652c6fc..fd85983 100644
--- a/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/testsJar/some-thing/src/test/java/FooTest.java
+++ b/subprojects/build-init/src/integTest/resources/org/gradle/buildinit/plugins/MavenConversionIntegrationTest/testsJar/some-thing/src/test/java/FooTest.java
@@ -5,4 +5,4 @@ public class FooTest {
   @Test public void test() {
     assertTrue(true);
   }
-}
\ No newline at end of file
+}
diff --git a/subprojects/build-init/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java b/subprojects/build-init/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
index 9ef6c17..017ed9e 100644
--- a/subprojects/build-init/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
+++ b/subprojects/build-init/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java
@@ -40,8 +40,10 @@
 import org.gradle.util.internal.GUtil;
 import org.gradle.util.internal.WrapUtil;
 import org.gradle.work.DisableCachingByDefault;
+import org.gradle.wrapper.Download;
 import org.gradle.wrapper.GradleWrapperMain;
 import org.gradle.wrapper.Install;
+import org.gradle.wrapper.Logger;
 import org.gradle.wrapper.WrapperExecutor;
 
 import javax.annotation.Nullable;
@@ -51,7 +53,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.URI;
 import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
@@ -73,7 +78,6 @@
 public abstract class Wrapper extends DefaultTask {
     public static final String DEFAULT_DISTRIBUTION_PARENT_NAME = Install.DEFAULT_DISTRIBUTION_PATH;
 
-
     /**
      * Specifies the Gradle distribution type.
      */
@@ -107,12 +111,16 @@ public enum PathBase {
     private PathBase archiveBase = PathBase.GRADLE_USER_HOME;
     private final Property<Integer> networkTimeout = getProject().getObjects().property(Integer.class);
     private final DistributionLocator locator = new DistributionLocator();
+    private final boolean isOffline;
+    private boolean distributionUrlConfigured = false;
 
     public Wrapper() {
         scriptFile = "gradlew";
         jarFile = "gradle/wrapper/gradle-wrapper.jar";
         distributionPath = DEFAULT_DISTRIBUTION_PARENT_NAME;
         archivePath = DEFAULT_DISTRIBUTION_PARENT_NAME;
+        isOffline = getProject().getGradle().getStartParameter().isOffline();
+        getValidateDistributionUrl().convention(true);
     }
 
     @Inject
@@ -130,6 +138,7 @@ void generate() {
         Properties existingProperties = propertiesFile.exists() ? GUtil.loadProperties(propertiesFile) : null;
 
         checkProperties(existingProperties);
+        validateDistributionUrl();
         writeProperties(propertiesFile, existingProperties);
         writeWrapperTo(jarFileDestination);
 
@@ -158,6 +167,26 @@ private void checkProperties(Properties existingProperties) {
         }
     }
 
+    private static final String DISTRIBUTION_URL_EXCEPTION_MESSAGE = "Test of distribution url %s failed. Please check the values set with --gradle-distribution-url and --gradle-version.";
+
+    private void validateDistributionUrl() {
+        if (distributionUrlConfigured && getValidateDistributionUrl().get()) {
+            String url = getDistributionUrl();
+            URI uri = URI.create(url);
+            if (uri.getScheme().equals("file")) {
+                if (!Files.exists(Paths.get(uri).toAbsolutePath())) {
+                    throw new UncheckedIOException(String.format(DISTRIBUTION_URL_EXCEPTION_MESSAGE, url));
+                }
+            } else if (uri.getScheme().startsWith("http") && !isOffline) {
+                try {
+                    new Download(new Logger(true), "gradlew", Download.UNKNOWN_VERSION).sendHeadRequest(uri);
+                } catch (Exception e) {
+                    throw new UncheckedIOException(String.format(DISTRIBUTION_URL_EXCEPTION_MESSAGE, url), e);
+                }
+            }
+        }
+    }
+
     private void writeWrapperTo(File destination) {
         URL jarFileSource = Wrapper.class.getResource("/gradle-wrapper.jar");
         if (jarFileSource == null) {
@@ -184,6 +213,7 @@ private void writeProperties(File propertiesFileDestination, Properties existing
         if (networkTimeout.isPresent()) {
             wrapperProperties.put(WrapperExecutor.NETWORK_TIMEOUT_PROPERTY, String.valueOf(networkTimeout.get()));
         }
+        wrapperProperties.put(WrapperExecutor.VALIDATE_DISTRIBUTION_URL, String.valueOf(getValidateDistributionUrl().get()));
         try {
             PropertiesUtils.store(wrapperProperties, propertiesFileDestination);
         } catch (IOException e) {
@@ -323,10 +353,13 @@ public String getGradleVersion() {
      * The version of the gradle distribution required by the wrapper.
      * This is usually the same version of Gradle you use for building your project.
      * The following labels are allowed to specify a version: {@code latest}, {@code release-candidate}, {@code nightly}, and {@code release-nightly}
+     *
+     * <p>The resulting distribution url is validated before it is written to the gradle-wrapper.properties file.
      */
     @Option(option = "gradle-version", description = "The version of the Gradle distribution required by the wrapper. " +
         "The following labels are allowed: latest, release-candidate, nightly, and release-nightly.")
     public void setGradleVersion(String gradleVersion) {
+        distributionUrlConfigured = true;
         this.gradleVersionResolver.setGradleVersionString(gradleVersion);
     }
 
@@ -393,9 +426,12 @@ public String getDistributionUrl() {
      * project, you might submit the distribution to your version control system. That way no download is necessary at
      * all. This might be in particular interesting, if you provide a custom gradle snapshot to the wrapper, because you
      * don't need to provide a download server then.
+     *
+     * <p>The distribution url is validated before it is written to the gradle-wrapper.properties file.
      */
     @Option(option = "gradle-distribution-url", description = "The URL to download the Gradle distribution from.")
     public void setDistributionUrl(String url) {
+        distributionUrlConfigured = true;
         this.distributionUrl = url;
     }
 
@@ -497,4 +533,15 @@ public void setArchiveBase(PathBase archiveBase) {
     public Property<Integer> getNetworkTimeout() {
         return networkTimeout;
     }
+
+    /**
+     * Indicates if this task will validate the distribution url that has been configured.
+     *
+     * @since 8.2
+     * @return whether this task will validate the distribution url
+     */
+    @Incubating
+    @Input
+    @Option(option = "validate-url", description = "Sets task to validate the configured distribution url")
+    public abstract Property<Boolean> getValidateDistributionUrl();
 }
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/WrapperPlugin.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/WrapperPlugin.java
index 43fa58c..1aa78a0 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/WrapperPlugin.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/WrapperPlugin.java
@@ -35,8 +35,6 @@
  */
 public abstract class WrapperPlugin implements Plugin<Project> {
 
-    private static final String SERVICES_GRADLE_ORG_VERSIONS_OVERRIDE_URL_PROPERTY = "org.gradle.internal.services.version.url.override";
-
     @Override
     public void apply(Project project) {
         if (project.getParent() == null) {
@@ -57,8 +55,7 @@ public void apply(Project project) {
     }
 
     private static String getVersionUrl() {
-        String baseUrl = System.getProperty(SERVICES_GRADLE_ORG_VERSIONS_OVERRIDE_URL_PROPERTY,
-            DistributionLocator.SERVICES_GRADLE_BASE_URL);
+        String baseUrl = DistributionLocator.getBaseUrl();
         return baseUrl + "/versions";
     }
 
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/BasicProjectGenerator.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/BasicProjectGenerator.java
index b4ad370..3520935 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/BasicProjectGenerator.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/BasicProjectGenerator.java
@@ -23,10 +23,16 @@
 import org.gradle.buildinit.plugins.internal.modifiers.Language;
 import org.gradle.buildinit.plugins.internal.modifiers.ModularizationOption;
 
-import java.util.Collections;
 import java.util.Optional;
 import java.util.Set;
 
+import static java.util.Collections.singleton;
+import static java.util.Optional.of;
+import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl.KOTLIN;
+import static org.gradle.buildinit.plugins.internal.modifiers.BuildInitTestFramework.NONE;
+import static org.gradle.buildinit.plugins.internal.modifiers.ComponentType.BASIC;
+import static org.gradle.buildinit.plugins.internal.modifiers.ModularizationOption.SINGLE_PROJECT;
+
 public class BasicProjectGenerator implements ProjectGenerator {
     private final BuildScriptBuilderFactory scriptBuilderFactory;
     private final DocumentationRegistry documentationRegistry;
@@ -45,14 +51,14 @@ public String getId() {
     public void generate(InitSettings settings) {
         scriptBuilderFactory.scriptForNewProjects(settings.getDsl(), "build", settings.isUseIncubatingAPIs())
             .fileComment("This is a general purpose Gradle build.\n"
-                + "Learn more about Gradle by exploring our samples at " + documentationRegistry.getSampleIndex())
+                + documentationRegistry.getSampleForMessage())
             .create(settings.getTarget())
             .generate();
     }
 
     @Override
     public ComponentType getComponentType() {
-        return ComponentType.BASIC;
+        return BASIC;
     }
 
     @Override
@@ -67,12 +73,12 @@ public boolean isJvmLanguage() {
 
     @Override
     public Set<ModularizationOption> getModularizationOptions() {
-        return Collections.singleton(ModularizationOption.SINGLE_PROJECT);
+        return singleton(SINGLE_PROJECT);
     }
 
     @Override
     public BuildInitDsl getDefaultDsl() {
-        return BuildInitDsl.GROOVY;
+        return KOTLIN;
     }
 
     @Override
@@ -82,16 +88,16 @@ public boolean supportsPackage() {
 
     @Override
     public BuildInitTestFramework getDefaultTestFramework() {
-        return BuildInitTestFramework.NONE;
+        return NONE;
     }
 
     @Override
     public Set<BuildInitTestFramework> getTestFrameworks() {
-        return Collections.singleton(BuildInitTestFramework.NONE);
+        return singleton(NONE);
     }
 
     @Override
     public Optional<String> getFurtherReading(InitSettings settings) {
-        return Optional.of("Learn more about Gradle by exploring our samples at " + documentationRegistry.getSampleIndex());
+        return of(documentationRegistry.getSampleForMessage());
     }
 }
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/BuildScriptBuilder.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/BuildScriptBuilder.java
index b440f34..2ac7268 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/BuildScriptBuilder.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/BuildScriptBuilder.java
@@ -2298,7 +2298,8 @@ public FailingHandler(DocumentationRegistry documentationRegistry) {
 
             @Override
             protected void handleInsecureURL(URI repoLocation, ScriptBlockImpl statements) {
-                LOGGER.error("Gradle found an insecure protocol in a repository definition. The current strategy for handling insecure URLs is to fail. For more options, see {}.", documentationRegistry.getDocumentationFor("build_init_plugin", "sec:allow_insecure"));
+                LOGGER.error("Gradle found an insecure protocol in a repository definition. The current strategy for handling insecure URLs is to fail. {}",
+                    documentationRegistry.getDocumentationRecommendationFor("options", "build_init_plugin", "sec:allow_insecure"));
                 throw new GradleException(String.format("Build generation aborted due to insecure protocol in repository: %s", repoLocation));
             }
         }
@@ -2314,7 +2315,8 @@ public WarningHandler(BuildInitDsl dsl, DocumentationRegistry documentationRegis
 
             @Override
             protected void handleInsecureURL(URI repoLocation, BuildScriptBuilder.ScriptBlockImpl statements) {
-                LOGGER.warn("Gradle found an insecure protocol in a repository definition. You will have to opt into allowing insecure protocols in the generated build file. See {}.", documentationRegistry.getDocumentationFor("build_init_plugin", "sec:allow_insecure"));
+                LOGGER.warn("Gradle found an insecure protocol in a repository definition. You will have to opt into allowing insecure protocols in the generated build file. {}",
+                    documentationRegistry.getDocumentationRecommendationFor("information on how to do this", "build_init_plugin", "sec:allow_insecure"));
                 // use the insecure URL as-is
                 statements.propertyAssignment(null, "url", new BuildScriptBuilder.MethodInvocationExpression(null, "uri", singletonList(new BuildScriptBuilder.StringValue(repoLocation.toString()))), true);
                 // Leave a commented out block for opting into using the insecure repository
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/CppProjectInitDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/CppProjectInitDescriptor.java
index a80110c..aa393af 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/CppProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/CppProjectInitDescriptor.java
@@ -45,8 +45,7 @@ public Language getLanguage() {
     public void generateProjectBuildScript(String projectName, InitSettings settings, BuildScriptBuilder buildScriptBuilder) {
         buildScriptBuilder
             .fileComment("This generated file contains a sample C++ project to get you started.")
-            .fileComment("For more details take a look at the Building C++ applications and libraries chapter in the Gradle")
-            .fileComment("User Manual available at " + documentationRegistry.getDocumentationFor("building_cpp_projects"));
+            .fileComment(documentationRegistry.getDocumentationRecommendationFor("details on building C++ applications and libraries", "building_cpp_projects"));
         configureBuildScript(settings, buildScriptBuilder);
     }
 
@@ -75,7 +74,7 @@ public BuildInitTestFramework getDefaultTestFramework() {
 
     @Override
     public Optional<String> getFurtherReading(InitSettings settings) {
-        return Optional.of(documentationRegistry.getSampleFor("building_cpp_" + getComponentType().pluralName()));
+        return Optional.of(documentationRegistry.getSampleForMessage("building_cpp_" + getComponentType().pluralName()));
     }
 
     protected abstract TemplateOperation sourceTemplateOperation(InitSettings settings);
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/GradlePropertiesGenerator.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/GradlePropertiesGenerator.java
new file mode 100644
index 0000000..3c20123
--- /dev/null
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/GradlePropertiesGenerator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 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.buildinit.plugins.internal;
+
+import org.gradle.api.UncheckedIOException;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class GradlePropertiesGenerator implements BuildContentGenerator {
+
+    @Override
+    public void generate(InitSettings settings) {
+        if (!settings.isUseIncubatingAPIs()) {
+            return;
+        }
+        File file = settings.getTarget().file("gradle.properties").getAsFile();
+        try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {
+            writer.println("# This file was generated by the Gradle 'init' task.");
+            writer.println("# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties");
+            writer.println();
+            writer.println("org.gradle.parallel=true");
+            writer.println("org.gradle.caching=true");
+            writer.println();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/GroovyGradlePluginProjectInitDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/GroovyGradlePluginProjectInitDescriptor.java
index bd43d0a..cab644b 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/GroovyGradlePluginProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/GroovyGradlePluginProjectInitDescriptor.java
@@ -58,6 +58,8 @@ public void generateProjectBuildScript(String projectName, InitSettings settings
         if (!settings.isUseTestSuites()) {
             buildScriptBuilder.testImplementationDependency("Use the awesome Spock testing and specification framework",
                 "org.spockframework:spock-core:" + libraryVersionProvider.getVersion("spock"));
+            buildScriptBuilder.testRuntimeOnlyDependency(null,
+                "org.junit.platform:junit-platform-launcher");
         }
     }
 
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/InitSettings.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/InitSettings.java
index eb6dc7a..187fc62 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/InitSettings.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/InitSettings.java
@@ -129,7 +129,7 @@ public boolean isUseIncubatingAPIs() {
 
     @Incubating
     public boolean isUseTestSuites() {
-        return useIncubatingAPIs; // The only Incubating API used, for now
+        return useIncubatingAPIs;
     }
 
     @Incubating
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JavaGradlePluginProjectInitDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JavaGradlePluginProjectInitDescriptor.java
index ead89fd..a24e7ae 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JavaGradlePluginProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JavaGradlePluginProjectInitDescriptor.java
@@ -59,6 +59,7 @@ public void generateProjectBuildScript(String projectName, InitSettings settings
                 "Use JUnit Jupiter for testing.",
                 "org.junit.jupiter:junit-jupiter:" + libraryVersionProvider.getVersion("junit-jupiter")
             );
+            buildScriptBuilder.testRuntimeOnlyDependency(null, "org.junit.platform:junit-platform-launcher");
         }
     }
 
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JvmGradlePluginProjectInitDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JvmGradlePluginProjectInitDescriptor.java
index ca8f555..6eb2f0c 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JvmGradlePluginProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JvmGradlePluginProjectInitDescriptor.java
@@ -51,8 +51,8 @@ public void generateProjectBuildScript(String projectName, InitSettings settings
 
         buildScriptBuilder
             .fileComment("This generated file contains a sample Gradle plugin project to get you started.")
-            .fileComment("For more details take a look at the Writing Custom Plugins chapter in the Gradle")
-            .fileComment("User Manual available at " + documentationRegistry.getDocumentationFor("custom_plugins"));
+            .fileComment(documentationRegistry.getDocumentationRecommendationFor("details on writing Custom Plugins", "custom_plugins"));
+
         buildScriptBuilder.plugin("Apply the Java Gradle plugin development plugin to add support for developing Gradle plugins", "java-gradle-plugin");
 
         buildScriptBuilder.block(null, "gradlePlugin", b -> b.containerElement("Define the plugin", "plugins", "greeting", null, g -> {
@@ -69,9 +69,14 @@ public void generateProjectBuildScript(String projectName, InitSettings settings
         } else {
             functionalTestSourceSet = buildScriptBuilder.createContainerElement("Add a source set for the functional test suite", "sourceSets", "functionalTest", "functionalTestSourceSet");
 
-            BuildScriptBuilder.Expression functionalTestConfiguration = buildScriptBuilder.containerElementExpression("configurations", "functionalTestImplementation");
-            BuildScriptBuilder.Expression testConfiguration = buildScriptBuilder.containerElementExpression("configurations", "testImplementation");
-            buildScriptBuilder.methodInvocation(null, functionalTestConfiguration, "extendsFrom", testConfiguration);
+            BuildScriptBuilder.Expression functionalTestImplementation = buildScriptBuilder.containerElementExpression("configurations", "functionalTestImplementation");
+            BuildScriptBuilder.Expression testImplementation = buildScriptBuilder.containerElementExpression("configurations", "testImplementation");
+            buildScriptBuilder.methodInvocation(null, functionalTestImplementation, "extendsFrom", testImplementation);
+
+            BuildScriptBuilder.Expression functionalTestRuntimeOnly = buildScriptBuilder.containerElementExpression("configurations", "functionalTestRuntimeOnly");
+            BuildScriptBuilder.Expression testRuntimeOnly = buildScriptBuilder.containerElementExpression("configurations", "testRuntimeOnly");
+            buildScriptBuilder.methodInvocation(null, functionalTestRuntimeOnly, "extendsFrom", testRuntimeOnly);
+
             BuildScriptBuilder.Expression functionalTest = buildScriptBuilder.taskRegistration("Add a task to run the functional tests", "functionalTest", "Test", b -> {
                 b.propertyAssignment(null, "testClassesDirs", buildScriptBuilder.propertyExpression(functionalTestSourceSet, "output.classesDirs"), true);
                 b.propertyAssignment(null, "classpath", buildScriptBuilder.propertyExpression(functionalTestSourceSet, "runtimeClasspath"), true);
@@ -102,7 +107,7 @@ public void generateSources(InitSettings settings, TemplateFactory templateFacto
 
     @Override
     public Optional<String> getFurtherReading(InitSettings settings) {
-        return Optional.of(documentationRegistry.getDocumentationFor("custom_plugins"));
+        return Optional.of(documentationRegistry.getDocumentationRecommendationFor("information", "custom_plugins"));
     }
 
     protected abstract TemplateOperation sourceTemplate(InitSettings settings, TemplateFactory templateFactory, String pluginId, String pluginClassName);
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JvmProjectInitDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JvmProjectInitDescriptor.java
index 04a1ac9..b42c194 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JvmProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/JvmProjectInitDescriptor.java
@@ -92,7 +92,7 @@ public Set<BuildInitTestFramework> getTestFrameworks() {
     @Override
     public Optional<String> getFurtherReading(InitSettings settings) {
         String multi = isSingleProject(settings) ? "" : "_multi_project";
-        return Optional.of(documentationRegistry.getSampleFor("building_" + getLanguage().getName() + "_" + getComponentType().pluralName() + multi));
+        return Optional.of(documentationRegistry.getSampleForMessage("building_" + getLanguage().getName() + "_" + getComponentType().pluralName() + multi));
     }
 
     @Override
@@ -115,8 +115,7 @@ public void generateProjectBuildScript(String projectName, InitSettings settings
 
         buildScriptBuilder
             .fileComment("This generated file contains a sample " + getLanguage() + " " + getComponentType() + " project to get you started.")
-            .fileComment("For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle")
-            .fileComment("User Manual available at " + documentationRegistry.getDocumentationFor("building_java_projects"));
+            .fileComment(documentationRegistry.getDocumentationRecommendationFor("details on building Java & JVM projects", "building_java_projects"));
 
         addStandardDependencies(buildScriptBuilder, false);
 
@@ -247,6 +246,7 @@ private void addTestFramework(BuildInitTestFramework testFramework, BuildScriptB
                 buildScriptBuilder.testImplementationDependency("Use the awesome Spock testing and specification framework even with Java",
                     "org.spockframework:spock-core:" + libraryVersionProvider.getVersion("spock"),
                     "junit:junit:" + libraryVersionProvider.getVersion("junit"));
+                buildScriptBuilder.testRuntimeOnlyDependency(null, "org.junit.platform:junit-platform-launcher");
                 buildScriptBuilder.taskMethodInvocation(
                     "Use JUnit Platform for unit tests.",
                     "test", "Test", "useJUnitPlatform");
@@ -264,6 +264,7 @@ private void addTestFramework(BuildInitTestFramework testFramework, BuildScriptB
                 buildScriptBuilder.testImplementationDependency(
                     "Use JUnit Jupiter for testing.",
                     "org.junit.jupiter:junit-jupiter:" + libraryVersionProvider.getVersion("junit-jupiter"));
+                buildScriptBuilder.testRuntimeOnlyDependency(null, "org.junit.platform:junit-platform-launcher");
 
                 buildScriptBuilder.taskMethodInvocation(
                     "Use JUnit Platform for unit tests.",
@@ -286,6 +287,7 @@ private void addTestFramework(BuildInitTestFramework testFramework, BuildScriptB
                 buildScriptBuilder.testImplementationDependency("Use the Kotlin JUnit 5 integration.", "org.jetbrains.kotlin:kotlin-test-junit5");
                 // TODO: Make this work with JUnit 5.6.0 again, see https://github.com/gradle/gradle/issues/13955
                 buildScriptBuilder.testImplementationDependency("Use the JUnit 5 integration.", "org.junit.jupiter:junit-jupiter-engine:" + libraryVersionProvider.getVersion("junit-jupiter"));
+                buildScriptBuilder.testRuntimeOnlyDependency(null, "org.junit.platform:junit-platform-launcher");
 
                 buildScriptBuilder.taskMethodInvocation(
                     "Use JUnit Platform for unit tests.",
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/KotlinGradlePluginProjectInitDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/KotlinGradlePluginProjectInitDescriptor.java
index 41b8a65..c37e4eb 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/KotlinGradlePluginProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/KotlinGradlePluginProjectInitDescriptor.java
@@ -60,6 +60,7 @@ public void generateProjectBuildScript(String projectName, InitSettings settings
 
         if (!settings.isUseTestSuites()) {
             buildScriptBuilder.testImplementationDependency("Use the Kotlin JUnit 5 integration.", "org.jetbrains.kotlin:kotlin-test-junit5");
+            buildScriptBuilder.testRuntimeOnlyDependency(null, "org.junit.platform:junit-platform-launcher");
         }
     }
 
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/LanguageSpecificAdaptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/LanguageSpecificAdaptor.java
index d0c56e0..229aa9b 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/LanguageSpecificAdaptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/LanguageSpecificAdaptor.java
@@ -79,10 +79,10 @@ public Optional<String> getFurtherReading(InitSettings settings) {
 
     @Override
     public BuildInitDsl getDefaultDsl() {
-        if (descriptor.getLanguage().equals(Language.KOTLIN)) {
-            return BuildInitDsl.KOTLIN;
+        if (descriptor.getLanguage().equals(Language.GROOVY)) {
+            return BuildInitDsl.GROOVY;
         }
-        return BuildInitDsl.GROOVY;
+        return BuildInitDsl.KOTLIN;
     }
 
     @Override
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/SimpleGlobalFilesBuildSettingsDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/SimpleGlobalFilesBuildSettingsDescriptor.java
index 39bcdf6..53b125d 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/SimpleGlobalFilesBuildSettingsDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/SimpleGlobalFilesBuildSettingsDescriptor.java
@@ -41,18 +41,16 @@ public void generate(InitSettings settings) {
 
     private BuildScriptBuilder builder(InitSettings settings) {
         BuildScriptBuilder builder = scriptBuilderFactory.scriptForNewProjects(settings.getDsl(), "settings", settings.isUseIncubatingAPIs());
-        builder.fileComment(
-                "The settings file is used to specify which projects to include in your build.\n\n"
-                    + "Detailed information about configuring a multi-project build in Gradle can be found\n"
-                    + "in the user manual at " + documentationRegistry.getDocumentationFor("multi_project_builds"));
+        builder.fileComment("The settings file is used to specify which projects to include in your build.\n\n")
+            .fileComment(documentationRegistry.getDocumentationRecommendationFor("detailed information on multi-project builds", "building_swift_projects"));
         if (settings.getModularizationOption() == ModularizationOption.WITH_LIBRARY_PROJECTS && settings.isUseIncubatingAPIs()) {
             builder.includePluginsBuild();
         }
 
-        if(settings.getJavaLanguageVersion().isPresent()){
+        if (settings.getJavaLanguageVersion().isPresent()) {
             builder.plugin(
                 "Apply the foojay-resolver plugin to allow automatic download of JDKs",
-                "org.gradle.toolchains.foojay-resolver",
+                "org.gradle.toolchains.foojay-resolver-convention",
                 "0.4.0");
         }
 
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/SwiftProjectInitDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/SwiftProjectInitDescriptor.java
index 3db29b3..721fc19 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/SwiftProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/SwiftProjectInitDescriptor.java
@@ -46,8 +46,7 @@ public Language getLanguage() {
     public void generateProjectBuildScript(String projectName, InitSettings settings, BuildScriptBuilder buildScriptBuilder) {
         buildScriptBuilder
             .fileComment("This generated file contains a sample Swift project to get you started.")
-            .fileComment("For more details take a look at the Building Swift applications and libraries chapter in the Gradle")
-            .fileComment("User Manual available at " + documentationRegistry.getDocumentationFor("building_swift_projects"));
+            .fileComment(documentationRegistry.getDocumentationRecommendationFor("details on building Swift applications and libraries", "building_swift_projects"));
         configureBuildScript(settings, buildScriptBuilder);
     }
 
@@ -75,7 +74,7 @@ public BuildInitTestFramework getDefaultTestFramework() {
 
     @Override
     public Optional<String> getFurtherReading(InitSettings settings) {
-        return Optional.of(documentationRegistry.getSampleFor("building_swift_" + getComponentType().pluralName()));
+        return Optional.of(documentationRegistry.getSampleForMessage("building_swift_" + getComponentType().pluralName()));
     }
 
     protected abstract TemplateOperation sourceTemplateOperation(InitSettings settings);
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/maven/PomProjectInitDescriptor.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/maven/PomProjectInitDescriptor.java
index 646d6e7..a44a2c7 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/maven/PomProjectInitDescriptor.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/maven/PomProjectInitDescriptor.java
@@ -143,7 +143,7 @@ public Set<BuildInitDsl> getDsls() {
 
     @Override
     public BuildInitDsl getDefaultDsl() {
-        return BuildInitDsl.GROOVY;
+        return BuildInitDsl.KOTLIN;
     }
 
     @Override
@@ -163,6 +163,6 @@ public Set<BuildInitTestFramework> getTestFrameworks() {
 
     @Override
     public Optional<String> getFurtherReading(InitSettings settings) {
-        return Optional.of(documentationRegistry.getDocumentationFor("migrating_from_maven"));
+        return Optional.of(documentationRegistry.getDocumentationRecommendationFor("information", "migrating_from_maven"));
     }
 }
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/modifiers/BuildInitDsl.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/modifiers/BuildInitDsl.java
index 279b574..8c68291 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/modifiers/BuildInitDsl.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/modifiers/BuildInitDsl.java
@@ -25,8 +25,8 @@
 
 public enum BuildInitDsl implements WithIdentifier {
 
-    GROOVY(".gradle"),
-    KOTLIN(".gradle.kts");
+    KOTLIN(".gradle.kts"),
+    GROOVY(".gradle");
 
     private final String fileExtension;
 
@@ -36,7 +36,7 @@ public enum BuildInitDsl implements WithIdentifier {
 
     public static BuildInitDsl fromName(@Nullable String name) {
         if (name == null) {
-            return GROOVY;
+            return KOTLIN;
         }
         for (BuildInitDsl language : values()) {
             if (language.getId().equals(name)) {
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/services/ProjectLayoutSetupRegistryFactory.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/services/ProjectLayoutSetupRegistryFactory.java
index 483bf44..be07060 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/services/ProjectLayoutSetupRegistryFactory.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/plugins/internal/services/ProjectLayoutSetupRegistryFactory.java
@@ -28,6 +28,7 @@
 import org.gradle.buildinit.plugins.internal.DefaultTemplateLibraryVersionProvider;
 import org.gradle.buildinit.plugins.internal.GitAttributesGenerator;
 import org.gradle.buildinit.plugins.internal.GitIgnoreGenerator;
+import org.gradle.buildinit.plugins.internal.GradlePropertiesGenerator;
 import org.gradle.buildinit.plugins.internal.GroovyGradlePluginProjectInitDescriptor;
 import org.gradle.buildinit.plugins.internal.JvmApplicationProjectInitDescriptor;
 import org.gradle.buildinit.plugins.internal.JavaGradlePluginProjectInitDescriptor;
@@ -71,8 +72,9 @@ public ProjectLayoutSetupRegistry createProjectLayoutSetupRegistry() {
         BuildContentGenerator resourcesGenerator = new ResourceDirsGenerator();
         BuildContentGenerator gitIgnoreGenerator = new GitIgnoreGenerator();
         BuildContentGenerator gitAttributesGenerator = new GitAttributesGenerator();
-        List<BuildContentGenerator> jvmProjectGenerators = ImmutableList.of(settingsDescriptor, gitIgnoreGenerator, gitAttributesGenerator, resourcesGenerator);
-        List<BuildContentGenerator> commonGenerators = ImmutableList.of(settingsDescriptor, gitIgnoreGenerator, gitAttributesGenerator);
+        BuildContentGenerator gradlePropertiesGenerator = new GradlePropertiesGenerator();
+        List<BuildContentGenerator> jvmProjectGenerators = ImmutableList.of(settingsDescriptor, gitIgnoreGenerator, gitAttributesGenerator, gradlePropertiesGenerator, resourcesGenerator);
+        List<BuildContentGenerator> commonGenerators = ImmutableList.of(settingsDescriptor, gitIgnoreGenerator, gitAttributesGenerator, gradlePropertiesGenerator);
         BuildInitializer basicType = of(new BasicProjectGenerator(scriptBuilderFactory, documentationRegistry), commonGenerators);
         PomProjectInitDescriptor mavenBuildConverter = new PomProjectInitDescriptor(mavenSettingsProvider, documentationRegistry, workerExecutor);
         ProjectLayoutSetupRegistry registry = new ProjectLayoutSetupRegistry(basicType, mavenBuildConverter, templateOperationBuilder);
diff --git a/subprojects/build-init/src/main/java/org/gradle/buildinit/tasks/InitBuild.java b/subprojects/build-init/src/main/java/org/gradle/buildinit/tasks/InitBuild.java
index 4ac3dc8..f3dabfa 100644
--- a/subprojects/build-init/src/main/java/org/gradle/buildinit/tasks/InitBuild.java
+++ b/subprojects/build-init/src/main/java/org/gradle/buildinit/tasks/InitBuild.java
@@ -84,7 +84,8 @@ public String getType() {
     /**
      * Should the build be split into multiple subprojects?
      *
-     * This property can be set via command-line option '--split-project'.
+     * This property can be set via the command-line options '--split-project'
+     * and '--no-split-project'.
      *
      * @since 6.7
      */
@@ -96,7 +97,7 @@ public Property<Boolean> getSplitProject() {
     }
 
     /**
-     * The desired DSL of build scripts to create, defaults to 'groovy'.
+     * The desired DSL of build scripts to create, defaults to 'kotlin'.
      *
      * This property can be set via command-line option '--dsl'.
      *
@@ -105,7 +106,7 @@ public Property<Boolean> getSplitProject() {
     @Optional
     @Input
     public String getDsl() {
-        return isNullOrEmpty(dsl) ? BuildInitDsl.GROOVY.getId() : dsl;
+        return isNullOrEmpty(dsl) ? BuildInitDsl.KOTLIN.getId() : dsl;
     }
 
     /**
@@ -220,7 +221,8 @@ public void setupProjectLayout() {
             toolChainVersion);
         initDescriptor.generate(settings);
 
-        initDescriptor.getFurtherReading(settings).ifPresent(link -> getLogger().lifecycle("Get more help with your project: {}", link));
+        initDescriptor.getFurtherReading(settings)
+            .ifPresent(link -> getLogger().lifecycle(link));
     }
 
     private static void validatePackageName(String packageName) {
diff --git a/subprojects/build-init/src/main/java/org/gradleinternal/buildinit/plugins/internal/maven/Maven2Gradle.java b/subprojects/build-init/src/main/java/org/gradleinternal/buildinit/plugins/internal/maven/Maven2Gradle.java
index 73912a7..c654897 100644
--- a/subprojects/build-init/src/main/java/org/gradleinternal/buildinit/plugins/internal/maven/Maven2Gradle.java
+++ b/subprojects/build-init/src/main/java/org/gradleinternal/buildinit/plugins/internal/maven/Maven2Gradle.java
@@ -245,7 +245,7 @@ private void testNg(List<Dependency> moduleDependencies, BuildScriptBuilder buil
     private Set<MavenProject> modules(Set<MavenProject> projects, boolean incReactors) {
         return projects.stream().filter(project -> {
             Optional<MavenProject> parentIsPartOfThisBuild = projects.stream().filter(proj ->
-                project.getParent() != null && proj.getArtifactId().equals(project.getParent().getArtifactId()) && proj.getGroupId().equals(project.getParent().getGroupId())
+                project.getParent() == null || (project.getParent() != null && proj.getArtifactId().equals(project.getParent().getArtifactId()) && proj.getGroupId().equals(project.getParent().getGroupId()))
             ).findFirst();
             return parentIsPartOfThisBuild.isPresent() && (incReactors || !"pom".equals(project.getPackaging()));
         }).collect(Collectors.toSet());
@@ -413,10 +413,10 @@ private void compilerSettings(MavenProject project, BuildScriptBuilder builder)
             if (configuration != null) {
                 Xpp3Dom configuredSource = configuration.getChild("source");
                 Xpp3Dom configuredTarget = configuration.getChild("target");
-                if (configuredSource != null && !configuredSource.getValue().trim().isEmpty()) {
+                if (configuredSource != null && configuredSource.getValue() != null && !configuredSource.getValue().trim().isEmpty()) {
                     source = configuredSource.getValue();
                 }
-                if (configuredTarget != null && !configuredTarget.getValue().trim().isEmpty()) {
+                if (configuredTarget != null && configuredTarget.getValue() != null && !configuredTarget.getValue().trim().isEmpty()) {
                     target = configuredTarget.getValue();
                 }
             }
diff --git a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/library-versions.properties b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/library-versions.properties
index b9adfa7..4ae9639 100644
--- a/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/library-versions.properties
+++ b/subprojects/build-init/src/main/resources/org/gradle/buildinit/tasks/templates/library-versions.properties
@@ -1,16 +1,16 @@
 #Generated file, please do not edit - Version values used in build-init templates
 commons-math=3.6.1
 commons-text=1.10.0
-groovy=3.0.13
+groovy=3.0.17
 guava=31.1-jre
-junit-jupiter=5.9.1
+junit-jupiter=5.9.2
 junit=4.13.2
-kotlin=1.8.10
+kotlin=1.8.21
 scala-library=2.13.10
 scala-xml=1.2.0
 scala=2.13
-scalatest=3.2.14
+scalatest=3.2.15
 scalatestplus-junit=3.2.2.0
-slf4j=2.0.5
+slf4j=2.0.7
 spock=2.2-groovy-3.0
 testng=7.5
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.groovy b/subprojects/build-init/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.groovy
index d948f4d..0d4c78e 100644
--- a/subprojects/build-init/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.groovy
+++ b/subprojects/build-init/src/test/groovy/org/gradle/api/tasks/wrapper/WrapperTest.groovy
@@ -21,7 +21,6 @@
 import org.gradle.api.tasks.wrapper.internal.DefaultWrapperVersionsResources
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.util.GradleVersion
-import org.gradle.util.internal.DistributionLocator
 import org.gradle.util.internal.GUtil
 import org.gradle.util.internal.WrapUtil
 import org.gradle.wrapper.GradleWrapperMain
@@ -79,6 +78,7 @@
         wrapper.getDistributionUrl() != null
         wrapper.getDistributionSha256Sum() == null
         !wrapper.getNetworkTimeout().isPresent()
+        wrapper.getValidateDistributionUrl()
     }
 
     def "determines Windows script path from unix script path with #inName"() {
@@ -107,7 +107,7 @@
         wrapper.setGradleVersion(version)
 
         then:
-        "$DistributionLocator.RELEASE_REPOSITORY$snapshot/gradle-$out-bin.zip" == wrapper.getDistributionUrl()
+        "https://services.gradle.org/distributions$snapshot/gradle-$out-bin.zip" == wrapper.getDistributionUrl()
 
         where:
         version                     | out                         | snapshot
@@ -145,6 +145,14 @@
         5000 == wrapper.getNetworkTimeout().get()
     }
 
+    def "uses defined validateDistributionUrl value"() {
+        when:
+        wrapper.setValidateDistributionUrl(false)
+
+        then:
+        !wrapper.getValidateDistributionUrl().get()
+    }
+
     def "execute with non extant wrapper jar parent directory"() {
         when:
         def unjarDir = temporaryFolder.createDir("unjar")
@@ -174,11 +182,23 @@
         properties.getProperty(WrapperExecutor.NETWORK_TIMEOUT_PROPERTY) == "6000"
     }
 
+    def "execute with validateDistributionUrl set"() {
+        given:
+        wrapper.setValidateDistributionUrl(false)
+
+        when:
+        execute(wrapper)
+        def properties = GUtil.loadProperties(expectedTargetWrapperProperties)
+
+        then:
+        properties.getProperty(WrapperExecutor.VALIDATE_DISTRIBUTION_URL) == "false"
+    }
+
     def "check inputs"() {
         expect:
         TaskPropertyTestUtils.getProperties(wrapper).keySet() == WrapUtil.toSet(
             "distributionBase", "distributionPath", "distributionUrl", "distributionSha256Sum",
-            "distributionType", "archiveBase", "archivePath", "gradleVersion", "networkTimeout")
+            "distributionType", "archiveBase", "archivePath", "gradleVersion", "networkTimeout", "validateDistributionUrl")
     }
 
     def "execute with extant wrapper jar parent directory and extant wrapper jar"() {
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/GradlePropertiesGeneratorTest.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/GradlePropertiesGeneratorTest.groovy
new file mode 100644
index 0000000..a33f8ff
--- /dev/null
+++ b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/GradlePropertiesGeneratorTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 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.buildinit.plugins.internal
+
+import org.gradle.api.file.Directory
+import org.gradle.api.file.RegularFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+class GradlePropertiesGeneratorTest extends Specification {
+
+    @Rule
+    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass())
+
+    InitSettings settings = Mock()
+    File propertiesFile = tmpDir.file("gradle.properties")
+
+    def setup() {
+        Directory target = Mock()
+        RegularFile propertiesRegularFile = Mock()
+        settings.target >> target
+        target.file('gradle.properties') >> propertiesRegularFile
+        propertiesRegularFile.asFile >> propertiesFile
+    }
+
+    def "generates gradle.properties file if incubating"() {
+        setup:
+        def generator = new GradlePropertiesGenerator()
+        settings.isUseIncubatingAPIs() >> true
+
+        when:
+        generator.generate(settings)
+
+        then:
+        propertiesFile.file
+        propertiesFile.text.contains('org.gradle.parallel=true')
+        propertiesFile.text.contains('org.gradle.caching=true')
+    }
+
+    def "doesn't generate gradle.properties file if not incubating"() {
+        setup:
+        def generator = new GradlePropertiesGenerator()
+        settings.isUseIncubatingAPIs() >> false
+
+        when:
+        generator.generate(settings)
+
+        then:
+        !propertiesFile.file
+    }
+}
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/TemplateFactoryTest.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/TemplateFactoryTest.groovy
index aaa6048..23cd9c3 100644
--- a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/TemplateFactoryTest.groovy
+++ b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/TemplateFactoryTest.groovy
@@ -119,7 +119,7 @@
     }
 
     private createInitSettings(String packageName) {
-        new InitSettings("project", false, ["app"], ModularizationOption.SINGLE_PROJECT, BuildInitDsl.GROOVY, packageName,
+        new InitSettings("project", false, ["app"], ModularizationOption.SINGLE_PROJECT, BuildInitDsl.KOTLIN, packageName,
             BuildInitTestFramework.NONE, InsecureProtocolOption.WARN, targetDir, empty())
     }
 
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/maven/MavenProjectsCreatorSpec.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/maven/MavenProjectsCreatorSpec.groovy
index f18e8bb..92b2b16 100644
--- a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/maven/MavenProjectsCreatorSpec.groovy
+++ b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/maven/MavenProjectsCreatorSpec.groovy
@@ -180,7 +180,7 @@
 </project>
 """
         def mavenProjects = creator.create(settings.buildSettings(), parentPom)
-        def converter = new Maven2Gradle(mavenProjects, target, BuildInitDsl.GROOVY, false, InsecureProtocolOption.ALLOW)
+        def converter = new Maven2Gradle(mavenProjects, target, BuildInitDsl.KOTLIN, false, InsecureProtocolOption.ALLOW)
 
         expect:
         converter.convert()
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/modifiers/BuildInitBuildScriptDslTest.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/modifiers/BuildInitBuildScriptDslTest.groovy
index 63a1418..f3c2047 100644
--- a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/modifiers/BuildInitBuildScriptDslTest.groovy
+++ b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/plugins/internal/modifiers/BuildInitBuildScriptDslTest.groovy
@@ -29,12 +29,12 @@
         BuildInitDsl.fromName("kotlin") == BuildInitDsl.KOTLIN
     }
 
-    def "should convert null build script DSL string to the default groovy"() {
+    def "should convert null build script DSL string to the default kotlin"() {
         when:
         def result = BuildInitDsl.fromName(null)
 
         then:
-        result == BuildInitDsl.GROOVY
+        result == BuildInitDsl.KOTLIN
     }
 
     def "should throw exception for unknown build script DSL"() {
@@ -44,8 +44,8 @@
         then:
         GradleException e = thrown()
         e.message == TextUtil.toPlatformLineSeparators("""The requested build script DSL 'unknown' is not supported. Supported DSLs:
-  - 'groovy'
-  - 'kotlin'""")
+  - 'kotlin'
+  - 'groovy'""")
     }
 
     def "should list all supported build script DSLs"() {
@@ -54,7 +54,7 @@
 
         then:
         result.size() == 2
-        result[0] == "groovy"
-        result[1] == "kotlin"
+        result[0] == "kotlin"
+        result[1] == "groovy"
     }
 }
diff --git a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/tasks/InitBuildSpec.groovy b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/tasks/InitBuildSpec.groovy
index 8e74331..ea278ce 100644
--- a/subprojects/build-init/src/test/groovy/org/gradle/buildinit/tasks/InitBuildSpec.groovy
+++ b/subprojects/build-init/src/test/groovy/org/gradle/buildinit/tasks/InitBuildSpec.groovy
@@ -74,8 +74,8 @@
         projectLayoutRegistry.get(ComponentType.BASIC, Language.NONE) >> projectSetupDescriptor
         projectSetupDescriptor.modularizationOptions >> [ModularizationOption.SINGLE_PROJECT]
         projectSetupDescriptor.componentType >> ComponentType.BASIC
-        projectSetupDescriptor.dsls >> [GROOVY]
-        projectSetupDescriptor.defaultDsl >> GROOVY
+        projectSetupDescriptor.dsls >> [KOTLIN]
+        projectSetupDescriptor.defaultDsl >> KOTLIN
         projectSetupDescriptor.testFrameworks >> [NONE]
         projectSetupDescriptor.defaultTestFramework >> NONE
         projectSetupDescriptor.getFurtherReading(_ as InitSettings) >> empty()
@@ -84,7 +84,7 @@
         init.setupProjectLayout()
 
         then:
-        1 * projectSetupDescriptor.generate({it.dsl == GROOVY && it.testFramework == NONE})
+        1 * projectSetupDescriptor.generate({it.dsl == KOTLIN && it.testFramework == NONE})
     }
 
     def "creates project with specified type and dsl and test framework"() {
@@ -111,7 +111,7 @@
         projectLayoutRegistry.get("some-type") >> projectSetupDescriptor
         projectSetupDescriptor.id >> "some-type"
         projectSetupDescriptor.modularizationOptions >> [ModularizationOption.SINGLE_PROJECT]
-        projectSetupDescriptor.dsls >> [GROOVY]
+        projectSetupDescriptor.dsls >> [KOTLIN]
         projectSetupDescriptor.testFrameworks >> [NONE, JUNIT]
         init.type = "some-type"
         init.testFramework = "spock"
diff --git a/subprojects/build-init/src/testFixtures/groovy/org/gradle/buildinit/plugins/fixtures/ScriptDslFixture.groovy b/subprojects/build-init/src/testFixtures/groovy/org/gradle/buildinit/plugins/fixtures/ScriptDslFixture.groovy
index f9ced2e..b37bcdf 100644
--- a/subprojects/build-init/src/testFixtures/groovy/org/gradle/buildinit/plugins/fixtures/ScriptDslFixture.groovy
+++ b/subprojects/build-init/src/testFixtures/groovy/org/gradle/buildinit/plugins/fixtures/ScriptDslFixture.groovy
@@ -30,7 +30,7 @@
 @CompileStatic
 class ScriptDslFixture {
 
-    static final List<BuildInitDsl> SCRIPT_DSLS = JavaVersion.current().isJava8Compatible() ? [GROOVY, KOTLIN] : [GROOVY]
+    static final List<BuildInitDsl> SCRIPT_DSLS = JavaVersion.current().isJava8Compatible() ? [KOTLIN, GROOVY] : [GROOVY]
 
     static final List<List<BuildInitDsl>> scriptDslCombinationsFor(int count) {
         return ([SCRIPT_DSLS] * count).combinations()
diff --git a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/AbstractBuildOption.java b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/AbstractBuildOption.java
index 8df35c2..f1321b0 100644
--- a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/AbstractBuildOption.java
+++ b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/AbstractBuildOption.java
@@ -22,6 +22,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Provides a basic infrastructure for build option implementations.
@@ -32,15 +33,24 @@ public abstract class AbstractBuildOption<T, V extends CommandLineOptionConfigur
 
     protected final String gradleProperty;
     protected final List<V> commandLineOptionConfigurations;
+    protected final String deprecatedGradleProperty;
 
     public AbstractBuildOption(String gradleProperty) {
-        this(gradleProperty, (V[]) null);
+        this(gradleProperty, null, Collections.<V>emptyList());
+    }
+
+    public AbstractBuildOption(String gradleProperty, String deprecatedGradleProperty, V... commandLineOptionConfiguration) {
+        this(gradleProperty, deprecatedGradleProperty, commandLineOptionConfiguration != null ? Arrays.asList(commandLineOptionConfiguration) : Collections.<V>emptyList());
     }
 
     public AbstractBuildOption(String gradleProperty, V... commandLineOptionConfiguration) {
-        this.gradleProperty = gradleProperty;
-        this.commandLineOptionConfigurations = commandLineOptionConfiguration != null ? Arrays.asList(commandLineOptionConfiguration) : Collections.<V>emptyList();
+        this(gradleProperty, null, commandLineOptionConfiguration != null ? Arrays.asList(commandLineOptionConfiguration) : Collections.<V>emptyList());
+    }
 
+    private AbstractBuildOption(String gradleProperty, String deprecatedGradleProperty, List<V> commandLineOptionConfigurations) {
+        this.gradleProperty = gradleProperty;
+        this.deprecatedGradleProperty = deprecatedGradleProperty;
+        this.commandLineOptionConfigurations = commandLineOptionConfigurations;
     }
 
     @Override
@@ -48,6 +58,11 @@ public String getGradleProperty() {
         return gradleProperty;
     }
 
+    @Override
+    public String getDeprecatedGradleProperty() {
+        return deprecatedGradleProperty;
+    }
+
     protected boolean isTrue(String value) {
         return value != null && value.trim().equalsIgnoreCase("true");
     }
@@ -66,4 +81,34 @@ protected CommandLineOption configureCommandLineOption(CommandLineParser parser,
 
         return option;
     }
+
+    protected OptionValue<String> getFromProperties(Map<String, String> properties) {
+        String value = properties.get(gradleProperty);
+        if (value != null) {
+            return new OptionValue<String>(value, Origin.forGradleProperty(gradleProperty));
+        }
+        if (deprecatedGradleProperty != null) {
+            value = properties.get(deprecatedGradleProperty);
+            if (value != null) {
+                return new OptionValue<String>(value, Origin.forGradleProperty(deprecatedGradleProperty));
+            }
+        }
+        return new OptionValue<String>(null, null);
+    }
+
+    protected static class OptionValue<T> {
+        private final T value;
+        private final Origin origin;
+
+        public OptionValue(T value, Origin origin) {
+            this.value = value;
+            this.origin = origin;
+        }
+        public T getValue() {
+            return value;
+        }
+        public Origin getOrigin() {
+            return origin;
+        }
+    }
 }
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 25fd78f..e14a0dd 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
@@ -31,19 +31,23 @@
 public abstract class BooleanBuildOption<T> extends AbstractBuildOption<T, BooleanCommandLineOptionConfiguration> {
 
     public BooleanBuildOption(String gradleProperty) {
-        super(gradleProperty);
+        this(gradleProperty, (String) null);
     }
 
     public BooleanBuildOption(String gradleProperty, BooleanCommandLineOptionConfiguration... commandLineOptionConfigurations) {
-        super(gradleProperty, commandLineOptionConfigurations);
+        this(gradleProperty, null, commandLineOptionConfigurations);
+    }
+
+    public BooleanBuildOption(String gradleProperty, String deprecatedGradleProperty, BooleanCommandLineOptionConfiguration... commandLineOptionConfigurations) {
+        super(gradleProperty, deprecatedGradleProperty, commandLineOptionConfigurations);
     }
 
     @Override
     public void applyFromProperty(Map<String, String> properties, T settings) {
-        String value = properties.get(gradleProperty);
-
+        OptionValue<String> propertyValue = getFromProperties(properties);
+        String value = propertyValue.getValue();
         if (value != null) {
-            applyTo(isTrue(value), settings, Origin.forGradleProperty(gradleProperty));
+            applyTo(isTrue(value), settings, propertyValue.getOrigin());
         }
     }
 
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 2fe46bb..94ea98e 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
@@ -38,4 +38,6 @@ public interface BuildOption<T> extends Option {
     void configure(CommandLineParser parser);
 
     void applyFromCommandLine(ParsedCommandLine options, T settings);
+
+    String getDeprecatedGradleProperty();
 }
diff --git a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/EnumBuildOption.java b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/EnumBuildOption.java
index 9772b27..7aaa82f 100644
--- a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/EnumBuildOption.java
+++ b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/EnumBuildOption.java
@@ -35,19 +35,23 @@ public abstract class EnumBuildOption<E extends Enum<E>, T> extends AbstractBuil
     private final Class<E> enumClass;
     private final List<E> possibleValues;
 
-    public EnumBuildOption(String displayName, Class<E> enumClass, E[] possibleValues, String gradleProperty, CommandLineOptionConfiguration... commandLineOptionConfigurations) {
-        super(gradleProperty, commandLineOptionConfigurations);
+    public EnumBuildOption(String displayName, Class<E> enumClass, E[] possibleValues, String gradleProperty, String deprecatedProperty, CommandLineOptionConfiguration... commandLineOptionConfigurations) {
+        super(gradleProperty, deprecatedProperty, commandLineOptionConfigurations);
         this.displayName = displayName;
         this.enumClass = enumClass;
         this.possibleValues = Collections.unmodifiableList(Arrays.asList(possibleValues));
     }
 
+    public EnumBuildOption(String displayName, Class<E> enumClass, E[] possibleValues, String gradleProperty, CommandLineOptionConfiguration... commandLineOptionConfigurations) {
+        this(displayName, enumClass, possibleValues, gradleProperty, null, commandLineOptionConfigurations);
+    }
+
     @Override
     public void applyFromProperty(Map<String, String> properties, T settings) {
-        String value = properties.get(gradleProperty);
-
+        OptionValue<String> propertyValue = getFromProperties(properties);
+        String value = propertyValue.getValue();
         if (value != null) {
-            applyTo(value, settings, Origin.forGradleProperty(gradleProperty));
+            applyTo(value, settings, propertyValue.getOrigin());
         }
     }
 
diff --git a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/IntegerBuildOption.java b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/IntegerBuildOption.java
index 55df09f..0e00337 100644
--- a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/IntegerBuildOption.java
+++ b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/IntegerBuildOption.java
@@ -27,6 +27,10 @@ public IntegerBuildOption(String gradleProperty) {
         super(gradleProperty);
     }
 
+    public IntegerBuildOption(String gradleProperty, String deprecatedGradleProperty) {
+        super(gradleProperty, deprecatedGradleProperty);
+    }
+
     public IntegerBuildOption(String gradleProperty, CommandLineOptionConfiguration... commandLineOptionConfigurations) {
         super(gradleProperty, commandLineOptionConfigurations);
     }
@@ -34,9 +38,10 @@ public IntegerBuildOption(String gradleProperty, CommandLineOptionConfiguration.
 
     @Override
     public void applyFromProperty(Map<String, String> properties, T settings) {
-        String value = properties.get(gradleProperty);
+        OptionValue<String> propertyValue = getFromProperties(properties);
+        String value = propertyValue.getValue();
         if (value != null) {
-            applyTo(Integer.parseInt(value), settings, Origin.forGradleProperty(gradleProperty));
+            applyTo(Integer.parseInt(value), settings, propertyValue.getOrigin());
         }
     }
 
diff --git a/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/StringInternalOption.java b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/StringInternalOption.java
new file mode 100644
index 0000000..85d0bef
--- /dev/null
+++ b/subprojects/build-option/src/main/java/org/gradle/internal/buildoption/StringInternalOption.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 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.buildoption;
+
+import javax.annotation.Nullable;
+
+public class StringInternalOption implements InternalOption<String> {
+    private final String systemPropertyName;
+    private final String defaultValue;
+
+    public StringInternalOption(String systemPropertyName, String defaultValue) {
+        this.systemPropertyName = systemPropertyName;
+        this.defaultValue = defaultValue;
+    }
+
+    @Nullable
+    @Override
+    public String getSystemPropertyName() {
+        return systemPropertyName;
+    }
+
+    @Override
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    @Override
+    public String convert(String value) {
+        return value;
+    }
+}
diff --git a/subprojects/build-profile/src/main/java/org/gradle/profile/BuildProfile.java b/subprojects/build-profile/src/main/java/org/gradle/profile/BuildProfile.java
index c5b0eb9..d37bf0d 100644
--- a/subprojects/build-profile/src/main/java/org/gradle/profile/BuildProfile.java
+++ b/subprojects/build-profile/src/main/java/org/gradle/profile/BuildProfile.java
@@ -52,7 +52,7 @@ public class BuildProfile {
 
     private final Map<String, ProjectProfile> projects = new LinkedHashMap<>();
     private final Map<String, ContinuousOperation> dependencySets = new LinkedHashMap<>();
-    private final Map<String, FragmentedOperation> transformations = Maps.newLinkedHashMap();
+    private final Map<String, FragmentedOperation> transforms = Maps.newLinkedHashMap();
     private long profilingStarted;
     private long buildStarted;
     private long settingsEvaluated;
@@ -146,17 +146,17 @@ public CompositeOperation<ContinuousOperation> getDependencySets() {
         return new CompositeOperation<>(profiles);
     }
 
-    public FragmentedOperation getTransformationProfile(String transformationDescription) {
-        FragmentedOperation profile = transformations.get(transformationDescription);
+    public FragmentedOperation getTransformProfile(String transformDescription) {
+        FragmentedOperation profile = transforms.get(transformDescription);
         if (profile == null) {
-            profile = new FragmentedOperation(transformationDescription);
-            transformations.put(transformationDescription, profile);
+            profile = new FragmentedOperation(transformDescription);
+            transforms.put(transformDescription, profile);
         }
         return profile;
     }
 
-    public CompositeOperation<FragmentedOperation> getTransformations() {
-        final List<FragmentedOperation> profiles = CollectionUtils.sort(transformations.values(), Operation.slowestFirst());
+    public CompositeOperation<FragmentedOperation> getTransforms() {
+        final List<FragmentedOperation> profiles = CollectionUtils.sort(transforms.values(), Operation.slowestFirst());
         return new CompositeOperation<>(profiles);
     }
 
@@ -244,11 +244,11 @@ public long getElapsedProjectsConfiguration() {
     }
 
     /**
-     * Get the total artifact transformation time.
+     * Get the total artifact transform time.
      */
     public long getElapsedArtifactTransformTime() {
         long result = 0;
-        for (FragmentedOperation transform : transformations.values()) {
+        for (FragmentedOperation transform : transforms.values()) {
             result += transform.getElapsedTime();
         }
         return result;
diff --git a/subprojects/build-profile/src/main/java/org/gradle/profile/ProfileEventAdapter.java b/subprojects/build-profile/src/main/java/org/gradle/profile/ProfileEventAdapter.java
index 042de4b..1fe1bdf 100644
--- a/subprojects/build-profile/src/main/java/org/gradle/profile/ProfileEventAdapter.java
+++ b/subprojects/build-profile/src/main/java/org/gradle/profile/ProfileEventAdapter.java
@@ -23,7 +23,7 @@
 import org.gradle.api.artifacts.DependencyResolutionListener;
 import org.gradle.api.artifacts.ResolvableDependencies;
 import org.gradle.api.initialization.Settings;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformListener;
+import org.gradle.api.internal.artifacts.transform.TransformExecutionListener;
 import org.gradle.api.internal.project.taskfactory.TaskIdentity;
 import org.gradle.api.invocation.Gradle;
 import org.gradle.api.tasks.TaskState;
@@ -37,10 +37,10 @@
  * Adapts various events to build a {@link BuildProfile} model.
  */
 @ListenerService
-public class ProfileEventAdapter implements InternalBuildListener, ProjectEvaluationListener, TaskListenerInternal, DependencyResolutionListener, ArtifactTransformListener {
+public class ProfileEventAdapter implements InternalBuildListener, ProjectEvaluationListener, TaskListenerInternal, DependencyResolutionListener, TransformExecutionListener {
     private final BuildStartedTime buildStartedTime;
     private final Clock clock;
-    private final ThreadLocal<ContinuousOperation> currentTransformation = new ThreadLocal<ContinuousOperation>();
+    private final ThreadLocal<ContinuousOperation> currentTransform = new ThreadLocal<>();
     private final BuildProfile buildProfile;
 
     public ProfileEventAdapter(BuildProfile buildProfile, BuildStartedTime buildStartedTime, Clock clock) {
@@ -123,18 +123,19 @@ public void afterResolve(ResolvableDependencies dependencies) {
         buildProfile.getDependencySetProfile(dependencies.getPath()).setFinish(now);
     }
 
+    // TransformExecutionListener
     @Override
-    public void beforeTransformerInvocation(Describable transformer, Describable subject) {
+    public void beforeTransformExecution(Describable transform, Describable subject) {
         long now = clock.getCurrentTime();
-        String transformationDescription = subject.getDisplayName() + " with " + transformer.getDisplayName();
-        FragmentedOperation transformationProfile = buildProfile.getTransformationProfile(transformationDescription);
-        currentTransformation.set(transformationProfile.start(now));
+        String transformDescription = subject.getDisplayName() + " with " + transform.getDisplayName();
+        FragmentedOperation transformProfile = buildProfile.getTransformProfile(transformDescription);
+        currentTransform.set(transformProfile.start(now));
     }
 
     @Override
-    public void afterTransformerInvocation(Describable transformer, Describable subject) {
+    public void afterTransformExecution(Describable transform, Describable subject) {
         long now = clock.getCurrentTime();
-        currentTransformation.get().setFinish(now);
-        currentTransformation.remove();
+        currentTransform.get().setFinish(now);
+        currentTransform.remove();
     }
 }
diff --git a/subprojects/build-profile/src/main/java/org/gradle/profile/ProfileReportRenderer.java b/subprojects/build-profile/src/main/java/org/gradle/profile/ProfileReportRenderer.java
index e1ebd816..44f4b58 100644
--- a/subprojects/build-profile/src/main/java/org/gradle/profile/ProfileReportRenderer.java
+++ b/subprojects/build-profile/src/main/java/org/gradle/profile/ProfileReportRenderer.java
@@ -169,7 +169,7 @@ public void render(BuildProfile model, SimpleHtmlWriter htmlWriter) throws IOExc
                                     htmlWriter.startElement("td").attribute("class", "numeric").characters(TimeFormatting.formatDurationVeryTerse(model.getElapsedArtifactTransformTime())).endElement();
                                 htmlWriter.endElement();
 
-                                for (Operation operation : model.getTransformations()) {
+                                for (Operation operation : model.getTransforms()) {
                                     htmlWriter.startElement("tr");
                                         htmlWriter.startElement("td").characters(operation.getDescription()).endElement();
                                         htmlWriter.startElement("td").attribute("class", "numeric").characters(TimeFormatting.formatDurationVeryTerse(operation.getElapsedTime())).endElement();
diff --git a/subprojects/build-profile/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy b/subprojects/build-profile/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy
index 8d29fd2..5c304fa 100644
--- a/subprojects/build-profile/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy
+++ b/subprojects/build-profile/src/test/groovy/org/gradle/profile/ProfileReportRendererTest.groovy
@@ -45,8 +45,8 @@
         model.getDependencySetProfile("runtime").start = time(12, 24, 0)
         model.getDependencySetProfile("runtime").finish = time(12, 24, 30)
 
-        model.getTransformationProfile("some transform").start(time(12, 22, 0)).setFinish(time(12, 22, 12))
-        model.getTransformationProfile("some other transform").start(time(12, 23, 0)).setFinish(time(12, 23, 19))
+        model.getTransformProfile("some transform").start(time(12, 22, 0)).setFinish(time(12, 22, 12))
+        model.getTransformProfile("some other transform").start(time(12, 23, 0)).setFinish(time(12, 23, 19))
 
         model.getProjectProfile("a").configurationOperation.start = time(12, 20, 7)
         model.getProjectProfile("a").configurationOperation.finish = time(12, 20, 10)
diff --git a/subprojects/cli/src/main/java/org/gradle/cli/SystemPropertiesCommandLineConverter.java b/subprojects/cli/src/main/java/org/gradle/cli/SystemPropertiesCommandLineConverter.java
index a0532e0..b833f20 100644
--- a/subprojects/cli/src/main/java/org/gradle/cli/SystemPropertiesCommandLineConverter.java
+++ b/subprojects/cli/src/main/java/org/gradle/cli/SystemPropertiesCommandLineConverter.java
@@ -31,4 +31,4 @@ protected String getPropertyOptionDetailed() {
     protected String getPropertyOptionDescription() {
         return "Set system property of the JVM (e.g. -Dmyprop=myvalue).";
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/cli/src/test/groovy/org/gradle/cli/AbstractPropertiesCommandLineConverterTest.groovy b/subprojects/cli/src/test/groovy/org/gradle/cli/AbstractPropertiesCommandLineConverterTest.groovy
index f66d0e7..cf3b2e8 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/AbstractPropertiesCommandLineConverterTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/AbstractPropertiesCommandLineConverterTest.groovy
@@ -70,4 +70,4 @@
         expect:
         convert("-Aprop=a b=c", "-Aprop2==", "-Aprop3=ab=") == [prop: 'a b=c', prop2: '=', prop3: 'ab=']
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/cli/src/test/groovy/org/gradle/cli/SystemPropertiesCommandLineConverterTest.groovy b/subprojects/cli/src/test/groovy/org/gradle/cli/SystemPropertiesCommandLineConverterTest.groovy
index 44491a1..7ce672f 100644
--- a/subprojects/cli/src/test/groovy/org/gradle/cli/SystemPropertiesCommandLineConverterTest.groovy
+++ b/subprojects/cli/src/test/groovy/org/gradle/cli/SystemPropertiesCommandLineConverterTest.groovy
@@ -29,4 +29,4 @@
         expect:
         convert("-Da=b", "-Dc=d") == [a: "b", c: "d"]
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/code-quality/build.gradle.kts b/subprojects/code-quality/build.gradle.kts
index e18e257..4c3fbb16 100644
--- a/subprojects/code-quality/build.gradle.kts
+++ b/subprojects/code-quality/build.gradle.kts
@@ -17,6 +17,7 @@
     implementation(project(":reporting"))
     implementation(project(":platform-jvm"))
     implementation(project(":file-collections"))
+    compileOnly(project(":internal-instrumentation-api"))
 
     implementation(libs.groovy)
     implementation(libs.groovyAnt)
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/AntWorkerMemoryLeakIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/AntWorkerMemoryLeakIntegrationTest.groovy
index f1baa06..6a893aa 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/AntWorkerMemoryLeakIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/AntWorkerMemoryLeakIntegrationTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.testing.fixture.GroovyCoverage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.VersionNumber
 import spock.lang.Issue
 
@@ -146,7 +146,7 @@
         return VersionNumber.parse(GroovyCoverage.SUPPORTED_BY_JDK.min()) <= VersionNumber.parse("2.4.7") ? [ "2.4.7" ] : []
     }
 
-    @Requires(TestPrecondition.JDK11_OR_LATER) // grgit 5 requires JDK 11, see https://github.com/ajoberstar/grgit/issues/355
+    @Requires(UnitTestPreconditions.Jdk11OrLater) // grgit 5 requires JDK 11, see https://github.com/ajoberstar/grgit/issues/355
     void "does not fail with a PermGen space error or a missing method exception"() {
         given:
         initGitDir()
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/checkstyle/CheckstylePluginToolchainsIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/checkstyle/CheckstylePluginToolchainsIntegrationTest.groovy
index 3e24451..a66f1ac 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/checkstyle/CheckstylePluginToolchainsIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/checkstyle/CheckstylePluginToolchainsIntegrationTest.groovy
@@ -27,6 +27,7 @@
 import org.hamcrest.Matcher
 import spock.lang.Issue
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
 import static org.gradle.util.Matchers.containsLine
 import static org.hamcrest.CoreMatchers.containsString
 
@@ -174,6 +175,7 @@
         outputContains("Running checkstyle with toolchain '${jdk.javaHome.absolutePath}'.")
         failure.assertHasDescription("Execution failed for task ':checkstyleMain'.")
         failure.assertHasErrorOutput("Name 'class1' must match pattern")
+        failure.assertHasResolutions(SCAN)
         file("build/reports/checkstyle/main.xml").assertContents(containsClass("org.gradle.class1"))
         file("build/reports/checkstyle/main.xml").assertContents(containsClass("org.gradle.class2"))
 
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/checkstyle/CheckstylePluginVersionIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/checkstyle/CheckstylePluginVersionIntegrationTest.groovy
index b5affeb..3809a23 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/checkstyle/CheckstylePluginVersionIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/checkstyle/CheckstylePluginVersionIntegrationTest.groovy
@@ -483,9 +483,9 @@
 
     private goodCode() {
         file('src/main/java/org/gradle/Class1.java') << 'package org.gradle; class Class1 { }'
-        file('src/test/java/org/gradle/TestClass1.java') << 'package org.gradle; class TestClass1 { }'
+        file('src/test/java/org/gradle/TestClass1.java') << 'package org.gradle; public class TestClass1 { @org.junit.Test public void test1() { } }'
         file('src/main/groovy/org/gradle/Class2.java') << 'package org.gradle; class Class2 { }'
-        file('src/test/groovy/org/gradle/TestClass2.java') << 'package org.gradle; class TestClass2 { }'
+        file('src/test/groovy/org/gradle/TestClass2.java') << 'package org.gradle; public class TestClass2 { @org.junit.Test public void test2() { } }'
     }
 
     private badCode() {
@@ -520,6 +520,8 @@
     implementation localGroovy()
 }
 
+testing.suites.test.useJUnit()
+
 checkstyle {
     toolVersion = '$version'
 }
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcCompilationClasspathIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcCompilationClasspathIntegrationTest.groovy
index ff5b06e..f4bce23 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcCompilationClasspathIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcCompilationClasspathIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class CodeNarcCompilationClasspathIntegrationTest extends AbstractIntegrationSpec {
 
@@ -47,7 +47,7 @@
         failure.assertHasCause('CodeNarc rule violations were found')
     }
 
-    @Requires(TestPrecondition.JDK15_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk15OrEarlier)
     def "an informative error is shown when a compilation classpath is specified on a CodeNarc task when using an incompatible CodeNarc version"() {
         given:
         buildFileWithCodeNarcAndCompilationClasspath(UNSUPPORTED_COMPILATION_CLASSPATH_VERSION)
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcGroovyVersionIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcGroovyVersionIntegrationTest.groovy
index f496a50..560b83c 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcGroovyVersionIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcGroovyVersionIntegrationTest.groovy
@@ -41,6 +41,7 @@
                 implementation("${group}:groovy")
             }
 
+            testing.suites.test.useJUnit()
             codenarc.toolVersion = "${codenarcVersion}"
         """.stripIndent()
     }
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcNonJvmIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcNonJvmIntegrationTest.groovy
index db92eeb..34b7bba 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcNonJvmIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcNonJvmIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
 import org.gradle.integtests.fixtures.TargetCoverage
 import org.gradle.quality.integtest.fixtures.CodeNarcCoverage
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testing.fixture.GroovyCoverage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.Issue
 
 @TargetCoverage({ CodeNarcCoverage.supportedVersionsByCurrentJdk })
-@Requires(TestPrecondition.STABLE_GROOVY)
+@Requires(UnitTestPreconditions.StableGroovy)
 class CodeNarcNonJvmIntegrationTest extends MultiVersionIntegrationSpec implements CodeNarcTestFixture {
 
     @Issue("https://github.com/gradle/gradle/issues/23343")
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcPluginIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcPluginIntegrationTest.groovy
index fea1792..740ddd5 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcPluginIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcPluginIntegrationTest.groovy
@@ -47,7 +47,7 @@
                 assert task instanceof CodeNarc
                 task.with {
                     assert description == "Run CodeNarc analysis for ${sourceSet.name} classes"
-                    assert source as List == sourceSet.allGroovy  as List
+                    assert source as List == sourceSet.groovy  as List
                     assert codenarcClasspath == project.configurations.codenarc
                     assert config.inputFiles.singleFile == project.file("config/codenarc/codenarc.xml")
                     assert configFile == project.file("config/codenarc/codenarc.xml")
@@ -111,7 +111,7 @@
                 assert task instanceof CodeNarc
                 task.with {
                     assert description == "Run CodeNarc analysis for ${sourceSet.name} classes"
-                    assert source as List == sourceSet.allGroovy as List
+                    assert source as List == sourceSet.groovy as List
                     assert codenarcClasspath == project.configurations.codenarc
                     assert config.inputFiles.singleFile == project.file("codenarc-config")
                     assert configFile == project.file("codenarc-config")
@@ -191,7 +191,7 @@
     }
 
     private void writeBuildFile() {
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: "groovy"
             apply plugin: "codenarc"
 
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcPluginVersionIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcPluginVersionIntegrationTest.groovy
index a642d05..65d2827 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcPluginVersionIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcPluginVersionIntegrationTest.groovy
@@ -21,17 +21,18 @@
 import org.gradle.integtests.fixtures.TargetCoverage
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.quality.integtest.fixtures.CodeNarcCoverage
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testing.fixture.GroovyCoverage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.gradle.util.internal.ToBeImplemented
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
 import static org.hamcrest.CoreMatchers.startsWith
 
 @TargetCoverage({ CodeNarcCoverage.supportedVersionsByCurrentJdk })
-@Requires(TestPrecondition.STABLE_GROOVY)
+@Requires(UnitTestPreconditions.StableGroovy)
 class CodeNarcPluginVersionIntegrationTest extends MultiVersionIntegrationSpec implements CodeNarcTestFixture {
     def setup() {
         buildFile << """
@@ -48,6 +49,8 @@
                 implementation localGroovy()
             }
 
+            testing.suites.test.useJUnit()
+
             ${JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_14) ?
             """
             configurations.codenarc {
@@ -116,6 +119,7 @@
         fails("check")
         failure.assertHasDescription("Execution failed for task ':codenarcTest'.")
         failure.assertThatCause(startsWith("CodeNarc rule violations were found. See the report at:"))
+        failure.assertHasResolutions(SCAN)
         !report("main").text.contains("Class2")
         report("test").text.contains("testclass2")
     }
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcTestFixture.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcTestFixture.groovy
index 621b65d..c9f8118 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcTestFixture.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/codenarc/CodeNarcTestFixture.groovy
@@ -22,7 +22,7 @@
 trait CodeNarcTestFixture {
     def goodCode() {
         file("src/main/groovy/org/gradle/class1.java") << "package org.gradle; class class1 { }"
-        file("src/test/groovy/org/gradle/testclass1.java") << "package org.gradle; class testclass1 { }"
+        file("src/test/groovy/org/gradle/testclass1.java") << "package org.gradle; public class testclass1 { @org.junit.Test public void test1() { } }"
         file("src/main/groovy/org/gradle/Class2.groovy") << "package org.gradle; class Class2 { }"
         file("src/test/groovy/org/gradle/TestClass2.groovy") << "package org.gradle; class TestClass2 { }"
     }
@@ -30,7 +30,7 @@
     def badCode() {
         file("src/main/groovy/org/gradle/class1.java") << "package org.gradle; class class1 { }"
         file("src/main/groovy/org/gradle/Class2.groovy") << "package org.gradle; class Class2 { }"
-        file("src/test/groovy/org/gradle/TestClass1.java") << "package org.gradle; class TestClass1 { }"
+        file("src/test/groovy/org/gradle/TestClass1.java") << "package org.gradle; public class TestClass1 { @org.junit.Test public void test1() { } }"
         file("src/test/groovy/org/gradle/testclass2.groovy") << "package org.gradle; class testclass2 { }"
     }
 
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/AbstractPmdPluginVersionIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/AbstractPmdPluginVersionIntegrationTest.groovy
index eda511f..887d02d 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/AbstractPmdPluginVersionIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/AbstractPmdPluginVersionIntegrationTest.groovy
@@ -19,7 +19,8 @@
 import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
 import org.gradle.integtests.fixtures.TargetCoverage
 import org.gradle.quality.integtest.fixtures.PmdCoverage
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.VersionNumber
 
 @TargetCoverage({ PmdCoverage.getSupportedVersionsByJdk() })
@@ -37,27 +38,27 @@
      * Checks if the current PMD version supports the current Java version, if not we set the sourceCompatibility to the max Java version supported by the current PMD version.
      */
     String requiredSourceCompatibility() {
-        if (versionNumber < VersionNumber.parse('6.0.0') && TestPrecondition.JDK9_OR_LATER.isFulfilled()) {
-            "sourceCompatibility = 1.8"
-        } else if (versionNumber < VersionNumber.parse('6.4.0') && TestPrecondition.JDK10_OR_LATER.isFulfilled()) {
-            "sourceCompatibility = 9"
-        } else if (versionNumber < VersionNumber.parse('6.6.0') && TestPrecondition.JDK11_OR_LATER.isFulfilled()) {
-            "sourceCompatibility = 10"
-        } else if (versionNumber < VersionNumber.parse('6.13.0') && TestPrecondition.JDK12_OR_LATER.isFulfilled()) {
-            "sourceCompatibility = 11"
-        } else if (versionNumber < VersionNumber.parse('6.18.0') && TestPrecondition.JDK13_OR_LATER.isFulfilled()) {
-            "sourceCompatibility = 12"
-        } else if (versionNumber < VersionNumber.parse('6.22.0') && TestPrecondition.JDK14_OR_LATER.isFulfilled()) {
-            "sourceCompatibility = 13"
-        } else if (versionNumber < VersionNumber.parse('6.48.0') && TestPrecondition.JDK19_OR_LATER.isFulfilled()) {
-            "sourceCompatibility = 18"
+        if (versionNumber < VersionNumber.parse('6.0.0') && TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk9OrLater)) {
+            "java.sourceCompatibility = 1.8"
+        } else if (versionNumber < VersionNumber.parse('6.4.0') && TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk10OrLater)) {
+            "java.sourceCompatibility = 9"
+        } else if (versionNumber < VersionNumber.parse('6.6.0') && TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk11OrLater)) {
+            "java.sourceCompatibility = 10"
+        } else if (versionNumber < VersionNumber.parse('6.13.0') && TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk12OrLater)) {
+            "java.sourceCompatibility = 11"
+        } else if (versionNumber < VersionNumber.parse('6.18.0') && TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk13OrLater)) {
+            "java.sourceCompatibility = 12"
+        } else if (versionNumber < VersionNumber.parse('6.22.0') && TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk14OrLater)) {
+            "java.sourceCompatibility = 13"
+        } else if (versionNumber < VersionNumber.parse('6.48.0') && TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk19OrLater)) {
+            "java.sourceCompatibility = 18"
         } else {
             "" // do not set a source compatibility for the DEFAULT_PMD_VERSION running on latest Java, this way we will catch if it breaks with a new Java version
         }
     }
 
     static boolean fileLockingIssuesSolved() {
-        return !TestPrecondition.WINDOWS.fulfilled || VersionNumber.parse("5.5.1") <= versionNumber
+        return TestPrecondition.doSatisfies(UnitTestPreconditions.Windows) || VersionNumber.parse("5.5.1") <= versionNumber
     }
 
     static boolean supportIncrementalAnalysis() {
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdPluginDependenciesIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdPluginDependenciesIntegrationTest.groovy
index f266315..fe0ba32 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdPluginDependenciesIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdPluginDependenciesIntegrationTest.groovy
@@ -22,11 +22,14 @@
 
     def setup() {
         buildFile << """
-            apply plugin: "java"
-            apply plugin: "pmd"
+            plugins {
+                id 'java-library'
+                id 'pmd'
+            }
 
             ${mavenCentralRepository()}
 
+            testing.suites.test.useJUnit()
             tasks.withType(Pmd) {
                 // clear the classpath to avoid file locking issues on PMD version < 5.5.1
                 classpath = files()
@@ -97,6 +100,22 @@
         // PMD Lvl 2 Warning BooleanInstantiation
         // PMD Lvl 3 Warning OverrideBothEqualsAndHashcode
         file("src/test/java/org/gradle/Class1Test.java") <<
-            "package org.gradle; class Class1Test<T> { public boolean equals(Object arg) { return java.lang.Boolean.valueOf(true); } }"
+            """
+            package org.gradle;
+
+            import static org.junit.Assert.assertTrue;
+
+            import org.junit.Test;
+
+            public class Class1Test<T> {
+                @Test
+                public void testFoo() {
+                    Class1 c = new Class1();
+                    assertTrue(c.isFoo("foo"));
+                }
+
+                public boolean equals(Object arg) { return java.lang.Boolean.valueOf(true); }
+            }
+            """
     }
 }
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdPluginVersionIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdPluginVersionIntegrationTest.groovy
index f2ce7a2..42a20b7 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdPluginVersionIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdPluginVersionIntegrationTest.groovy
@@ -20,6 +20,7 @@
 import org.hamcrest.Matcher
 import spock.lang.Issue
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
 import static org.gradle.util.Matchers.containsLine
 import static org.hamcrest.CoreMatchers.containsString
 import static org.hamcrest.CoreMatchers.not
@@ -36,8 +37,12 @@
 
             ${mavenCentralRepository()}
 
+            testing.suites.test.useJUnit()
+
             pmd {
                 toolVersion = '$version'
+                // Set target JDK for PMD <5.x tests, so the test code is accepted
+                targetJdk = TargetJdk.VERSION_1_7
                 ${supportIncrementalAnalysis() ? "" : "incrementalAnalysis = false"}
             }
 
@@ -67,6 +72,7 @@
         fails("check")
         failure.assertHasDescription("Execution failed for task ':pmdTest'.")
         failure.assertThatCause(containsString("2 PMD rule violations were found. See the report at:"))
+        failure.assertHasResolutions(SCAN)
         file("build/reports/pmd/main.xml").assertContents(not(containsClass("org.gradle.Class1")))
         file("build/reports/pmd/test.xml").assertContents(containsClass("org.gradle.Class1Test"))
     }
@@ -349,7 +355,21 @@
         file("src/main/java/org/gradle/Class1.java") <<
             "package org.gradle; class Class1 { public boolean isFoo(Object arg) { return true; } }"
         file("src/test/java/org/gradle/Class1Test.java") <<
-            "package org.gradle; class Class1Test { public boolean isFoo(Object arg) { return true; } }"
+            """
+            package org.gradle;
+
+            import static org.junit.Assert.assertTrue;
+
+            import org.junit.Test;
+
+            public class Class1Test {
+                @Test
+                public void testFoo() {
+                    Class1 c = new Class1();
+                    assertTrue(c.isFoo("foo"));
+                }
+            }
+            """
     }
 
     private badCode() {
@@ -359,7 +379,23 @@
         // PMD Lvl 2 Warning AvoidMultipleUnaryOperators
         // PMD Lvl 3 Warning OverrideBothEqualsAndHashcode
         file("src/test/java/org/gradle/Class1Test.java") <<
-            "package org.gradle; class Class1Test { public boolean equals(Object arg) { return !!true; } }"
+            """
+            package org.gradle;
+
+            import static org.junit.Assert.assertTrue;
+
+            import org.junit.Test;
+
+            public class Class1Test {
+                @Test
+                public void testFoo() {
+                    Class1 c = new Class1();
+                    assertTrue(c.isFoo("foo"));
+                }
+
+                public boolean equals(Object arg) { return !!true; }
+            }
+            """
     }
 
     private customCode() {
diff --git a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdRelocationIntegrationTest.groovy b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdRelocationIntegrationTest.groovy
index 9ac5037..0b4d1e7 100644
--- a/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdRelocationIntegrationTest.groovy
+++ b/subprojects/code-quality/src/integTest/groovy/org/gradle/api/plugins/quality/pmd/PmdRelocationIntegrationTest.groovy
@@ -31,7 +31,22 @@
         projectDir.file("src/main/java/org/gradle/Class1.java") <<
             "package org.gradle; class Class1 { public boolean is() { return true; } }"
         projectDir.file("src/main/java/org/gradle/Class1Test.java") <<
-            "package org.gradle; class Class1Test { public boolean is() { return true; } }"
+            """
+            package org.gradle;
+
+            import static org.junit.Assert.assertTrue;
+
+            import org.junit.Test;
+
+            public class Class1Test {
+                @Test
+                public void testFoo() {
+                    Class1 c = new Class1();
+                    assertTrue(c.isFoo("foo"));
+                }
+            }
+            """
+
 
         projectDir.file("build.gradle") << """
             apply plugin: "java"
@@ -41,7 +56,6 @@
 
             task pmd(type: Pmd) {
                 source "src/main/java"
-                ignoreFailures = true
             }
         """
     }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcPlugin.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcPlugin.java
index ebd0e5d..2833405 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcPlugin.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/CodeNarcPlugin.java
@@ -48,8 +48,8 @@
 public abstract class CodeNarcPlugin extends AbstractCodeQualityPlugin<CodeNarc> {
 
     public static final String DEFAULT_CODENARC_VERSION = appropriateCodeNarcVersion();
-    static final String STABLE_VERSION = "3.1.0";
-    static final String STABLE_VERSION_WITH_GROOVY4_SUPPORT = "3.1.0-groovy-4.0";
+    static final String STABLE_VERSION = "3.2.0";
+    static final String STABLE_VERSION_WITH_GROOVY4_SUPPORT = "3.2.0-groovy-4.0";
     private CodeNarcExtension extension;
 
     @Override
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.java
index 786fe14..49e493c 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.java
@@ -245,7 +245,7 @@ public void setPmdClasspath(FileCollection pmdClasspath) {
     }
 
     /**
-     * The built-in rule sets to be used. See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_rules_java.html">official list</a> of built-in rule sets.
+     * The built-in rule sets to be used. See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_rules_java.html">official list</a> of built-in rule sets.
      *
      * <pre>
      *     ruleSets = ["basic", "braces"]
@@ -257,7 +257,7 @@ public List<String> getRuleSets() {
     }
 
     /**
-     * The built-in rule sets to be used. See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_rules_java.html">official list</a> of built-in rule sets.
+     * The built-in rule sets to be used. See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_rules_java.html">official list</a> of built-in rule sets.
      *
      * <pre>
      *     ruleSets = ["basic", "braces"]
@@ -285,7 +285,7 @@ public void setTargetJdk(TargetJdk targetJdk) {
     /**
      * The custom rule set to be used (if any). Replaces {@code ruleSetFiles}, except that it does not currently support multiple rule sets.
      *
-     * See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set.
+     * See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set.
      *
      * <pre>
      *     ruleSetConfig = resources.text.fromFile(resources.file("config/pmd/myRuleSets.xml"))
@@ -303,7 +303,7 @@ public TextResource getRuleSetConfig() {
     /**
      * The custom rule set to be used (if any). Replaces {@code ruleSetFiles}, except that it does not currently support multiple rule sets.
      *
-     * See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set.
+     * See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set.
      *
      * <pre>
      *     ruleSetConfig = resources.text.fromFile(resources.file("config/pmd/myRuleSets.xml"))
@@ -316,7 +316,7 @@ public void setRuleSetConfig(@Nullable TextResource ruleSetConfig) {
     }
 
     /**
-     * The custom rule set files to be used. See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set file.
+     * The custom rule set files to be used. See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set file.
      * If you want to only use custom rule sets, you must clear {@code ruleSets}.
      *
      * <pre>
@@ -330,7 +330,7 @@ public FileCollection getRuleSetFiles() {
     }
 
     /**
-     * The custom rule set files to be used. See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set file.
+     * The custom rule set files to be used. See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set file.
      * This adds to the default rule sets defined by {@link #getRuleSets()}.
      *
      * <pre>
@@ -451,7 +451,7 @@ public void setClasspath(@Nullable FileCollection classpath) {
     /**
      * Controls whether to use incremental analysis or not.
      *
-     * This is only supported for PMD 6.0.0 or better. See <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_incremental_analysis.html"></a> for more details.
+     * This is only supported for PMD 6.0.0 or better. See <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_incremental_analysis.html"></a> for more details.
      *
      * @since 5.6
      */
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.java
index c9a26a4..19f6450 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.java
@@ -56,7 +56,7 @@ public PmdExtension(Project project) {
     }
 
     /**
-     * The built-in rule sets to be used. See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_rules_java.html">official list</a> of built-in rule sets.
+     * The built-in rule sets to be used. See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_rules_java.html">official list</a> of built-in rule sets.
      *
      * If not configured explicitly, the returned conventional value is "category/java/errorprone.xml", unless {@link #getRuleSetConfig()} returns.
      * a non-null value or the return value of {@link #getRuleSetFiles()} is non-empty, in which case the conventional value is an empty list
@@ -70,7 +70,7 @@ public List<String> getRuleSets() {
     }
 
     /**
-     * The built-in rule sets to be used. See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_rules_java.html">official list</a> of built-in rule sets.
+     * The built-in rule sets to be used. See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_rules_java.html">official list</a> of built-in rule sets.
      *
      * <pre>
      *     ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]
@@ -135,7 +135,7 @@ public void setTargetJdk(Object value) {
      *
      * This is equivalent to PMD's Ant task minimumPriority property.
      *
-     * See the official documentation for the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_configuring_rules.html">list of priorities</a>.
+     * See the official documentation for the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_configuring_rules.html">list of priorities</a>.
      *
      * <pre>
      *     rulesMinimumPriority = 3
@@ -150,7 +150,7 @@ public Property<Integer> getRulesMinimumPriority() {
     /**
      * The custom rule set to be used (if any). Replaces {@code ruleSetFiles}, except that it does not currently support multiple rule sets.
      *
-     * See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set.
+     * See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set.
      *
      * <pre>
      *     ruleSetConfig = resources.text.fromFile("config/pmd/myRuleSet.xml")
@@ -166,7 +166,7 @@ public TextResource getRuleSetConfig() {
     /**
      * The custom rule set to be used (if any). Replaces {@code ruleSetFiles}, except that it does not currently support multiple rule sets.
      *
-     * See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set.
+     * See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set.
      *
      * <pre>
      *     ruleSetConfig = resources.text.fromFile("config/pmd/myRuleSet.xml")
@@ -179,7 +179,7 @@ public void setRuleSetConfig(@Nullable TextResource ruleSetConfig) {
     }
 
     /**
-     * The custom rule set files to be used. See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set file.
+     * The custom rule set files to be used. See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set file.
      *
      * <pre>
      *     ruleSetFiles = files("config/pmd/myRuleSet.xml")
@@ -190,7 +190,7 @@ public FileCollection getRuleSetFiles() {
     }
 
     /**
-     * The custom rule set files to be used. See the <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set file.
+     * The custom rule set files to be used. See the <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_making_rulesets.html">official documentation</a> for how to author a rule set file.
      * This adds to the default rule sets defined by {@link #getRuleSets()}.
      *
      * <pre>
@@ -231,7 +231,7 @@ public void setConsoleOutput(boolean consoleOutput) {
     /**
      * Controls whether to use incremental analysis or not.
      *
-     * This is only supported for PMD 6.0.0 or better. See <a href="https://pmd.github.io/pmd-6.39.0/pmd_userdocs_incremental_analysis.html"></a> for more details.
+     * This is only supported for PMD 6.0.0 or better. See <a href="https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_userdocs_incremental_analysis.html"></a> for more details.
      *
      * @since 5.6
      */
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java
index d0298f4..3ec26da 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java
@@ -17,11 +17,13 @@
 
 import org.gradle.api.JavaVersion;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.ProjectLayout;
 import org.gradle.api.file.RegularFile;
 import org.gradle.api.internal.ConventionMapping;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.plugins.jvm.internal.JvmPluginServices;
 import org.gradle.api.plugins.quality.internal.AbstractCodeQualityPlugin;
 import org.gradle.api.provider.Provider;
@@ -59,7 +61,8 @@
  */
 public abstract class PmdPlugin extends AbstractCodeQualityPlugin<Pmd> {
 
-    public static final String DEFAULT_PMD_VERSION = "6.48.0";
+    // When updating DEFAULT_PMD_VERSION, also update links in Pmd and PmdExtension!
+    public static final String DEFAULT_PMD_VERSION = "6.55.0";
     private static final String PMD_ADDITIONAL_AUX_DEPS_CONFIGURATION = "pmdAux";
 
     private PmdExtension extension;
@@ -113,10 +116,8 @@ public TargetJdk getDefaultTargetJdk(JavaVersion javaVersion) {
     @Override
     protected void createConfigurations() {
         super.createConfigurations();
-        project.getConfigurations().create(PMD_ADDITIONAL_AUX_DEPS_CONFIGURATION, additionalAuxDepsConfiguration -> {
+        project.getConfigurations().createWithRole(PMD_ADDITIONAL_AUX_DEPS_CONFIGURATION, ConfigurationRoles.BUCKET, additionalAuxDepsConfiguration -> {
             additionalAuxDepsConfiguration.setDescription("The additional libraries that are available for type resolution during analysis");
-            additionalAuxDepsConfiguration.setCanBeResolved(false);
-            additionalAuxDepsConfiguration.setCanBeConsumed(false);
             additionalAuxDepsConfiguration.setVisible(false);
         });
     }
@@ -205,15 +206,14 @@ protected void configureForSourceSet(final SourceSet sourceSet, final Pmd task)
         task.setDescription("Run PMD analysis for " + sourceSet.getName() + " classes");
         task.setSource(sourceSet.getAllJava());
         ConventionMapping taskMapping = task.getConventionMapping();
-        ConfigurationContainer configurations = project.getConfigurations();
+        RoleBasedConfigurationContainerInternal configurations = project.getConfigurations();
 
         Configuration compileClasspath = configurations.getByName(sourceSet.getCompileClasspathConfigurationName());
         Configuration pmdAdditionalAuxDepsConfiguration = configurations.getByName(PMD_ADDITIONAL_AUX_DEPS_CONFIGURATION);
 
         // TODO: Consider checking if the resolution consistency is enabled for compile/runtime.
-        Configuration pmdAuxClasspath = configurations.create(sourceSet.getName() + "PmdAuxClasspath");
+        @SuppressWarnings("deprecation") Configuration pmdAuxClasspath = configurations.createWithRole(sourceSet.getName() + "PmdAuxClasspath", ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE);
         pmdAuxClasspath.extendsFrom(compileClasspath, pmdAdditionalAuxDepsConfiguration);
-        pmdAuxClasspath.setCanBeConsumed(false);
         pmdAuxClasspath.setVisible(false);
         // This is important to get transitive implementation dependencies. PMD may load referenced classes for analysis so it expects the classpath to be "closed" world.
         getJvmPluginServices().configureAsRuntimeClasspath(pmdAuxClasspath);
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AbstractCodeQualityPlugin.java b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AbstractCodeQualityPlugin.java
index c7f56e5..887b3ee 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AbstractCodeQualityPlugin.java
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/AbstractCodeQualityPlugin.java
@@ -96,12 +96,10 @@ protected void beforeApply() {
     }
 
     protected void createConfigurations() {
-        Configuration configuration = project.getConfigurations().create(getConfigurationName());
+        Configuration configuration = project.getConfigurations().resolvableBucket(getConfigurationName());
         configuration.setVisible(false);
         configuration.setTransitive(true);
         configuration.setDescription("The " + getToolName() + " libraries to be used for this project.");
-        configuration.setCanBeResolved(true);
-        configuration.setCanBeConsumed(false);
 
         // Don't need these things, they're provided by the runtime
         configuration.exclude(excludeProperties("ant", "ant"));
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/CheckstyleInvoker.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/CheckstyleInvoker.groovy
index f17690b..2d0a5ad 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/CheckstyleInvoker.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/CheckstyleInvoker.groovy
@@ -21,6 +21,7 @@
 import org.gradle.api.GradleException
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.exceptions.MarkedVerificationException
 import org.gradle.api.internal.project.antbuilder.AntBuilderDelegate
 import org.gradle.api.plugins.quality.Checkstyle
 import org.gradle.internal.logging.ConsoleRenderer
@@ -129,11 +130,13 @@
         }
 
         def reportXml = parseCheckstyleXml(isXmlRequired, xmlOutputLocation)
+
+        def message = getMessage(isXmlRequired, xmlOutputLocation, isHtmlRequired, htmlOutputLocation, isSarifRequired, sarifOutputLocation, reportXml)
         if (ant.project.properties[FAILURE_PROPERTY_NAME] && !ignoreFailures) {
-            throw new GradleException(getMessage(isXmlRequired, xmlOutputLocation, isHtmlRequired, htmlOutputLocation, isSarifRequired, sarifOutputLocation, reportXml))
+            throw new MarkedVerificationException(message)
         } else {
             if (violationsExist(reportXml)) {
-                LOGGER.warn(getMessage(isXmlRequired, xmlOutputLocation, isHtmlRequired, htmlOutputLocation, isSarifRequired, sarifOutputLocation, reportXml))
+                LOGGER.warn(message)
             }
         }
     }
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/CodeNarcInvoker.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/CodeNarcInvoker.groovy
index efdd7e6..f799e4a 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/CodeNarcInvoker.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/CodeNarcInvoker.groovy
@@ -20,6 +20,7 @@
 import org.gradle.api.Action
 import org.gradle.api.GradleException
 import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.exceptions.MarkedVerificationException
 import org.gradle.api.internal.project.ant.AntLoggingAdapter
 import org.gradle.api.internal.project.antbuilder.AntBuilderDelegate
 import org.gradle.internal.logging.ConsoleRenderer
@@ -82,7 +83,7 @@
                     LOGGER.warn(message)
                     return
                 }
-                throw new GradleException(message, e)
+                throw new MarkedVerificationException(message, e)
             }
             if (e.message == /codenarc doesn't support the nested "classpath" element./) {
                 def message = "The compilationClasspath property of CodeNarc task can only be non-empty when using CodeNarc 0.27.0 or newer."
diff --git a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/PmdInvoker.groovy b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/PmdInvoker.groovy
index 7a16183..a1d2657 100644
--- a/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/PmdInvoker.groovy
+++ b/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/internal/PmdInvoker.groovy
@@ -19,6 +19,7 @@
 import org.gradle.api.Action
 import org.gradle.api.GradleException
 import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.exceptions.MarkedVerificationException
 import org.gradle.api.internal.project.antbuilder.AntBuilderDelegate
 import org.gradle.api.specs.Spec
 import org.gradle.internal.Cast
@@ -157,7 +158,7 @@
             if (parameters.ignoreFailures.get() || ((failureCount as Integer) <= maxFailures)) {
                 LOGGER.warn(message)
             } else {
-                throw new GradleException(message)
+                throw new MarkedVerificationException(message)
             }
         }
     }
diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildPathAssignmentIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildPathAssignmentIntegrationTest.groovy
index 72833d1..7dc05f2 100644
--- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildPathAssignmentIntegrationTest.groovy
+++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildPathAssignmentIntegrationTest.groovy
@@ -20,6 +20,7 @@
 import org.gradle.integtests.fixtures.BuildOperationsFixture
 import org.gradle.integtests.fixtures.build.BuildTestFile
 import org.gradle.integtests.fixtures.build.BuildTestFixture
+import org.gradle.internal.build.BuildStateRegistry
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 
 class CompositeBuildBuildPathAssignmentIntegrationTest extends AbstractCompositeBuildIntegrationTest {
@@ -39,6 +40,7 @@
         def includingBuild = builds.find { it.rootProjectName == 'includingBuild' }
 
         when:
+        executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
         succeeds(includingBuild, "help")
         then:
         assignedBuildPaths ==~ [
@@ -47,6 +49,13 @@
             ':',
             ':buildLogic'
         ]
+
+        extractBuildIdentifierOutput(output) ==~ [
+            [path: ':', name: ':'],
+            [path: ':includedBuild', name: 'includedBuild'],
+            [path: ':includedBuild:buildLogic', name: 'buildLogic'],
+            [path: ':buildLogic', name: 'buildLogic']
+        ]
     }
 
     def "build paths are selected based on the directory hierarchy"() {
@@ -70,6 +79,7 @@
         def includingBuild = builds.find { it.rootProjectName == 'includingBuild' }
 
         when:
+        executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
         succeeds(includingBuild, 'help')
         then:
         assignedBuildPaths ==~ [
@@ -82,6 +92,17 @@
             ':nested',
             ':nested:buildLogic'
         ]
+
+        extractBuildIdentifierOutput(output) ==~ [
+            [path: ':', name: ':'],
+            [path: ':buildLogic', name: 'buildLogic'],
+            [path: ':includedBuild', name: 'includedBuild'],
+            [path: ':includedBuild:buildLogic', name: 'buildLogic'],
+            [path: ':includedBuild:nested', name: 'nested'],
+            [path: ':includedBuild:nested:buildLogic', name: 'buildLogic'],
+            [path: ':nested', name: 'nested'],
+            [path: ':nested:buildLogic', name: 'buildLogic']
+        ]
     }
 
     def "buildSrc is relative to its including build"() {
@@ -104,11 +125,24 @@
         }
 
         when:
+        executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
         succeeds(includingBuild, 'help')
         then:
         assignedBuildPaths ==~ [
             ':includedBuild', ':includedBuild:buildSrc', ':includedBuild:nested', ':includedBuild:nested:buildSrc',
             ':', ':buildSrc', ':nested', ':nested:buildSrc', ':nested:buildSrc:buildSrc']
+
+        extractBuildIdentifierOutput(output) ==~ [
+            [path: ':', name: ':'],
+            [path: ':buildSrc', name: 'buildSrc'],
+            [path: ':includedBuild', name: 'includedBuild'],
+            [path: ':includedBuild:buildSrc', name: 'buildSrc'],
+            [path: ':includedBuild:nested', name: 'nested'],
+            [path: ':includedBuild:nested:buildSrc', name: 'buildSrc'],
+            [path: ':nested', name: 'nested'],
+            [path: ':nested:buildSrc', name: 'buildSrc'],
+            [path: ':nested:buildSrc:buildSrc', name: 'buildSrc']
+        ]
     }
 
     def "uses the directory hierarchy to determine the build path when the builds are not nested"() {
@@ -126,12 +160,21 @@
         }
         def includingBuild = builds.find { it.rootProjectName == 'includingBuild' }
         def includedBuildBNested = createDir('includedBuildB/nested')
-        new BuildTestFixture(includedBuildBNested).multiProjectBuild('includedBuildB', ['project1', 'project2'])
+        new BuildTestFixture(includedBuildBNested).multiProjectBuild('includedBuildB-nested', ['project1', 'project2'])
 
         when:
+        executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
         succeeds(includingBuild, 'help')
         then:
         assignedBuildPaths ==~ [':', ':includedBuildB', ':includedBuildA', ':nested', ':includedBuildB:nested']
+
+        extractBuildIdentifierOutput(output) ==~ [
+            [path: ':', name: ':'],
+            [path: ':includedBuildA', name: 'includedBuildA'],
+            [path: ':includedBuildB', name: 'includedBuildB'],
+            [path: ':includedBuildB:nested', name: 'nested'],
+            [path: ':nested', name: 'nested']
+        ]
     }
 
     def "does not resolve the path conflict when the parent build is included #laterDescription"() {
@@ -182,14 +225,34 @@
         fixture.progress(BuildIdentifiedProgressDetails).findAll { it.details.buildPath }*.details*.buildPath
     }
 
-    private void configureBuildConfigurationOutput(List<BuildTestFile> builds) {
+    private void configureNestedBuild(List<BuildTestFile> builds) {
         builds.each {
-            it.buildFile << """
-                    subprojects {
-                        // The Java plugin forces the configuration of the included builds
-                        apply plugin: 'java'
+            it.settingsFile << """
+                gradle.projectsEvaluated {
+                    // only print from the root build of the build tree to avoid duplication
+                    if (gradle.owner.identityPath.path == ':') {
+                        def registry = gradle.services.get(${BuildStateRegistry.name})
+                        registry.visitBuilds { b ->
+                            def buildId = b.buildIdentifier
+                            println "Build path=" + buildId.buildPath + " name=" + buildId.name
+                        }
                     }
-                """
+                }
+            """
+            it.buildFile << """
+                subprojects {
+                    // The Java plugin forces the configuration of the included builds
+                    apply plugin: 'java'
+                }
+            """
+        }
+    }
+
+    private extractBuildIdentifierOutput(String output) {
+        output.readLines().findAll { it.startsWith("Build path=") }.collect { line ->
+            def matcher = line =~ /Build path=(.+) name=(.+)/
+            assert matcher.matches()
+            [path: matcher.group(1), name: matcher.group(2)]
         }
     }
 
@@ -197,7 +260,7 @@
         def rootSpec = new RootNestedBuildSpec(temporaryFolder)
         rootSpec.with(configuration)
         def builds = rootSpec.nestedBuilds
-        configureBuildConfigurationOutput(builds)
+        configureNestedBuild(builds)
         builds
     }
 
diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildSrcIdentityIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildSrcIdentityIntegrationTest.groovy
index fce4e3b..587a9e4 100644
--- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildSrcIdentityIntegrationTest.groovy
+++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildBuildSrcIdentityIntegrationTest.groovy
@@ -28,7 +28,7 @@
                 allprojects {
                     apply plugin: 'java'
                 }
-"""
+            """
         }
         includedBuilds << buildB
     }
@@ -150,11 +150,12 @@
             classes.doLast {
                 def components = configurations.compileClasspath.incoming.resolutionResult.allComponents.id
                 assert components.size() == 2
-                // TODO - should encode 'buildB' somewhere
+                assert components[0].build.buildPath == ':buildB:buildSrc'
                 assert components[0].build.name == 'buildSrc'
                 assert components[0].build.currentBuild
                 assert components[0].projectPath == ':'
-                assert components[0].projectName == 'buildSrc'
+                assert components[0].projectName == '$rootProjectName'
+                assert components[1].build.buildPath == ':buildB:buildSrc'
                 assert components[1].build.name == 'buildSrc'
                 assert components[1].build.currentBuild
                 assert components[1].projectPath == ':a'
@@ -163,18 +164,25 @@
                 def selectors = configurations.runtimeClasspath.incoming.resolutionResult.allDependencies.requested
                 assert selectors.size() == 1
                 assert selectors[0].displayName == 'project :buildB:buildSrc:a'
+                assert selectors[0].buildPath == ':buildB:buildSrc'
                 assert selectors[0].buildName == 'buildSrc'
                 assert selectors[0].projectPath == ':a'
             }
         """
 
+        2.times {
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.isCurrentBuild() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+        }
+        executer.expectDocumentedDeprecationWarning("The ProjectComponentSelector.getBuildName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+
         expect:
         execute(buildA, ":assemble")
 
         where:
-        settings                     | display
-        ""                           | "default root project name"
-        "rootProject.name='someLib'" | "configured root project name"
+        settings                     | rootProjectName | display
+        ""                           | "buildSrc"      | "default root project name"
+        "rootProject.name='someLib'" | "someLib"       | "configured root project name"
     }
 }
 
diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy
index 4b8a8fa..3d8f32e 100644
--- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy
+++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy
@@ -107,6 +107,7 @@
                 project(':b', 'test:b:') {
                     artifact(name: 'b-transitive')
                     edge('com.acme.external:external:1.0', ':includedBuild', 'com.acme.external:external:2.0-SNAPSHOT') {
+                        compositeSubstitute()
                         artifact(name: 'c-foo', fileName: 'c-foo.jar')
                     }
                 }
@@ -124,6 +125,7 @@
                 project(':b', 'test:b:') {
                     artifact(name: 'b-transitive')
                     edge('com.acme.external:external:1.0', ':includedBuild', 'com.acme.external:external:2.0-SNAPSHOT') {
+                        compositeSubstitute()
                         artifact(name: 'c-bar', fileName: 'c-bar.jar')
                     }
                 }
@@ -216,6 +218,7 @@
                     artifact(name: 'b-transitive')
                     module('com.acme.external:external:1.2') {
                         edge('com.acme.external:c:0.1', ':includedBuild', 'com.acme.external:c:2.0-SNAPSHOT') {
+                            compositeSubstitute()
                             artifact(name: 'c-foo', fileName: 'c-foo.jar')
                         }
                     }
@@ -235,6 +238,7 @@
                     artifact(name: 'b-transitive')
                     module('com.acme.external:external:1.2') {
                         edge('com.acme.external:c:0.1', ':includedBuild', 'com.acme.external:c:2.0-SNAPSHOT') {
+                            compositeSubstitute()
                             artifact(name: 'c-bar', fileName: 'c-bar.jar')
                         }
                     }
@@ -324,6 +328,7 @@
                     artifact(name: 'b-transitive')
                     module('com.acme.external:external:1.2') {
                         edge('com.acme.external:c:0.1', ':includedBuild', 'com.acme.external:c:2.0-SNAPSHOT') {
+                            compositeSubstitute()
                             artifact(name: 'c-foo', fileName: 'c-foo.jar')
                         }
                     }
@@ -347,6 +352,7 @@
                     artifact(name: 'b-transitive')
                     module('com.acme.external:external:1.2') {
                         edge('com.acme.external:c:0.1', ':includedBuild', 'com.acme.external:c:2.0-SNAPSHOT') {
+                            compositeSubstitute()
                             artifact(name: 'c-bar', fileName: 'c-bar.jar')
                         }
                     }
@@ -438,6 +444,7 @@
                 project(':b', 'test:b:') {
                     artifact(name: 'b-transitive')
                     edge('com.acme.external:external:1.0', ':includedBuild', 'com.acme.external:external:2.0-SNAPSHOT') {
+                        compositeSubstitute()
                         artifact(name: 'c-foo', fileName: 'c-foo.jar')
                     }
                 }
@@ -455,6 +462,7 @@
                 project(':b', 'test:b:') {
                     artifact(name: 'b-transitive')
                     edge('com.acme.external:external:1.0', ':includedBuild', 'com.acme.external:external:2.0-SNAPSHOT') {
+                        compositeSubstitute()
                         artifact(name: 'c-bar', fileName: 'c-bar.jar')
                     }
                 }
@@ -567,6 +575,7 @@
                 project(':b', 'test:b:') {
                     artifact(name: 'b-transitive')
                     edge('com.acme.external:external:1.0', ':includedBuild', 'com.acme.external:external:2.0-SNAPSHOT') {
+                        compositeSubstitute()
                         artifact(name: 'c-foo', fileName: 'c-foo.jar')
                     }
                 }
@@ -584,6 +593,7 @@
                 project(':b', 'test:b:') {
                     artifact(name: 'b-transitive')
                     edge('com.acme.external:external:1.0', ':includedBuild', 'com.acme.external:external:2.0-SNAPSHOT') {
+                        compositeSubstitute()
                         artifact(name: 'c-bar', fileName: 'c-bar.jar')
                     }
                 }
@@ -802,6 +812,7 @@
                 project(':b', 'test:b:') {
                     artifact(name: 'b-transitive')
                     edge('com.acme.external:external:1.0', ':includedBuild', 'com.acme.external:external:2.0-SNAPSHOT') {
+                        compositeSubstitute()
                         artifact(name: 'c-foo', fileName: 'c-foo.jar')
                     }
                 }
@@ -819,6 +830,7 @@
                 project(':b', 'test:b:') {
                     artifact(name: 'b-transitive')
                     edge('com.acme.external:external:1.0', ':includedBuild', 'com.acme.external:external:2.0-SNAPSHOT') {
+                        compositeSubstitute()
                         artifact(name: 'c-bar', fileName: 'c-bar.jar')
                     }
                 }
diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildDependencyGraphIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildDependencyGraphIntegrationTest.groovy
index 91108ae..dedca39 100644
--- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildDependencyGraphIntegrationTest.groovy
+++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildDependencyGraphIntegrationTest.groovy
@@ -21,6 +21,12 @@
 import org.gradle.integtests.fixtures.build.BuildTestFile
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 /**
  * Tests for resolving dependency graph with substitution within a composite build.
  */
@@ -395,6 +401,7 @@
         checkGraph {
             module("org.external:external-dep:1.0") {
                 edge("org.test:buildB:1.0", ":buildB", "org.test:buildB:2.0") {
+                    forced()
                     compositeSubstitute()
                 }
             }
@@ -431,9 +438,11 @@
         checkGraph {
             module("org.external:external-dep:1.0") {
                 edge("org.test:something:1.0", ":buildB", "org.test:buildB:2.0") {
+                    selectedByRule()
                     compositeSubstitute()
                 }
                 edge("org.other:something-else:1.0", ":buildB:b1", "org.test:b1:2.0") {
+                    selectedByRule()
                     compositeSubstitute()
                 }
             }
@@ -690,6 +699,7 @@
             "  - None of the consumable configurations have attributes.")
     }
 
+    public static final REPOSITORY_HINT = repositoryHint("Maven POM")
     @ToBeFixedForConfigurationCache(because = "different error reporting")
     def "includes build identifier in error message on failure to resolve dependencies of included build"() {
         def m = mavenRepo.module("org.test", "test", "1.2")
@@ -739,9 +749,14 @@
         failure.assertHasCause("""Could not find org.test:test:1.2.
 Searched in the following locations:
   - ${m.pom.file.toURL()}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :buildC""")
+        failure.assertHasResolutions(REPOSITORY_HINT,
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
+
 
         when:
         m.publish()
diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildIdentityIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildIdentityIntegrationTest.groovy
index 7f04a7b..a1bf39b 100644
--- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildIdentityIntegrationTest.groovy
+++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildIdentityIntegrationTest.groovy
@@ -27,7 +27,7 @@
                 allprojects {
                     apply plugin: 'java'
                 }
-"""
+            """
         }
         includedBuilds << buildB
     }
@@ -140,6 +140,7 @@
             def rootProvider = runtimeClasspath.incoming.resolutionResult.rootComponent
             classes.doLast {
                 def rootComponent = rootProvider.get()
+                assert rootComponent.id.build.buildPath == ':'
                 assert rootComponent.id.build.name == ':'
                 assert rootComponent.id.build.currentBuild
                 assert rootComponent.id.projectPath == ':'
@@ -149,14 +150,16 @@
                 assert components.size() == 1
                 def buildRootProject = components[0]
                 def componentId = components[0].id
+                assert componentId.build.buildPath == ':${buildName}'
                 assert componentId.build.name == '${buildName}'
                 assert !componentId.build.currentBuild
                 assert componentId.projectPath == ':'
-                assert componentId.projectName == '${buildName}'
+                assert componentId.projectName == '${dependencyName}'
 
                 components = buildRootProject.dependencies.selected
                 assert components.size() == 1
                 componentId = components[0].id
+                assert componentId.build.buildPath == ':${buildName}'
                 assert componentId.build.name == '${buildName}'
                 assert !componentId.build.currentBuild
                 assert componentId.projectPath == ':b1'
@@ -169,12 +172,18 @@
                 selectors = buildRootProject.dependencies.requested
                 assert selectors.size() == 1
                 assert selectors[0].displayName == 'project :${buildName}:b1'
-                // TODO - should be build name
-                assert selectors[0].buildName == 'buildB'
+                assert selectors[0].buildPath == ':${buildName}'
+                assert selectors[0].buildName == '${buildName}'
                 assert selectors[0].projectPath == ':b1'
             }
         """
 
+        3.times {
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.isCurrentBuild() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+        }
+        executer.expectDocumentedDeprecationWarning("The ProjectComponentSelector.getBuildName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+
         expect:
         execute(buildA, ":assemble")
 
@@ -183,4 +192,69 @@
         ""                           | "buildB"  | "buildB"       | "default root project name"
         "rootProject.name='someLib'" | "buildB"  | "someLib"      | "configured root project name"
     }
+
+    def "project component identifiers know if projects belong to the current build or not"() {
+        def buildC = singleProjectBuild('buildC') {
+            buildFile << "apply plugin: 'java'"
+        }
+        includeBuild(buildC)
+
+        buildA.buildFile << """
+            dependencies {
+                testImplementation 'org.test:buildC'
+                testImplementation 'org.test:buildA' // self dependency
+            }
+        """
+        buildB.buildFile << """
+            dependencies {
+                testImplementation 'org.test:buildC'
+                testImplementation 'org.test:buildB' // self dependency
+            }
+        """
+        buildC.buildFile << """
+            dependencies {
+                testImplementation 'org.test:buildC' // self dependency
+            }
+        """
+
+        buildA.buildFile << """
+            def rootProvider = configurations.testRuntimeClasspath.incoming.resolutionResult.rootComponent
+            classes.doLast {
+                def rootComponent = rootProvider.get()
+                def projectInOtherBuild = rootComponent.dependencies[0].selected
+                def self = rootComponent.dependencies[1].selected
+                assert rootComponent.id.build.currentBuild
+                assert self.id.build.currentBuild
+                assert self == rootComponent
+                assert !projectInOtherBuild.id.build.currentBuild
+            }
+        """
+        buildB.buildFile << """
+            def rootProvider = configurations.testRuntimeClasspath.incoming.resolutionResult.rootComponent
+            classes.doLast {
+                def rootComponent = rootProvider.get()
+                def projectInOtherBuild = rootComponent.dependencies[0].selected
+                def self = rootComponent.dependencies[1].selected
+                assert rootComponent.id.build.currentBuild
+                assert self.id.build.currentBuild
+                assert self == rootComponent
+                assert !projectInOtherBuild.id.build.currentBuild
+            }
+        """
+        buildC.buildFile << """
+            def rootProvider = configurations.testRuntimeClasspath.incoming.resolutionResult.rootComponent
+            classes.doLast {
+                def rootComponent = rootProvider.get()
+                def self = rootComponent.dependencies[0].selected
+                assert rootComponent.id.build.currentBuild
+                assert self.id.build.currentBuild
+                assert self == rootComponent
+            }
+        """
+
+        8.times { executer.expectDocumentedDeprecationWarning("The BuildIdentifier.isCurrentBuild() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation") }
+
+        expect:
+        execute(buildA, ":buildC:assemble", ":buildB:assemble", ":assemble")
+    }
 }
diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildProjectDependencyConflictResolutionIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildProjectDependencyConflictResolutionIntegrationTest.groovy
index cccebde..874d59d 100644
--- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildProjectDependencyConflictResolutionIntegrationTest.groovy
+++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildProjectDependencyConflictResolutionIntegrationTest.groovy
@@ -27,7 +27,7 @@
 
     @Override
     String getBuildId() {
-        "new org.gradle.api.internal.artifacts.DefaultBuildIdentifier(projectName)"
+        "new org.gradle.api.internal.artifacts.DefaultBuildIdentifier(org.gradle.util.Path.path(':' + projectName))"
     }
 
     @Override
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/AbstractCompositeParticipantBuildState.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/AbstractCompositeParticipantBuildState.java
index 79ec134..2a27fb9 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/AbstractCompositeParticipantBuildState.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/AbstractCompositeParticipantBuildState.java
@@ -29,6 +29,7 @@
 import org.gradle.internal.build.BuildState;
 import org.gradle.internal.build.CompositeBuildParticipantBuildState;
 import org.gradle.internal.buildtree.BuildTreeState;
+import org.gradle.util.Path;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -70,12 +71,8 @@ private void registerProject(Set<Pair<ModuleVersionIdentifier, ProjectComponentI
 
     @Override
     public ProjectComponentIdentifier idToReferenceProjectFromAnotherBuild(ProjectComponentIdentifier identifier) {
-        // Need to use a 'foreign' build id to make BuildIdentifier.isCurrentBuild and BuildIdentifier.name work in dependency results
         DefaultProjectComponentIdentifier original = (DefaultProjectComponentIdentifier) identifier;
-        String name = getIdentityPath().getName();
-        if (name == null) {
-            name = getBuildIdentifier().getName();
-        }
-        return new DefaultProjectComponentIdentifier(new ForeignBuildIdentifier(getBuildIdentifier().getName(), name), original.getIdentityPath(), original.projectPath(), original.getProjectName());
+        Path foreignBuildPath = Path.path(getBuildIdentifier().getBuildPath());
+        return new DefaultProjectComponentIdentifier(new ForeignBuildIdentifier(foreignBuildPath), original.getIdentityPath(), original.projectPath(), original.getProjectName());
     }
 }
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/CompositeBuildServices.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/CompositeBuildServices.java
index 2e502a0..f13f2d5 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/CompositeBuildServices.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/CompositeBuildServices.java
@@ -24,6 +24,8 @@
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.composite.internal.plugins.CompositeBuildPluginResolverContributor;
 import org.gradle.internal.buildtree.GlobalDependencySubstitutionRegistry;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.service.ServiceRegistration;
 import org.gradle.internal.service.scopes.AbstractPluginServiceRegistry;
@@ -75,8 +77,8 @@ public CompositeBuildContext createCompositeBuildContext() {
             return new DefaultBuildableCompositeBuildContext();
         }
 
-        public DefaultLocalComponentInAnotherBuildProvider createLocalComponentProvider() {
-            return new DefaultLocalComponentInAnotherBuildProvider(new IncludedBuildDependencyMetadataBuilder());
+        public DefaultLocalComponentInAnotherBuildProvider createLocalComponentProvider(CalculatedValueContainerFactory calculatedValueContainerFactory, LocalComponentGraphResolveStateFactory resolveStateFactory) {
+            return new DefaultLocalComponentInAnotherBuildProvider(new IncludedBuildDependencyMetadataBuilder(), resolveStateFactory, calculatedValueContainerFactory);
         }
     }
 }
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultBuildControllers.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultBuildControllers.java
index 31be3d5..d57a52e 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultBuildControllers.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultBuildControllers.java
@@ -142,7 +142,7 @@ private Comparator<BuildIdentifier> idComparator() {
             if (id2.equals(DefaultBuildIdentifier.ROOT)) {
                 return -1;
             }
-            return id1.getName().compareTo(id2.getName());
+            return id1.getBuildPath().compareTo(id2.getBuildPath());
         };
     }
 }
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuild.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuild.java
index ec5bdf0..c842d77 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuild.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuild.java
@@ -40,12 +40,10 @@ public class DefaultIncludedBuild extends AbstractCompositeParticipantBuildState
     private final Path identityPath;
     private final BuildDefinition buildDefinition;
     private final boolean isImplicit;
-    private final BuildState owner;
     private final IncludedBuildImpl model;
 
     public DefaultIncludedBuild(
         BuildIdentifier buildIdentifier,
-        Path identityPath,
         BuildDefinition buildDefinition,
         boolean isImplicit,
         BuildState owner,
@@ -55,10 +53,9 @@ public DefaultIncludedBuild(
         // Use a defensive copy of the build definition, as it may be mutated during build execution
         super(buildTree, buildDefinition.newInstance(), owner);
         this.buildIdentifier = buildIdentifier;
-        this.identityPath = identityPath;
+        this.identityPath = Path.path(buildIdentifier.getBuildPath());
         this.buildDefinition = buildDefinition;
         this.isImplicit = isImplicit;
-        this.owner = owner;
         this.model = instantiator.newInstance(IncludedBuildImpl.class, this);
     }
 
@@ -125,11 +122,6 @@ public File getBuildRootDir() {
     }
 
     @Override
-    public Path getCurrentPrefixForProjectsInChildBuilds() {
-        return owner.getCurrentPrefixForProjectsInChildBuilds().child(buildIdentifier.getName());
-    }
-
-    @Override
     public Path calculateIdentityPathForProject(Path projectPath) {
         return getIdentityPath().append(projectPath);
     }
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuildFactory.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuildFactory.java
index 08c38d7..a273e1c 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuildFactory.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuildFactory.java
@@ -24,7 +24,6 @@
 import org.gradle.internal.build.IncludedBuildState;
 import org.gradle.internal.buildtree.BuildTreeState;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.util.Path;
 
 import java.io.File;
 
@@ -50,11 +49,10 @@ private void validateBuildDirectory(File dir) {
     }
 
     @Override
-    public IncludedBuildState createBuild(BuildIdentifier buildIdentifier, Path identityPath, BuildDefinition buildDefinition, boolean isImplicit, BuildState owner) {
+    public IncludedBuildState createBuild(BuildIdentifier buildIdentifier, BuildDefinition buildDefinition, boolean isImplicit, BuildState owner) {
         validateBuildDirectory(buildDefinition.getBuildRootDir());
         return new DefaultIncludedBuild(
             buildIdentifier,
-            identityPath,
             buildDefinition,
             isImplicit,
             owner,
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuildRegistry.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuildRegistry.java
index a8be020..baf227c 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuildRegistry.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultIncludedBuildRegistry.java
@@ -171,7 +171,7 @@ private void maybeAddBuildSrcBuild(BuildState owner) {
 
         BuildDefinition buildDefinition = buildStateFactory.buildDefinitionFor(buildSrcDir, owner);
         Path identityPath = assignPath(owner, buildDefinition.getName(), buildDefinition.getBuildRootDir());
-        BuildIdentifier buildIdentifier = idFor(buildDefinition.getName());
+        BuildIdentifier buildIdentifier = idFor(identityPath);
         StandAloneNestedBuild build = buildStateFactory.createNestedBuild(buildIdentifier, identityPath, buildDefinition, owner);
         buildSrcBuildsByOwner.put(owner, build);
         nestedBuildsByRootDir.put(buildSrcDir, build);
@@ -187,7 +187,7 @@ public NestedBuildTree addNestedBuildTree(BuildDefinition buildDefinition, Build
         String name = MoreObjects.firstNonNull(buildName, dir.getName());
         validateNameIsNotBuildSrc(name, dir);
         Path identityPath = assignPath(owner, name, dir);
-        BuildIdentifier buildIdentifier = idFor(name);
+        BuildIdentifier buildIdentifier = idFor(identityPath);
         return buildStateFactory.createNestedTree(buildDefinition, buildIdentifier, identityPath, owner);
     }
 
@@ -222,10 +222,10 @@ private IncludedBuildState registerBuild(BuildDefinition buildDefinition, boolea
                 throw new IllegalStateException("build name is required");
             }
             validateNameIsNotBuildSrc(buildName, buildDir);
-            Path idPath = buildPath != null ? buildPath : assignPath(rootBuild, buildDefinition.getName(), buildDir);
-            BuildIdentifier buildIdentifier = idFor(buildName);
+            Path idPath = buildPath != null ? buildPath : assignPath(rootBuild, buildName, buildDir);
+            BuildIdentifier buildIdentifier = idFor(idPath);
 
-            includedBuild = includedBuildFactory.createBuild(buildIdentifier, idPath, buildDefinition, isImplicit, rootBuild);
+            includedBuild = includedBuildFactory.createBuild(buildIdentifier, buildDefinition, isImplicit, rootBuild);
             includedBuildsByRootDir.put(buildDir, includedBuild);
             nestedBuildsByRootDir.put(buildDir, includedBuild);
             pendingIncludedBuilds.add(includedBuild);
@@ -239,15 +239,8 @@ private IncludedBuildState registerBuild(BuildDefinition buildDefinition, boolea
         return includedBuild;
     }
 
-    private BuildIdentifier idFor(String buildName) {
-        BuildIdentifier buildIdentifier = new DefaultBuildIdentifier(buildName);
-
-        // Create a synthetic id for the build, if the id is already used
-        // Should instead use a structured id implementation of some kind instead
-        for (int count = 1; buildsByIdentifier.containsKey(buildIdentifier); count++) {
-            buildIdentifier = new DefaultBuildIdentifier(buildName + ":" + count);
-        }
-        return buildIdentifier;
+    private BuildIdentifier idFor(Path absoluteBuildPath) {
+        return new DefaultBuildIdentifier(absoluteBuildPath);
     }
 
     private Path assignPath(BuildState owner, String name, File dir) {
@@ -277,7 +270,7 @@ private static boolean isPrefix(File prefix, File toCheck) {
     @Override
     public void resetStateForAllBuilds() {
         for (BuildState build : buildsByIdentifier.values()) {
-            build.resetLifecycle();
+            build.resetModel();
         }
     }
 
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultLocalComponentInAnotherBuildProvider.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultLocalComponentInAnotherBuildProvider.java
index f073692..1fe3feb 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultLocalComponentInAnotherBuildProvider.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultLocalComponentInAnotherBuildProvider.java
@@ -16,35 +16,77 @@
 
 package org.gradle.composite.internal;
 
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.LocalComponentInAnotherBuildProvider;
+import org.gradle.api.internal.project.HoldsProjectState;
 import org.gradle.api.internal.project.ProjectState;
+import org.gradle.api.internal.tasks.NodeExecutionContext;
+import org.gradle.internal.Describables;
 import org.gradle.internal.build.CompositeBuildParticipantBuildState;
 import org.gradle.internal.build.IncludedBuildState;
-import org.gradle.internal.component.local.model.DefaultLocalComponentGraphResolveState;
 import org.gradle.internal.component.local.model.LocalComponentGraphResolveState;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory;
 import org.gradle.internal.component.local.model.LocalComponentMetadata;
+import org.gradle.internal.model.CalculatedValueContainer;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
+import org.gradle.internal.model.ValueCalculator;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Provides the metadata for a local component consumed from a build that is not the producing build.
  *
  * <p>Currently, the metadata for a component is different based on whether it is consumed from the producing build or from another build. This difference should go away, but in the meantime this class provides the mapping.
  */
-public class DefaultLocalComponentInAnotherBuildProvider implements LocalComponentInAnotherBuildProvider {
+public class DefaultLocalComponentInAnotherBuildProvider implements LocalComponentInAnotherBuildProvider, HoldsProjectState {
     private final IncludedBuildDependencyMetadataBuilder dependencyMetadataBuilder;
+    private final LocalComponentGraphResolveStateFactory resolveStateFactory;
+    private final CalculatedValueContainerFactory calculatedValueContainerFactory;
+    private final Map<ProjectComponentIdentifier, CalculatedValueContainer<LocalComponentGraphResolveState, ?>> projects = new ConcurrentHashMap<>();
 
-    public DefaultLocalComponentInAnotherBuildProvider(IncludedBuildDependencyMetadataBuilder dependencyMetadataBuilder) {
+    public DefaultLocalComponentInAnotherBuildProvider(
+        IncludedBuildDependencyMetadataBuilder dependencyMetadataBuilder,
+        LocalComponentGraphResolveStateFactory resolveStateFactory,
+        CalculatedValueContainerFactory calculatedValueContainerFactory
+    ) {
         this.dependencyMetadataBuilder = dependencyMetadataBuilder;
+        this.resolveStateFactory = resolveStateFactory;
+        this.calculatedValueContainerFactory = calculatedValueContainerFactory;
     }
 
     public LocalComponentGraphResolveState getComponent(ProjectState projectState) {
-        // TODO - this should work for any build, rather than just an included build
-        CompositeBuildParticipantBuildState buildState = (CompositeBuildParticipantBuildState) projectState.getOwner();
-        if (buildState instanceof IncludedBuildState) {
-            // make sure the build is configured now (not do this for the root build, as we are already configuring it right now)
-            buildState.ensureProjectsConfigured();
+        ProjectComponentIdentifier projectIdentifier = projectState.getComponentIdentifier();
+        CalculatedValueContainer<LocalComponentGraphResolveState, ?> valueContainer = projects.computeIfAbsent(projectIdentifier, projectComponentIdentifier ->
+            calculatedValueContainerFactory.create(Describables.of("metadata of", projectIdentifier), new MetadataSupplier(projectState)));
+        // Calculate the value after adding the entry to the map, so that the value container can take care of thread synchronization
+        valueContainer.finalizeIfNotAlready();
+        return valueContainer.get();
+    }
+
+    @Override
+    public void discardAll() {
+        projects.clear();
+    }
+
+    private class MetadataSupplier implements ValueCalculator<LocalComponentGraphResolveState> {
+        private final ProjectState projectState;
+
+        public MetadataSupplier(ProjectState projectState) {
+            this.projectState = projectState;
         }
-        // Metadata builder uses mutable project state, so synchronize access to the project state
-        LocalComponentMetadata metadata = projectState.fromMutableState(p -> dependencyMetadataBuilder.build(buildState, projectState.getComponentIdentifier()));
-        return new DefaultLocalComponentGraphResolveState(metadata);
+
+        @Override
+        public LocalComponentGraphResolveState calculateValue(NodeExecutionContext context) {
+            // TODO - this should work for any build, rather than just an included build
+            CompositeBuildParticipantBuildState buildState = (CompositeBuildParticipantBuildState) projectState.getOwner();
+            if (buildState instanceof IncludedBuildState) {
+                // make sure the build is configured now (not do this for the root build, as we are already configuring it right now)
+                buildState.ensureProjectsConfigured();
+            }
+            // Metadata builder uses mutable project state, so synchronize access to the project state
+            LocalComponentMetadata metadata = projectState.fromMutableState(p -> dependencyMetadataBuilder.build(buildState, projectState.getComponentIdentifier()));
+            return resolveStateFactory.stateFor(metadata);
+        }
     }
 }
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultNestedBuild.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultNestedBuild.java
index 78730cf..c815a98 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultNestedBuild.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultNestedBuild.java
@@ -104,11 +104,6 @@ public <T> T run(Function<? super BuildTreeLifecycleController, T> buildAction)
     }
 
     @Override
-    public Path getCurrentPrefixForProjectsInChildBuilds() {
-        return owner.getCurrentPrefixForProjectsInChildBuilds().child(buildDefinition.getName());
-    }
-
-    @Override
     public Path calculateIdentityPathForProject(Path projectPath) {
         return getBuildController().getGradle().getIdentityPath().append(projectPath);
     }
@@ -135,20 +130,4 @@ public RuntimeException finishBuildTree(List<Throwable> failures) {
             return exceptionAnalyser.transform(failures);
         }
     }
-
-    private class FinishThisBuildOnlyFinishExecutor implements BuildTreeFinishExecutor {
-        private final ExceptionAnalyser exceptionAnalyser;
-
-        public FinishThisBuildOnlyFinishExecutor(ExceptionAnalyser exceptionAnalyser) {
-            this.exceptionAnalyser = exceptionAnalyser;
-        }
-
-        @Override
-        @Nullable
-        public RuntimeException finishBuildTree(List<Throwable> failures) {
-            RuntimeException reportable = exceptionAnalyser.transform(failures);
-            ExecutionResult<Void> finishResult = getBuildController().finishBuild(reportable);
-            return exceptionAnalyser.transform(ExecutionResult.maybeFailed(reportable).withFailures(finishResult).getFailures());
-        }
-    }
 }
diff --git a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultRootBuildState.java b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultRootBuildState.java
index e091a35..95c1d0a 100644
--- a/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultRootBuildState.java
+++ b/subprojects/composite-builds/src/main/java/org/gradle/composite/internal/DefaultRootBuildState.java
@@ -134,11 +134,6 @@ public StartParameterInternal getStartParameter() {
     }
 
     @Override
-    public Path getCurrentPrefixForProjectsInChildBuilds() {
-        return Path.ROOT;
-    }
-
-    @Override
     public Path calculateIdentityPathForProject(Path path) {
         return path;
     }
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 fa7b739..adfef9e 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,14 +19,12 @@
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.BuildIdentifier;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.initialization.Settings;
 import org.gradle.api.internal.BuildDefinition;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.StartParameterInternal;
 import org.gradle.initialization.RunNestedBuildBuildOperationType;
 import org.gradle.initialization.exception.ExceptionAnalyser;
 import org.gradle.initialization.layout.BuildLayout;
-import org.gradle.internal.InternalBuildAdapter;
 import org.gradle.internal.Pair;
 import org.gradle.internal.build.AbstractBuildState;
 import org.gradle.internal.build.BuildLifecycleController;
@@ -56,9 +54,7 @@
 public class RootOfNestedBuildTree extends AbstractBuildState implements NestedRootBuild {
     private final BuildIdentifier buildIdentifier;
     private final Path identityPath;
-    private final BuildState owner;
     private final BuildTreeLifecycleController buildTreeLifecycleController;
-    private String buildName;
 
     public RootOfNestedBuildTree(
         BuildDefinition buildDefinition,
@@ -70,8 +66,6 @@ public RootOfNestedBuildTree(
         super(buildTree, buildDefinition, owner);
         this.buildIdentifier = buildIdentifier;
         this.identityPath = identityPath;
-        this.owner = owner;
-        this.buildName = buildDefinition.getName() == null ? buildIdentifier.getName() : buildDefinition.getName();
 
         BuildScopeServices buildServices = getBuildServices();
         BuildLifecycleController buildLifecycleController = getBuildController();
@@ -108,11 +102,6 @@ public boolean isImplicitBuild() {
     }
 
     @Override
-    public Path getCurrentPrefixForProjectsInChildBuilds() {
-        return owner.getCurrentPrefixForProjectsInChildBuilds().child(buildName);
-    }
-
-    @Override
     public Path calculateIdentityPathForProject(Path projectPath) {
         return getBuildController().getGradle().getIdentityPath().append(projectPath);
     }
@@ -145,12 +134,6 @@ public <T> T run(Function<? super BuildTreeLifecycleController, T> action) {
         return executor.call(new CallableBuildOperation<T>() {
             @Override
             public T call(BuildOperationContext context) {
-                gradle.addBuildListener(new InternalBuildAdapter() {
-                    @Override
-                    public void settingsEvaluated(Settings settings) {
-                        buildName = settings.getRootProject().getName();
-                    }
-                });
                 T result = action.apply(buildTreeLifecycleController);
                 context.setResult(new RunNestedBuildBuildOperationType.Result() {
                 });
diff --git a/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildRegistryTest.groovy b/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildRegistryTest.groovy
index 92a5ad4..282b39c 100644
--- a/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildRegistryTest.groovy
+++ b/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildRegistryTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.composite.internal
 
+import org.gradle.api.artifacts.component.BuildIdentifier
 import org.gradle.api.initialization.ProjectDescriptor
 import org.gradle.api.internal.BuildDefinition
 import org.gradle.api.internal.DocumentationRegistry
@@ -124,14 +125,13 @@
         def dir = tmpDir.createDir("b1")
         def buildDefinition = build(dir)
         def includedBuild = Stub(IncludedBuildState)
-        def buildIdentifier = new DefaultBuildIdentifier("b1")
-        def idPath = Path.path(":b1")
+        def buildIdentifier = buildIdentifier(":b1")
         includedBuild.buildIdentifier >> buildIdentifier
         includedBuild.buildRootDir >> dir
 
         given:
         registry.attachRootBuild(rootBuild())
-        includedBuildFactory.createBuild(buildIdentifier, idPath, buildDefinition, false, _ as BuildState) >> includedBuild
+        includedBuildFactory.createBuild(buildIdentifier, buildDefinition, false, _ as BuildState) >> includedBuild
 
         when:
         def result = registry.addIncludedBuild(buildDefinition)
@@ -170,7 +170,6 @@
         def buildDefinition1 = build(dir1, "b1")
         def buildDefinition2 = build(dir2, "b2")
         def buildDefinition3 = build(dir3, "b3")
-        def id1 = new DefaultBuildIdentifier("b1")
         def includedBuild1 = expectIncludedBuildAdded("b1", buildDefinition1)
         def includedBuild2 = expectIncludedBuildAdded("b2", buildDefinition2)
         def includedBuild3 = expectIncludedBuildAdded("b3", buildDefinition3)
@@ -185,7 +184,7 @@
 
         registry.includedBuilds as List == [includedBuild1, includedBuild2, includedBuild3]
 
-        registry.getBuild(id1).is(includedBuild1)
+        registry.getBuild(buildIdentifier(":b1")).is(includedBuild1)
     }
 
     def "can add the same included build multiple times"() {
@@ -210,7 +209,7 @@
 
         given:
         registry.attachRootBuild(rootBuild())
-        includedBuildFactory.createBuild(new DefaultBuildIdentifier("b1"), Path.path(":b1"), buildDefinition, true, _ as BuildState) >> includedBuild
+        includedBuildFactory.createBuild(buildIdentifier(":b1"), buildDefinition, true, _ as BuildState) >> includedBuild
 
         when:
         def result = registry.addImplicitIncludedBuild(buildDefinition)
@@ -243,7 +242,7 @@
         def nestedBuild = registry.getBuildSrcNestedBuild(rootBuild)
         nestedBuild != null
         nestedBuild.implicitBuild
-        nestedBuild.buildIdentifier == new DefaultBuildIdentifier("buildSrc")
+        nestedBuild.buildIdentifier == buildIdentifier(":buildSrc")
         nestedBuild.identityPath == Path.path(":buildSrc")
 
         and:
@@ -271,12 +270,12 @@
 
         expect:
         def nestedBuild1 = registry.getBuildSrcNestedBuild(rootBuild)
-        nestedBuild1.buildIdentifier == new DefaultBuildIdentifier("buildSrc")
+        nestedBuild1.buildIdentifier == buildIdentifier(":buildSrc")
         nestedBuild1.identityPath == Path.path(":buildSrc")
 
         def nestedBuild2 = registry.getBuildSrcNestedBuild(parent)
         // Shows current behaviour, not necessarily desired behaviour
-        nestedBuild2.buildIdentifier == new DefaultBuildIdentifier("buildSrc:1")
+        nestedBuild2.buildIdentifier == buildIdentifier(":parent:buildSrc")
         nestedBuild2.identityPath == Path.path(":parent:buildSrc")
     }
 
@@ -298,7 +297,7 @@
 
     IncludedBuildState expectIncludedBuildAdded(String name, BuildDefinition buildDefinition) {
         def idPath = Path.path(":$name")
-        def buildIdentifier = new DefaultBuildIdentifier(name)
+        def buildIdentifier = new DefaultBuildIdentifier(idPath)
 
         def gradle = Stub(GradleInternal)
         def services = Stub(ServiceRegistry)
@@ -313,7 +312,7 @@
 
         services.get(PublicBuildPath) >> Stub(PublicBuildPath)
 
-        includedBuildFactory.createBuild(buildIdentifier, idPath, buildDefinition, false, _) >> includedBuild
+        includedBuildFactory.createBuild(buildIdentifier, buildDefinition, false, _) >> includedBuild
 
         return includedBuild
     }
@@ -364,4 +363,7 @@
         return buildController
     }
 
+    private BuildIdentifier buildIdentifier(String path) {
+        new DefaultBuildIdentifier(Path.path(path))
+    }
 }
diff --git a/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildTaskGraphParallelTest.groovy b/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildTaskGraphParallelTest.groovy
index 5472913..8d6f506 100644
--- a/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildTaskGraphParallelTest.groovy
+++ b/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildTaskGraphParallelTest.groovy
@@ -29,7 +29,7 @@
 import org.gradle.api.internal.plugins.PluginManagerInternal
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.project.ProjectState
-import org.gradle.api.internal.project.taskfactory.TaskIdentity
+import org.gradle.api.internal.project.taskfactory.TestTaskIdentities
 import org.gradle.api.internal.tasks.NodeExecutionContext
 import org.gradle.api.internal.tasks.TaskDestroyablesInternal
 import org.gradle.api.internal.tasks.TaskLocalStateInternal
@@ -129,7 +129,7 @@
 
     def "runs scheduled unrelated work across multiple builds"() {
         def services = new TreeServices(workers)
-        def childBuild = build(services, new DefaultBuildIdentifier("child"))
+        def childBuild = build(services, new DefaultBuildIdentifier(Path.path(":child")))
         def build = build(services, DefaultBuildIdentifier.ROOT)
         def childNode = new TestNode("child build node")
         def node = new TestNode("main build node")
@@ -157,7 +157,7 @@
 
     def "runs scheduled related work across multiple builds"() {
         def services = new TreeServices(workers)
-        def childBuild = build(services, new DefaultBuildIdentifier("child"))
+        def childBuild = build(services, new DefaultBuildIdentifier(Path.path(":child")))
         def build = build(services, DefaultBuildIdentifier.ROOT)
         def childNode = new TestNode("child build node")
         def node = new DelegateNode("main build node", [childNode])
@@ -211,7 +211,7 @@
 
     def "fails when no further nodes can be selected across multiple builds"() {
         def services = new TreeServices(manyWorkers)
-        def childBuild = build(services, new DefaultBuildIdentifier("child"))
+        def childBuild = build(services, new DefaultBuildIdentifier(Path.path(":child")))
         def build = build(services, DefaultBuildIdentifier.ROOT)
         def node = new DependenciesStuckNode("main build node")
         def childNode = new DependenciesStuckNode("child build node")
@@ -240,7 +240,7 @@
         stdout.stdOut.contains("- main build node (state=SHOULD_RUN")
         stdout.stdOut.contains("- :task (state=SHOULD_RUN")
         stdout.stdOut.contains("- Ordinal groups: group 0 entry nodes: [:task (SHOULD_RUN)]")
-        stdout.stdOut.contains("- Build 'child':")
+        stdout.stdOut.contains("- Build ':child':")
         stdout.stdOut.contains("- child build node (state=SHOULD_RUN")
         stdout.stdOut.contains("- :child:task (state=SHOULD_RUN")
         stdout.stdOut.contains("- group 0 entry nodes: [:child:task (SHOULD_RUN)]")
@@ -277,8 +277,8 @@
         _ * dependencies.getDependencies(_) >> [dependsOn].toSet()
         _ * task.taskDependencies >> dependencies
         _ * task.project >> project
-        _ * task.identityPath >> Path.path(":${services.identifier.name}:task")
-        _ * task.taskIdentity >> TaskIdentity.create("task", DefaultTask, project)
+        _ * task.identityPath >> Path.path(services.identifier.buildPath).child("task")
+        _ * task.taskIdentity >> TestTaskIdentities.create("task", DefaultTask, project)
         _ * task.destroyables >> Stub(TaskDestroyablesInternal)
         _ * task.localState >> Stub(TaskLocalStateInternal)
         _ * project.gradle >> services.gradle
@@ -340,7 +340,7 @@
         }
 
         @Override
-        void resetLifecycle() {
+        void resetModel() {
         }
 
         @Override
@@ -408,6 +408,16 @@
         }
 
         @Override
+        ExecutionResult<Void> beforeModelDiscarded(boolean failed) {
+            throw new UnsupportedOperationException()
+        }
+
+        @Override
+        ExecutionResult<Void> beforeModelReset() {
+            throw new UnsupportedOperationException()
+        }
+
+        @Override
         ExecutionResult<Void> finishBuild(@Nullable Throwable failure) {
             throw new UnsupportedOperationException()
         }
diff --git a/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildTest.groovy b/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildTest.groovy
index b261e4c..3f2d1f6 100644
--- a/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildTest.groovy
+++ b/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultIncludedBuildTest.groovy
@@ -23,7 +23,6 @@
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.SettingsInternal
 import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
-import org.gradle.api.internal.artifacts.ForeignBuildIdentifier
 import org.gradle.internal.build.BuildLifecycleController
 import org.gradle.internal.build.BuildModelControllerServices
 import org.gradle.internal.build.BuildState
@@ -56,7 +55,10 @@
         services.add(Stub(BuildTreeWorkGraphController))
         _ * buildTree.services >> services
 
-        build = new DefaultIncludedBuild(Stub(BuildIdentifier), Path.path(":a:b:c"), buildDefinition, false, owningBuild, buildTree, Mock(Instantiator))
+        def buildId = Stub(BuildIdentifier) {
+            buildPath >> Path.path(":a:b:c")
+        }
+        build = new DefaultIncludedBuild(buildId, buildDefinition, false, owningBuild, buildTree, Mock(Instantiator))
     }
 
     def "creates a foreign id for projects"() {
@@ -64,7 +66,6 @@
 
         expect:
         def id = build.idToReferenceProjectFromAnotherBuild(projectId)
-        id.build instanceof ForeignBuildIdentifier
         id.identityPath == projectId.identityPath
         id.projectPath == projectId.projectPath
         id.projectName == projectId.projectName
diff --git a/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultNestedBuildTest.groovy b/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultNestedBuildTest.groovy
index d6e4ab2..e2bd304 100644
--- a/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultNestedBuildTest.groovy
+++ b/subprojects/composite-builds/src/test/groovy/org/gradle/composite/internal/DefaultNestedBuildTest.groovy
@@ -20,6 +20,7 @@
 import org.gradle.api.internal.BuildDefinition
 import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.internal.GradleInternal
+import org.gradle.api.logging.LogLevel
 import org.gradle.initialization.exception.ExceptionAnalyser
 import org.gradle.internal.build.BuildLifecycleController
 import org.gradle.internal.build.BuildModelControllerServices
@@ -53,7 +54,6 @@
 
     DefaultNestedBuild build() {
         _ * factory.servicesForBuild(buildDefinition, _, owner) >> Mock(BuildModelControllerServices.Supplier)
-        _ * owner.currentPrefixForProjectsInChildBuilds >> Path.path(":owner")
         _ * factory.newInstance(buildDefinition, _, owner, _) >> controller
         _ * buildDefinition.name >> "nested"
         services.add(Stub(BuildOperationExecutor))
@@ -76,7 +76,7 @@
 
     def "runs action and does not finish build"() {
         given:
-        services.add(new BuildModelParameters(false, false, false, false, false, false, false))
+        services.add(new BuildModelParameters(false, false, false, false, false, false, false, LogLevel.DEBUG))
         def build = build()
 
         when:
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildServiceIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildServiceIntegrationTest.groovy
index 4c812c0..25a0553 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildServiceIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheBuildServiceIntegrationTest.groovy
@@ -21,9 +21,12 @@
 import org.gradle.api.Project
 import org.gradle.api.provider.Property
 import org.gradle.api.provider.Provider
+import org.gradle.api.provider.ValueSource
+import org.gradle.api.provider.ValueSourceParameters
 import org.gradle.api.services.BuildService
 import org.gradle.api.services.BuildServiceParameters
 import org.gradle.api.services.ServiceReference
+import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.TaskAction
@@ -38,7 +41,6 @@
 import org.gradle.tooling.events.FinishEvent
 import org.gradle.tooling.events.OperationCompletionListener
 import org.gradle.tooling.events.task.TaskFinishEvent
-import org.gradle.util.internal.ToBeImplemented
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 
@@ -513,49 +515,206 @@
         outputContains 'probe(classloader2) => 6'
     }
 
-    @ToBeImplemented("https://github.com/gradle/gradle/issues/22337")
-    def "build service can be used as input of value source obtained at configuration time"() {
+    @Issue("https://github.com/gradle/gradle/issues/22337")
+    def "build service can be injected to buildSrc task with #injectionAnnotation"() {
         given:
         def configurationCache = newConfigurationCacheFixture()
-        buildFile("""
-            abstract class EmptyService implements BuildService<${BuildServiceParameters.name}.None> {
-                int getValue() {
-                    return 42
-                }
-            }
 
-            abstract class ServiceValueSource implements ValueSource<Integer, Params> {
-                interface Params extends ValueSourceParameters {
-                    Property<EmptyService> getService()
-                }
-                @Override
-                Integer obtain(){
-                    return parameters.service.get().value
-                }
-            }
-            def serviceProvider = gradle.sharedServices.registerIfAbsent("counter", EmptyService) {}
-            def valueSource = providers.of(ServiceValueSource) {
-                parameters {
-                    service = serviceProvider
-                }
-            }.get()
+        file("buildSrc/build.gradle") << ("""
+            ${constantServiceImpl("ConstantBuildService", "constant")}
+            def serviceProvider = gradle.sharedServices.registerIfAbsent("constant", ConstantBuildService) {}
 
-            task check {
-                doLast {
-                    println "valueSource = " + valueSource
-                }
-            }
+            ${serviceTaskImpl("printValue", injectionAnnotation, propertyType, valueGetter, taskConfiguration)}
+
+            tasks.named("jar") { dependsOn("printValue") }
         """)
 
         when:
-        configurationCacheRun "check"
+        configurationCacheRun()
 
         then:
-        outputContains("valueSource = 42")
-        // TODO(https://github.com/gradle/gradle/issues/22337) A clear error message should be provided at store time
+        // Note that load-after-store doesn't check build fingerprint
         configurationCache.assertStateStored()
-        expect:
-        configurationCacheFails "check"
+
+        when:
+        configurationCacheRun()
+
+        then: "Verify that fingerprint check works"
+        configurationCache.assertStateLoaded()
+
+        where:
+        injectionAnnotation                    | propertyType           | taskConfiguration                                       | valueGetter
+        "${ServiceReference.name}('constant')" | "ConstantBuildService" | ""                                                      | "value.get().value"
+        Internal.name                          | "ConstantBuildService" | "value = serviceProvider; usesService(serviceProvider)" | "value.get().value"
+        Input.name                             | "String"               | "value = serviceProvider.map { it.value }"              | "value.get()"
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/22337")
+    def "build service can be injected to build task with #injectionAnnotation"() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+
+        buildScript("""
+            ${constantServiceImpl("ConstantBuildService", "constant")}
+            def serviceProvider = gradle.sharedServices.registerIfAbsent("constant", ConstantBuildService) {}
+
+            ${serviceTaskImpl("printValue", injectionAnnotation, propertyType, valueGetter, taskConfiguration)}
+        """)
+
+        when:
+        configurationCacheRun("printValue")
+
+        then:
+        // Note that load-after-store doesn't check build fingerprint
+        configurationCache.assertStateStored()
+
+        when:
+        configurationCacheRun("printValue")
+
+        then: "Verify that fingerprint check works"
+        configurationCache.assertStateLoaded()
+
+        where:
+        injectionAnnotation                    | propertyType           | taskConfiguration                                       | valueGetter
+        "${ServiceReference.name}('constant')" | "ConstantBuildService" | ""                                                      | "value.get().value"
+        Internal.name                          | "ConstantBuildService" | "value = serviceProvider; usesService(serviceProvider)" | "value.get().value"
+        Input.name                             | "String"               | "value = serviceProvider.map { it.value }"              | "value.get()"
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/22337")
+    def "build service cannot be injected to buildSrc task with ValueSource as #injectionAnnotation"() {
+        given:
+        file("buildSrc/build.gradle") << ("""
+            ${constantServiceImpl("ConstantBuildService", "constant")}
+            def serviceProvider = gradle.sharedServices.registerIfAbsent("constant", ConstantBuildService) {}
+
+            ${convertingValueSourceImpl("ConvertingValueSource", valueSourceInputType, "String", conversion)}
+            def valueSource = providers.of(ConvertingValueSource) {
+                parameters.input = $valueSourceInput
+            }
+            ${serviceTaskImpl("printValue", injectionAnnotation, "String", "value.get()", "value = valueSource")}
+
+            tasks.named("jar") { dependsOn("printValue") }
+        """)
+
+        when:
+        configurationCacheFails()
+
+        then:
+        outputContains("Configuration cache entry discarded with 1 problem.")
+        problems.assertFailureHasProblems(failure) {
+            totalProblemsCount = 1
+            // TODO(mlopatkin): Is it possible to figure out a correct location? Report falls back to "Gradle runtime"
+            withProblem("Unknown location: " +
+                "cannot serialize BuildServiceProvider of service 'ConstantBuildService' with name 'constant' used at configuration time as these are not supported with the configuration cache.")
+            problemsWithStackTraceCount = 0
+        }
+
+        where:
+        injectionAnnotation | valueSourceInputType   | valueSourceInput                   | conversion
+        Input.name          | "ConstantBuildService" | "serviceProvider"                  | "parameters.input.get().value"
+        Internal.name       | "ConstantBuildService" | "serviceProvider"                  | "parameters.input.get().value"
+        Input.name          | "String"               | "serviceProvider.map { it.value} " | "parameters.input.get()"
+        Internal.name       | "String"               | "serviceProvider.map { it.value} " | "parameters.input.get()"
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/22337")
+    def "build service cannot be used in ValueSource if it is obtained at configuration time"() {
+        given:
+        buildScript("""
+            ${constantServiceImpl("ConstantBuildService", "constant")}
+
+            ${convertingValueSourceImpl("ConvertingValueSource", "ConstantBuildService", "String", "parameters.input.get().value")}
+            def valueSource = providers.of(ConvertingValueSource) {
+                parameters.input = gradle.sharedServices.registerIfAbsent("constant", ConstantBuildService) {}
+            }
+
+            println(valueSource.get())
+        """)
+
+        when:
+        configurationCacheFails()
+
+        then:
+        outputContains("Configuration cache entry discarded with 1 problem.")
+        problems.assertFailureHasProblems(failure) {
+            totalProblemsCount = 1
+            withProblem("Build file 'build.gradle': " +
+                "cannot serialize BuildServiceProvider of service 'ConstantBuildService' with name 'constant' used at configuration time as these are not supported with the configuration cache.")
+            problemsWithStackTraceCount = 0
+        }
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/22337")
+    def "build service can be used in ValueSource if it is used as task input with #injectionAnnotation"() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+
+        buildScript("""
+            ${constantServiceImpl("ConstantBuildService", "constant")}
+
+            ${convertingValueSourceImpl("ConvertingValueSource", "ConstantBuildService", "String", "parameters.input.get().value")}
+            def valueSource = providers.of(ConvertingValueSource) {
+                parameters.input = gradle.sharedServices.registerIfAbsent("constant", ConstantBuildService) {}
+            }
+
+            ${serviceTaskImpl("printValue", injectionAnnotation, "String", "value.get()", "value = valueSource")}
+        """)
+
+        when:
+        configurationCacheRun("printValue")
+
+        then:
+        // Note that load-after-store doesn't check build fingerprint
+        configurationCache.assertStateStored()
+
+        when:
+        configurationCacheRun("printValue")
+
+        then: "Verify that fingerprint check works"
+        configurationCache.assertStateLoaded()
+
+        where:
+        injectionAnnotation << [Internal.name, Input.name]
+    }
+
+    static String constantServiceImpl(String serviceClassName, String value) {
+        """
+        abstract class $serviceClassName implements ${BuildService.name}<${BuildServiceParameters.name}.None>{
+            String getValue() {
+                return "$value"
+            }
+        }
+        """
+    }
+
+    static String serviceTaskImpl(String name, String injectionAnnotation, String propertyType, String valueGetter, String taskConfiguration) {
+        """
+        abstract class ServiceTask extends DefaultTask {
+            @$injectionAnnotation
+            abstract Property<$propertyType> getValue()
+
+            @TaskAction def action()  { println("ServiceTask value = \${$valueGetter}") }
+        }
+
+        tasks.register("$name", ServiceTask) {
+            $taskConfiguration
+        }
+        """
+    }
+
+    static String convertingValueSourceImpl(String valueSourceClassName, String inputType, String returnType, String conversion) {
+        """
+        abstract class $valueSourceClassName implements ${ValueSource.name}<$returnType, Params> {
+            interface Params extends ${ValueSourceParameters.name} {
+                Property<$inputType> getInput()
+            }
+
+            @Override $returnType obtain() {
+                return $conversion
+            }
+        }
+        """
     }
 
     @Issue("https://github.com/gradle/gradle/issues/23700")
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheDependencyResolutionFeaturesIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheDependencyResolutionFeaturesIntegrationTest.groovy
index f3b32c7..b02bf20 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheDependencyResolutionFeaturesIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheDependencyResolutionFeaturesIntegrationTest.groovy
@@ -608,6 +608,24 @@
         new MavenLocalRepo() | _
     }
 
+    def "disables configuration cache when --export-keys is used"() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+
+        when:
+        configurationCacheRun("help")
+
+        then:
+        configurationCache.assertStateStored()
+
+        when:
+        configurationCacheRun("help", "--export-keys")
+
+        then:
+        configurationCache.assertNoConfigurationCache()
+        outputContains("Calculating task graph as configuration cache cannot be reused due to --export-keys")
+    }
+
     def "invalidates configuration cache when dependency lock file changes"() {
         server.start()
         def v3 = remoteRepo.module("thing", "lib", "1.3").publish()
@@ -730,7 +748,6 @@
         configurationCacheRun("resolve1", "--write-verification-metadata", "sha256")
 
         then:
-        configurationCache.assertStateStored()
         def verificationFile = file("gradle/verification-metadata.xml")
         verificationFile.isFile()
 
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheDirIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheDirIntegrationTest.groovy
index 145bfc0..7d6eabb 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheDirIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheDirIntegrationTest.groovy
@@ -54,4 +54,27 @@
         then:
         configurationCache.assertStateLoaded()
     }
+
+    def "configuration cache honours org.gradle.projectcachedir"() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+
+        when:
+        configurationCacheRun 'help', '-Dorg.gradle.projectcachedir=custom-cache-dir'
+
+        then:
+        !file('.gradle/configuration-cache').exists()
+
+        and:
+        file('custom-cache-dir/configuration-cache').isDirectory()
+
+        and:
+        configurationCache.assertStateStored()
+
+        when:
+        configurationCacheRun 'help', '-Dorg.gradle.projectcachedir=custom-cache-dir'
+
+        then:
+        configurationCache.assertStateLoaded()
+    }
 }
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 5cedb56..f574145 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
@@ -19,6 +19,8 @@
 import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheOption
 import org.gradle.integtests.fixtures.configurationcache.ConfigurationCacheFixture
 
+import static org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheProblemsOption.Value.WARN
+
 
 class ConfigurationCacheEnablementIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
     ConfigurationCacheFixture fixture = new ConfigurationCacheFixture(this)
@@ -98,4 +100,45 @@
         "long option"     | DISABLE_CLI_OPT
         "system property" | DISABLE_SYS_PROP
     }
+
+    def "can enable configuration cache using incubating and final property variants"() {
+        given:
+        buildFile """
+        task check {
+            assert gradle.startParameter.configurationCache.get() == ${ccOn}
+            assert ${maxProblems == _ } || gradle.startParameter.configurationCacheMaxProblems == ${maxProblems}
+            assert ${problemsAs == _ } || gradle.startParameter.configurationCacheProblems.name() == "${problemsAs}"
+            assert ${quiet == _ } || gradle.startParameter.configurationCacheQuiet == ${quiet}
+            assert ${debug == _ } || gradle.startParameter.configurationCacheDebug == ${debug}
+            assert ${recreate == _ } || gradle.startParameter.configurationCacheRecreateCache == ${recreate}
+        }
+        """
+
+        when:
+        run(task, *options)
+
+        then:
+        if (ccOn) {
+            result.assertHasPostBuildOutput("Configuration cache entry stored.")
+        }
+
+        where:
+        task    | ccOn  | problemsAs| maxProblems   | quiet | recreate  | debug | options
+        "check" | false | WARN      | _             | _     | _         | _     | ["-Dorg.gradle.configuration-cache.problems=warn"]
+        "check" | false | _         | 100           | _     | _         | _     | ["-Dorg.gradle.configuration-cache.max-problems=100"]
+        "check" | false | _         | _             | true  | _         | _     | ["-Dorg.gradle.configuration-cache.internal.quiet=true"]
+        "check" | false | _         | _             | _     | true      | _     | ["-Dorg.gradle.configuration-cache.internal.recreate-cache=true"]
+        "check" | false | _         | _             | _     | _         | true  | ["-Dorg.gradle.configuration-cache.internal.debug=true"]
+        "check" | false | WARN      | _             | _     | _         | _     | ["-Dorg.gradle.unsafe.configuration-cache-problems=warn"]
+        "check" | false | _         | 100           | _     | _         | _     | ["-Dorg.gradle.unsafe.configuration-cache.max-problems=100"]
+        "check" | false | _         | _             | true  | _         | _     | ["-Dorg.gradle.unsafe.configuration-cache.quiet=true"]
+        "check" | false | _         | _             | _     | true      | _     | ["-Dorg.gradle.unsafe.configuration-cache.recreate-cache=true"]
+        "check" | false | _         | _             | _     | _         | true  | ["-Dorg.gradle.unsafe.configuration-cache.debug=true"]
+        "check" | true  | _         | _             | _     | _         | _     | ["--configuration-cache"]
+        "check" | true  | _         | _             | _     | _         | _     | ["-Dorg.gradle.configuration-cache=true"]
+        "check" | true  | _         | _             | _     | _         | _     | ["-Dorg.gradle.unsafe.configuration-cache=true"]
+        "check" | false | _         | _             | _     | _         | _     | ["--no-configuration-cache"]
+        "check" | false | _         | _             | _     | _         | _     | ["-Dorg.gradle.configuration-cache=false"]
+        "check" | false | _         | _             | _     | _         | _     | ["-Dorg.gradle.unsafe.configuration-cache=false"]
+    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheEncryptionIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheEncryptionIntegrationTest.groovy
new file mode 100644
index 0000000..8d6dfd1
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheEncryptionIntegrationTest.groovy
@@ -0,0 +1,225 @@
+/*
+ * 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 com.google.common.primitives.Bytes
+import groovy.transform.EqualsAndHashCode
+import groovy.transform.ToString
+import org.gradle.internal.nativeintegration.filesystem.FileSystem
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+import org.gradle.testfixtures.internal.NativeServicesTestFixture
+
+import java.nio.file.FileVisitOption
+import java.nio.file.Files
+import java.nio.file.Path
+import java.security.KeyStore
+import java.util.stream.Stream
+
+import static org.gradle.initialization.IGradlePropertiesLoader.ENV_PROJECT_PROPERTIES_PREFIX
+
+class ConfigurationCacheEncryptionIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
+    TestFile keyStoreDir
+
+    def setup() {
+        keyStoreDir = new TestFile(testDirectory, 'keystores')
+    }
+
+    def "configuration cache can be loaded without errors using #encryptionTransformation"() {
+        given:
+        def additionalOpts = [
+            "-Dorg.gradle.configuration-cache.internal.encryption-alg=${encryptionTransformation}"
+        ]
+        def configurationCache = newConfigurationCacheFixture()
+        runWithEncryption(true, ["help"], additionalOpts)
+
+        when:
+        runWithEncryption(true, ["help"], additionalOpts)
+
+        then:
+        configurationCache.assertStateLoaded()
+
+        where:
+        _ | encryptionTransformation
+        _ | "AES/ECB/PKCS5PADDING"
+        _ | "AES/CBC/PKCS5PADDING"
+    }
+
+    def "configuration cache is #encrypted if enabled=#enabled"() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+        buildFile """
+            @${EqualsAndHashCode.name}
+            @${ToString.name}
+            class SensitiveData {
+                String sensitiveField
+            }
+            class SensitiveTask extends DefaultTask {
+                @Internal
+                List<Object> values = new ArrayList()
+                @Input
+                final Property<String> sensitiveInput1 = project.objects.property(String).convention("sensitive_convention")
+                @Input
+                final Property<String> sensitiveInput2 = project.objects.property(String).convention(sensitiveInput1)
+            }
+            tasks.register("useSensitive", SensitiveTask) {
+                it.values += [
+                    "sensitive_value1",
+                    new SensitiveData(sensitiveField: "sensitive_value2"),
+                    sensitive_property_name,
+                    sensitive_property_name2,
+                    System.getenv("SENSITIVE_ENV_VAR_NAME")
+                ]
+                it.sensitiveInput2.set("sensitive_value3")
+            }
+            tasks.withType(SensitiveTask).configureEach {
+                doLast {
+                    println("Running \${name}")
+                    assert it.values == [
+                        "sensitive_value1",
+                        new SensitiveData(sensitiveField: "sensitive_value2"),
+                        "sensitive_property_value",
+                        "sensitive_property_value2",
+                        "sensitive_env_var_value"
+                    ]
+                    assert it.sensitiveInput1.get() == "sensitive_convention"
+                    assert it.sensitiveInput2.get() == "sensitive_value3"
+                }
+            }
+        """
+        when:
+        runWithEncryption(enabled, ["useSensitive"], ["-Psensitive_property_name=sensitive_property_value"], [
+            (ENV_PROJECT_PROPERTIES_PREFIX + 'sensitive_property_name2'): 'sensitive_property_value2',
+            "SENSITIVE_ENV_VAR_NAME": 'sensitive_env_var_value'
+        ])
+
+        then:
+        configurationCache.assertStateStored()
+        def cacheDir = new File(this.testDirectory, ".gradle/configuration-cache")
+        isFoundInDirectory(cacheDir, "sensitive_property_name".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive_property_value".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive_property_name2".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive_property_value2".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive_value1".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive_value2".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive_env_var_value".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive_value3".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive_convention".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "sensitive".getBytes()) == !enabled
+        isFoundInDirectory(cacheDir, "SENSITIVE".getBytes()) == !enabled
+        where:
+        encrypted     | enabled
+        "encrypted"   | true
+        "unencrypted" | false
+    }
+
+    private boolean isFoundInDirectory(File startDir, byte[] toFind) {
+        try (Stream<Path> tree = Files.walk(startDir.toPath(), FileVisitOption.FOLLOW_LINKS)) {
+            return tree.filter { it.toFile().file }
+                .anyMatch {
+                    isSubArray(Files.readAllBytes(it), toFind)
+                }
+        }
+    }
+
+    def "new configuration cache entry if keystore is not found"() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+        runWithEncryption()
+        findKeystoreFile().delete()
+
+        when:
+        runWithEncryption()
+
+        then:
+        configurationCache.assertStateStored()
+        outputContains("Calculating task graph as no configuration cache is available for tasks: help")
+    }
+
+    def "new configuration cache entry if key is not found"() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+        runWithEncryption()
+
+        and:
+        def keyStoreFile = findKeystoreFile()
+
+        KeyStore ks = KeyStore.getInstance(KeyStoreKeySource.KEYSTORE_TYPE)
+        keyStoreFile.withInputStream { ks.load(it, new char[]{'c', 'c'}) }
+        ks.deleteEntry("gradle-secret")
+        keyStoreFile.withOutputStream { ks.store(it, new char[]{'c', 'c'}) }
+
+        when:
+        runWithEncryption()
+
+        then:
+        configurationCache.assertStateStored()
+        outputContains("Calculating task graph as no configuration cache is available for tasks: help")
+    }
+
+    @Requires(UnitTestPreconditions.NotWindows)
+    def "build fails if keystore cannot be created"() {
+        given:
+        def fs = NativeServicesTestFixture.instance.get(FileSystem)
+        assert keyStoreDir.mkdir()
+        fs.chmod(keyStoreDir, 0444)
+
+        when:
+        fails(*(["help", "--configuration-cache"] + encryptionOptions))
+
+        then:
+        failureDescriptionStartsWith "Error loading encryption key from custom Java keystore at ${keyStoreDir}"
+
+        cleanup:
+        fs.chmod(keyStoreDir, 0666)
+    }
+
+    void runWithEncryption(
+        boolean enabled = true,
+        List<String> tasks = ["help"],
+        List<String> additionalArgs = [],
+        Map<String, String> envVars = [:]
+    ) {
+        def allArgs = tasks + getEncryptionOptions(enabled) + additionalArgs
+        executer.withEnvironmentVars(envVars)
+        configurationCacheRun(*allArgs)
+    }
+
+    private List<String> getEncryptionOptions(boolean enabled = true) {
+        if (!enabled) {
+            return [
+                "-Dorg.gradle.configuration-cache.internal.encryption=false"
+            ]
+        }
+        return [
+            '-s',
+            "-Dorg.gradle.configuration-cache.internal.key-store-dir=${keyStoreDir}",
+        ]
+    }
+
+    private boolean isSubArray(byte[] contents, byte[] toFind) {
+        Bytes.indexOf(contents, toFind) >= 0
+    }
+
+    private TestFile findKeystoreFile() {
+        def keyStoreDirFiles = keyStoreDir.allDescendants()
+        def keyStorePath = keyStoreDirFiles.find { it.endsWith('gradle.keystore') }
+        assert keyStorePath != null
+        keyStoreDir.file(keyStorePath)
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheFeatureFlagsIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheFeatureFlagsIntegrationTest.groovy
index f86942c..4ca7aeb 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheFeatureFlagsIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheFeatureFlagsIntegrationTest.groovy
@@ -17,39 +17,30 @@
 package org.gradle.configurationcache
 
 import org.gradle.api.internal.FeaturePreviews
-import org.gradle.configurationcache.fixtures.ExternalProcessFixture
-
-import static org.gradle.configurationcache.fixtures.ExternalProcessFixture.exec
+import org.gradle.internal.buildoption.FeatureFlags
 
 class ConfigurationCacheFeatureFlagsIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
     def "toggling feature flag with system property invalidates cache"() {
         given:
         def configurationCache = newConfigurationCacheFixture()
-        ExternalProcessFixture execFixture = new ExternalProcessFixture(testDirectory)
 
-        def snippets = exec().groovy.newSnippets(execFixture)
-
-        buildFile("""
-            ${snippets.imports}
-
-            ${snippets.body}
-
+        and: 'feature flag access at configuration time'
+        buildFile """
+            gradle.services.get($FeatureFlags.name).isEnabled(${FeaturePreviews.name}.Feature.STABLE_CONFIGURATION_CACHE)
             task check {}
-        """)
+        """
 
         when:
-        configurationCacheRun("check")
+        configurationCacheRunLenient "check"
 
         then:
         configurationCache.assertStateStored()
 
         when:
-        configurationCacheFails("-D${FeaturePreviews.Feature.STABLE_CONFIGURATION_CACHE.systemPropertyName}=true", "check")
+        configurationCacheRunLenient "-D${FeaturePreviews.Feature.STABLE_CONFIGURATION_CACHE.systemPropertyName}=true", "check"
 
         then:
-        problems.assertFailureHasProblems(failure) {
-            withProblem("Build file 'build.gradle': external process started")
-        }
+        configurationCache.assertStateStored()
     }
 
     def "feature flag state is restored when running from cache"() {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheFlowScopeIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheFlowScopeIntegrationTest.groovy
index 5f4223b..0ebec12 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheFlowScopeIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheFlowScopeIntegrationTest.groovy
@@ -23,7 +23,108 @@
 
 class ConfigurationCacheFlowScopeIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
 
-    def '#target #injectionStyle with #parameter can react to task execution result'() {
+    def 'flow actions are isolated from each other'() {
+        given: 'flow actions that share a bean'
+        buildFile '''
+            import org.gradle.api.flow.*
+            import org.gradle.api.services.*
+            import java.util.concurrent.atomic.AtomicInteger
+
+            class FlowActionPlugin implements Plugin<Project> {
+                final FlowScope flowScope
+                final FlowProviders flowProviders
+                @Inject FlowActionPlugin(FlowScope flowScope, FlowProviders flowProviders) {
+                    this.flowScope = flowScope
+                    this.flowProviders = flowProviders
+                }
+                void apply(Project target) {
+                    def sharedBean = new Bean()
+                    def sharedBeanProvider = flowProviders.buildWorkResult.map { sharedBean }
+                    2.times {
+                        flowScope.always(IncrementAndPrint) {
+                            parameters.bean = sharedBeanProvider
+                        }
+                    }
+                }
+            }
+
+            class Bean {
+                AtomicInteger value = new AtomicInteger(41)
+            }
+
+            class IncrementAndPrint implements FlowAction<Parameters> {
+                interface Parameters extends FlowParameters {
+                    @Input Property<Bean> getBean()
+                }
+                void execute(Parameters parameters) {
+                    parameters.with {
+                        println("Bean.value = " + bean.get().value.incrementAndGet())
+                    }
+                }
+            }
+
+            apply type: FlowActionPlugin
+        '''
+
+        when:
+        configurationCacheRun 'help'
+
+        then: 'shared bean should have been isolated'
+        output.count('Bean.value = 42') == 2
+        outputDoesNotContain 'Bean.value = 43'
+    }
+
+    def 'flow actions cannot depend on tasks'() {
+        given:
+        buildFile '''
+            import org.gradle.api.flow.*
+            import org.gradle.api.services.*
+            import org.gradle.api.tasks.*
+
+            class FlowActionPlugin implements Plugin<Project> {
+                final FlowScope flowScope
+                final FlowProviders flowProviders
+                @Inject FlowActionPlugin(FlowScope flowScope, FlowProviders flowProviders) {
+                    this.flowScope = flowScope
+                    this.flowProviders = flowProviders
+                }
+                void apply(Project target) {
+                    def producer = target.tasks.register('producer', Producer) {
+                        outputFile = target.layout.buildDirectory.file('out')
+                    }
+                    flowScope.always(PrintAction) {
+                        parameters.text = producer.flatMap { it.outputFile }.map { it.asFile.text }
+                    }
+                }
+            }
+
+            abstract class Producer extends DefaultTask {
+                @OutputFile abstract RegularFileProperty getOutputFile()
+                @TaskAction def produce() {
+                    outputFile.get().asFile << "42"
+                }
+            }
+
+            class PrintAction implements FlowAction<Parameters> {
+                interface Parameters extends FlowParameters {
+                    @Input Property<String> getText()
+                }
+                void execute(Parameters parameters) {
+                    println(parameters.text.get())
+                }
+            }
+
+            apply type: FlowActionPlugin
+        '''
+
+        when:
+        configurationCacheFails 'producer'
+
+        then:
+        failureCauseContains "Property 'text' cannot carry a dependency on task ':producer' as these are not yet supported."
+    }
+
+    def '#target #injectionStyle with #parameter can react to build work result'() {
         given:
         def configCache = newConfigurationCacheFixture()
 
@@ -166,7 +267,7 @@
 
                 void apply($targetType target) {
                     flowScope.always(SetLavaLampColor) {
-                        parameters.color = flowProviders.requestedTasksResult.map {
+                        parameters.color = flowProviders.buildWorkResult.map {
                             it.failure.present ? 'red' : 'green'
                         }
                     }
@@ -177,7 +278,7 @@
 
                 interface Parameters extends FlowParameters {
                     @ServiceReference("lamp") Property<LavaLamp> getLamp()
-                    Property<String> getColor()
+                    @Input Property<String> getColor()
                 }
 
                 void execute(Parameters parameters) {
@@ -296,7 +397,7 @@
 
                 void apply($targetType target) {
                     flowScope.always(SetLavaLampColor) {
-                        parameters.color = flowProviders.requestedTasksResult.map {
+                        parameters.color = flowProviders.buildWorkResult.map {
                             it.failure.present ? 'red' : 'green'
                         }
                     }
@@ -378,7 +479,7 @@
                     def lamp = target.gradle.sharedServices.registerIfAbsent('lamp', LavaLamp) {}
                     flowScope.always(SetLavaLampColor) {
                         ${namedAnnotation ? '' : 'parameters.lamp = lamp'}
-                        parameters.color = flowProviders.requestedTasksResult.map {
+                        parameters.color = flowProviders.buildWorkResult.map {
                             it.failure.present ? 'red' : 'green'
                         }
                     }
@@ -427,4 +528,74 @@
     private TestFile scriptFileFor(ScriptTarget target) {
         file(target.fileName)
     }
+
+    def "task cannot depend on buildWorkResult, fails with clear message and problem is reported"() {
+        given:
+        buildFile '''
+            abstract class Fails extends DefaultTask {
+
+                @Input abstract Property<String> getColor()
+
+                @TaskAction void wontRun() {
+                    assert false
+                }
+            }
+
+            abstract class FailsPlugin implements Plugin<Project> {
+
+                @Inject abstract FlowProviders getFlowProviders()
+
+                void apply(Project target) {
+                    target.tasks.register('fails', Fails)  {
+                        color = flowProviders.buildWorkResult.map {
+                            it.failure.present ? 'red' : 'green'
+                        }
+                    }
+                }
+            }
+
+            apply type: FailsPlugin
+        '''
+
+        when:
+        configurationCacheFails 'fails'
+
+        then:
+        failureHasCause "Failed to calculate the value of task ':fails' property 'color'."
+        failureHasCause "Cannot access the value of 'BuildWorkResult' before it becomes available!"
+        failureDescriptionStartsWith "Configuration cache problems found in this build"
+    }
+
+    def "value source with build work result provider cannot be obtained at configuration time"() {
+        given:
+        buildFile("""
+        import org.gradle.api.provider.*
+
+        abstract class ResultSource implements ValueSource<String, Params> {
+            interface Params extends ValueSourceParameters {
+                Property<BuildWorkResult> getWorkResult();
+            }
+
+            @Override String obtain() {
+                return "I'm not using my parameter"
+            }
+        }
+
+        interface FlowProvidersGetter {
+            @Inject FlowProviders getFlowProviders()
+        }
+
+        def flowProviders = objects.newInstance(FlowProvidersGetter).flowProviders
+
+        providers.of(ResultSource) {
+            parameters.workResult = flowProviders.buildWorkResult
+        }.get()
+
+        """)
+
+        expect:
+        configurationCacheFails()
+        // TODO(mlopatkin) The error message can be improved.
+        failureHasCause(~/Could not isolate value ResultSource(.*) of type ResultSource.Params/)
+    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheGradlePropertiesIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheGradlePropertiesIntegrationTest.groovy
index cad45da..5bca97d 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheGradlePropertiesIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheGradlePropertiesIntegrationTest.groovy
@@ -16,14 +16,14 @@
 
 package org.gradle.configurationcache
 
-
+import org.gradle.configurationcache.fixtures.GradlePropertiesIncludedBuildFixture
 import org.gradle.configurationcache.fixtures.SystemPropertiesCompositeBuildFixture
-import org.gradle.util.internal.ToBeImplemented
 import spock.lang.Issue
 
 import static org.gradle.initialization.IGradlePropertiesLoader.ENV_PROJECT_PROPERTIES_PREFIX
 import static org.gradle.initialization.IGradlePropertiesLoader.SYSTEM_PROJECT_PROPERTIES_PREFIX
 
+@org.gradle.test.fixtures.Flaky(because='https://github.com/gradle/gradle-private/issues/3859')
 class ConfigurationCacheGradlePropertiesIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
 
     def "invalidates cache when set of Gradle property defining system properties changes"() {
@@ -152,54 +152,28 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/19793")
-    @ToBeImplemented("Fails with 'GradleProperties has not been loaded yet.' accessing gradle properties on a warm cache")
-    def "gradle properties must be accessible from task in included build"() {
+    def "gradle properties must be accessible from task in #build"() {
         given:
         def configurationCache = newConfigurationCacheFixture()
-        createDir('included1') {
-            file('gradle.properties') << """
-                P1=foo
-                P2=zoo
-            """
-            groovyFile file("build.gradle"), """
-            tasks.register('includedTask') {
-                def p1 = providers.gradleProperty("P1")
-                doLast {
-                    println("P1=\${p1.get()}")
-                }
-            }
-            """
-        }
-        file('settings.gradle') << """
-            includeBuild('included1')
-        """
-        groovyFile file("build.gradle"), """
-        tasks.register('delegatingTask') {
-            dependsOn gradle.includedBuild('included1').task(':includedTask')
-        }
-        """
+        build.setup(this)
 
         when:
-        configurationCacheRun "delegatingTask"
+        configurationCacheRun build.task()
 
         then:
         configurationCache.assertStateStored()
-        outputContains """
-> Task :included1:includedTask
-P1=foo
-
-> Task :delegatingTask
-        """
+        outputContains build.expectedPropertyOutput()
 
         when:
-        configurationCacheFails "delegatingTask"
+        configurationCacheRun build.task()
 
         then:
         configurationCache.assertStateLoaded()
-        outputContains """
-> Task :included1:includedTask FAILED
-"""
-        failureCauseContains("GradleProperties has not been loaded yet.")
+        outputContains build.expectedPropertyOutput()
+        outputDoesNotContain "GradleProperties has not been loaded yet."
+
+        where:
+        build << GradlePropertiesIncludedBuildFixture.builds()
     }
 
     @Issue("https://github.com/gradle/gradle/issues/19184")
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheIntegrationTest.groovy
index c6beca1..20a2ef1 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheIntegrationTest.groovy
@@ -24,6 +24,18 @@
 
 class ConfigurationCacheIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
 
+    def "configuration cache is out of incubation"() {
+        given:
+        settingsFile << ""
+
+        when:
+        run("help", "--configuration-cache")
+
+        then:
+        result.assertHasPostBuildOutput("Configuration cache entry stored.")
+        !output.contains("Configuration cache is an incubating feature.")
+    }
+
     def "configuration cache for Help plugin task '#task' on empty project"() {
         given:
         settingsFile.createFile()
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheJavaIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheJavaIntegrationTest.groovy
index 17f6d88..e72c410 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheJavaIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheJavaIntegrationTest.groovy
@@ -565,7 +565,7 @@
             }
 
             val manifestClasspath by configurations.creating {
-                isCanBeResolved = true
+                assert(isCanBeResolved)
                 isCanBeConsumed = false
                 isTransitive = false
                 attributes {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheProblemReportingIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheProblemReportingIntegrationTest.groovy
index e19497d..d9bc9c5 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheProblemReportingIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheProblemReportingIntegrationTest.groovy
@@ -783,19 +783,71 @@
         "Gradle.addBuildListener"                     | "gradle.addBuildListener(new BuildAdapter())"
         "Gradle.addListener"                          | "gradle.addListener(new BuildAdapter())"
         "Gradle.buildFinished"                        | "gradle.buildFinished {}"
+        "Gradle.useLogger"                            | "gradle.useLogger(new TaskExecutionAdapter())"
         "TaskExecutionGraph.addTaskExecutionListener" | "gradle.taskGraph.addTaskExecutionListener(new TaskExecutionAdapter())"
         "TaskExecutionGraph.beforeTask"               | "gradle.taskGraph.beforeTask {}"
         "TaskExecutionGraph.afterTask"                | "gradle.taskGraph.afterTask {}"
     }
 
+    def "reports unsupported build listener #listenerType registration on #registrationPoint"() {
+        given:
+        buildFile("""
+            class TestAdapter implements TestListener {
+                @Override void beforeSuite(TestDescriptor suite) {}
+                @Override void afterSuite(TestDescriptor suite, TestResult result) {}
+                @Override void beforeTest(TestDescriptor testDescriptor) {}
+                @Override void afterTest(TestDescriptor testDescriptor, TestResult result) {}
+            }
+
+            class TestOutputAdapter implements TestOutputListener {
+                @Override void onOutput(TestDescriptor testDescriptor, TestOutputEvent outputEvent) {}
+            }
+
+            gradle.$registrationPoint(new ${listenerType}())
+        """)
+
+        expect:
+        executer.noDeprecationChecks()
+        configurationCacheFails 'help'
+
+        where:
+        [registrationPoint, listenerType] << [
+            ["addListener", "useLogger"],
+            ["BuildAdapter", "TaskExecutionAdapter", "TestAdapter", "TestOutputAdapter"]
+        ].combinations()
+    }
+
+    def "reports registration of unsupported build listener implementing supported listener too on #registrationPoint"() {
+        given:
+        buildFile("""
+            class SneakyListener extends TaskExecutionAdapter implements TaskExecutionGraphListener {
+                @Override void graphPopulated(TaskExecutionGraph graph) {}
+            }
+
+            gradle.$registrationPoint(new SneakyListener())
+        """)
+
+        expect:
+        executer.noDeprecationChecks()
+        configurationCacheFails 'help'
+
+        where:
+        registrationPoint << ["addListener", "useLogger"]
+    }
+
     def "does not report problems on configuration listener registration on #registrationPoint"() {
 
         given:
         buildFile << """
 
             class ProjectEvaluationAdapter implements ProjectEvaluationListener {
-                void beforeEvaluate(Project project) {}
-                void afterEvaluate(Project project, ProjectState state) {}
+                @Override void beforeEvaluate(Project project) {}
+                @Override void afterEvaluate(Project project, ProjectState state) {}
+            }
+
+            class DependencyResolutionAdapter implements DependencyResolutionListener {
+                @Override void beforeResolve(ResolvableDependencies dependencies) {}
+                @Override void afterResolve(ResolvableDependencies dependencies) {}
             }
 
             $code
@@ -806,15 +858,21 @@
         postBuildOutputContains("Configuration cache entry stored.")
 
         where:
-        registrationPoint                     | code
-        "Gradle.addProjectEvaluationListener" | "gradle.addProjectEvaluationListener(new ProjectEvaluationAdapter())"
-        "Gradle.addListener"                  | "gradle.addListener(new ProjectEvaluationAdapter())"
-        "Gradle.beforeSettings"               | "gradle.beforeSettings {}"
-        "Gradle.settingsEvaluated"            | "gradle.settingsEvaluated {}"
-        "Gradle.projectsLoaded"               | "gradle.projectsLoaded {}"
-        "Gradle.beforeProject"                | "gradle.beforeProject {}"
-        "Gradle.afterProject"                 | "gradle.afterProject {}"
-        "Gradle.projectsEvaluated"            | "gradle.projectsEvaluated {}"
+        registrationPoint                                  | code
+        "Gradle.addProjectEvaluationListener"              | "gradle.addProjectEvaluationListener(new ProjectEvaluationAdapter())"
+        "Gradle.addListener(ProjectEvaluationListener)"    | "gradle.addListener(new ProjectEvaluationAdapter())"
+        "Gradle.addListener(TaskExecutionGraphListener)"   | "gradle.addListener({g -> } as TaskExecutionGraphListener)"
+        "Gradle.addListener(DependencyResolutionListener)" | "gradle.addListener(new DependencyResolutionAdapter())"
+        "Gradle.beforeSettings"                            | "gradle.beforeSettings {}"
+        "Gradle.settingsEvaluated"                         | "gradle.settingsEvaluated {}"
+        "Gradle.projectsLoaded"                            | "gradle.projectsLoaded {}"
+        "Gradle.beforeProject"                             | "gradle.beforeProject {}"
+        "Gradle.afterProject"                              | "gradle.afterProject {}"
+        "Gradle.projectsEvaluated"                         | "gradle.projectsEvaluated {}"
+        "Gradle.taskGraph.whenReady"                       | "gradle.taskGraph.whenReady {}"
+        "Gradle.useLogger(ProjectEvaluationListener)"      | "gradle.useLogger(new ProjectEvaluationAdapter())"
+        "Gradle.useLogger(TaskExecutionGraphListener)"     | "gradle.useLogger({g -> } as TaskExecutionGraphListener)"
+        "Gradle.useLogger(DependencyResolutionListener)"   | "gradle.useLogger(new DependencyResolutionAdapter())"
     }
 
     def "summarizes unsupported properties"() {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheProjectLayoutIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheProjectLayoutIntegrationTest.groovy
new file mode 100644
index 0000000..7370f3f
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheProjectLayoutIntegrationTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 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
+
+class ConfigurationCacheProjectLayoutIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
+
+    def "buildDirectory #desc derived from task provider is compatible"() {
+        given:
+        buildFile """
+            abstract class Producer extends DefaultTask {
+
+                @OutputFile
+                abstract RegularFileProperty getDirName()
+
+                @TaskAction
+                def produce() {
+                    dirName.get().asFile.text = 'dir'
+                }
+            }
+
+            abstract class Consumer extends DefaultTask {
+
+                @OutputFile
+                abstract RegularFileProperty getOutputFile()
+
+                @TaskAction
+                def consume() {
+                    outputFile.get().asFile.write('42')
+                }
+            }
+
+            def computeDirName = tasks.register('producer', Producer) {
+                dirName = layout.buildDirectory.file('dirName.txt')
+            }
+
+            tasks.register('answer', Consumer) {
+                // TODO:configuration should this `dependsOn` be necessary here?
+                dependsOn 'producer'
+
+                Provider<String> computedDirName = computeDirName.flatMap { it.dirName }.map { it.asFile.text }
+                def file = layout.buildDirectory.$provider
+                outputFile = file
+                doLast {
+                    println(file.get().asFile.text)
+                }
+            }
+        """
+
+        when:
+        configurationCacheRun 'answer'
+
+        then:
+        '42' == file('build/dir/answer.txt').text
+        outputContains '42'
+
+        where:
+        desc   | provider
+        'dir'  | 'dir(computedDirName).map { it.file("answer.txt") }'
+        'file' | 'file(computedDirName.map { "$it/answer.txt" })'
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheReportIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheReportIntegrationTest.groovy
index 3b1edec..6974b9c 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheReportIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheReportIntegrationTest.groovy
@@ -20,8 +20,9 @@
 import com.microsoft.playwright.BrowserContext
 import com.microsoft.playwright.Page
 import com.microsoft.playwright.Playwright
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.fixtures.Flaky
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import java.util.function.Consumer
 
@@ -31,7 +32,8 @@
 // dependencies on Linux and times out downloading the driver on MacOS.
 //
 // Comment out the @Requires annotation below to run the test locally on non Windows platforms.
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
+@Flaky(because = "https://github.com/gradle/gradle-private/issues/3820")
 class ConfigurationCacheReportIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
 
     def "report with problem loads successfully"() {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheSkipCacheIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheSkipCacheIntegrationTest.groovy
index 42bda07..2ca6511 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheSkipCacheIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheSkipCacheIntegrationTest.groovy
@@ -34,8 +34,8 @@
             }
             tasks.register("myTask", MyTask) {
                 // use an undeclared input so we can test --refresh-dependencies
-                // File.bytes is not tracked for now; we'll have to change it once we start to track it
-                message.set(new String(new File("message").bytes))
+                // URL.text is not tracked for now; we'll have to change it once we start to track it
+                message.set(file("message").toPath().toUri().toURL().text)
             }
         """
         file("message") << "foo"
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheStableConfigurationCacheIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheStableConfigurationCacheIntegrationTest.groovy
index b97fd29..c1c5cfb 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheStableConfigurationCacheIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheStableConfigurationCacheIntegrationTest.groovy
@@ -19,19 +19,53 @@
 import org.gradle.configurationcache.fixtures.ExternalProcessFixture
 
 class ConfigurationCacheStableConfigurationCacheIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
-    def "external processes at the configuration time do not fail build if the flag is not enabled"() {
+
+    def setup() {
+        settingsFile '''
+            enableFeaturePreview 'STABLE_CONFIGURATION_CACHE'
+        '''
+    }
+
+    def 'external processes at configuration time are reported as problems'() {
         given:
         def snippets = ExternalProcessFixture.processBuilder().groovy.newSnippets(new ExternalProcessFixture(testDirectory))
 
-        buildFile("""
+        buildFile """
             ${snippets.imports}
             ${snippets.body}
-        """)
+        """
 
         when:
-        configurationCacheRun(":help")
+        configurationCacheFails ":help"
 
         then:
-        outputContains("Hello")
+        problems.assertFailureHasProblems(failure) {
+            withProblem "Build file 'build.gradle': external process started"
+        }
+    }
+
+    def 'project access at execution time is either a problem or a deprecation'() {
+        given:
+        buildFile '''
+            tasks.register('problematic') { doLast { println project.name } }
+        '''
+
+        when:
+        if (withCC) {
+            configurationCacheFails ':problematic'
+        } else {
+            executer.expectDocumentedDeprecationWarning "Invocation of Task.project at execution time has been deprecated. This will fail with an error in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_7.html#task_project"
+            succeeds ':problematic'
+        }
+
+        then:
+        if (withCC) {
+            problems.assertFailureHasProblems(failure) {
+                withProblem "Build file 'build.gradle': line 2: invocation of 'Task.project' at execution time is unsupported."
+            }
+        }
+
+        where:
+        withCC << [true, false]
     }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheSupportedTypesIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheSupportedTypesIntegrationTest.groovy
index 50450a4..7bd0066 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheSupportedTypesIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheSupportedTypesIntegrationTest.groovy
@@ -27,6 +27,7 @@
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
 import org.gradle.workers.WorkerExecutor
 import org.slf4j.Logger
+import spock.lang.Issue
 
 import javax.inject.Inject
 import java.util.logging.Level
@@ -505,4 +506,46 @@
         'an insecure uri'  | 'fromInsecureUri(project.uri(project.file("resource.txt")))'
         'an archive entry' | 'fromArchiveEntry("resource.zip", "resource.txt")'
     }
+
+    @Issue('https://github.com/gradle/gradle/issues/22255')
+    def "finalizeValueOnRead property provider is evaluated only once"() {
+        given:
+        buildFile << """
+            class Oracle extends DefaultTask {
+
+                @Internal final Property<String> answer
+
+                Oracle() {
+                    answer = project.objects.property(String)
+                    answer.finalizeValueOnRead()
+                    answer.set(
+                        project.provider {
+                            println 'Thinking...'
+                            '42'
+                        }
+                    )
+                }
+
+                @TaskAction
+                def answer() {
+                    println('The answer is ' + answer.get())
+                }
+            }
+
+            tasks.register('oracle', Oracle)
+        """
+
+        when:
+        configurationCacheRun 'oracle'
+
+        then:
+        output.count('Thinking...') == 1
+
+        when:
+        configurationCacheRun 'oracle'
+
+        then:
+        output.count('Thinking...') == 0
+        outputContains 'The answer is 42'
+    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTaskExecutionIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTaskExecutionIntegrationTest.groovy
index 593b5fb..b9604f3 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTaskExecutionIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTaskExecutionIntegrationTest.groovy
@@ -16,7 +16,62 @@
 
 package org.gradle.configurationcache
 
+import spock.lang.Issue
+
 class ConfigurationCacheTaskExecutionIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
+
+    @Issue("https://github.com/gradle/gradle/issues/24411")
+    def "task marked not compatible may cause configuration of other tasks without failing build"() {
+        given:
+        buildFile("""
+            tasks.register("innocent") { task ->
+                // This task calls getProject() as part of its configuration.
+                println("Materialized task \${task.name} from project \${task.project.name}")
+            }
+
+            tasks.register("offender") {
+                notCompatibleWithConfigurationCache("calls getProject")
+                // This task reaches out to other task at execution time and triggers its configuration.
+                doLast { task ->
+                    println(task.project.tasks.named("innocent").get())
+                }
+            }
+        """)
+
+        when:
+        configurationCacheRun("offender")
+
+        then:
+        problems.assertResultHasProblems(result) {
+            withProblemsWithStackTraceCount(2)
+            withProblem("Build file 'build.gradle': line 11: invocation of 'Task.project' at execution time is unsupported.")
+            withProblem("Build file 'build.gradle': line 4: execution of task ':offender' caused invocation of 'Task.project' in other task at execution time which is unsupported.")
+        }
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/22522')
+    def "reports problem for extra property accessed at execution time"() {
+        given:
+        buildKotlinFile '''
+            tasks.register("report") {
+                extra["outputFile"] = file("$buildDir/output.txt")
+                outputs.files(extra["outputFile"])
+                doLast {
+                    val outputFile: File by extra
+                    outputFile.writeText("")
+                }
+            }
+        '''
+
+        when:
+        configurationCacheFails(WARN_PROBLEMS_CLI_OPT, 'report')
+
+        then:
+        problems.assertResultHasProblems(failure) {
+            withProblem "Task `:report` of type `org.gradle.api.DefaultTask`: invocation of 'Task.extensions' at execution time is unsupported."
+        }
+    }
+
     def "honors task up-to-date spec"() {
         buildFile << """
             abstract class TaskWithComplexInputs extends DefaultTask {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTaskRealizationBuildOperationIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTaskRealizationBuildOperationIntegrationTest.groovy
new file mode 100644
index 0000000..031bd71
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTaskRealizationBuildOperationIntegrationTest.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 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 com.google.common.collect.Iterables
+import org.gradle.api.internal.tasks.RealizeTaskBuildOperationType
+import org.gradle.integtests.fixtures.BuildOperationsFixture
+
+class ConfigurationCacheTaskRealizationBuildOperationIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
+    def buildOperations = new BuildOperationsFixture(executer, testDirectoryProvider)
+
+    def "load after store emits two realization operations with the same id"() {
+        buildFile << """
+            tasks.register("foo")
+        """
+
+        when:
+        configurationCacheRun(":foo")
+        then:
+        def realizeOps = buildOperations.all(RealizeTaskBuildOperationType)
+        realizeOps.size() == 2
+        realizeOps*.details.each {
+            assert it.taskPath == ":foo"
+        }
+        realizeOps.first().details.eager == false
+        realizeOps.last().details.eager == true
+        def uniqueId = Iterables.getOnlyElement(realizeOps*.details*.taskId as Set)
+
+        when:
+        configurationCacheRun(":foo")
+        then:
+        def realizeOp = buildOperations.only(RealizeTaskBuildOperationType)
+        with(realizeOp.details) {
+            taskPath == ":foo"
+            taskId == uniqueId
+            eager == true
+        }
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTestkitIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTestkitIntegrationTest.groovy
index 5847de9..af33184 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTestkitIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTestkitIntegrationTest.groovy
@@ -17,11 +17,11 @@
 package org.gradle.configurationcache
 
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testkit.runner.GradleRunner
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ConfigurationCacheTestkitIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
     def "reports when a TestKit build runs with a Java agent and configuration caching enabled"() {
         def builder = artifactBuilder()
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTransformNodeIdBuildOperationIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTransformNodeIdBuildOperationIntegrationTest.groovy
new file mode 100644
index 0000000..443130a
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheTransformNodeIdBuildOperationIntegrationTest.groovy
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2023 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 com.google.common.collect.Iterables
+import org.gradle.integtests.fixtures.BuildOperationsFixture
+import org.gradle.integtests.resolve.transform.ArtifactTransformTestFixture
+import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType
+import org.gradle.internal.taskgraph.NodeIdentity
+
+class ConfigurationCacheTransformNodeIdBuildOperationIntegrationTest extends AbstractConfigurationCacheIntegrationTest implements ArtifactTransformTestFixture {
+
+    def buildOperations = new BuildOperationsFixture(executer, testDirectoryProvider)
+
+    def setup() {
+        // So that dependency resolution results from previous executions do not interfere
+        requireOwnGradleUserHomeDir()
+    }
+
+    def "transform step node ids are stable when using load after store"() {
+        settingsFile << """
+            include 'producer', 'consumer'
+        """
+
+        setupBuildWithColorAttributes()
+        buildFile << """
+            allprojects {
+                dependencies {
+                    registerTransform(MakeColor) {
+                        from.attribute(color, 'blue')
+                        to.attribute(color, 'red')
+                        parameters.targetColor.set('red')
+                        parameters.multiplier.set(2)
+                    }
+                    registerTransform(MakeColor) {
+                        from.attribute(color, 'red')
+                        to.attribute(color, 'green')
+                        parameters.targetColor.set('green')
+                        parameters.multiplier.set(1)
+                    }
+                }
+            }
+
+            interface TargetColor extends TransformParameters {
+                @Input
+                Property<String> getTargetColor()
+                @Input
+                Property<Integer> getMultiplier()
+            }
+
+            abstract class MakeColor implements TransformAction<TargetColor> {
+                @InputArtifact
+                abstract Provider<FileSystemLocation> getInputArtifact()
+
+                void transform(TransformOutputs outputs) {
+                    def input = inputArtifact.get().asFile
+                    println "processing [\${input.name}]"
+                    assert input.file
+                    for (def i : 1..parameters.multiplier.get()) {
+                        def output = outputs.file(input.name + "." + parameters.targetColor.get() + "-" + i)
+                        output.text = input.text + "-" + parameters.targetColor.get() + "-" + i
+                    }
+                }
+            }
+        """
+
+        buildFile << """
+            project(":consumer") {
+                dependencies {
+                    implementation project(":producer")
+                }
+            }
+        """
+
+        when:
+        configurationCacheRun ":consumer:resolve"
+
+        then:
+        executedAndNotSkipped(":consumer:resolve")
+        Map<String, Long> uniqueIdsFromStore = uniqueIdsPerColor
+
+        when:
+        configurationCacheRun ":consumer:resolve"
+        then:
+        uniqueIdsPerColor == uniqueIdsFromStore
+    }
+
+    private Map<String, Long> getUniqueIdsPerColor() {
+        transformStepIdentities.collectEntries { color, identities ->
+            def nodeIds = (identities*.transformStepNodeId as Set<Long>)
+            [(color): Iterables.getOnlyElement(nodeIds)]
+        }
+    }
+
+    private Map<String, List<Map<String, ?>>> getTransformStepIdentities() {
+        def ops = buildOperations.all(CalculateTaskGraphBuildOperationType)
+        def identities = ops.collect { op ->
+            def plannedNodes = op.result.executionPlan as List<Map<String, ?>>
+            def transformStepNodeIdentities = plannedNodes*.nodeIdentity.findAll { it.nodeType == NodeIdentity.NodeType.TRANSFORM_STEP.name() }
+            return transformStepNodeIdentities
+        }
+        def targetColors = (identities.collect { it*.targetAttributes.color }.flatten() as Set<String>)
+        return targetColors.collectEntries { color ->
+            [(color): identities.collect {plannedTransformSteps ->
+                Iterables.getOnlyElement(plannedTransformSteps.findAll { it.targetAttributes.color == color }) as Map<String, ?>
+            }]
+        }
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheUnsupportedTypesIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheUnsupportedTypesIntegrationTest.groovy
index e6c040a..43d9066 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheUnsupportedTypesIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheUnsupportedTypesIntegrationTest.groovy
@@ -51,6 +51,7 @@
 import org.gradle.api.attributes.DisambiguationRuleChain
 import org.gradle.api.file.SourceDirectorySet
 import org.gradle.api.initialization.Settings
+import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.artifacts.DefaultDependencyConstraintSet
 import org.gradle.api.internal.artifacts.DefaultDependencySet
 import org.gradle.api.internal.artifacts.DefaultResolvedDependency
@@ -82,6 +83,7 @@
 import org.gradle.api.internal.attributes.DefaultDisambiguationRuleChain
 import org.gradle.api.internal.file.DefaultSourceDirectorySet
 import org.gradle.api.internal.project.DefaultProject
+import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.tasks.DefaultSourceSet
 import org.gradle.api.internal.tasks.DefaultSourceSetContainer
 import org.gradle.api.internal.tasks.DefaultTaskContainer
@@ -95,6 +97,7 @@
 import org.gradle.initialization.DefaultSettings
 import org.gradle.internal.locking.DefaultDependencyLockingHandler
 import org.gradle.invocation.DefaultGradle
+import spock.lang.Shared
 
 import java.util.concurrent.Executor
 import java.util.concurrent.Executors.DefaultThreadFactory
@@ -104,6 +107,81 @@
 
 class ConfigurationCacheUnsupportedTypesIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
 
+    @Shared
+    private def disallowedServiceTypesAtExecution = [Project, ProjectInternal, Gradle, GradleInternal]
+
+    def "reports in task when injected service of #serviceType accessed at execution time"() {
+        given:
+        buildFile << """
+            abstract class Foo extends DefaultTask {
+
+                @Inject
+                public abstract ${serviceType.name} getInjected()
+
+                @TaskAction
+                void action() {
+                    println(getInjected())
+                }
+            }
+
+            tasks.register('foo', Foo)
+        """
+
+        when:
+        configurationCacheRunLenient "foo"
+
+        then:
+        problems.assertResultHasProblems(result) {
+            withTotalProblemsCount(1)
+            withUniqueProblems(
+                "Class `Foo`: accessing non-serializable type '${serviceType.name}'"
+            )
+        }
+
+        where:
+        serviceType << disallowedServiceTypesAtExecution
+    }
+
+    def "reports in plugin when service of #serviceType accessed at execution time"() {
+        given:
+        buildFile << """
+            abstract class MyPlugin implements Plugin<Project> {
+
+                @Inject
+                public abstract ${serviceType.name} getInjected()
+
+                void apply(Project target) {
+                    registerTask(this, target)
+                }
+
+                private void registerTask(MyPlugin plugin, Project project) {
+                    project.tasks.register("foo") {
+                        doFirst {
+                            println(plugin.getInjected())
+                        }
+                    }
+                }
+            }
+
+            apply plugin: MyPlugin
+        """
+
+        when:
+        configurationCacheRunLenient "foo"
+
+        then:
+        problems.assertResultHasProblems(result) {
+            withTotalProblemsCount(1)
+            withUniqueProblems(
+                "Plugin class 'MyPlugin': accessing non-serializable type '${serviceType.name}'"
+            )
+        }
+
+        where:
+        serviceType << disallowedServiceTypesAtExecution
+    }
+
+
     def "reports when task field references an object of type #baseType"() {
         buildFile << """
             plugins { id "java" }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheValueSourceIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheValueSourceIntegrationTest.groovy
index d4b1ff9..01a138b 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheValueSourceIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/ConfigurationCacheValueSourceIntegrationTest.groovy
@@ -206,6 +206,102 @@
         configurationCache.assertStateStored()
     }
 
+    def "exception thrown from ValueSource becomes problem if the exception is #exceptionHandlerDescritpion"() {
+        given:
+        buildFile("""
+            import org.gradle.api.provider.*
+
+            abstract class BrokenValueSource implements ValueSource<String, ValueSourceParameters.None>, Describable {
+                @Override String obtain() { throw new RuntimeException("Broken!") }
+                @Override String getDisplayName() { "some name" }
+            }
+
+            try {
+                providers.of(BrokenValueSource) {}.get()
+            } catch (Throwable ex) {
+                $exceptionHandlerImpl
+            }
+        """)
+
+        when:
+        configurationCacheFails()
+
+        then:
+        outputContains("Configuration cache entry discarded with 1 problem.")
+        failure.assertHasFailures(expectedFailuresCount)
+        problems.assertFailureHasProblems(failure) {
+            totalProblemsCount == 1
+            problemsWithStackTraceCount == 1
+            withProblem("Build file 'build.gradle': line 5: failed to compute value with custom source 'BrokenValueSource' (some name) with java.lang.RuntimeException: Broken!")
+        }
+
+        where:
+        exceptionHandlerDescritpion | exceptionHandlerImpl | expectedFailuresCount
+        "rethrown"                  | "throw ex"           | 2  // The original exception propagates and fails the build, and configuration cache problem is reported too.
+        "ignored"                   | "// ignored"         | 1  // Only the configuration cache problem is reported
+    }
+
+    def "exception thrown from ValueSource when populating the cache invalidates the entry upon fingerprint check "() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+        buildFile("""
+            import org.gradle.api.provider.*
+
+            abstract class BrokenValueSource implements ValueSource<String, ValueSourceParameters.None> {
+                @Override String obtain() { throw new RuntimeException("Broken!") }
+            }
+
+            try {
+                providers.of(BrokenValueSource) {}.get()
+            } catch (Throwable ignored) {
+            }
+        """)
+
+        when:
+        configurationCacheRunLenient()
+
+        then:
+        configurationCache.assertStateStored()
+
+        when:
+        configurationCacheRunLenient()
+
+        then:
+        configurationCache.assertStateStored()
+        outputContains("configuration cache cannot be reused because a build logic input of type 'BrokenValueSource' failed when storing the entry with java.lang.RuntimeException: Broken!.")
+    }
+
+    def "exception thrown from ValueSource when computing its value for fingerprint checking fails the build"() {
+        given:
+        def configurationCache = newConfigurationCacheFixture()
+        buildFile("""
+            import org.gradle.api.provider.*
+
+            abstract class SometimesBrokenValueSource implements ValueSource<String, ValueSourceParameters.None> {
+                @Override String obtain() {
+                    if (Boolean.getBoolean("should.fail")) {
+                        throw new RuntimeException("Broken!")
+                    }
+                    return "not broken"
+                }
+            }
+
+            providers.of(SometimesBrokenValueSource) {}.get()
+        """)
+
+        when:
+        configurationCacheRun()
+
+        then:
+        configurationCache.assertStateStored()
+
+        when:
+        configurationCacheFails("-Dshould.fail=true")
+
+        then:
+        failure.assertHasDescription("Broken!")
+    }
+
     def "ValueSource can use #accessor without making it an input"() {
         given:
         def configurationCache = newConfigurationCacheFixture()
@@ -341,9 +437,6 @@
         def configurationCache = newConfigurationCacheFixture()
         ShellScript testScript = ShellScript.builder().printText("Hello, world").writeTo(testDirectory, "script")
 
-        settingsFile("""
-            enableFeaturePreview("STABLE_CONFIGURATION_CACHE")
-        """)
         buildFile.text = """
             import ${ByteArrayOutputStream.name}
             import org.gradle.api.provider.*
@@ -524,4 +617,28 @@
         "systemProperty" | "-D"
         "gradleProperty" | "-P"
     }
+
+    def "value source with non-serializable output"() {
+        buildFile("""
+        import org.gradle.api.provider.*
+
+        abstract class BrokenSource implements ValueSource<Thread, ValueSourceParameters.None> {
+            @Override Thread obtain() {
+                return new Thread()
+            }
+        }
+
+        providers.of(BrokenSource) {}.get()
+        """)
+
+        when:
+        configurationCacheFails()
+
+        then:
+        problems.assertFailureHasProblems(failure) {
+            totalProblemsCount = 1
+            withProblem("Build file 'build.gradle': cannot serialize object of type 'java.lang.Thread', a subtype of 'java.lang.Thread', as these are not supported with the configuration cache.")
+            problemsWithStackTraceCount = 0
+        }
+    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/GradlePropertiesIncludedBuildFixture.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/GradlePropertiesIncludedBuildFixture.groovy
new file mode 100644
index 0000000..98b4916
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/GradlePropertiesIncludedBuildFixture.groovy
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2023 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
+
+class GradlePropertiesIncludedBuildFixture {
+
+    static List<BuildWithGradleProperties> builds() {
+        [new BuildSrc(), new IncludedBuild()]
+    }
+
+    static abstract class BuildWithGradleProperties {
+
+        abstract void setup(AbstractIntegrationSpec spec)
+
+        abstract String task()
+
+        abstract String expectedPropertyOutput()
+
+        protected static String echoTaskForProperty(String propertyName) {
+            """
+                task echo(type: DefaultTask) {
+                    def property = providers.gradleProperty('$propertyName')
+                        doFirst {
+                            println("$propertyName: " + property.orNull)
+                        }
+                    }
+            """
+        }
+    }
+
+    static class BuildSrc extends BuildWithGradleProperties {
+
+        @Override
+        void setup(AbstractIntegrationSpec spec) {
+            spec.file("buildSrc/gradle.properties") << "bar=101"
+            spec.file("buildSrc/build.gradle") << echoTaskForProperty("bar")
+        }
+
+        @Override
+        String task() {
+            return ":buildSrc:echo"
+        }
+
+        @Override
+        String expectedPropertyOutput() {
+            return "bar: 101"
+        }
+
+        @Override
+        String toString() {
+            return "buildSrc"
+        }
+    }
+
+    static class IncludedBuild extends BuildWithGradleProperties {
+
+        @Override
+        void setup(AbstractIntegrationSpec spec) {
+            spec.file("included-build/gradle.properties") << "bar=101"
+            spec.file("included-build/build.gradle") << echoTaskForProperty("bar")
+            spec.settingsFile("includeBuild('included-build')")
+        }
+
+        @Override
+        String task() {
+            return ":included-build:echo"
+        }
+
+        @Override
+        String expectedPropertyOutput() {
+            return "bar: 101"
+        }
+
+        @Override
+        String toString() {
+            return "included build"
+        }
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/ToolingApiSpec.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/ToolingApiSpec.groovy
index 343678e..4f11a85 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/ToolingApiSpec.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/ToolingApiSpec.groovy
@@ -29,6 +29,7 @@
 import org.gradle.tooling.BuildAction
 import org.gradle.tooling.BuildActionExecuter
 import org.gradle.tooling.BuildException
+import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.provider.model.ToolingModelBuilder
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
 
@@ -225,23 +226,22 @@
     def <T, S> Pair<T, S> runPhasedBuildAction(BuildAction<T> projectsLoadedAction, BuildAction<S> modelAction, @DelegatesTo(BuildActionExecuter) Closure config = {}) {
         T projectsLoadedModel = null
         S buildModel = null
-        result = toolingApiExecutor.runBuildWithToolingConnection { connection ->
+        result = toolingApiExecutor.runBuildWithToolingConnection { ProjectConnection connection ->
             def output = new ByteArrayOutputStream()
             def error = new ByteArrayOutputStream()
             def args = executer.allArgs
             args.remove("--no-daemon")
 
-            def builder = connection.action()
+
+            def actionExecuter = connection.action()
                 .projectsLoaded(projectsLoadedAction, { Object model ->
                     projectsLoadedModel = model
-                })
-                .buildFinished(modelAction, { Object model ->
+                }).buildFinished(modelAction, { Object model ->
                     buildModel = model
-                })
-                .build()
-            config.delegate = builder
+                }).build()
+            config.delegate = actionExecuter
             config.call()
-            builder
+            actionExecuter
                 .withArguments(args)
                 .setStandardOutput(new TeeOutputStream(output, System.out))
                 .setStandardError(new TeeOutputStream(error, System.err))
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/TransformFixture.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/TransformFixture.groovy
new file mode 100644
index 0000000..e6df6a0
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/fixtures/TransformFixture.groovy
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2023 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.api.DefaultTask
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.transform.InputArtifact
+import org.gradle.api.artifacts.transform.TransformAction
+import org.gradle.api.artifacts.transform.TransformOutputs
+import org.gradle.api.artifacts.transform.TransformParameters
+import org.gradle.api.artifacts.type.ArtifactTypeDefinition
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.FileSystemLocation
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.gradle.test.fixtures.file.TestFile
+
+import java.nio.file.Files
+import java.util.stream.Collectors
+
+/**
+ * A helper to define a plugin that applies a simple transform action to dependencies.
+ */
+class TransformFixture {
+    static final String TRANSFORM_PLUGIN_ID = "org.gradle.test.transform"
+    static final String TRANSFORM_SOURCE_CONFIGURATION = "transformSource"
+
+    private final String additionalTransformImports;
+    private final String additionalTransformFields;
+    private final String additionalTransformImpl;
+
+    TransformFixture(String additionalTransformImports, String additionalTransformFields, String additionalTransformImpl) {
+        this.additionalTransformImports = additionalTransformImports
+        this.additionalTransformFields = additionalTransformFields
+        this.additionalTransformImpl = additionalTransformImpl
+    }
+
+    def withTransformPlugin(TestFile pluginDirectory) {
+        pluginDirectory.create {
+            file("build.gradle") << """
+                    plugins {
+                        id "java-gradle-plugin"
+                    }
+
+                    gradlePlugin {
+                        plugins {
+                            transformPlugin {
+                                id = "$TRANSFORM_PLUGIN_ID"
+                                implementationClass = "TransformPlugin"
+                            }
+                        }
+                    }
+                """
+            file("src/main/java/TransformPlugin.java") << """
+                import ${Plugin.name};
+                import ${Project.name};
+                import ${Configuration.name};
+                import ${FileSystemLocation.name};
+                import ${Collectors.name};
+                import static ${ArtifactTypeDefinition.name}.ARTIFACT_TYPE_ATTRIBUTE;
+
+                public class TransformPlugin implements Plugin<Project> {
+                    private static final String JAR_TYPE = "${ArtifactTypeDefinition.JAR_TYPE}";
+                    private static final String TXT_TYPE = "txt";
+
+                    @Override
+                    public void apply(Project project) {
+                        registerTransform(project);
+                        Configuration base = createBaseConfiguration(project);
+                        Configuration transformed = createTransformedConfiguration(project, base);
+                        registerFileCollectionTask(project, transformed);
+                        registerProviderTask(project, transformed);
+                    }
+
+                    private void registerTransform(Project project) {
+                        project.getDependencies().registerTransform(FileSizeTransform.class, spec -> {
+                            spec.getFrom().attribute(ARTIFACT_TYPE_ATTRIBUTE, JAR_TYPE);
+                            spec.getTo().attribute(ARTIFACT_TYPE_ATTRIBUTE, TXT_TYPE);
+                        });
+                    }
+
+                    private Configuration createBaseConfiguration(Project project) {
+                        return project.getConfigurations().create("$TRANSFORM_SOURCE_CONFIGURATION", cfg -> {
+                            cfg.setCanBeConsumed(false);
+                            cfg.setCanBeResolved(true);
+                        });
+                    }
+
+                    private Configuration createTransformedConfiguration(Project project, Configuration base) {
+                        return project.getConfigurations().create("transformed", transformed -> {
+                            transformed.setCanBeConsumed(false);
+                            transformed.setCanBeResolved(true);
+                            transformed.extendsFrom(base);
+
+                            transformed.getAttributes().attribute(ARTIFACT_TYPE_ATTRIBUTE, TXT_TYPE);
+                        });
+                    }
+
+                    private void registerFileCollectionTask(Project project, Configuration transformed) {
+                        project.getTasks().register("resolveCollection", ResolveTask.class, task -> {
+                            task.getInputSizes().setFrom(transformed);
+                        });
+                    }
+
+                    private void registerProviderTask(Project project, Configuration transformed) {
+                        project.getTasks().register("resolveProviders", ResolveProviderTask.class, task -> {
+                            task.getInputFiles().set(
+                                    // Mapped provider is evaluated differently when storing into the configuration cache.
+                                    // This is similar to what AGP does when performing JdkImageTransform
+                                    transformed.getElements().map(
+                                            locations -> locations.stream()
+                                                    .map(FileSystemLocation::getAsFile)
+                                                    .collect(Collectors.toList())
+                                    )
+                            );
+                        });
+                    }
+                }
+            """
+            file("src/main/java/ResolveTask.java") << """
+                import ${DefaultTask.name};
+                import ${ConfigurableFileCollection.name};
+                import ${InputFiles.name};
+                import ${PathSensitive.name};
+                import ${PathSensitivity.name};
+                import ${TaskAction.name};
+
+                public abstract class ResolveTask extends DefaultTask {
+                    @InputFiles
+                    @PathSensitive(PathSensitivity.NONE)
+                    public abstract ConfigurableFileCollection getInputSizes();
+
+                    @TaskAction
+                    public void action() {
+                        getInputSizes().getFiles().forEach(file -> {
+                            System.out.println(file.getName());
+                        });
+                    }
+                }
+            """
+            file("src/main/java/ResolveProviderTask.java") << """
+                import ${DefaultTask.name};
+                import ${ListProperty.name};
+                import ${InputFiles.name};
+                import ${PathSensitive.name};
+                import ${PathSensitivity.name};
+                import ${TaskAction.name};
+                import ${File.name};
+
+                public abstract class ResolveProviderTask extends DefaultTask {
+                    @InputFiles
+                    @PathSensitive(PathSensitivity.NAME_ONLY)
+                    public abstract ListProperty<File> getInputFiles();
+
+                    @TaskAction
+                    public void action() {
+                        getInputFiles().get().forEach(file -> System.out.println(file.getName()));
+                    }
+                }
+            """
+
+            file("src/main/java/FileSizeTransform.java") << """
+                import ${InputArtifact.name};
+                import ${TransformAction.name};
+                import ${TransformOutputs.name};
+                import ${TransformParameters.name};
+                import ${FileSystemLocation.name};
+                import ${Provider.name};
+
+                import ${File.name};
+                import ${Files.name};
+                import ${Collections.name};
+
+                $additionalTransformImports
+
+                public abstract class FileSizeTransform implements TransformAction<TransformParameters.None> {
+                    @InputArtifact
+                    public abstract Provider<FileSystemLocation> getInputArtifact();
+
+                    $additionalTransformFields
+
+                    @Override
+                    public void transform(TransformOutputs outputs) {
+                        File input = getInputArtifact().get().getAsFile();
+                        File output = outputs.file(input.getName() + ".txt");
+
+                        try {
+                            Files.write(output.toPath(), Collections.singleton(String.valueOf(input.length())));
+                        } catch (Exception e) {
+                            throw new RuntimeException(e);
+                        }
+
+                        $additionalTransformImpl
+                    }
+                }
+            """
+        }
+    }
+
+    def withJavaLibrarySubproject(TestFile projectDir) {
+        projectDir.create {
+            file("build.gradle") << """
+                plugins { id("java-library") }
+            """
+
+            file("src/main/java/Foo.java") << """
+                public class Foo {}
+            """
+        }
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/AbstractProcessIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/AbstractProcessIntegrationTest.groovy
index dc3e34a..f196260 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/AbstractProcessIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/AbstractProcessIntegrationTest.groovy
@@ -18,19 +18,7 @@
 
 import org.gradle.configurationcache.AbstractConfigurationCacheIntegrationTest
 import org.gradle.configurationcache.fixtures.ExternalProcessFixture
-import org.gradle.integtests.fixtures.GroovyBuildScriptLanguage
 
 abstract class AbstractProcessIntegrationTest extends AbstractConfigurationCacheIntegrationTest {
     ExternalProcessFixture execOperationsFixture = new ExternalProcessFixture(testDirectory)
-
-    def settingsFileWithStableConfigurationCache() {
-        settingsFileWithStableConfigurationCache("")
-    }
-
-    def settingsFileWithStableConfigurationCache(@GroovyBuildScriptLanguage String script) {
-        settingsFile << script
-        settingsFile << """
-            enableFeaturePreview('STABLE_CONFIGURATION_CACHE')
-        """
-    }
 }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInBuildScriptIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInBuildScriptIntegrationTest.groovy
index cb117d3..2872986 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInBuildScriptIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInBuildScriptIntegrationTest.groovy
@@ -25,7 +25,6 @@
 class ProcessInBuildScriptIntegrationTest extends AbstractProcessIntegrationTest {
     def "using #snippetsFactory.summary in #location.toLowerCase() #file is a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file(file) << """
             ${snippets.imports}
@@ -81,7 +80,6 @@
         testDirectory.file(file) << """
             ${snippets.imports}
 
-            enableFeaturePreview("STABLE_CONFIGURATION_CACHE")
             ${snippets.body}
         """
 
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInInitScriptIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInInitScriptIntegrationTest.groovy
index f5979fe..092ccb0 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInInitScriptIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInInitScriptIntegrationTest.groovy
@@ -16,7 +16,6 @@
 
 package org.gradle.configurationcache.inputs.process
 
-import spock.lang.Ignore
 
 import static org.gradle.configurationcache.fixtures.ExternalProcessFixture.exec
 import static org.gradle.configurationcache.fixtures.ExternalProcessFixture.javaexec
@@ -25,7 +24,6 @@
 import static org.gradle.configurationcache.fixtures.ExternalProcessFixture.stringArrayExecute
 
 class ProcessInInitScriptIntegrationTest extends AbstractProcessIntegrationTest {
-    @Ignore("init scripts are evaluated too early for feature flag to take effect")
     def "using #snippetsFactory.summary in initialization script #file is a problem"() {
         given:
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInPluginBuildScriptIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInPluginBuildScriptIntegrationTest.groovy
index c77f0dc..2cf3c65 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInPluginBuildScriptIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInPluginBuildScriptIntegrationTest.groovy
@@ -25,7 +25,7 @@
 class ProcessInPluginBuildScriptIntegrationTest extends AbstractProcessIntegrationTest {
     def "using #snippetsFactory.summary in included plugin settings #file is a problem"() {
         given:
-        settingsFileWithStableConfigurationCache("""
+        settingsFile("""
             pluginManagement {
                 includeBuild('included')
             }
@@ -62,7 +62,7 @@
 
     def "using #snippetsFactory.summary in included plugin build #file is a problem"() {
         given:
-        settingsFileWithStableConfigurationCache("""
+        settingsFile("""
             pluginManagement {
                 includeBuild('included')
             }
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInPluginIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInPluginIntegrationTest.groovy
index 10eb6c6..10aac02 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInPluginIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInPluginIntegrationTest.groovy
@@ -21,7 +21,6 @@
 import org.gradle.api.initialization.Settings
 import org.gradle.process.ExecOperations
 import org.gradle.test.fixtures.dsl.GradleDsl
-import spock.lang.Ignore
 
 import javax.inject.Inject
 
@@ -34,7 +33,6 @@
 class ProcessInPluginIntegrationTest extends AbstractProcessIntegrationTest {
     def "using #snippetsFactory.summary in convention plugin #file is a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file("buildSrc/build.gradle.kts") << """
             plugins {
@@ -85,7 +83,6 @@
 
     def "using #snippetsFactory.summary in java project plugin application is a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file("buildSrc/src/main/java/SneakyPlugin.java") << """
             import ${ExecOperations.name};
@@ -129,7 +126,6 @@
         runtimeExec().java                   | _
     }
 
-    @Ignore("settings plugins are applied too early for feature flag to take effect")
     def "using #snippetsFactory.summary in java settings plugin application is a problem"() {
         given:
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInTaskIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInTaskIntegrationTest.groovy
index 6c99881..ffa9d3d 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInTaskIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInTaskIntegrationTest.groovy
@@ -35,7 +35,6 @@
 class ProcessInTaskIntegrationTest extends AbstractProcessIntegrationTest {
     def "using #snippetsFactory.summary in task configuration is a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file("buildSrc/src/main/java/SneakyTask.java") << """
             import ${DefaultTask.name};
@@ -83,7 +82,6 @@
 
     def "using #snippetsFactory.summary in task action is not a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file("buildSrc/src/main/java/SneakyTask.java") << """
             import ${DefaultTask.name};
@@ -124,7 +122,6 @@
 
     def "using #snippetsFactory.summary in task action of buildSrc is not a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file("buildSrc/build.gradle") << """
             import ${DefaultTask.name};
@@ -168,7 +165,6 @@
 
     def "using #snippetsFactory.summary in worker task action of buildSrc is not a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file("buildSrc/build.gradle") << """
             import ${DefaultTask.name}
@@ -262,7 +258,7 @@
             println("Applied script plugin")
         """
 
-        settingsFileWithStableConfigurationCache("""
+        settingsFile("""
             pluginManagement {
                 includeBuild('included')
             }
@@ -291,7 +287,6 @@
 
     def "using #snippetsFactory.summary in task up-to-date is not a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file("buildSrc/src/main/java/SneakyTask.java") << """
             import ${DefaultTask.name};
@@ -337,7 +332,6 @@
 
     def "using #snippetsFactory.summary in up-to-date task of buildSrc is not a problem"() {
         given:
-        settingsFileWithStableConfigurationCache()
         def snippets = snippetsFactory.newSnippets(execOperationsFixture)
         testDirectory.file("buildSrc/build.gradle") << """
             import ${DefaultTask.name};
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInTransformIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInTransformIntegrationTest.groovy
new file mode 100644
index 0000000..352f35e
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/ProcessInTransformIntegrationTest.groovy
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2022 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.inputs.process
+
+import org.gradle.configurationcache.fixtures.ExternalProcessFixture.Snippets
+import org.gradle.configurationcache.fixtures.ExternalProcessFixture.SnippetsFactory
+import org.gradle.configurationcache.fixtures.TransformFixture
+import org.gradle.process.ExecOperations
+
+import javax.inject.Inject
+
+import static org.gradle.configurationcache.fixtures.ExternalProcessFixture.exec
+import static org.gradle.configurationcache.fixtures.ExternalProcessFixture.javaexec
+import static org.gradle.configurationcache.fixtures.ExternalProcessFixture.processBuilder
+
+class ProcessInTransformIntegrationTest extends AbstractProcessIntegrationTest {
+    def "using #snippetsFactory.summary in transform action with #task is not a problem"(SnippetsFactory snippetsFactory, String task) {
+        given:
+        getTransformFixture(snippetsFactory.newSnippets(execOperationsFixture)).tap {
+            withTransformPlugin(testDirectory.createDir("buildSrc"))
+            withJavaLibrarySubproject(testDirectory.createDir("subproject"))
+        }
+
+        settingsFile("""
+            include("subproject")
+        """)
+
+        buildFile("""
+            plugins { id("${TransformFixture.TRANSFORM_PLUGIN_ID}") }
+            ${mavenCentralRepository()}
+            dependencies {
+                ${TransformFixture.TRANSFORM_SOURCE_CONFIGURATION} "org.apache.commons:commons-math3:3.6.1"
+                ${TransformFixture.TRANSFORM_SOURCE_CONFIGURATION} project(":subproject")
+            }
+        """)
+
+        when:
+        configurationCacheRun(":$task")
+
+        then:
+        outputContains("Hello")
+
+        where:
+        [snippetsFactory, task] << [
+            [
+                exec("getExecOperations()").java,
+                javaexec("getExecOperations()").java,
+                processBuilder().java
+            ],
+            [
+                "resolveCollection",
+                "resolveProviders"
+            ]
+        ].combinations()
+    }
+
+    def "using #snippetsFactory.summary in transform action with #task of buildSrc build is not a problem"(SnippetsFactory snippetsFactory, String task) {
+        given:
+        createDir("buildSrc") {
+            getTransformFixture(snippetsFactory.newSnippets(execOperationsFixture)).tap {
+                withTransformPlugin(dir("transform-plugin"))
+                withJavaLibrarySubproject(dir("subproject"))
+            }
+
+            file("settings.gradle") << """
+                pluginManagement { includeBuild("transform-plugin") }
+                include("subproject")
+            """
+
+            file("build.gradle") << """
+            plugins { id("${TransformFixture.TRANSFORM_PLUGIN_ID}") }
+            ${mavenCentralRepository()}
+            dependencies {
+                ${TransformFixture.TRANSFORM_SOURCE_CONFIGURATION} "org.apache.commons:commons-math3:3.6.1"
+                ${TransformFixture.TRANSFORM_SOURCE_CONFIGURATION} project(":subproject")
+            }
+
+            tasks.named("classes") {
+                dependsOn("$task")
+            }
+            """
+        }
+
+        buildFile("""
+        tasks.register("check") {}
+        """)
+        when:
+        configurationCacheRun(":check")
+
+        then:
+        outputContains("Hello")
+
+        where:
+        [snippetsFactory, task] << [
+            [
+                exec("getExecOperations()").java,
+                javaexec("getExecOperations()").java,
+                processBuilder().java
+            ],
+            [
+                "resolveCollection",
+                "resolveProviders"
+            ]
+        ].combinations()
+    }
+
+    private TransformFixture getTransformFixture(Snippets snippets) {
+        return new TransformFixture(
+            """
+            import ${Inject.name};
+            import ${ExecOperations.name};
+            ${snippets.imports}
+            """,
+
+            """
+            @Inject
+            public abstract ExecOperations getExecOperations();
+            """,
+
+            snippets.body
+        )
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/instrument/AbstractProcessInstrumentationIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/instrument/AbstractProcessInstrumentationIntegrationTest.groovy
index 564a46f..df602f4 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/instrument/AbstractProcessInstrumentationIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/process/instrument/AbstractProcessInstrumentationIntegrationTest.groovy
@@ -30,7 +30,6 @@
 
     def setup() {
         testDirectory.createDir(pwd)
-        settingsFile("enableFeaturePreview('STABLE_CONFIGURATION_CACHE')")
     }
 
 
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/AbstractUndeclaredBuildInputsIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/AbstractUndeclaredBuildInputsIntegrationTest.groovy
index cb2b4f0..767b524 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/AbstractUndeclaredBuildInputsIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/AbstractUndeclaredBuildInputsIntegrationTest.groovy
@@ -297,10 +297,13 @@
         outputContains("because file '$testFileName' has changed")
 
         where:
-        testCase                                | fileAccess
-        "reading text with default encoding"    | (TestFile it) -> { UndeclaredFileAccess.fileText(testFilePath(it)) }
-        "reading text with customized encoding" | (TestFile it) -> { UndeclaredFileAccess.fileTextWithEncoding(testFilePath(it)) }
-        "constructing a file input stream"      | (TestFile it) -> { UndeclaredFileAccess.fileInputStreamConstructor(testFilePath(it)) }
+        testCase                                        | fileAccess
+        "reading text with default encoding"            | (TestFile it) -> { UndeclaredFileAccess.fileText(testFilePath(it)) }
+        "reading text with customized encoding"         | (TestFile it) -> { UndeclaredFileAccess.fileTextWithEncoding(testFilePath(it)) }
+        "constructing a file input stream"              | (TestFile it) -> { UndeclaredFileAccess.fileInputStreamConstructor(testFilePath(it)) }
+        "constructing a byte channel"                   | (TestFile it) -> { UndeclaredFileAccess.filesNewByteChannel(testFilePath(it)) }
+        "constructing a byte channel with open options" | (TestFile it) -> { UndeclaredFileAccess.filesNewByteChannelWithOpenOptions(testFilePath(it)) }
+        "reading lines from a file"                     | (TestFile it) -> { UndeclaredFileAccess.fileReadLines(testFilePath(it)) }
     }
 }
 
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/FilesInTransformIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/FilesInTransformIntegrationTest.groovy
new file mode 100644
index 0000000..3062a05
--- /dev/null
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/FilesInTransformIntegrationTest.groovy
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2022 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.inputs.undeclared
+
+
+import org.gradle.configurationcache.fixtures.TransformFixture
+import org.gradle.configurationcache.inputs.process.AbstractProcessIntegrationTest
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.internal.TextUtil
+
+class FilesInTransformIntegrationTest extends AbstractProcessIntegrationTest {
+    def setup() {
+        inputFile.text = "INPUT FILE CONTENT"
+    }
+
+    def "reading a file in transform action with #task does not create a build input"() {
+        given:
+        getTransformFixture().tap {
+            withTransformPlugin(testDirectory.createDir("buildSrc"))
+            withJavaLibrarySubproject(testDirectory.createDir("subproject"))
+        }
+
+        settingsFile("""
+            include("subproject")
+        """)
+
+        buildFile("""
+            plugins { id("${TransformFixture.TRANSFORM_PLUGIN_ID}") }
+            ${mavenCentralRepository()}
+            dependencies {
+                ${TransformFixture.TRANSFORM_SOURCE_CONFIGURATION} "org.apache.commons:commons-math3:3.6.1"
+                ${TransformFixture.TRANSFORM_SOURCE_CONFIGURATION} project(":subproject")
+            }
+        """)
+
+        when:
+        configurationCacheRun(":$task")
+
+        then:
+        outputContains("INPUT FILE CONTENT")
+        problems.assertResultHasProblems(result) {
+            withNoInputs()
+        }
+
+        where:
+        task << ["resolveCollection", "resolveProviders"]
+    }
+
+    def "reading a file in transform action with #task of buildSrc build does not create a build input"() {
+        given:
+        createDir("buildSrc") {
+            getTransformFixture().tap {
+                withTransformPlugin(dir("transform-plugin"))
+                withJavaLibrarySubproject(dir("subproject"))
+            }
+
+            file("settings.gradle") << """
+                pluginManagement { includeBuild("transform-plugin") }
+                include("subproject")
+            """
+
+            file("build.gradle") << """
+            plugins { id("${TransformFixture.TRANSFORM_PLUGIN_ID}") }
+            ${mavenCentralRepository()}
+            dependencies {
+                ${TransformFixture.TRANSFORM_SOURCE_CONFIGURATION} "org.apache.commons:commons-math3:3.6.1"
+                ${TransformFixture.TRANSFORM_SOURCE_CONFIGURATION} project(":subproject")
+            }
+
+            tasks.named("classes") {
+                dependsOn("$task")
+            }
+            """
+        }
+
+        buildFile("""
+        tasks.register("check") {}
+        """)
+
+        when:
+        configurationCacheRun(":check")
+
+        then:
+        outputContains("INPUT FILE CONTENT")
+        problems.assertResultHasProblems(result) {
+            withNoInputs()
+        }
+
+        where:
+        task << ["resolveCollection", "resolveProviders"]
+    }
+
+    private TransformFixture getTransformFixture() {
+        return new TransformFixture(
+            """
+            import ${BufferedReader.name};
+            import ${FileInputStream.name};
+            import ${InputStreamReader.name};
+            """,
+
+            "",
+
+            """
+            try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("${TextUtil.escapeString(inputFile.absolutePath)}")))) {
+                String line = in.readLine();
+                while (line != null) {
+                    System.out.println(line);
+                    line = in.readLine();
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+            """
+        )
+    }
+
+    private TestFile getInputFile() {
+        return testDirectory.file("test-input.txt")
+    }
+}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/GroovyPluginImplementation.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/GroovyPluginImplementation.groovy
index 124ae8c..6e35016 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/GroovyPluginImplementation.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/GroovyPluginImplementation.groovy
@@ -24,6 +24,8 @@
 trait GroovyPluginImplementation {
     void groovyDsl(TestFile sourceFile, BuildInputRead read) {
         sourceFile << """
+            ${read.requiredImports().collect { "import $it" }.join("\n")}
+
             println("apply = " + ${read.groovyExpression})
             tasks.register("thing") {
                 doLast {
@@ -38,6 +40,8 @@
             import ${Project.name}
             import ${Plugin.name}
 
+            ${read.requiredImports().collect { "import $it" }.join("\n")}
+
             class SneakyPlugin implements Plugin<Project> {
                 public void apply(Project project) {
                     def value = ${read.groovyExpression}
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildScriptBlockIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildScriptBlockIntegrationTest.groovy
index 52ce1f2..65b07c1 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildScriptBlockIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovyBuildScriptBlockIntegrationTest.groovy
@@ -25,6 +25,8 @@
     @Override
     void buildLogicApplication(BuildInputRead read) {
         buildFile << """
+            ${read.requiredImports().collect { "import $it" }.join("\n")}
+
             buildscript {
                 println("apply = " + ${read.groovyExpression})
             }
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 baa8080..e0f14ac 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,6 +16,9 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
+
+@LocalOnly(because = "https://github.com/gradle/gradle-private/issues/3821")
 class UndeclaredBuildInputsDynamicGroovyBuildSrcIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements GroovyPluginImplementation {
     @Override
     String getLocation() {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovySettingsScriptIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovySettingsScriptIntegrationTest.groovy
index d17cf6a..db78dda 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovySettingsScriptIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsDynamicGroovySettingsScriptIntegrationTest.groovy
@@ -25,6 +25,8 @@
     @Override
     void buildLogicApplication(BuildInputRead read) {
         settingsFile << """
+            ${read.requiredImports().collect { "import $it" }.join("\n")}
+
             println("apply = " + ${read.groovyExpression})
             gradle.rootProject {
                 tasks.register("thing") {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsJavaBuildSrcIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsJavaBuildSrcIntegrationTest.groovy
index cd99caa..8073422 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsJavaBuildSrcIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsJavaBuildSrcIntegrationTest.groovy
@@ -16,6 +16,9 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
+
+@LocalOnly(because = "https://github.com/gradle/gradle-private/issues/3821")
 class UndeclaredBuildInputsJavaBuildSrcIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements JavaPluginImplementation {
     @Override
     String getLocation() {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsJavaLambdaBuildSrcIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsJavaLambdaBuildSrcIntegrationTest.groovy
index 0185058..2b89746 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsJavaLambdaBuildSrcIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsJavaLambdaBuildSrcIntegrationTest.groovy
@@ -16,6 +16,9 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
+
+@LocalOnly(because = "https://github.com/gradle/gradle-private/issues/3821")
 class UndeclaredBuildInputsJavaLambdaBuildSrcIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements JavaPluginImplementation {
     @Override
     String getLocation() {
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 ef901da..9a7b648 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,6 +16,9 @@
 
 package org.gradle.configurationcache.inputs.undeclared
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
+
+@LocalOnly(because = "https://github.com/gradle/gradle-private/issues/3821")
 class UndeclaredBuildInputsStaticGroovyBuildSrcIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements GroovyPluginImplementation {
     @Override
     String getLocation() {
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsTestKitInjectedJavaPluginIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsTestKitInjectedJavaPluginIntegrationTest.groovy
index f3304ea..fb38068 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsTestKitInjectedJavaPluginIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredBuildInputsTestKitInjectedJavaPluginIntegrationTest.groovy
@@ -29,14 +29,14 @@
 import org.gradle.internal.nativeintegration.services.NativeServices
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testkit.runner.GradleRunner
 import org.gradle.testkit.runner.internal.ToolingApiGradleExecutor
-import org.gradle.util.Requires
 import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class UndeclaredBuildInputsTestKitInjectedJavaPluginIntegrationTest extends AbstractUndeclaredBuildInputsIntegrationTest implements JavaPluginImplementation {
     TestFile jar
     TestFile testKitDir
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredFileAccess.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredFileAccess.groovy
index 8322c22..fff11e9 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredFileAccess.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/inputs/undeclared/UndeclaredFileAccess.groovy
@@ -21,6 +21,7 @@
 
 import java.nio.charset.StandardCharsets
 import java.nio.file.Files
+import java.nio.file.StandardOpenOption
 import java.util.function.Supplier
 
 abstract class UndeclaredFileAccess extends BuildInputRead {
@@ -38,6 +39,7 @@
             FilenameFilter.class,
             Files.class,
             StandardCharsets.class,
+            StandardOpenOption.class,
             IOException.class,
             Supplier.class,
             FileInputStream.class
@@ -126,6 +128,86 @@
         }
     }
 
+    static UndeclaredFileAccess filesNewByteChannel(String filePath) {
+        new UndeclaredFileAccess(filePath) {
+            @Override
+            String getKotlinExpression() {
+                "Files.newByteChannel(File(\"$filePath\").toPath()).close()"
+            }
+
+            @Override
+            String getJavaExpression() {
+                """
+                ((Supplier<Object>) () -> {
+                    try {
+                        Files.newByteChannel(new File("$filePath").toPath()).close();
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                    return null;
+                }).get()"""
+            }
+
+            @Override
+            String getGroovyExpression() {
+                "{ Files.newByteChannel(new File(\"$filePath\").toPath()).close(); null }()"
+            }
+        }
+    }
+
+    static UndeclaredFileAccess filesNewByteChannelWithOpenOptions(String filePath) {
+        new UndeclaredFileAccess(filePath) {
+            @Override
+            String getKotlinExpression() {
+                "Files.newByteChannel(File(\"$filePath\").toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE).close()"
+            }
+
+            @Override
+            String getJavaExpression() {
+                """
+                ((Supplier<Object>) () -> {
+                    try {
+                        Files.newByteChannel(new File("$filePath").toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE).close();
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                    return null;
+                }).get()"""
+            }
+
+            @Override
+            String getGroovyExpression() {
+                "{ Files.newByteChannel(new File(\"$filePath\").toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE).close(); null }()"
+            }
+        }
+    }
+
+    static UndeclaredFileAccess fileReadLines(String filePath) {
+        new UndeclaredFileAccess(filePath) {
+            @Override
+            String getKotlinExpression() {
+                "File(\"$filePath\").readLines()"
+            }
+
+            @Override
+            String getJavaExpression() {
+                """((Supplier<Object>) () -> {
+                    try {
+                        return Files.readAllLines(new File("$filePath").toPath());
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                }).get()"""
+            }
+
+            @Override
+            String getGroovyExpression() {
+                // TODO test the GroovyResourceMethods and dynamic calls once supported
+                "kotlin.io.FilesKt.readLines(new File(\"$filePath\"), StandardCharsets.UTF_8)"
+            }
+        }
+    }
+
     static UndeclaredFileAccess directoryContentWithFileFilter(String directoryPath) {
         new UndeclaredFileAccess(directoryPath) {
             @Override
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsAccessFromGroovyDslIntegrationTest.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsAccessFromGroovyDslIntegrationTest.groovy
index f45b55d..c0f0a58 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsAccessFromGroovyDslIntegrationTest.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsAccessFromGroovyDslIntegrationTest.groovy
@@ -568,6 +568,18 @@
         file("sub/sub-sub/build.gradle") << """
             println(bar)
         """
+        executer.expectDocumentedDeprecationWarning(
+            "The Project.getConvention() method has been deprecated. " +
+            "This is scheduled to be removed in Gradle 9.0. " +
+            "Consult the upgrading guide for further information: " +
+            "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+        )
+        executer.expectDocumentedDeprecationWarning(
+            "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: " +
+                "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+        )
 
         when:
         configurationCacheFails(":sub:sub-sub:help")
diff --git a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsFixture.groovy b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsFixture.groovy
index 20e6019..205d2f2 100644
--- a/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsFixture.groovy
+++ b/subprojects/configuration-cache/src/integTest/groovy/org/gradle/configurationcache/isolated/IsolatedProjectsFixture.groovy
@@ -193,7 +193,6 @@
 
     private void assertHasWarningThatIncubatingFeatureUsed() {
         spec.outputContains(ConfigurationCacheFixture.ISOLATED_PROJECTS_MESSAGE)
-        spec.outputDoesNotContain(ConfigurationCacheFixture.CONFIGURATION_CACHE_MESSAGE)
         spec.outputDoesNotContain(ConfigurationCacheFixture.CONFIGURE_ON_DEMAND_MESSAGE)
     }
 
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheAwareBuildTreeWorkController.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheAwareBuildTreeWorkController.kt
index 0ff9120..c326438 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheAwareBuildTreeWorkController.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheAwareBuildTreeWorkController.kt
@@ -52,7 +52,12 @@
         }
 
         cache.finalizeCacheEntry()
-        buildRegistry.resetStateForAllBuilds()
+        buildRegistry.visitBuilds { build ->
+            build.beforeModelReset().rethrow()
+        }
+        buildRegistry.visitBuilds { build ->
+            build.resetModel()
+        }
 
         return workGraph.withNewWorkGraph { graph ->
             val finalizedGraph = cache.loadRequestedTasks(graph)
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheException.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheException.kt
index cc2e2fc..e5b9ba8 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheException.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheException.kt
@@ -51,7 +51,7 @@
     object Documentation {
 
         val maxProblems: String
-            get() = DocumentationRegistry().getDocumentationFor("configuration_cache", "config_cache:usage:max_problems")
+            get() = DocumentationRegistry().getDocumentationRecommendationFor("on this", "configuration_cache", "config_cache:usage:max_problems")
     }
 
     protected
@@ -81,7 +81,7 @@
     summary: () -> String
 ) : ConfigurationCacheProblemsException(
     "Maximum number of configuration cache problems has been reached.\n" +
-        "This behavior can be adjusted, see ${Documentation.maxProblems}.",
+        "This behavior can be adjusted. ${Documentation.maxProblems}",
     causes,
     summary
 )
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheIO.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheIO.kt
index 8bf6d91..7b3e45a 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheIO.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheIO.kt
@@ -68,6 +68,9 @@
     private
     val codecs = codecs()
 
+    private
+    val encryptionService by lazy { service<EncryptionService>() }
+
     internal
     fun writeCacheEntryDetailsTo(
         buildStateRegistry: BuildStateRegistry,
@@ -167,7 +170,7 @@
         stateFile: ConfigurationCacheStateFile,
         action: suspend DefaultReadContext.(ConfigurationCacheState) -> T
     ): T {
-        return withReadContextFor(stateFile.inputStream()) { codecs ->
+        return withReadContextFor(encryptionService.inputStream(stateFile.stateType, stateFile::inputStream)) { codecs ->
             ConfigurationCacheState(codecs, stateFile, eventEmitter, host).run {
                 action(this)
             }
@@ -179,8 +182,9 @@
         stateFile: ConfigurationCacheStateFile,
         action: suspend DefaultWriteContext.(ConfigurationCacheState) -> T
     ): T {
+
         val build = host.currentBuild
-        val (context, codecs) = writerContextFor(stateFile.outputStream(), build.gradle.owner.displayName.displayName + " state")
+        val (context, codecs) = writerContextFor(encryptionService.outputStream(stateFile.stateType, stateFile::outputStream), build.gradle.owner.displayName.displayName + " state")
         return context.useToRun {
             runWriteOperation {
                 action(ConfigurationCacheState(codecs, stateFile, eventEmitter, host))
@@ -327,6 +331,7 @@
             documentationRegistry = service(),
             javaSerializationEncodingLookup = service(),
             flowProviders = service(),
+            transformStepNodeFactory = service(),
         )
 
     private
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheKey.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheKey.kt
index 7955db5..d4dd3fb 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheKey.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheKey.kt
@@ -29,9 +29,11 @@
 
 
 @ServiceScope(Scopes.BuildTree::class)
+internal
 class ConfigurationCacheKey(
     private val startParameter: ConfigurationCacheStartParameter,
-    private val buildActionRequirements: BuildActionModelRequirements
+    private val buildActionRequirements: BuildActionModelRequirements,
+    private val encryptionConfiguration: EncryptionConfiguration
 ) {
 
     val string: String by unsafeLazy {
@@ -71,6 +73,22 @@
         }
 
         putBoolean(startParameter.isOffline)
+        putBuildScan()
+        putBoolean(encryptionConfiguration.isEncrypting)
+        putHash(encryptionConfiguration.encryptionKeyHashCode)
+    }
+
+    private
+    fun Hasher.putBuildScan() {
+        putByte(
+            startParameter.run {
+                when {
+                    isNoBuildScan -> 0
+                    isBuildScan -> 1
+                    else -> 3
+                }
+            }
+        )
     }
 
     private
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheRepository.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheRepository.kt
index 95a7c18..334b57f 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheRepository.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheRepository.kt
@@ -30,6 +30,7 @@
 import org.gradle.cache.internal.streams.DefaultValueStore
 import org.gradle.cache.internal.streams.ValueStore
 import org.gradle.cache.scopes.BuildTreeScopedCacheBuilderFactory
+import org.gradle.configurationcache.ConfigurationCacheStateStore.StateFile
 import org.gradle.configurationcache.extensions.toDefaultLowerCase
 import org.gradle.configurationcache.extensions.unsafeLazy
 import org.gradle.internal.Factory
@@ -69,9 +70,9 @@
         private val cacheDir: File,
         private val onFileAccess: (File) -> Unit
     ) : Layout() {
-        override fun fileForRead(stateType: StateType) = ReadableConfigurationCacheStateFile(cacheDir.stateFile(stateType))
+        override fun fileForRead(stateType: StateType) = ReadableConfigurationCacheStateFile(cacheDir.stateFile(stateType), stateType)
 
-        override fun fileFor(stateType: StateType): ConfigurationCacheStateFile = WriteableConfigurationCacheStateFile(cacheDir.stateFile(stateType), onFileAccess)
+        override fun fileFor(stateType: StateType): ConfigurationCacheStateFile = WriteableConfigurationCacheStateFile(cacheDir.stateFile(stateType), stateType, onFileAccess)
     }
 
     private
@@ -80,7 +81,7 @@
     ) : Layout() {
         override fun fileForRead(stateType: StateType) = fileFor(stateType)
 
-        override fun fileFor(stateType: StateType): ConfigurationCacheStateFile = ReadableConfigurationCacheStateFile(cacheDir.stateFile(stateType))
+        override fun fileFor(stateType: StateType): ConfigurationCacheStateFile = ReadableConfigurationCacheStateFile(cacheDir.stateFile(stateType), stateType)
     }
 
     override fun stop() {
@@ -89,11 +90,15 @@
 
     private
     inner class ReadableConfigurationCacheStateFile(
-        private val file: File
+        private val file: File,
+        override val stateType: StateType
     ) : ConfigurationCacheStateFile {
         override val exists: Boolean
             get() = file.isFile
 
+        override val stateFile: StateFile
+            get() = StateFile(stateType, file)
+
         override fun outputStream(): OutputStream =
             throw UnsupportedOperationException()
 
@@ -110,18 +115,23 @@
 
         override fun stateFileForIncludedBuild(build: BuildDefinition): ConfigurationCacheStateFile =
             ReadableConfigurationCacheStateFile(
-                includedBuildFileFor(file, build)
+                includedBuildFileFor(file, build),
+                stateType
             )
     }
 
     private
     inner class WriteableConfigurationCacheStateFile(
         private val file: File,
+        override val stateType: StateType,
         private val onFileAccess: (File) -> Unit
     ) : ConfigurationCacheStateFile {
         override val exists: Boolean
             get() = false
 
+        override val stateFile: StateFile
+            get() = StateFile(stateType, file)
+
         override fun outputStream(): OutputStream =
             file.also(onFileAccess).outputStream()
 
@@ -141,6 +151,7 @@
         override fun stateFileForIncludedBuild(build: BuildDefinition): ConfigurationCacheStateFile =
             WriteableConfigurationCacheStateFile(
                 includedBuildFileFor(file, build),
+                stateType,
                 onFileAccess
             )
     }
@@ -149,9 +160,10 @@
     inner class StoreImpl(
         private val baseDir: File
     ) : ConfigurationCacheStateStore {
-        override fun assignSpoolFile(stateType: StateType): File {
+        override fun assignSpoolFile(stateType: StateType): StateFile {
             Files.createDirectories(baseDir.toPath())
-            return Files.createTempFile(baseDir.toPath(), stateType.fileBaseName, ".tmp").toFile()
+            val tempFile = Files.createTempFile(baseDir.toPath(), stateType.fileBaseName, ".tmp")
+            return StateFile(stateType, tempFile.toFile())
         }
 
         override fun <T> createValueStore(stateType: StateType, writer: ValueStore.Writer<T>, reader: ValueStore.Reader<T>): ValueStore<T> {
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 ad00981..1de8b33 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
@@ -27,8 +27,11 @@
 import org.gradle.configurationcache.problems.ConfigurationCacheReport
 import org.gradle.configurationcache.serialization.beans.BeanConstructors
 import org.gradle.configurationcache.services.RemoteScriptUpToDateChecker
+import org.gradle.execution.ExecutionAccessChecker
+import org.gradle.execution.ExecutionAccessListener
 import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.internal.event.ListenerManager
+import org.gradle.internal.execution.WorkExecutionTracker
 import org.gradle.internal.resource.connector.ResourceConnectorFactory
 import org.gradle.internal.resource.connector.ResourceConnectorSpecification
 import org.gradle.internal.resource.transfer.ExternalResourceConnector
@@ -51,6 +54,7 @@
 
     override fun registerBuildTreeServices(registration: ServiceRegistration) {
         registration.run {
+            add(DefaultEncryptionService::class.java)
             add(ConfigurationCacheKey::class.java)
             add(ConfigurationCacheReport::class.java)
             add(DeprecatedFeaturesListener::class.java)
@@ -59,8 +63,10 @@
             add(ConfigurationCacheRepository::class.java)
             add(InputTrackingState::class.java)
             add(InstrumentedInputAccessListener::class.java)
+            add(InstrumentedExecutionAccessListener::class.java)
             add(ConfigurationCacheFingerprintController::class.java)
             addProvider(RemoteScriptUpToDateCheckerProvider)
+            addProvider(ExecutionAccessCheckerProvider)
         }
     }
 
@@ -106,19 +112,37 @@
     }
 
     private
+    object ExecutionAccessCheckerProvider {
+
+        fun createExecutionAccessChecker(
+            listenerManager: ListenerManager,
+            modelParameters: BuildModelParameters,
+            configurationTimeBarrier: ConfigurationTimeBarrier
+        ): ExecutionAccessChecker = when {
+            modelParameters.isConfigurationCache -> {
+                val broadcaster = listenerManager.getBroadcaster(ExecutionAccessListener::class.java)
+                ConfigurationTimeBarrierBasedExecutionAccessChecker(configurationTimeBarrier, broadcaster)
+            }
+
+            else -> DefaultExecutionAccessChecker()
+        }
+    }
+
+    private
     object TaskExecutionAccessCheckerProvider {
         fun createTaskExecutionAccessChecker(
             configurationTimeBarrier: ConfigurationTimeBarrier,
             modelParameters: BuildModelParameters,
             /** In non-CC builds, [ConfigurationCacheStartParameter] is not registered; accepting a list here is a way to ignore its absence. */
             configurationCacheStartParameter: List<ConfigurationCacheStartParameter>,
-            listenerManager: ListenerManager
+            listenerManager: ListenerManager,
+            workExecutionTracker: WorkExecutionTracker,
         ): TaskExecutionAccessChecker {
             val broadcast = listenerManager.getBroadcaster(TaskExecutionAccessListener::class.java)
             return when {
-                !modelParameters.isConfigurationCache -> TaskExecutionAccessCheckers.TaskStateBased(broadcast)
-                configurationCacheStartParameter.single().taskExecutionAccessPreStable -> TaskExecutionAccessCheckers.TaskStateBased(broadcast)
-                else -> TaskExecutionAccessCheckers.ConfigurationTimeBarrierBased(configurationTimeBarrier, broadcast)
+                !modelParameters.isConfigurationCache -> TaskExecutionAccessCheckers.TaskStateBased(broadcast, workExecutionTracker)
+                configurationCacheStartParameter.single().taskExecutionAccessPreStable -> TaskExecutionAccessCheckers.TaskStateBased(broadcast, workExecutionTracker)
+                else -> TaskExecutionAccessCheckers.ConfigurationTimeBarrierBased(configurationTimeBarrier, broadcast, workExecutionTracker)
             }
         }
     }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheState.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheState.kt
index 82f3653..45792d3 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheState.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheState.kt
@@ -20,7 +20,6 @@
 import org.gradle.api.cache.Cleanup
 import org.gradle.api.cache.MarkingStrategy
 import org.gradle.api.file.FileCollection
-import org.gradle.internal.file.FileSystemDefaultExcludesProvider
 import org.gradle.api.flow.FlowScope
 import org.gradle.api.internal.BuildDefinition
 import org.gradle.api.internal.FeaturePreviews
@@ -38,28 +37,30 @@
 import org.gradle.configurationcache.problems.DocumentationSection.NotYetImplementedSourceDependencies
 import org.gradle.configurationcache.serialization.DefaultReadContext
 import org.gradle.configurationcache.serialization.DefaultWriteContext
+import org.gradle.configurationcache.serialization.IsolateOwner
 import org.gradle.configurationcache.serialization.ReadContext
 import org.gradle.configurationcache.serialization.WriteContext
 import org.gradle.configurationcache.serialization.codecs.Codecs
 import org.gradle.configurationcache.serialization.logNotImplemented
 import org.gradle.configurationcache.serialization.readCollection
 import org.gradle.configurationcache.serialization.readEnum
-import org.gradle.configurationcache.serialization.readFile
 import org.gradle.configurationcache.serialization.readList
 import org.gradle.configurationcache.serialization.readNonNull
 import org.gradle.configurationcache.serialization.readStrings
 import org.gradle.configurationcache.serialization.withDebugFrame
 import org.gradle.configurationcache.serialization.withGradleIsolate
+import org.gradle.configurationcache.serialization.withIsolate
 import org.gradle.configurationcache.serialization.writeCollection
 import org.gradle.configurationcache.serialization.writeEnum
-import org.gradle.configurationcache.serialization.writeFile
 import org.gradle.configurationcache.serialization.writeStrings
 import org.gradle.configurationcache.services.ConfigurationCacheEnvironmentChangeTracker
 import org.gradle.execution.plan.Node
 import org.gradle.initialization.BuildIdentifiedProgressDetails
 import org.gradle.initialization.BuildStructureOperationProject
+import org.gradle.initialization.GradlePropertiesController
 import org.gradle.initialization.ProjectsIdentifiedProgressDetails
 import org.gradle.initialization.RootBuildCacheControllerSettingsProcessor
+import org.gradle.initialization.layout.BuildLayout
 import org.gradle.internal.Actions
 import org.gradle.internal.build.BuildProjectRegistry
 import org.gradle.internal.build.BuildState
@@ -74,6 +75,7 @@
 import org.gradle.internal.enterprise.core.GradleEnterprisePluginAdapter
 import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager
 import org.gradle.internal.execution.BuildOutputCleanupRegistry
+import org.gradle.internal.file.FileSystemDefaultExcludesProvider
 import org.gradle.internal.operations.BuildOperationProgressEventEmitter
 import org.gradle.plugin.management.internal.PluginRequests
 import org.gradle.util.Path
@@ -84,14 +86,37 @@
 
 
 internal
-enum class StateType {
-    Work, Model, Entry, BuildFingerprint, ProjectFingerprint, IntermediateModels, ProjectMetadata
+enum class StateType(val encryptable: Boolean = false) {
+    /**
+     * Contains the state for the entire build.
+     */
+    Work(true),
+    /**
+     * Contains the model objects sent back to the IDE in response to a TAPI request.
+     */
+    Model(true),
+    /**
+     * Contains the model objects queried by the IDE provided build action in order to calculate the model to send back.
+     */
+    IntermediateModels(true),
+    /**
+     * Contains the dependency resolution metadata for each project.
+     */
+    ProjectMetadata(false),
+    BuildFingerprint(true),
+    ProjectFingerprint(true),
+    /**
+     * The index file that points to all of these things
+     */
+    Entry(false)
 }
 
 
 internal
 interface ConfigurationCacheStateFile {
     val exists: Boolean
+    val stateType: StateType
+    val stateFile: ConfigurationCacheStateStore.StateFile
     fun outputStream(): OutputStream
     fun inputStream(): InputStream
     fun delete()
@@ -297,11 +322,22 @@
             val buildPath = read() as Path
             rootBuild.addIncludedBuild(definition, settingsFile, buildPath)
         }
+
+        build.gradle.loadGradleProperties()
         // Decode the build state using the contextualized IO service for the build
         return build.gradle.serviceOf<ConfigurationCacheIO>().readIncludedBuildStateFrom(stateFileFor((build.state as NestedBuildState).buildDefinition), build)
     }
 
     private
+    fun GradleInternal.loadGradleProperties() {
+        val settingDir = serviceOf<BuildLayout>().settingsDir
+        // Load Gradle properties from a file but skip applying system properties defined here.
+        // System properties from the file may be mutated by the build logic, and the execution-time values are already restored by the EnvironmentChangeTracker.
+        // Applying properties from file overwrites these modifications.
+        serviceOf<GradlePropertiesController>().loadGradlePropertiesFrom(settingDir, false)
+    }
+
+    private
     suspend fun DefaultWriteContext.writeBuildSrcBuild(state: StandAloneNestedBuild, buildTreeState: StoredBuildTreeState) {
         val gradle = state.mutableModel
         withGradleIsolate(gradle, userTypesCodec) {
@@ -319,6 +355,7 @@
             val ownerIdentifier = readNonNull<BuildIdentifier>()
             rootBuild.getBuildSrcOf(ownerIdentifier)
         }
+        build.gradle.loadGradleProperties()
         // Decode the build state using the contextualized IO service for the build
         return build.gradle.serviceOf<ConfigurationCacheIO>().readIncludedBuildStateFrom(stateFileFor((build.state as NestedBuildState).buildDefinition), build)
     }
@@ -418,7 +455,7 @@
 
     private
     suspend fun WriteContext.writeFlowScopeOf(gradle: GradleInternal) {
-        withGradleIsolate(gradle, userTypesCodec) {
+        withIsolate(IsolateOwner.OwnerFlowScope(gradle), userTypesCodec) {
             val flowScopeState = buildFlowScopeOf(gradle).store()
             write(flowScopeState)
         }
@@ -426,7 +463,7 @@
 
     private
     suspend fun DefaultReadContext.readFlowScopeOf(gradle: GradleInternal) {
-        withGradleIsolate(gradle, userTypesCodec) {
+        withIsolate(IsolateOwner.OwnerFlowScope(gradle), userTypesCodec) {
             buildFlowScopeOf(gradle).load(readNonNull())
         }
     }
@@ -576,7 +613,7 @@
     suspend fun DefaultWriteContext.writeBuildDefinition(buildDefinition: BuildDefinition) {
         buildDefinition.run {
             writeString(name!!)
-            writeFile(buildRootDir)
+            write(buildRootDir)
             write(fromBuild)
             writeBoolean(isPluginBuild)
         }
@@ -585,7 +622,7 @@
     private
     suspend fun DefaultReadContext.readIncludedBuildDefinition(parentBuild: ConfigurationCacheBuild): BuildDefinition {
         val includedBuildName = readString()
-        val includedBuildRootDir = readFile()
+        val includedBuildRootDir: File? = read()?.uncheckedCast()
         val fromBuild = readNonNull<PublicBuildPath>()
         val pluginBuild = readBoolean()
         return BuildDefinition.fromStartParameterForBuild(
@@ -786,7 +823,7 @@
 
     private
     fun isRelevantBuildEventListener(provider: RegisteredBuildServiceProvider<*, *>) =
-        provider.buildIdentifier.name != BUILD_SRC
+        Path.path(provider.buildIdentifier.buildPath).name != BUILD_SRC
 
     private
     val BuildState.projectsAvailable
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheStateStore.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheStateStore.kt
index 751d264..3e208a9 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheStateStore.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ConfigurationCacheStateStore.kt
@@ -22,7 +22,10 @@
 
 internal
 interface ConfigurationCacheStateStore {
-    fun assignSpoolFile(stateType: StateType): File
+
+    data class StateFile(val stateType: StateType, val file: File)
+
+    fun assignSpoolFile(stateType: StateType): StateFile
 
     /**
      * Loads some value from zero or more state files.
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
index ebf17d3..1732eeb 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildTreeModelControllerServices.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DefaultBuildTreeModelControllerServices.kt
@@ -19,9 +19,11 @@
 import org.gradle.api.GradleException
 import org.gradle.api.internal.BuildType
 import org.gradle.api.internal.StartParameterInternal
+import org.gradle.api.logging.LogLevel
 import org.gradle.configurationcache.initialization.ConfigurationCacheInjectedClasspathInstrumentationStrategy
 import org.gradle.configurationcache.initialization.ConfigurationCacheStartParameter
 import org.gradle.configurationcache.initialization.DefaultConfigurationCacheProblemsListener
+import org.gradle.configurationcache.initialization.InstrumentedExecutionAccessListenerRegistry
 import org.gradle.configurationcache.initialization.VintageInjectedClasspathInstrumentationStrategy
 import org.gradle.configurationcache.problems.ConfigurationCacheProblems
 import org.gradle.configurationcache.problems.DefaultProblemFactory
@@ -31,6 +33,7 @@
 import org.gradle.configurationcache.services.ConfigurationCacheEnvironmentChangeTracker
 import org.gradle.configurationcache.services.VintageEnvironmentChangeTracker
 import org.gradle.execution.selection.BuildTaskSelector
+import org.gradle.initialization.StartParameterBuildOptions
 import org.gradle.internal.build.BuildStateRegistry
 import org.gradle.internal.buildoption.DefaultInternalOptions
 import org.gradle.internal.buildoption.InternalFlag
@@ -67,20 +70,29 @@
         val isolatedProjects = startParameter.isolatedProjects.get()
         val parallelToolingActions = (isolatedProjects || requirements.startParameter.isParallelProjectExecutionEnabled) && options.getOption(parallelBuilding).get()
         val invalidateCoupledProjects = isolatedProjects && options.getOption(invalidateCoupledProjects).get()
+        val configurationCacheLogLevel = if (startParameter.isConfigurationCacheQuiet) LogLevel.INFO else LogLevel.LIFECYCLE
         val modelParameters = if (requirements.isCreatesModel) {
             // When creating a model, disable certain features - only enable configure on demand and configuration cache when isolated projects is enabled
-            BuildModelParameters(isolatedProjects, isolatedProjects, isolatedProjects, true, isolatedProjects, parallelToolingActions, invalidateCoupledProjects)
+            BuildModelParameters(isolatedProjects, isolatedProjects, isolatedProjects, true, isolatedProjects, parallelToolingActions, invalidateCoupledProjects, configurationCacheLogLevel)
         } else {
-            val configurationCache = startParameter.configurationCache.get() || isolatedProjects
-            val configureOnDemand = startParameter.isConfigureOnDemand || isolatedProjects
-            BuildModelParameters(configureOnDemand, configurationCache, isolatedProjects, false, false, parallelToolingActions, invalidateCoupledProjects)
+            val configurationCache = isolatedProjects || startParameter.configurationCache.get()
+            val configureOnDemand = isolatedProjects || startParameter.isConfigureOnDemand
+
+            fun disabledConfigurationCacheBuildModelParameters(buildOptionReason: String): BuildModelParameters {
+                logger.log(configurationCacheLogLevel, "{} as configuration cache cannot be reused due to --{}", requirements.actionDisplayName.capitalizedDisplayName, buildOptionReason)
+                return BuildModelParameters(configureOnDemand, false, false, false, false, parallelToolingActions, invalidateCoupledProjects, configurationCacheLogLevel)
+            }
+
+            when {
+                configurationCache && startParameter.writeDependencyVerifications.isNotEmpty() -> disabledConfigurationCacheBuildModelParameters(StartParameterBuildOptions.DependencyVerificationWriteOption.LONG_OPTION)
+                configurationCache && startParameter.isExportKeys -> disabledConfigurationCacheBuildModelParameters(StartParameterBuildOptions.ExportKeysOption.LONG_OPTION)
+                else -> BuildModelParameters(configureOnDemand, configurationCache, isolatedProjects, false, false, parallelToolingActions, invalidateCoupledProjects, configurationCacheLogLevel)
+            }
         }
 
         if (!startParameter.isConfigurationCacheQuiet) {
             if (modelParameters.isIsolatedProjects) {
                 IncubationLogger.incubatingFeatureUsed("Isolated projects")
-            } else if (modelParameters.isConfigurationCache) {
-                IncubationLogger.incubatingFeatureUsed("Configuration cache")
             }
         }
         if (!modelParameters.isIsolatedProjects && modelParameters.isConfigureOnDemand) {
@@ -98,7 +110,7 @@
         return BuildTreeModelControllerServices.Supplier { registration ->
             registration.add(BuildType::class.java, BuildType.TASKS)
             // Configuration cache is not supported for nested build trees
-            val buildModelParameters = BuildModelParameters(startParameter.isConfigureOnDemand, false, false, true, false, false, false)
+            val buildModelParameters = BuildModelParameters(startParameter.isConfigureOnDemand, false, false, true, false, false, false, LogLevel.LIFECYCLE)
             val requirements = RunTasksRequirements(startParameter)
             registerServices(registration, buildModelParameters, requirements)
         }
@@ -121,6 +133,7 @@
             registration.add(BeanStateWriterLookup::class.java)
             registration.add(BeanStateReaderLookup::class.java)
             registration.add(JavaSerializationEncodingLookup::class.java)
+            registration.add(InstrumentedExecutionAccessListenerRegistry::class.java)
             registration.addProvider(ConfigurationCacheBuildTreeProvider())
         } else {
             registration.add(VintageInjectedClasspathInstrumentationStrategy::class.java)
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 e4b89ee..c39d170 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
@@ -40,6 +40,7 @@
 import org.gradle.internal.buildtree.BuildTreeWorkGraph
 import org.gradle.internal.classpath.Instrumented
 import org.gradle.internal.component.local.model.LocalComponentGraphResolveState
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory
 import org.gradle.internal.concurrent.CompositeStoppable
 import org.gradle.internal.concurrent.Stoppable
 import org.gradle.internal.operations.BuildOperationExecutor
@@ -63,6 +64,8 @@
     private val virtualFileSystem: BuildLifecycleAwareVirtualFileSystem,
     private val buildOperationExecutor: BuildOperationExecutor,
     private val cacheFingerprintController: ConfigurationCacheFingerprintController,
+    private val encryptionService: EncryptionService,
+    private val resolveStateFactory: LocalComponentGraphResolveStateFactory,
     /**
      * Force the [FileSystemAccess] service to be initialized as it initializes important static state.
      */
@@ -100,7 +103,7 @@
     val intermediateModels = lazy { IntermediateModelController(host, cacheIO, store, cacheFingerprintController) }
 
     private
-    val projectMetadata = lazy { ProjectMetadataController(host, cacheIO, store) }
+    val projectMetadata = lazy { ProjectMetadataController(host, cacheIO, resolveStateFactory, store) }
 
     private
     val cacheIO by lazy { host.service<ConfigurationCacheIO>() }
@@ -222,15 +225,6 @@
             ConfigurationCacheAction.STORE
         }
 
-        startParameter.isWriteDependencyVerifications -> {
-            logBootstrapSummary(
-                "{} as configuration cache cannot be reused due to {}",
-                buildActionModelRequirements.actionDisplayName.capitalizedDisplayName,
-                "--write-verification-metadata"
-            )
-            ConfigurationCacheAction.STORE
-        }
-
         else -> {
             when (val checkedFingerprint = checkFingerprint()) {
                 is CheckedFingerprint.NotFound -> {
@@ -419,7 +413,7 @@
     private
     fun startCollectingCacheFingerprint() {
         cacheFingerprintController.maybeStartCollectingFingerprint(store.assignSpoolFile(StateType.BuildFingerprint), store.assignSpoolFile(StateType.ProjectFingerprint)) {
-            cacheFingerprintWriterContextFor(it)
+            cacheFingerprintWriterContextFor(encryptionService.outputStream(it.stateType, it.file::outputStream))
         }
     }
 
@@ -427,7 +421,7 @@
     fun cacheFingerprintWriterContextFor(outputStream: OutputStream): DefaultWriteContext {
         val (context, codecs) = cacheIO.writerContextFor(outputStream, "fingerprint")
         return context.apply {
-            push(IsolateOwner.OwnerHost(host), codecs.userTypesCodec())
+            push(IsolateOwner.OwnerHost(host), codecs.fingerprintTypesCodec())
         }
     }
 
@@ -488,9 +482,9 @@
 
     private
     fun <T> readFingerprintFile(fingerprintFile: ConfigurationCacheStateFile, action: suspend ReadContext.(ConfigurationCacheFingerprintController.Host) -> T): T =
-        fingerprintFile.inputStream().use { inputStream ->
+        encryptionService.inputStream(fingerprintFile.stateType, fingerprintFile::inputStream).use { inputStream ->
             cacheIO.withReadContextFor(inputStream) { codecs ->
-                withIsolate(IsolateOwner.OwnerHost(host), codecs.userTypesCodec()) {
+                withIsolate(IsolateOwner.OwnerHost(host), codecs.fingerprintTypesCodec()) {
                     action(object : ConfigurationCacheFingerprintController.Host {
                         override val valueSourceProviderFactory: ValueSourceProviderFactory
                             get() = host.service()
@@ -508,7 +502,7 @@
 
     private
     fun loadGradleProperties() {
-        gradlePropertiesController.loadGradlePropertiesFrom(startParameter.settingsDirectory)
+        gradlePropertiesController.loadGradlePropertiesFrom(startParameter.settingsDirectory, true)
     }
 
     private
@@ -528,10 +522,7 @@
 
     private
     val configurationCacheLogLevel: LogLevel
-        get() = when (startParameter.isQuiet) {
-            true -> LogLevel.INFO
-            else -> LogLevel.LIFECYCLE
-        }
+        get() = startParameter.configurationCacheLogLevel
 }
 
 
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DeprecatedFeaturesListener.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DeprecatedFeaturesListener.kt
index ac0466e..8ef8ab2 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DeprecatedFeaturesListener.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/DeprecatedFeaturesListener.kt
@@ -18,46 +18,85 @@
 
 import org.gradle.api.internal.BuildScopeListenerRegistrationListener
 import org.gradle.api.internal.FeaturePreviews
+import org.gradle.api.internal.FeaturePreviews.Feature.STABLE_CONFIGURATION_CACHE
 import org.gradle.api.internal.TaskInternal
 import org.gradle.api.internal.tasks.execution.TaskExecutionAccessListener
+import org.gradle.execution.ExecutionAccessListener
 import org.gradle.internal.buildoption.FeatureFlags
+import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.internal.deprecation.DeprecationLogger
 import org.gradle.internal.service.scopes.ListenerService
+import org.gradle.internal.service.scopes.Scopes
+import org.gradle.internal.service.scopes.ServiceScope
 
 
+/**
+ * Reports deprecations when [FeaturePreviews.Feature.STABLE_CONFIGURATION_CACHE] is enabled
+ * but the configuration cache is not (since the deprecated features are already reported as problems
+ * in that case).
+ */
 @ListenerService
+@ServiceScope(Scopes.BuildTree::class)
 internal
 class DeprecatedFeaturesListener(
-    private val featureFlags: FeatureFlags
-) : BuildScopeListenerRegistrationListener, TaskExecutionAccessListener {
+    private val featureFlags: FeatureFlags,
+    private val buildModelParameters: BuildModelParameters
+) : BuildScopeListenerRegistrationListener, TaskExecutionAccessListener, ExecutionAccessListener {
 
     override fun onBuildScopeListenerRegistration(listener: Any, invocationDescription: String, invocationSource: Any) {
-        if (isStableConfigurationCacheEnabled()) {
-            DeprecationLogger.deprecateAction("Listener registration using $invocationDescription()")
-                .willBecomeAnErrorInGradle9()
-                .withUpgradeGuideSection(7, "task_execution_events")
-                .nagUser()
+        if (shouldNag()) {
+            nagUserAbout("Listener registration using $invocationDescription()", 7, "task_execution_events")
         }
     }
 
-    override fun onProjectAccess(invocationDescription: String, task: TaskInternal) {
-        if (isStableConfigurationCacheEnabled()) {
-            DeprecationLogger.deprecateAction("Invocation of $invocationDescription at execution time")
-                .willBecomeAnErrorInGradle9()
-                .withUpgradeGuideSection(7, "task_project")
-                .nagUser()
+    override fun onProjectAccess(invocationDescription: String, task: TaskInternal, runningTask: TaskInternal?) {
+        if (shouldNagFor(task, runningTask)) {
+            nagUserAbout("Invocation of $invocationDescription at execution time", 7, "task_project")
         }
     }
 
-    override fun onTaskDependenciesAccess(invocationDescription: String, task: TaskInternal) {
-        if (isStableConfigurationCacheEnabled()) {
+    override fun onTaskDependenciesAccess(invocationDescription: String, task: TaskInternal, runningTask: TaskInternal?) {
+        if (shouldNagFor(task, runningTask)) {
             throwUnsupported("Invocation of $invocationDescription at execution time")
         }
     }
 
+    override fun onConventionAccess(invocationDescription: String, task: TaskInternal, runningTask: TaskInternal?) {
+        if (shouldNagFor(task, runningTask)) {
+            nagUserAbout("Invocation of $invocationDescription at execution time", 8, "task_convention")
+        }
+    }
+
+    override fun disallowedAtExecutionInjectedServiceAccessed(injectedServiceType: Class<*>, getterName: String, consumer: String) {
+        if (shouldNag()) {
+            throwUnsupported("Invocation of $injectedServiceType at execution time")
+        }
+    }
+
     private
-    fun isStableConfigurationCacheEnabled(): Boolean =
-        featureFlags.isEnabled(FeaturePreviews.Feature.STABLE_CONFIGURATION_CACHE)
+    fun nagUserAbout(action: String, upgradeGuideMajorVersion: Int, upgradeGuideSection: String) {
+        DeprecationLogger.deprecateAction(action)
+            .willBecomeAnErrorInGradle9()
+            .withUpgradeGuideSection(upgradeGuideMajorVersion, upgradeGuideSection)
+            .nagUser()
+    }
+
+    private
+    fun shouldNagFor(task: TaskInternal, runningTask: TaskInternal?) =
+        shouldNag() && shouldReportInContext(task, runningTask)
+
+    private
+    fun shouldNag(): Boolean =
+        // TODO:configuration-cache - this listener shouldn't be registered when cc is enabled
+        !buildModelParameters.isConfigurationCache && featureFlags.isEnabled(STABLE_CONFIGURATION_CACHE)
+
+    /**
+     * Only nag about tasks that are actually executing, but not tasks that are configured by the executing tasks.
+     * A task is unlikely to reach out to other tasks without violating other constraints.
+     **/
+    private
+    fun shouldReportInContext(task: TaskInternal, runningTask: TaskInternal?) =
+        runningTask == null || task === runningTask
 
     private
     fun throwUnsupported(reason: String): Nothing =
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/EncryptionService.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/EncryptionService.kt
new file mode 100644
index 0000000..02b2138
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/EncryptionService.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2023 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.cache.CacheBuilder
+import org.gradle.cache.PersistentCache
+import org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory
+import org.gradle.configurationcache.extensions.useToRun
+import org.gradle.configurationcache.initialization.ConfigurationCacheStartParameter
+import org.gradle.internal.hash.HashCode
+import org.gradle.internal.hash.Hashing
+import org.gradle.internal.service.scopes.Scopes
+import org.gradle.internal.service.scopes.ServiceScope
+import org.gradle.util.internal.EncryptionAlgorithm
+import org.gradle.util.internal.EncryptionAlgorithm.EncryptionException
+import org.gradle.util.internal.SupportedEncryptionAlgorithm
+import java.io.File
+import java.io.InputStream
+import java.io.OutputStream
+import java.security.InvalidKeyException
+import java.security.KeyStore
+import javax.crypto.CipherInputStream
+import javax.crypto.CipherOutputStream
+import javax.crypto.KeyGenerator
+import javax.crypto.SecretKey
+
+
+/**
+ * Provides access to configuration parameters to control encryption.
+ */
+internal
+interface EncryptionConfiguration {
+    val isEncrypting: Boolean
+    val encryptionKeyHashCode: HashCode
+    val encryptionAlgorithm: EncryptionAlgorithm
+}
+
+
+/**
+ * A service for encrypting/decrypting streams.
+ */
+internal
+interface EncryptionService : EncryptionConfiguration {
+    fun outputStream(stateType: StateType, output: () -> OutputStream): OutputStream
+    fun inputStream(stateType: StateType, input: () -> InputStream): InputStream
+}
+
+
+@ServiceScope(Scopes.BuildTree::class)
+internal
+class DefaultEncryptionService(
+    private val startParameter: ConfigurationCacheStartParameter,
+    private val cacheBuilderFactory: GlobalScopedCacheBuilderFactory,
+) : EncryptionService {
+
+    private
+    val secretKey: SecretKey? by lazy {
+        when {
+            // encryption is on by default
+            startParameter.encryptionRequested -> produceSecretKey()
+            else -> {
+                logger.warn("Encryption of the configuration cache is disabled.")
+                null
+            }
+        }
+    }
+
+    private
+    fun produceSecretKey() =
+        secretKeySource().let { keySource ->
+            try {
+                secretKeyFrom(keySource).also { key ->
+                    assertKeyLength(key)
+                }
+            } catch (e: EncryptionException) {
+                if (e.message != null) {
+                    throw e
+                }
+                throw EncryptionException("Error loading encryption key from ${keySource.sourceDescription}", e.cause)
+            } catch (e: Exception) {
+                throw EncryptionException("Error loading encryption key from ${keySource.sourceDescription}", e)
+            }
+        }
+
+    private
+    fun secretKeyFrom(keySource: KeyStoreKeySource) =
+        cacheBuilderFor(keySource)
+            .withInitializer {
+                keySource.createKeyStoreAndGenerateKey(keyStoreFile())
+            }.open().useToRun {
+                try {
+                    keySource.loadSecretKeyFromExistingKeystore(keyStoreFile())
+                } catch (loadException: Exception) {
+                    // try to recover from a tampered-with keystore by generating a new key
+                    // TODO:configuration-cache do we really need this?
+                    try {
+                        keySource.createKeyStoreAndGenerateKey(keyStoreFile())
+                    } catch (e: Exception) {
+                        e.addSuppressed(loadException)
+                        throw e
+                    }
+                }
+            }
+
+    private
+    fun cacheBuilderFor(keySource: KeyStoreKeySource): CacheBuilder =
+        cacheBuilderFactory
+            .run { keySource.customKeyStoreDir?.let { createCacheBuilderFactory(it) } ?: this }
+            .createCacheBuilder("cc-keystore")
+            .withDisplayName("Gradle Configuration Cache keystore")
+
+    private
+    fun PersistentCache.keyStoreFile(): File =
+        File(baseDir, "gradle.keystore")
+
+    private
+    fun assertKeyLength(key: SecretKey) {
+        val keyLength = key.encoded.size
+        if (keyLength < 16) {
+            throw InvalidKeyException("Encryption key length is $keyLength bytes, but must be at least 16 bytes long")
+        }
+    }
+
+    override
+    val encryptionAlgorithm: EncryptionAlgorithm by lazy {
+        SupportedEncryptionAlgorithm.byTransformation(startParameter.encryptionAlgorithm)
+    }
+
+    override val isEncrypting: Boolean
+        get() = secretKey != null
+
+    override val encryptionKeyHashCode: HashCode by lazy {
+        secretKey?.let {
+            Hashing.sha512().newHasher().apply {
+                putBytes(it.encoded)
+                putString(encryptionAlgorithm.transformation)
+            }.hash()
+        } ?: Hashing.newHasher().hash()
+    }
+
+    private
+    fun shouldEncryptStreams(stateType: StateType) =
+        isEncrypting && stateType.encryptable
+
+    override fun outputStream(stateType: StateType, output: () -> OutputStream): OutputStream =
+        if (shouldEncryptStreams(stateType))
+            encryptingOutputStream(output.invoke())
+        else
+            output.invoke()
+
+    override fun inputStream(stateType: StateType, input: () -> InputStream): InputStream =
+        if (shouldEncryptStreams(stateType))
+            decryptingInputStream(input.invoke())
+        else
+            input.invoke()
+
+    private
+    fun decryptingInputStream(inputStream: InputStream): InputStream {
+        val cipher = newEncryptionSession().decryptingCipher(inputStream::read)
+        return CipherInputStream(inputStream, cipher)
+    }
+
+    private
+    fun encryptingOutputStream(outputStream: OutputStream): OutputStream {
+        val cipher = newEncryptionSession().encryptingCipher(outputStream::write)
+        return CipherOutputStream(outputStream, cipher)
+    }
+
+    private
+    fun newEncryptionSession(): EncryptionAlgorithm.Session =
+        encryptionAlgorithm.newSession(secretKey)
+
+    private
+    fun secretKeySource() = KeyStoreKeySource(
+        encryptionAlgorithm = encryptionAlgorithm.algorithm,
+        customKeyStoreDir = startParameter.keystoreDir?.let { File(it) },
+        keyAlias = "gradle-secret"
+    )
+}
+
+
+class KeyStoreKeySource(
+    val encryptionAlgorithm: String,
+    val customKeyStoreDir: File?,
+    val keyAlias: String,
+) {
+
+    private
+    val keyProtection = KeyStore.PasswordProtection(CharArray(0))
+
+    private
+    val keyStore by lazy {
+        KeyStore.getInstance(KEYSTORE_TYPE)
+    }
+
+    val sourceDescription: String
+        get() = customKeyStoreDir?.let { "custom Java keystore at $it" }
+            ?: "default Gradle configuration cache keystore"
+
+    fun createKeyStoreAndGenerateKey(keyStoreFile: File): SecretKey {
+        logger.debug("No keystore found")
+        keyStore.load(null, KEYSTORE_PASSWORD)
+        return generateKey(keyStoreFile, keyAlias).also {
+            logger.debug("Key added to a new keystore at {}", keyStoreFile)
+        }
+    }
+
+    fun loadSecretKeyFromExistingKeystore(keyStoreFile: File): SecretKey {
+        logger.debug("Loading keystore from {}", keyStoreFile)
+        keyStoreFile.inputStream().use { fis ->
+            keyStore.load(fis, KEYSTORE_PASSWORD)
+        }
+        val entry = keyStore.getEntry(keyAlias, keyProtection) as KeyStore.SecretKeyEntry?
+        if (entry != null) {
+            return entry.secretKey.also {
+                logger.debug("Retrieved key")
+            }
+        }
+        logger.debug("No key found")
+        return generateKey(keyStoreFile, keyAlias).also {
+            logger.warn("Key added to existing keystore at {}", keyStoreFile)
+        }
+    }
+
+    private
+    fun generateKey(keyStoreFile: File, alias: String): SecretKey {
+        val newKey = KeyGenerator.getInstance(encryptionAlgorithm).generateKey()
+        require(newKey != null) {
+            "Failed to generate encryption key using $encryptionAlgorithm."
+        }
+        logger.debug("Generated key")
+        val entry = KeyStore.SecretKeyEntry(newKey)
+        keyStore.setEntry(alias, entry, keyProtection)
+        keyStoreFile.outputStream().use { fos ->
+            keyStore.store(fos, KEYSTORE_PASSWORD)
+        }
+        return newKey
+    }
+
+    companion object {
+        // JKS does not support non-PrivateKeys
+        const val KEYSTORE_TYPE = "pkcs12"
+        val KEYSTORE_PASSWORD = charArrayOf('c', 'c')
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ExecutionAccessChecker.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ExecutionAccessChecker.kt
new file mode 100644
index 0000000..2453bfb
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ExecutionAccessChecker.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 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.provider.ConfigurationTimeBarrier
+import org.gradle.execution.ExecutionAccessChecker
+import org.gradle.execution.ExecutionAccessListener
+
+
+internal
+class ConfigurationTimeBarrierBasedExecutionAccessChecker(
+    private val configurationTimeBarrier: ConfigurationTimeBarrier,
+    private val broadcaster: ExecutionAccessListener
+) : ExecutionAccessChecker {
+    override fun disallowedAtExecutionInjectedServiceAccessed(injectedServiceType: Class<*>, getterName: String, consumer: String) {
+        if (shouldReportExecutionTimeAccess()) {
+            broadcaster.disallowedAtExecutionInjectedServiceAccessed(injectedServiceType, getterName, consumer)
+        }
+    }
+
+    private
+    fun shouldReportExecutionTimeAccess(): Boolean =
+        !configurationTimeBarrier.isAtConfigurationTime
+}
+
+
+internal
+class DefaultExecutionAccessChecker : ExecutionAccessChecker {
+
+    override fun disallowedAtExecutionInjectedServiceAccessed(injectedServiceType: Class<*>, getterName: String, consumer: String) {}
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/InstrumentedExecutionAccessListener.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/InstrumentedExecutionAccessListener.kt
new file mode 100644
index 0000000..de6e72f
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/InstrumentedExecutionAccessListener.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 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.execution.ExecutionAccessChecker
+import org.gradle.internal.classpath.InstrumentedExecutionAccess
+import org.gradle.internal.service.scopes.Scopes
+import org.gradle.internal.service.scopes.ServiceScope
+
+
+@ServiceScope(Scopes.BuildTree::class)
+internal
+class InstrumentedExecutionAccessListener(
+    private val executionAccessChecker: ExecutionAccessChecker
+) : InstrumentedExecutionAccess.Listener {
+
+    override fun disallowedAtExecutionInjectedServiceAccessed(injectedServiceType: Class<*>, propertyName: String, consumer: String) {
+        executionAccessChecker.disallowedAtExecutionInjectedServiceAccessed(injectedServiceType, propertyName, consumer)
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ProblemReportingCrossProjectModelAccess.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ProblemReportingCrossProjectModelAccess.kt
index 2d573bd..0540d44 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ProblemReportingCrossProjectModelAccess.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/ProblemReportingCrossProjectModelAccess.kt
@@ -28,7 +28,6 @@
 import org.gradle.api.Project
 import org.gradle.api.ProjectEvaluationListener
 import org.gradle.api.Task
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.dsl.ArtifactHandler
 import org.gradle.api.artifacts.dsl.DependencyFactory
 import org.gradle.api.artifacts.dsl.DependencyHandler
@@ -46,6 +45,7 @@
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.ProcessOperations
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import org.gradle.api.internal.file.FileOperations
 import org.gradle.api.internal.file.FileResolver
 import org.gradle.api.internal.initialization.ClassLoaderScope
@@ -63,7 +63,6 @@
 import org.gradle.api.logging.Logger
 import org.gradle.api.logging.LoggingManager
 import org.gradle.api.model.ObjectFactory
-import org.gradle.api.plugins.Convention
 import org.gradle.api.plugins.ObjectConfigurationAction
 import org.gradle.api.plugins.PluginContainer
 import org.gradle.api.provider.Property
@@ -529,7 +528,7 @@
             return delegate.ant(configureAction)
         }
 
-        override fun getConfigurations(): ConfigurationContainer {
+        override fun getConfigurations(): RoleBasedConfigurationContainerInternal {
             onAccess()
             return delegate.configurations
         }
@@ -555,7 +554,7 @@
         }
 
         @Deprecated("The concept of conventions is deprecated. Use extensions instead.")
-        override fun getConvention(): Convention {
+        override fun getConvention(): @Suppress("deprecation") org.gradle.api.plugins.Convention {
             onAccess()
             @Suppress("deprecation")
             return delegate.convention
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/TaskExecutionAccessCheckers.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/TaskExecutionAccessCheckers.kt
index e7a77ed..4d3fd8d 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/TaskExecutionAccessCheckers.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/TaskExecutionAccessCheckers.kt
@@ -20,23 +20,35 @@
 import org.gradle.api.internal.provider.ConfigurationTimeBarrier
 import org.gradle.api.internal.tasks.TaskExecutionAccessChecker
 import org.gradle.api.internal.tasks.execution.TaskExecutionAccessListener
+import org.gradle.configurationcache.serialization.Workarounds
+import org.gradle.internal.execution.WorkExecutionTracker
 
 
 abstract class AbstractTaskProjectAccessChecker(
-    private val broadcaster: TaskExecutionAccessListener
+    private val broadcaster: TaskExecutionAccessListener,
+    private val workExecutionTracker: WorkExecutionTracker
 ) : TaskExecutionAccessChecker {
     override fun notifyProjectAccess(task: TaskInternal) {
         if (shouldReportExecutionTimeAccess(task)) {
-            broadcaster.onProjectAccess("Task.project", task)
+            broadcaster.onProjectAccess("Task.project", task, currentTask())
         }
     }
 
     override fun notifyTaskDependenciesAccess(task: TaskInternal, invocationDescription: String) {
         if (shouldReportExecutionTimeAccess(task)) {
-            broadcaster.onTaskDependenciesAccess(invocationDescription, task)
+            broadcaster.onTaskDependenciesAccess(invocationDescription, task, currentTask())
         }
     }
 
+    override fun notifyConventionAccess(task: TaskInternal, invocationDescription: String) {
+        if (shouldReportExecutionTimeAccess(task)) {
+            broadcaster.onConventionAccess(invocationDescription, task, currentTask())
+        }
+    }
+
+    private
+    fun currentTask() = workExecutionTracker.currentTask.orElse(null)
+
     protected
     abstract fun shouldReportExecutionTimeAccess(task: TaskInternal): Boolean
 }
@@ -44,16 +56,17 @@
 
 object TaskExecutionAccessCheckers {
 
-    class TaskStateBased(broadcaster: TaskExecutionAccessListener) : AbstractTaskProjectAccessChecker(broadcaster) {
+    class TaskStateBased(broadcaster: TaskExecutionAccessListener, workExecutionTracker: WorkExecutionTracker) : AbstractTaskProjectAccessChecker(broadcaster, workExecutionTracker) {
         override fun shouldReportExecutionTimeAccess(task: TaskInternal): Boolean = task.state.executing
     }
 
     class ConfigurationTimeBarrierBased(
         private val configurationTimeBarrier: ConfigurationTimeBarrier,
-        broadcaster: TaskExecutionAccessListener
-    ) : AbstractTaskProjectAccessChecker(broadcaster) {
+        broadcaster: TaskExecutionAccessListener,
+        workExecutionTracker: WorkExecutionTracker
+    ) : AbstractTaskProjectAccessChecker(broadcaster, workExecutionTracker) {
 
         override fun shouldReportExecutionTimeAccess(task: TaskInternal): Boolean =
-            !configurationTimeBarrier.isAtConfigurationTime
+            !configurationTimeBarrier.isAtConfigurationTime && !Workarounds.canAccessProjectAtExecutionTime(task)
     }
 }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintChecker.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintChecker.kt
index 5548393..c894ba6 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintChecker.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintChecker.kt
@@ -25,6 +25,7 @@
 import org.gradle.configurationcache.extensions.fileSystemEntryType
 import org.gradle.configurationcache.extensions.filterKeysByPrefix
 import org.gradle.configurationcache.extensions.uncheckedCast
+import org.gradle.configurationcache.logger
 import org.gradle.configurationcache.serialization.ReadContext
 import org.gradle.internal.file.FileType
 import org.gradle.internal.hash.HashCode
@@ -43,6 +44,8 @@
 class ConfigurationCacheFingerprintChecker(private val host: Host) {
 
     interface Host {
+        val isEncrypted: Boolean
+        val encryptionKeyHashCode: HashCode
         val gradleUserHomeDir: File
         val allInitScripts: List<File>
         val startParameterProperties: Map<String, Any?>
@@ -284,11 +287,19 @@
 
     private
     fun checkFingerprintValueIsUpToDate(obtainedValue: ObtainedValue): InvalidationReason? {
-        val valueSource = host.instantiateValueSourceOf(obtainedValue)
-        if (obtainedValue.value.get() != valueSource.obtain()) {
-            return buildLogicInputHasChanged(valueSource)
+        return obtainedValue.value.map { fingerprintedValue ->
+            val valueSource = host.instantiateValueSourceOf(obtainedValue)
+            if (fingerprintedValue != valueSource.obtain()) {
+                buildLogicInputHasChanged(valueSource)
+            } else {
+                null
+            }
+        }.getOrMapFailure { failure ->
+            // This can only happen if someone ignored configuration cache problems and still stored the entry.
+            // We're invalidating the cache to save the user a manual "rm -rf .gradle/configuration-cache", as there is no way out.
+            logger.info("The build logic input of type ${obtainedValue.valueSourceType} cannot be checked because it failed when storing the entry", failure)
+            buildLogicInputFailed(obtainedValue, failure)
         }
-        return null
     }
 
     private
@@ -314,6 +325,10 @@
         } ?: "a build logic input of type '${unpackType(valueSource).simpleName}' has changed"
 
     private
+    fun buildLogicInputFailed(obtainedValue: ObtainedValue, failure: Throwable): InvalidationReason =
+        "a build logic input of type '${obtainedValue.valueSourceType.simpleName}' failed when storing the entry with $failure"
+
+    private
     class ProjectInvalidationState {
         // When true, the project is definitely invalid
         // When false, validity is not known
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintController.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintController.kt
index 77cb2f0..d0406e4 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintController.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintController.kt
@@ -24,23 +24,29 @@
 import org.gradle.api.internal.provider.ValueSourceProviderFactory
 import org.gradle.configurationcache.CheckedFingerprint
 import org.gradle.configurationcache.ConfigurationCacheStateFile
+import org.gradle.configurationcache.ConfigurationCacheStateStore.StateFile
+import org.gradle.configurationcache.EncryptionService
 import org.gradle.configurationcache.InputTrackingState
 import org.gradle.configurationcache.extensions.directoryContentHash
 import org.gradle.configurationcache.extensions.uncheckedCast
 import org.gradle.configurationcache.initialization.ConfigurationCacheStartParameter
+import org.gradle.configurationcache.problems.ConfigurationCacheProblems
 import org.gradle.configurationcache.problems.ConfigurationCacheReport
+import org.gradle.configurationcache.problems.DocumentationSection
 import org.gradle.configurationcache.problems.ProblemFactory
 import org.gradle.configurationcache.problems.PropertyProblem
+import org.gradle.configurationcache.problems.StructuredMessage
+import org.gradle.configurationcache.problems.StructuredMessageBuilder
 import org.gradle.configurationcache.serialization.DefaultWriteContext
 import org.gradle.configurationcache.serialization.ReadContext
 import org.gradle.configurationcache.services.ConfigurationCacheEnvironmentChangeTracker
 import org.gradle.configurationcache.services.RemoteScriptUpToDateChecker
-import org.gradle.internal.agents.AgentControl
+import org.gradle.internal.agents.AgentStatus
 import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.internal.concurrent.Stoppable
 import org.gradle.internal.event.ListenerManager
 import org.gradle.internal.execution.FileCollectionFingerprinterRegistry
-import org.gradle.internal.execution.TaskExecutionTracker
+import org.gradle.internal.execution.WorkExecutionTracker
 import org.gradle.internal.execution.WorkInputListeners
 import org.gradle.internal.execution.impl.DefaultFileNormalizationSpec
 import org.gradle.internal.execution.model.InputNormalizer
@@ -55,8 +61,6 @@
 import org.gradle.util.internal.BuildCommencedTimeProvider
 import org.gradle.util.internal.GFileUtils
 import java.io.File
-import java.io.FileOutputStream
-import java.io.OutputStream
 import java.net.URI
 import java.nio.file.Files
 
@@ -78,11 +82,14 @@
     private val directoryFileTreeFactory: DirectoryFileTreeFactory,
     private val report: ConfigurationCacheReport,
     private val problemFactory: ProblemFactory,
-    private val taskExecutionTracker: TaskExecutionTracker,
+    private val workExecutionTracker: WorkExecutionTracker,
     private val environmentChangeTracker: ConfigurationCacheEnvironmentChangeTracker,
     private val inputTrackingState: InputTrackingState,
     private val scriptFileResolverListeners: ScriptFileResolverListeners,
-    private val remoteScriptUpToDateChecker: RemoteScriptUpToDateChecker
+    private val remoteScriptUpToDateChecker: RemoteScriptUpToDateChecker,
+    private val agentStatus: AgentStatus,
+    private val problems: ConfigurationCacheProblems,
+    private val encryptionService: EncryptionService
 ) : Stoppable {
 
     interface Host {
@@ -96,7 +103,7 @@
     private
     abstract class WritingState {
 
-        open fun maybeStart(buildScopedSpoolFile: File, projectScopedSpoolFile: File, writeContextForOutputStream: (OutputStream) -> DefaultWriteContext): WritingState =
+        open fun maybeStart(buildScopedSpoolFile: StateFile, projectScopedSpoolFile: StateFile, writeContextForOutputStream: (StateFile) -> DefaultWriteContext): WritingState =
             illegalStateFor("start")
 
         open fun pause(): WritingState =
@@ -121,16 +128,15 @@
 
     private
     inner class Idle : WritingState() {
-        override fun maybeStart(buildScopedSpoolFile: File, projectScopedSpoolFile: File, writeContextForOutputStream: (OutputStream) -> DefaultWriteContext): WritingState {
-            val buildScopedOutputStream = FileOutputStream(buildScopedSpoolFile)
-            val projectScopedOutputStream = FileOutputStream(projectScopedSpoolFile)
+        override fun maybeStart(buildScopedSpoolFile: StateFile, projectScopedSpoolFile: StateFile, writeContextForOutputStream: (StateFile) -> DefaultWriteContext): WritingState {
+
             val fingerprintWriter = ConfigurationCacheFingerprintWriter(
                 CacheFingerprintWriterHost(),
-                writeContextForOutputStream(buildScopedOutputStream),
-                writeContextForOutputStream(projectScopedOutputStream),
+                writeContextForOutputStream(buildScopedSpoolFile),
+                writeContextForOutputStream(projectScopedSpoolFile),
                 fileCollectionFactory,
                 directoryFileTreeFactory,
-                taskExecutionTracker,
+                workExecutionTracker,
                 environmentChangeTracker,
                 inputTrackingState
             )
@@ -145,10 +151,10 @@
     private
     inner class Writing(
         private val fingerprintWriter: ConfigurationCacheFingerprintWriter,
-        private val buildScopedSpoolFile: File,
-        private val projectScopedSpoolFile: File
+        private val buildScopedSpoolFile: StateFile,
+        private val projectScopedSpoolFile: StateFile
     ) : WritingState() {
-        override fun maybeStart(buildScopedSpoolFile: File, projectScopedSpoolFile: File, writeContextForOutputStream: (OutputStream) -> DefaultWriteContext): WritingState {
+        override fun maybeStart(buildScopedSpoolFile: StateFile, projectScopedSpoolFile: StateFile, writeContextForOutputStream: (StateFile) -> DefaultWriteContext): WritingState {
             return this
         }
 
@@ -168,10 +174,10 @@
     private
     inner class Paused(
         private val fingerprintWriter: ConfigurationCacheFingerprintWriter,
-        private val buildScopedSpoolFile: File,
-        private val projectScopedSpoolFile: File
+        private val buildScopedSpoolFile: StateFile,
+        private val projectScopedSpoolFile: StateFile
     ) : WritingState() {
-        override fun maybeStart(buildScopedSpoolFile: File, projectScopedSpoolFile: File, writeContextForOutputStream: (OutputStream) -> DefaultWriteContext): WritingState {
+        override fun maybeStart(buildScopedSpoolFile: StateFile, projectScopedSpoolFile: StateFile, writeContextForOutputStream: (StateFile) -> DefaultWriteContext): WritingState {
             addListener(fingerprintWriter)
             // Continue with the current spool file, rather than starting a new one
             return Writing(fingerprintWriter, this.buildScopedSpoolFile, this.projectScopedSpoolFile)
@@ -187,18 +193,18 @@
 
         override fun commit(buildScopedFingerprint: ConfigurationCacheStateFile, projectScopedFingerprint: ConfigurationCacheStateFile): WritingState {
             closeStreams()
-            buildScopedFingerprint.moveFrom(buildScopedSpoolFile)
-            projectScopedFingerprint.moveFrom(projectScopedSpoolFile)
+            buildScopedFingerprint.moveFrom(buildScopedSpoolFile.file)
+            projectScopedFingerprint.moveFrom(projectScopedSpoolFile.file)
             return Committed()
         }
 
         override fun dispose(): WritingState {
             closeStreams()
-            if (buildScopedSpoolFile.exists()) {
-                Files.delete(buildScopedSpoolFile.toPath())
+            if (buildScopedSpoolFile.file.exists()) {
+                Files.delete(buildScopedSpoolFile.file.toPath())
             }
-            if (projectScopedSpoolFile.exists()) {
-                Files.delete(projectScopedSpoolFile.toPath())
+            if (projectScopedSpoolFile.file.exists()) {
+                Files.delete(projectScopedSpoolFile.file.toPath())
             }
             return Idle()
         }
@@ -222,7 +228,7 @@
     // Start fingerprinting if not already started and not already committed
     // This should be strict but currently this method may be called multiple times when a
     // build invocation both runs tasks and queries models
-    fun maybeStartCollectingFingerprint(buildScopedSpoolFile: File, projectScopedSpoolFile: File, writeContextForOutputStream: (OutputStream) -> DefaultWriteContext) {
+    fun maybeStartCollectingFingerprint(buildScopedSpoolFile: StateFile, projectScopedSpoolFile: StateFile, writeContextForOutputStream: (StateFile) -> DefaultWriteContext) {
         writingState = writingState.maybeStart(buildScopedSpoolFile, projectScopedSpoolFile, writeContextForOutputStream)
     }
 
@@ -281,6 +287,12 @@
     inner class CacheFingerprintWriterHost :
         ConfigurationCacheFingerprintWriter.Host {
 
+        override val isEncrypted: Boolean
+            get() = encryptionService.isEncrypting
+
+        override val encryptionKeyHashCode: HashCode
+            get() = encryptionService.encryptionKeyHashCode
+
         override val gradleUserHomeDir: File
             get() = startParameter.gradleUserHomeDir
 
@@ -297,7 +309,7 @@
             get() = modelParameters.isIntermediateModelCache
 
         override val instrumentationAgentUsed: Boolean
-            get() = AgentControl.isInstrumentationAgentApplied()
+            get() = agentStatus.isAgentInstrumentationEnabled
 
         override fun hashCodeOf(file: File) =
             fileSystemAccess.read(file.absolutePath).hash
@@ -314,6 +326,9 @@
         override fun reportInput(input: PropertyProblem) =
             report.onInput(input)
 
+        override fun reportProblem(exception: Throwable?, documentationSection: DocumentationSection?, message: StructuredMessageBuilder) =
+            problems.onProblem(problemFactory.problem(StructuredMessage.build(message), exception, documentationSection))
+
         override fun location(consumer: String?) =
             problemFactory.locationForCaller(consumer)
     }
@@ -326,6 +341,12 @@
         private
         val gradleProperties by lazy(host::gradleProperties)
 
+        override val isEncrypted: Boolean
+            get() = encryptionService.isEncrypting
+
+        override val encryptionKeyHashCode: HashCode
+            get() = encryptionService.encryptionKeyHashCode
+
         override val gradleUserHomeDir: File
             get() = startParameter.gradleUserHomeDir
 
@@ -342,7 +363,7 @@
             get() = modelParameters.isInvalidateCoupledProjects
 
         override val instrumentationAgentUsed: Boolean
-            get() = AgentControl.isInstrumentationAgentApplied()
+            get() = agentStatus.isAgentInstrumentationEnabled
 
         override fun gradleProperty(propertyName: String): String? =
             gradleProperties.find(propertyName)?.uncheckedCast()
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintWriter.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintWriter.kt
index 8db9068..8b6584e 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintWriter.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintWriter.kt
@@ -59,6 +59,7 @@
 import org.gradle.configurationcache.problems.PropertyProblem
 import org.gradle.configurationcache.problems.PropertyTrace
 import org.gradle.configurationcache.problems.StructuredMessage
+import org.gradle.configurationcache.problems.StructuredMessageBuilder
 import org.gradle.configurationcache.serialization.DefaultWriteContext
 import org.gradle.configurationcache.services.ConfigurationCacheEnvironment
 import org.gradle.configurationcache.services.ConfigurationCacheEnvironmentChangeTracker
@@ -67,10 +68,10 @@
 import org.gradle.internal.buildoption.FeatureFlag
 import org.gradle.internal.buildoption.FeatureFlagListener
 import org.gradle.internal.concurrent.CompositeStoppable
-import org.gradle.internal.execution.TaskExecutionTracker
 import org.gradle.internal.execution.UnitOfWork
 import org.gradle.internal.execution.UnitOfWork.InputFileValueSupplier
 import org.gradle.internal.execution.UnitOfWork.InputVisitor
+import org.gradle.internal.execution.WorkExecutionTracker
 import org.gradle.internal.execution.WorkInputListener
 import org.gradle.internal.hash.HashCode
 import org.gradle.internal.properties.InputBehavior
@@ -90,7 +91,7 @@
     projectScopedContext: DefaultWriteContext,
     private val fileCollectionFactory: FileCollectionFactory,
     private val directoryFileTreeFactory: DirectoryFileTreeFactory,
-    private val taskExecutionTracker: TaskExecutionTracker,
+    private val workExecutionTracker: WorkExecutionTracker,
     private val environmentChangeTracker: ConfigurationCacheEnvironmentChangeTracker,
     private val inputTrackingState: InputTrackingState,
 ) : ValueSourceProviderFactory.ValueListener,
@@ -109,6 +110,8 @@
     ConfigurationCacheEnvironment.Listener {
 
     interface Host {
+        val isEncrypted: Boolean
+        val encryptionKeyHashCode: HashCode
         val gradleUserHomeDir: File
         val allInitScripts: List<File>
         val startParameterProperties: Map<String, Any?>
@@ -119,6 +122,7 @@
         fun hashCodeOf(file: File): HashCode
         fun hashCodeOfDirectoryContent(file: File): HashCode
         fun displayNameOf(file: File): String
+        fun reportProblem(exception: Throwable? = null, documentationSection: DocumentationSection? = null, message: StructuredMessageBuilder)
         fun reportInput(input: PropertyProblem)
         fun location(consumer: String?): PropertyTrace
     }
@@ -169,8 +173,7 @@
     var closestChangingValue: ConfigurationCacheFingerprint.ChangingDependencyResolutionValue? = null
 
     init {
-        val initScripts = host.allInitScripts
-        buildScopedSink.initScripts(initScripts)
+        buildScopedSink.initScripts(host.allInitScripts)
         buildScopedSink.write(
             ConfigurationCacheFingerprint.GradleEnvironment(
                 host.gradleUserHomeDir,
@@ -239,7 +242,7 @@
     fun isInputTrackingDisabled() = !inputTrackingState.isEnabledForCurrentThread()
 
     private
-    fun isExecutingTask() = taskExecutionTracker.currentTask.isPresent
+    fun isExecutingWork() = workExecutionTracker.currentTask.isPresent || workExecutionTracker.isExecutingTransformAction
 
     override fun fileObserved(file: File) {
         fileObserved(file, null)
@@ -261,7 +264,7 @@
     }
 
     override fun directoryChildrenObserved(directory: File, consumer: String?) {
-        if (isInputTrackingDisabled() || isExecutingTask()) {
+        if (isInputTrackingDisabled() || isExecutingWork()) {
             return
         }
         sink().captureDirectoryChildren(directory)
@@ -269,7 +272,7 @@
     }
 
     override fun fileSystemEntryObserved(file: File, consumer: String?) {
-        if (isInputTrackingDisabled() || isExecutingTask()) {
+        if (isInputTrackingDisabled() || isExecutingWork()) {
             return
         }
         sink().captureFileSystemEntry(file)
@@ -318,7 +321,7 @@
     }
 
     override fun fileOpened(file: File, consumer: String?) {
-        if (isInputTrackingDisabled() || isExecutingTask()) {
+        if (isInputTrackingDisabled() || isExecutingWork()) {
             // Ignore files that are read as part of the task actions. These should really be task
             // inputs. Otherwise, we risk fingerprinting files such as:
             // - temporary files that will be gone at the end of the build.
@@ -330,7 +333,7 @@
     }
 
     override fun fileCollectionObserved(fileCollection: FileCollectionInternal) {
-        if (isInputTrackingDisabled() || isExecutingTask()) {
+        if (isInputTrackingDisabled() || isExecutingWork()) {
             // See #fileOpened() above
             return
         }
@@ -381,6 +384,18 @@
         obtainedValue: ValueSourceProviderFactory.ValueListener.ObtainedValue<T, P>,
         source: org.gradle.api.provider.ValueSource<T, P>
     ) {
+        obtainedValue.value.failure.ifPresent { exception ->
+            host.reportProblem(exception) {
+                text("failed to compute value with custom source ")
+                reference(obtainedValue.valueSourceType)
+                source.displayNameIfAvailable?.let {
+                    text(" ($it)")
+                }
+                text(" with ")
+                text(exception.toString())
+            }
+        }
+
         // TODO(https://github.com/gradle/gradle/issues/22494) ValueSources become part of the fingerprint even if they are only obtained
         //  inside other value sources. This is not really necessary for the correctness and causes excessive cache invalidation.
         when (val parameters = obtainedValue.valueSourceParameters) {
@@ -426,12 +441,14 @@
             }
 
             else -> {
-                sink().write(ValueSource(obtainedValue.uncheckedCast()))
+                // Custom ValueSource implementations may fail to serialize here.
+                // Writing with explicit trace helps to avoid attributing these failures to "Gradle runtime".
+                // TODO(mlopatkin): can we do even better and pinpoint the exact stacktrace in case of failure?
+                val trace = locationFor(null)
+                sink().write(ValueSource(obtainedValue.uncheckedCast()), trace)
                 reportUniqueValueSourceInput(
-                    displayName = when (source) {
-                        is Describable -> source.displayName
-                        else -> null
-                    },
+                    trace,
+                    displayName = source.displayNameIfAvailable,
                     typeName = obtainedValue.valueSourceType.simpleName
                 )
             }
@@ -439,6 +456,13 @@
     }
 
     private
+    val org.gradle.api.provider.ValueSource<*, *>.displayNameIfAvailable: String?
+        get() = when (this) {
+            is Describable -> displayName
+            else -> null
+        }
+
+    private
     fun isSystemPropertyLoaded(key: String): Boolean {
         return environmentChangeTracker.isSystemPropertyLoaded(key)
     }
@@ -572,16 +596,16 @@
     }
 
     private
-    fun reportUniqueValueSourceInput(displayName: String?, typeName: String) {
+    fun reportUniqueValueSourceInput(trace: PropertyTrace, displayName: String?, typeName: String) {
         // We assume different types won't ever produce identical display names
         if (reportedValueSources.add(displayName ?: typeName)) {
-            reportValueSourceInput(displayName, typeName)
+            reportValueSourceInput(trace, displayName, typeName)
         }
     }
 
     private
-    fun reportValueSourceInput(displayName: String?, typeName: String) {
-        reportInput(consumer = null, documentationSection = null) {
+    fun reportValueSourceInput(trace: PropertyTrace, displayName: String?, typeName: String) {
+        reportInput(trace, documentationSection = null) {
             text("value from custom source ")
             reference(typeName)
             displayName?.let {
@@ -718,9 +742,18 @@
         documentationSection: DocumentationSection?,
         messageBuilder: StructuredMessage.Builder.() -> Unit
     ) {
+        reportInput(locationFor(consumer), documentationSection, messageBuilder)
+    }
+
+    private
+    fun reportInput(
+        trace: PropertyTrace,
+        documentationSection: DocumentationSection?,
+        messageBuilder: StructuredMessage.Builder.() -> Unit
+    ) {
         host.reportInput(
             PropertyProblem(
-                locationFor(consumer),
+                trace,
                 StructuredMessage.build(messageBuilder),
                 null,
                 documentationSection = documentationSection
@@ -787,7 +820,7 @@
             }
         }
 
-        abstract fun write(value: ConfigurationCacheFingerprint)
+        abstract fun write(value: ConfigurationCacheFingerprint, trace: PropertyTrace? = null)
 
         fun inputFile(file: File) =
             InputFile(
@@ -804,8 +837,8 @@
         host: Host,
         private val writer: ScopedFingerprintWriter<ConfigurationCacheFingerprint>
     ) : Sink(host) {
-        override fun write(value: ConfigurationCacheFingerprint) {
-            writer.write(value)
+        override fun write(value: ConfigurationCacheFingerprint, trace: PropertyTrace?) {
+            writer.write(value, trace)
         }
 
         fun initScripts(initScripts: List<File>) {
@@ -824,8 +857,8 @@
         private val project: Path,
         private val writer: ScopedFingerprintWriter<ProjectSpecificFingerprint>
     ) : Sink(host) {
-        override fun write(value: ConfigurationCacheFingerprint) {
-            writer.write(ProjectSpecificFingerprint.ProjectFingerprint(project, value))
+        override fun write(value: ConfigurationCacheFingerprint, trace: PropertyTrace?) {
+            writer.write(ProjectSpecificFingerprint.ProjectFingerprint(project, value), trace)
         }
     }
 
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ScopedFingerprintWriter.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ScopedFingerprintWriter.kt
index b08d740..7eadba7 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ScopedFingerprintWriter.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/fingerprint/ScopedFingerprintWriter.kt
@@ -16,8 +16,10 @@
 
 package org.gradle.configurationcache.fingerprint
 
+import org.gradle.configurationcache.problems.PropertyTrace
 import org.gradle.configurationcache.serialization.DefaultWriteContext
 import org.gradle.configurationcache.serialization.runWriteOperation
+import org.gradle.configurationcache.serialization.withPropertyTrace
 import java.io.Closeable
 
 
@@ -34,9 +36,11 @@
         }
     }
 
-    fun write(value: T) {
+    fun write(value: T, trace: PropertyTrace? = null) {
         synchronized(writeContext) {
-            unsafeWrite(value)
+            withPropertyTrace(trace) {
+                unsafeWrite(value)
+            }
         }
     }
 
@@ -46,4 +50,13 @@
             write(value)
         }
     }
+
+    private
+    inline fun withPropertyTrace(trace: PropertyTrace?, block: ScopedFingerprintWriter<T>.() -> Unit) {
+        if (trace != null) {
+            writeContext.withPropertyTrace(trace) { block() }
+        } else {
+            block()
+        }
+    }
 }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/BuildFlowScope.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/BuildFlowScope.kt
index ab180ae..a9e2770 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/BuildFlowScope.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/BuildFlowScope.kt
@@ -114,7 +114,7 @@
     }
 
     override fun buildFinished(result: BuildResult) {
-        setRequestedTasksResult(result.failure)
+        setBuildWorkResult(result.failure)
         schedulePendingActions()
     }
 
@@ -129,8 +129,8 @@
     }
 
     private
-    fun setRequestedTasksResult(failure: Throwable?) {
-        flowProviders.requestedTasksResult.uncheckedCast<RequestedTasksResultProvider>().apply {
+    fun setBuildWorkResult(failure: Throwable?) {
+        flowProviders.buildWorkResult.uncheckedCast<BuildWorkResultProvider>().apply {
             set { Optional.ofNullable(failure) }
         }
     }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/DefaultFlowProviders.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/DefaultFlowProviders.kt
index 2c9e62f..8b6b610 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/DefaultFlowProviders.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/DefaultFlowProviders.kt
@@ -16,8 +16,8 @@
 
 package org.gradle.configurationcache.flow
 
+import org.gradle.api.flow.BuildWorkResult
 import org.gradle.api.flow.FlowProviders
-import org.gradle.api.flow.RequestedTasksResult
 import org.gradle.api.internal.provider.AbstractMinimalProvider
 import org.gradle.api.internal.provider.ValueSupplier
 import org.gradle.api.provider.Provider
@@ -29,36 +29,36 @@
 class DefaultFlowProviders : FlowProviders {
 
     private
-    val requestedTasksResult by lazy {
-        RequestedTasksResultProvider()
+    val buildWorkResult by lazy {
+        BuildWorkResultProvider()
     }
 
-    override fun getRequestedTasksResult(): Provider<RequestedTasksResult> =
-        requestedTasksResult
+    override fun getBuildWorkResult(): Provider<BuildWorkResult> =
+        buildWorkResult
 }
 
 
 internal
-class RequestedTasksResultProvider : AbstractMinimalProvider<RequestedTasksResult>() {
+class BuildWorkResultProvider : AbstractMinimalProvider<BuildWorkResult>() {
 
     private
-    var result: RequestedTasksResult? = null
+    var result: BuildWorkResult? = null
 
-    fun set(result: RequestedTasksResult) {
+    fun set(result: BuildWorkResult) {
         require(this.result == null)
         this.result = result
     }
 
-    override fun getType(): Class<RequestedTasksResult> = RequestedTasksResult::class.java
+    override fun getType(): Class<BuildWorkResult> =
+        BuildWorkResult::class.java
 
-    override fun calculateOwnValue(consumer: ValueSupplier.ValueConsumer): ValueSupplier.Value<out RequestedTasksResult> {
+    override fun calculateOwnValue(consumer: ValueSupplier.ValueConsumer): ValueSupplier.Value<out BuildWorkResult> {
         require(result != null) {
-            "Cannot access flow provider value before it becomes available!"
+            "Cannot access the value of '${BuildWorkResult::class.simpleName}' before it becomes available!"
         }
         return ValueSupplier.Value.ofNullable(result)
     }
 
-    override fun calculateExecutionTimeValue(): ValueSupplier.ExecutionTimeValue<out RequestedTasksResult> {
-        return ValueSupplier.ExecutionTimeValue.changingValue(this)
-    }
+    override fun calculateExecutionTimeValue(): ValueSupplier.ExecutionTimeValue<out BuildWorkResult> =
+        ValueSupplier.ExecutionTimeValue.changingValue(this)
 }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/FlowParametersInstantiator.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/FlowParametersInstantiator.kt
index d72eac8..d110977 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/FlowParametersInstantiator.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/flow/FlowParametersInstantiator.kt
@@ -16,17 +16,23 @@
 
 package org.gradle.configurationcache.flow
 
+import com.google.common.collect.ImmutableMap
+import org.gradle.api.Task
 import org.gradle.api.flow.FlowParameters
 import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.tasks.AbstractTaskDependencyResolveContext
 import org.gradle.api.internal.tasks.properties.InspectionSchemeFactory
+import org.gradle.api.services.BuildService
 import org.gradle.api.services.ServiceReference
 import org.gradle.api.tasks.Input
 import org.gradle.internal.instantiation.InstantiatorFactory
 import org.gradle.internal.properties.PropertyValue
 import org.gradle.internal.properties.PropertyVisitor
+import org.gradle.internal.reflect.DefaultTypeValidationContext
 import org.gradle.internal.reflect.ProblemRecordingTypeValidationContext
-import org.gradle.internal.reflect.problems.ValidationProblemId
+import org.gradle.internal.reflect.validation.Severity
 import org.gradle.internal.reflect.validation.TypeValidationProblem
+import org.gradle.internal.reflect.validation.TypeValidationProblemRenderer
 import org.gradle.internal.service.ServiceRegistry
 import org.gradle.internal.service.scopes.Scopes
 import org.gradle.internal.service.scopes.ServiceScope
@@ -49,24 +55,41 @@
 
     private
     fun <P : FlowParameters> validate(type: Class<P>, parameters: P) {
+        val problems = ImmutableMap.builder<String, Severity>()
         inspection.propertyWalker.visitProperties(
             parameters,
             object : ProblemRecordingTypeValidationContext(DocumentationRegistry(), type, { Optional.empty() }) {
                 override fun recordProblem(problem: TypeValidationProblem) {
-                    if (problem.id != ValidationProblemId.MISSING_ANNOTATION) {
-                        TODO("${where(problem)}: ${problem.shortDescription}")
-                    }
+                    problems.put(
+                        TypeValidationProblemRenderer.renderMinimalInformationAbout(problem),
+                        problem.severity
+                    )
                 }
             },
             object : PropertyVisitor {
-                override fun visitServiceReference(propertyName: String, optional: Boolean, value: PropertyValue, serviceName: String?) {
+                override fun visitServiceReference(propertyName: String, optional: Boolean, value: PropertyValue, serviceName: String?, buildServiceType: Class<out BuildService<*>>) {
                     value.maybeFinalizeValue()
                 }
 
                 override fun visitInputProperty(propertyName: String, value: PropertyValue, optional: Boolean) {
+
+                    val taskDependencies = value.taskDependencies
+                    taskDependencies.visitDependencies(
+                        object : AbstractTaskDependencyResolveContext() {
+                            override fun add(dependency: Any) {
+                                problems.put(
+                                    "Property '$propertyName' cannot carry a dependency on $dependency as these are not yet supported.",
+                                    Severity.ERROR
+                                )
+                            }
+
+                            override fun getTask(): Task? = null
+                        }
+                    )
                 }
             }
         )
+        DefaultTypeValidationContext.throwOnProblemsOf(type, problems.build())
     }
 
     private
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/AbstractInjectedClasspathInstrumentationStrategy.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/AbstractInjectedClasspathInstrumentationStrategy.kt
index 3cb18bf..b4d1d4c 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/AbstractInjectedClasspathInstrumentationStrategy.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/AbstractInjectedClasspathInstrumentationStrategy.kt
@@ -16,6 +16,7 @@
 
 package org.gradle.configurationcache.initialization
 
+import org.gradle.internal.agents.AgentUtils
 import org.gradle.internal.classpath.CachedClasspathTransformer
 import org.gradle.plugin.use.resolve.service.internal.InjectedClasspathInstrumentationStrategy
 import java.lang.management.ManagementFactory
@@ -23,7 +24,7 @@
 
 abstract class AbstractInjectedClasspathInstrumentationStrategy : InjectedClasspathInstrumentationStrategy {
     override fun getTransform(): CachedClasspathTransformer.StandardTransform {
-        val isThirdPartyAgentPresent = ManagementFactory.getRuntimeMXBean().inputArguments.find { isThirdPartyJavaAgentSwitch(it) } != null
+        val isThirdPartyAgentPresent = ManagementFactory.getRuntimeMXBean().inputArguments.find { AgentUtils.isThirdPartyJavaAgentSwitch(it) } != null
         return if (isThirdPartyAgentPresent) {
             // Currently, the build logic instrumentation can interfere with Java agents, such as Jacoco
             // So, disable or fail or whatever based on which execution modes are enabled
@@ -33,10 +34,5 @@
         }
     }
 
-    private
-    fun isThirdPartyJavaAgentSwitch(argument: String): Boolean {
-        return argument.startsWith("-javaagent:") && !argument.contains("gradle-instrumentation-agent")
-    }
-
     abstract fun whenThirdPartyAgentPresent(): CachedClasspathTransformer.StandardTransform
 }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/ConfigurationCacheProblemsListener.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/ConfigurationCacheProblemsListener.kt
index 2b7737f..75e3855 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/ConfigurationCacheProblemsListener.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/ConfigurationCacheProblemsListener.kt
@@ -19,7 +19,6 @@
 import org.gradle.api.InvalidUserCodeException
 import org.gradle.api.internal.BuildScopeListenerRegistrationListener
 import org.gradle.api.internal.ExternalProcessStartedListener
-import org.gradle.api.internal.FeaturePreviews
 import org.gradle.api.internal.GeneratedSubclasses
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.SettingsInternal.BUILD_SRC
@@ -28,6 +27,7 @@
 import org.gradle.api.internal.provider.ConfigurationTimeBarrier
 import org.gradle.api.internal.tasks.execution.TaskExecutionAccessListener
 import org.gradle.configurationcache.InputTrackingState
+import org.gradle.configurationcache.problems.DocumentationSection
 import org.gradle.configurationcache.problems.DocumentationSection.RequirementsBuildListeners
 import org.gradle.configurationcache.problems.DocumentationSection.RequirementsExternalProcess
 import org.gradle.configurationcache.problems.DocumentationSection.RequirementsSafeCredentials
@@ -37,15 +37,17 @@
 import org.gradle.configurationcache.problems.PropertyProblem
 import org.gradle.configurationcache.problems.PropertyTrace
 import org.gradle.configurationcache.problems.StructuredMessage
+import org.gradle.configurationcache.serialization.Workarounds.canAccessConventions
+import org.gradle.execution.ExecutionAccessListener
 import org.gradle.internal.buildoption.FeatureFlags
-import org.gradle.internal.execution.TaskExecutionTracker
+import org.gradle.internal.execution.WorkExecutionTracker
 import org.gradle.internal.service.scopes.ListenerService
 import org.gradle.internal.service.scopes.Scopes
 import org.gradle.internal.service.scopes.ServiceScope
 
 
 @ServiceScope(Scopes.BuildTree::class)
-interface ConfigurationCacheProblemsListener : TaskExecutionAccessListener, BuildScopeListenerRegistrationListener, ExternalProcessStartedListener, CredentialListener
+interface ConfigurationCacheProblemsListener : ExecutionAccessListener, TaskExecutionAccessListener, BuildScopeListenerRegistrationListener, ExternalProcessStartedListener, CredentialListener
 
 
 @ListenerService
@@ -53,27 +55,44 @@
     private val problems: ProblemsListener,
     private val problemFactory: ProblemFactory,
     private val configurationTimeBarrier: ConfigurationTimeBarrier,
-    private val taskExecutionTracker: TaskExecutionTracker,
+    private val workExecutionTracker: WorkExecutionTracker,
     private val featureFlags: FeatureFlags,
-    private val inputTrackingState: InputTrackingState,
+    private val inputTrackingState: InputTrackingState
 ) : ConfigurationCacheProblemsListener {
 
-    override fun onProjectAccess(invocationDescription: String, task: TaskInternal) {
-        if (atConfigurationTime()) {
-            return
-        }
-        onTaskExecutionAccessProblem(invocationDescription, task)
+    override fun disallowedAtExecutionInjectedServiceAccessed(injectedServiceType: Class<*>, getterName: String, consumer: String) {
+        problems.onProblem(
+            PropertyProblem(
+                problemFactory.locationForCaller(consumer),
+                StructuredMessage.build {
+                    text("accessing non-serializable type ")
+                    reference(injectedServiceType)
+                    text(" caused by invocation ")
+                    reference(getterName)
+                },
+                InvalidUserCodeException("Accessing non-serializable type '$injectedServiceType' during execution time is unsupported."),
+                DocumentationSection.RequirementsDisallowedTypes
+            )
+        )
     }
 
-    override fun onTaskDependenciesAccess(invocationDescription: String, task: TaskInternal) {
-        if (atConfigurationTime()) {
+    override fun onProjectAccess(invocationDescription: String, task: TaskInternal, runningTask: TaskInternal?) {
+        onTaskExecutionAccessProblem(invocationDescription, task, runningTask)
+    }
+
+    override fun onConventionAccess(invocationDescription: String, task: TaskInternal, runningTask: TaskInternal?) {
+        if (canAccessConventions(task.javaClass.name, invocationDescription)) {
             return
         }
-        onTaskExecutionAccessProblem(invocationDescription, task)
+        onTaskExecutionAccessProblem(invocationDescription, task, runningTask)
+    }
+
+    override fun onTaskDependenciesAccess(invocationDescription: String, task: TaskInternal, runningTask: TaskInternal?) {
+        onTaskExecutionAccessProblem(invocationDescription, task, runningTask)
     }
 
     override fun onExternalProcessStarted(command: String, consumer: String?) {
-        if (!isStableConfigurationCacheEnabled() || !atConfigurationTime() || isExecutingTask() || isInputTrackingDisabled()) {
+        if (!atConfigurationTime() || isExecutingWork() || isInputTrackingDisabled()) {
             return
         }
         problems.onProblem(
@@ -90,16 +109,37 @@
     }
 
     private
-    fun onTaskExecutionAccessProblem(invocationDescription: String, task: TaskInternal) {
-        problemsListenerFor(task).onProblem(
+    fun onTaskExecutionAccessProblem(invocationDescription: String, task: TaskInternal, runningTask: TaskInternal?) {
+        // It is possible that another task is being executed now and causes `task` to be configured.
+        // In this case, we shouldn't attribute the error to the `task` alone, as it can be misleading,
+        // and that usage can be benign. This is especially important when the currently running task is
+        // marked as `notCompatibleWithConfigurationCache` - attributing the error to `task` will cause
+        // the build to fail when it really shouldn't.
+        val contextTask = runningTask ?: task
+        val isExecutingOtherTask = contextTask != task
+        problemsListenerFor(contextTask).onProblem(
             problemFactory.problem {
-                text("invocation of ")
-                reference(invocationDescription)
-                text(" at execution time is unsupported.")
+                if (isExecutingOtherTask) {
+                    text("execution of task ")
+                    reference(contextTask.path)
+                    text(" caused invocation of ")
+                    reference(invocationDescription)
+                    text(" in other task at execution time which is unsupported.")
+                } else {
+                    text("invocation of ")
+                    reference(invocationDescription)
+                    text(" at execution time is unsupported.")
+                }
             }
-                .exception("Invocation of '$invocationDescription' by $task at execution time is unsupported.")
+                .exception(
+                    if (isExecutingOtherTask) {
+                        "Execution of $runningTask caused invocation of '$invocationDescription' by $task at execution time which is unsupported."
+                    } else {
+                        "Invocation of '$invocationDescription' by $task at execution time is unsupported."
+                    }
+                )
                 .documentationSection(RequirementsUseProjectDuringExecution)
-                .mapLocation { locationForTask(it, task) }
+                .mapLocation { locationForTask(it, contextTask) }
                 .build()
         )
     }
@@ -166,15 +206,12 @@
         } ?: false
 
     private
-    fun atConfigurationTime() =
-        configurationTimeBarrier.isAtConfigurationTime
+    fun atConfigurationTime() = configurationTimeBarrier.isAtConfigurationTime
+
 
     private
     fun isInputTrackingDisabled() = !inputTrackingState.isEnabledForCurrentThread()
 
     private
-    fun isStableConfigurationCacheEnabled() = featureFlags.isEnabled(FeaturePreviews.Feature.STABLE_CONFIGURATION_CACHE)
-
-    private
-    fun isExecutingTask() = taskExecutionTracker.currentTask.isPresent
+    fun isExecutingWork() = workExecutionTracker.currentTask.isPresent || workExecutionTracker.isExecutingTransformAction
 }
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 e95925c..deacbe6 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
@@ -18,16 +18,19 @@
 
 import org.gradle.StartParameter
 import org.gradle.api.internal.StartParameterInternal
+import org.gradle.api.logging.LogLevel
 import org.gradle.configurationcache.extensions.unsafeLazy
 import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheProblemsOption
 import org.gradle.initialization.layout.BuildLayout
 import org.gradle.internal.Factory
 import org.gradle.internal.buildoption.InternalFlag
 import org.gradle.internal.buildoption.InternalOptions
+import org.gradle.internal.buildoption.StringInternalOption
 import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.internal.deprecation.DeprecationLogger
 import org.gradle.internal.service.scopes.Scopes
 import org.gradle.internal.service.scopes.ServiceScope
+import org.gradle.util.internal.SupportedEncryptionAlgorithm
 import java.io.File
 
 
@@ -36,15 +39,24 @@
     private val buildLayout: BuildLayout,
     private val startParameter: StartParameterInternal,
     options: InternalOptions,
-    modelParameters: BuildModelParameters
+    private val modelParameters: BuildModelParameters
 ) {
-    val loadAfterStore: Boolean = !modelParameters.isRequiresBuildModel && options.getOption(InternalFlag("org.gradle.configuration-cache.internal.load-after-store", true)).get()
+    val loadAfterStore: Boolean = !modelParameters.isRequiresBuildModel && options.getInternalFlag("org.gradle.configuration-cache.internal.load-after-store", true)
 
-    val taskExecutionAccessPreStable: Boolean = options.getOption(InternalFlag("org.gradle.configuration-cache.internal.task-execution-access-pre-stable")).get()
+    val taskExecutionAccessPreStable: Boolean = options.getInternalFlag("org.gradle.configuration-cache.internal.task-execution-access-pre-stable")
+
+    val encryptionRequested: Boolean = options.getInternalFlag("org.gradle.configuration-cache.internal.encryption", true)
+
+    val keystoreDir: String? = options.getInternalString("org.gradle.configuration-cache.internal.key-store-dir", null)
+
+    val encryptionAlgorithm: String = options.getInternalString("org.gradle.configuration-cache.internal.encryption-alg", SupportedEncryptionAlgorithm.AES_ECB_PADDING.transformation)
 
     val gradleProperties: Map<String, Any?>
         get() = startParameter.projectProperties
 
+    val configurationCacheLogLevel: LogLevel
+        get() = modelParameters.configurationCacheLogLevel
+
     val isQuiet: Boolean
         get() = startParameter.isConfigurationCacheQuiet
 
@@ -91,9 +103,6 @@
     val isUpdateDependencyLocks
         get() = startParameter.lockedDependenciesToUpdate.isNotEmpty()
 
-    val isWriteDependencyVerifications
-        get() = startParameter.writeDependencyVerifications.isNotEmpty()
-
     val requestedTaskNames: List<String> by unsafeLazy {
         startParameter.taskNames
     }
@@ -109,4 +118,20 @@
 
     val includedBuilds: List<File>
         get() = startParameter.includedBuilds
+
+    val isBuildScan: Boolean
+        get() = startParameter.isBuildScan
+
+    val isNoBuildScan: Boolean
+        get() = startParameter.isNoBuildScan
 }
+
+
+private
+fun InternalOptions.getInternalFlag(systemPropertyName: String, defaultValue: Boolean = false): Boolean =
+    getOption(InternalFlag(systemPropertyName, defaultValue)).get()
+
+
+private
+fun InternalOptions.getInternalString(systemPropertyName: String, defaultValue: String?) =
+    getOption(StringInternalOption(systemPropertyName, defaultValue)).get()
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/InstrumentedExecutionAccessListenerRegistry.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/InstrumentedExecutionAccessListenerRegistry.kt
new file mode 100644
index 0000000..74d56b9
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/initialization/InstrumentedExecutionAccessListenerRegistry.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.initialization
+
+import org.gradle.configurationcache.InstrumentedExecutionAccessListener
+import org.gradle.internal.classpath.InstrumentedExecutionAccess
+import org.gradle.internal.concurrent.Stoppable
+import org.gradle.internal.service.scopes.ListenerService
+
+
+@ListenerService
+internal
+class InstrumentedExecutionAccessListenerRegistry(
+    instrumentedExecutionAccessListener: InstrumentedExecutionAccessListener
+) : Stoppable {
+
+    init {
+        InstrumentedExecutionAccess.setListener(instrumentedExecutionAccessListener)
+    }
+
+    override fun stop() {
+        InstrumentedExecutionAccess.discardListener()
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/metadata/ProjectMetadataController.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/metadata/ProjectMetadataController.kt
index e54c13b..7a873d5 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/metadata/ProjectMetadataController.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/metadata/ProjectMetadataController.kt
@@ -16,14 +16,12 @@
 
 package org.gradle.configurationcache.metadata
 
-import com.google.common.collect.ImmutableSet
 import org.gradle.api.Project
 import org.gradle.api.artifacts.ModuleVersionIdentifier
 import org.gradle.api.artifacts.component.ComponentIdentifier
 import org.gradle.api.artifacts.component.ComponentSelector
 import org.gradle.api.internal.attributes.EmptySchema
 import org.gradle.api.internal.attributes.ImmutableAttributes
-import org.gradle.api.internal.initialization.RootScriptDomainObjectContext
 import org.gradle.configurationcache.ConfigurationCacheIO
 import org.gradle.configurationcache.ConfigurationCacheStateStore
 import org.gradle.configurationcache.DefaultConfigurationCache
@@ -33,7 +31,6 @@
 import org.gradle.configurationcache.serialization.ReadContext
 import org.gradle.configurationcache.serialization.WriteContext
 import org.gradle.configurationcache.serialization.ownerService
-import org.gradle.configurationcache.serialization.readCollection
 import org.gradle.configurationcache.serialization.readList
 import org.gradle.configurationcache.serialization.readNonNull
 import org.gradle.configurationcache.serialization.runReadOperation
@@ -41,16 +38,19 @@
 import org.gradle.configurationcache.serialization.writeCollection
 import org.gradle.internal.Describables
 import org.gradle.internal.component.external.model.ImmutableCapabilities
-import org.gradle.internal.component.local.model.BuildableLocalConfigurationMetadata
-import org.gradle.internal.component.local.model.DefaultLocalComponentGraphResolveState
 import org.gradle.internal.component.local.model.DefaultLocalComponentMetadata
+import org.gradle.internal.component.local.model.DefaultLocalConfigurationMetadata
 import org.gradle.internal.component.local.model.LocalComponentGraphResolveState
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory
 import org.gradle.internal.component.local.model.LocalComponentMetadata
 import org.gradle.internal.component.local.model.LocalConfigurationGraphResolveMetadata
+import org.gradle.internal.component.local.model.LocalConfigurationMetadata
+import org.gradle.internal.component.local.model.LocalVariantMetadata
 import org.gradle.internal.component.local.model.PublishArtifactLocalArtifactMetadata
+import org.gradle.internal.component.model.DependencyMetadata
 import org.gradle.internal.component.model.LocalComponentDependencyMetadata
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata
 import org.gradle.internal.component.model.VariantResolveMetadata
+import org.gradle.internal.model.CalculatedValueContainerFactory
 import org.gradle.internal.serialize.Decoder
 import org.gradle.internal.serialize.Encoder
 import org.gradle.util.Path
@@ -60,6 +60,7 @@
 class ProjectMetadataController(
     private val host: DefaultConfigurationCache.Host,
     private val cacheIO: ConfigurationCacheIO,
+    private val resolveStateFactory: LocalComponentGraphResolveStateFactory,
     store: ConfigurationCacheStateStore
 ) : ProjectStateStore<Path, LocalComponentGraphResolveState>(store, StateType.ProjectMetadata) {
 
@@ -91,15 +92,14 @@
 
     private
     suspend fun WriteContext.writeConfiguration(configuration: LocalConfigurationGraphResolveMetadata) {
-        val artifactMetadata = configuration.prepareToResolveArtifacts()
         writeString(configuration.name)
         write(configuration.attributes)
-        writeDependencies(artifactMetadata.dependencies)
-        writeVariants(artifactMetadata.variants)
+        writeDependencies(configuration.dependencies)
+        writeVariants(configuration.prepareToResolveArtifacts().variants)
     }
 
     private
-    suspend fun WriteContext.writeDependencies(dependencies: List<LocalOriginDependencyMetadata>) {
+    suspend fun WriteContext.writeDependencies(dependencies: List<DependencyMetadata>) {
         writeCollection(dependencies) {
             write(it.selector)
             writeBoolean(it.isConstraint)
@@ -127,70 +127,75 @@
         return context.runReadOperation {
             val id = readNonNull<ComponentIdentifier>()
             val moduleVersionId = readNonNull<ModuleVersionIdentifier>()
-            val metadata = DefaultLocalComponentMetadata(moduleVersionId, id, Project.DEFAULT_STATUS, EmptySchema.INSTANCE, RootScriptDomainObjectContext.INSTANCE, ownerService())
-            readConfigurationsInto(metadata)
-            DefaultLocalComponentGraphResolveState(metadata)
+
+            val configurations = readConfigurations(id, ownerService()).associateBy { it.name }
+            val configurationsFactory = DefaultLocalComponentMetadata.ConfigurationsMapMetadataFactory(configurations)
+
+            val metadata = DefaultLocalComponentMetadata(moduleVersionId, id, Project.DEFAULT_STATUS, EmptySchema.INSTANCE, configurationsFactory, null)
+            resolveStateFactory.stateFor(metadata)
         }
     }
 
     private
-    suspend fun ReadContext.readConfigurationsInto(metadata: DefaultLocalComponentMetadata) {
-        readCollection {
-            readConfigurationInto(metadata)
+    suspend fun ReadContext.readConfigurations(componentId: ComponentIdentifier, factory: CalculatedValueContainerFactory): List<LocalConfigurationMetadata> {
+        return readList {
+            readConfiguration(componentId, factory)
         }
     }
 
     private
-    suspend fun ReadContext.readConfigurationInto(metadata: DefaultLocalComponentMetadata) {
+    suspend fun ReadContext.readConfiguration(componentId: ComponentIdentifier, factory: CalculatedValueContainerFactory): LocalConfigurationMetadata {
         val configurationName = readString()
         val configurationAttributes = readNonNull<ImmutableAttributes>()
-        val configuration = metadata.addConfiguration(configurationName, null, emptySet(), ImmutableSet.of(configurationName), true, true, configurationAttributes, true, null, true, ImmutableCapabilities.EMPTY)
-        readDependenciesInto(metadata, configuration)
-        readVariantsInto(configuration)
+        val dependencies = readDependencies(componentId)
+        val variants = readVariants(factory).toSet()
+
+        return DefaultLocalConfigurationMetadata(
+            configurationName, configurationName, componentId, true, true, setOf(configurationName), configurationAttributes,
+            ImmutableCapabilities.EMPTY, true, false, true, dependencies, emptySet(), emptyList(),
+            variants, factory, emptyList()
+        )
     }
 
     private
-    suspend fun ReadContext.readDependenciesInto(metadata: DefaultLocalComponentMetadata, configuration: BuildableLocalConfigurationMetadata) {
-        readCollection {
+    suspend fun ReadContext.readDependencies(componentId: ComponentIdentifier): List<LocalComponentDependencyMetadata> {
+        return readList {
             val selector = readNonNull<ComponentSelector>()
             val constraint = readBoolean()
-            configuration.addDependency(
-                LocalComponentDependencyMetadata(
-                    metadata.id,
-                    selector,
-                    null,
-                    null,
-                    ImmutableAttributes.EMPTY,
-                    null,
-                    emptyList(),
-                    emptyList(),
-                    false,
-                    false,
-                    true,
-                    constraint,
-                    false,
-                    null
-                )
+            LocalComponentDependencyMetadata(
+                componentId,
+                selector,
+                null,
+                null,
+                ImmutableAttributes.EMPTY,
+                null,
+                emptyList(),
+                emptyList(),
+                false,
+                false,
+                true,
+                constraint,
+                false,
+                null
             )
         }
     }
 
     private
-    suspend fun ReadContext.readVariantsInto(configuration: BuildableLocalConfigurationMetadata) {
-        readCollection {
-            readVariantInto(configuration)
+    suspend fun ReadContext.readVariants(factory: CalculatedValueContainerFactory): List<LocalVariantMetadata> {
+        return readList {
+            readVariant(factory)
         }
     }
 
     private
-    suspend fun ReadContext.readVariantInto(configuration: BuildableLocalConfigurationMetadata) {
+    suspend fun ReadContext.readVariant(factory: CalculatedValueContainerFactory): LocalVariantMetadata {
         val variantName = readString()
         val identifier = readNonNull<VariantResolveMetadata.Identifier>()
         val attributes = readNonNull<ImmutableAttributes>()
-        // TODO - don't unpack the artifacts
         val artifacts = readList {
-            readNonNull<PublishArtifactLocalArtifactMetadata>().publishArtifact
+            readNonNull<PublishArtifactLocalArtifactMetadata>()
         }
-        configuration.addVariant(variantName, identifier, Describables.of(variantName), attributes, ImmutableCapabilities.EMPTY, artifacts)
+        return LocalVariantMetadata(variantName, identifier, Describables.of(variantName), attributes, ImmutableCapabilities.EMPTY, artifacts, factory)
     }
 }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/problems/JsonModelWriter.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/problems/JsonModelWriter.kt
index e493723..90e5f32 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/problems/JsonModelWriter.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/problems/JsonModelWriter.kt
@@ -246,8 +246,12 @@
 
     private
     fun jsonString(value: String) {
-        buffer.addJsonEscapedString(value)
-        write(buffer.toStringAndRecycle())
+        if (value.isEmpty()) {
+            write("\"\"")
+        } else {
+            buffer.addJsonEscapedString(value)
+            write(buffer.toStringAndRecycle())
+        }
     }
 
     private
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/problems/PropertyProblem.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/problems/PropertyProblem.kt
index aa59c1c..9e4474c 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/problems/PropertyProblem.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/problems/PropertyProblem.kt
@@ -36,6 +36,7 @@
     NotYetImplementedSourceDependencies("config_cache:not_yet_implemented:source_dependencies"),
     NotYetImplementedJavaSerialization("config_cache:not_yet_implemented:java_serialization"),
     NotYetImplementedTestKitJavaAgent("config_cache:not_yet_implemented:testkit_build_with_java_agent"),
+    NotYetImplementedBuildServiceInFingerprint("config_cache:not_yet_implemented:build_services_in_fingerprint"),
     RequirementsBuildListeners("config_cache:requirements:build_listeners"),
     RequirementsDisallowedTypes("config_cache:requirements:disallowed_types"),
     RequirementsExternalProcess("config_cache:requirements:external_processes"),
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Codec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Codec.kt
index e13a5ae..5307102 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Codec.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Codec.kt
@@ -127,6 +127,14 @@
     class OwnerHost(override val delegate: DefaultConfigurationCache.Host) : IsolateOwner() {
         override fun <T> service(type: Class<T>): T = delegate.service(type)
     }
+
+    class OwnerFlowScope(override val delegate: Gradle) : IsolateOwner() {
+        override fun <T> service(type: Class<T>): T = (delegate as GradleInternal).services.get(type)
+    }
+
+    class OwnerFlowAction(override val delegate: OwnerFlowScope) : IsolateOwner() {
+        override fun <T> service(type: Class<T>): T = delegate.service(type)
+    }
 }
 
 
@@ -158,6 +166,7 @@
     override var trace: PropertyTrace
 
     fun push(codec: Codec<Any?>)
+    fun push(owner: IsolateOwner)
     fun push(owner: IsolateOwner, codec: Codec<Any?>)
     fun pop()
 
@@ -200,6 +209,17 @@
 
 
 internal
+inline fun <T : MutableIsolateContext, R> T.withIsolate(owner: IsolateOwner, block: T.() -> R): R {
+    push(owner)
+    try {
+        return block()
+    } finally {
+        pop()
+    }
+}
+
+
+internal
 inline fun <T : MutableIsolateContext, R> T.withCodec(codec: Codec<Any?>, block: T.() -> R): R {
     push(codec)
     try {
@@ -230,18 +250,18 @@
 
 
 internal
-inline fun WriteContext.encodePreservingIdentityOf(reference: Any, encode: WriteContext.(Any) -> Unit) {
+inline fun <T : Any> WriteContext.encodePreservingIdentityOf(reference: T, encode: WriteContext.(T) -> Unit) {
     encodePreservingIdentityOf(isolate.identities, reference, encode)
 }
 
 
 internal
-inline fun WriteContext.encodePreservingSharedIdentityOf(reference: Any, encode: WriteContext.(Any) -> Unit) =
+inline fun <T : Any> WriteContext.encodePreservingSharedIdentityOf(reference: T, encode: WriteContext.(T) -> Unit) =
     encodePreservingIdentityOf(sharedIdentities, reference, encode)
 
 
 internal
-inline fun WriteContext.encodePreservingIdentityOf(identities: WriteIdentities, reference: Any, encode: WriteContext.(Any) -> Unit) {
+inline fun <T : Any> WriteContext.encodePreservingIdentityOf(identities: WriteIdentities, reference: T, encode: WriteContext.(T) -> Unit) {
     val id = identities.getId(reference)
     if (id != null) {
         writeSmallInt(id)
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Combinators.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Combinators.kt
index 559e49e..52151ab 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Combinators.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Combinators.kt
@@ -340,7 +340,7 @@
 
 
 internal
-fun Encoder.writeFile(file: File?) {
+fun Encoder.writeFile(file: File) {
     BaseSerializerFactory.FILE_SERIALIZER.write(this, file)
 }
 
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Contexts.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Contexts.kt
index 232575c..b0e6154 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Contexts.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Contexts.kt
@@ -394,6 +394,10 @@
         currentCodec = codec
     }
 
+    override fun push(owner: IsolateOwner) {
+        push(owner, currentCodec)
+    }
+
     override fun push(owner: IsolateOwner, codec: Codec<Any?>) {
         contexts.add(0, Pair(currentIsolate, currentCodec))
         currentIsolate = newIsolate(owner)
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Logging.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Logging.kt
index 31b86cb..a51b001 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Logging.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Logging.kt
@@ -77,18 +77,13 @@
 ) {
     logPropertyProblem(
         action,
-        PropertyProblem(
-            trace,
-            build {
-                text("cannot ")
-                text(action)
-                unsupportedThings()
-                text(" as these are not supported with the configuration cache.")
-            },
-            null,
-            documentationSection
-        )
-    )
+        documentationSection = documentationSection,
+    ) {
+        text("cannot ")
+        text(action)
+        unsupportedThings()
+        text(" as these are not supported with the configuration cache.")
+    }
 }
 
 
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Workarounds.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Workarounds.kt
index d675458..54c38b6 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Workarounds.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/Workarounds.kt
@@ -16,6 +16,7 @@
 
 package org.gradle.configurationcache.serialization
 
+import org.gradle.api.internal.TaskInternal
 import java.lang.reflect.Field
 
 
@@ -51,10 +52,29 @@
             isBuildScanPlugin(from)
         }
 
+    fun canAccessProjectAtExecutionTime(task: TaskInternal) =
+        withWorkaroundsFor("task-project") {
+            val className = task.javaClass.name
+            className.startsWith("com.android.build.gradle.tasks.ShaderCompile") ||
+                className.startsWith("com.android.build.gradle.tasks.MapSourceSetPathsTask") ||
+                className.startsWith("com.android.build.gradle.tasks.MergeResources")
+        }
+
+    fun canAccessConventions(from: String, area: String) =
+        withWorkaroundsFor(area) {
+            from.startsWith("com.android.build.gradle.tasks.factory.AndroidUnitTest") || callStackHasElement {
+                isBuildScanPlugin(className)
+            }
+        }
+
+    private
+    inline fun callStackHasElement(stackElementMatcher: StackTraceElement.() -> Boolean) = Thread.currentThread().stackTrace.any(stackElementMatcher)
+
     private
     fun isBuildScanPlugin(from: String): Boolean = from.run {
         startsWith("com.gradle.scan.plugin.internal.")
             || startsWith("com.gradle.enterprise.agent.")
+            || startsWith("com.gradle.enterprise.gradleplugin.testacceleration.")
     }
 
     // TODO(https://github.com/gradle/gradle-org-conventions-plugin/issues/18) Remove the workaround when our conventions plugin is compatible.
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/BindingsBackedCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/BindingsBackedCodec.kt
index 1b27067..704a95e 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/BindingsBackedCodec.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/BindingsBackedCodec.kt
@@ -123,6 +123,7 @@
 
 internal
 class BindingsBuilder(initialBindings: List<Binding>) {
+
     private
     val bindings = ArrayList(initialBindings)
 
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/ClosureCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/ClosureCodec.kt
index 941ee23..678eb42 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/ClosureCodec.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/ClosureCodec.kt
@@ -30,7 +30,8 @@
 import org.gradle.configurationcache.serialization.WriteContext
 import org.gradle.configurationcache.serialization.decodeBean
 import org.gradle.configurationcache.serialization.encodeBean
-import org.gradle.configurationcache.serialization.readNonNull
+import org.gradle.configurationcache.serialization.readEnum
+import org.gradle.configurationcache.serialization.writeEnum
 import org.gradle.groovy.scripts.BasicScript
 import org.gradle.internal.metaobject.ConfigureDelegate
 
@@ -65,28 +66,28 @@
     }
 
     private
-    suspend fun WriteContext.writeReference(value: Any?) {
+    fun WriteContext.writeReference(value: Any?) {
         // Cannot warn about a script reference here, because we don't know whether the closure will attempt to use the script object when it executes,
         // and since almost every closure in a Groovy build script legitimately has the script as an owner, this will generate false problems.
         // So instead, warn when the script object is used by the closure when executing
-        when {
-            value is BasicScript && value.scriptTarget is Project -> write(ClosureReference.Project)
-            value is BasicScript && value.scriptTarget is Settings -> write(ClosureReference.Settings)
-            value is BasicScript && value.scriptTarget is Gradle -> write(ClosureReference.Init)
-            else -> write(ClosureReference.NotScript)
-        }
+        writeEnum(
+            when {
+                value is BasicScript && value.scriptTarget is Project -> ClosureReference.Project
+                value is BasicScript && value.scriptTarget is Settings -> ClosureReference.Settings
+                value is BasicScript && value.scriptTarget is Gradle -> ClosureReference.Init
+                else -> ClosureReference.NotScript
+            }
+        )
     }
 
     private
-    suspend fun ReadContext.readReference(): Any {
-        val reference = readNonNull<ClosureReference>()
-        return when (reference) {
+    fun ReadContext.readReference(): Any =
+        when (readEnum<ClosureReference>()) {
             ClosureReference.Project -> BrokenScript(Project::class.java)
             ClosureReference.Settings -> BrokenScript(Settings::class.java)
             ClosureReference.Init -> BrokenScript(Gradle::class.java)
             ClosureReference.NotScript -> BrokenObject
         }
-    }
 
     private
     enum class ClosureReference {
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/Codecs.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/Codecs.kt
index 36923cd..969ecc6 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/Codecs.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/Codecs.kt
@@ -22,9 +22,10 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSetToFileCollectionFactory
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.BuildIdentifierSerializer
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.CapabilitySerializer
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformActionScheme
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformParameterScheme
-import org.gradle.api.internal.artifacts.transform.TransformationNode
+import org.gradle.api.internal.artifacts.transform.TransformActionScheme
+import org.gradle.api.internal.artifacts.transform.TransformParameterScheme
+import org.gradle.api.internal.artifacts.transform.TransformStepNode
+import org.gradle.api.internal.artifacts.transform.TransformStepNodeFactory
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory
 import org.gradle.api.internal.file.FileCollectionFactory
 import org.gradle.api.internal.file.FileFactory
@@ -42,15 +43,15 @@
 import org.gradle.configurationcache.serialization.codecs.jos.JavaObjectSerializationCodec
 import org.gradle.configurationcache.serialization.codecs.jos.JavaSerializationEncodingLookup
 import org.gradle.configurationcache.serialization.codecs.transform.CalculateArtifactsCodec
-import org.gradle.configurationcache.serialization.codecs.transform.ChainedTransformationNodeCodec
+import org.gradle.configurationcache.serialization.codecs.transform.ChainedTransformStepNodeCodec
 import org.gradle.configurationcache.serialization.codecs.transform.ComponentVariantIdentifierCodec
-import org.gradle.configurationcache.serialization.codecs.transform.DefaultTransformerCodec
+import org.gradle.configurationcache.serialization.codecs.transform.DefaultTransformCodec
 import org.gradle.configurationcache.serialization.codecs.transform.FinalizeTransformDependenciesNodeCodec
-import org.gradle.configurationcache.serialization.codecs.transform.InitialTransformationNodeCodec
-import org.gradle.configurationcache.serialization.codecs.transform.IsolateTransformerParametersNodeCodec
+import org.gradle.configurationcache.serialization.codecs.transform.InitialTransformStepNodeCodec
+import org.gradle.configurationcache.serialization.codecs.transform.IsolateTransformParametersCodec
+import org.gradle.configurationcache.serialization.codecs.transform.TransformChainCodec
+import org.gradle.configurationcache.serialization.codecs.transform.TransformStepCodec
 import org.gradle.configurationcache.serialization.codecs.transform.TransformStepSpecCodec
-import org.gradle.configurationcache.serialization.codecs.transform.TransformationChainCodec
-import org.gradle.configurationcache.serialization.codecs.transform.TransformationStepCodec
 import org.gradle.configurationcache.serialization.codecs.transform.TransformedArtifactCodec
 import org.gradle.configurationcache.serialization.codecs.transform.TransformedExternalArtifactSetCodec
 import org.gradle.configurationcache.serialization.codecs.transform.TransformedProjectArtifactSetCodec
@@ -104,8 +105,8 @@
     classLoaderHierarchyHasher: ClassLoaderHierarchyHasher,
     isolatableFactory: IsolatableFactory,
     managedFactoryRegistry: ManagedFactoryRegistry,
-    parameterScheme: ArtifactTransformParameterScheme,
-    actionScheme: ArtifactTransformActionScheme,
+    parameterScheme: TransformParameterScheme,
+    actionScheme: TransformActionScheme,
     attributesFactory: ImmutableAttributesFactory,
     valueSourceProviderFactory: ValueSourceProviderFactory,
     calculatedValueContainerFactory: CalculatedValueContainerFactory,
@@ -117,105 +118,136 @@
     documentationRegistry: DocumentationRegistry,
     javaSerializationEncodingLookup: JavaSerializationEncodingLookup,
     flowProviders: FlowProviders,
+    transformStepNodeFactory: TransformStepNodeFactory,
 ) {
     private
-    val userTypesBindings = Bindings.of {
+    val userTypesBindings: Bindings
 
-        unsupportedTypes()
+    private
+    val fingerprintUserTypesBindings: Bindings
 
-        baseTypes()
+    init {
+        fun makeUserTypeBindings(providersBlock: BindingsBuilder.() -> Unit) = Bindings.of {
+            unsupportedTypes()
 
-        bind(HASHCODE_SERIALIZER)
+            baseTypes()
 
-        providerTypes(
-            propertyFactory,
-            filePropertyFactory,
-            valueSourceProviderFactory,
-            buildStateRegistry,
-            flowProviders
-        )
+            bind(HASHCODE_SERIALIZER)
 
-        bind(ListenerBroadcastCodec(listenerManager))
-        bind(LoggerCodec)
+            providersBlock()
 
-        fileCollectionTypes(directoryFileTreeFactory, fileCollectionFactory, artifactSetConverter, fileOperations, fileFactory, patternSetFactory)
+            bind(ListenerBroadcastCodec(listenerManager))
+            bind(LoggerCodec)
 
-        bind(ApiTextResourceAdapterCodec)
+            fileCollectionTypes(directoryFileTreeFactory, fileCollectionFactory, artifactSetConverter, fileOperations, fileFactory, patternSetFactory)
 
-        bind(ClosureCodec)
-        bind(GroovyMetaClassCodec)
-        bind(SerializedLambdaParametersCheckingCodec)
+            bind(ApiTextResourceAdapterCodec)
 
-        // Dependency management types
-        bind(ArtifactCollectionCodec(calculatedValueContainerFactory, artifactSetConverter))
-        bind(ImmutableAttributesCodec(attributesFactory, managedFactoryRegistry))
-        bind(AttributeContainerCodec(attributesFactory, managedFactoryRegistry))
-        bind(ComponentVariantIdentifierCodec)
-        bind(InitialTransformationNodeCodec(buildOperationExecutor, calculatedValueContainerFactory))
-        bind(ChainedTransformationNodeCodec(buildOperationExecutor, calculatedValueContainerFactory))
-        bind(TransformationStepCodec(inputFingerprinter))
-        bind(TransformationChainCodec())
-        bind(DefaultTransformerCodec(fileLookup, actionScheme))
-        bind(DefaultResolvableArtifactCodec(calculatedValueContainerFactory))
-        bind(TransformStepSpecCodec)
-        bind(PublishArtifactLocalArtifactMetadataCodec)
-        bind(TransformedProjectArtifactSetCodec())
-        bind(TransformedExternalArtifactSetCodec())
-        bind(CalculateArtifactsCodec(calculatedValueContainerFactory))
-        bind(TransformedArtifactCodec(calculatedValueContainerFactory))
-        bind(LocalFileDependencyBackedArtifactSetCodec(instantiator, attributesFactory, calculatedValueContainerFactory))
-        bind(CalculatedValueContainerCodec(calculatedValueContainerFactory))
-        bind(IsolateTransformerParametersNodeCodec(parameterScheme, isolatableFactory, buildOperationExecutor, classLoaderHierarchyHasher, fileCollectionFactory, documentationRegistry))
-        bind(FinalizeTransformDependenciesNodeCodec())
-        bind(ResolveArtifactNodeCodec)
-        bind(WorkNodeActionCodec)
-        bind(CapabilitySerializer())
+            bind(ClosureCodec)
+            bind(GroovyMetaClassCodec)
+            bind(SerializedLambdaParametersCheckingCodec)
 
-        bind(DefaultCopySpecCodec(patternSetFactory, fileCollectionFactory, instantiator))
-        bind(DestinationRootCopySpecCodec(fileResolver))
+            // Dependency management types
+            bind(ArtifactCollectionCodec(calculatedValueContainerFactory, artifactSetConverter))
+            bind(ImmutableAttributesCodec(attributesFactory, managedFactoryRegistry))
+            bind(AttributeContainerCodec(attributesFactory, managedFactoryRegistry))
+            bind(ComponentVariantIdentifierCodec)
+            bind(InitialTransformStepNodeCodec(transformStepNodeFactory, buildOperationExecutor, calculatedValueContainerFactory))
+            bind(ChainedTransformStepNodeCodec(transformStepNodeFactory, buildOperationExecutor, calculatedValueContainerFactory))
+            bind(TransformStepCodec(inputFingerprinter))
+            bind(TransformChainCodec())
+            bind(DefaultTransformCodec(fileLookup, actionScheme))
+            bind(DefaultResolvableArtifactCodec(calculatedValueContainerFactory))
+            bind(TransformStepSpecCodec)
+            bind(PublishArtifactLocalArtifactMetadataCodec)
+            bind(TransformedProjectArtifactSetCodec())
+            bind(TransformedExternalArtifactSetCodec())
+            bind(CalculateArtifactsCodec(calculatedValueContainerFactory))
+            bind(TransformedArtifactCodec(calculatedValueContainerFactory))
+            bind(LocalFileDependencyBackedArtifactSetCodec(instantiator, attributesFactory, calculatedValueContainerFactory))
+            bind(CalculatedValueContainerCodec(calculatedValueContainerFactory))
+            bind(IsolateTransformParametersCodec(parameterScheme, isolatableFactory, buildOperationExecutor, classLoaderHierarchyHasher, fileCollectionFactory, documentationRegistry))
+            bind(FinalizeTransformDependenciesNodeCodec())
+            bind(ResolveArtifactNodeCodec)
+            bind(WorkNodeActionCodec)
+            bind(CapabilitySerializer())
 
-        bind(TaskReferenceCodec)
+            bind(DefaultCopySpecCodec(patternSetFactory, fileCollectionFactory, instantiator))
+            bind(DestinationRootCopySpecCodec(fileResolver))
 
-        bind(CachedEnvironmentStateCodec)
+            bind(TaskReferenceCodec)
 
-        bind(IsolatedManagedValueCodec(managedFactoryRegistry))
-        bind(IsolatedImmutableManagedValueCodec(managedFactoryRegistry))
-        bind(IsolatedJavaSerializedValueSnapshotCodec)
-        bind(IsolatedArrayCodec)
-        bind(IsolatedSetCodec)
-        bind(IsolatedListCodec)
-        bind(IsolatedMapCodec)
-        bind(MapEntrySnapshotCodec)
-        bind(IsolatedEnumValueSnapshotCodec)
-        bind(StringValueSnapshotCodec)
-        bind(IntegerValueSnapshotCodec)
-        bind(FileValueSnapshotCodec)
-        bind(BooleanValueSnapshotCodec)
-        bind(NullValueSnapshotCodec)
+            bind(CachedEnvironmentStateCodec)
 
-        bind(ServicesCodec())
+            bind(IsolatedManagedValueCodec(managedFactoryRegistry))
+            bind(IsolatedImmutableManagedValueCodec(managedFactoryRegistry))
+            bind(IsolatedJavaSerializedValueSnapshotCodec)
+            bind(IsolatedArrayCodec)
+            bind(IsolatedSetCodec)
+            bind(IsolatedListCodec)
+            bind(IsolatedMapCodec)
+            bind(MapEntrySnapshotCodec)
+            bind(IsolatedEnumValueSnapshotCodec)
+            bind(StringValueSnapshotCodec)
+            bind(IntegerValueSnapshotCodec)
+            bind(FileValueSnapshotCodec)
+            bind(BooleanValueSnapshotCodec)
+            bind(NullValueSnapshotCodec)
 
-        bind(ProxyCodec)
+            bind(ServicesCodec())
 
-        // Java serialization integration
-        bind(unsupported<Externalizable>(NotYetImplementedJavaSerialization))
-        bind(JavaObjectSerializationCodec(javaSerializationEncodingLookup))
+            bind(ProxyCodec)
 
-        bind(BeanSpecCodec)
+            // Java serialization integration
+            bind(unsupported<Externalizable>(NotYetImplementedJavaSerialization))
+            bind(JavaObjectSerializationCodec(javaSerializationEncodingLookup))
+
+            bind(BeanSpecCodec)
+
+            bind(RegisteredFlowActionCodec)
+        }
+
+        userTypesBindings = makeUserTypeBindings {
+            providerTypes(
+                propertyFactory,
+                filePropertyFactory,
+                nestedProviderCodec(
+                    valueSourceProviderFactory,
+                    buildStateRegistry,
+                    flowProviders
+                )
+            )
+        }
+
+        fingerprintUserTypesBindings = makeUserTypeBindings {
+            providerTypes(
+                propertyFactory,
+                filePropertyFactory,
+                nestedProviderCodecForFingerprint(
+                    valueSourceProviderFactory
+                )
+            )
+        }
     }
 
-    fun userTypesCodec(): Codec<Any?> = userTypesBindings.append {
+    private
+    fun Bindings.completeWithReentrantBeanCodec() = append {
         // This protects the BeanCodec against StackOverflowErrors but
         // we can still get them for the other codecs, for instance,
         // with deeply nested Lists, deeply nested Maps, etc.
+        // The reentrant codec is stateful, and cannot be cached because of it.
         bind(reentrant(BeanCodec))
     }.build()
 
+    fun userTypesCodec(): Codec<Any?> = userTypesBindings.completeWithReentrantBeanCodec()
+
+    fun fingerprintTypesCodec(): Codec<Any?> = fingerprintUserTypesBindings.completeWithReentrantBeanCodec()
+
     private
     val internalTypesBindings = Bindings.of {
         baseTypes()
 
-        providerTypes(propertyFactory, filePropertyFactory, valueSourceProviderFactory, buildStateRegistry, flowProviders)
+        providerTypes(propertyFactory, filePropertyFactory, nestedProviderCodec(valueSourceProviderFactory, buildStateRegistry, flowProviders))
         fileCollectionTypes(directoryFileTreeFactory, fileCollectionFactory, artifactSetConverter, fileOperations, fileFactory, patternSetFactory)
 
         bind(TaskInAnotherBuildCodec(includedTaskGraph))
@@ -227,7 +259,7 @@
         val userTypesCodec = userTypesCodec()
 
         bind(TaskNodeCodec(userTypesCodec, taskNodeFactory))
-        bind(DelegatingCodec<TransformationNode>(userTypesCodec))
+        bind(DelegatingCodec<TransformStepNode>(userTypesCodec))
         bind(ActionNodeCodec(userTypesCodec))
         bind(OrdinalNodeCodec(ordinalGroupFactory))
 
@@ -238,11 +270,8 @@
     fun BindingsBuilder.providerTypes(
         propertyFactory: PropertyFactory,
         filePropertyFactory: FilePropertyFactory,
-        valueSourceProviderFactory: ValueSourceProviderFactory,
-        buildStateRegistry: BuildStateRegistry,
-        flowProviders: FlowProviders
+        nestedCodec: FixedValueReplacingProviderCodec
     ) {
-        val nestedCodec = FixedValueReplacingProviderCodec(valueSourceProviderFactory, buildStateRegistry, flowProviders)
         bind(ListPropertyCodec(propertyFactory, nestedCodec))
         bind(SetPropertyCodec(propertyFactory, nestedCodec))
         bind(MapPropertyCodec(propertyFactory, nestedCodec))
@@ -252,6 +281,32 @@
         bind(ProviderCodec(nestedCodec))
     }
 
+    /**
+     * Returns a Codec for Provider implementations suitable for all Provider implementations.
+     */
+    private
+    fun nestedProviderCodec(
+        valueSourceProviderFactory: ValueSourceProviderFactory,
+        buildStateRegistry: BuildStateRegistry,
+        flowProviders: FlowProviders
+    ) = FixedValueReplacingProviderCodec(
+        ValueSourceProviderCodec(valueSourceProviderFactory),
+        BuildServiceProviderCodec(buildStateRegistry),
+        FlowProvidersCodec(flowProviders)
+    )
+
+    /**
+     * Returns a Codec for Provider implementations supported in the fingerprinting context. For example, BuildServiceProviders are not supported.
+     */
+    private
+    fun nestedProviderCodecForFingerprint(
+        valueSourceProviderFactory: ValueSourceProviderFactory
+    ) = FixedValueReplacingProviderCodec(
+        ValueSourceProviderCodec(valueSourceProviderFactory),
+        UnsupportedFingerprintBuildServiceProviderCodec,
+        UnsupportedFingerprintFlowProviders
+    )
+
     private
     fun BindingsBuilder.fileCollectionTypes(
         directoryFileTreeFactory: DirectoryFileTreeFactory,
@@ -308,6 +363,8 @@
         bind(treeMapCodec)
         bind(concurrentHashMapCodec)
         bind(ImmutableMapCodec)
+        bind(propertiesCodec)
+        bind(hashtableCodec)
 
         // Arrays
         bind(BYTE_ARRAY_SERIALIZER)
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/CollectionCodecs.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/CollectionCodecs.kt
index 0e2e177..1851007 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/CollectionCodecs.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/CollectionCodecs.kt
@@ -26,7 +26,9 @@
 import org.gradle.configurationcache.serialization.readMapInto
 import org.gradle.configurationcache.serialization.writeCollection
 import org.gradle.configurationcache.serialization.writeMap
+import java.util.Hashtable
 import java.util.LinkedList
+import java.util.Properties
 import java.util.TreeMap
 import java.util.TreeSet
 import java.util.concurrent.ConcurrentHashMap
@@ -178,6 +180,21 @@
 val concurrentHashMapCodec: Codec<ConcurrentHashMap<Any?, Any?>> = mapCodec { ConcurrentHashMap<Any?, Any?>(it) }
 
 
+/*
+ * Cannot rely on Java serialization as
+ * Hashtable's readObject() calls unsupported ObjectInputStream#readFields().
+ */
+internal
+val hashtableCodec: Codec<Hashtable<Any?, Any?>> = mapCodec { Hashtable(it) }
+
+
+/*
+ * Decodes Properties as Properties instead of Hashtable.
+ */
+internal
+val propertiesCodec: Codec<Properties> = mapCodec { Properties() }
+
+
 internal
 val treeMapCodec: Codec<TreeMap<Any?, Any?>> = codec(
     {
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/LocalFileDependencyBackedArtifactSetCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/LocalFileDependencyBackedArtifactSetCodec.kt
index 66b7e6d..bda2a71 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/LocalFileDependencyBackedArtifactSetCodec.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/LocalFileDependencyBackedArtifactSetCodec.kt
@@ -27,21 +27,20 @@
 import org.gradle.api.capabilities.CapabilitiesMetadata
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.CollectionCallbackActionDecorator
-import org.gradle.api.internal.artifacts.ArtifactTransformRegistration
+import org.gradle.api.internal.artifacts.TransformRegistration
 import org.gradle.api.internal.artifacts.VariantTransformRegistry
-import org.gradle.api.internal.artifacts.configurations.ConfigurationIdentity
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.LocalFileDependencyBackedArtifactSet
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariantSet
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformDependencies
-import org.gradle.api.internal.artifacts.transform.DefaultArtifactTransformDependencies
-import org.gradle.api.internal.artifacts.transform.ExtraExecutionGraphDependenciesResolverFactory
+import org.gradle.api.internal.artifacts.transform.DefaultTransformDependencies
+import org.gradle.api.internal.artifacts.transform.TransformChain
+import org.gradle.api.internal.artifacts.transform.TransformDependencies
+import org.gradle.api.internal.artifacts.transform.TransformStep
 import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependencies
 import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependenciesResolver
-import org.gradle.api.internal.artifacts.transform.Transformation
-import org.gradle.api.internal.artifacts.transform.TransformationStep
+import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependenciesResolverFactory
 import org.gradle.api.internal.artifacts.transform.TransformedVariantFactory
 import org.gradle.api.internal.artifacts.transform.VariantDefinition
 import org.gradle.api.internal.artifacts.transform.VariantSelector
@@ -71,6 +70,7 @@
 import org.gradle.internal.component.model.VariantResolveMetadata
 import org.gradle.internal.model.CalculatedValueContainerFactory
 import org.gradle.internal.reflect.Instantiator
+import org.gradle.operations.dependencies.configurations.ConfigurationIdentity
 
 
 class LocalFileDependencyBackedArtifactSetCodec(
@@ -83,8 +83,8 @@
         //   - calculate the attributes for each of the files eagerly rather than writing the mappings
         //   - when the selector would not apply a transform, then write only the files and nothing else
         //   - otherwise, write only the transform and attributes for each file rather than writing the transform registry
-        val noRequestedAttributes = value.selector.requestedAttributes.isEmpty
-        writeBoolean(noRequestedAttributes)
+        val requestedAttributes = !value.selector.requestedAttributes.isEmpty
+        writeBoolean(requestedAttributes)
         write(value.dependencyMetadata.componentId)
         write(value.dependencyMetadata.files)
         write(value.componentFilter)
@@ -99,8 +99,8 @@
             }
         }
 
-        if (!noRequestedAttributes) {
-            // Write the file extension -> transformation mappings
+        if (requestedAttributes) {
+            // Write the file extension -> transform mappings
             // This currently uses a dummy set of variants to calculate the mappings.
             // Do not write this if it will not be used
             // TODO - simplify extracting the mappings
@@ -110,11 +110,11 @@
             val mappings = mutableMapOf<ImmutableAttributes, MappingSpec>()
             value.artifactTypeRegistry.visitArtifactTypes { sourceAttributes ->
                 val recordingSet = RecordingVariantSet(value.dependencyMetadata.files, sourceAttributes)
-                val selected = value.selector.select(recordingSet, recordingSet)
+                val selected = value.selector.maybeSelect(recordingSet, recordingSet)
                 if (selected == ResolvedArtifactSet.EMPTY) {
                     // Don't need to record the mapping
                 } else if (recordingSet.targetAttributes != null) {
-                    mappings[sourceAttributes] = TransformMapping(recordingSet.targetAttributes!!, recordingSet.transformation!!)
+                    mappings[sourceAttributes] = TransformMapping(recordingSet.targetAttributes!!, recordingSet.transformChain!!)
                 } else {
                     mappings[sourceAttributes] = IdentityMapping
                 }
@@ -128,7 +128,7 @@
     }
 
     override suspend fun ReadContext.decode(): LocalFileDependencyBackedArtifactSet {
-        val noRequestedAttributes = readBoolean()
+        val requestedAttributes = readBoolean()
         val componentId = read() as ComponentIdentifier?
         val files = readNonNull<FileCollectionInternal>()
         val filter = readNonNull<Spec<ComponentIdentifier>>()
@@ -149,7 +149,7 @@
             registry
         }
 
-        val selector = if (noRequestedAttributes) {
+        val selector = if (!requestedAttributes) {
             NoTransformsSelector()
         } else {
             val matchingOnArtifactFormat = readBoolean()
@@ -167,7 +167,7 @@
     private val attributes: ImmutableAttributes
 ) : ResolvedVariantSet, ResolvedVariant, VariantSelector.Factory, ResolvedArtifactSet {
     var targetAttributes: ImmutableAttributes? = null
-    var transformation: Transformation? = null
+    var transformChain: TransformChain? = null
 
     override fun asDescribable(): DisplayName {
         return Describables.of(source)
@@ -224,10 +224,10 @@
     override fun asTransformed(
         sourceVariant: ResolvedVariant,
         variantDefinition: VariantDefinition,
-        dependenciesResolver: ExtraExecutionGraphDependenciesResolverFactory,
+        dependenciesResolverFactory: TransformUpstreamDependenciesResolverFactory,
         transformedVariantFactory: TransformedVariantFactory
     ): ResolvedArtifactSet {
-        this.transformation = variantDefinition.transformation
+        this.transformChain = variantDefinition.transformChain
         this.targetAttributes = variantDefinition.targetAttributes
         return sourceVariant.artifacts
     }
@@ -239,16 +239,16 @@
 
 
 private
-class TransformMapping(private val targetAttributes: ImmutableAttributes, private val transformation: Transformation) : MappingSpec(), VariantDefinition {
+class TransformMapping(private val targetAttributes: ImmutableAttributes, private val transformChain: TransformChain) : MappingSpec(), VariantDefinition {
     override fun getTargetAttributes(): ImmutableAttributes {
         return targetAttributes
     }
 
-    override fun getTransformation(): Transformation {
-        return transformation
+    override fun getTransformChain(): TransformChain {
+        return transformChain
     }
 
-    override fun getTransformationStep(): TransformationStep {
+    override fun getTransformStep(): TransformStep {
         throw UnsupportedOperationException()
     }
 
@@ -321,17 +321,17 @@
 
 
 private
-class EmptyDependenciesResolverFactory : ExtraExecutionGraphDependenciesResolverFactory, TransformUpstreamDependenciesResolver, TransformUpstreamDependencies {
+class EmptyDependenciesResolverFactory : TransformUpstreamDependenciesResolverFactory, TransformUpstreamDependenciesResolver, TransformUpstreamDependencies {
 
     override fun getConfigurationIdentity(): ConfigurationIdentity? {
         return null
     }
 
-    override fun create(componentIdentifier: ComponentIdentifier, transformation: Transformation): TransformUpstreamDependenciesResolver {
+    override fun create(componentIdentifier: ComponentIdentifier, transformChain: TransformChain): TransformUpstreamDependenciesResolver {
         return this
     }
 
-    override fun dependenciesFor(transformationStep: TransformationStep): TransformUpstreamDependencies {
+    override fun dependenciesFor(transformStep: TransformStep): TransformUpstreamDependencies {
         return this
     }
 
@@ -346,8 +346,8 @@
     override fun finalizeIfNotAlready() {
     }
 
-    override fun computeArtifacts(): Try<ArtifactTransformDependencies> {
-        return Try.successful(DefaultArtifactTransformDependencies(FileCollectionFactory.empty()))
+    override fun computeArtifacts(): Try<TransformDependencies> {
+        return Try.successful(DefaultTransformDependencies(FileCollectionFactory.empty()))
     }
 }
 
@@ -358,7 +358,7 @@
         componentIdentifier: ComponentIdentifier,
         sourceVariant: ResolvedVariant,
         variantDefinition: VariantDefinition,
-        dependenciesResolverFactory: ExtraExecutionGraphDependenciesResolverFactory
+        dependenciesResolverFactory: TransformUpstreamDependenciesResolverFactory
     ): ResolvedArtifactSet {
         throw UnsupportedOperationException("Should not be called")
     }
@@ -367,7 +367,7 @@
         componentIdentifier: ComponentIdentifier,
         sourceVariant: ResolvedVariant,
         variantDefinition: VariantDefinition,
-        dependenciesResolverFactory: ExtraExecutionGraphDependenciesResolverFactory
+        dependenciesResolverFactory: TransformUpstreamDependenciesResolverFactory
     ): ResolvedArtifactSet {
         throw UnsupportedOperationException("Should not be called")
     }
@@ -380,7 +380,7 @@
         throw UnsupportedOperationException("Should not be called")
     }
 
-    override fun getTransforms(): MutableList<ArtifactTransformRegistration> {
+    override fun getRegistrations(): MutableList<TransformRegistration> {
         throw UnsupportedOperationException("Should not be called")
     }
 }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/ProviderCodecs.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/ProviderCodecs.kt
index 640eb3c..77be620 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/ProviderCodecs.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/ProviderCodecs.kt
@@ -19,6 +19,8 @@
 import org.gradle.api.artifacts.component.BuildIdentifier
 import org.gradle.api.file.Directory
 import org.gradle.api.file.RegularFile
+import org.gradle.api.flow.FlowAction
+import org.gradle.api.flow.FlowParameters
 import org.gradle.api.flow.FlowProviders
 import org.gradle.api.internal.file.DefaultFilePropertyFactory.DefaultDirectoryVar
 import org.gradle.api.internal.file.DefaultFilePropertyFactory.DefaultRegularFileVar
@@ -41,16 +43,25 @@
 import org.gradle.api.services.internal.BuildServiceRegistryInternal
 import org.gradle.configurationcache.extensions.serviceOf
 import org.gradle.configurationcache.extensions.uncheckedCast
-import org.gradle.configurationcache.flow.RequestedTasksResultProvider
+import org.gradle.configurationcache.flow.BuildWorkResultProvider
+import org.gradle.configurationcache.flow.RegisteredFlowAction
+import org.gradle.configurationcache.problems.PropertyTrace
 import org.gradle.configurationcache.serialization.Codec
+import org.gradle.configurationcache.serialization.IsolateContext
+import org.gradle.configurationcache.serialization.IsolateOwner
+import org.gradle.configurationcache.serialization.MutableIsolateContext
 import org.gradle.configurationcache.serialization.ReadContext
 import org.gradle.configurationcache.serialization.WriteContext
 import org.gradle.configurationcache.serialization.decodePreservingIdentity
 import org.gradle.configurationcache.serialization.decodePreservingSharedIdentity
 import org.gradle.configurationcache.serialization.encodePreservingIdentityOf
 import org.gradle.configurationcache.serialization.encodePreservingSharedIdentityOf
+import org.gradle.configurationcache.serialization.logPropertyProblem
 import org.gradle.configurationcache.serialization.readClassOf
 import org.gradle.configurationcache.serialization.readNonNull
+import org.gradle.configurationcache.serialization.withDebugFrame
+import org.gradle.configurationcache.serialization.withIsolate
+import org.gradle.configurationcache.serialization.withPropertyTrace
 import org.gradle.internal.build.BuildStateRegistry
 
 
@@ -60,15 +71,15 @@
  */
 internal
 class FixedValueReplacingProviderCodec(
-    valueSourceProviderFactory: ValueSourceProviderFactory,
-    buildStateRegistry: BuildStateRegistry,
-    flowProviders: FlowProviders
+    valueSourceProviderCodec: Codec<ValueSourceProvider<*, *>>,
+    buildServiceProviderCodec: Codec<BuildServiceProvider<*, *>>,
+    flowProvidersCodec: Codec<BuildWorkResultProvider>,
 ) {
     private
     val providerWithChangingValueCodec = Bindings.of {
-        bind(ValueSourceProviderCodec(valueSourceProviderFactory))
-        bind(BuildServiceProviderCodec(buildStateRegistry))
-        bind(FlowProvidersCodec(flowProviders))
+        bind(valueSourceProviderCodec)
+        bind(buildServiceProviderCodec)
+        bind(flowProvidersCodec)
         bind(BeanCodec)
     }.build()
 
@@ -133,13 +144,60 @@
 internal
 class FlowProvidersCodec(
     private val flowProviders: FlowProviders
-) : Codec<RequestedTasksResultProvider> {
+) : Codec<BuildWorkResultProvider> {
 
-    override suspend fun WriteContext.encode(value: RequestedTasksResultProvider) {
+    override suspend fun WriteContext.encode(value: BuildWorkResultProvider) {
+        if (isolate.owner !is IsolateOwner.OwnerFlowAction) {
+            logPropertyProblem("serialize") {
+                reference(BuildWorkResultProvider::class)
+                text(" can only be used as input to flow actions.")
+            }
+        }
     }
 
-    override suspend fun ReadContext.decode(): RequestedTasksResultProvider {
-        return flowProviders.requestedTasksResult.uncheckedCast()
+    override suspend fun ReadContext.decode(): BuildWorkResultProvider {
+        return flowProviders.buildWorkResult.uncheckedCast()
+    }
+}
+
+
+internal
+object RegisteredFlowActionCodec : Codec<RegisteredFlowAction> {
+
+    override suspend fun WriteContext.encode(value: RegisteredFlowAction) {
+        val owner = verifiedIsolateOwner()
+        val flowActionClass = value.type
+        withDebugFrame({ flowActionClass.name }) {
+            writeClass(flowActionClass)
+            withFlowActionIsolate(flowActionClass, owner) {
+                write(value.parameters)
+            }
+        }
+    }
+
+    override suspend fun ReadContext.decode(): RegisteredFlowAction {
+        val flowActionClass = readClassOf<FlowAction<FlowParameters>>()
+        withFlowActionIsolate(flowActionClass, verifiedIsolateOwner()) {
+            return RegisteredFlowAction(flowActionClass, read()?.uncheckedCast())
+        }
+    }
+
+    private
+    inline fun <T : MutableIsolateContext, R> T.withFlowActionIsolate(flowActionClass: Class<*>, owner: IsolateOwner.OwnerFlowScope, block: T.() -> R): R {
+        withIsolate(IsolateOwner.OwnerFlowAction(owner)) {
+            withPropertyTrace(PropertyTrace.BuildLogicClass(flowActionClass.name)) {
+                return block()
+            }
+        }
+    }
+
+    private
+    fun IsolateContext.verifiedIsolateOwner(): IsolateOwner.OwnerFlowScope {
+        val owner = isolate.owner
+        require(owner is IsolateOwner.OwnerFlowScope) {
+            "Flow actions must belong to a Flow scope!"
+        }
+        return owner
     }
 }
 
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/TaskNodeCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/TaskNodeCodec.kt
index 41c954d..4f78e40 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/TaskNodeCodec.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/TaskNodeCodec.kt
@@ -90,6 +90,7 @@
             writeClass(taskType)
             writeString(projectPath)
             writeString(taskName)
+            writeLong(task.taskIdentity.uniqueId)
             writeNullableString(task.reasonTaskIsIncompatibleWithConfigurationCache.orElse(null))
 
             withDebugFrame({ taskType.name }) {
@@ -118,9 +119,10 @@
         val taskType = readClassOf<Task>()
         val projectPath = readString()
         val taskName = readString()
+        val uniqueId = readLong()
         val incompatibleReason = readNullableString()
 
-        val task = createTask(projectPath, taskName, taskType, incompatibleReason)
+        val task = createTask(projectPath, taskName, taskType, uniqueId, incompatibleReason)
 
         withTaskOf(taskType, task, userTypesCodec) {
             readUpToDateSpec(task)
@@ -172,7 +174,7 @@
 
     private
     suspend fun WriteContext.writeRequiredServices(task: TaskInternal) {
-        writeCollection(task.requiredServices.elements)
+        writeCollection(task.requiredServices.searchServices())
     }
 
     private
@@ -475,8 +477,8 @@
 
 
 private
-fun ReadContext.createTask(projectPath: String, taskName: String, taskClass: Class<out Task>, incompatibleReason: String?): TaskInternal {
-    val task = getProject(projectPath).tasks.createWithoutConstructor(taskName, taskClass) as TaskInternal
+fun ReadContext.createTask(projectPath: String, taskName: String, taskClass: Class<out Task>, uniqueId: Long, incompatibleReason: String?): TaskInternal {
+    val task = getProject(projectPath).tasks.createWithoutConstructor(taskName, taskClass, uniqueId) as TaskInternal
     if (incompatibleReason != null) {
         task.notCompatibleWithConfigurationCache(incompatibleReason)
     }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/UnsupportedTypesCodecs.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/UnsupportedTypesCodecs.kt
index bbfebc5..66e3b82 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/UnsupportedTypesCodecs.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/UnsupportedTypesCodecs.kt
@@ -50,10 +50,18 @@
 import org.gradle.api.invocation.Gradle
 import org.gradle.api.publish.Publication
 import org.gradle.api.services.BuildService
+import org.gradle.api.services.internal.BuildServiceProvider
 import org.gradle.api.tasks.SourceSet
 import org.gradle.api.tasks.SourceSetContainer
 import org.gradle.api.tasks.TaskContainer
 import org.gradle.api.tasks.TaskDependency
+import org.gradle.configurationcache.flow.BuildWorkResultProvider
+import org.gradle.configurationcache.problems.DocumentationSection
+import org.gradle.configurationcache.serialization.Codec
+import org.gradle.configurationcache.serialization.IsolateContext
+import org.gradle.configurationcache.serialization.ReadContext
+import org.gradle.configurationcache.serialization.WriteContext
+import org.gradle.configurationcache.serialization.logUnsupported
 import org.gradle.configurationcache.serialization.unsupported
 import org.gradle.internal.scripts.GradleScript
 import org.gradle.internal.service.DefaultServiceRegistry
@@ -135,3 +143,45 @@
     // Gradle implementation types
     bind(unsupported<DefaultServiceRegistry>())
 }
+
+
+internal
+object UnsupportedFingerprintBuildServiceProviderCodec : Codec<BuildServiceProvider<*, *>> {
+    override suspend fun WriteContext.encode(value: BuildServiceProvider<*, *>) {
+        logUnsupported("serialize",
+            documentationSection = DocumentationSection.NotYetImplementedBuildServiceInFingerprint) {
+            text(" BuildServiceProvider of service ")
+            reference(value.serviceDetails.implementationType)
+            text(" with name ")
+            reference(value.serviceDetails.name)
+            text(" used at configuration time")
+        }
+    }
+
+    override suspend fun ReadContext.decode(): BuildServiceProvider<*, *>? {
+        logUnsupported("deserialize", BuildServiceProvider::class, documentationSection = DocumentationSection.NotYetImplementedBuildServiceInFingerprint)
+        return null
+    }
+}
+
+
+internal
+object UnsupportedFingerprintFlowProviders : Codec<BuildWorkResultProvider> {
+    override suspend fun WriteContext.encode(value: BuildWorkResultProvider) {
+        logUnsupported("serialize")
+    }
+
+    override suspend fun ReadContext.decode(): BuildWorkResultProvider? {
+        logUnsupported("deserialize")
+        return null
+    }
+
+    private
+    fun IsolateContext.logUnsupported(action: String) {
+        logUnsupported(action) {
+            text(" ")
+            reference(BuildWorkResultProvider::class)
+            text(" used at configuration time")
+        }
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/AbstractTransformStepNodeCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/AbstractTransformStepNodeCodec.kt
new file mode 100644
index 0000000..cd7fd93
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/AbstractTransformStepNodeCodec.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 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.serialization.codecs.transform
+
+import org.gradle.api.internal.artifacts.transform.TransformStepNode
+import org.gradle.configurationcache.serialization.Codec
+import org.gradle.configurationcache.serialization.ReadContext
+import org.gradle.configurationcache.serialization.WriteContext
+import org.gradle.configurationcache.serialization.decodePreservingSharedIdentity
+import org.gradle.configurationcache.serialization.encodePreservingSharedIdentityOf
+
+
+internal
+abstract class AbstractTransformStepNodeCodec<T : TransformStepNode> : Codec<T> {
+
+    override suspend fun WriteContext.encode(value: T) {
+        encodePreservingSharedIdentityOf(value) { doEncode(value) }
+    }
+
+    override suspend fun ReadContext.decode(): T =
+        decodePreservingSharedIdentity { doDecode() }
+
+    protected
+    abstract suspend fun WriteContext.doEncode(value: T)
+
+    protected
+    abstract suspend fun ReadContext.doDecode(): T
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/AbstractTransformationNodeCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/AbstractTransformationNodeCodec.kt
deleted file mode 100644
index 0cd872b..0000000
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/AbstractTransformationNodeCodec.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2020 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.serialization.codecs.transform
-
-import org.gradle.api.internal.artifacts.transform.TransformationNode
-import org.gradle.configurationcache.serialization.Codec
-import org.gradle.configurationcache.serialization.ReadContext
-import org.gradle.configurationcache.serialization.WriteContext
-import org.gradle.configurationcache.serialization.decodePreservingSharedIdentity
-import org.gradle.configurationcache.serialization.encodePreservingSharedIdentityOf
-
-
-internal
-abstract class AbstractTransformationNodeCodec<T : TransformationNode> : Codec<T> {
-
-    override suspend fun WriteContext.encode(value: T) {
-        encodePreservingSharedIdentityOf(value) { doEncode(value) }
-    }
-
-    override suspend fun ReadContext.decode(): T =
-        decodePreservingSharedIdentity { doDecode() }
-
-    protected
-    abstract suspend fun WriteContext.doEncode(value: T)
-
-    protected
-    abstract suspend fun ReadContext.doDecode(): T
-}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/CalculateArtifactsCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/CalculateArtifactsCodec.kt
index 4ed8e9d..c2bf6e7 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/CalculateArtifactsCodec.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/CalculateArtifactsCodec.kt
@@ -25,7 +25,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet
 import org.gradle.api.internal.artifacts.transform.AbstractTransformedArtifactSet
-import org.gradle.api.internal.artifacts.transform.BoundTransformationStep
+import org.gradle.api.internal.artifacts.transform.BoundTransformStep
 import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.api.internal.tasks.TaskDependencyContainer
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext
@@ -55,7 +55,7 @@
         val files = mutableListOf<Artifact>()
         value.delegate.visitExternalArtifacts { files.add(Artifact(file, artifactName.classifier)) }
         write(files)
-        val steps = unpackTransformationSteps(value.steps)
+        val steps = unpackTransformSteps(value.steps)
         writeCollection(steps)
     }
 
@@ -65,7 +65,13 @@
         val capabilities: List<Capability> = readList().uncheckedCast()
         val files = readNonNull<List<Artifact>>()
         val steps: List<TransformStepSpec> = readList().uncheckedCast()
-        return AbstractTransformedArtifactSet.CalculateArtifacts(ownerId, FixedFilesArtifactSet(ownerId, files, calculatedValueContainerFactory), targetAttributes, capabilities, ImmutableList.copyOf(steps.map { BoundTransformationStep(it.transformation, it.recreate()) }))
+        return AbstractTransformedArtifactSet.CalculateArtifacts(
+            ownerId,
+            FixedFilesArtifactSet(ownerId, files, calculatedValueContainerFactory),
+            targetAttributes,
+            capabilities,
+            ImmutableList.copyOf(steps.map { BoundTransformStep(it.transformStep, it.recreateDependencies()) })
+        )
     }
 
     private
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/ChainedTransformStepNodeCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/ChainedTransformStepNodeCodec.kt
new file mode 100644
index 0000000..6f2926c
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/ChainedTransformStepNodeCodec.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 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.serialization.codecs.transform
+
+import org.gradle.api.attributes.AttributeContainer
+import org.gradle.api.internal.artifacts.transform.ComponentVariantIdentifier
+import org.gradle.api.internal.artifacts.transform.TransformStepNode
+import org.gradle.api.internal.artifacts.transform.TransformStepNodeFactory
+import org.gradle.configurationcache.serialization.ReadContext
+import org.gradle.configurationcache.serialization.WriteContext
+import org.gradle.configurationcache.serialization.readNonNull
+import org.gradle.internal.model.CalculatedValueContainerFactory
+import org.gradle.internal.operations.BuildOperationExecutor
+
+
+internal
+class ChainedTransformStepNodeCodec(
+    private val transformStepNodeFactory: TransformStepNodeFactory,
+    private val buildOperationExecutor: BuildOperationExecutor,
+    private val calculatedValueContainerFactory: CalculatedValueContainerFactory
+) : AbstractTransformStepNodeCodec<TransformStepNode.ChainedTransformStepNode>() {
+
+    override suspend fun WriteContext.doEncode(value: TransformStepNode.ChainedTransformStepNode) {
+        writeLong(value.transformStepNodeId)
+        write(value.targetComponentVariant)
+        write(value.sourceAttributes)
+        write(unpackTransformStep(value))
+        write(value.previousTransformStepNode)
+    }
+
+    override suspend fun ReadContext.doDecode(): TransformStepNode.ChainedTransformStepNode {
+        val transformStepNodeId = readLong()
+        val targetComponentVariant = readNonNull<ComponentVariantIdentifier>()
+        val sourceAttributes = readNonNull<AttributeContainer>()
+        val transformStepSpec = readNonNull<TransformStepSpec>()
+        val previousStep = readNonNull<TransformStepNode>()
+        return transformStepNodeFactory.recreateChained(transformStepNodeId, targetComponentVariant, sourceAttributes, transformStepSpec.transformStep, previousStep, transformStepSpec.recreateDependencies(), buildOperationExecutor, calculatedValueContainerFactory)
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/ChainedTransformationNodeCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/ChainedTransformationNodeCodec.kt
deleted file mode 100644
index f2c7a5a..0000000
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/ChainedTransformationNodeCodec.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2020 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.serialization.codecs.transform
-
-import org.gradle.api.attributes.AttributeContainer
-import org.gradle.api.internal.artifacts.transform.ComponentVariantIdentifier
-import org.gradle.api.internal.artifacts.transform.TransformationNode
-import org.gradle.configurationcache.serialization.ReadContext
-import org.gradle.configurationcache.serialization.WriteContext
-import org.gradle.configurationcache.serialization.readNonNull
-import org.gradle.internal.model.CalculatedValueContainerFactory
-import org.gradle.internal.operations.BuildOperationExecutor
-
-
-internal
-class ChainedTransformationNodeCodec(
-    private val buildOperationExecutor: BuildOperationExecutor,
-    private val calculatedValueContainerFactory: CalculatedValueContainerFactory
-) : AbstractTransformationNodeCodec<TransformationNode.ChainedTransformationNode>() {
-
-    override suspend fun WriteContext.doEncode(value: TransformationNode.ChainedTransformationNode) {
-        write(value.targetComponentVariant)
-        write(value.sourceAttributes)
-        write(unpackTransformationStep(value))
-        write(value.previousTransformationNode)
-    }
-
-    override suspend fun ReadContext.doDecode(): TransformationNode.ChainedTransformationNode {
-        val targetComponentVariant = readNonNull<ComponentVariantIdentifier>()
-        val sourceAttributes = readNonNull<AttributeContainer>()
-        val transformationStep = readNonNull<TransformStepSpec>()
-        val previousStep = readNonNull<TransformationNode>()
-        return TransformationNode.chained(targetComponentVariant, sourceAttributes, transformationStep.transformation, previousStep, transformationStep.recreate(), buildOperationExecutor, calculatedValueContainerFactory)
-    }
-}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/DefaultTransformCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/DefaultTransformCodec.kt
new file mode 100644
index 0000000..dbef30a
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/DefaultTransformCodec.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020 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.serialization.codecs.transform
+
+import org.gradle.api.artifacts.transform.TransformAction
+import org.gradle.api.internal.artifacts.transform.DefaultTransform
+import org.gradle.api.internal.artifacts.transform.TransformActionScheme
+import org.gradle.api.internal.attributes.ImmutableAttributes
+import org.gradle.api.internal.file.FileLookup
+import org.gradle.configurationcache.serialization.Codec
+import org.gradle.configurationcache.serialization.ReadContext
+import org.gradle.configurationcache.serialization.WriteContext
+import org.gradle.configurationcache.serialization.decodePreservingSharedIdentity
+import org.gradle.configurationcache.serialization.encodePreservingSharedIdentityOf
+import org.gradle.configurationcache.serialization.readClassOf
+import org.gradle.configurationcache.serialization.readEnum
+import org.gradle.configurationcache.serialization.readNonNull
+import org.gradle.configurationcache.serialization.writeEnum
+import org.gradle.internal.execution.model.InputNormalizer
+import org.gradle.internal.fingerprint.DirectorySensitivity
+import org.gradle.internal.fingerprint.LineEndingSensitivity
+import org.gradle.internal.model.CalculatedValueContainer
+import org.gradle.internal.service.ServiceRegistry
+
+
+internal
+class DefaultTransformCodec(
+    private val fileLookup: FileLookup,
+    private val actionScheme: TransformActionScheme
+) : Codec<DefaultTransform> {
+
+    override suspend fun WriteContext.encode(value: DefaultTransform) {
+        encodePreservingSharedIdentityOf(value) {
+            writeClass(value.implementationClass)
+            write(value.fromAttributes)
+            write(value.toAttributes)
+            writeEnum(value.inputArtifactNormalizer as InputNormalizer)
+            writeEnum(value.inputArtifactDependenciesNormalizer as InputNormalizer)
+            writeBoolean(value.isCacheable)
+            writeEnum(value.inputArtifactDirectorySensitivity)
+            writeEnum(value.inputArtifactDependenciesDirectorySensitivity)
+            writeEnum(value.inputArtifactLineEndingNormalization)
+            writeEnum(value.inputArtifactDependenciesLineEndingNormalization)
+            write(value.isolatedParameters)
+            // TODO - isolate now and discard node, if isolation is scheduled but has no dependencies
+        }
+    }
+
+    override suspend fun ReadContext.decode(): DefaultTransform {
+        return decodePreservingSharedIdentity {
+            val implementationClass = readClassOf<TransformAction<*>>()
+            val fromAttributes = readNonNull<ImmutableAttributes>()
+            val toAttributes = readNonNull<ImmutableAttributes>()
+            val inputArtifactNormalizer = readEnum<InputNormalizer>()
+            val inputArtifactDependenciesNormalizer = readEnum<InputNormalizer>()
+            val isCacheable = readBoolean()
+            val inputArtifactDirectorySensitivity = readEnum<DirectorySensitivity>()
+            val inputArtifactDependenciesDirectorySensitivity = readEnum<DirectorySensitivity>()
+            val inputArtifactLineEndingNormalization = readEnum<LineEndingSensitivity>()
+            val inputArtifactDependenciesLineEndingNormalization = readEnum<LineEndingSensitivity>()
+            val isolatedParameters = readNonNull<CalculatedValueContainer<DefaultTransform.IsolatedParameters, DefaultTransform.IsolateTransformParameters>>()
+            DefaultTransform(
+                implementationClass,
+                isolatedParameters,
+                fromAttributes,
+                toAttributes,
+                inputArtifactNormalizer,
+                inputArtifactDependenciesNormalizer,
+                isCacheable,
+                fileLookup,
+                actionScheme.instantiationScheme,
+                isolate.owner.service(ServiceRegistry::class.java),
+                inputArtifactDirectorySensitivity,
+                inputArtifactDependenciesDirectorySensitivity,
+                inputArtifactLineEndingNormalization,
+                inputArtifactDependenciesLineEndingNormalization
+            )
+        }
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/DefaultTransformerCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/DefaultTransformerCodec.kt
deleted file mode 100644
index a9b49ae..0000000
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/DefaultTransformerCodec.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2020 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.serialization.codecs.transform
-
-import org.gradle.api.artifacts.transform.TransformAction
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformActionScheme
-import org.gradle.api.internal.artifacts.transform.DefaultTransformer
-import org.gradle.api.internal.attributes.ImmutableAttributes
-import org.gradle.api.internal.file.FileLookup
-import org.gradle.configurationcache.serialization.Codec
-import org.gradle.configurationcache.serialization.ReadContext
-import org.gradle.configurationcache.serialization.WriteContext
-import org.gradle.configurationcache.serialization.decodePreservingSharedIdentity
-import org.gradle.configurationcache.serialization.encodePreservingSharedIdentityOf
-import org.gradle.configurationcache.serialization.readClassOf
-import org.gradle.configurationcache.serialization.readEnum
-import org.gradle.configurationcache.serialization.readNonNull
-import org.gradle.configurationcache.serialization.writeEnum
-import org.gradle.internal.execution.model.InputNormalizer
-import org.gradle.internal.fingerprint.DirectorySensitivity
-import org.gradle.internal.fingerprint.LineEndingSensitivity
-import org.gradle.internal.model.CalculatedValueContainer
-import org.gradle.internal.service.ServiceRegistry
-
-
-internal
-class DefaultTransformerCodec(
-    private val fileLookup: FileLookup,
-    private val actionScheme: ArtifactTransformActionScheme
-) : Codec<DefaultTransformer> {
-
-    override suspend fun WriteContext.encode(value: DefaultTransformer) {
-        encodePreservingSharedIdentityOf(value) {
-            writeClass(value.implementationClass)
-            write(value.fromAttributes)
-            write(value.toAttributes)
-            writeEnum(value.inputArtifactNormalizer as InputNormalizer)
-            writeEnum(value.inputArtifactDependenciesNormalizer as InputNormalizer)
-            writeBoolean(value.isCacheable)
-            writeEnum(value.inputArtifactDirectorySensitivity)
-            writeEnum(value.inputArtifactDependenciesDirectorySensitivity)
-            writeEnum(value.inputArtifactLineEndingNormalization)
-            writeEnum(value.inputArtifactDependenciesLineEndingNormalization)
-            write(value.isolatedParameters)
-            // TODO - isolate now and discard node, if isolation is scheduled but has no dependencies
-        }
-    }
-
-    override suspend fun ReadContext.decode(): DefaultTransformer {
-        return decodePreservingSharedIdentity {
-            val implementationClass = readClassOf<TransformAction<*>>()
-            val fromAttributes = readNonNull<ImmutableAttributes>()
-            val toAttributes = readNonNull<ImmutableAttributes>()
-            val inputArtifactNormalizer = readEnum<InputNormalizer>()
-            val inputArtifactDependenciesNormalizer = readEnum<InputNormalizer>()
-            val isCacheable = readBoolean()
-            val inputArtifactDirectorySensitivity = readEnum<DirectorySensitivity>()
-            val inputArtifactDependenciesDirectorySensitivity = readEnum<DirectorySensitivity>()
-            val inputArtifactLineEndingNormalization = readEnum<LineEndingSensitivity>()
-            val inputArtifactDependenciesLineEndingNormalization = readEnum<LineEndingSensitivity>()
-            val isolatedParameters = readNonNull<CalculatedValueContainer<DefaultTransformer.IsolatedParameters, DefaultTransformer.IsolateTransformerParameters>>()
-            DefaultTransformer(
-                implementationClass,
-                isolatedParameters,
-                fromAttributes,
-                toAttributes,
-                inputArtifactNormalizer,
-                inputArtifactDependenciesNormalizer,
-                isCacheable,
-                fileLookup,
-                actionScheme.instantiationScheme,
-                isolate.owner.service(ServiceRegistry::class.java),
-                inputArtifactDirectorySensitivity,
-                inputArtifactDependenciesDirectorySensitivity,
-                inputArtifactLineEndingNormalization,
-                inputArtifactDependenciesLineEndingNormalization
-            )
-        }
-    }
-}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/InitialTransformStepNodeCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/InitialTransformStepNodeCodec.kt
new file mode 100644
index 0000000..b898272
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/InitialTransformStepNodeCodec.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 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.serialization.codecs.transform
+
+import org.gradle.api.attributes.AttributeContainer
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
+import org.gradle.api.internal.artifacts.transform.ComponentVariantIdentifier
+import org.gradle.api.internal.artifacts.transform.TransformStepNode
+import org.gradle.api.internal.artifacts.transform.TransformStepNodeFactory
+import org.gradle.configurationcache.serialization.ReadContext
+import org.gradle.configurationcache.serialization.WriteContext
+import org.gradle.configurationcache.serialization.readNonNull
+import org.gradle.internal.model.CalculatedValueContainerFactory
+import org.gradle.internal.operations.BuildOperationExecutor
+
+
+internal
+class InitialTransformStepNodeCodec(
+    private val transformStepNodeFactory: TransformStepNodeFactory,
+    private val buildOperationExecutor: BuildOperationExecutor,
+    private val calculatedValueContainerFactory: CalculatedValueContainerFactory
+) : AbstractTransformStepNodeCodec<TransformStepNode.InitialTransformStepNode>() {
+
+    override suspend fun WriteContext.doEncode(value: TransformStepNode.InitialTransformStepNode) {
+        writeLong(value.transformStepNodeId)
+        write(value.targetComponentVariant)
+        write(value.sourceAttributes)
+        write(unpackTransformStep(value))
+        write(value.inputArtifact)
+    }
+
+    override suspend fun ReadContext.doDecode(): TransformStepNode.InitialTransformStepNode {
+        val transformStepNodeId = readLong()
+        val targetComponentVariant = readNonNull<ComponentVariantIdentifier>()
+        val sourceAttributes = readNonNull<AttributeContainer>()
+        val transformStepSpec = readNonNull<TransformStepSpec>()
+        val artifacts = readNonNull<ResolvableArtifact>()
+        return transformStepNodeFactory.recreateInitial(transformStepNodeId, targetComponentVariant, sourceAttributes, transformStepSpec.transformStep, artifacts, transformStepSpec.recreateDependencies(), buildOperationExecutor, calculatedValueContainerFactory)
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/InitialTransformationNodeCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/InitialTransformationNodeCodec.kt
deleted file mode 100644
index 025d087..0000000
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/InitialTransformationNodeCodec.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2020 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.serialization.codecs.transform
-
-import org.gradle.api.attributes.AttributeContainer
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
-import org.gradle.api.internal.artifacts.transform.ComponentVariantIdentifier
-import org.gradle.api.internal.artifacts.transform.TransformationNode
-import org.gradle.configurationcache.serialization.ReadContext
-import org.gradle.configurationcache.serialization.WriteContext
-import org.gradle.configurationcache.serialization.readNonNull
-import org.gradle.internal.model.CalculatedValueContainerFactory
-import org.gradle.internal.operations.BuildOperationExecutor
-
-
-internal
-class InitialTransformationNodeCodec(
-    private val buildOperationExecutor: BuildOperationExecutor,
-    private val calculatedValueContainerFactory: CalculatedValueContainerFactory
-) : AbstractTransformationNodeCodec<TransformationNode.InitialTransformationNode>() {
-
-    override suspend fun WriteContext.doEncode(value: TransformationNode.InitialTransformationNode) {
-        write(value.targetComponentVariant)
-        write(value.sourceAttributes)
-        write(unpackTransformationStep(value))
-        write(value.inputArtifact)
-    }
-
-    override suspend fun ReadContext.doDecode(): TransformationNode.InitialTransformationNode {
-        val targetComponentVariant = readNonNull<ComponentVariantIdentifier>()
-        val sourceAttributes = readNonNull<AttributeContainer>()
-        val transformationStep = readNonNull<TransformStepSpec>()
-        val artifacts = readNonNull<ResolvableArtifact>()
-        return TransformationNode.initial(targetComponentVariant, sourceAttributes, transformationStep.transformation, artifacts, transformationStep.recreate(), buildOperationExecutor, calculatedValueContainerFactory)
-    }
-}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/IsolateTransformParametersCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/IsolateTransformParametersCodec.kt
new file mode 100644
index 0000000..79cd88c
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/IsolateTransformParametersCodec.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2020 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.serialization.codecs.transform
+
+import org.gradle.api.artifacts.transform.TransformParameters
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.artifacts.transform.DefaultTransform
+import org.gradle.api.internal.artifacts.transform.TransformParameterScheme
+import org.gradle.api.internal.file.FileCollectionFactory
+import org.gradle.api.internal.initialization.RootScriptDomainObjectContext
+import org.gradle.configurationcache.extensions.uncheckedCast
+import org.gradle.configurationcache.serialization.Codec
+import org.gradle.configurationcache.serialization.ReadContext
+import org.gradle.configurationcache.serialization.WriteContext
+import org.gradle.internal.hash.ClassLoaderHierarchyHasher
+import org.gradle.internal.isolation.IsolatableFactory
+import org.gradle.internal.operations.BuildOperationExecutor
+
+
+class IsolateTransformParametersCodec(
+    val parameterScheme: TransformParameterScheme,
+    val isolatableFactory: IsolatableFactory,
+    val buildOperationExecutor: BuildOperationExecutor,
+    val classLoaderHierarchyHasher: ClassLoaderHierarchyHasher,
+    val fileCollectionFactory: FileCollectionFactory,
+    val documentationRegistry: DocumentationRegistry
+) : Codec<DefaultTransform.IsolateTransformParameters> {
+    override suspend fun WriteContext.encode(value: DefaultTransform.IsolateTransformParameters) {
+        write(value.parameterObject)
+        writeClass(value.implementationClass)
+        writeBoolean(value.isCacheable)
+    }
+
+    override suspend fun ReadContext.decode(): DefaultTransform.IsolateTransformParameters? {
+        val parameterObject: TransformParameters? = read()?.uncheckedCast()
+        val implementationClass = readClass()
+        val cacheable = readBoolean()
+
+        return DefaultTransform.IsolateTransformParameters(
+            parameterObject,
+            implementationClass,
+            cacheable,
+            RootScriptDomainObjectContext.INSTANCE,
+            parameterScheme.inspectionScheme.propertyWalker,
+            isolatableFactory,
+            buildOperationExecutor,
+            classLoaderHierarchyHasher,
+            fileCollectionFactory,
+            documentationRegistry
+        )
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/IsolateTransformerParametersNodeCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/IsolateTransformerParametersNodeCodec.kt
deleted file mode 100644
index 0c47855..0000000
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/IsolateTransformerParametersNodeCodec.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2020 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.serialization.codecs.transform
-
-import org.gradle.api.artifacts.transform.TransformParameters
-import org.gradle.api.internal.DocumentationRegistry
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformParameterScheme
-import org.gradle.api.internal.artifacts.transform.DefaultTransformer
-import org.gradle.api.internal.file.FileCollectionFactory
-import org.gradle.api.internal.initialization.RootScriptDomainObjectContext
-import org.gradle.configurationcache.extensions.uncheckedCast
-import org.gradle.configurationcache.serialization.Codec
-import org.gradle.configurationcache.serialization.ReadContext
-import org.gradle.configurationcache.serialization.WriteContext
-import org.gradle.internal.hash.ClassLoaderHierarchyHasher
-import org.gradle.internal.isolation.IsolatableFactory
-import org.gradle.internal.operations.BuildOperationExecutor
-
-
-class IsolateTransformerParametersNodeCodec(
-    val parameterScheme: ArtifactTransformParameterScheme,
-    val isolatableFactory: IsolatableFactory,
-    val buildOperationExecutor: BuildOperationExecutor,
-    val classLoaderHierarchyHasher: ClassLoaderHierarchyHasher,
-    val fileCollectionFactory: FileCollectionFactory,
-    val documentationRegistry: DocumentationRegistry
-) : Codec<DefaultTransformer.IsolateTransformerParameters> {
-    override suspend fun WriteContext.encode(value: DefaultTransformer.IsolateTransformerParameters) {
-        write(value.parameterObject)
-        writeClass(value.implementationClass)
-        writeBoolean(value.isCacheable)
-    }
-
-    override suspend fun ReadContext.decode(): DefaultTransformer.IsolateTransformerParameters? {
-        val parameterObject: TransformParameters? = read()?.uncheckedCast()
-        val implementationClass = readClass()
-        val cacheable = readBoolean()
-
-        return DefaultTransformer.IsolateTransformerParameters(
-            parameterObject,
-            implementationClass,
-            cacheable,
-            RootScriptDomainObjectContext.INSTANCE,
-            parameterScheme.inspectionScheme.propertyWalker,
-            isolatableFactory,
-            buildOperationExecutor,
-            classLoaderHierarchyHasher,
-            fileCollectionFactory,
-            documentationRegistry
-        )
-    }
-}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformChainCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformChainCodec.kt
new file mode 100644
index 0000000..21e18bb
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformChainCodec.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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.serialization.codecs.transform
+
+import org.gradle.api.internal.artifacts.transform.TransformChain
+import org.gradle.api.internal.artifacts.transform.TransformStep
+import org.gradle.configurationcache.serialization.Codec
+import org.gradle.configurationcache.serialization.ReadContext
+import org.gradle.configurationcache.serialization.WriteContext
+import org.gradle.configurationcache.serialization.readNonNull
+
+
+class TransformChainCodec : Codec<TransformChain> {
+    override suspend fun WriteContext.encode(value: TransformChain) {
+        write(value.init)
+        write(value.last)
+    }
+
+    override suspend fun ReadContext.decode(): TransformChain {
+        val init = read() as TransformChain?
+        val last = readNonNull<TransformStep>()
+        return TransformChain(init, last)
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformStepCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformStepCodec.kt
new file mode 100644
index 0000000..41009e9
--- /dev/null
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformStepCodec.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 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.serialization.codecs.transform
+
+import org.gradle.api.internal.DomainObjectContext
+import org.gradle.api.internal.artifacts.transform.Transform
+import org.gradle.api.internal.artifacts.transform.TransformInvocationFactory
+import org.gradle.api.internal.artifacts.transform.TransformStep
+import org.gradle.configurationcache.serialization.Codec
+import org.gradle.configurationcache.serialization.ReadContext
+import org.gradle.configurationcache.serialization.WriteContext
+import org.gradle.configurationcache.serialization.decodePreservingSharedIdentity
+import org.gradle.configurationcache.serialization.encodePreservingSharedIdentityOf
+import org.gradle.configurationcache.serialization.readNonNull
+import org.gradle.internal.execution.InputFingerprinter
+
+
+internal
+class TransformStepCodec(
+    private val inputFingerprinter: InputFingerprinter
+) : Codec<TransformStep> {
+
+    override suspend fun WriteContext.encode(value: TransformStep) {
+        encodePreservingSharedIdentityOf(value) {
+            val project = value.owningProject ?: throw UnsupportedOperationException("TransformStep must have an owning project to be encoded.")
+            writeString(project.path)
+            write(value.transform)
+        }
+    }
+
+    override suspend fun ReadContext.decode(): TransformStep {
+        return decodePreservingSharedIdentity {
+            val path = readString()
+            val transform = readNonNull<Transform>()
+            val project = getProject(path)
+            val services = project.services
+            TransformStep(
+                transform,
+                services[TransformInvocationFactory::class.java],
+                services[DomainObjectContext::class.java],
+                inputFingerprinter
+            )
+        }
+    }
+}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformationChainCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformationChainCodec.kt
deleted file mode 100644
index 787e99b..0000000
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformationChainCodec.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2020 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.serialization.codecs.transform
-
-import org.gradle.api.internal.artifacts.transform.Transformation
-import org.gradle.api.internal.artifacts.transform.TransformationChain
-import org.gradle.configurationcache.serialization.Codec
-import org.gradle.configurationcache.serialization.ReadContext
-import org.gradle.configurationcache.serialization.WriteContext
-import org.gradle.configurationcache.serialization.readNonNull
-
-
-class TransformationChainCodec : Codec<TransformationChain> {
-    override suspend fun WriteContext.encode(value: TransformationChain) {
-        write(value.first)
-        write(value.second)
-    }
-
-    override suspend fun ReadContext.decode(): TransformationChain? {
-        val first = readNonNull<Transformation>()
-        val second = readNonNull<Transformation>()
-        return TransformationChain(first, second)
-    }
-}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformationStepCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformationStepCodec.kt
deleted file mode 100644
index 45652b1..0000000
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformationStepCodec.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2020 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.serialization.codecs.transform
-
-import org.gradle.api.internal.DomainObjectContext
-import org.gradle.api.internal.artifacts.transform.TransformationStep
-import org.gradle.api.internal.artifacts.transform.Transformer
-import org.gradle.api.internal.artifacts.transform.TransformerInvocationFactory
-import org.gradle.configurationcache.serialization.Codec
-import org.gradle.configurationcache.serialization.ReadContext
-import org.gradle.configurationcache.serialization.WriteContext
-import org.gradle.configurationcache.serialization.decodePreservingSharedIdentity
-import org.gradle.configurationcache.serialization.encodePreservingSharedIdentityOf
-import org.gradle.configurationcache.serialization.readNonNull
-import org.gradle.internal.execution.InputFingerprinter
-
-
-internal
-class TransformationStepCodec(
-    private val inputFingerprinter: InputFingerprinter
-) : Codec<TransformationStep> {
-
-    override suspend fun WriteContext.encode(value: TransformationStep) {
-        encodePreservingSharedIdentityOf(value) {
-            val project = value.owningProject ?: throw UnsupportedOperationException("Transformation must have an owning project to be encoded.")
-            writeString(project.path)
-            write(value.transformer)
-        }
-    }
-
-    override suspend fun ReadContext.decode(): TransformationStep {
-        return decodePreservingSharedIdentity {
-            val path = readString()
-            val transformer = readNonNull<Transformer>()
-            val project = getProject(path)
-            val services = project.services
-            TransformationStep(
-                transformer,
-                services[TransformerInvocationFactory::class.java],
-                services[DomainObjectContext::class.java],
-                inputFingerprinter
-            )
-        }
-    }
-}
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformedArtifactCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformedArtifactCodec.kt
index 8629a93..eb6d58e 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformedArtifactCodec.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformedArtifactCodec.kt
@@ -19,7 +19,7 @@
 import org.gradle.api.artifacts.component.ComponentIdentifier
 import org.gradle.api.capabilities.Capability
 import org.gradle.api.internal.artifacts.PreResolvedResolvableArtifact
-import org.gradle.api.internal.artifacts.transform.BoundTransformationStep
+import org.gradle.api.internal.artifacts.transform.BoundTransformStep
 import org.gradle.api.internal.artifacts.transform.TransformingAsyncArtifactListener
 import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.api.internal.tasks.TaskDependencyContainer
@@ -47,7 +47,7 @@
         writeCollection(value.capabilities)
         write(value.artifact.id.componentIdentifier)
         write(value.artifact.file)
-        write(unpackTransformationSteps(value.transformationSteps))
+        write(unpackTransformSteps(value.transformSteps))
     }
 
     override suspend fun ReadContext.decode(): TransformingAsyncArtifactListener.TransformedArtifact {
@@ -58,7 +58,7 @@
         val file = readNonNull<File>()
         val artifactId = ComponentFileArtifactIdentifier(ownerId, file.name)
         val artifact = PreResolvedResolvableArtifact(null, DefaultIvyArtifactName.forFile(file, null), artifactId, calculatedValueContainerFactory.create(Describables.of(artifactId), file), TaskDependencyContainer.EMPTY, calculatedValueContainerFactory)
-        val steps = readNonNull<List<TransformStepSpec>>().map { BoundTransformationStep(it.transformation, it.recreate()) }
+        val steps = readNonNull<List<TransformStepSpec>>().map { BoundTransformStep(it.transformStep, it.recreateDependencies()) }
         return TransformingAsyncArtifactListener.TransformedArtifact(variantName, target, capabilities, artifact, steps)
     }
 }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformedProjectArtifactSetCodec.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformedProjectArtifactSetCodec.kt
index aef6be0..68820fa 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformedProjectArtifactSetCodec.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/TransformedProjectArtifactSetCodec.kt
@@ -17,7 +17,7 @@
 package org.gradle.configurationcache.serialization.codecs.transform
 
 import org.gradle.api.internal.artifacts.transform.ComponentVariantIdentifier
-import org.gradle.api.internal.artifacts.transform.TransformationNode
+import org.gradle.api.internal.artifacts.transform.TransformStepNode
 import org.gradle.api.internal.artifacts.transform.TransformedProjectArtifactSet
 import org.gradle.configurationcache.extensions.uncheckedCast
 import org.gradle.configurationcache.serialization.Codec
@@ -41,7 +41,7 @@
     override suspend fun ReadContext.decode(): TransformedProjectArtifactSet {
         return decodePreservingSharedIdentity {
             val targetVariant = readNonNull<ComponentVariantIdentifier>()
-            val nodes: List<TransformationNode> = readList().uncheckedCast()
+            val nodes: List<TransformStepNode> = readList().uncheckedCast()
             TransformedProjectArtifactSet(targetVariant, nodes)
         }
     }
diff --git a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/Transforms.kt b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/Transforms.kt
index 57c18b8..ca78986 100644
--- a/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/Transforms.kt
+++ b/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/serialization/codecs/transform/Transforms.kt
@@ -17,34 +17,34 @@
 package org.gradle.configurationcache.serialization.codecs.transform
 
 import org.gradle.api.file.FileCollection
-import org.gradle.api.internal.artifacts.configurations.ConfigurationIdentity
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformDependencies
-import org.gradle.api.internal.artifacts.transform.BoundTransformationStep
-import org.gradle.api.internal.artifacts.transform.DefaultArtifactTransformDependencies
+import org.gradle.api.internal.artifacts.transform.BoundTransformStep
+import org.gradle.api.internal.artifacts.transform.DefaultTransformDependencies
 import org.gradle.api.internal.artifacts.transform.DefaultTransformUpstreamDependenciesResolver
+import org.gradle.api.internal.artifacts.transform.TransformDependencies
+import org.gradle.api.internal.artifacts.transform.TransformStep
+import org.gradle.api.internal.artifacts.transform.TransformStepNode
 import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependencies
-import org.gradle.api.internal.artifacts.transform.TransformationNode
-import org.gradle.api.internal.artifacts.transform.TransformationStep
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext
 import org.gradle.configurationcache.serialization.Codec
 import org.gradle.configurationcache.serialization.ReadContext
 import org.gradle.configurationcache.serialization.WriteContext
 import org.gradle.configurationcache.serialization.readNonNull
 import org.gradle.internal.Try
+import org.gradle.operations.dependencies.configurations.ConfigurationIdentity
 
 
-sealed class TransformStepSpec(val transformation: TransformationStep) {
-    abstract fun recreate(): TransformUpstreamDependencies
+sealed class TransformStepSpec(val transformStep: TransformStep) {
+    abstract fun recreateDependencies(): TransformUpstreamDependencies
 
-    class NoDependencies(transformation: TransformationStep) : TransformStepSpec(transformation) {
-        override fun recreate(): TransformUpstreamDependencies {
+    class NoDependencies(transformStep: TransformStep) : TransformStepSpec(transformStep) {
+        override fun recreateDependencies(): TransformUpstreamDependencies {
             return DefaultTransformUpstreamDependenciesResolver.NO_DEPENDENCIES
         }
     }
 
-    class FileDependencies(transformation: TransformationStep, val files: FileCollection, val configurationIdentity: ConfigurationIdentity) : TransformStepSpec(transformation) {
-        override fun recreate(): TransformUpstreamDependencies {
-            return FixedUpstreamDependencies(DefaultArtifactTransformDependencies(files), configurationIdentity)
+    class FileDependencies(transformStep: TransformStep, val files: FileCollection, val configurationIdentity: ConfigurationIdentity) : TransformStepSpec(transformStep) {
+        override fun recreateDependencies(): TransformUpstreamDependencies {
+            return FixedUpstreamDependencies(DefaultTransformDependencies(files), configurationIdentity)
         }
     }
 }
@@ -52,7 +52,7 @@
 
 object TransformStepSpecCodec : Codec<TransformStepSpec> {
     override suspend fun WriteContext.encode(value: TransformStepSpec) {
-        write(value.transformation)
+        write(value.transformStep)
         if (value is TransformStepSpec.FileDependencies) {
             writeBoolean(true)
             write(value.files)
@@ -63,36 +63,36 @@
     }
 
     override suspend fun ReadContext.decode(): TransformStepSpec {
-        val transformation = readNonNull<TransformationStep>()
+        val transformStep = readNonNull<TransformStep>()
         return if (readBoolean()) {
-            return TransformStepSpec.FileDependencies(transformation, read() as FileCollection, read() as ConfigurationIdentity)
+            return TransformStepSpec.FileDependencies(transformStep, read() as FileCollection, read() as ConfigurationIdentity)
         } else {
-            TransformStepSpec.NoDependencies(transformation)
+            TransformStepSpec.NoDependencies(transformStep)
         }
     }
 }
 
 
-fun unpackTransformationSteps(steps: List<BoundTransformationStep>): List<TransformStepSpec> {
-    return steps.map { unpackTransformationStep(it.transformation, it.upstreamDependencies) }
+fun unpackTransformSteps(steps: List<BoundTransformStep>): List<TransformStepSpec> {
+    return steps.map { unpackTransformStep(it.transformStep, it.upstreamDependencies) }
 }
 
 
-fun unpackTransformationStep(node: TransformationNode): TransformStepSpec {
-    return unpackTransformationStep(node.transformationStep, node.upstreamDependencies)
+fun unpackTransformStep(node: TransformStepNode): TransformStepSpec {
+    return unpackTransformStep(node.transformStep, node.upstreamDependencies)
 }
 
 
-fun unpackTransformationStep(transformation: TransformationStep, upstreamDependencies: TransformUpstreamDependencies): TransformStepSpec {
-    return if (transformation.requiresDependencies()) {
-        TransformStepSpec.FileDependencies(transformation, upstreamDependencies.selectedArtifacts(), upstreamDependencies.configurationIdentity!!)
+fun unpackTransformStep(transformStep: TransformStep, upstreamDependencies: TransformUpstreamDependencies): TransformStepSpec {
+    return if (transformStep.requiresDependencies()) {
+        TransformStepSpec.FileDependencies(transformStep, upstreamDependencies.selectedArtifacts(), upstreamDependencies.configurationIdentity!!)
     } else {
-        TransformStepSpec.NoDependencies(transformation)
+        TransformStepSpec.NoDependencies(transformStep)
     }
 }
 
 
-class FixedUpstreamDependencies(private val dependencies: ArtifactTransformDependencies, private val configurationIdentity: ConfigurationIdentity) : TransformUpstreamDependencies {
+class FixedUpstreamDependencies(private val dependencies: TransformDependencies, private val configurationIdentity: ConfigurationIdentity) : TransformUpstreamDependencies {
 
     override fun getConfigurationIdentity(): ConfigurationIdentity {
         return configurationIdentity
@@ -109,7 +109,7 @@
     override fun finalizeIfNotAlready() {
     }
 
-    override fun computeArtifacts(): Try<ArtifactTransformDependencies> {
+    override fun computeArtifacts(): Try<TransformDependencies> {
         return Try.successful(dependencies)
     }
 }
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 5aa564f..d34903d 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
@@ -17,12 +17,16 @@
 package org.gradle.configurationcache
 
 import org.gradle.api.internal.StartParameterInternal
+import org.gradle.api.logging.LogLevel
 import org.gradle.configurationcache.initialization.ConfigurationCacheStartParameter
 import org.gradle.initialization.layout.BuildLayout
 import org.gradle.internal.buildoption.DefaultInternalOptions
 import org.gradle.internal.buildtree.BuildModelParameters
 import org.gradle.internal.buildtree.RunTasksRequirements
+import org.gradle.internal.hash.HashCode
+import org.gradle.internal.hash.Hashing
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.internal.EncryptionAlgorithm
 import org.hamcrest.CoreMatchers.equalTo
 import org.hamcrest.CoreMatchers.not
 import org.hamcrest.MatcherAssert.assertThat
@@ -132,9 +136,17 @@
                 ),
                 startParameter,
                 DefaultInternalOptions(mapOf()),
-                BuildModelParameters(false, true, false, false, false, false, false)
+                BuildModelParameters(false, true, false, false, false, false, false, LogLevel.LIFECYCLE)
             ),
-            RunTasksRequirements(startParameter)
+            RunTasksRequirements(startParameter),
+            object : EncryptionConfiguration {
+                override val encryptionKeyHashCode: HashCode
+                    get() = Hashing.newHasher().hash()
+                override val isEncrypting: Boolean
+                    get() = false
+                override val encryptionAlgorithm: EncryptionAlgorithm
+                    get() = EncryptionAlgorithm.NONE
+            }
         ).string
     }
 
diff --git a/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintCheckerTest.kt b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintCheckerTest.kt
index b91b423..aee3c57 100644
--- a/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintCheckerTest.kt
+++ b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/fingerprint/ConfigurationCacheFingerprintCheckerTest.kt
@@ -313,6 +313,9 @@
         override fun push(codec: Codec<Any?>): Unit =
             undefined()
 
+        override fun push(owner: IsolateOwner): Unit =
+            undefined()
+
         override fun push(owner: IsolateOwner, codec: Codec<Any?>): Unit =
             undefined()
 
@@ -419,6 +422,9 @@
         override fun push(codec: Codec<Any?>): Unit =
             undefined()
 
+        override fun push(owner: IsolateOwner): Unit =
+            undefined()
+
         override fun push(owner: IsolateOwner, codec: Codec<Any?>): Unit =
             undefined()
 
diff --git a/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/problems/JsonModelWriterTest.kt b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/problems/JsonModelWriterTest.kt
new file mode 100644
index 0000000..3e7b598
--- /dev/null
+++ b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/problems/JsonModelWriterTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.problems
+
+import groovy.json.JsonSlurper
+import org.gradle.configurationcache.extensions.uncheckedCast
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.hasEntry
+import org.junit.Test
+import java.io.StringWriter
+
+
+class JsonModelWriterTest {
+
+    @Test
+    fun `encodes model with empty strings correctly`() {
+        assertThat(
+            jsonModelFor {
+                beginModel()
+                writeDiagnostic(
+                    DiagnosticKind.INPUT,
+                    PropertyProblem(
+                        PropertyTrace.Unknown,
+                        StructuredMessage.build { reference("") }
+                    )
+                )
+                endModel("", "", 0)
+            },
+            hasEntry(
+                "diagnostics",
+                listOf(
+                    mapOf(
+                        "trace" to listOf(mapOf("kind" to "Unknown")),
+                        "input" to listOf(mapOf("name" to ""))
+                    )
+                )
+            )
+        )
+    }
+
+    private
+    fun jsonModelFor(builder: JsonModelWriter.() -> Unit): Map<String, Any> =
+        JsonSlurper().parseText(
+            StringWriter().also {
+                JsonModelWriter(it).apply(builder)
+            }.toString()
+        ).uncheckedCast()
+}
diff --git a/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/serialization/codecs/AbstractUserTypeCodecTest.kt b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/serialization/codecs/AbstractUserTypeCodecTest.kt
index 5e65e3b..36b4618 100644
--- a/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/serialization/codecs/AbstractUserTypeCodecTest.kt
+++ b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/serialization/codecs/AbstractUserTypeCodecTest.kt
@@ -182,5 +182,6 @@
         documentationRegistry = mock(),
         javaSerializationEncodingLookup = JavaSerializationEncodingLookup(),
         flowProviders = mock(),
+        transformStepNodeFactory = mock(),
     )
 }
diff --git a/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/serialization/codecs/BaseTypeCodecTest.kt b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/serialization/codecs/BaseTypeCodecTest.kt
new file mode 100644
index 0000000..9343a00
--- /dev/null
+++ b/subprojects/configuration-cache/src/test/kotlin/org/gradle/configurationcache/serialization/codecs/BaseTypeCodecTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 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.serialization.codecs
+
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import java.util.Hashtable
+import java.util.Properties
+
+
+class BaseTypeCodecTest : AbstractUserTypeCodecTest() {
+
+    @Test
+    fun `can handle Properties`() {
+        val properties = Properties()
+        properties.setProperty("prop1", "value1")
+        properties.setProperty("prop2", "value2")
+        configurationCacheRoundtripOf(properties).run {
+            assertThat(properties, equalTo(this))
+        }
+    }
+
+    @Test
+    fun `can handle Hashtable`() {
+        val hashtable = Hashtable<String, Any>(100)
+        hashtable.put("key1", "value1")
+        hashtable.put("key2", true)
+        hashtable.put("key3", 42)
+        configurationCacheRoundtripOf(hashtable).run {
+            assertThat(hashtable, equalTo(this))
+        }
+    }
+}
diff --git a/subprojects/core-api/src/main/java/org/gradle/BuildListener.java b/subprojects/core-api/src/main/java/org/gradle/BuildListener.java
index 3b409e5..3fb2401 100644
--- a/subprojects/core-api/src/main/java/org/gradle/BuildListener.java
+++ b/subprojects/core-api/src/main/java/org/gradle/BuildListener.java
@@ -15,8 +15,10 @@
  */
 package org.gradle;
 
+import org.gradle.api.flow.FlowProviders;
 import org.gradle.api.initialization.Settings;
 import org.gradle.api.invocation.Gradle;
+import org.gradle.internal.DeprecatedInGradleScope;
 import org.gradle.internal.service.scopes.EventScope;
 import org.gradle.internal.service.scopes.Scopes;
 
@@ -26,6 +28,7 @@
  * @see org.gradle.api.invocation.Gradle#addListener(Object)
  */
 @EventScope(Scopes.Build.class)
+@DeprecatedInGradleScope
 public interface BuildListener {
 
     /**
@@ -65,6 +68,7 @@ default void beforeSettings(Settings settings) {}
      *
      * @param result The result of the build. Never null.
      * @deprecated This method is not supported when configuration caching is enabled.
+     * @see FlowProviders#getBuildWorkResult()
      */
     @Deprecated
     void buildFinished(BuildResult result);
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/NamedDomainObjectSet.java b/subprojects/core-api/src/main/java/org/gradle/api/NamedDomainObjectSet.java
index 74bb010..8f97143 100755
--- a/subprojects/core-api/src/main/java/org/gradle/api/NamedDomainObjectSet.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/NamedDomainObjectSet.java
@@ -31,7 +31,7 @@
  *
  * @param <T> The type of objects in the set
  */
-public interface NamedDomainObjectSet<T> extends NamedDomainObjectCollection<T>, Set<T> {
+public interface NamedDomainObjectSet<T> extends NamedDomainObjectCollection<T>, DomainObjectSet<T> {
 
     /**
      * {@inheritDoc}
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/CacheableRule.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/CacheableRule.java
index accf186..09dd515 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/CacheableRule.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/CacheableRule.java
@@ -22,7 +22,8 @@
 import java.lang.annotation.Target;
 
 /**
- * Denotates a rule which execution is subject to caching.
+ * Declares a rule eligible for caching.
+ *
  * @since 4.9
  */
 @Documented
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ComponentSelection.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ComponentSelection.java
index dcd6fd1..8a5e7db 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ComponentSelection.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ComponentSelection.java
@@ -21,7 +21,7 @@
 
 import javax.annotation.Nullable;
 
-/***
+/**
  * Represents a tuple of the component selector of a module and a candidate version
  * to be evaluated in a component selection rule.
  */
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ComponentSelectionRules.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ComponentSelectionRules.java
index 1157d45..55da7dc 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ComponentSelectionRules.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ComponentSelectionRules.java
@@ -21,7 +21,7 @@
 import org.gradle.api.Action;
 import org.gradle.internal.HasInternalProtocol;
 
-/***
+/**
  * Represents a container for component selection rules.  Rules can be applied as part of the
  * resolutionStrategy of a configuration and individual components can be explicitly accepted
  * or rejected by rule.  Components that are neither accepted or rejected will be subject to
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/Configuration.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/Configuration.java
index 2aeb829..d9dd8a7 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/Configuration.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/Configuration.java
@@ -52,6 +52,8 @@ public interface Configuration extends FileCollection, HasConfigurableAttributes
      * Returns the resolution strategy used by this configuration.
      * The resolution strategy provides extra details on how to resolve this configuration.
      * See docs for {@link ResolutionStrategy} for more info and examples.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations, but will not warn if used otherwise.
      *
      * @return resolution strategy
      * @since 1.0-milestone-6
@@ -61,6 +63,8 @@ public interface Configuration extends FileCollection, HasConfigurableAttributes
     /**
      * The resolution strategy provides extra details on how to resolve this configuration.
      * See docs for {@link ResolutionStrategy} for more info and examples.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations, but will not warn if used otherwise.
      *
      * @param closure closure applied to the {@link ResolutionStrategy}
      * @return this configuration instance
@@ -71,6 +75,8 @@ public interface Configuration extends FileCollection, HasConfigurableAttributes
     /**
      * The resolution strategy provides extra details on how to resolve this configuration.
      * See docs for {@link ResolutionStrategy} for more info and examples.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations, but will not warn if used otherwise.
      *
      * @param action action applied to the {@link ResolutionStrategy}
      * @return this configuration instance
@@ -86,6 +92,8 @@ enum State { UNRESOLVED, RESOLVED, RESOLVED_WITH_FAILURES }
 
     /**
      * Returns the state of the configuration.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations, but will not warn if used otherwise.
      *
      * @see org.gradle.api.artifacts.Configuration.State
      * @return The state of the configuration
@@ -120,6 +128,8 @@ public String determineName(Configuration c) {
     /**
      * Sets the visibility of this configuration. When visible is set to true, this configuration is visible outside
      * the project it belongs to. The default value is true.
+     * 
+     * @implSpec Usage: This method should only be called on consumable configurations, but will not warn if used otherwise.
      *
      * @param visible true if this is a visible configuration
      * @return this configuration
@@ -185,15 +195,18 @@ public String determineName(Configuration c) {
     Configuration setDescription(@Nullable String description);
 
     /**
-     * Gets a ordered set including this configuration and all superconfigurations
+     * Gets an ordered set including this configuration and all superconfigurations
      * recursively.
-     * @return the list of all configurations
+     * @return the set of all configurations
      */
     Set<Configuration> getHierarchy();
 
     /**
      * Resolves this configuration. This locates and downloads the files which make up this configuration, and returns
      * the resulting set of files.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has allowed this usage but marked it as deprecated.
      *
      * @return The files of this configuration.
      */
@@ -202,6 +215,10 @@ public String determineName(Configuration c) {
     /**
      * Takes a closure which gets coerced into a {@link Spec}. Behaves otherwise in the same way as
      * {@link #files(org.gradle.api.specs.Spec)}.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations and should fail if
+     * called on a configuration that does not permit this usage.  It should warn if called on a configuration that has
+     * allowed this usage but marked it as deprecated.
      *
      * @param dependencySpecClosure The closure describing a filter applied to the all the dependencies of this configuration (including dependencies from extended configurations).
      * @return The files of a subset of dependencies of this configuration.
@@ -213,6 +230,10 @@ public String determineName(Configuration c) {
      * But only the resulting set of files belonging to the subset of dependencies specified by the dependencySpec
      * is returned.
      *
+     * @implSpec Usage: This method should only be called on resolvable configurations and should fail if
+     * called on a configuration that does not permit this usage.  It should warn if called on a configuration that has
+     * allowed this usage but marked it as deprecated.
+     *
      * @param dependencySpec The spec describing a filter applied to the all the dependencies of this configuration (including dependencies from extended configurations).
      * @return The files of a subset of dependencies of this configuration.
      */
@@ -223,6 +244,10 @@ public String determineName(Configuration c) {
      * But only the resulting set of files belonging to the specified dependencies
      * is returned.
      *
+     * @implSpec Usage: This method should only be called on resolvable configurations and should fail if
+     * called on a configuration that does not permit this usage.  It should warn if called on a configuration that has
+     * allowed this usage but marked it as deprecated.
+     *
      * @param dependencies The dependencies to be resolved
      * @return The files of a subset of dependencies of this configuration.
      */
@@ -233,6 +258,10 @@ public String determineName(Configuration c) {
      * This locates and downloads the files which make up this configuration. Only the resulting set of files belonging to the subset
      * of dependencies specified by the dependencySpec is contained in the FileCollection.
      *
+     * @implSpec Usage: This method should only be called on resolvable configurations and should fail if
+     * called on a configuration that does not permit this usage.  It should warn if called on a configuration that has
+     * allowed this usage but marked it as deprecated.
+     *
      * @param dependencySpec The spec describing a filter applied to the all the dependencies of this configuration (including dependencies from extended configurations).
      * @return The FileCollection with a subset of dependencies of this configuration.
      */
@@ -242,6 +271,10 @@ public String determineName(Configuration c) {
      * Takes a closure which gets coerced into a {@link Spec}. Behaves otherwise in the same way as
      * {@link #fileCollection(org.gradle.api.specs.Spec)}.
      *
+     * @implSpec Usage: This method should only be called on resolvable configurations and should fail if
+     * called on a configuration that does not permit this usage.  It should warn if called on a configuration that has
+     * allowed this usage but marked it as deprecated.
+     *
      * @param dependencySpecClosure The closure describing a filter applied to the all the dependencies of this configuration (including dependencies from extended configurations).
      * @return The FileCollection with a subset of dependencies of this configuration.
      */
@@ -252,6 +285,10 @@ public String determineName(Configuration c) {
      * This locates and downloads the files which make up this configuration. Only the resulting set of files belonging to specified
      * dependencies is contained in the FileCollection.
      *
+     * @implSpec Usage: This method should only be called on resolvable configurations and should fail if
+     * called on a configuration that does not permit this usage.  It should warn if called on a configuration that has
+     * allowed this usage but marked it as deprecated.
+     *
      * @param dependencies The dependencies for which the FileCollection should contain the files.
      * @return The FileCollection with a subset of dependencies of this configuration.
      */
@@ -261,6 +298,10 @@ public String determineName(Configuration c) {
      * Resolves this configuration. This locates and downloads the files which make up this configuration, and returns
      * a {@link ResolvedConfiguration} that may be used to determine information about the resolve (including errors).
      *
+     * @implSpec Usage: This method should only be called on resolvable configurations and should fail if
+     * called on a configuration that does not permit this usage.  It should warn if called on a configuration that has
+     * allowed this usage but marked it as deprecated.
+     *
      * @return The ResolvedConfiguration object
      */
     ResolvedConfiguration getResolvedConfiguration();
@@ -278,6 +319,8 @@ public String determineName(Configuration c) {
     /**
      * Returns a {@code TaskDependency} object containing all required dependencies to build the local dependencies
      * (e.g. project dependencies) belonging to this configuration or to one of its super configurations.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations, but will not warn if used otherwise.
      *
      * @return a TaskDependency object
      */
@@ -303,6 +346,8 @@ public String determineName(Configuration c) {
      * <p>
      * This method does not resolve the configuration. Therefore, the return value does not include
      * transitive dependencies.
+     * 
+     * @implSpec Usage: This method should only be called on declarable configurations, but will not warn if used otherwise.
      *
      * @return the set of dependencies
      * @see #extendsFrom(Configuration...)
@@ -325,6 +370,8 @@ public String determineName(Configuration c) {
      * Gets the set of dependency constraints directly contained in this configuration
      * (ignoring superconfigurations).
      *
+     * @implSpec Usage: This method should only be called on declarable configurations, but will not warn if used otherwise.
+     *
      * @return the set of dependency constraints
      *
      * @since 4.6
@@ -343,6 +390,8 @@ public String determineName(Configuration c) {
 
     /**
      * Returns the artifacts of this configuration excluding the artifacts of extended configurations.
+     * 
+     * @implSpec Usage: This method should only be called on consumable configurations, but will not warn if used otherwise.
      *
      * @return The set.
      */
@@ -389,11 +438,14 @@ public String determineName(Configuration c) {
      *      dependencies.add(owner.project.dependencies.create("org.gradle:my-util:1.0"))
      * }
      * </pre>
-     *
+     * <p>
      * A {@code Configuration} is considered empty even if it extends another, non-empty {@code Configuration}.
-     *
+     * <p>
      * If multiple actions are supplied, each action will be executed until the set of dependencies is no longer empty.
      * Remaining actions will be ignored.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if 
+     * called on a configuration that does not permit this usage, or has allowed this usage but marked it as deprecated.
      *
      * @param action the action to execute when the configuration has no defined dependencies.
      * @return this
@@ -436,10 +488,13 @@ public String determineName(Configuration c) {
      *
      * @return All the configurations belonging to the configuration container that this set belongs to itself.
      */
+    @Deprecated
     Set<Configuration> getAll();
 
     /**
      * Returns the incoming dependencies of this configuration.
+     * 
+     * @implSpec Usage: This method should only be called on consumable and resolvable configurations, but will not warn if used otherwise.
      *
      * @return The incoming dependencies of this configuration. Never {@code null}.
      */
@@ -449,6 +504,8 @@ public String determineName(Configuration c) {
      * Returns the outgoing {@link ConfigurationPublications} instance that advertises and allows configuring the artifacts and variants published by this configuration.
      * <p>
      * This allows adding additional artifacts and accessing and configuring variants to publish.
+     * 
+     * @implSpec Usage: This method should only be called on consumable configurations, but will not warn if used otherwise.
      *
      * @return The outgoing publications object containing artifacts and variants published by this configuration.
      * @since 3.4
@@ -457,6 +514,8 @@ public String determineName(Configuration c) {
 
     /**
      * Configures the outgoing {@link ConfigurationPublications} instance that advertises and allows configuring the artifacts and variants published by this configuration.
+     * 
+     * @implSpec Usage: This method should only be called on consumable configurations, but will not warn if used otherwise.
      *
      * @param action The action to perform the configuration.
      * @since 3.4
@@ -468,6 +527,13 @@ public String determineName(Configuration c) {
      * (without contributions from superconfigurations).  The new configuration will be in the
      * UNRESOLVED state, but will retain all other attributes of this configuration except superconfigurations.
      * {@link #getHierarchy()} for the copy will not include any superconfigurations.
+     * <p>
+     * This method is only intended for use for specific situations involving resolvable configuration, it is
+     * <strong>NOT</strong> intended as a general-purpose copying mechanism.
+     *
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has allowed this usage but marked it as deprecated.
+     *
      * @return copy of this configuration
      */
     Configuration copy();
@@ -477,6 +543,13 @@ public String determineName(Configuration c) {
      * and those derived from superconfigurations.  The new configuration will be in the
      * UNRESOLVED state, but will retain all other attributes of this configuration except superconfigurations.
      * {@link #getHierarchy()} for the copy will not include any superconfigurations.
+     * <p>
+     * This method is only intended for use for specific situations involving resolvable configuration, it is
+     * <strong>NOT</strong> intended as a general-purpose copying mechanism.
+     *
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has allowed this usage but marked it as deprecated.
+     *
      * @return copy of this configuration
      */
     Configuration copyRecursive();
@@ -484,6 +557,12 @@ public String determineName(Configuration c) {
     /**
      * Creates a copy of this configuration ignoring superconfigurations (see {@link #copy()} but filtering
      * the dependencies using the specified dependency spec.
+     * <p>
+     * This method is only intended for use for specific situations involving resolvable configuration, it is
+     * <strong>NOT</strong> intended as a general-purpose copying mechanism.
+     *
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has allowed this usage but marked it as deprecated.
      *
      * @param dependencySpec filtering requirements
      * @return copy of this configuration
@@ -493,6 +572,12 @@ public String determineName(Configuration c) {
     /**
      * Creates a copy of this configuration with dependencies from superconfigurations (see {@link #copyRecursive()})
      * but filtering the dependencies using the dependencySpec.
+     * <p>
+     * This method is only intended for use for specific situations involving resolvable configuration, it is
+     * <strong>NOT</strong> intended as a general-purpose copying mechanism.
+     *
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has allowed this usage but marked it as deprecated.
      *
      * @param dependencySpec filtering requirements
      * @return copy of this configuration
@@ -501,6 +586,12 @@ public String determineName(Configuration c) {
 
     /**
      * Takes a closure which gets coerced into a {@link Spec}. Behaves otherwise in the same way as {@link #copy(org.gradle.api.specs.Spec)}
+     *  <p>
+     * This method is only intended for use for specific situations involving resolvable configuration, it is
+     * <strong>NOT</strong> intended as a general-purpose copying mechanism.
+     *
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has allowed this usage but marked it as deprecated.
      *
      * @param dependencySpec filtering requirements
      * @return copy of this configuration
@@ -509,6 +600,12 @@ public String determineName(Configuration c) {
 
     /**
      * Takes a closure which gets coerced into a {@link Spec}. Behaves otherwise in the same way as {@link #copyRecursive(org.gradle.api.specs.Spec)}
+     * <p>
+     * This method is only intended for use for specific situations involving resolvable configuration, it is
+     * <strong>NOT</strong> intended as a general-purpose copying mechanism.
+     *
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has allowed this usage but marked it as deprecated.
      *
      * @param dependencySpec filtering requirements
      * @return copy of this configuration
@@ -544,10 +641,31 @@ public String determineName(Configuration c) {
     boolean isCanBeResolved();
 
     /**
+     * Configures if a configuration can have dependencies declared upon it.
+     *
+     * @since 8.2
+     */
+    @Incubating
+    void setCanBeDeclared(boolean allowed);
+
+    /**
+     * Returns true if it is allowed to declare dependencies upon this configuration.
+     * Defaults to true.
+     * @return true if this configuration can have dependencies declared
+     *
+     * @since 8.2
+     */
+    @Incubating
+    boolean isCanBeDeclared();
+
+    /**
      * Tells that this configuration, when resolved, should resolve versions consistently
      * from the resolution result of another resolvable configuration. For example, it's
      * expected that the versions of the runtime classpath are the same as the versions
      * from the compile classpath.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has had allowed this usage but marked it as deprecated.
      *
      * @param versionsSource another resolvable configuration to use as reference for versions
      * @return this configuration
@@ -558,7 +676,10 @@ public String determineName(Configuration c) {
     Configuration shouldResolveConsistentlyWith(Configuration versionsSource);
 
     /**
-     * Disables consistent resolution for this configuration
+     * Disables consistent resolution for this configuration.
+     * 
+     * @implSpec Usage: This method should only be called on resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has had allowed this usage but marked it as deprecated.
      *
      * @since 6.8
      */
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/component/BuildIdentifier.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/component/BuildIdentifier.java
index d8db0be..be1f11d 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/component/BuildIdentifier.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/component/BuildIdentifier.java
@@ -20,13 +20,27 @@
  * Identifies a Gradle build. The identifier is unique within a Gradle invocation, so for example, each included build will have a different identifier.
  */
 public interface BuildIdentifier {
+
+    /**
+     * Absolute build path of the build within the Gradle invocation.
+     *
+     * @since 8.2
+     */
+    String getBuildPath();
+
     /**
      * The name of the build.
+     *
+     * @deprecated Use {@link #getBuildPath()} instead.
      */
+    @Deprecated
     String getName();
 
     /**
      * Is this build the one that's currently executing?
+     *
+     * @deprecated Compare {@link #getBuildPath()} with the build path of the current build instead.
      */
+    @Deprecated
     boolean isCurrentBuild();
 }
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/component/ProjectComponentSelector.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/component/ProjectComponentSelector.java
index a2ac7ba..f59f060 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/component/ProjectComponentSelector.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/component/ProjectComponentSelector.java
@@ -25,10 +25,19 @@
 @UsedByScanPlugin
 public interface ProjectComponentSelector extends ComponentSelector {
     /**
+     * Absolute build path of the build within the Gradle invocation to select a project from.
+     *
+     * @since 8.2
+     */
+    String getBuildPath();
+
+    /**
      * The name of the build to select a project from.
      *
      * @return The build name
+     * @deprecated Use {@link #getBuildPath()} instead.
      */
+    @Deprecated
     String getBuildName();
 
     /**
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/dsl/DependencyHandler.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/dsl/DependencyHandler.java
index 5b59939..7cc5e51 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/dsl/DependencyHandler.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/dsl/DependencyHandler.java
@@ -195,8 +195,9 @@
  * <p>The notation <code>project(':project-a')</code> is similar to the syntax you use
  * when configuring a projectA in a multi-module gradle project.
  *
- * <p>By default, when you declare dependency to projectA, you actually declare dependency to the 'default' configuration of the projectA.
- * If you need to depend on a specific configuration of projectA, use map notation for projects:
+ * <p>Project dependencies are resolved by treating each consumable configuration in the target
+ * project as a variant and performing variant-aware attribute matching against them.
+ * However, in order to override this process, an explicit target configuration can be specified:
  * <p><code><i>configurationName</i> project(path: ':project-a', configuration: 'someOtherConfiguration')</code>
  *
  * <p>Project dependencies are represented using a {@link org.gradle.api.artifacts.ProjectDependency}.
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ivy/IvyModuleDescriptor.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ivy/IvyModuleDescriptor.java
index fb5f60d..329e326 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ivy/IvyModuleDescriptor.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/ivy/IvyModuleDescriptor.java
@@ -21,7 +21,7 @@
  * The metadata about an Ivy module that acts as an input to a component metadata rule.
  */
 public interface IvyModuleDescriptor {
-    /***
+    /**
      * Returns the branch attribute of the info element in this descriptor.
      *
      * @return the branch for this descriptor, or null if no branch was declared in the descriptor.
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/repositories/IvyArtifactRepository.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/repositories/IvyArtifactRepository.java
index c357449..6f7b3d5 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/repositories/IvyArtifactRepository.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/repositories/IvyArtifactRepository.java
@@ -154,7 +154,7 @@ public interface IvyArtifactRepository extends ArtifactRepository, UrlArtifactRe
      * @param config The action used to configure the layout.
      * @since 5.0
      */
-    void patternLayout(Action<? super  IvyPatternRepositoryLayout> config);
+    void patternLayout(Action<? super IvyPatternRepositoryLayout> config);
 
     /**
      * Returns the meta-data provider used when resolving artifacts from this repository. The provider is responsible for locating and interpreting the meta-data
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/result/ResolutionResult.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/result/ResolutionResult.java
index 5f623f6..e82647a 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/result/ResolutionResult.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/result/ResolutionResult.java
@@ -21,6 +21,7 @@
 import org.gradle.api.Action;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.provider.Provider;
+import org.gradle.internal.HasInternalProtocol;
 import org.gradle.internal.scan.UsedByScanPlugin;
 
 import java.util.Set;
@@ -30,6 +31,7 @@
  * in the resolved dependency graph, and the dependencies between them.
  */
 @UsedByScanPlugin
+@HasInternalProtocol
 public interface ResolutionResult {
 
     /**
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/transform/VariantTransformConfigurationException.java b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/transform/VariantTransformConfigurationException.java
index a61c54e..24b64ff 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/artifacts/transform/VariantTransformConfigurationException.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/artifacts/transform/VariantTransformConfigurationException.java
@@ -20,7 +20,7 @@
 import org.gradle.internal.exceptions.Contextual;
 
 /**
- * An exception to report a problem during transformation execution.
+ * An exception to report a problem during a transform execution.
  *
  * @since 3.5
  */
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/execution/TaskActionListener.java b/subprojects/core-api/src/main/java/org/gradle/api/execution/TaskActionListener.java
index dd5b781..7aa8e59 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/execution/TaskActionListener.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/execution/TaskActionListener.java
@@ -16,6 +16,7 @@
 package org.gradle.api.execution;
 
 import org.gradle.api.Task;
+import org.gradle.internal.DeprecatedInGradleScope;
 import org.gradle.internal.service.scopes.EventScope;
 import org.gradle.internal.service.scopes.Scopes;
 
@@ -26,6 +27,7 @@
  */
 @EventScope(Scopes.Build.class)
 @Deprecated
+@DeprecatedInGradleScope
 public interface TaskActionListener {
     /**
      * This method is called immediately before the task starts performing its actions.
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/execution/TaskExecutionListener.java b/subprojects/core-api/src/main/java/org/gradle/api/execution/TaskExecutionListener.java
index 0f8721b..0e05663 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/execution/TaskExecutionListener.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/execution/TaskExecutionListener.java
@@ -18,6 +18,7 @@
 
 import org.gradle.api.Task;
 import org.gradle.api.tasks.TaskState;
+import org.gradle.internal.DeprecatedInGradleScope;
 import org.gradle.internal.service.scopes.EventScope;
 import org.gradle.internal.service.scopes.Scopes;
 
@@ -30,6 +31,7 @@
  */
 @EventScope(Scopes.Build.class)
 @Deprecated
+@DeprecatedInGradleScope
 public interface TaskExecutionListener {
     /**
      * This method is called immediately before a task is executed.
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/flow/BuildWorkResult.java b/subprojects/core-api/src/main/java/org/gradle/api/flow/BuildWorkResult.java
new file mode 100644
index 0000000..98d922c
--- /dev/null
+++ b/subprojects/core-api/src/main/java/org/gradle/api/flow/BuildWorkResult.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 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.api.flow;
+
+import org.gradle.api.Incubating;
+
+import java.util.Optional;
+
+/**
+ * Summary result of the execution of the work scheduled for the current build.
+ *
+ * @see FlowProviders#getBuildWorkResult()
+ * @since 8.1
+ */
+@Incubating
+public interface BuildWorkResult {
+
+    /**
+     * A summary of the configuration time and execution time failure(s) that occurred as Gradle tried to execute the
+     * scheduled work.
+     *
+     * @return {@link Optional#empty() empty} when no failures occur.
+     */
+    Optional<Throwable> getFailure();
+}
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/flow/FlowAction.java b/subprojects/core-api/src/main/java/org/gradle/api/flow/FlowAction.java
index 14cd61f..1aaa292 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/flow/FlowAction.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/flow/FlowAction.java
@@ -46,7 +46,8 @@
  * class FFPlay implements FlowAction&lt;FFPlay.Parameters&gt; {
  *
  *     interface Parameters extends FlowParameters {
- *         RegularFileProperty getMediaFile();
+ *         {@literal @}Input
+ *         Property&lt;File&gt; getMediaFile();
  *     }
  *
  *     private final ExecOperations execOperations;
@@ -61,7 +62,7 @@
  *         execOperations.exec(spec -&gt; {
  *             spec.commandLine(
  *                 "ffplay", "-nodisp", "-autoexit", "-hide_banner", "-loglevel", "quiet",
- *                 parameters.getMediaFile().get().getAsFile().getAbsolutePath()
+ *                 parameters.getMediaFile().get().getAbsolutePath()
  *             );
  *             spec.setIgnoreExitValue(true);
  *         });
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/flow/FlowProviders.java b/subprojects/core-api/src/main/java/org/gradle/api/flow/FlowProviders.java
index d7e4aa5..e5a14e5 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/flow/FlowProviders.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/flow/FlowProviders.java
@@ -16,7 +16,6 @@
 
 package org.gradle.api.flow;
 
-import org.gradle.StartParameter;
 import org.gradle.api.Incubating;
 import org.gradle.api.provider.Provider;
 import org.gradle.internal.service.scopes.Scopes;
@@ -33,15 +32,14 @@
 public interface FlowProviders {
 
     /**
-     * Returns a {@link Provider provider} for the summary of the result of executing the
-     * {@link StartParameter#getTaskRequests() requested tasks}.
+     * Returns a {@link Provider provider} for the summary result of the execution of the work scheduled
+     * for the current build.
      * <p>
-     * The returned {@link Provider#get() provider's value} becomes available after all requested tasks
-     * have completed - successfully or otherwise - or after a configuration phase failure prevents the execution
-     * of the requested tasks.
+     * The returned {@link Provider#get() provider's value} becomes available after the scheduled work
+     * has completed - successfully or otherwise - or after a configuration phase failure prevents execution.
      * </p>
      * <p>
-     * <b>IMPORTANT:</b> trying to access the provider's value before the requested tasks have finished will
+     * <b>IMPORTANT:</b> trying to access the provider's value before the scheduled work has finished will
      * result in an error.
      * </p>
      *
@@ -65,7 +63,7 @@ public interface FlowProviders {
      *         final File soundsDir = new File(target.getSettingsDir(), "sounds");
      *         flowScope.always(FFPlay.class, spec -&gt;
      *             spec.getParameters().getMediaFile().fileProvider(
-     *                 flowProviders.getRequestedTasksResult().map(result -&gt;
+     *                 flowProviders.getBuildWorkResult().map(result -&gt;
      *                     new File(
      *                         soundsDir,
      *                         result.getFailure().isPresent() ? "sad-trombone.mp3" : "tada.mp3"
@@ -80,5 +78,5 @@ public interface FlowProviders {
      * @see FlowAction
      * @see FlowScope
      */
-    Provider<RequestedTasksResult> getRequestedTasksResult();
+    Provider<BuildWorkResult> getBuildWorkResult();
 }
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/flow/RequestedTasksResult.java b/subprojects/core-api/src/main/java/org/gradle/api/flow/RequestedTasksResult.java
deleted file mode 100644
index 7b2af11..0000000
--- a/subprojects/core-api/src/main/java/org/gradle/api/flow/RequestedTasksResult.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2022 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.api.flow;
-
-import org.gradle.StartParameter;
-import org.gradle.api.Incubating;
-
-import java.util.Optional;
-
-/**
- * Summary of the result of executing the
- * {@link StartParameter#getTaskRequests() requested tasks}.
- *
- * @see FlowProviders#getRequestedTasksResult()
- * @since 8.1
- */
-@Incubating
-public interface RequestedTasksResult {
-
-    /**
-     * A summary of the failure(s) that occurred as Gradle tried to execute the requested tasks.
-     *
-     * @return {@link Optional#empty() empty} when no failures occur.
-     */
-    Optional<Throwable> getFailure();
-}
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformListener.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformListener.java
deleted file mode 100644
index 3abe9a4..0000000
--- a/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformListener.java
+++ /dev/null
@@ -1,35 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.Describable;
-import org.gradle.internal.service.scopes.EventScope;
-import org.gradle.internal.service.scopes.Scopes;
-
-@EventScope(Scopes.Build.class)
-public interface ArtifactTransformListener {
-
-    /**
-     * This method is called immediately before a transformer is invoked.
-     */
-    void beforeTransformerInvocation(Describable transformer, Describable subject);
-
-    /**
-     * This method is call immediately after a transformer has been invoked.
-     */
-    void afterTransformerInvocation(Describable transformer, Describable subject);
-}
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionListener.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionListener.java
new file mode 100644
index 0000000..c3e146c
--- /dev/null
+++ b/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionListener.java
@@ -0,0 +1,35 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.Describable;
+import org.gradle.internal.service.scopes.EventScope;
+import org.gradle.internal.service.scopes.Scopes;
+
+@EventScope(Scopes.Build.class)
+public interface TransformExecutionListener {
+
+    /**
+     * This method is called immediately before a transform is executed.
+     */
+    void beforeTransformExecution(Describable transform, Describable subject);
+
+    /**
+     * This method is call immediately after a transform has been executed.
+     */
+    void afterTransformExecution(Describable transform, Describable subject);
+}
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformNodeDependency.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformNodeDependency.java
new file mode 100644
index 0000000..6c60a14
--- /dev/null
+++ b/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformNodeDependency.java
@@ -0,0 +1,23 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+/**
+ * Marker interface to signal a dependency on an artifact transform.
+ */
+public interface TransformNodeDependency {
+}
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationDependency.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationDependency.java
deleted file mode 100644
index 0a7a73e..0000000
--- a/subprojects/core-api/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationDependency.java
+++ /dev/null
@@ -1,23 +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.api.internal.artifacts.transform;
-
-/**
- * Empty interface for now to signal a dependency on an artifact transformation.
- */
-public interface TransformationDependency {
-}
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/lambdas/SerializableLambdas.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/lambdas/SerializableLambdas.java
index 8475482..b69e513 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/internal/lambdas/SerializableLambdas.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/internal/lambdas/SerializableLambdas.java
@@ -23,6 +23,7 @@
 
 import java.io.Serializable;
 import java.util.concurrent.Callable;
+import java.util.function.BiFunction;
 
 /**
  * Provides a mechanism for creating Java lambdas that can be stored to the configuration cache.
@@ -51,6 +52,10 @@ public static <T> Callable<T> callable(SerializableCallable<T> callable) {
         return callable;
     }
 
+    public static <T, U, R> BiFunction<T, U, R> bifunction(SerializableBiFunction<T, U, R> f) {
+        return f;
+    }
+
     /**
      * A {@link Serializable} version of {@link Spec}.
      */
@@ -76,6 +81,12 @@ public interface SerializableTransformer<OUT, IN> extends Transformer<OUT, IN>,
     }
 
     /**
+     * A {@link Serializable} version of {@link BiFunction}.
+     */
+    public interface SerializableBiFunction<T, U, R> extends BiFunction<T, U, R>, Serializable {
+    }
+
+    /**
      * A {@link Serializable} version of {@link Callable}.
      */
     public interface SerializableCallable<T> extends Callable<T>, Serializable {
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/lambdas/package-info.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/lambdas/package-info.java
new file mode 100644
index 0000000..5050f0d
--- /dev/null
+++ b/subprojects/core-api/src/main/java/org/gradle/api/internal/lambdas/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 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.
+ */
+/**
+ * Utility functions for dealing with lambdas in a configuration-cache compatible way.
+ */
+@NonNullApi
+package org.gradle.api.internal.lambdas;
+
+import org.gradle.api.NonNullApi;
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/internal/properties/GradleProperties.java b/subprojects/core-api/src/main/java/org/gradle/api/internal/properties/GradleProperties.java
index 8c74f88..f69df04 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/internal/properties/GradleProperties.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/internal/properties/GradleProperties.java
@@ -34,4 +34,6 @@ public interface GradleProperties {
      * @param properties read-only properties to be merged with the set of loaded properties.
      */
     Map<String, Object> mergeProperties(Map<String, Object> properties);
+
+    Map<String, Object> getProperties();
 }
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/invocation/Gradle.java b/subprojects/core-api/src/main/java/org/gradle/api/invocation/Gradle.java
index 6a15cc7..bf4adfc 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/invocation/Gradle.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/invocation/Gradle.java
@@ -25,6 +25,7 @@
 import org.gradle.api.ProjectEvaluationListener;
 import org.gradle.api.UnknownDomainObjectException;
 import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.flow.FlowProviders;
 import org.gradle.api.initialization.IncludedBuild;
 import org.gradle.api.initialization.Settings;
 import org.gradle.api.plugins.ExtensionAware;
@@ -272,6 +273,7 @@ public interface Gradle extends PluginAware, ExtensionAware {
      *
      * @param closure The closure to execute.
      * @deprecated This method is not supported when configuration caching is enabled.
+     * @see FlowProviders#getBuildWorkResult()
      */
     @Deprecated
     void buildFinished(Closure closure);
@@ -284,6 +286,7 @@ public interface Gradle extends PluginAware, ExtensionAware {
      * @param action The action to execute.
      * @since 3.4
      * @deprecated This method is not supported when configuration caching is enabled.
+     * @see FlowProviders#getBuildWorkResult()
      */
     @Deprecated
     void buildFinished(Action<? super BuildResult> action);
@@ -301,10 +304,8 @@ public interface Gradle extends PluginAware, ExtensionAware {
      * Adds the given listener to this build. The listener may implement any of the given listener interfaces:
      *
      * <ul>
-     * <li>{@link org.gradle.BuildListener}
      * <li>{@link org.gradle.api.execution.TaskExecutionGraphListener}
      * <li>{@link org.gradle.api.ProjectEvaluationListener}
-     * <li>{@link org.gradle.api.logging.StandardOutputListener}
      * <li>{@link org.gradle.api.artifacts.DependencyResolutionListener}
      * </ul>
      *
@@ -312,6 +313,7 @@ public interface Gradle extends PluginAware, ExtensionAware {
      * Their usage is deprecated and adding a listener of these types become an error in a future Gradle version:</p>
      *
      * <ul>
+     * <li>{@link org.gradle.BuildListener}
      * <li>{@link org.gradle.api.execution.TaskExecutionListener}
      * <li>{@link org.gradle.api.execution.TaskActionListener}
      * <li>{@link org.gradle.api.tasks.testing.TestListener}
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/package-info.java b/subprojects/core-api/src/main/java/org/gradle/api/package-info.java
index 4a4670f..072e4ce 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/package-info.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/package-info.java
@@ -15,13 +15,8 @@
  */
 
 /**
- * <p><b>Start Here:</b> Gradle's {@link org.gradle.api.Project} API, which is available from your build files. The
- * API used from your build files is made up of 2 main interfaces:</p>
- *
- * <ul>
- * <li>{@link org.gradle.api.Project}</li>
- * <li>{@link org.gradle.api.Task}</li>
- * </ul>
+ * <p>Gradle's build language API, which is available from your build files.
+ * Location of the main interfaces involved in build scripts.</p>
  */
 @org.gradle.api.NonNullApi
 package org.gradle.api;
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/plugins/Convention.java b/subprojects/core-api/src/main/java/org/gradle/api/plugins/Convention.java
index 69e2439..396bb05 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/plugins/Convention.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/plugins/Convention.java
@@ -25,14 +25,21 @@
  * Convention}, and the properties and methods of the convention object become available as properties and methods of
  * the object which the convention is associated to. A convention object is simply a POJO or POGO. Usually, a {@code
  * Convention} is used by plugins to extend a {@link org.gradle.api.Project} or a {@link org.gradle.api.Task}.</p>
+ *
+ * @deprecated Use extensions instead. This is scheduled for removal in Gradle 9.
+ * @see org.gradle.api.plugins.ExtensionAware
  */
+@Deprecated
 public interface Convention extends ExtensionContainer {
 
     /**
      * Returns the plugin convention objects contained in this convention.
      *
      * @return The plugins. Returns an empty map when this convention does not contain any convention objects.
+     * @deprecated Use extensions instead. This is scheduled for removal in Gradle 9.
+     * @see org.gradle.api.plugins.ExtensionAware
      */
+    @Deprecated
     Map<String, Object> getPlugins();
 
     /**
@@ -42,7 +49,10 @@ public interface Convention extends ExtensionContainer {
      * @return The object. Never returns null.
      * @throws IllegalStateException When there is no such object contained in this convention, or when there are
      * multiple such objects.
+     * @deprecated Use extensions instead. This is scheduled for removal in Gradle 9.
+     * @see org.gradle.api.plugins.ExtensionAware
      */
+    @Deprecated
     <T> T getPlugin(Class<T> type) throws IllegalStateException;
 
     /**
@@ -51,8 +61,11 @@ public interface Convention extends ExtensionContainer {
      * @param type The convention object type.
      * @return The object. Returns null if there is no such object.
      * @throws IllegalStateException When there are multiple matching objects.
+     * @deprecated Use extensions instead. This is scheduled for removal in Gradle 9.
+     * @see org.gradle.api.plugins.ExtensionAware
      */
     @Nullable
+    @Deprecated
     <T> T findPlugin(Class<T> type) throws IllegalStateException;
 
     /**
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/provider/ProviderFactory.java b/subprojects/core-api/src/main/java/org/gradle/api/provider/ProviderFactory.java
index b08cdc1..9d14e76 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/provider/ProviderFactory.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/provider/ProviderFactory.java
@@ -254,7 +254,6 @@ public interface ProviderFactory {
      * @return the provider, never returns null
      * @since 6.1
      */
-    @Incubating
     <T, P extends ValueSourceParameters>
     Provider<T> of(
         Class<? extends ValueSource<T, P>> valueSourceType,
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSource.java b/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSource.java
index a62a428..226082c 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSource.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSource.java
@@ -17,7 +17,6 @@
 package org.gradle.api.provider;
 
 import org.gradle.api.Action;
-import org.gradle.api.Incubating;
 import org.gradle.api.file.RegularFile;
 
 import javax.annotation.Nullable;
@@ -42,7 +41,7 @@
  * To integrate a new type of value source, create an abstract subclass of this interface
  * and use {@link ProviderFactory#of(Class, Action)} to get a provider to a configured source.
  * The returned {@link org.gradle.api.provider.Provider} can be passed to tasks or queried
- * by build logic during the configuration phase, in which case the source would be automatically
+ * by build logic during the configuration phase. In the latter case, the source would be automatically
  * considered as an input to the work graph cache.
  * </p>
  * <p>
@@ -60,7 +59,7 @@
  * Currently, only a small subset of services is supported:
  * <ul>
  *     <li>{@link org.gradle.process.ExecOperations} provides means to execute external processes.
- *     It is possible to use this service even during the configuration time. However, as the
+ *     It is possible to use this service even at configuration time. However, as the
  *     returned value is used to check the configuration cache, the {@link #obtain()} method will
  *     be called during each build. Calling slow commands here will slow things down.</li>
  * </ul>
@@ -81,6 +80,8 @@
  * For example, if the {@link #obtain()} method calls {@code System.getenv("FOO")} then changes to
  * the {@code FOO} environment variable only invalidate the cache if the value returned by the
  * {@code obtain()} method itself changes. The same applies to reading files or system properties.
+ * Starting an external process with a standard API (for example, {@code java.lang.ProcessBuilder}) is
+ * also allowed.
  * </p>
  * @param <T> The type of value obtained from this source.
  * @param <P> The source specific parameter type.
@@ -91,7 +92,6 @@
  * @see <a href="https://docs.gradle.org/current/userguide/configuration_cache.html">Configuration Cache</a>
  * @since 6.1
  */
-@Incubating
 public interface ValueSource<T, P extends ValueSourceParameters> {
 
     /**
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSourceParameters.java b/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSourceParameters.java
index 15dd31b..d396cf3 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSourceParameters.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSourceParameters.java
@@ -16,8 +16,6 @@
 
 package org.gradle.api.provider;
 
-import org.gradle.api.Incubating;
-
 /**
  * Marker interface for parameter objects to {@link ValueSource}s.
  *
@@ -32,14 +30,12 @@
  *
  * @since 6.1
  */
-@Incubating
 public interface ValueSourceParameters {
     /**
      * Used for sources without parameters.
      *
      * @since 6.1
      */
-    @Incubating
     final class None implements ValueSourceParameters {
         private None() {
         }
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSourceSpec.java b/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSourceSpec.java
index 29b488d..8ec4c82 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSourceSpec.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/provider/ValueSourceSpec.java
@@ -17,7 +17,6 @@
 package org.gradle.api.provider;
 
 import org.gradle.api.Action;
-import org.gradle.api.Incubating;
 
 
 /**
@@ -27,7 +26,6 @@
  * @param <P> The value source specific parameter type.
  * @since 6.1
  */
-@Incubating
 public interface ValueSourceSpec<P extends ValueSourceParameters> {
 
     /**
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/services/ServiceReference.java b/subprojects/core-api/src/main/java/org/gradle/api/services/ServiceReference.java
index 4515d64..aa6e3e3 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/services/ServiceReference.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/services/ServiceReference.java
@@ -17,6 +17,8 @@
 package org.gradle.api.services;
 
 import org.gradle.api.Incubating;
+import org.gradle.api.provider.Property;
+import org.gradle.api.tasks.Optional;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
@@ -25,7 +27,18 @@
 import java.lang.annotation.Target;
 
 /**
- * <p>Marks a task property as being a holder to a {@link BuildService}</p>.
+ * <p>Marks a task property as being a holder to a {@link BuildService}.</p>
+ *
+ * <p>
+ * When you annotate a shared build service property with this annotation,
+ * there is no need to explicitly declare the association between the task and the service;
+ * also, if you provide a service name to the annotation, and a shared build service is
+ * registered with that name, it will be automatically assigned to the property when the
+ * task is created.
+ * </p>
+ * <p>
+ * It is an error to apply this annotation to a property whose type is not a subtype of {@link BuildService}.
+ * </p>
  *
  * @since 8.0
  */
@@ -35,7 +48,23 @@
 @Incubating
 public @interface ServiceReference {
     /**
-     * The optional name of the service which the annotated element references.
+     * <p>The optional name of the service which the annotated element references.</p>
+     *
+     * <p>
+     * In case a service name is provided, if a shared build service is registered with that name,
+     * a provider to the service will be automatically assigned to the property.
+     * </p>
+     * <p>
+     * If a shared build service with the specified name is not found, and no value or convention
+     * is explicitly set on the property:
+     * </p>
+     * <ul>
+     * <li>if the property is optional, an exception will only occur if an attempt is made to obtain the value of the property (see {@link Property#get()});</li>
+     * <li>if the property is mandatory, before the task starts executing, a validation error will be issued.</li>
+     * </ul>
+     *
+     * @see Optional
+     * @see Property#get()
      */
     String value() default "";
 }
diff --git a/subprojects/core-api/src/main/java/org/gradle/api/toolchain/management/package-info.java b/subprojects/core-api/src/main/java/org/gradle/api/toolchain/management/package-info.java
index 951be44..de427c3 100644
--- a/subprojects/core-api/src/main/java/org/gradle/api/toolchain/management/package-info.java
+++ b/subprojects/core-api/src/main/java/org/gradle/api/toolchain/management/package-info.java
@@ -19,4 +19,4 @@
  * @since 7.6
  */
 @org.gradle.api.NonNullApi
-package org.gradle.api.toolchain.management;
\ No newline at end of file
+package org.gradle.api.toolchain.management;
diff --git a/subprojects/core-api/src/main/java/org/gradle/internal/DeprecatedInGradleScope.java b/subprojects/core-api/src/main/java/org/gradle/internal/DeprecatedInGradleScope.java
new file mode 100644
index 0000000..0db1693
--- /dev/null
+++ b/subprojects/core-api/src/main/java/org/gradle/internal/DeprecatedInGradleScope.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Listener interfaces annotated with this should no longer be used as arguments for {@link org.gradle.api.invocation.Gradle#addListener(Object)} or
+ * {@link org.gradle.api.invocation.Gradle#useLogger(Object)}. Such listeners do not work correctly when the configuration cache is enabled.
+ *
+ * @since 8.1
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+public @interface DeprecatedInGradleScope {
+}
diff --git a/subprojects/core-api/src/main/java/org/gradle/internal/deprecation/DeprecatableConfiguration.java b/subprojects/core-api/src/main/java/org/gradle/internal/deprecation/DeprecatableConfiguration.java
index 4bc8e9c..21b2c6f 100644
--- a/subprojects/core-api/src/main/java/org/gradle/internal/deprecation/DeprecatableConfiguration.java
+++ b/subprojects/core-api/src/main/java/org/gradle/internal/deprecation/DeprecatableConfiguration.java
@@ -18,78 +18,107 @@
 
 import org.gradle.api.artifacts.Configuration;
 
-import javax.annotation.Nullable;
 import java.util.List;
-import java.util.function.Function;
 
+/**
+ * This internal interface extends {@link Configuration} adding functionality to allow plugins to deprecate
+ * configurations.
+ * <p>
+ * This interface also contains a few methods unrelated to deprecation, but which need to be available to
+ * other gradle subprojects.  These methods include:
+ * <ul>
+ *     <li>{@link #preventUsageMutation()}</li>
+ *     <li>{@link #setCanBeDeclared(boolean)}</li>
+ *     <li>{@link #isCanBeDeclared()}</li>
+ * </ul>
+ * These methods would be better suited for the base {@link Configuration} interface, or the (inaccessible from this project)
+ * {@link org.gradle.api.internal.artifacts.configurations.ConfigurationInternal ConfigurationInternal}
+ * interface, but we want to hide them from the public API.
+ */
+@SuppressWarnings("JavadocReference")
 public interface DeprecatableConfiguration extends Configuration {
 
     /**
-     * @return configurations that should be used to declare dependencies instead of this configuration.
-     *         Returns 'null' if this configuration is not deprecated for declaration.
+     * Return the names of configurations that should be used to declare dependencies instead of this configuration.
+     *
+     * <p>This property is only relevant if this configuration deprecated for declaration.</p>
      */
-    @Nullable
     List<String> getDeclarationAlternatives();
 
     /**
-     * @return deprecation message builder to be used for nagging when this configuration is consumed.
-     *         Returns 'null' if this configuration is not deprecated for consumption.
+     * Return the names of configurations that should be used to consume a component instead of consuming this configuration.
+     *
+     * <p>This property is only relevant if this configuration deprecated for resolution.</p>
      */
-    @Nullable
-    DeprecationMessageBuilder.WithDocumentation getConsumptionDeprecation();
-
-    /**
-     * @return configurations that should be used to consume a component instead of consuming this configuration.
-     *         Returns 'null' if this configuration is not deprecated for resolution.
-     */
-    @Nullable
     List<String> getResolutionAlternatives();
 
     /**
-     * Allows plugins to deprecate the consumability property (canBeConsumed() == true) of a configuration that will be changed in the next major Gradle version.
+     * Sets suggested configurations which can be used for dependency declaration instead of this configuration.
      *
-     * @param deprecation deprecation message builder to use for nagging upon consumption of this configuration
-     * @return this configuration
+     * <p>This property is only relevant if this configuration deprecated for declaration.</p>
      */
-    DeprecatableConfiguration deprecateForConsumption(Function<DeprecationMessageBuilder.DeprecateConfiguration, DeprecationMessageBuilder.WithDocumentation> deprecation);
+    void addDeclarationAlternatives(String... alternativesForDeclaring);
 
     /**
-     * Convinience method for {@link #deprecateForConsumption(Function)} which uses the default messaging behavior.
+     * Sets suggested configurations which can be used for dependency resolution instead of this configuration.
      *
-     * @return this configuration
-    */
-    default DeprecatableConfiguration deprecateForConsumption() {
-        return deprecateForConsumption(depSpec -> DeprecationLogger.deprecateConfiguration(getName())
+     * <p>This property is only relevant if this configuration deprecated for resolution.</p>
+     */
+    void addResolutionAlternatives(String... alternativesForResolving);
+
+    /**
+     * If this configuration is deprecated for consumption, emit a deprecation warning.
+     */
+    default void maybeEmitConsumptionDeprecation() {
+        if (isDeprecatedForConsumption()) {
+            DeprecationLogger.deprecateConfiguration(getName())
                 .forConsumption()
                 .willBecomeAnErrorInGradle9()
-                .withUserManual("dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations"));
+                .withUserManual("dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+                .nagUser();
+        }
     }
 
     /**
-     * Allows plugins to deprecate the resolvability property (canBeResolved() == true) of a configuration that will be changed in the next major Gradle version.
-     *
-     * @param alternativesForResolving alternative configurations that can be used for dependency resolution
-     * @return this configuration
+     * If this configuration is deprecated for declaration, emit a deprecation warning.
      */
-    DeprecatableConfiguration deprecateForResolution(String... alternativesForResolving);
+    default void maybeEmitDeclarationDeprecation() {
+        if (isDeprecatedForDeclarationAgainst()) {
+            DeprecationLogger.deprecateConfiguration(getName())
+                .forDependencyDeclaration()
+                .replaceWith(getDeclarationAlternatives())
+                .willBecomeAnErrorInGradle9()
+                .withUpgradeGuideSection(5, "dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+                .nagUser();
+        }
+    }
 
     /**
-     * Allows plugins to deprecate a configuration that will be removed in the next major Gradle version.
-     *
-     * @param alternativesForDeclaring alternative configurations that can be used to declare dependencies
-     * @return this configuration
+     * If this configuration is deprecated for resolution, emit a deprecation warning.
      */
-    DeprecatableConfiguration deprecateForDeclarationAgainst(String... alternativesForDeclaring);
+    default void maybeEmitResolutionDeprecation() {
+        if (isDeprecatedForResolution()) {
+            DeprecationLogger.deprecateConfiguration(getName())
+                .forResolution()
+                .replaceWith(getResolutionAlternatives())
+                .willBecomeAnErrorInGradle9()
+                .withUpgradeGuideSection(5, "dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+                .nagUser();
+        }
+    }
 
     boolean isDeprecatedForConsumption();
     boolean isDeprecatedForResolution();
     boolean isDeprecatedForDeclarationAgainst();
 
     default boolean canSafelyBeResolved() {
-        if (!isCanBeResolved()) {
-            return false;
-        }
-        List<String> resolutionAlternatives = getResolutionAlternatives();
-        return resolutionAlternatives == null;
+        return isCanBeResolved() && !isDeprecatedForResolution();
     }
+
+    /**
+     * Prevents any calls to methods that change this configuration's allowed usage (e.g. {@link #setCanBeConsumed(boolean)},
+     * {@link #setCanBeResolved(boolean)}, {@link #setCanBeDeclared(boolean)}) from succeeding; and causes them
+     * to throw an exception.
+     */
+    void preventUsageMutation();
 }
diff --git a/subprojects/core-api/src/main/java/org/gradle/internal/installation/CurrentGradleInstallationLocator.java b/subprojects/core-api/src/main/java/org/gradle/internal/installation/CurrentGradleInstallationLocator.java
index 88d6b0b..c349d4e 100644
--- a/subprojects/core-api/src/main/java/org/gradle/internal/installation/CurrentGradleInstallationLocator.java
+++ b/subprojects/core-api/src/main/java/org/gradle/internal/installation/CurrentGradleInstallationLocator.java
@@ -89,4 +89,4 @@ private static File determineDistRootDir(File codeSource) {
         return null;
     }
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/core-api/src/main/java/org/gradle/process/JavaExecSpec.java b/subprojects/core-api/src/main/java/org/gradle/process/JavaExecSpec.java
index f5219d6..392f54b 100644
--- a/subprojects/core-api/src/main/java/org/gradle/process/JavaExecSpec.java
+++ b/subprojects/core-api/src/main/java/org/gradle/process/JavaExecSpec.java
@@ -15,12 +15,15 @@
  */
 package org.gradle.process;
 
+import org.gradle.api.Incubating;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.jvm.ModularitySpec;
 import org.gradle.api.model.ReplacedBy;
+import org.gradle.api.provider.ListProperty;
 import org.gradle.api.provider.Property;
 import org.gradle.api.tasks.Classpath;
 import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Internal;
 import org.gradle.api.tasks.Nested;
 import org.gradle.api.tasks.Optional;
 import org.gradle.internal.deprecation.DeprecationLogger;
@@ -34,6 +37,18 @@
 public interface JavaExecSpec extends JavaForkOptions, BaseExecSpec {
 
     /**
+     * Extra JVM arguments to be to use to launch the JVM for the process.
+     *
+     * Must be used to set a convention for JVM arguments.
+     *
+     * @since 8.1
+     */
+    @Incubating
+    @Optional
+    @Internal
+    ListProperty<String> getJvmArguments();
+
+    /**
      * The name of the main module to be executed if the application should run as a Java module.
      *
      * @since 6.4
diff --git a/subprojects/core-api/src/main/java/org/gradle/testfixtures/package-info.java b/subprojects/core-api/src/main/java/org/gradle/testfixtures/package-info.java
index 70071c2..06c5390 100644
--- a/subprojects/core-api/src/main/java/org/gradle/testfixtures/package-info.java
+++ b/subprojects/core-api/src/main/java/org/gradle/testfixtures/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Classes and interfaces for testing custom task and plugin implementations.
  */
-package org.gradle.testfixtures;
\ No newline at end of file
+package org.gradle.testfixtures;
diff --git a/subprojects/core-api/src/main/java/org/gradle/util/internal/SupportedEncryptionAlgorithm.java b/subprojects/core-api/src/main/java/org/gradle/util/internal/SupportedEncryptionAlgorithm.java
new file mode 100644
index 0000000..61ecd8f
--- /dev/null
+++ b/subprojects/core-api/src/main/java/org/gradle/util/internal/SupportedEncryptionAlgorithm.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2023 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.util.internal;
+
+import javax.annotation.Nonnull;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Arrays;
+
+/**
+ * Encryption algorithms required/used in Gradle.
+ */
+public enum SupportedEncryptionAlgorithm implements EncryptionAlgorithm {
+    AES_CBC_PADDING("AES/CBC/PKCS5PADDING", "AES", 16),
+    AES_ECB_PADDING("AES/ECB/PKCS5PADDING", "AES", 0);
+    private final String transformation;
+    private final int initVectorLength;
+    private final String algorithm;
+
+    SupportedEncryptionAlgorithm(String transformation, String algorithm, int initVectorLength) {
+        this.transformation = transformation;
+        this.algorithm = algorithm;
+        this.initVectorLength = initVectorLength;
+    }
+    private AlgorithmParameterSpec getDecryptionParameter(IVLoader ivLoader) throws IOException {
+        assert initVectorLength > 0;
+        byte[] initVector = new byte[initVectorLength];
+        ivLoader.load(initVector);
+        return createParameter(initVector);
+    }
+
+    private AlgorithmParameterSpec getEncryptionParameter(byte[] initVector, IVCollector ivCollector) throws IOException {
+        assert initVector != null;
+        assert initVector.length == initVectorLength;
+        ivCollector.collect(initVector);
+        return createParameter(initVector);
+    }
+
+    @Override
+    public String getTransformation() {
+        return transformation;
+    }
+
+    @Nonnull
+    private static AlgorithmParameterSpec createParameter(@Nonnull byte[] initVector) {
+        return new IvParameterSpec(initVector);
+    }
+
+    private Cipher encryptingCipher(SecretKey key, IVCollector collector) throws EncryptionException {
+        try {
+            Cipher newCipher = Cipher.getInstance(transformation);
+            newCipher.init(Cipher.ENCRYPT_MODE, key);
+            if (initVectorLength > 0) {
+                assert collector != null;
+                byte[] iv = newCipher.getIV();
+                assert iv != null;
+                collector.collect(iv);
+            }
+            return newCipher;
+        } catch (IOException|GeneralSecurityException e) {
+            throw new EncryptionException(e);
+        }
+    }
+
+    private Cipher decryptingCipher(SecretKey key, IVLoader loader) throws EncryptionException {
+        try {
+            Cipher newCipher = Cipher.getInstance(transformation);
+            if (initVectorLength > 0) {
+                assert loader != null;
+                newCipher.init(Cipher.DECRYPT_MODE, key, getDecryptionParameter(loader));
+            } else {
+                newCipher.init(Cipher.DECRYPT_MODE, key);
+            }
+            return newCipher;
+        } catch (GeneralSecurityException|IOException e) {
+            throw new EncryptionException(e);
+        }
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    @Override
+    public Session newSession(SecretKey key) {
+        return new Session() {
+            @Override
+            public Cipher decryptingCipher(IVLoader loader) {
+                return SupportedEncryptionAlgorithm.this.decryptingCipher(key, loader);
+            }
+
+            @Override
+            public Cipher encryptingCipher(IVCollector collector) {
+                return SupportedEncryptionAlgorithm.this.encryptingCipher(key, collector);
+            }
+
+            @Override
+            public EncryptionAlgorithm getAlgorithm() {
+                return SupportedEncryptionAlgorithm.this;
+            }
+
+            @Override
+            public SecretKey getKey() {
+                return key;
+            }
+        };
+    }
+
+    @Nonnull
+    public static EncryptionAlgorithm byTransformation(String transformation) {
+        return Arrays.stream(values())
+            .filter(it -> it.transformation.equals(transformation))
+            .findFirst()
+            .map(it -> (EncryptionAlgorithm) it)
+            .orElse(SupportedEncryptionAlgorithm.NONE);
+    }
+}
diff --git a/subprojects/core-api/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy b/subprojects/core-api/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy
index 3112ed8..532a574 100644
--- a/subprojects/core-api/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy
+++ b/subprojects/core-api/src/test/groovy/org/gradle/initialization/BuildLayoutParametersTest.groovy
@@ -19,9 +19,9 @@
 import org.gradle.StartParameter
 import org.gradle.internal.SystemProperties
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Issue
 import spock.lang.Specification
@@ -33,7 +33,7 @@
     @Rule SetSystemProperties props = new SetSystemProperties()
     @Rule TestNameTestDirectoryProvider temp = new TestNameTestDirectoryProvider(getClass())
 
-    @Requires(TestPrecondition.NOT_EC2_AGENT)
+    @Requires(UnitTestPreconditions.NotEC2Agent)
     @Issue('https://github.com/gradle/gradle-private/issues/2876')
     def "has reasonable defaults"() {
         expect:
diff --git a/subprojects/core/build.gradle.kts b/subprojects/core/build.gradle.kts
index f7ed359..46d3334 100755
--- a/subprojects/core/build.gradle.kts
+++ b/subprojects/core/build.gradle.kts
@@ -44,6 +44,7 @@
     implementation(project(":worker-processes"))
     implementation(project(":normalization-java"))
     implementation(project(":wrapper-shared"))
+    implementation(project(":internal-instrumentation-api"))
 
     implementation(libs.groovy)
     implementation(libs.groovyAnt)
@@ -62,7 +63,7 @@
     implementation(libs.fastutil)
     implementation(libs.guava)
     implementation(libs.inject)
-    implementation(libs.asm)
+    implementation(libs.asmTree)
     implementation(libs.asmCommons)
     implementation(libs.slf4jApi)
     implementation(libs.commonsIo)
@@ -138,6 +139,7 @@
     testFixturesImplementation(libs.guava)
     testFixturesImplementation(libs.ant)
     testFixturesImplementation(libs.groovyAnt)
+    testFixturesImplementation(libs.asm)
 
     testFixturesRuntimeOnly(project(":plugin-use")) {
         because("This is a core extension module (see DynamicModulesClassPathProvider.GRADLE_EXTENSION_MODULES)")
@@ -182,10 +184,17 @@
         because("Some tests utilise the 'java-gradle-plugin' and with that TestKit")
     }
     crossVersionTestDistributionRuntimeOnly(project(":distributions-core"))
+
+    annotationProcessor(project(":internal-instrumentation-processor"))
+    annotationProcessor(platform(project(":distributions-dependencies")))
+
+    testAnnotationProcessor(project(":internal-instrumentation-processor"))
+    testAnnotationProcessor(platform(project(":distributions-dependencies")))
 }
 
 strictCompile {
     ignoreRawTypes() // raw types used in public API
+    ignoreAnnotationProcessing() // Without this, javac will complain about unclaimed annotations
 }
 
 packageCycles {
@@ -196,6 +205,11 @@
     setForkEvery(200)
 }
 
+// Disable annotation processing for Groovy, as it is not compatible with Groovy IC
+tasks.withType<GroovyCompile>().configureEach {
+    options.compilerArgs.add("-proc:none")
+}
+
 tasks.compileTestGroovy {
     groovyOptions.fork("memoryInitialSize" to "128M", "memoryMaximumSize" to "1G")
 }
diff --git a/subprojects/core/src/crossVersionTest/groovy/org/gradle/testfixtures/ProjectBuilderCrossVersionIntegrationTest.groovy b/subprojects/core/src/crossVersionTest/groovy/org/gradle/testfixtures/ProjectBuilderCrossVersionIntegrationTest.groovy
index e445a34..5922244 100644
--- a/subprojects/core/src/crossVersionTest/groovy/org/gradle/testfixtures/ProjectBuilderCrossVersionIntegrationTest.groovy
+++ b/subprojects/core/src/crossVersionTest/groovy/org/gradle/testfixtures/ProjectBuilderCrossVersionIntegrationTest.groovy
@@ -21,15 +21,15 @@
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.GradleExecuter
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 import static org.gradle.integtests.fixtures.RepoScriptBlockUtil.mavenCentralRepositoryDefinition
-import static org.gradle.util.TestPrecondition.JDK11_OR_EARLIER
 
 @Issue("GRADLE-3558")
-@Requires(JDK11_OR_EARLIER)
+@Requires(UnitTestPreconditions.Jdk11OrEarlier)
 // Avoid testing version range in favor of better coverage build performance.
 @TargetVersions(['5.0', '6.8'])
 class ProjectBuilderCrossVersionIntegrationTest extends MultiVersionIntegrationSpec {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/JansiEndUserIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/JansiEndUserIntegrationTest.groovy
index d042199..a081091 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/JansiEndUserIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/JansiEndUserIntegrationTest.groovy
@@ -139,7 +139,7 @@
 
     static String annotationProcessorDependency(File repoDir, String processorDependency) {
         """
-            sourceCompatibility = '1.7'
+            java.sourceCompatibility = '1.7'
 
             repositories {
                 maven {
@@ -200,7 +200,7 @@
 
                 group = '$group'
                 version = '$version'
-                sourceCompatibility = '1.7'
+                java.sourceCompatibility = '1.7'
 
                 dependencies {
                     implementation 'org.fusesource.jansi:jansi:$JANSI_VERSION'
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/NameValidationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/NameValidationIntegrationTest.groovy
index 67ef2ed..99764b2 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/NameValidationIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/NameValidationIntegrationTest.groovy
@@ -17,9 +17,8 @@
 package org.gradle
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class NameValidationIntegrationTest extends AbstractIntegrationSpec {
 
@@ -33,7 +32,7 @@
 
         then:
         assertFailureContainsForbiddenCharacterMessage('project name', 'this::is::a::namespace',
-            " Set the 'rootProject.name' or adjust the 'include' statement (see https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]) for more details).")
+            " Set the 'rootProject.name' or adjust the 'include' statement (see ${settingsDslUrl} for more details).")
     }
 
     def "subproject names must not contain forbidden characters"() {
@@ -45,7 +44,11 @@
 
         then:
         assertFailureContainsForbiddenCharacterMessage('project name', 'name|with|pipes',
-            " Set the 'rootProject.name' or adjust the 'include' statement (see https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]) for more details).")
+            " Set the 'rootProject.name' or adjust the 'include' statement (see ${settingsDslUrl} for more details).")
+    }
+
+    private getSettingsDslUrl() {
+        documentationRegistry.getDslRefForProperty("org.gradle.api.initialization.Settings", "include(java.lang.String[])")
     }
 
     def "task names must not contain forbidden characters"() {
@@ -80,7 +83,7 @@
 
         then:
         assertFailureContainsForbiddenStartOrEndCharacterMessage('project name', '.problematic-name',
-            " Set the 'rootProject.name' or adjust the 'include' statement (see https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]) for more details).")
+            " Set the 'rootProject.name' or adjust the 'include' statement (see ${settingsDslUrl} for more details).")
     }
 
     def "project names must not end with ."() {
@@ -93,10 +96,10 @@
 
         then:
         assertFailureContainsForbiddenStartOrEndCharacterMessage('project name', 'problematic-name.',
-            " Set the 'rootProject.name' or adjust the 'include' statement (see https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]) for more details).")
+            " Set the 'rootProject.name' or adjust the 'include' statement (see ${settingsDslUrl} for more details).")
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE) // all forbidden characters are illegal on Windows
+    @Requires(UnitTestPreconditions.UnixDerivative) // all forbidden characters are illegal on Windows
     def "does not fail when project name overrides an invalid folder name"() {
         given:
         def buildFolder = file(".folder: name")
@@ -111,7 +114,7 @@
         output.contains("customName")
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE) // all forbidden characters are illegal on Windows
+    @Requires(UnitTestPreconditions.UnixDerivative) // all forbidden characters are illegal on Windows
     def "does not assign an invalid project name from folder names"() {
         given:
         def buildFolder = file(".folder: name.")
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
index 24895fd..fae79d9 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ConfigurationOnDemandIntegrationTest.groovy
@@ -35,21 +35,6 @@
         file("gradle.properties") << "org.gradle.configureondemand=true"
     }
 
-    def "print deprecation warning when used with the Kotlin DSL"() {
-        buildFile.delete()
-        buildKotlinFile << ""
-
-        when:
-        executer.expectDocumentedDeprecationWarning("Using the configuration on demand feature with the Kotlin DSL. " +
-            "This behavior has been deprecated. This will fail with an error in Gradle 9.0. " +
-            "See https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:limitations for more details.")
-        run("help")
-
-        then:
-        fixture.assertProjectsConfigured(":")
-        output.count("Configuration on demand is an incubating feature") == 1
-    }
-
     @IgnoreIf({ GradleContextualExecuter.isParallel() }) //parallel mode hides incubating message
     def "presents incubating message"() {
         file("gradle.properties") << "org.gradle.configureondemand=false"
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/DynamicMethodLookupIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/DynamicMethodLookupIntegrationTest.groovy
index bdb29d0..b5835a8 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/DynamicMethodLookupIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/DynamicMethodLookupIntegrationTest.groovy
@@ -96,6 +96,19 @@
 }
 """
 
+        executer.expectDocumentedDeprecationWarning(
+            "The Project.getConvention() method has been deprecated. " +
+             "This is scheduled to be removed in Gradle 9.0. " +
+            "Consult the upgrading guide for further information: " +
+            "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+        )
+        executer.expectDocumentedDeprecationWarning(
+            "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: " +
+                "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+        )
+
         expect:
         succeeds()
     }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/DynamicObjectIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/DynamicObjectIntegrationTest.groovy
index 4707b6f..6b51a07 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/DynamicObjectIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/DynamicObjectIntegrationTest.groovy
@@ -66,6 +66,9 @@
                 "}"
         )
 
+        expectProjectConventionDeprecationWarnings()
+        expectConventionTypeDeprecationWarnings(2)
+
         expect:
         succeeds("testTask")
     }
@@ -101,6 +104,9 @@
                 "}"
         )
 
+        expectProjectConventionDeprecationWarnings()
+        expectConventionTypeDeprecationWarnings()
+
         expect:
         succeeds("testTask")
     }
@@ -119,6 +125,9 @@
 }
 '''
 
+        expectProjectConventionDeprecationWarnings()
+        expectConventionTypeDeprecationWarnings(2)
+
         expect:
         succeeds()
     }
@@ -281,6 +290,8 @@
             }
 '''
 
+        expectConventionTypeDeprecationWarnings(7)
+        expectAbstractTaskConventionDeprecationWarnings(3)
 
         expect:
         succeeds("defaultTask")
@@ -452,6 +463,9 @@
             assert prop3(12) == 24
         """
 
+        expectProjectConventionDeprecationWarnings()
+        expectConventionTypeDeprecationWarnings(2)
+
         expect:
         succeeds()
     }
@@ -890,6 +904,9 @@
             }
         """
 
+        expectProjectConventionDeprecationWarnings(4)
+        expectConventionTypeDeprecationWarnings(4)
+
         expect:
         succeeds()
     }
@@ -1047,4 +1064,37 @@
         expect:
         succeeds("run")
     }
+
+    private void expectProjectConventionDeprecationWarnings(int repeated = 1) {
+        repeated.times {
+            executer.expectDocumentedDeprecationWarning(
+                "The Project.getConvention() method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+            )
+        }
+    }
+
+    private void expectConventionTypeDeprecationWarnings(int repeated = 1) {
+        repeated.times {
+            executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+            )
+        }
+    }
+
+    private void expectAbstractTaskConventionDeprecationWarnings(int repeated = 1) {
+        repeated.times {
+            executer.expectDocumentedDeprecationWarning(
+                "The AbstractTask.getConvention() method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+            )
+        }
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/HttpScriptPluginIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/HttpScriptPluginIntegrationSpec.groovy
index b606fdb..fc4300f 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/HttpScriptPluginIntegrationSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/HttpScriptPluginIntegrationSpec.groovy
@@ -19,7 +19,6 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.integtests.fixtures.executer.ExecutionFailure
 import org.gradle.internal.deprecation.Documentation
 import org.gradle.test.fixtures.keystore.TestKeyStore
 import org.gradle.test.fixtures.server.http.HttpServer
@@ -89,12 +88,10 @@
 """
 
         then:
-        ExecutionFailure failure = fails(":help")
-        failure.assertHasCause("Applying script plugins from insecure URIs, without explicit opt-in, is unsupported. " +
-            "The provided URI '${server.uri("/external.gradle")}' uses an insecure protocol (HTTP). " +
-            "Use '${GUtil.toSecureUrl(server.uri("/external.gradle"))}' instead or try 'apply from: resources.text.fromInsecureUri(\"${server.uri("/external.gradle")}\")' to fix this. " +
-            Documentation.dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)").consultDocumentationMessage()
-        )
+        fails(":help")
+            .assertHasCause("Applying script plugins from insecure URIs, without explicit opt-in, is unsupported. The provided URI '${server.uri("/external.gradle")}' uses an insecure protocol (HTTP).")
+            .assertHasResolution("Use '${GUtil.toSecureUrl(server.uri("/external.gradle"))}' instead or try 'apply from: resources.text.fromInsecureUri(\"${server.uri("/external.gradle")}\")'.")
+            .assertHasResolution(Documentation.dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)").consultDocumentationMessage())
     }
 
     def "does not complain when applying script plugin via http using text resource"() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/PluginServiceInjectionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/PluginServiceInjectionIntegrationTest.groovy
index bbf63e4..446f64b 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/PluginServiceInjectionIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/PluginServiceInjectionIntegrationTest.groovy
@@ -16,8 +16,10 @@
 
 package org.gradle.api
 
+import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.file.FileSystemOperations
 import org.gradle.api.file.ProjectLayout
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.provider.ProviderFactory
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
@@ -175,6 +177,8 @@
             ExecutionEngine,
             FileSystemOperations,
             ExecOperations,
+            ConfigurationContainer, // this is a supertype of the RoleBasedConfigurationContainerInternal, we want to ensure it can still be injected without asking for the subtype
+            RoleBasedConfigurationContainerInternal
         ].collect { it.name }
     }
 
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigurationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigurationIntegrationTest.groovy
index cb1af7b..f7220b1 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigurationIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/ProjectConfigurationIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.api
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
 
 class ProjectConfigurationIntegrationTest extends AbstractIntegrationSpec {
 
@@ -100,4 +101,16 @@
         result.assertHasDescription("A problem occurred evaluating root project 'root'.")
         failure.assertHasCause("Cannot run Project.afterEvaluate(Action) when the project is already evaluated.")
     }
+
+    @Issue("https://github.com/gradle/gradle/issues/4823")
+    def "evaluationDependsOn deep project forces evaluation of parents"() {
+        given:
+        settingsFile << "include(':a', ':b:c')"
+        file("a/build.gradle") << "evaluationDependsOn(':b:c')"
+        file("b/build.gradle") << "plugins { id('org.gradle.hello-world') version '0.2' apply false }"
+        file("b/c/build.gradle") << "import org.gradle.plugin.HelloWorldTask"
+
+        expect:
+        succeeds 'help'
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/UndefinedBuildExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/UndefinedBuildExecutionIntegrationTest.groovy
index bcfc3e8..cd54854 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/UndefinedBuildExecutionIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/UndefinedBuildExecutionIntegrationTest.groovy
@@ -20,6 +20,10 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+
 class UndefinedBuildExecutionIntegrationTest extends AbstractIntegrationSpec {
     def setup() {
         useTestDirectoryThatIsNotEmbeddedInAnotherBuild()
@@ -34,8 +38,9 @@
         failure.assertHasDescription("Directory '$testDirectory' does not contain a Gradle build.")
         failure.assertHasResolutions(
             "Run gradle init to create a new Gradle build in this directory.",
-            "Run with --stacktrace option to get the stack trace.",
-            "Run with --info or --debug option to get more log output.") // Don't suggest running with --scan for a missing build
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            GET_HELP) // Don't suggest running with --scan for a missing build
 
         testDirectory.assertIsEmptyDir()
         assertNoProjectCaches(executer.gradleUserHomeDir)
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/file/FileCollectionSymlinkIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/file/FileCollectionSymlinkIntegrationTest.groovy
index 9c782f4..4fe1c51 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/file/FileCollectionSymlinkIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/file/FileCollectionSymlinkIntegrationTest.groovy
@@ -23,15 +23,15 @@
 import org.gradle.internal.reflect.problems.ValidationProblemId
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.internal.reflect.validation.ValidationTestFor
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import static org.gradle.util.internal.TextUtil.escapeString
 import static org.gradle.work.ChangeType.ADDED
 import static org.gradle.work.ChangeType.REMOVED
 
-@Requires(TestPrecondition.SYMLINKS)
+@Requires(UnitTestPreconditions.Symlinks)
 class FileCollectionSymlinkIntegrationTest extends AbstractIntegrationSpec implements ValidationMessageChecker {
     def setup() {
         expectReindentedValidationMessage()
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/model/ObjectFactoryIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/model/ObjectFactoryIntegrationTest.groovy
index 06600f3..295e910 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/model/ObjectFactoryIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/model/ObjectFactoryIntegrationTest.groovy
@@ -836,4 +836,45 @@
         expect:
         succeeds()
     }
+
+    def "plugin can create instance of interface with nested ExtensiblePolymorphicDomainObjectContainer using named managed types"() {
+        given:
+        buildFile """
+            interface Element extends Named {
+            }
+            interface Thing extends Element {
+                Property<Integer> getValue()
+            }
+            interface AnotherThing extends Element {
+                Property<String> getValue()
+            }
+
+            interface Bag {
+                ExtensiblePolymorphicDomainObjectContainer<Element> getThings()
+            }
+
+            def bag = project.objects.newInstance(Bag)
+            bag.things.registerBinding(Thing, Thing)
+            bag.things.registerBinding(AnotherThing, AnotherThing)
+
+            bag.things {
+                a(Thing) {
+                    value = 1
+                }
+                b(AnotherThing) {
+                    value = "foo"
+                }
+            }
+
+            def container = bag.things
+            assert container.size() == 2
+            assert container[0].name == 'a'
+            assert container[0].value.get() == 1
+            assert container[1].name == 'b'
+            assert container[1].value.get() == "foo"
+        """
+
+        expect:
+        succeeds()
+    }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/tasks/TaskCacheabilityReasonIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/tasks/TaskCacheabilityReasonIntegrationTest.groovy
index 8de25f6..0ac2034 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/internal/tasks/TaskCacheabilityReasonIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/internal/tasks/TaskCacheabilityReasonIntegrationTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.BuildOperationsFixture
 import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import spock.lang.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 import javax.annotation.Nullable
 
@@ -62,6 +62,13 @@
             @DisableCachingByDefault(because = 'do-not-cache-by-default reason')
             class NotCacheableByDefaultWithReason extends UnspecifiedCacheabilityTask {}
 
+            @UntrackedTask(because = 'untracked-task reason')
+            class UntrackedTrackWithReason extends UnspecifiedCacheabilityTask {}
+
+            @UntrackedTask(because = 'untracked-task-with-cacheable reason')
+            @CacheableTask
+            class UntrackedTrackWithReasonWithCacheable extends UnspecifiedCacheabilityTask {}
+
             @CacheableTask
             class Cacheable extends UnspecifiedCacheabilityTask {}
 
@@ -157,6 +164,41 @@
         assertCachingDisabledFor NO_OUTPUTS_DECLARED, "No outputs declared"
     }
 
+
+    def "cacheability for a untracked task via API is NOT_ENABLED_FOR_TASK with message"() {
+        buildFile """
+            task untrackedTrackWithReason(type: UnspecifiedCacheabilityTask) {
+                doNotTrackState("Untracked for testing from API")
+            }
+        """
+        when:
+        withBuildCache().run "untrackedTrackWithReason"
+        then:
+        assertCachingDisabledFor NOT_ENABLED_FOR_TASK, "Task is untracked because: Untracked for testing from API"
+    }
+
+
+    def "cacheability for a untracked task is NOT_ENABLED_FOR_TASK with message"() {
+        buildFile """
+            task untrackedTrackWithReason(type: UntrackedTrackWithReason) {}
+        """
+        when:
+        withBuildCache().run "untrackedTrackWithReason"
+        then:
+        assertCachingDisabledFor NOT_ENABLED_FOR_TASK, "Task is untracked because: untracked-task reason"
+    }
+
+    def "cacheability for a untracked task is NOT_ENABLED_FOR_TASK with message when marked cacheable"() {
+        buildFile """
+            task untrackedTrackWithReasonWithCacheable(type: UntrackedTrackWithReasonWithCacheable) {}
+        """
+        when:
+        withBuildCache().run "untrackedTrackWithReasonWithCacheable"
+        then:
+        assertCachingDisabledFor DO_NOT_CACHE_IF_SPEC_SATISFIED, "Task is untracked because: untracked-task-with-cacheable reason"
+    }
+
+
     def "cacheability for a task with no outputs is NOT_ENABLED_FOR_TASK"() {
         buildFile """
             task noOutputs(type: NoOutputs) {}
@@ -344,7 +386,7 @@
     }
 
     // This test only works in embedded mode because of the use of validation test fixtures
-    @Requires({ GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsEmbeddedExecutor)
     def "cacheability for task with disabled optimizations is VALIDATION_FAILURE"() {
         when:
         executer.noDeprecationChecks()
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/resource/TextResourceIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/resource/TextResourceIntegrationTest.groovy
index 683dd8e..3edccfb 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/resource/TextResourceIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/resource/TextResourceIntegrationTest.groovy
@@ -124,14 +124,10 @@
             }
 """
         then:
-        def failure = fails("uriText")
-
-        failure.assertHasCause(
-            "Loading a TextResource from an insecure URI, without explicit opt-in, is unsupported. " +
-                "The provided URI '${server.uri}/myConfig-${uuid}.txt' uses an insecure protocol (HTTP). " +
-                "Switch the URI to '${GUtil.toSecureUrl(server.uri)}/myConfig-${uuid}.txt' or try 'resources.text.fromInsecureUri(\"${server.uri}/myConfig-${uuid}.txt\")' to silence the warning. " +
-                Documentation.dslReference(TextResourceFactory, "fromInsecureUri(java.lang.Object)").consultDocumentationMessage()
-        )
+        fails("uriText")
+            .assertHasCause("Loading a TextResource from an insecure URI, without explicit opt-in, is unsupported. The provided URI '${server.uri}/myConfig-${uuid}.txt' uses an insecure protocol (HTTP).")
+            .assertHasResolution("Switch the URI to '${GUtil.toSecureUrl(server.uri)}/myConfig-${uuid}.txt' or try 'resources.text.fromInsecureUri(\"${server.uri}/myConfig-${uuid}.txt\")' to silence the warning.")
+            .assertHasResolution(Documentation.dslReference(TextResourceFactory, "fromInsecureUri(java.lang.Object)").consultDocumentationMessage())
     }
 
     def "uri backed text resource over https"() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/services/BuildServiceIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/services/BuildServiceIntegrationTest.groovy
index 908a683..2785e77 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/services/BuildServiceIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/services/BuildServiceIntegrationTest.groovy
@@ -21,6 +21,7 @@
 import org.gradle.api.artifacts.transform.TransformParameters
 import org.gradle.api.file.FileSystemOperations
 import org.gradle.api.file.ProjectLayout
+import org.gradle.api.internal.AbstractTask
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.provider.ProviderFactory
 import org.gradle.api.services.internal.BuildServiceProvider
@@ -379,17 +380,23 @@
         customTaskUsingServiceViaProperty("@${ServiceReference.name}")
         buildFile """
             def service1 = gradle.sharedServices.registerIfAbsent("counter1", CountingService) {
-                parameters.initial = 10
+                parameters.initial = 1
                 maxParallelUsages = 1
             }
             def service2 = gradle.sharedServices.registerIfAbsent("counter2", CountingService) {
                 parameters.initial = 10
                 maxParallelUsages = 1
             }
+            def service3 = gradle.sharedServices.registerIfAbsent("counter3", CountingService) {
+                parameters.initial = 100
+                maxParallelUsages = 1
+            }
 
             task unambiguous(type: Consumer) {
                 // explicit assignment avoids ambiguity
-                counter.convention(service1)
+                counter.convention(service2)
+                // explicit usage declaration required to avoid warning
+                usesService(service2)
                 doLast {
                     counter.get()
                 }
@@ -426,6 +433,7 @@
             task named(type: Consumer) {
                 // override service with an explicit assignment
                 counter.set(counterProvider2)
+                usesService(counterProvider2)
             }
         """
         enableStableConfigurationCache()
@@ -1463,6 +1471,50 @@
         }
     }
 
+    def "should not resolve providers when computing shared resources"() {
+        serviceImplementation()
+        buildFile """
+            import ${Inject.name}
+
+            def serviceProvider = gradle.sharedServices.registerIfAbsent("counter", CountingService) {
+                parameters.initial = 10
+            }
+
+            abstract class NestedBean {
+            }
+
+            abstract class Greeter extends DefaultTask {
+                @Inject
+                abstract ObjectFactory getObjects()
+
+                @Nested
+                final Property<NestedBean> unrelated = objects.property(NestedBean).convention(project.providers.provider {
+                    println("Resolving provider")
+                    def trace = new Throwable().getStackTrace()
+                    def getSharedResources = ${AbstractTask.name}.methods.find { it.name == "getSharedResources" }
+                    assert getSharedResources != null
+                    assert !trace.any {
+                        it.className == "${AbstractTask.name}" && it.methodName == getSharedResources.name
+                    }
+                    objects.newInstance(NestedBean)
+                })
+                @Internal
+                final Property<String> subject = project.objects.property(String).value("World")
+            }
+
+            tasks.register('hello', Greeter) {
+                it.usesService(serviceProvider)
+                doLast {
+                    println("Hello, \${subject.get()}")
+                }
+            }
+        """
+        expect:
+        succeeds("hello")
+        outputContains("Hello, World")
+        outputContains("Resolving provider")
+    }
+
     private void enableStableConfigurationCache() {
         settingsFile '''
             enableFeaturePreview 'STABLE_CONFIGURATION_CACHE'
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ArchiveTaskPermissionsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ArchiveTaskPermissionsIntegrationTest.groovy
index 42d9b56..6984a0f 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ArchiveTaskPermissionsIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/ArchiveTaskPermissionsIntegrationTest.groovy
@@ -19,15 +19,15 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.archives.TestReproducibleArchives
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.junit.Assert.assertTrue
 
 @TestReproducibleArchives
 class ArchiveTaskPermissionsIntegrationTest extends AbstractIntegrationSpec {
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "file and directory permissions are preserved when using #taskName task"() {
         given:
         createDir('parent') {
@@ -57,7 +57,7 @@
         "Tar"    | "untarTo"
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "file and directory permissions can be overridden in #taskName task"() {
         given:
         createDir('parent') {
@@ -92,7 +92,7 @@
 
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "file and directory permissions are preserved for unpacked #taskName archives"() {
         given:
         TestFile testDir = createDir('testdir') {
@@ -121,7 +121,7 @@
         "Tar"    | "tarTo"    | "tarTree"
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "file and directory permissions are not preserved when dealing with #taskName archives on OS with no permission support"() {
         given:
         TestFile testDir = createDir('root') {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/BuildResultLoggerIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/BuildResultLoggerIntegrationTest.groovy
index 0c71870..3ec3c67 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/BuildResultLoggerIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/BuildResultLoggerIntegrationTest.groovy
@@ -21,9 +21,9 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
-import spock.lang.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 class BuildResultLoggerIntegrationTest extends AbstractIntegrationSpec implements DirectoryBuildCacheFixture, ValidationMessageChecker {
     def setup() {
@@ -204,7 +204,7 @@
         result.assertHasPostBuildOutput "5 actionable tasks: 1 executed, 4 up-to-date"
     }
 
-    @Requires({ GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsEmbeddedExecutor)
     // this test only works in embedded mode because of the use of validation test fixtures
     def "work validation warnings are mentioned in summary"() {
         buildFile << """
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CacheTaskArchiveErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CacheTaskArchiveErrorIntegrationTest.groovy
index 7fc82d0..b1f843d 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CacheTaskArchiveErrorIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CacheTaskArchiveErrorIntegrationTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.TestBuildCache
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import java.util.function.Consumer
@@ -204,7 +204,7 @@
         localCache.listCacheFailedFiles().size() == 1
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     @Issue("https://github.com/gradle/gradle/issues/9906")
     def "don't cache if task produces broken symlink"() {
         def link = file('root/link')
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CachedCustomTaskExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CachedCustomTaskExecutionIntegrationTest.groovy
index 8b57daa..6698bdb 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CachedCustomTaskExecutionIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CachedCustomTaskExecutionIntegrationTest.groovy
@@ -16,15 +16,17 @@
 
 package org.gradle.api.tasks
 
-import org.apache.commons.io.FileUtils
+
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture
+import org.gradle.integtests.fixtures.TestBuildCache
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import spock.lang.IgnoreIf
 import spock.lang.Issue
-import spock.lang.Requires
 
 import static org.gradle.api.tasks.LocalStateFixture.defineTaskWithLocalState
 import static org.gradle.util.internal.TextUtil.escapeString
@@ -44,12 +46,11 @@
 
             class MyTask extends DefaultTask {}
         """
-        assert listCacheFiles().size() == 0
+
         when:
         withBuildCache().run "help"
         then:
         result.assertTaskNotSkipped(":buildSrc:compileGroovy")
-        listCacheFiles().size() == 1 // compileGroovy
 
         expect:
         file("buildSrc/build").assertIsDir().deleteDir()
@@ -592,8 +593,8 @@
         withBuildCache().fails "customTask"
         then:
         def expectedMessage = message.replace("PATH", file("build/output").path)
-        failureHasCause(~/Failed to store cache entry $CACHE_KEY_PATTERN for task ':customTask': Could not pack tree 'output': ${escapeString(expectedMessage)}/)
-        errorOutput.contains "Could not pack tree 'output': $expectedMessage"
+        failureHasCause(~/Failed to store cache entry $CACHE_KEY_PATTERN for task ':customTask': .*${escapeString(expectedMessage)}/)
+        errorOutput.contains expectedMessage
 
         where:
         expected | actual | message
@@ -631,9 +632,11 @@
         cleanBuildDir()
         withBuildCache().run "producer"
 
-        // Store to local cache again
+        // Store to a different local cache
+        def alternativeLocalCache = new TestBuildCache(file("cache-dir-2").createDir())
+        settingsFile << alternativeLocalCache.localCacheConfiguration()
+
         when:
-        cleanLocalBuildCache()
         withBuildCache().run "producer", "--info", "--rerun-tasks"
         then:
         !output.contains("Caching disabled for task ':producer'")
@@ -806,7 +809,7 @@
         noExceptionThrown()
     }
 
-    @Requires({ GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsEmbeddedExecutor)
     // this test only works in embedded mode because of the use of validation test fixtures
     def "invalid tasks are not cached"() {
         buildFile << """
@@ -840,7 +843,6 @@
             |  Caching has been disabled to ensure correctness. Please consult deprecation warnings for more details.
         """.stripMargin())
         executedAndNotSkipped(":invalid")
-        listCacheFiles().isEmpty()
     }
 
     private static String defineCachedTask(String suffix = "") {
@@ -938,13 +940,6 @@
         file("build").assertIsDir().deleteDir()
     }
 
-    private void cleanLocalBuildCache() {
-        listCacheFiles().each { file ->
-            println "Deleting cache entry: $file"
-            FileUtils.forceDelete(file)
-        }
-    }
-
     void taskIsNotCached(String task) {
         withBuildCache().run task
         executedAndNotSkipped(task)
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CachedTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CachedTaskIntegrationTest.groovy
index a920e60..e5590d6 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CachedTaskIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CachedTaskIntegrationTest.groovy
@@ -52,6 +52,38 @@
         metadata.contains("userName=")
     }
 
+    def "storing in the cache can be disabled"() {
+        buildFile << defineCacheableTask()
+        buildFile << """
+            apply plugin: 'base'
+
+            def storeInCache = project.hasProperty('storeInCache')
+            cacheable.doLast {
+                if (!storeInCache) {
+                    outputs.doNotStoreInCache()
+                }
+            }
+        """
+
+        when:
+        withBuildCache().run("cacheable")
+
+        then:
+        listCacheFiles().empty
+
+        when:
+        withBuildCache().run("clean", "cacheable", "-PstoreInCache")
+
+        then:
+        listCacheFiles().size() == 1
+
+        when:
+        withBuildCache().run("clean", "cacheable")
+
+        then:
+        skipped ":cacheable"
+    }
+
     def "task is cacheable after previous failure"() {
         buildFile << """
             task foo {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyErrorIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyErrorIntegrationTest.groovy
index 79e5b1f..d7cdabc 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyErrorIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyErrorIntegrationTest.groovy
@@ -15,12 +15,13 @@
  */
 package org.gradle.api.tasks
 
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.executer.ExecutionFailure
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.PreconditionVerifier
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.PreconditionVerifier
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Assert
 import org.junit.Rule
 import org.junit.Test
@@ -55,7 +56,7 @@
     }
 
     @Test
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     void reportsSymLinkWhichPointsToNothing() {
         TestFile link = testFile('src/file')
         link.createLink(testFile('missing'))
@@ -77,7 +78,7 @@
     }
 
     @Test
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     void reportsUnreadableSourceDir() {
         TestFile dir = testFile('src').createDir()
         def oldPermissions = dir.permissions
@@ -101,7 +102,7 @@
             failure.assertHasDocumentedCause("Cannot access input property 'rootSpec\$1' of task ':copy'. " +
                 "Accessing unreadable inputs or outputs is not supported. " +
                 "Declare the task as untracked by using Task.doNotTrackState(). " +
-                "See https://docs.gradle.org/current/userguide/incremental_build.html#disable-state-tracking for more details."
+                new DocumentationRegistry().getDocumentationRecommendationFor("information", "incremental_build", "disable-state-tracking")
             )
             failure.assertHasCause("java.nio.file.AccessDeniedException: ${dir}")
         } finally {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyPermissionsIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyPermissionsIntegrationTest.groovy
index a3139e4..a29681d 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyPermissionsIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopyPermissionsIntegrationTest.groovy
@@ -18,15 +18,15 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import static org.junit.Assert.assertTrue
 
 class CopyPermissionsIntegrationTest extends AbstractIntegrationSpec implements UnreadableCopyDestinationFixture {
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "file permissions are preserved in copy action"() {
         given:
         def testSourceFile = file(testFileName)
@@ -49,7 +49,7 @@
         testFileName << ["reference.txt", "\u0627\u0644\u0627\u0655\u062F\u0627\u0631\u0629.txt"]
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "directory permissions are preserved in copy action"() {
         given:
         TestFile parent = getTestDirectory().createDir("testparent")
@@ -72,7 +72,7 @@
         mode << [0755, 0776]
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "fileMode can be modified in copy task"() {
         given:
 
@@ -111,7 +111,7 @@
         mode << [0755, 0776]
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "file permissions can be modified with eachFile closure"() {
         given:
         def testSourceFile = file("reference.txt") << 'test file"'
@@ -145,7 +145,7 @@
         file("build/tmp/reference.txt").mode == 0755
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "fileMode can be modified in copy action"() {
         given:
         file("reference.txt") << 'test file"'
@@ -173,7 +173,7 @@
 
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "dirMode can be modified in copy task"() {
         given:
         TestFile parent = getTestDirectory().createDir("testparent")
@@ -211,7 +211,7 @@
         mode << [0755, 0776]
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "file permissions are not preserved on OS without permission support"() {
         given:
         def testSourceFile = file("reference.txt") << 'test file"'
@@ -232,7 +232,7 @@
         testTargetFile.canWrite()
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     @Issue('https://github.com/gradle/gradle/issues/2639')
     def "excluded files' permissions should be ignored"() {
         given:
@@ -261,7 +261,7 @@
         file('src/unauthorized').mode = 0777
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     @Issue('https://github.com/gradle/gradle/issues/9576')
     def "unreadable #type not produced by task fails"() {
         given:
@@ -294,7 +294,7 @@
         'directory' | { it.createDir() }  | { "java.nio.file.AccessDeniedException: ${it.absolutePath}" }
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     @Issue('https://github.com/gradle/gradle/issues/9576')
     def "can copy into destination directory with unreadable file when using doNotTrackState"() {
         given:
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopySpecIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopySpecIntegrationSpec.groovy
index 174fd77..0e0eebc 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopySpecIntegrationSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/CopySpecIntegrationSpec.groovy
@@ -19,8 +19,8 @@
 import groovy.test.NotYetImplemented
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.TestResources
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Issue
 
@@ -219,7 +219,7 @@
         false // TODO This test can pass on Windows with proper locale, this force the test to fail, remove once fixed
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     @Issue("https://github.com/gradle/gradle/issues/2552")
     def "copying files to a directory with named pipes fails"() {
         def input = file("input.txt").createFile()
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskUnixDerivativeSymlinkIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskUnixDerivativeSymlinkIntegrationTest.groovy
index f27f48c..a65a7f1 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskUnixDerivativeSymlinkIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskUnixDerivativeSymlinkIntegrationTest.groovy
@@ -17,10 +17,10 @@
 package org.gradle.api.tasks
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.UNIX_DERIVATIVE)
+@Requires(UnitTestPreconditions.UnixDerivative)
 class DeleteTaskUnixDerivativeSymlinkIntegrationTest extends DeleteIntegrationTest {
     @Override
     protected void createSymbolicLink(TestFile link, TestFile target) {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskWindowsJunctionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskWindowsJunctionIntegrationTest.groovy
index 5552898..0ae75e0 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskWindowsJunctionIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskWindowsJunctionIntegrationTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.api.tasks
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.util.WindowsSymbolicLinkUtil.createWindowsJunction
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class DeleteTaskWindowsJunctionIntegrationTest extends DeleteIntegrationTest {
     @Override
     protected void createSymbolicLink(TestFile link, TestFile target) {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskWindowsSymbolicLinkIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskWindowsSymbolicLinkIntegrationTest.groovy
index 221f4a8..b506010 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskWindowsSymbolicLinkIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/DeleteTaskWindowsSymbolicLinkIntegrationTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.api.tasks
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.util.WindowsSymbolicLinkUtil.createWindowsSymbolicLink
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class DeleteTaskWindowsSymbolicLinkIntegrationTest extends DeleteIntegrationTest {
     @Override
     protected void createSymbolicLink(TestFile link, TestFile target) {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalBuildSymlinkHandlingIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalBuildSymlinkHandlingIntegrationTest.groovy
index 6c8ece8..5774d5fb 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalBuildSymlinkHandlingIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/IncrementalBuildSymlinkHandlingIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.internal.reflect.problems.ValidationProblemId
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.internal.reflect.validation.ValidationTestFor
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import java.nio.file.Files
 import java.nio.file.StandardCopyOption
 
-@Requires(TestPrecondition.SYMLINKS)
+@Requires(UnitTestPreconditions.Symlinks)
 class IncrementalBuildSymlinkHandlingIntegrationTest extends AbstractIntegrationSpec implements ValidationMessageChecker {
     def setup() {
         expectReindentedValidationMessage()
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/JavaExecJavaVersionIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/JavaExecJavaVersionIntegrationSpec.groovy
index 025ab0a..8814861 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/JavaExecJavaVersionIntegrationSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/JavaExecJavaVersionIntegrationSpec.groovy
@@ -18,13 +18,14 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import spock.lang.Issue
 
 import static org.gradle.api.JavaVersion.VERSION_1_8
 import static org.gradle.api.JavaVersion.VERSION_1_9
 
-@Requires(adhoc = { AvailableJavaHomes.getAvailableJdks(VERSION_1_8).size() > 1 && AvailableJavaHomes.getJdk(VERSION_1_9) })
+@Requires([IntegTestPreconditions.MoreThanOneJava8HomeAvailable, IntegTestPreconditions.Java9HomeAvailable ])
 class JavaExecJavaVersionIntegrationSpec extends AbstractIntegrationSpec {
 
     def setup() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/NestedInputIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/NestedInputIntegrationTest.groovy
index 6c19abe..796032c 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/NestedInputIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/NestedInputIntegrationTest.groovy
@@ -16,15 +16,18 @@
 
 package org.gradle.api.tasks
 
+import groovy.test.NotYetImplemented
 import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.file.RegularFileProperty
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture
 import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.reflect.problems.ValidationProblemId
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.internal.reflect.validation.ValidationTestFor
 import org.gradle.test.fixtures.file.TestFile
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 class NestedInputIntegrationTest extends AbstractIntegrationSpec implements DirectoryBuildCacheFixture, ValidationMessageChecker {
@@ -782,6 +785,90 @@
         output.contains("Input property 'nested.key1' has been removed for task ':myTask'")
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/24594")
+    @ValidationTestFor(ValidationProblemId.NESTED_MAP_UNSUPPORTED_KEY_TYPE)
+    def "nested map with #type key is validated without deprecation warning"() {
+        buildFile << """
+            abstract class CustomTask extends DefaultTask {
+                @Nested
+                abstract MapProperty<$type, Object> getLazyMap()
+
+                @Nested
+                Map<$type, Object> eagerMap = [:]
+
+                @OutputFile
+                abstract RegularFileProperty getOutputFile()
+
+                @TaskAction
+                void execute() {
+                    outputFile.getAsFile().get() << lazyMap.get()
+                    outputFile.getAsFile().get() << eagerMap
+                }
+            }
+
+            tasks.register("customTask", CustomTask) {
+                lazyMap.put($value, "example")
+                eagerMap.put($value, "example")
+                outputFile = file("output.txt")
+            }
+
+            enum Letter { A, B, C }
+        """
+
+        when:
+        run("customTask")
+
+        then:
+        executedAndNotSkipped(":customTask")
+        file("output.txt").text == "[$expectedValue:example][$expectedValue:example]"
+
+        where:
+        type      | value      | expectedValue
+        'Integer' | 100        | 100
+        'String'  | '"foo"'    | 'foo'
+        'Enum'    | 'Letter.A' | 'A'
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/24594")
+    @ValidationTestFor(ValidationProblemId.NESTED_MAP_UNSUPPORTED_KEY_TYPE)
+    def "nested map with unsupported key type is validated with deprecation warning"() {
+        buildFile << """
+            abstract class CustomTask extends DefaultTask {
+                @Nested
+                abstract MapProperty<Boolean, Object> getUnsupportedLazyMap()
+
+                @Nested
+                Map<Boolean, Object> unsupportedEagerMap = [:]
+
+                @OutputFile
+                abstract RegularFileProperty getOutputFile()
+
+                @TaskAction
+                void execute() {
+                    outputFile.getAsFile().get() << unsupportedLazyMap.get()
+                    outputFile.getAsFile().get() << unsupportedEagerMap
+                }
+            }
+
+            tasks.register("customTask", CustomTask) {
+                unsupportedLazyMap.put(true, "example")
+                unsupportedEagerMap.put(false, "example")
+                outputFile = file("output.txt")
+            }
+        """
+
+        when:
+        expectThatExecutionOptimizationDisabledWarningIsDisplayed(executer,
+            "Type 'CustomTask' property 'unsupportedEagerMap' where key of nested map is of type 'java.lang.Boolean'. " +
+                "Reason: Key of nested map must be one of the following types: 'Enum', 'Integer', 'String'.",
+            'validation_problems',
+            'unsupported_key_type_of_nested_map')
+        run("customTask")
+
+        then:
+        executedAndNotSkipped(":customTask")
+        file("output.txt").text == "[true:example][false:example]"
+    }
 
     private static String namedBeanClass() {
         """
@@ -993,6 +1080,47 @@
         project2.file('build/tmp/myTask/output.txt').text == "hello"
     }
 
+    @NotYetImplemented
+    // TODO: Remove IgnoreIf embedded when issue is fixed
+    @IgnoreIf({ GradleContextualExecuter.embedded })
+    @Issue("https://github.com/gradle/gradle/issues/24405")
+    def "nested bean as input with null string is serialized correctly"() {
+        buildFile << """
+            interface SiteExtension {
+                @Nested
+                CustomData getCustomData();
+            }
+            interface CustomData {
+                Property<String> getWebsiteUrl();
+                Property<String> getVcsUrl();
+            }
+
+            def extension = project.getExtensions().create("site", SiteExtension.class);
+            abstract class CrashTask extends DefaultTask {
+                @OutputDirectory
+                abstract DirectoryProperty getOutputDir();
+
+                @Input
+                abstract Property<CustomData> getCustomData();
+
+                @TaskAction
+                void run() {
+                }
+            }
+
+            extension.customData.vcsUrl = "goose"
+            tasks.register("crashTask", CrashTask) {
+                customData = extension.customData
+                outputDir = file("build/crashTask")
+            }
+        """
+
+        expect:
+        // TODO: Remove `requireIsolatedDaemons` once the issue is fixed
+        executer.requireIsolatedDaemons()
+        succeeds "crashTask"
+    }
+
     private TestFile nestedBeanWithAction(TestFile projectDir = temporaryFolder.testDirectory) {
         return projectDir.file("buildSrc/src/main/java/NestedBeanWithAction.java") << """
             import org.gradle.api.tasks.Nested;
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCreationBuildOperationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCreationBuildOperationIntegrationTest.groovy
index baa51c6..f8d959e 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCreationBuildOperationIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskCreationBuildOperationIntegrationTest.groovy
@@ -23,9 +23,16 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.BuildOperationsFixture
 import org.gradle.integtests.fixtures.build.BuildTestFixture
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.logging.events.LogEvent
 import org.gradle.internal.operations.BuildOperationType
 import org.gradle.internal.operations.trace.BuildOperationRecord
+import org.gradle.util.Path
+
+import java.util.stream.Collectors
+
+import static java.util.stream.Collectors.groupingBy
+import static java.util.stream.Collectors.mapping
 
 class TaskCreationBuildOperationIntegrationTest extends AbstractIntegrationSpec {
 
@@ -270,6 +277,20 @@
         allRealizeOps.findAll { !it.details.eager }.each {
             assert it.details.taskId in allRegisterIds
         }
+        verifyUniqueIdPerTaskPath()
+    }
+
+    private void verifyUniqueIdPerTaskPath() {
+        def allRealizeOps = buildOperations.all(RealizeTaskBuildOperationType)
+        def idsByTaskPath = allRealizeOps.stream()
+                .map(it -> it.details)
+                .collect(groupingBy(
+                        { it -> Path.path((String) it.buildPath).append(Path.path((String) it.taskPath)) },
+                        mapping({ it -> (Long) it.taskId }, Collectors.toSet())
+                ))
+        idsByTaskPath.each { entry ->
+            assert entry.value.size() == 1
+        }
     }
 
     private static Spec<? super BuildOperationRecord> withAnyPath(String buildPath, String... paths) {
@@ -285,15 +306,24 @@
     }
 
     private <T extends BuildOperationType<?, ?>> BuildOperationRecord verifyTaskDetails(Map<String, ?> expectedDetails, Class<T> type, Spec<? super BuildOperationRecord> spec) {
-        def op = buildOperations.only(type, spec)
-        verifyTaskDetails(expectedDetails, op)
-        op
+        def ops = buildOperations.all(type, spec)
+        assert !ops.empty
+        if (type == RealizeTaskBuildOperationType && GradleContextualExecuter.configCache) {
+            // When using load after store, the task will be realized twice:
+            //  - at configuration time
+            //  - after loading from the configuration cache
+            // Though even with load after store we can't assume == 2 here, since some of the tests fail the build before loading form the configuration cache.
+            assert ops.size() <= 2
+        } else {
+            assert ops.size() == 1
+        }
+        // Only verify the first event since it is the one from the configuration phase.
+        verifyTaskDetails(expectedDetails, ops.first())
+        ops.first()
     }
 
     private <T extends BuildOperationType<?, ?>> BuildOperationRecord verifyTaskDetails(Class<T> type, Spec<? super BuildOperationRecord> spec) {
-        def op = buildOperations.only(type, spec)
-        verifyTaskDetails([:], op)
-        op
+        verifyTaskDetails([:], type, spec)
     }
 
     static final Map<?, ?> DEFAULT_EXPECTED_DETAILS = [
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskParametersIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskParametersIntegrationTest.groovy
index b27eac3..f14472f 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskParametersIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/TaskParametersIntegrationTest.groovy
@@ -30,8 +30,9 @@
 import org.gradle.internal.reflect.problems.ValidationProblemId
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.internal.reflect.validation.ValidationTestFor
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import spock.lang.Issue
-import spock.lang.Requires
 
 class TaskParametersIntegrationTest extends AbstractIntegrationSpec implements ValidationMessageChecker {
 
@@ -380,7 +381,7 @@
         "123"                | "123 as short"
     }
 
-    @Requires({ GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsEmbeddedExecutor)
     // this test only works in embedded mode because of the use of validation test fixtures
     def "invalid task causes VFS to drop"() {
         buildFile << """
@@ -406,7 +407,7 @@
         outputContains("Invalidating VFS because task ':invalid' failed validation")
     }
 
-    @Requires({ GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsEmbeddedExecutor)
     // this test only works in embedded mode because of the use of validation test fixtures
     def "validation warnings are displayed once"() {
         buildFile << """
@@ -432,7 +433,7 @@
         output.count("- Type 'InvalidTask' property 'inputFile' test problem. Reason: This is a test.") == 1
     }
 
-    @Requires({ GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsEmbeddedExecutor)
     // this test only works in embedded mode because of the use of validation test fixtures
     def "validation warnings are reported even when task is skipped"() {
         buildFile << """
@@ -984,26 +985,26 @@
         succeeds("printCounts")
         then:
         executedAndNotSkipped(':myTask')
-        outputContains("outputFileCount = 2, inputFileCount = 3, inputValueCount = 1, nestedInputCount = 4, nestedInputValueCount = 1")
+        outputContains("outputFileCount = 2, inputFileCount = 3, inputValueCount = 1, nestedInputCount = 3, nestedInputValueCount = 1")
 
         when:
         inputFile.text = "changed"
         withBuildCache().succeeds("printCounts")
         then:
         executedAndNotSkipped(':myTask')
-        outputContains("outputFileCount = 2, inputFileCount = 3, inputValueCount = 1, nestedInputCount = 4, nestedInputValueCount = 1")
+        outputContains("outputFileCount = 2, inputFileCount = 3, inputValueCount = 1, nestedInputCount = 3, nestedInputValueCount = 1")
 
         when:
         succeeds("printCounts")
         then:
         skipped(':myTask')
-        outputContains("outputFileCount = 1, inputFileCount = 2, inputValueCount = 1, nestedInputCount = 4, nestedInputValueCount = 1")
+        outputContains("outputFileCount = 1, inputFileCount = 2, inputValueCount = 1, nestedInputCount = 3, nestedInputValueCount = 1")
 
         when:
         file('build').deleteDir()
         withBuildCache().succeeds("printCounts")
         then:
         skipped(':myTask')
-        outputContains("outputFileCount = 1, inputFileCount = 2, inputValueCount = 1, nestedInputCount = 4, nestedInputValueCount = 1")
+        outputContains("outputFileCount = 1, inputFileCount = 2, inputValueCount = 1, nestedInputCount = 3, nestedInputValueCount = 1")
     }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/UnreadableCopyDestinationFixture.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/UnreadableCopyDestinationFixture.groovy
index c99efb5..821c508 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/UnreadableCopyDestinationFixture.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/UnreadableCopyDestinationFixture.groovy
@@ -17,6 +17,7 @@
 package org.gradle.api.tasks
 
 import groovy.transform.SelfType
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
 @SelfType(AbstractIntegrationSpec)
@@ -24,7 +25,7 @@
     private static final String COPY_UNREADABLE_DESTINATION_FAILURE = "Cannot access a file in the destination directory. " +
         "Copying to a directory which contains unreadable content is not supported. " +
         "Declare the task as untracked by using Task.doNotTrackState(). " +
-        "See https://docs.gradle.org/current/userguide/incremental_build.html#disable-state-tracking for more details."
+        new DocumentationRegistry().getDocumentationRecommendationFor("information", "incremental_build", "disable-state-tracking")
 
     void expectUnreadableCopyDestinationFailure() {
         failure.assertHasDocumentedCause(COPY_UNREADABLE_DESTINATION_FAILURE)
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/UntrackedTaskIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/UntrackedTaskIntegrationTest.groovy
index b1228f6..341a6f3 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/UntrackedTaskIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/UntrackedTaskIntegrationTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class UntrackedTaskIntegrationTest extends AbstractIntegrationSpec implements DirectoryBuildCacheFixture, ValidationMessageChecker {
 
@@ -49,14 +49,14 @@
         then:
         executedAndNotSkipped(":myTask")
         outputContains("Task ':myTask' is not up-to-date because:")
-        outputContains("Task state is not tracked.")
+        outputContains("Task is untracked because: For testing")
 
         when:
         run("myTask", "--info")
         then:
         executedAndNotSkipped(":myTask")
         outputContains("Task ':myTask' is not up-to-date because:")
-        outputContains("Task state is not tracked.")
+        outputContains("Task is untracked because: For testing")
     }
 
     def "fails when incremental task is marked as untracked"() {
@@ -103,7 +103,7 @@
         then:
         executedAndNotSkipped(":myTask")
         outputContains("Task ':myTask' is not up-to-date because:")
-        outputContains("Task state is not tracked.")
+        outputContains("Task is untracked because: For testing")
     }
 
     def "untracked task is not cached"() {
@@ -176,7 +176,7 @@
         skipped(":mySubclassOfUntrackedTask")
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "untracked tasks can produce and consume unreadable content"() {
         def rootDir = file("build/root")
         def unreadableDir = rootDir.file("unreadable")
@@ -201,7 +201,7 @@
         unreadableDir.setReadable(true)
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "tracked task producing unreadable content fails"() {
         def rootDir = file("build/root")
         def unreadableDir = rootDir.file("unreadable")
@@ -224,14 +224,14 @@
         failure.assertHasDocumentedCause("Cannot access output property 'outputDir' of task ':producer'. " +
             "Accessing unreadable inputs or outputs is not supported. " +
             "Declare the task as untracked by using Task.doNotTrackState(). " +
-            "See https://docs.gradle.org/current/userguide/incremental_build.html#disable-state-tracking for more details.")
+            getDisableStateTrackingLink())
         failureHasCause("java.nio.file.AccessDeniedException: ${unreadableDir.absolutePath}")
 
         cleanup:
         unreadableDir.setReadable(true)
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "tracked task producing named pipe fails"() {
         def rootDir = file("build/root")
         def namedPipe = rootDir.file("unreadable")
@@ -254,11 +254,11 @@
         failure.assertHasDocumentedCause("Cannot access output property 'outputDir' of task ':producer'. " +
             "Accessing unreadable inputs or outputs is not supported. " +
             "Declare the task as untracked by using Task.doNotTrackState(). " +
-            "See https://docs.gradle.org/current/userguide/incremental_build.html#disable-state-tracking for more details.")
+            getDisableStateTrackingLink())
         failureHasCause("java.io.IOException: Cannot snapshot ${namedPipe}: not a regular file")
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "tracked task consuming unreadable content fails"() {
         def rootDir = file("build/root")
         def unreadableDir = rootDir.file("unreadable")
@@ -281,13 +281,17 @@
         failure.assertHasDocumentedCause("Cannot access input property 'inputDir' of task ':consumer'. " +
             "Accessing unreadable inputs or outputs is not supported. " +
             "Declare the task as untracked by using Task.doNotTrackState(). " +
-            "See https://docs.gradle.org/current/userguide/incremental_build.html#disable-state-tracking for more details.")
+            getDisableStateTrackingLink())
         failureHasCause("java.nio.file.AccessDeniedException: ${unreadableDir.absolutePath}")
 
         cleanup:
         unreadableDir.setReadable(true)
     }
 
+    def getDisableStateTrackingLink() {
+        documentationRegistry.getDocumentationRecommendationFor("information", "incremental_build", "disable-state-tracking")
+    }
+
     def "does not clean up stale outputs for untracked tasks"() {
         def untrackedStaleFile = file("build/untracked/stale-output-file").createFile()
         def untrackedOutputFile = file("build/untracked/output.txt")
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/WritePropertiesIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/WritePropertiesIntegrationTest.groovy
index cc888a4..1c3f42f 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/WritePropertiesIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/WritePropertiesIntegrationTest.groovy
@@ -19,6 +19,8 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleExecuter
 
+import static org.gradle.api.internal.DocumentationRegistry.BASE_URL
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
 import static org.gradle.util.internal.GUtil.loadProperties
 
 class WritePropertiesIntegrationTest extends AbstractIntegrationSpec {
@@ -59,7 +61,8 @@
 
     private expectOutputDeprecation(GradleExecuter runWithTasks) {
         runWithTasks.expectDocumentedDeprecationWarning("The WriteProperties.outputFile property has been deprecated. This is scheduled to be removed in Gradle 9.0." +
-            " Please use the destinationFile property instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.WriteProperties.html#org.gradle.api.tasks.WriteProperties:outputFile for more details.")
+            " Please use the destinationFile property instead. " +
+            String.format(RECOMMENDATION, "information", "${BASE_URL}/dsl/org.gradle.api.tasks.WriteProperties.html#org.gradle.api.tasks.WriteProperties:outputFile"))
     }
 
     def "simple properties are written sorted alphabetically with #outputProprertyName"() {
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy
index dc2d800..1f210ad 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.api.tasks.bundling
 
+import org.apache.commons.io.FileUtils
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
@@ -437,22 +438,14 @@
         given: "2 archive files"
         createTar('test1.tar') {
             subdir1 {
-                file ('file.txt').text = 'original text 1'
+                file('file.txt').text = 'original text 1'
             }
             subdir2 {
                 file('file2.txt').text = 'original text 2'
-                file ('file3.txt').text =  'original text 3'
+                file('file3.txt').text = 'original text 3'
             }
         }
-        createTar('test2.tar') {
-            subdir1 {
-                file ('file.txt').text = 'original text 1'
-            }
-            subdir2 {
-                file('file2.txt').text = 'original text 2'
-                file ('file3.txt').text =  'original text 3'
-            }
-        }
+        FileUtils.copyFile(file('test1.tar'), file('test2.tar'));
 
         and: "where a build edits each archive differently via a visitor"
         file('build.gradle') << """
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/options/TaskOptionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/options/TaskOptionIntegrationTest.groovy
index 9564add..1192efc 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/options/TaskOptionIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/options/TaskOptionIntegrationTest.groovy
@@ -170,6 +170,8 @@
         then:
         outputContains("""
 Options
+     --no-prop2     Disables option --prop2
+
      --prop1     Configures command line option 'prop1'.
 
      --prop2     Configures command line option 'prop2'.
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/cache/internal/DefaultGeneratedGradleJarCacheIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/cache/internal/DefaultGeneratedGradleJarCacheIntegrationTest.groovy
index 0334ce1..491e296 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/cache/internal/DefaultGeneratedGradleJarCacheIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/cache/internal/DefaultGeneratedGradleJarCacheIntegrationTest.groovy
@@ -19,6 +19,7 @@
 import org.gradle.api.Action
 import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping
 import org.gradle.cache.internal.scopes.DefaultGlobalScopedCacheBuilderFactory
+import org.gradle.internal.agents.AgentStatus
 import org.gradle.internal.logging.services.LoggingServiceRegistry
 import org.gradle.internal.service.DefaultServiceRegistry
 import org.gradle.internal.service.ServiceRegistryBuilder
@@ -52,7 +53,7 @@
     def services = (DefaultServiceRegistry) ServiceRegistryBuilder.builder()
             .parent(NativeServicesTestFixture.getInstance())
             .provider(LoggingServiceRegistry.NO_OP)
-            .provider(new GlobalScopeServices(false))
+            .provider(new GlobalScopeServices(false, AgentStatus.disabled()))
             .build()
 
     def factory = services.get(CacheFactory.class)
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/caching/configuration/internal/BuildCacheCompositeConfigurationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/caching/configuration/internal/BuildCacheCompositeConfigurationIntegrationTest.groovy
index 5b80b42..a070e75 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/caching/configuration/internal/BuildCacheCompositeConfigurationIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/caching/configuration/internal/BuildCacheCompositeConfigurationIntegrationTest.groovy
@@ -162,13 +162,33 @@
                             Thread.sleep(1000)
                         }
                     }
+                    ${mavenCentralRepository()}
+                    testing.suites.test.useJUnitJupiter()
                 }
                 tasks.build.dependsOn(subprojects.tasks.build)
                 tasks.clean.dependsOn(subprojects.tasks.clean)
             """
-            file("src/test/java/Test.java") << """class Test {}"""
-            file("first/src/test/java/Test.java") << """class TestFirst {}"""
-            file("second/src/test/java/Test.java") << """class TestSecond {}"""
+            file("src/test/java/Test.java") <<
+                """
+                class Test {
+                    @org.junit.jupiter.api.Test
+                    public void test() {}
+                }
+                """
+            file("first/src/test/java/TestFirst.java") <<
+                """
+                class TestFirst {
+                    @org.junit.jupiter.api.Test
+                    public void test() {}
+                }
+                """
+            file("second/src/test/java/TestSecond.java") <<
+                """
+                class TestSecond {
+                    @org.junit.jupiter.api.Test
+                    public void test() {}
+                }
+                """
         }
 
         settingsFile << localCache.localCacheConfiguration() << """
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/configuration/ExecuteUserLifecycleListenerBuildOperationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/configuration/ExecuteUserLifecycleListenerBuildOperationIntegrationTest.groovy
index d9a0e86..315fdf3 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/configuration/ExecuteUserLifecycleListenerBuildOperationIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/configuration/ExecuteUserLifecycleListenerBuildOperationIntegrationTest.groovy
@@ -497,7 +497,6 @@
         verifyHasChildren(rootAfterEvaluated, rootOtherScriptAppId, 'other script', expectedProjectOps)
     }
 
-    @ToBeFixedForConfigurationCache(because = "build listener")
     def 'taskGraph whenReady action listeners are attributed to the correct registrant'() {
         given:
         def addGradleListeners = { String source ->
@@ -550,6 +549,7 @@
         verifyHasChildren(whenReadyEvaluated, rootOtherScriptAppId, 'other script', expectedGradleOps)
     }
 
+    @ToBeFixedForConfigurationCache(because = "build listener")
     def 'listeners that implement multiple interfaces are decorated correctly'() {
         given:
         def addGradleListeners = { String source ->
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/execution/taskgraph/ParallelTaskExecutionIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/execution/taskgraph/ParallelTaskExecutionIntegrationTest.groovy
index a212f28..5d030c4 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/execution/taskgraph/ParallelTaskExecutionIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/execution/taskgraph/ParallelTaskExecutionIntegrationTest.groovy
@@ -20,15 +20,14 @@
 import org.gradle.integtests.fixtures.RepoScriptBlockUtil
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
-import org.gradle.test.fixtures.Flaky
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.junit.Rule
-import spock.lang.IgnoreIf
 import spock.lang.Issue
-import spock.lang.Requires
 import spock.lang.Timeout
 
-@IgnoreIf({ GradleContextualExecuter.parallel })
+@Requires(IntegTestPreconditions.NotParallelExecutor)
 // no point, always runs in parallel
 class ParallelTaskExecutionIntegrationTest extends AbstractIntegrationSpec implements ValidationMessageChecker {
 
@@ -382,7 +381,6 @@
         }
     }
 
-    @Flaky(because = "https://github.com/gradle/gradle-private/issues/3570")
     def "tasks are not run in parallel if destroy files overlap with input files (create/use first)"() {
         given:
         withParallelThreads(2)
@@ -524,7 +522,7 @@
         }
     }
 
-    @Requires({ GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsEmbeddedExecutor)
     // this test only works in embedded mode because of the use of validation test fixtures
     def "other tasks are not started when an invalid task is running"() {
         given:
@@ -550,7 +548,7 @@
         run ":aPingWithCacheableWarnings", ":bPing", ":cPing"
     }
 
-    @Requires({ GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsEmbeddedExecutor)
     // this test only works in embedded mode because of the use of validation test fixtures
     def "invalid task is not executed in parallel with other task"() {
         given:
@@ -646,11 +644,11 @@
             }
             subprojects {
                 configurations.create("myconfig1") {
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
                 }
                 configurations.create("myconfig2") {
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
                 }
 
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcIdentityIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcIdentityIntegrationTest.groovy
index 8a95a4f..c221687 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcIdentityIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/initialization/buildsrc/BuildSrcIdentityIntegrationTest.groovy
@@ -18,6 +18,12 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 class BuildSrcIdentityIntegrationTest extends AbstractIntegrationSpec {
     def "includes build identifier in logging output with #display"() {
         file("buildSrc/build.gradle") << """
@@ -96,9 +102,14 @@
         failure.assertHasCause("""Could not find org.test:test:1.2.
 Searched in the following locations:
   - ${m.pom.file.toURL()}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :buildSrc""")
+        failure.assertHasResolutions(repositoryHint("Maven POM"),
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
+
 
         when:
         m.publish()
@@ -155,10 +166,12 @@
             classes.doLast {
                 def components = configurations.compileClasspath.incoming.resolutionResult.allComponents.id
                 assert components.size() == 2
+                assert components[0].build.buildPath == ':buildSrc'
                 assert components[0].build.name == 'buildSrc'
                 assert components[0].build.currentBuild
                 assert components[0].projectPath == ':'
-                assert components[0].projectName == 'buildSrc'
+                assert components[0].projectName == '$rootProjectName'
+                assert components[1].build.buildPath == ':buildSrc'
                 assert components[1].build.name == 'buildSrc'
                 assert components[1].build.currentBuild
                 assert components[1].projectPath == ':a'
@@ -167,17 +180,24 @@
                 def selectors = configurations.runtimeClasspath.incoming.resolutionResult.allDependencies.requested
                 assert selectors.size() == 1
                 assert selectors[0].displayName == 'project :buildSrc:a'
+                assert selectors[0].buildPath == ':buildSrc'
                 assert selectors[0].buildName == 'buildSrc'
                 assert selectors[0].projectPath == ':a'
             }
         """
 
+        2.times {
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.isCurrentBuild() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+        }
+        executer.expectDocumentedDeprecationWarning("The ProjectComponentSelector.getBuildName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+
         expect:
         succeeds()
 
         where:
-        settings                     | display
-        ""                           | "default root project name"
-        "rootProject.name='someLib'" | "configured root project name"
+        settings                     | rootProjectName | display
+        ""                           | "buildSrc"      | "default root project name"
+        "rootProject.name='someLib'" | "someLib"       | "configured root project name"
     }
 }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/internal/classpath/BuildScriptClasspathIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/internal/classpath/BuildScriptClasspathIntegrationSpec.groovy
index 2483dfd..2913c76 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/internal/classpath/BuildScriptClasspathIntegrationSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/internal/classpath/BuildScriptClasspathIntegrationSpec.groovy
@@ -16,14 +16,18 @@
 
 package org.gradle.internal.classpath
 
+
 import org.gradle.api.internal.cache.CacheConfigurationsInternal
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.fixtures.cache.FileAccessTimeJournalFixture
+import org.gradle.integtests.fixtures.executer.AbstractGradleExecuter
 import org.gradle.integtests.fixtures.executer.ArtifactBuilder
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.server.http.HttpServer
 import org.gradle.test.fixtures.server.http.MavenHttpRepository
 import org.junit.Rule
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 import spock.lang.Unroll
 
@@ -92,6 +96,7 @@
         loopNumber << (1..6).toList()
     }
 
+    @IgnoreIf({ AbstractGradleExecuter.agentInstrumentationEnabled }) // Agent-based instrumentation doesn't expose cached JARs
     def "build script classloader copies jar files to cache"() {
         given:
         createBuildFileThatPrintsClasspathURLs("""
@@ -312,6 +317,148 @@
         noExceptionThrown()
     }
 
+    @IgnoreIf({ AvailableJavaHomes.getJdk11() == null || AvailableJavaHomes.getJdk8() == null })
+    def "proper version is selected for multi-release jar"() {
+        given:
+        createDir("mrjar") {
+            file("build.gradle") << """
+                plugins {
+                    id("java")
+                    id 'me.champeau.mrjar' version "0.1.1"
+                }
+
+                multiRelease {
+                    targetVersions 8, 11
+                }
+            """
+            file("src/main/java/org/gradle/test/mrjar/Foo.java") << """
+                package org.gradle.test.mrjar;
+
+                public class Foo {
+                    public static String getBar() {
+                        return "DEFAULT";
+                    }
+                }
+            """
+            file("src/java11/java/org/gradle/test/mrjar/Foo.java") << """
+                package org.gradle.test.mrjar;
+
+                public class Foo {
+                    public static String getBar() {
+                        return "11";
+                    }
+                }
+            """
+        }
+
+        buildScript("""
+            buildscript {
+                dependencies {
+                    classpath "org.gradle.test:mrjar:1.+"
+                }
+            }
+
+            import org.gradle.test.mrjar.Foo
+
+            tasks.register("printFoo") {
+                doLast {
+                    println("JAR = \${Foo.bar}")
+                }
+            }
+        """)
+        settingsFile("""
+            includeBuild("mrjar") {
+                dependencySubstitution {
+                    substitute module('org.gradle.test:mrjar') using project(':')
+                }
+            }
+        """)
+
+        def java8Home = AvailableJavaHomes.getJdk8().javaHome
+        def java11Home = AvailableJavaHomes.getJdk11().javaHome
+
+        when:
+        executer.withJavaHome(java8Home).withArguments("-Porg.gradle.java.installations.paths=$java8Home,$java11Home")
+        succeeds("printFoo")
+
+        then:
+        outputContains("JAR = DEFAULT")
+
+        when:
+        executer.withJavaHome(java11Home).withArguments("-Porg.gradle.java.installations.paths=$java8Home,$java11Home")
+        succeeds("printFoo")
+
+        then:
+        outputContains("JAR = 11")
+    }
+
+    def "class with #lambdaCount lambdas can be instrumented"() {
+        given:
+        createDir("buildSrc/src/main/java") {
+            try(def src = file("ManyLambdas.java").newWriter()) {
+                src.append("""
+                    import ${List.name};
+                    import ${ArrayList.name};
+
+                    public class ManyLambdas {
+                        public List<Runnable> createLotsOfLambdas() {
+                            List<Runnable> runnables = new ArrayList<>($lambdaCount);
+                """)
+                for (int i = 1; i <= lambdaCount; ++i) {
+                    src.append("""
+                            runnables.add(() -> System.out.println("lambda #" + $i));
+                    """)
+                }
+                src.append("""
+                            return runnables;
+                        }
+                    }
+                """)
+            }
+        }
+        buildScript("""
+            abstract class LambdaTask extends DefaultTask {
+                @Input
+                abstract ListProperty<Runnable> getMyActions()
+
+                @TaskAction
+                def runMyActions() {
+                    myActions.get().forEach {
+                        it.run()
+                    }
+                }
+            }
+
+            def getDeserializeMethodsCount(Class<?> cls) {
+                return Arrays.stream(cls.getDeclaredMethods()).filter {
+                    it.name.startsWith('\$deserializeLambda')
+                }.count()
+            }
+
+            tasks.register("lambda", LambdaTask) {
+                myActions = new ManyLambdas().createLotsOfLambdas()
+
+                doFirst {
+                    println("generated method count = \${getDeserializeMethodsCount(ManyLambdas)}")
+                }
+            }
+        """)
+
+        when:
+        succeeds("lambda")
+
+        then:
+        outputContains("generated method count = $expectedMethodCount")
+        outputContains("lambda #1")
+        outputContains("lambda #$lambdaCount")
+
+        where:
+        lambdaCount || expectedMethodCount
+        1000        || 1
+        2000        || 2
+        3200        || 3
+    }
+
     void notInJarCache(String filename) {
         inJarCache(filename, false)
     }
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/internal/id/ConfigurationCacheableIdFactoryIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/internal/id/ConfigurationCacheableIdFactoryIntegrationTest.groovy
new file mode 100644
index 0000000..9163a1b
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/internal/id/ConfigurationCacheableIdFactoryIntegrationTest.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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.id
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
+class ConfigurationCacheableIdFactoryIntegrationTest extends AbstractIntegrationSpec {
+
+    @IgnoreIf({ GradleContextualExecuter.configCache })
+    def "unable to create new configuration-cacheable ids after load"() {
+        buildFile << """
+            def factory = gradle.services.get(${ConfigurationCacheableIdFactory.name})
+
+            task run {
+                doLast {
+                    def id = factory.createId()
+                    println("Created new id: \$id")
+                }
+            }
+        """
+
+        when:
+        succeeds ":run"
+        then:
+        executedAndNotSkipped(":run")
+        outputContains("Created new id:")
+
+        when:
+        executer.withArgument("--configuration-cache")
+        runAndFail ":run"
+
+        then:
+        failure.assertHasCause("Cannot create a new id after one has been loaded")
+    }
+
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/AbstractWorkerProcessIntegrationSpec.groovy b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/AbstractWorkerProcessIntegrationSpec.groovy
index 3713ca9..6185062 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/AbstractWorkerProcessIntegrationSpec.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/AbstractWorkerProcessIntegrationSpec.groovy
@@ -27,12 +27,13 @@
 import org.gradle.api.logging.LogLevel
 import org.gradle.cache.UnscopedCacheBuilderFactory
 import org.gradle.cache.internal.CacheFactory
-import org.gradle.cache.internal.DefaultUnscopedCacheBuilderFactory
 import org.gradle.cache.internal.CacheScopeMapping
+import org.gradle.cache.internal.DefaultUnscopedCacheBuilderFactory
 import org.gradle.cache.internal.scopes.DefaultCacheScopeMapping
 import org.gradle.cache.internal.scopes.DefaultGlobalScopedCacheBuilderFactory
 import org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory
 import org.gradle.initialization.layout.GlobalCacheDir
+import org.gradle.internal.agents.AgentStatus
 import org.gradle.internal.id.LongIdGenerator
 import org.gradle.internal.jvm.inspection.CachingJvmMetadataDetector
 import org.gradle.internal.jvm.inspection.DefaultJvmMetadataDetector
@@ -65,7 +66,7 @@
     DefaultServiceRegistry services = (DefaultServiceRegistry) ServiceRegistryBuilder.builder()
         .parent(NativeServicesTestFixture.getInstance())
         .provider(LoggingServiceRegistry.NO_OP)
-        .provider(new GlobalScopeServices(false))
+        .provider(new GlobalScopeServices(false, AgentStatus.disabled()))
         .build()
     final MessagingServer server = services.get(MessagingServer.class)
     @Rule
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegrationTest.groovy
index 8774b81..420d7fd 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/PathLimitationIntegrationTest.groovy
@@ -24,8 +24,8 @@
 import org.gradle.process.internal.worker.WorkerProcessBuilder
 import org.gradle.process.internal.worker.WorkerProcessContext
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Timeout
 
 import static org.junit.Assert.assertFalse
@@ -41,7 +41,7 @@
         broadcast.add(listenerMock)
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "WorkerProcessBuilder handles workingDir with absolute path length #absolutePathLength"() {
         when:
         def testWorkingDir = generateTestWorkingDirectory(absolutePathLength)
@@ -52,7 +52,7 @@
         absolutePathLength << [258, 259, 260]
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "JavaProcessBuilder handles workingDir with absolute path length #absolutePathLength"() {
         when:
         def testWorkingDir = generateTestWorkingDirectory(absolutePathLength)
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/WorkerProcessIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/WorkerProcessIntegrationTest.groovy
index 6ad279b..8372c72 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/process/internal/WorkerProcessIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/process/internal/WorkerProcessIntegrationTest.groovy
@@ -30,8 +30,8 @@
 import org.gradle.process.internal.worker.WorkerProcess
 import org.gradle.process.internal.worker.WorkerProcessBuilder
 import org.gradle.process.internal.worker.WorkerProcessContext
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import spock.lang.Timeout
 
@@ -259,7 +259,7 @@
         stdout.stdErr == ""
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "handles output when worker fails before logging is started"() {
         when:
         execute(worker(new RemoteProcess()).jvmArgs("-Dorg.gradle.native.dir=/dev/null").expectStartFailure())
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/util/internal/VersionNumberIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/util/internal/VersionNumberIntegrationTest.groovy
index 149b48a..6362b91 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/util/internal/VersionNumberIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/util/internal/VersionNumberIntegrationTest.groovy
@@ -17,12 +17,16 @@
 package org.gradle.util.internal
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.dsl.GradleDsl
 
+import static org.junit.Assume.assumeFalse
+
 
 class VersionNumberIntegrationTest extends AbstractIntegrationSpec {
 
     def "nullability with Kotlin jsr-305 strict"() {
+        assumeFalse(GradleContextualExecuter.embedded)
 
         given:
         file("src/main/kotlin/Test.kt") << """
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/AbstractTask.java b/subprojects/core/src/main/java/org/gradle/api/internal/AbstractTask.java
index a715425..81c52da 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/AbstractTask.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/AbstractTask.java
@@ -48,13 +48,14 @@
 import org.gradle.api.internal.tasks.TaskRequiredServices;
 import org.gradle.api.internal.tasks.TaskStateInternal;
 import org.gradle.api.internal.tasks.execution.DescribingAndSpec;
+import org.gradle.api.internal.tasks.properties.ServiceReferenceSpec;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.services.BuildService;
+import org.gradle.api.services.internal.BuildServiceProvider;
 import org.gradle.api.services.internal.BuildServiceRegistryInternal;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.Internal;
@@ -65,6 +66,7 @@
 import org.gradle.configuration.internal.UserCodeApplicationContext;
 import org.gradle.internal.Cast;
 import org.gradle.internal.Factory;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.execution.history.changes.InputChangesInternal;
 import org.gradle.internal.extensibility.ExtensibleDynamicObject;
 import org.gradle.internal.hash.ClassLoaderHierarchyHasher;
@@ -95,6 +97,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.stream.Collectors;
 
 import static org.gradle.util.internal.GUtil.uncheckedCall;
 
@@ -583,15 +586,27 @@ public void setProperty(String name, Object value) {
     @Internal
     @Override
     @Deprecated
-    public Convention getConvention() {
-        assertDynamicObject();
-        return extensibleDynamicObject.getConvention();
+    public org.gradle.api.plugins.Convention getConvention() {
+        DeprecationLogger.deprecateMethod(AbstractTask.class, "getConvention()")
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "deprecated_access_to_conventions")
+            .nagUser();
+        return getConventionVia("Task.convention", false);
     }
 
     @Internal
     @Override
     public ExtensionContainer getExtensions() {
-        return getConvention();
+        return getConventionVia("Task.extensions", true);
+    }
+
+    private org.gradle.api.plugins.Convention getConventionVia(String invocationDescription, boolean disableDeprecationForConventionAccess) {
+        notifyConventionAccess(invocationDescription);
+        assertDynamicObject();
+        if (disableDeprecationForConventionAccess) {
+            return DeprecationLogger.whileDisabled(() -> extensibleDynamicObject.getConvention());
+        }
+        return extensibleDynamicObject.getConvention();
     }
 
     @Internal
@@ -1039,10 +1054,25 @@ public TaskRequiredServices getRequiredServices() {
     }
 
     @Override
+    public void acceptServiceReferences(Set<ServiceReferenceSpec> serviceReferences) {
+        if (!taskRequiredServices.hasServiceReferences()) {
+            BuildServiceRegistryInternal buildServiceRegistry = getBuildServiceRegistry();
+            List<? extends BuildServiceProvider<?, ?>> asConsumedServices = serviceReferences.stream()
+                .map(it -> buildServiceRegistry.consume(it.getBuildServiceName(), it.getBuildServiceType()))
+                .collect(Collectors.toList());
+            taskRequiredServices.acceptServiceReferences(asConsumedServices);
+        }
+    }
+
+    @Override
     public List<ResourceLock> getSharedResources() {
         return getBuildServiceRegistry().getSharedResources(taskRequiredServices.getElements());
     }
 
+    private void notifyConventionAccess(String invocationDescription) {
+        taskExecutionAccessChecker.notifyConventionAccess(this, invocationDescription);
+    }
+
     private BuildServiceRegistryInternal getBuildServiceRegistry() {
         return getServices().get(BuildServiceRegistryInternal.class);
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/CompositeDomainObjectSet.java b/subprojects/core/src/main/java/org/gradle/api/internal/CompositeDomainObjectSet.java
index 9d215d2..ebec154 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/CompositeDomainObjectSet.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/CompositeDomainObjectSet.java
@@ -41,7 +41,6 @@ public class CompositeDomainObjectSet<T> extends DelegatingDomainObjectSet<T> im
     private final Spec<T> uniqueSpec = new ItemIsUniqueInCompositeSpec();
     private final Spec<T> notInSpec = new ItemNotInCompositeSpec();
 
-    private final DefaultDomainObjectSet<T> backingSet;
     private final CollectionCallbackActionDecorator callbackActionDecorator;
 
     @SafeVarargs
@@ -52,17 +51,16 @@ public static <T> CompositeDomainObjectSet<T> create(Class<T> type, DomainObject
 
     @SafeVarargs
     public static <T> CompositeDomainObjectSet<T> create(Class<T> type, CollectionCallbackActionDecorator callbackActionDecorator, DomainObjectCollection<? extends T>... collections) {
-        DefaultDomainObjectSet<T> backingSet = new DefaultDomainObjectSet<T>(type, new DomainObjectCompositeCollection<T>(), callbackActionDecorator);
-        CompositeDomainObjectSet<T> out = new CompositeDomainObjectSet<T>(backingSet, callbackActionDecorator);
+        DefaultDomainObjectSet<T> delegate = new DefaultDomainObjectSet<T>(type, new DomainObjectCompositeCollection<T>(), callbackActionDecorator);
+        CompositeDomainObjectSet<T> out = new CompositeDomainObjectSet<T>(delegate, callbackActionDecorator);
         for (DomainObjectCollection<? extends T> c : collections) {
             out.addCollection(c);
         }
         return out;
     }
 
-    private CompositeDomainObjectSet(DefaultDomainObjectSet<T> backingSet, CollectionCallbackActionDecorator callbackActionDecorator) {
-        super(backingSet);
-        this.backingSet = backingSet;
+    private CompositeDomainObjectSet(DefaultDomainObjectSet<T> delegate, CollectionCallbackActionDecorator callbackActionDecorator) {
+        super(delegate);
         this.callbackActionDecorator = callbackActionDecorator;
     }
 
@@ -89,9 +87,14 @@ public boolean isSatisfiedBy(T element) {
         }
     }
 
+    @Override
+    protected DefaultDomainObjectSet<T> getDelegate() {
+        return (DefaultDomainObjectSet<T>) super.getDelegate();
+    }
+
     @SuppressWarnings("unchecked")
     protected DomainObjectCompositeCollection<T> getStore() {
-        return (DomainObjectCompositeCollection) this.backingSet.getStore();
+        return (DomainObjectCompositeCollection) getDelegate().getStore();
     }
 
     @Override
@@ -110,13 +113,13 @@ public void addCollection(DomainObjectCollection<? extends T> collection) {
             collection.all(new InternalAction<T>() {
                 @Override
                 public void execute(T t) {
-                    backingSet.getEventRegister().fireObjectAdded(t);
+                    getDelegate().getEventRegister().fireObjectAdded(t);
                 }
             });
             collection.whenObjectRemoved(new Action<T>() {
                 @Override
                 public void execute(T t) {
-                    backingSet.getEventRegister().fireObjectRemoved(t);
+                    getDelegate().getEventRegister().fireObjectRemoved(t);
                 }
             });
         }
@@ -125,7 +128,7 @@ public void execute(T t) {
     public void removeCollection(DomainObjectCollection<? extends T> collection) {
         getStore().removeComposited(collection);
         for (T item : collection) {
-            backingSet.getEventRegister().fireObjectRemoved(item);
+            getDelegate().getEventRegister().fireObjectRemoved(item);
         }
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/ConventionTask.java b/subprojects/core/src/main/java/org/gradle/api/internal/ConventionTask.java
index 54a9faa..9848346 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/ConventionTask.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/ConventionTask.java
@@ -20,6 +20,7 @@
 import org.gradle.api.DefaultTask;
 import org.gradle.api.Task;
 import org.gradle.api.tasks.Internal;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.extensibility.ConventionAwareHelper;
 import org.gradle.work.DisableCachingByDefault;
 
@@ -44,7 +45,8 @@ public Task conventionMapping(String property, Closure mapping) {
     @SuppressWarnings("deprecation")
     public ConventionMapping getConventionMapping() {
         if (conventionMapping == null) {
-            conventionMapping = new ConventionAwareHelper(this, getConvention());
+            org.gradle.api.plugins.Convention convention = DeprecationLogger.whileDisabled(this::getConvention);
+            conventionMapping = new ConventionAwareHelper(this, convention);
         }
         return conventionMapping;
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/DefaultDomainObjectCollection.java b/subprojects/core/src/main/java/org/gradle/api/internal/DefaultDomainObjectCollection.java
index d2a75d0..98428d5 100755
--- a/subprojects/core/src/main/java/org/gradle/api/internal/DefaultDomainObjectCollection.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/DefaultDomainObjectCollection.java
@@ -23,7 +23,7 @@
 import org.gradle.api.internal.collections.CollectionFilter;
 import org.gradle.api.internal.collections.DefaultCollectionEventRegister;
 import org.gradle.api.internal.collections.ElementSource;
-import org.gradle.api.internal.collections.FilteredCollection;
+import org.gradle.api.internal.collections.FilteredElementSource;
 import org.gradle.api.internal.provider.CollectionProviderInternal;
 import org.gradle.api.internal.provider.DefaultListProperty;
 import org.gradle.api.internal.provider.PropertyHost;
@@ -101,7 +101,7 @@ protected <S extends T> ElementSource<S> filteredStore(final CollectionFilter<S>
     }
 
     protected <S extends T> ElementSource<S> filteredStore(CollectionFilter<S> filter, ElementSource<T> elementSource) {
-        return new FilteredCollection<T, S>(elementSource, filter);
+        return new FilteredElementSource<T, S>(elementSource, filter);
     }
 
     protected <S extends T> CollectionEventRegister<S> filteredEvents(CollectionFilter<S> filter) {
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/DefaultNamedDomainObjectCollection.java b/subprojects/core/src/main/java/org/gradle/api/internal/DefaultNamedDomainObjectCollection.java
index 5436344..7518916 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/DefaultNamedDomainObjectCollection.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/DefaultNamedDomainObjectCollection.java
@@ -775,7 +775,7 @@ public Class<?> getType() {
     }
 
     @Nullable
-    protected NamedDomainObjectProvider<? extends T> findDomainObject(String name) {
+    private NamedDomainObjectProvider<? extends T> findDomainObject(String name) {
         NamedDomainObjectProvider<? extends T> provider = searchForDomainObject(name);
         // Run the rules and try to find something again.
         if (provider == null) {
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/DefaultNamedDomainObjectList.java b/subprojects/core/src/main/java/org/gradle/api/internal/DefaultNamedDomainObjectList.java
index 6d49c95..99941ee 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/DefaultNamedDomainObjectList.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/DefaultNamedDomainObjectList.java
@@ -20,7 +20,7 @@
 import org.gradle.api.Namer;
 import org.gradle.api.internal.collections.CollectionFilter;
 import org.gradle.api.internal.collections.ElementSource;
-import org.gradle.api.internal.collections.FilteredList;
+import org.gradle.api.internal.collections.FilteredIndexedElementSource;
 import org.gradle.api.internal.collections.IndexedElementSource;
 import org.gradle.api.internal.collections.ListElementSource;
 import org.gradle.api.specs.Spec;
@@ -130,7 +130,7 @@ public List<T> subList(int fromIndex, int toIndex) {
 
     @Override
     protected <S extends T> IndexedElementSource<S> filteredStore(CollectionFilter<S> filter, ElementSource<T> elementSource) {
-        return new FilteredList<T, S>(elementSource, filter);
+        return new FilteredIndexedElementSource<T, S>(elementSource, filter);
     }
 
     @Override
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/DelegatingDomainObjectSet.java b/subprojects/core/src/main/java/org/gradle/api/internal/DelegatingDomainObjectSet.java
index ea55a26..72f773b 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/DelegatingDomainObjectSet.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/DelegatingDomainObjectSet.java
@@ -29,10 +29,14 @@
 import java.util.Set;
 
 public class DelegatingDomainObjectSet<T> implements DomainObjectSet<T> {
-    private final DomainObjectSet<T> backingSet;
+    private final DomainObjectSet<T> delegate;
 
-    public DelegatingDomainObjectSet(DomainObjectSet<T> backingSet) {
-        this.backingSet = backingSet;
+    public DelegatingDomainObjectSet(DomainObjectSet<T> delegate) {
+        this.delegate = delegate;
+    }
+
+    protected DomainObjectSet<T> getDelegate() {
+        return delegate;
     }
 
     @Override
@@ -42,17 +46,17 @@ public DomainObjectSet<T> matching(Closure spec) {
 
     @Override
     public DomainObjectSet<T> matching(Spec<? super T> spec) {
-        return backingSet.matching(spec);
+        return delegate.matching(spec);
     }
 
     @Override
     public <S extends T> DomainObjectSet<S> withType(Class<S> type) {
-        return backingSet.withType(type);
+        return delegate.withType(type);
     }
 
     @Override
     public void all(Action<? super T> action) {
-        backingSet.all(action);
+        delegate.all(action);
     }
 
     @Override
@@ -62,12 +66,12 @@ public void all(Closure action) {
 
     @Override
     public void configureEach(Action<? super T> action) {
-        backingSet.configureEach(action);
+        delegate.configureEach(action);
     }
 
     @Override
     public Action<? super T> whenObjectAdded(Action<? super T> action) {
-        return backingSet.whenObjectAdded(action);
+        return delegate.whenObjectAdded(action);
     }
 
     @Override
@@ -77,7 +81,7 @@ public void whenObjectAdded(Closure action) {
 
     @Override
     public Action<? super T> whenObjectRemoved(Action<? super T> action) {
-        return backingSet.whenObjectRemoved(action);
+        return delegate.whenObjectRemoved(action);
     }
 
     @Override
@@ -87,7 +91,7 @@ public void whenObjectRemoved(Closure action) {
 
     @Override
     public <S extends T> DomainObjectCollection<S> withType(Class<S> type, Action<? super S> configureAction) {
-        return backingSet.withType(type, configureAction);
+        return delegate.withType(type, configureAction);
     }
 
     @Override
@@ -97,81 +101,81 @@ public <S extends T> DomainObjectCollection<S> withType(Class<S> type, Closure c
 
     @Override
     public void addLater(Provider<? extends T> provider) {
-        backingSet.addLater(provider);
+        delegate.addLater(provider);
     }
 
     @Override
     public void addAllLater(Provider<? extends Iterable<T>> provider) {
-        backingSet.addAllLater(provider);
+        delegate.addAllLater(provider);
     }
 
     @Override
     public boolean add(T o) {
-        return backingSet.add(o);
+        return delegate.add(o);
     }
 
     @Override
     public boolean addAll(Collection<? extends T> c) {
-        return backingSet.addAll(c);
+        return delegate.addAll(c);
     }
 
     @Override
     public void clear() {
-        backingSet.clear();
+        delegate.clear();
     }
 
     @Override
     public boolean contains(Object o) {
-        return backingSet.contains(o);
+        return delegate.contains(o);
     }
 
     @Override
     public boolean containsAll(Collection<?> c) {
-        return backingSet.containsAll(c);
+        return delegate.containsAll(c);
     }
 
     @Override
     public boolean isEmpty() {
-        return backingSet.isEmpty();
+        return delegate.isEmpty();
     }
 
     @Override
     public Iterator<T> iterator() {
-        return backingSet.iterator();
+        return delegate.iterator();
     }
 
     @Override
     public boolean remove(Object o) {
-        return backingSet.remove(o);
+        return delegate.remove(o);
     }
 
     @Override
     public boolean removeAll(Collection<?> c) {
-        return backingSet.removeAll(c);
+        return delegate.removeAll(c);
     }
 
     @Override
     public boolean retainAll(Collection<?> c) {
-        return backingSet.retainAll(c);
+        return delegate.retainAll(c);
     }
 
     @Override
     public int size() {
-        return backingSet.size();
+        return delegate.size();
     }
 
     @Override
     public Object[] toArray() {
-        return backingSet.toArray();
+        return delegate.toArray();
     }
 
     @Override
     public <T> T[] toArray(T[] a) {
-        return backingSet.toArray(a);
+        return delegate.toArray(a);
     }
 
     @Override
     public Set<T> findAll(Closure spec) {
-        return backingSet.findAll(spec);
+        return delegate.findAll(spec);
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/DelegatingNamedDomainObjectSet.java b/subprojects/core/src/main/java/org/gradle/api/internal/DelegatingNamedDomainObjectSet.java
new file mode 100644
index 0000000..b3b7c56
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/DelegatingNamedDomainObjectSet.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2023 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.api.internal;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectCollectionSchema;
+import org.gradle.api.NamedDomainObjectProvider;
+import org.gradle.api.NamedDomainObjectSet;
+import org.gradle.api.Namer;
+import org.gradle.api.Rule;
+import org.gradle.api.UnknownDomainObjectException;
+import org.gradle.api.specs.Spec;
+
+import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+/**
+ * A {@Link NamedDomainObjectSet} which delegates all methods to a provided delegate.
+ */
+public class DelegatingNamedDomainObjectSet<T> extends DelegatingDomainObjectSet<T> implements NamedDomainObjectSet<T> {
+
+    public DelegatingNamedDomainObjectSet(NamedDomainObjectSet<T> backingSet) {
+        super(backingSet);
+    }
+
+    @Override
+    protected NamedDomainObjectSet<T> getDelegate() {
+        return (NamedDomainObjectSet<T>) super.getDelegate();
+    }
+
+    @Override
+    public <S extends T> NamedDomainObjectSet<S> withType(Class<S> type) {
+        return getDelegate().withType(type);
+    }
+
+    @Override
+    public NamedDomainObjectSet<T> matching(Spec<? super T> spec) {
+        return getDelegate().matching(spec);
+    }
+
+    @Override
+    public NamedDomainObjectSet<T> matching(Closure spec) {
+        return getDelegate().matching(spec);
+    }
+
+    @Override
+    public NamedDomainObjectProvider<T> named(String name) throws UnknownDomainObjectException {
+        return getDelegate().named(name);
+    }
+
+    @Override
+    public NamedDomainObjectProvider<T> named(String name, Action<? super T> configurationAction) throws UnknownDomainObjectException {
+        return getDelegate().named(name, configurationAction);
+    }
+
+    @Override
+    public <S extends T> NamedDomainObjectProvider<S> named(String name, Class<S> type) throws UnknownDomainObjectException {
+        return getDelegate().named(name, type);
+    }
+
+    @Override
+    public <S extends T> NamedDomainObjectProvider<S> named(String name, Class<S> type, Action<? super S> configurationAction) throws UnknownDomainObjectException {
+        return getDelegate().named(name, type, configurationAction);
+    }
+
+    @Override
+    public Rule addRule(String description, Closure ruleAction) {
+        return getDelegate().addRule(description, ruleAction);
+    }
+
+    @Override
+    public Rule addRule(String description, Action<String> ruleAction) {
+        return getDelegate().addRule(description, ruleAction);
+    }
+
+    @Override
+    public Rule addRule(Rule rule) {
+        return getDelegate().addRule(rule);
+    }
+
+    @Override
+    public T findByName(String name) {
+        return getDelegate().findByName(name);
+    }
+
+    @Override
+    public SortedMap<String, T> getAsMap() {
+        return getDelegate().getAsMap();
+    }
+
+    @Override
+    public NamedDomainObjectCollectionSchema getCollectionSchema() {
+        return getDelegate().getCollectionSchema();
+    }
+
+    @Override
+    public T getAt(String name) throws UnknownDomainObjectException {
+        return getDelegate().getAt(name);
+    }
+
+    @Override
+    public T getByName(String name) throws UnknownDomainObjectException {
+        return getDelegate().getByName(name);
+    }
+
+    @Override
+    public T getByName(String name, Closure configureClosure) throws UnknownDomainObjectException {
+        return getDelegate().getByName(name, configureClosure);
+    }
+
+    @Override
+    public T getByName(String name, Action<? super T> configureAction) throws UnknownDomainObjectException {
+        return getDelegate().getByName(name, configureAction);
+    }
+
+    @Override
+    public Namer<T> getNamer() {
+        return getDelegate().getNamer();
+    }
+
+    @Override
+    public SortedSet<String> getNames() {
+        return getDelegate().getNames();
+    }
+
+    @Override
+    public List<Rule> getRules() {
+        return getDelegate().getRules();
+    }
+
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/TaskInternal.java b/subprojects/core/src/main/java/org/gradle/api/internal/TaskInternal.java
index dac8098..d43e815 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/TaskInternal.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/TaskInternal.java
@@ -22,6 +22,7 @@
 import org.gradle.api.internal.tasks.InputChangesAwareTaskAction;
 import org.gradle.api.internal.tasks.TaskRequiredServices;
 import org.gradle.api.internal.tasks.TaskStateInternal;
+import org.gradle.api.internal.tasks.properties.ServiceReferenceSpec;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.Internal;
 import org.gradle.api.tasks.TaskDependency;
@@ -34,6 +35,7 @@
 import java.io.File;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 
 public interface TaskInternal extends Task, Configurable<Task> {
 
@@ -109,6 +111,8 @@ public interface TaskInternal extends Task, Configurable<Task> {
     @Internal
     TaskRequiredServices getRequiredServices();
 
+    void acceptServiceReferences(Set<ServiceReferenceSpec> serviceReferences);
+
     /**
      * <p>Gets the shared resources required by this task.</p>
      */
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/TaskOutputsEnterpriseInternal.java b/subprojects/core/src/main/java/org/gradle/api/internal/TaskOutputsEnterpriseInternal.java
new file mode 100755
index 0000000..18274e8
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/TaskOutputsEnterpriseInternal.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010 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.api.internal;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.NonNullApi;
+
+/**
+ * Gradle Enterprise specific extensions of {@link TaskOutputsInternal}.
+ * <p>
+ * This class exists to hide these additional methods from the public API since {@link DefaultTask#getOutputs()}
+ * returns {@link TaskOutputsInternal} rather than {@link org.gradle.api.tasks.TaskOutputs}.
+ *
+ * @since 8.2
+ */
+@NonNullApi
+public interface TaskOutputsEnterpriseInternal extends TaskOutputsInternal {
+
+    /**
+     * Whether the task's outputs should be stored in the build cache.
+     */
+    boolean getStoreInCache();
+
+    /**
+     * Avoid storing the task's outputs in the build cache.
+     * <p>
+     * This does not prevent the task from being up-to-date.
+     */
+    void doNotStoreInCache();
+
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/TypedDomainObjectContainerWrapper.java b/subprojects/core/src/main/java/org/gradle/api/internal/TypedDomainObjectContainerWrapper.java
index 6fa6b8e..f1f681e 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/TypedDomainObjectContainerWrapper.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/TypedDomainObjectContainerWrapper.java
@@ -18,39 +18,23 @@
 
 import groovy.lang.Closure;
 import org.gradle.api.Action;
-import org.gradle.api.DomainObjectCollection;
-import org.gradle.api.NamedDomainObjectProvider;
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.NamedDomainObjectCollectionSchema;
 import org.gradle.api.NamedDomainObjectContainer;
-import org.gradle.api.NamedDomainObjectSet;
-import org.gradle.api.Namer;
-import org.gradle.api.Rule;
-import org.gradle.api.UnknownDomainObjectException;
-import org.gradle.api.provider.Provider;
-import org.gradle.api.specs.Spec;
+import org.gradle.api.NamedDomainObjectProvider;
 import org.gradle.internal.metaobject.MethodAccess;
 import org.gradle.internal.metaobject.MethodMixIn;
 import org.gradle.internal.metaobject.PropertyAccess;
 import org.gradle.internal.metaobject.PropertyMixIn;
 import org.gradle.util.internal.ConfigureUtil;
 
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-
-public class TypedDomainObjectContainerWrapper<U> implements NamedDomainObjectContainer<U>, MethodMixIn, PropertyMixIn {
+public class TypedDomainObjectContainerWrapper<U> extends DelegatingNamedDomainObjectSet<U> implements NamedDomainObjectContainer<U>, MethodMixIn, PropertyMixIn {
     private final Class<U> type;
     private final AbstractPolymorphicDomainObjectContainer<? super U> parent;
-    private final NamedDomainObjectSet<U> delegate;
 
     public TypedDomainObjectContainerWrapper(Class<U> type, AbstractPolymorphicDomainObjectContainer<? super U> parent) {
+        super(parent.withType(type));
         this.parent = parent;
         this.type = type;
-        this.delegate = parent.withType(type);
     }
 
     @Override
@@ -99,238 +83,4 @@ public NamedDomainObjectProvider<U> register(String name) throws InvalidUserData
         return parent.register(name, type);
     }
 
-    @Override
-    public Set<U> findAll(Closure spec) {
-        return delegate.findAll(spec);
-    }
-
-    @Override
-    public NamedDomainObjectSet<U> matching(Closure spec) {
-        return delegate.matching(spec);
-    }
-
-    @Override
-    public NamedDomainObjectProvider<U> named(String name) throws UnknownDomainObjectException {
-        return delegate.named(name);
-    }
-
-    @Override
-    public NamedDomainObjectProvider<U> named(String name, Action<? super U> configurationAction) throws UnknownDomainObjectException {
-        return delegate.named(name, configurationAction);
-    }
-
-    @Override
-    public <S extends U> NamedDomainObjectProvider<S> named(String name, Class<S> type) throws UnknownDomainObjectException {
-        return delegate.named(name, type);
-    }
-
-    @Override
-    public <S extends U> NamedDomainObjectProvider<S> named(String name, Class<S> type, Action<? super S> configurationAction) throws UnknownDomainObjectException {
-        return delegate.named(name, type, configurationAction);
-    }
-
-    @Override
-    public NamedDomainObjectSet<U> matching(Spec<? super U> spec) {
-        return delegate.matching(spec);
-    }
-
-    @Override
-    public <S extends U> NamedDomainObjectSet<S> withType(Class<S> type) {
-        return delegate.withType(type);
-    }
-
-    @Override
-    public boolean add(U e) {
-        return delegate.add(e);
-    }
-
-    @Override
-    public void addLater(Provider<? extends U> provider) {
-        delegate.addLater(provider);
-    }
-
-    @Override
-    public void addAllLater(Provider<? extends Iterable<U>> provider) {
-        delegate.addAllLater(provider);
-    }
-
-    @Override
-    public boolean addAll(Collection<? extends U> c) {
-        return delegate.addAll(c);
-    }
-
-    @Override
-    public Rule addRule(String description, Closure ruleAction) {
-        return delegate.addRule(description, ruleAction);
-    }
-
-    @Override
-    public Rule addRule(String description, Action<String> ruleAction) {
-        return delegate.addRule(description, ruleAction);
-    }
-
-    @Override
-    public Rule addRule(Rule rule) {
-        return delegate.addRule(rule);
-    }
-
-    @Override
-    public U findByName(String name) {
-        return delegate.findByName(name);
-    }
-
-    @Override
-    public SortedMap<String, U> getAsMap() {
-        return delegate.getAsMap();
-    }
-
-    @Override
-    public NamedDomainObjectCollectionSchema getCollectionSchema() {
-        return delegate.getCollectionSchema();
-    }
-
-    @Override
-    public U getAt(String name) throws UnknownDomainObjectException {
-        return delegate.getAt(name);
-    }
-
-    @Override
-    public U getByName(String name) throws UnknownDomainObjectException {
-        return delegate.getByName(name);
-    }
-
-    @Override
-    public U getByName(String name, Closure configureClosure) throws UnknownDomainObjectException {
-        return delegate.getByName(name, configureClosure);
-    }
-
-    @Override
-    public U getByName(String name, Action<? super U> configureAction) throws UnknownDomainObjectException {
-        return delegate.getByName(name, configureAction);
-    }
-
-    @Override
-    public Namer<U> getNamer() {
-        return delegate.getNamer();
-    }
-
-    @Override
-    public SortedSet<String> getNames() {
-        return delegate.getNames();
-    }
-
-    @Override
-    public List<Rule> getRules() {
-        return delegate.getRules();
-    }
-
-    @Override
-    public void all(Action<? super U> action) {
-        delegate.all(action);
-    }
-
-    @Override
-    public void all(Closure action) {
-        delegate.all(action);
-    }
-
-    @Override
-    public void configureEach(Action<? super U> action) {
-        delegate.configureEach(action);
-    }
-
-    @Override
-    public Action<? super U> whenObjectAdded(Action<? super U> action) {
-        return delegate.whenObjectAdded(action);
-    }
-
-    @Override
-    public void whenObjectAdded(Closure action) {
-        delegate.whenObjectAdded(action);
-    }
-
-    @Override
-    public Action<? super U> whenObjectRemoved(Action<? super U> action) {
-        return delegate.whenObjectRemoved(action);
-    }
-
-    @Override
-    public void whenObjectRemoved(Closure action) {
-        delegate.whenObjectRemoved(action);
-    }
-
-    @Override
-    public <S extends U> DomainObjectCollection<S> withType(Class<S> type, Action<? super S> configureAction) {
-        return delegate.withType(type, configureAction);
-    }
-
-    @Override
-    public <S extends U> DomainObjectCollection<S> withType(Class<S> type, Closure configureClosure) {
-        return delegate.withType(type, configureClosure);
-    }
-
-    @Override
-    public void clear() {
-        delegate.clear();
-    }
-
-    @Override
-    public boolean contains(Object o) {
-        return delegate.contains(o);
-    }
-
-    @Override
-    public boolean containsAll(Collection<?> c) {
-        return delegate.containsAll(c);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        return delegate.equals(o);
-    }
-
-    @Override
-    public int hashCode() {
-        return delegate.hashCode();
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return delegate.isEmpty();
-    }
-
-    @Override
-    public Iterator<U> iterator() {
-        return delegate.iterator();
-    }
-
-    @Override
-    public boolean remove(Object o) {
-        return delegate.remove(o);
-    }
-
-    @Override
-    public boolean removeAll(Collection<?> c) {
-        return delegate.removeAll(c);
-    }
-
-    @Override
-    public boolean retainAll(Collection<?> c) {
-        return delegate.retainAll(c);
-    }
-
-    @Override
-    public int size() {
-        return delegate.size();
-    }
-
-    @Override
-    public Object[] toArray() {
-        return delegate.toArray();
-    }
-
-    @Override
-    public <T> T[] toArray(T[] a) {
-        return delegate.toArray(a);
-    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/DefaultBuildIdentifier.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/DefaultBuildIdentifier.java
index d917615..be378bb 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/DefaultBuildIdentifier.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/DefaultBuildIdentifier.java
@@ -18,26 +18,42 @@
 
 import com.google.common.base.Objects;
 import org.gradle.api.artifacts.component.BuildIdentifier;
+import org.gradle.internal.deprecation.DeprecationLogger;
+import org.gradle.util.Path;
 
 public class DefaultBuildIdentifier implements BuildIdentifier {
-    public static final BuildIdentifier ROOT = new DefaultBuildIdentifier(":");
-    private final String name;
+    public static final BuildIdentifier ROOT = new DefaultBuildIdentifier(Path.ROOT);
+    private final Path buildPath;
 
-    public DefaultBuildIdentifier(String name) {
-        this.name = name;
+    public DefaultBuildIdentifier(Path buildPath) {
+        if (!buildPath.isAbsolute()) {
+            throw new IllegalArgumentException("Build path must be absolute: " + buildPath);
+        }
+
+        this.buildPath = buildPath;
     }
 
-    public String getIdName() {
-        return name;
+    @Override
+    public String getBuildPath() {
+        return buildPath.toString();
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public String getName() {
-        return name;
+        DeprecationLogger.deprecateMethod(BuildIdentifier.class, "getName()")
+            .withAdvice("Use getBuildPath() to get a unique identifier for the build.")
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "build_identifier_name_and_current_deprecation")
+            .nagUser();
+
+        return buildPath.getName() == null ? ":" : buildPath.getName();
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public boolean isCurrentBuild() {
+        nagAboutDeprecatedIsCurrentBuild();
         return true;
     }
 
@@ -50,16 +66,24 @@ public boolean equals(Object o) {
             return false;
         }
         DefaultBuildIdentifier that = (DefaultBuildIdentifier) o;
-        return name.equals(that.name);
+        return buildPath.equals(that.buildPath);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(name);
+        return Objects.hashCode(buildPath);
     }
 
     @Override
     public String toString() {
-        return "build '" + name + "'";
+        return "build '" + buildPath + "'";
+    }
+
+    protected static void nagAboutDeprecatedIsCurrentBuild() {
+        DeprecationLogger.deprecateMethod(BuildIdentifier.class, "isCurrentBuild()")
+            .withAdvice("Use getBuildPath() to get a unique identifier for the build.")
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "build_identifier_name_and_current_deprecation")
+            .nagUser();
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/DependencyResolutionServices.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/DependencyResolutionServices.java
index a455193..e446efc 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/DependencyResolutionServices.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/DependencyResolutionServices.java
@@ -23,6 +23,13 @@
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.model.ObjectFactory;
 
+/**
+ * Provides access to services required for dependency resolution.
+ * <p>
+ * Note that changes to this type, even seemingly safe ones such as narrowing the return types, can
+ * cause problems for IDEs (the IDE tests should fail upon such changes, alerting us to
+ * this problem).  Thus, this internal API should be treated as semi-public.
+ */
 public interface DependencyResolutionServices {
     RepositoryHandler getResolveRepositoryHandler();
 
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/ForeignBuildIdentifier.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/ForeignBuildIdentifier.java
index 87b9a34..4bcebf8 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/ForeignBuildIdentifier.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/ForeignBuildIdentifier.java
@@ -16,24 +16,20 @@
 
 package org.gradle.api.internal.artifacts;
 
+import org.gradle.util.Path;
+
 /**
  * A build that is not the current build. This type exists only to provide an answer to {@link #isCurrentBuild()}, which should not exist.
  */
 public class ForeignBuildIdentifier extends DefaultBuildIdentifier {
-    private final String legacyName;
 
-    public ForeignBuildIdentifier(String name, String legacyName) {
-        super(name);
-        this.legacyName = legacyName;
-    }
-
-    @Override
-    public String getName() {
-        return legacyName;
+    public ForeignBuildIdentifier(Path buildPath) {
+        super(buildPath);
     }
 
     @Override
     public boolean isCurrentBuild() {
+        nagAboutDeprecatedIsCurrentBuild();
         return false;
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRole.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRole.java
new file mode 100644
index 0000000..b146a66
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRole.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 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.api.internal.artifacts.configurations;
+
+/**
+ * Defines how a {@link org.gradle.api.artifacts.Configuration} is intended to be used.
+ *
+ * Standard roles are defined in {@link ConfigurationRoles}.
+ *
+ * @since 8.1
+ */
+public interface ConfigurationRole {
+    /**
+     * Returns a human-readable name for this role.
+     */
+    String getName();
+
+    boolean isConsumable();
+    boolean isResolvable();
+    boolean isDeclarable();
+    boolean isConsumptionDeprecated();
+    boolean isResolutionDeprecated();
+    boolean isDeclarationAgainstDeprecated();
+
+    /**
+     * Obtains a human-readable summary of the usage allowed by the given role.
+     */
+    default String describeUsage() {
+        return UsageDescriber.describeRole(this);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRoles.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRoles.java
new file mode 100644
index 0000000..b91b8e3
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRoles.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2022 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.api.internal.artifacts.configurations;
+
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Defines {@link ConfigurationRole}s representing common allowed usage patterns.
+ *
+ * These should be preferred over defining custom roles; whenever possible.  Use {@link #byUsage(boolean, boolean, boolean)}
+ * to attempt to locate a matching role by its usage characteristics.
+ *
+ * @since 8.1
+ */
+public final class ConfigurationRoles {
+
+    private ConfigurationRoles() {
+        // Private to prevent instantiation.
+    }
+
+    /**
+     * An unrestricted configuration, which can be used for any purpose.
+     *
+     * This is available for backwards compatibility, but should not be used for new configurations.  It is
+     * the default role for configurations created when another more specific role is <strong>not</strong> specified.
+     */
+    @Deprecated
+    public static final ConfigurationRole LEGACY = createNonDeprecatedRole("Legacy", true, true, true);
+
+    /**
+     * Meant to be used only for consumption by other projects.
+     */
+    public static final ConfigurationRole CONSUMABLE = createNonDeprecatedRole("Consumable", true, false, false);
+
+    /**
+     * Meant to be used only for resolving dependencies.
+     */
+    public static final ConfigurationRole RESOLVABLE = createNonDeprecatedRole("Resolvable", false, true, false);
+
+    /**
+     * Meant as a temporary solution for situations where we need to declare dependencies against a resolvable configuration.
+     *
+     * These situations should be updated to use a separate bucket configuration for declaring dependencies and extend it with a separate resolvable configuration.
+     */
+    @Deprecated
+    public static final ConfigurationRole RESOLVABLE_BUCKET = createNonDeprecatedRole("Resolvable Bucket", false, true, true);
+
+    /**
+     * Meant as a temporary solution for situations where we need to declare dependencies against a consumable configuration.
+     *
+     * This <strong>SHOULD NOT</strong> be necessary, and is a symptom of an over-permissive configuration.
+     */
+    @Deprecated
+    public static final ConfigurationRole CONSUMABLE_BUCKET = createNonDeprecatedRole("Consumable Bucket", true, false, true);
+
+    /**
+     * Meant to be used only for declaring dependencies.
+     *
+     * AKA {@code DECLARABLE}.
+     */
+    public static final ConfigurationRole BUCKET = createNonDeprecatedRole("Bucket", false, false, true);
+
+    /**
+     * Creates a new role which is not deprecated for any usage.
+     */
+    private static ConfigurationRole createNonDeprecatedRole(String name, boolean consumable, boolean resolvable, boolean declarable) {
+        return new DefaultConfigurationRole(name, consumable, resolvable, declarable, false, false, false);
+    }
+
+    private static final Set<ConfigurationRole> ALL = ImmutableSet.of(
+        LEGACY, CONSUMABLE, RESOLVABLE, RESOLVABLE_BUCKET, CONSUMABLE_BUCKET, BUCKET
+    );
+
+    /**
+     * Locates a pre-defined role allowing the given usage.
+     *
+     * @param consumable whether this role is consumable
+     * @param resolvable whether this role is resolvable
+     * @param declarable whether this role is declarable
+     *
+     * @return the role enum token with matching usage characteristics, if one exists; otherwise {@link Optional#empty()}
+     */
+    public static Optional<ConfigurationRole> byUsage(boolean consumable, boolean resolvable, boolean declarable) {
+        for (ConfigurationRole role : ALL) {
+            if (role.isConsumable() == consumable && role.isResolvable() == resolvable && role.isDeclarable() == declarable) {
+                return Optional.of(role);
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRolesForMigration.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRolesForMigration.java
new file mode 100644
index 0000000..b1c7705
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRolesForMigration.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.configurations;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * This class defines a set of configuration roles which each describe an intermediate state between a current role
+ * and a future role which will replace it in the next major Gradle version. These roles represent a narrowing migration
+ * from one role to another by marking usages which are present in the current role but not present in the eventual role
+ * as deprecated.
+ *
+ * <p>The roles here are all meant to be temporary roles used for migration only, to be removed in Gradle 9.0.</p>
+ *
+ * <p>This is meant to only support <strong>narrowing migrations</strong>, that restrict usage that was previously
+ * allowed. The migrations should transition from a role defined in {@link ConfigurationRoles} to another
+ * role defined in {@link ConfigurationRoles}. This is <strong>not</strong> meant to support general-case
+ * migrations from any usage pattern to any other.</p>
+ */
+public final class ConfigurationRolesForMigration {
+
+    private ConfigurationRolesForMigration() {
+        // Private to prevent instantiation.
+    }
+
+    @Deprecated
+    public static final ConfigurationRole LEGACY_TO_RESOLVABLE_BUCKET = difference(ConfigurationRoles.LEGACY, ConfigurationRoles.RESOLVABLE_BUCKET);
+    @Deprecated
+    public static final ConfigurationRole LEGACY_TO_CONSUMABLE = difference(ConfigurationRoles.LEGACY, ConfigurationRoles.CONSUMABLE);
+
+    @Deprecated
+    public static final ConfigurationRole RESOLVABLE_BUCKET_TO_RESOLVABLE = difference(ConfigurationRoles.RESOLVABLE_BUCKET, ConfigurationRoles.RESOLVABLE);
+    @Deprecated
+    public static final ConfigurationRole CONSUMABLE_BUCKET_TO_CONSUMABLE = difference(ConfigurationRoles.CONSUMABLE_BUCKET, ConfigurationRoles.CONSUMABLE);
+
+    /**
+     * A resolvable bucket that will become a bucket in the next major version.
+     */
+    @SuppressWarnings("deprecation")
+    public static final ConfigurationRole RESOLVABLE_BUCKET_TO_BUCKET = difference(ConfigurationRoles.RESOLVABLE_BUCKET, ConfigurationRoles.BUCKET);
+
+    /**
+     * Computes the difference between two roles, such that any usage that is allowed in the
+     * initial role but not allowed in the eventual role will be deprecated in the returned role.
+     */
+    private static ConfigurationRole difference(ConfigurationRole initialRole, ConfigurationRole eventualRole) {
+        Preconditions.checkArgument(
+            !initialRole.isConsumptionDeprecated() && !initialRole.isResolutionDeprecated() && !initialRole.isDeclarationAgainstDeprecated(),
+            "The initial role must not contain deprecated usages."
+        );
+        Preconditions.checkArgument(
+            !eventualRole.isConsumptionDeprecated() && !eventualRole.isResolutionDeprecated() && !eventualRole.isDeclarationAgainstDeprecated(),
+            "The eventual role must not contain deprecated usages."
+        );
+
+        /*
+         * Since we're assuming strictly narrowing usage from a non-deprecated initial role, for each usage we want this migration
+         * role to deprecate a usage iff that usage will change from allowed -> disallowed when migrating from the initial role to the
+         * eventual role.
+         */
+        boolean consumptionDeprecated = initialRole.isConsumable() && !eventualRole.isConsumable();
+        boolean resolutionDeprecated = initialRole.isResolvable() && !eventualRole.isResolvable();
+        boolean declarationAgainstDeprecated = initialRole.isDeclarable() && !eventualRole.isDeclarable();
+
+        return new DefaultConfigurationRole(
+            initialRole.getName(),
+            initialRole.isConsumable(),
+            initialRole.isResolvable(),
+            initialRole.isDeclarable(),
+            consumptionDeprecated,
+            resolutionDeprecated,
+            declarationAgainstDeprecated
+        );
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationRole.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationRole.java
new file mode 100644
index 0000000..cb56014
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationRole.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.configurations;
+
+import java.util.Objects;
+
+/**
+ * Default implementation of {@link ConfigurationRole}.
+ */
+public final class DefaultConfigurationRole implements ConfigurationRole {
+
+    private final String name;
+    private final boolean consumable;
+    private final boolean resolvable;
+    private final boolean declarable;
+    private final boolean consumptionDeprecated;
+    private final boolean resolutionDeprecated;
+    private final boolean declarationDeprecated;
+
+    public DefaultConfigurationRole(
+        String name,
+        boolean consumable,
+        boolean resolvable,
+        boolean declarable,
+        boolean consumptionDeprecated,
+        boolean resolutionDeprecated,
+        boolean declarationDeprecated
+    ) {
+        this.name = name;
+        this.consumable = consumable;
+        this.resolvable = resolvable;
+        this.declarable = declarable;
+        this.consumptionDeprecated = consumptionDeprecated;
+        this.resolutionDeprecated = resolutionDeprecated;
+        this.declarationDeprecated = declarationDeprecated;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean isConsumable() {
+        return consumable;
+    }
+
+    @Override
+    public boolean isResolvable() {
+        return resolvable;
+    }
+
+    @Override
+    public boolean isDeclarable() {
+        return declarable;
+    }
+
+    @Override
+    public boolean isConsumptionDeprecated() {
+        return consumptionDeprecated;
+    }
+
+    @Override
+    public boolean isResolutionDeprecated() {
+        return resolutionDeprecated;
+    }
+
+    @Override
+    public boolean isDeclarationAgainstDeprecated() {
+        return declarationDeprecated;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultConfigurationRole that = (DefaultConfigurationRole) o;
+        return consumable == that.consumable &&
+            resolvable == that.resolvable &&
+            declarable == that.declarable &&
+            consumptionDeprecated == that.consumptionDeprecated &&
+            resolutionDeprecated == that.resolutionDeprecated &&
+            declarationDeprecated == that.declarationDeprecated &&
+            name.equals(that.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, consumable, resolvable, declarable, consumptionDeprecated, resolutionDeprecated, declarationDeprecated);
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/RoleBasedConfigurationContainerInternal.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/RoleBasedConfigurationContainerInternal.java
new file mode 100644
index 0000000..12ed700
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/RoleBasedConfigurationContainerInternal.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.configurations;
+
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.internal.Actions;
+import org.gradle.internal.deprecation.DeprecatableConfiguration;
+
+/**
+ * Extends {@link ConfigurationContainer} with methods that can use {@link ConfigurationRole}s to
+ * define the allowed usage of a configuration at creation time.
+ * <p>
+ * This is an internal API, and is not yet intended for use outside of the Gradle build.
+ */
+public interface RoleBasedConfigurationContainerInternal extends ConfigurationContainer {
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * using the role of {@link ConfigurationRoles#CONSUMABLE}.
+     */
+    default Configuration consumable(String name, boolean lockRole) {
+        return createWithRole(name, ConfigurationRoles.CONSUMABLE, lockRole);
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * using the role of {@link ConfigurationRoles#RESOLVABLE}.
+     */
+    default Configuration resolvable(String name, boolean lockRole) {
+        return createWithRole(name, ConfigurationRoles.RESOLVABLE, lockRole);
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * using the role of {@link ConfigurationRoles#RESOLVABLE_BUCKET}.
+     */
+    @SuppressWarnings("deprecation")
+    default Configuration resolvableBucket(String name, boolean lockRole) {
+        return createWithRole(name, ConfigurationRoles.RESOLVABLE_BUCKET, lockRole);
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * using the role of {@link ConfigurationRoles#BUCKET}.
+     */
+    default Configuration bucket(String name, boolean lockRole) {
+        return createWithRole(name, ConfigurationRoles.BUCKET, lockRole);
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * using the role of {@link ConfigurationRoles#CONSUMABLE} that is <strong>NOT</strong> locked
+     * against further usage mutations.
+     */
+    default Configuration consumable(String name) {
+        return consumable(name, false);
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * using the role of {@link ConfigurationRoles#RESOLVABLE} that is <strong>NOT</strong> locked
+     * against further usage mutations.
+     */
+    default Configuration resolvable(String name) {
+        return resolvable(name, false);
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * using the role of {@link ConfigurationRoles#RESOLVABLE_BUCKET} that is <strong>NOT</strong> locked
+     * against further usage mutations.
+     */
+    @SuppressWarnings("deprecation")
+    default Configuration resolvableBucket(String name) {
+        return resolvableBucket(name, false);
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * using the role of {@link ConfigurationRoles#BUCKET} that is <strong>NOT</strong> locked
+     * against further usage mutations.
+     */
+    default Configuration bucket(String name) {
+        return bucket(name, false);
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #create(String)} using the given role
+     * at creation.
+     *
+     * @param name the name of the configuration
+     * @param role the role defining the configuration's allowed usage
+     * @param lockUsage {@code true} if the configuration's allowed usage should be locked to prevent any changes; {@code false} otherwise
+     * @param configureAction an action to run upon the configuration's creation to configure it
+     * @return the new configuration
+     */
+    Configuration createWithRole(String name, ConfigurationRole role, boolean lockUsage, Action<? super Configuration> configureAction);
+
+    /**
+     * Creates a new configuration in the same manner as {@link #create(String)} using the given role
+     * at creation.
+     *
+     * @param name the name of the configuration
+     * @param role the role defining the configuration's allowed usage
+     * @param lockUsage {@code true} if the configuration's allowed usage should be locked to prevent any changes; {@code false} otherwise
+     * @return the new configuration
+     */
+    default Configuration createWithRole(String name, ConfigurationRole role, boolean lockUsage) {
+        return createWithRole(name, role, lockUsage, Actions.doNothing());
+    }
+
+    /**
+     * Creates a new configuration in the same manner as {@link #create(String)} using the given role
+     * at creation and configuring it with the given action, without automatically locking the configuration's allowed usage.
+     *
+     * @param name the name of the configuration
+     * @param role the role defining the configuration's allowed usage
+     * @param configureAction an action to run upon the configuration's creation to configure it
+     * @return the new configuration
+     */
+    default Configuration createWithRole(String name, ConfigurationRole role, Action<? super Configuration> configureAction) {
+        return createWithRole(name, role, false, configureAction);
+    }
+
+
+    /**
+     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
+     * without locking the configuration's allowed usage.
+     */
+    default Configuration createWithRole(String name, ConfigurationRole role) {
+        return createWithRole(name, role, false);
+    }
+
+    /**
+     * Runs the same proces as {@link #maybeCreateWithRole(String, ConfigurationRole, boolean, boolean)}, without locking the configuration's allowed usage
+     * or asserting that an existing matching configuration's usage matches the given role.
+     *
+     * @param name the name of the configuration
+     * @param role the role defining the configuration's allowed usage
+     * @return the matching or new configuration
+     */
+    default Configuration maybeCreateWithRole(String name, ConfigurationRole role) {
+        return maybeCreateWithRole(name, role, false, false);
+    }
+
+    /**
+     * If it does not already exist, creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)};
+     * if the configuration does already exist, this method will <strong>NOT</strong>> change anything about its allowed usage or its role,
+     * but <strong>CAN</strong> optionally confirm that the current usage of the configuration
+     * matches the given role and/or prevent any further changes to the configuration's allowed usage.
+     *
+     * @param name the name of the configuration
+     * @param role the role defining the configuration's allowed usage
+     * @param lockUsage {@code true} if the configuration's allowed usage should be locked to prevent any changes; {@code false} otherwise
+     * @param assertInRole {@code true} if the configuration's current usage should be confirmed to match that specified by the given role
+     * @return the matching or new configuration
+     */
+    default Configuration maybeCreateWithRole(String name, ConfigurationRole role, boolean lockUsage, boolean assertInRole) {
+        DeprecatableConfiguration configuration = (DeprecatableConfiguration) findByName(name);
+        if (configuration == null) {
+            return createWithRole(name, role, lockUsage);
+        } else {
+            if (assertInRole) {
+                RoleChecker.assertIsInRole(configuration, role);
+            }
+            if (lockUsage) {
+               configuration.preventUsageMutation();
+            }
+            return configuration;
+        }
+    }
+
+    /**
+     * This static util class hides methods internal to the {@code default} methods in the {@link RoleBasedConfigurationContainerInternal} interface.
+     */
+    abstract class RoleChecker {
+        private RoleChecker() { /* not instantiable */ }
+
+        /**
+         * Checks that the current allowed usage of a configuration is the same as that specified by a given role.
+         *
+         * @param configuration the configuration to check
+         * @param role the role to check against
+         * @return {@code true} if so; {@code false} otherwise
+         */
+        public static boolean isUsageConsistentWithRole(DeprecatableConfiguration configuration, ConfigurationRole role) {
+            return (role.isConsumable() == configuration.isCanBeConsumed())
+                    && (role.isResolvable() == configuration.isCanBeResolved())
+                    && (role.isDeclarable() == configuration.isCanBeDeclared())
+                    && (role.isConsumptionDeprecated() == configuration.isDeprecatedForConsumption())
+                    && (role.isResolutionDeprecated() == configuration.isDeprecatedForResolution())
+                    && (role.isDeclarationAgainstDeprecated() == configuration.isDeprecatedForDeclarationAgainst());
+        }
+
+        /**
+         * Checks that the current allowed usage of a configuration is the same as that specified by a given role,
+         * and throws an exception with a message describing the differences if not.
+         *
+         * @param configuration the configuration to check
+         * @param role the role to check against
+         */
+        public static void assertIsInRole(DeprecatableConfiguration configuration, ConfigurationRole role) {
+            if (!isUsageConsistentWithRole(configuration, role)) {
+                throw new GradleException(describeDifferenceFromRole(configuration, role));
+            }
+        }
+
+        private static String describeDifferenceFromRole(DeprecatableConfiguration configuration, ConfigurationRole role) {
+            if (!isUsageConsistentWithRole(configuration, role)) {
+                return "Usage for configuration: " + configuration.getName() + " is not consistent with the role: " + role.getName() + ".\n" +
+                        "Expected that it is:\n" +
+                        role.describeUsage() + "\n" +
+                        "But is actually is:\n" +
+                        UsageDescriber.describeUsage(configuration.isCanBeConsumed(), configuration.isCanBeResolved(), configuration.isCanBeDeclared(),
+                                configuration.isDeprecatedForConsumption(), configuration.isDeprecatedForResolution(), configuration.isDeprecatedForDeclarationAgainst());
+            } else {
+                return "Usage for configuration: " + configuration.getName() + " is consistent with the role: " + role.getName() + ".";
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/UsageDescriber.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/UsageDescriber.java
new file mode 100644
index 0000000..f5c8a80
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/configurations/UsageDescriber.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.configurations;
+
+import org.gradle.internal.deprecation.DeprecatableConfiguration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This static util class can be used to build a human-readable description of the usage a role or configuration allows.
+ */
+public abstract class UsageDescriber {
+    public static final String DEFAULT_CUSTOM_ROLE_NAME = "Custom Role";
+
+    private static final String CONSUMABLE = "Consumable - this configuration can be selected by another project as a dependency";
+    private static final String RESOLVABLE = "Resolvable - this configuration can be resolved by this project to a set of files";
+    private static final String DECLARABLE_AGAINST = "Declarable - this configuration can have dependencies added to it";
+    private static final String UNUSABLE = "This configuration does not allow any usage";
+
+    private static final String IS_DEPRECATED = "(but this behavior is marked deprecated)";
+
+    private UsageDescriber() { /* not instantiable */ }
+
+    /**
+     * Builds a human-readable description of the usage allowed by the given role.
+     *
+     * @param role the role to describe
+     * @return a human-readable description of the role's allowed usage
+     */
+    public static String describeRole(ConfigurationRole role) {
+        return describeUsage(role.isConsumable(), role.isResolvable(), role.isDeclarable(),
+            role.isConsumptionDeprecated(), role.isResolutionDeprecated(), role.isDeclarationAgainstDeprecated());
+    }
+
+    /**
+     * Builds a human-readable description of the current usage allowed by the given configuration.
+     *
+     * @param configuration the configuration to describe
+     * @return a human-readable description of the role's allowed usage
+     */
+    public static String describeCurrentUsage(DeprecatableConfiguration configuration) {
+        return describeUsage(configuration.isCanBeConsumed(), configuration.isCanBeResolved(), configuration.isCanBeDeclared(),
+            configuration.isDeprecatedForConsumption(), configuration.isDeprecatedForResolution(), configuration.isDeprecatedForDeclarationAgainst());
+    }
+
+    /**
+     * Builds a human-readable description of the usage allowed by the given set of flags.
+     *
+     * @param isConsumable whether the configuration is consumable
+     * @param isResolvable whether the configuration is resolvable
+     * @param isDeclarable whether the configuration is declarable
+     * @param isConsumptionDeprecated whether the configuration's consumable behavior is deprecated
+     * @param isResolutionDeprecated whether the configuration's resolvable behavior is deprecated
+     * @param isDeclarationAgainstDeprecated whether the configuration's declarable behavior is deprecated
+     * @return description of the given usage
+     */
+    public static String describeUsage(boolean isConsumable, boolean isResolvable, boolean isDeclarable,
+                                       boolean isConsumptionDeprecated, boolean isResolutionDeprecated, boolean isDeclarationAgainstDeprecated) {
+        List<String> descriptions = new ArrayList<>();
+        if (isConsumable) {
+            descriptions.add("\t" + CONSUMABLE + describeDeprecation(isConsumptionDeprecated));
+        }
+        if (isResolvable) {
+            descriptions.add("\t" + RESOLVABLE + describeDeprecation(isResolutionDeprecated));
+        }
+        if (isDeclarable) {
+            descriptions.add("\t" + DECLARABLE_AGAINST + describeDeprecation(isDeclarationAgainstDeprecated));
+        }
+        if (descriptions.isEmpty()) {
+            descriptions.add("\t" + UNUSABLE);
+        }
+        return String.join("\n", descriptions);
+    }
+
+    private static String describeDeprecation(boolean deprecated) {
+        return deprecated ? " " + IS_DEPRECATED : "";
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
index 0b7d26b..c643a9c 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/artifacts/dependencies/DefaultProjectDependency.java
@@ -26,11 +26,10 @@
 import org.gradle.api.internal.artifacts.CachingDependencyResolveContext;
 import org.gradle.api.internal.artifacts.DependencyResolveContext;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.TaskDependencyInternal;
-import org.gradle.internal.deprecation.DeprecatableConfiguration;
 import org.gradle.api.internal.tasks.DefaultTaskDependencyFactory;
 import org.gradle.api.internal.tasks.TaskDependencyFactory;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
+import org.gradle.api.internal.tasks.TaskDependencyInternal;
+import org.gradle.internal.deprecation.DeprecatableConfiguration;
 import org.gradle.internal.exceptions.ConfigurationNotConsumableException;
 import org.gradle.util.internal.GUtil;
 
@@ -86,17 +85,10 @@ public Configuration findProjectConfiguration() {
         if (!selectedConfiguration.isCanBeConsumed()) {
             throw new ConfigurationNotConsumableException(dependencyProject.getDisplayName(), selectedConfiguration.getName());
         }
-        warnIfConfigurationIsDeprecated((DeprecatableConfiguration) selectedConfiguration);
+        ((DeprecatableConfiguration) selectedConfiguration).maybeEmitConsumptionDeprecation();
         return selectedConfiguration;
     }
 
-    private void warnIfConfigurationIsDeprecated(DeprecatableConfiguration selectedConfiguration) {
-        DeprecationMessageBuilder.WithDocumentation consumptionDeprecation = selectedConfiguration.getConsumptionDeprecation();
-        if (consumptionDeprecation != null) {
-            consumptionDeprecation.nagUser();
-        }
-    }
-
     @Override
     public ProjectDependency copy() {
         DefaultProjectDependency copiedProjectDependency = new DefaultProjectDependency(dependencyProject, getTargetConfiguration(), buildProjectDependencies, taskDependencyFactory);
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/cache/DefaultCacheConfigurations.java b/subprojects/core/src/main/java/org/gradle/api/internal/cache/DefaultCacheConfigurations.java
index d61e78f..55db9ce 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/cache/DefaultCacheConfigurations.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/cache/DefaultCacheConfigurations.java
@@ -49,7 +49,9 @@ abstract public class DefaultCacheConfigurations implements CacheConfigurationsI
     private static final String SNAPSHOT_WRAPPERS = "snapshotWrappers";
     private static final String DOWNLOADED_RESOURCES = "downloadedResources";
     private static final String CREATED_RESOURCES = "createdResources";
-    static final String UNSAFE_MODIFICATION_ERROR = "The property '%s' was modified from an unsafe location (for instance a settings script or plugin).  This property can only be changed in an init script, preferably stored in the init.d directory inside the Gradle user home directory. See " + DOCUMENTATION_REGISTRY.getDocumentationFor("directory_layout", "dir:gradle_user_home:configure_cache_cleanup") + " for more information.";
+    static final String UNSAFE_MODIFICATION_ERROR = "The property '%s' was modified from an unsafe location (for instance a settings script or plugin).  " +
+        "This property can only be changed in an init script, preferably stored in the init.d directory inside the Gradle user home directory. " +
+        DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("information on this", "directory_layout", "dir:gradle_user_home:configure_cache_cleanup");
 
     private final CacheResourceConfigurationInternal releasedWrappersConfiguration;
     private final CacheResourceConfigurationInternal snapshotWrappersConfiguration;
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/catalog/parser/TomlCatalogFileParser.java b/subprojects/core/src/main/java/org/gradle/api/internal/catalog/parser/TomlCatalogFileParser.java
index bea58fb..ba51304 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/catalog/parser/TomlCatalogFileParser.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/catalog/parser/TomlCatalogFileParser.java
@@ -35,15 +35,18 @@
 import javax.annotation.Nullable;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Comparator;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
 import static org.gradle.api.internal.catalog.problems.DefaultCatalogProblemBuilder.buildProblem;
 import static org.gradle.api.internal.catalog.problems.DefaultCatalogProblemBuilder.maybeThrowError;
 import static org.gradle.problems.internal.RenderingUtils.oxfordListOf;
+import static org.gradle.util.internal.TextUtil.getPluralEnding;
 
 public class TomlCatalogFileParser {
     public static final String CURRENT_VERSION = "1.1";
@@ -79,42 +82,46 @@ public class TomlCatalogFileParser {
         "rejectAll"
     );
 
-    public static void parse(InputStream in, VersionCatalogBuilder builder) throws IOException {
+    public static void parse(Path catalogFilePath, VersionCatalogBuilder builder) throws IOException {
         StrictVersionParser strictVersionParser = new StrictVersionParser(Interners.newStrongInterner());
-        TomlParseResult result = Toml.parse(in);
-        assertNoParseErrors(result, builder);
-        TomlTable metadataTable = result.getTable(METADATA_KEY);
-        verifyMetadata(builder, metadataTable);
-        TomlTable librariesTable = result.getTable(LIBRARIES_KEY);
-        TomlTable bundlesTable = result.getTable(BUNDLES_KEY);
-        TomlTable versionsTable = result.getTable(VERSIONS_KEY);
-        TomlTable pluginsTable = result.getTable(PLUGINS_KEY);
-        Sets.SetView<String> unknownTle = Sets.difference(result.keySet(), TOP_LEVEL_ELEMENTS);
-        if (!unknownTle.isEmpty()) {
-            throwVersionCatalogProblem(builder, VersionCatalogProblemId.TOML_SYNTAX_ERROR, spec ->
-                spec.withShortDescription(() -> "Unknown top level elements " + unknownTle)
-                    .happensBecause(() -> "TOML file contains an unexpected top-level element")
-                    .addSolution(() -> "Make sure the top-level elements of your TOML file is one of " + oxfordListOf(TOP_LEVEL_ELEMENTS, "or"))
-                    .documented());
+        try (InputStream inputStream = Files.newInputStream(catalogFilePath)) {
+            TomlParseResult result = Toml.parse(inputStream);
+            assertNoParseErrors(result, catalogFilePath, builder);
+            TomlTable metadataTable = result.getTable(METADATA_KEY);
+            verifyMetadata(builder, metadataTable);
+            TomlTable librariesTable = result.getTable(LIBRARIES_KEY);
+            TomlTable bundlesTable = result.getTable(BUNDLES_KEY);
+            TomlTable versionsTable = result.getTable(VERSIONS_KEY);
+            TomlTable pluginsTable = result.getTable(PLUGINS_KEY);
+            Sets.SetView<String> unknownTle = Sets.difference(result.keySet(), TOP_LEVEL_ELEMENTS);
+            if (!unknownTle.isEmpty()) {
+                throwVersionCatalogProblem(builder, VersionCatalogProblemId.TOML_SYNTAX_ERROR, spec ->
+                    spec.withShortDescription(() -> "Unknown top level elements " + unknownTle)
+                        .happensBecause(() -> "TOML file contains an unexpected top-level element")
+                        .addSolution(() -> "Make sure the top-level elements of your TOML file is one of " + oxfordListOf(TOP_LEVEL_ELEMENTS, "or"))
+                        .documented());
+            }
+            parseLibraries(librariesTable, builder, strictVersionParser);
+            parsePlugins(pluginsTable, builder, strictVersionParser);
+            parseBundles(bundlesTable, builder);
+            parseVersions(versionsTable, builder, strictVersionParser);
         }
-        parseLibraries(librariesTable, builder, strictVersionParser);
-        parsePlugins(pluginsTable, builder, strictVersionParser);
-        parseBundles(bundlesTable, builder);
-        parseVersions(versionsTable, builder, strictVersionParser);
     }
 
-    private static void assertNoParseErrors(TomlParseResult result, VersionCatalogBuilder builder) {
+    private static void assertNoParseErrors(TomlParseResult result, Path catalogFilePath, VersionCatalogBuilder builder) {
         if (result.hasErrors()) {
             List<TomlParseError> errors = result.errors();
             throwVersionCatalogProblem(builder, VersionCatalogProblemId.TOML_SYNTAX_ERROR, spec ->
-                spec.withShortDescription(() -> "Parsing failed with " + errors.size() + " error" + (errors.size() > 1 ? "s" : ""))
+                spec.withShortDescription(() -> "Parsing failed with " + errors.size() + " error" + getPluralEnding(errors))
                     .happensBecause(() -> {
                         StringBuilder reason = new StringBuilder();
                         for (TomlParseError error : errors) {
                             if (reason.length() > 0) {
                                 reason.append("\n");
                             }
-                            reason.append("At line ")
+                            reason.append("In file '")
+                                .append(catalogFilePath.toAbsolutePath())
+                                .append("' at line ")
                                 .append(error.position().line()).append(", column ")
                                 .append(error.position().column())
                                 .append(": ")
@@ -148,8 +155,8 @@ private static void parseLibraries(@Nullable TomlTable librariesTable, VersionCa
         }
         List<String> keys = librariesTable.keySet()
             .stream()
-            .sorted(Comparator.comparing(String::length))
-            .collect(Collectors.toList());
+            .sorted(comparing(String::length))
+            .collect(toList());
         for (String alias : keys) {
             parseLibrary(alias, librariesTable, builder, strictVersionParser);
         }
@@ -161,8 +168,8 @@ private static void parsePlugins(@Nullable TomlTable pluginsTable, VersionCatalo
         }
         List<String> keys = pluginsTable.keySet()
             .stream()
-            .sorted(Comparator.comparing(String::length))
-            .collect(Collectors.toList());
+            .sorted(comparing(String::length))
+            .collect(toList());
         for (String alias : keys) {
             parsePlugin(alias, pluginsTable, builder, strictVersionParser);
         }
@@ -174,8 +181,8 @@ private static void parseVersions(@Nullable TomlTable versionsTable, VersionCata
         }
         List<String> keys = versionsTable.keySet()
             .stream()
-            .sorted(Comparator.comparing(String::length))
-            .collect(Collectors.toList());
+            .sorted(comparing(String::length))
+            .collect(toList());
         for (String alias : keys) {
             parseVersion(alias, versionsTable, builder, strictVersionParser);
         }
@@ -185,11 +192,11 @@ private static void parseBundles(@Nullable TomlTable bundlesTable, VersionCatalo
         if (bundlesTable == null) {
             return;
         }
-        List<String> keys = bundlesTable.keySet().stream().sorted().collect(Collectors.toList());
+        List<String> keys = bundlesTable.keySet().stream().sorted().collect(toList());
         for (String alias : keys) {
             List<String> bundled = expectArray(builder, "bundle", alias, bundlesTable, alias).toList().stream()
                 .map(String::valueOf)
-                .collect(Collectors.toList());
+                .collect(toList());
             builder.bundle(alias, bundled);
         }
     }
@@ -239,7 +246,7 @@ private static void expectedKeys(TomlTable table, Set<String> allowedKeys, Strin
         if (!allowedKeys.containsAll(actualKeys)) {
             Set<String> difference = Sets.difference(actualKeys, allowedKeys);
             throw new InvalidUserDataException("On " + context + " expected to find any of " + oxfordListOf(allowedKeys, "or")
-                + " but found unexpected key" + (difference.size() > 1 ? "s " : " ") + oxfordListOf(difference, "and")
+                + " but found unexpected key" + getPluralEnding(difference)+ " " + oxfordListOf(difference, "and")
                 + ".");
         }
     }
@@ -306,7 +313,7 @@ private static void parseLibrary(String alias, TomlTable librariesTable, Version
             rejectedVersions = rejectedArray != null ? rejectedArray.toList().stream()
                 .map(String::valueOf)
                 .map(v -> notEmpty(builder, v, "rejected version", alias))
-                .collect(Collectors.toList()) : null;
+                .collect(toList()) : null;
             rejectAll = expectBoolean(builder, "alias", alias, versionTable, "rejectAll");
         } else if (version != null) {
             throwUnexpectedVersionSyntax(alias, builder, version);
@@ -374,7 +381,7 @@ private static void parsePlugin(String alias, TomlTable librariesTable, VersionC
             rejectedVersions = rejectedArray != null ? rejectedArray.toList().stream()
                 .map(String::valueOf)
                 .map(v -> notEmpty(builder, v, "rejected version", alias))
-                .collect(Collectors.toList()) : null;
+                .collect(toList()) : null;
             rejectAll = expectBoolean(builder, "alias", alias, versionTable, "rejectAll");
         } else if (version != null) {
             throwUnexpectedVersionSyntax(alias, builder, version);
@@ -421,7 +428,7 @@ private static void parseVersion(String alias, TomlTable versionsTable, VersionC
             rejectedVersions = rejectedArray != null ? rejectedArray.toList().stream()
                 .map(String::valueOf)
                 .map(v -> notEmpty(builder, v, "rejected version", alias))
-                .collect(Collectors.toList()) : null;
+                .collect(toList()) : null;
             rejectAll = expectBoolean(builder, "alias", alias, versionTable, "rejectAll");
         } else if (version != null) {
             throwUnexpectedVersionSyntax(alias, builder, version);
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/catalog/problems/DefaultCatalogProblemBuilder.java b/subprojects/core/src/main/java/org/gradle/api/internal/catalog/problems/DefaultCatalogProblemBuilder.java
index 5bb6f1b..c5d1c8c 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/catalog/problems/DefaultCatalogProblemBuilder.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/catalog/problems/DefaultCatalogProblemBuilder.java
@@ -134,7 +134,7 @@ public VersionCatalogProblem build() {
             shortDescription,
             longDescription,
             reason,
-            () -> docLink == null ? null : DOCUMENTATION_REGISTRY.getDocumentationFor(docLink.page, docLink.section),
+            () -> docLink == null ? null : DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("information", docLink.page, docLink.section),
             solutions.stream().map(this::toSolution).collect(toList())
         );
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/catalog/problems/VersionCatalogProblem.java b/subprojects/core/src/main/java/org/gradle/api/internal/catalog/problems/VersionCatalogProblem.java
index 5041d12..db85751 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/catalog/problems/VersionCatalogProblem.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/catalog/problems/VersionCatalogProblem.java
@@ -25,6 +25,8 @@
 
 import static org.apache.commons.lang.StringUtils.capitalize;
 import static org.apache.commons.lang.StringUtils.uncapitalize;
+import static org.gradle.internal.reflect.validation.TypeValidationProblemRenderer.renderSolutions;
+import static org.gradle.util.internal.TextUtil.endLineWithDot;
 
 public class VersionCatalogProblem extends BaseProblem<VersionCatalogProblemId, StandardSeverity, String> {
     VersionCatalogProblem(VersionCatalogProblemId versionCatalogProblemId,
@@ -40,37 +42,16 @@ public class VersionCatalogProblem extends BaseProblem<VersionCatalogProblemId,
 
     public void reportInto(TreeFormatter output) {
         TreeFormatter formatter = new TreeFormatter();
-        formatter.node("Problem: In " + uncapitalize(getWhere()) + ", " + maybeAppendDot(uncapitalize(getShortDescription())));
+        formatter.node("Problem: In " + uncapitalize(getWhere()) + ", " + endLineWithDot(uncapitalize(getShortDescription())));
         getWhy().ifPresent(reason -> {
             formatter.blankLine();
-            formatter.node("Reason: " + capitalize(maybeAppendDot(reason)));
+            formatter.node("Reason: " + capitalize(endLineWithDot(reason)));
         });
-        List<Solution> possibleSolutions = getPossibleSolutions();
-        int solutionCount = possibleSolutions.size();
-        if (solutionCount > 0) {
-            formatter.blankLine();
-            if (solutionCount == 1) {
-                formatter.node("Possible solution: " + capitalize(maybeAppendDot(possibleSolutions.get(0).getShortDescription())));
-            } else {
-                formatter.node("Possible solutions");
-                formatter.startNumberedChildren();
-                possibleSolutions.forEach(solution ->
-                    formatter.node(capitalize(maybeAppendDot(solution.getShortDescription())))
-                );
-                formatter.endChildren();
-            }
-        }
+        renderSolutions(formatter, getPossibleSolutions());
         getDocumentationLink().ifPresent(docLink -> {
             formatter.blankLine();
-            formatter.node("Please refer to ").append(docLink).append(" for more details about this problem.");
+            formatter.node(docLink);
         });
         output.node(formatter.toString());
     }
-
-    private static String maybeAppendDot(String txt) {
-        if (txt.endsWith(".") || txt.endsWith("\n")) {
-            return txt;
-        }
-        return txt + ".";
-    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/TaskExecutionMode.java b/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/TaskExecutionMode.java
index 9df6a45..0c1e671 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/TaskExecutionMode.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/TaskExecutionMode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 the original author or authors.
+ * Copyright 2023 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,50 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.gradle.api.internal.changedetection;
 
-import javax.annotation.Nullable;
 import java.util.Optional;
 
-/**
- * Keeps information about the execution mode of a task.
- */
-public enum TaskExecutionMode {
-    INCREMENTAL(null, true, true),
-    NO_OUTPUTS("Task has not declared any outputs despite executing actions.", false, false),
-    RERUN_TASKS_ENABLED("Executed with '--rerun-tasks'.", true, false),
-    UP_TO_DATE_WHEN_FALSE("Task.upToDateWhen is false.", true, false),
-    UNTRACKED("Task state is not tracked.", false, false);
-
-    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
-    private final Optional<String> rebuildReason;
-    private final boolean taskHistoryMaintained;
-    private final boolean allowedToUseCachedResults;
-
-    TaskExecutionMode(@Nullable String rebuildReason, boolean taskHistoryMaintained, boolean allowedToUseCachedResults) {
-        this.rebuildReason = Optional.ofNullable(rebuildReason);
-        this.taskHistoryMaintained = taskHistoryMaintained;
-        this.allowedToUseCachedResults = allowedToUseCachedResults;
-    }
-
+public interface TaskExecutionMode {
     /**
      * Return rebuild reason if any.
      */
-    public Optional<String> getRebuildReason() {
-        return rebuildReason;
-    }
+    Optional<String> getRebuildReason();
 
     /**
      * Returns whether the execution history should be stored.
      */
-    public boolean isTaskHistoryMaintained() {
-        return taskHistoryMaintained;
-    }
+    boolean isTaskHistoryMaintained();
 
     /**
      * Returns whether it is okay to use results loaded from cache instead of executing the task.
      */
-    public boolean isAllowedToUseCachedResults() {
-        return allowedToUseCachedResults;
-    }
+    boolean isAllowedToUseCachedResults();
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionMode.java b/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionMode.java
new file mode 100644
index 0000000..7c652af
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionMode.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 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.api.internal.changedetection.changes;
+
+import org.gradle.api.internal.changedetection.TaskExecutionMode;
+
+import javax.annotation.Nullable;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Keeps information about the execution mode of a task.
+ */
+public class DefaultTaskExecutionMode implements TaskExecutionMode {
+
+    private static final DefaultTaskExecutionMode UP_TO_DATE_WHEN_FALSE = new DefaultTaskExecutionMode("Task.upToDateWhen is false.", true, false);
+    private static final DefaultTaskExecutionMode UNTRACKED_NO_REASON = new DefaultTaskExecutionMode("Task state is not tracked.", false, false);
+    private static final DefaultTaskExecutionMode RERUN_TASKS_ENABLED = new DefaultTaskExecutionMode("Executed with '--rerun-tasks'.", true, false);
+    private static final DefaultTaskExecutionMode NO_OUTPUTS = new DefaultTaskExecutionMode("Task has not declared any outputs despite executing actions.", false, false);
+    private static final DefaultTaskExecutionMode INCREMENTAL = new DefaultTaskExecutionMode(null, true, true);
+    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+    private final Optional<String> rebuildReason;
+    private final boolean taskHistoryMaintained;
+    private final boolean allowedToUseCachedResults;
+
+    DefaultTaskExecutionMode(@Nullable String rebuildReason, boolean taskHistoryMaintained, boolean allowedToUseCachedResults) {
+        this.rebuildReason = Optional.ofNullable(rebuildReason);
+        this.taskHistoryMaintained = taskHistoryMaintained;
+        this.allowedToUseCachedResults = allowedToUseCachedResults;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Optional<String> getRebuildReason() {
+        return rebuildReason;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isTaskHistoryMaintained() {
+        return taskHistoryMaintained;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isAllowedToUseCachedResults() {
+        return allowedToUseCachedResults;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultTaskExecutionMode that = (DefaultTaskExecutionMode) o;
+        return taskHistoryMaintained == that.taskHistoryMaintained && allowedToUseCachedResults == that.allowedToUseCachedResults && Objects.equals(rebuildReason, that.rebuildReason);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(rebuildReason, taskHistoryMaintained, allowedToUseCachedResults);
+    }
+
+    /**
+     * The execution mode for incremental tasks.
+     */
+    public static TaskExecutionMode incremental() {
+        return INCREMENTAL;
+    }
+
+    /**
+     * The execution mode when task did not declare any outputs.
+     * The message will be `Task has not declared any outputs despite executing actions.`.
+     */
+    public static TaskExecutionMode noOutputs() {
+        return NO_OUTPUTS;
+    }
+
+    /**
+     * The execution mode when the command was run with --rerun-tasks.
+     * The message will be `Executed with '--rerun-tasks'.`.
+     */
+    public static TaskExecutionMode rerunTasksEnabled() {
+        return RERUN_TASKS_ENABLED;
+    }
+
+    /**
+     * The execution mode when the Task.upToDateWhen is set to false.
+     * The message will be `Task.upToDateWhen is false.`.
+     */
+    public static TaskExecutionMode upToDateWhenFalse() {
+        return UP_TO_DATE_WHEN_FALSE;
+    }
+
+    /**
+     * The execution mode when the task is marked explicitly untracked.
+     * The message will be `Task state is not tracked.`.
+     */
+    public static TaskExecutionMode untracked() {
+        return UNTRACKED_NO_REASON;
+    }
+
+    /**
+     * The execution mode when the task is marked explicitly untracked.
+     * The message will be `"Task is untracked because: " + reason`.
+     */
+    public static TaskExecutionMode untracked(String reason) {
+        return new DefaultTaskExecutionMode("Task is untracked because: " + reason, false, false);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionModeResolver.java b/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionModeResolver.java
index c45888c..7804ed9 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionModeResolver.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionModeResolver.java
@@ -39,7 +39,7 @@ public DefaultTaskExecutionModeResolver(StartParameter startParameter) {
     @Override
     public TaskExecutionMode getExecutionMode(TaskInternal task, TaskProperties properties) {
         if (task.getReasonNotToTrackState().isPresent()) {
-            return TaskExecutionMode.UNTRACKED;
+            return DefaultTaskExecutionMode.untracked(task.getReasonNotToTrackState().get());
         }
         // Only false if no declared outputs AND no Task.upToDateWhen spec. We force to true for incremental tasks.
         AndSpec<? super TaskInternal> upToDateSpec = task.getOutputs().getUpToDateSpec();
@@ -47,19 +47,19 @@ public TaskExecutionMode getExecutionMode(TaskInternal task, TaskProperties prop
             if (requiresInputChanges(task)) {
                 throw new InvalidUserCodeException("You must declare outputs or use `TaskOutputs.upToDateWhen()` when using the incremental task API");
             } else {
-                return TaskExecutionMode.NO_OUTPUTS;
+                return DefaultTaskExecutionMode.noOutputs();
             }
         }
 
         if (startParameter.isRerunTasks()) {
-            return TaskExecutionMode.RERUN_TASKS_ENABLED;
+            return DefaultTaskExecutionMode.rerunTasksEnabled();
         }
 
         if (!upToDateSpec.isSatisfiedBy(task)) {
-            return TaskExecutionMode.UP_TO_DATE_WHEN_FALSE;
+            return DefaultTaskExecutionMode.upToDateWhenFalse();
         }
 
-        return TaskExecutionMode.INCREMENTAL;
+        return DefaultTaskExecutionMode.incremental();
     }
 
     private static boolean requiresInputChanges(TaskInternal task) {
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredCollection.java b/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredCollection.java
deleted file mode 100644
index 00911be..0000000
--- a/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredCollection.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright 2011 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.api.internal.collections;
-
-import org.gradle.api.Action;
-import org.gradle.api.internal.MutationGuard;
-import org.gradle.api.internal.WithEstimatedSize;
-import org.gradle.api.internal.provider.CollectionProviderInternal;
-import org.gradle.api.internal.provider.ProviderInternal;
-import org.gradle.internal.Cast;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-public class FilteredCollection<T, S extends T> implements ElementSource<S> {
-    protected final ElementSource<T> collection;
-    protected final CollectionFilter<S> filter;
-
-    public FilteredCollection(ElementSource<T> collection, CollectionFilter<S> filter) {
-        this.collection = collection;
-        this.filter = filter;
-    }
-
-    @Override
-    public boolean add(S o) {
-        throw new UnsupportedOperationException(String.format("Cannot add '%s' to '%s' as it is a filtered collection", o, this));
-    }
-
-    @Override
-    public boolean addRealized(S o) {
-        throw new UnsupportedOperationException(String.format("Cannot add '%s' to '%s' as it is a filtered collection", o, this));
-    }
-
-    @Override
-    public void clear() {
-        throw new UnsupportedOperationException(String.format("Cannot clear '%s' as it is a filtered collection", this));
-    }
-
-    protected boolean accept(Object o) {
-        return filter.filter(o) != null;
-    }
-
-    @Override
-    public boolean contains(Object o) {
-        return collection.contains(o) && accept(o);
-    }
-
-    @Override
-    public boolean containsAll(Collection<?> c) {
-        if (collection.containsAll(c)) {
-            for (Object o : c) {
-                if (!accept(o)) {
-                    return false;
-                }
-            }
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public boolean constantTimeIsEmpty() {
-        return collection.constantTimeIsEmpty();
-    }
-
-    @Override
-    public boolean isEmpty() {
-        if (collection.isEmpty()) {
-            return true;
-        } else {
-            for (T o : this) {
-                if (accept(o)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    @Override
-    public int estimatedSize() {
-        return collection.estimatedSize();
-    }
-
-    @Override
-    public MutationGuard getMutationGuard() {
-        return collection.getMutationGuard();
-    }
-
-    private static class FilteringIterator<T, S extends T> implements Iterator<S>, WithEstimatedSize {
-        private final CollectionFilter<S> filter;
-        private final Iterator<T> iterator;
-        private final int estimatedSize;
-
-        private S next;
-
-        FilteringIterator(ElementSource<T> collection, CollectionFilter<S> filter) {
-            this.iterator = collection.iteratorNoFlush();
-            this.filter = filter;
-            this.estimatedSize = collection.estimatedSize();
-            this.next = findNext();
-        }
-
-        private S findNext() {
-            while (iterator.hasNext()) {
-                T potentialNext = iterator.next();
-                S filtered = filter.filter(potentialNext);
-                if (filtered != null) {
-                    return filtered;
-                }
-            }
-
-            return null;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return next != null;
-        }
-
-        @Override
-        public S next() {
-            if (next != null) {
-                S thisNext = next;
-                next = findNext();
-                return thisNext;
-            } else {
-                throw new NoSuchElementException();
-            }
-        }
-
-        @Override
-        public void remove() {
-            throw new UnsupportedOperationException("This iterator does not support removal");
-        }
-
-        @Override
-        public int estimatedSize() {
-            return estimatedSize;
-        }
-    }
-
-    @Override
-    public Iterator<S> iterator() {
-        collection.realizePending(filter.getType());
-        return new FilteringIterator<T, S>(collection, filter);
-    }
-
-    @Override
-    public boolean remove(Object o) {
-        throw new UnsupportedOperationException(String.format("Cannot remove '%s' from '%s' as it is a filtered collection", o, this));
-    }
-
-    @Override
-    public int size() {
-        int i = 0;
-        // NOTE: There isn't much we can do about collection.matching { } filters as the spec requires a realized element, unless make major changes
-        for (T o : this) {
-            if (accept(o)) {
-                ++i;
-            }
-        }
-        return i;
-    }
-
-    @Override
-    public Iterator<S> iteratorNoFlush() {
-        return new FilteringIterator<T, S>(collection, filter);
-    }
-
-    @Override
-    public void realizePending() {
-        realizePending(filter.getType());
-    }
-
-    @Override
-    public void realizePending(Class<?> type) {
-        collection.realizePending(type);
-    }
-
-    @Override
-    public boolean addPending(ProviderInternal<? extends S> provider) {
-        return collection.addPending(provider);
-    }
-
-    @Override
-    public boolean removePending(ProviderInternal<? extends S> provider) {
-        return collection.removePending(provider);
-    }
-
-    @Override
-    public boolean addPendingCollection(CollectionProviderInternal<S, ? extends Iterable<S>> provider) {
-        CollectionProviderInternal<T, ? extends Iterable<T>> providerOfT = Cast.uncheckedCast(provider);
-        return collection.addPendingCollection(providerOfT);
-    }
-
-    @Override
-    public boolean removePendingCollection(CollectionProviderInternal<S, ? extends Iterable<S>> provider) {
-        CollectionProviderInternal<T, ? extends Iterable<T>> providerOfT = Cast.uncheckedCast(provider);
-        return collection.removePendingCollection(providerOfT);
-    }
-
-    @Override
-    public void onRealize(Action<S> action) { }
-
-    @Override
-    public void realizeExternal(ProviderInternal<? extends S> provider) {
-        collection.realizeExternal(provider);
-    }
-}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredElementSource.java b/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredElementSource.java
new file mode 100644
index 0000000..8034b12
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredElementSource.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2011 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.api.internal.collections;
+
+import org.gradle.api.Action;
+import org.gradle.api.internal.MutationGuard;
+import org.gradle.api.internal.WithEstimatedSize;
+import org.gradle.api.internal.provider.CollectionProviderInternal;
+import org.gradle.api.internal.provider.ProviderInternal;
+import org.gradle.internal.Cast;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class FilteredElementSource<T, S extends T> implements ElementSource<S> {
+    protected final ElementSource<T> collection;
+    protected final CollectionFilter<S> filter;
+
+    public FilteredElementSource(ElementSource<T> collection, CollectionFilter<S> filter) {
+        this.collection = collection;
+        this.filter = filter;
+    }
+
+    @Override
+    public boolean add(S o) {
+        throw new UnsupportedOperationException(String.format("Cannot add '%s' to '%s' as it is a filtered collection", o, this));
+    }
+
+    @Override
+    public boolean addRealized(S o) {
+        throw new UnsupportedOperationException(String.format("Cannot add '%s' to '%s' as it is a filtered collection", o, this));
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException(String.format("Cannot clear '%s' as it is a filtered collection", this));
+    }
+
+    protected boolean accept(Object o) {
+        return filter.filter(o) != null;
+    }
+
+    @Override
+    public boolean contains(Object o) {
+        return collection.contains(o) && accept(o);
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> c) {
+        if (collection.containsAll(c)) {
+            for (Object o : c) {
+                if (!accept(o)) {
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean constantTimeIsEmpty() {
+        return collection.constantTimeIsEmpty();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        if (collection.isEmpty()) {
+            return true;
+        } else {
+            for (T o : this) {
+                if (accept(o)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    @Override
+    public int estimatedSize() {
+        return collection.estimatedSize();
+    }
+
+    @Override
+    public MutationGuard getMutationGuard() {
+        return collection.getMutationGuard();
+    }
+
+    private static class FilteringIterator<T, S extends T> implements Iterator<S>, WithEstimatedSize {
+        private final CollectionFilter<S> filter;
+        private final Iterator<T> iterator;
+        private final int estimatedSize;
+
+        private S next;
+
+        FilteringIterator(ElementSource<T> collection, CollectionFilter<S> filter) {
+            this.iterator = collection.iteratorNoFlush();
+            this.filter = filter;
+            this.estimatedSize = collection.estimatedSize();
+            this.next = findNext();
+        }
+
+        private S findNext() {
+            while (iterator.hasNext()) {
+                T potentialNext = iterator.next();
+                S filtered = filter.filter(potentialNext);
+                if (filtered != null) {
+                    return filtered;
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        @Override
+        public S next() {
+            if (next != null) {
+                S thisNext = next;
+                next = findNext();
+                return thisNext;
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException("This iterator does not support removal");
+        }
+
+        @Override
+        public int estimatedSize() {
+            return estimatedSize;
+        }
+    }
+
+    @Override
+    public Iterator<S> iterator() {
+        collection.realizePending(filter.getType());
+        return new FilteringIterator<T, S>(collection, filter);
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        throw new UnsupportedOperationException(String.format("Cannot remove '%s' from '%s' as it is a filtered collection", o, this));
+    }
+
+    @Override
+    public int size() {
+        int i = 0;
+        // NOTE: There isn't much we can do about collection.matching { } filters as the spec requires a realized element, unless make major changes
+        for (T o : this) {
+            if (accept(o)) {
+                ++i;
+            }
+        }
+        return i;
+    }
+
+    @Override
+    public Iterator<S> iteratorNoFlush() {
+        return new FilteringIterator<T, S>(collection, filter);
+    }
+
+    @Override
+    public void realizePending() {
+        realizePending(filter.getType());
+    }
+
+    @Override
+    public void realizePending(Class<?> type) {
+        collection.realizePending(type);
+    }
+
+    @Override
+    public boolean addPending(ProviderInternal<? extends S> provider) {
+        return collection.addPending(provider);
+    }
+
+    @Override
+    public boolean removePending(ProviderInternal<? extends S> provider) {
+        return collection.removePending(provider);
+    }
+
+    @Override
+    public boolean addPendingCollection(CollectionProviderInternal<S, ? extends Iterable<S>> provider) {
+        CollectionProviderInternal<T, ? extends Iterable<T>> providerOfT = Cast.uncheckedCast(provider);
+        return collection.addPendingCollection(providerOfT);
+    }
+
+    @Override
+    public boolean removePendingCollection(CollectionProviderInternal<S, ? extends Iterable<S>> provider) {
+        CollectionProviderInternal<T, ? extends Iterable<T>> providerOfT = Cast.uncheckedCast(provider);
+        return collection.removePendingCollection(providerOfT);
+    }
+
+    @Override
+    public void onRealize(Action<S> action) { }
+
+    @Override
+    public void realizeExternal(ProviderInternal<? extends S> provider) {
+        collection.realizeExternal(provider);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredIndexedElementSource.java b/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredIndexedElementSource.java
new file mode 100644
index 0000000..c79ea3a
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredIndexedElementSource.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2011 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.api.internal.collections;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+// TODO make this work with pending elements
+public class FilteredIndexedElementSource<T, S extends T> extends FilteredElementSource<T, S> implements IndexedElementSource<S> {
+    public FilteredIndexedElementSource(ElementSource<T> collection, CollectionFilter<S> filter) {
+        super(collection, filter);
+    }
+
+    @Override
+    public void add(int index, S element) {
+        throw new UnsupportedOperationException(String.format("Cannot add '%s' to '%s' as it is a filtered collection", element, this));
+    }
+
+    @Override
+    public S get(int index) {
+        int nextIndex = 0;
+        for (T t : collection) {
+            S s = filter.filter(t);
+            if (s != null) {
+                if (nextIndex == index) {
+                    return s;
+                }
+                nextIndex++;
+            }
+        }
+        throw new IndexOutOfBoundsException();
+    }
+
+    @Override
+    public S set(int index, S element) {
+        throw new UnsupportedOperationException(String.format("Cannot set '%s' in '%s' as it is a filtered collection", element, this));
+    }
+
+    @Override
+    public S remove(int index) {
+        throw new UnsupportedOperationException(String.format("Cannot remove element from '%s' as it is a filtered collection", this));
+    }
+
+    @Override
+    public int indexOf(Object o) {
+        int nextIndex = 0;
+        for (T t : collection) {
+            S s = filter.filter(t);
+            if (s != null) {
+                if (s.equals(o)) {
+                    return nextIndex;
+                }
+                nextIndex++;
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public int lastIndexOf(Object o) {
+        int nextIndex = 0;
+        int lastMatch = -1;
+        for (T t : collection) {
+            S s = filter.filter(t);
+            if (s != null) {
+                if (s.equals(o)) {
+                    lastMatch = nextIndex;
+                }
+                nextIndex++;
+            }
+        }
+        return lastMatch;
+    }
+
+    @Override
+    public ListIterator<S> listIterator() {
+        return new FilteredListIterator<S>(iterator());
+    }
+
+    @Override
+    public ListIterator<S> listIterator(int index) {
+        ListIterator<S> iterator = listIterator();
+        for (int i = 0; i < index; i++) {
+            iterator.next();
+        }
+        return iterator;
+    }
+
+    @Override
+    public List<S> subList(int fromIndex, int toIndex) {
+        throw new UnsupportedOperationException("Not implemented yet.");
+    }
+
+    private static class FilteredListIterator<T> implements ListIterator<T> {
+        private final Iterator<T> iterator;
+        private int nextIndex;
+
+        FilteredListIterator(Iterator<T> iterator) {
+            this.iterator = iterator;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return iterator.hasNext();
+        }
+
+        @Override
+        public boolean hasPrevious() {
+            throw new UnsupportedOperationException("Not implemented yet.");
+        }
+
+        @Override
+        public T next() {
+            nextIndex++;
+            return iterator.next();
+        }
+
+        @Override
+        public T previous() {
+            throw new UnsupportedOperationException("Not implemented yet.");
+        }
+
+        @Override
+        public int nextIndex() {
+            return nextIndex;
+        }
+
+        @Override
+        public int previousIndex() {
+            return nextIndex - 1;
+        }
+
+        @Override
+        public void add(T t) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void set(T t) {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredList.java b/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredList.java
deleted file mode 100644
index cafc552..0000000
--- a/subprojects/core/src/main/java/org/gradle/api/internal/collections/FilteredList.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2011 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.api.internal.collections;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-
-// TODO make this work with pending elements
-public class FilteredList<T, S extends T> extends FilteredCollection<T, S> implements IndexedElementSource<S> {
-    public FilteredList(ElementSource<T> collection, CollectionFilter<S> filter) {
-        super(collection, filter);
-    }
-
-    @Override
-    public void add(int index, S element) {
-        throw new UnsupportedOperationException(String.format("Cannot add '%s' to '%s' as it is a filtered collection", element, this));
-    }
-
-    @Override
-    public S get(int index) {
-        int nextIndex = 0;
-        for (T t : collection) {
-            S s = filter.filter(t);
-            if (s != null) {
-                if (nextIndex == index) {
-                    return s;
-                }
-                nextIndex++;
-            }
-        }
-        throw new IndexOutOfBoundsException();
-    }
-
-    @Override
-    public S set(int index, S element) {
-        throw new UnsupportedOperationException(String.format("Cannot set '%s' in '%s' as it is a filtered collection", element, this));
-    }
-
-    @Override
-    public S remove(int index) {
-        throw new UnsupportedOperationException(String.format("Cannot remove element from '%s' as it is a filtered collection", this));
-    }
-
-    @Override
-    public int indexOf(Object o) {
-        int nextIndex = 0;
-        for (T t : collection) {
-            S s = filter.filter(t);
-            if (s != null) {
-                if (s.equals(o)) {
-                    return nextIndex;
-                }
-                nextIndex++;
-            }
-        }
-        return -1;
-    }
-
-    @Override
-    public int lastIndexOf(Object o) {
-        int nextIndex = 0;
-        int lastMatch = -1;
-        for (T t : collection) {
-            S s = filter.filter(t);
-            if (s != null) {
-                if (s.equals(o)) {
-                    lastMatch = nextIndex;
-                }
-                nextIndex++;
-            }
-        }
-        return lastMatch;
-    }
-
-    @Override
-    public ListIterator<S> listIterator() {
-        return new FilteredListIterator<S>(iterator());
-    }
-
-    @Override
-    public ListIterator<S> listIterator(int index) {
-        ListIterator<S> iterator = listIterator();
-        for (int i = 0; i < index; i++) {
-            iterator.next();
-        }
-        return iterator;
-    }
-
-    @Override
-    public List<S> subList(int fromIndex, int toIndex) {
-        throw new UnsupportedOperationException("Not implemented yet.");
-    }
-
-    private static class FilteredListIterator<T> implements ListIterator<T> {
-        private final Iterator<T> iterator;
-        private int nextIndex;
-
-        FilteredListIterator(Iterator<T> iterator) {
-            this.iterator = iterator;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return iterator.hasNext();
-        }
-
-        @Override
-        public boolean hasPrevious() {
-            throw new UnsupportedOperationException("Not implemented yet.");
-        }
-
-        @Override
-        public T next() {
-            nextIndex++;
-            return iterator.next();
-        }
-
-        @Override
-        public T previous() {
-            throw new UnsupportedOperationException("Not implemented yet.");
-        }
-
-        @Override
-        public int nextIndex() {
-            return nextIndex;
-        }
-
-        @Override
-        public int previousIndex() {
-            return nextIndex - 1;
-        }
-
-        @Override
-        public void add(T t) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void remove() {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void set(T t) {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/exceptions/MarkedVerificationException.java b/subprojects/core/src/main/java/org/gradle/api/internal/exceptions/MarkedVerificationException.java
new file mode 100644
index 0000000..304bde2
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/exceptions/MarkedVerificationException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.api.internal.exceptions;
+
+import org.gradle.api.tasks.VerificationException;
+import org.gradle.internal.exceptions.NonGradleCause;
+
+public class MarkedVerificationException extends VerificationException implements NonGradleCause {
+    public MarkedVerificationException(String message) {
+        super(message);
+    }
+
+    public MarkedVerificationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/package-info.java b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/package-info.java
index b9cba97..c5e9b6e 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/package-info.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/package-info.java
@@ -17,4 +17,4 @@
 @NonNullApi
 package org.gradle.api.internal.file.archive;
 
-import org.gradle.api.NonNullApi;
\ No newline at end of file
+import org.gradle.api.NonNullApi;
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptHandler.java b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptHandler.java
index cdd0ead..09c4670 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptHandler.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/DefaultScriptHandler.java
@@ -25,6 +25,8 @@
 import org.gradle.api.internal.DynamicObjectAware;
 import org.gradle.api.internal.artifacts.DependencyResolutionServices;
 import org.gradle.api.internal.artifacts.JavaEcosystemSupport;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.groovy.scripts.ScriptSource;
@@ -57,7 +59,7 @@ public class DefaultScriptHandler implements ScriptHandler, ScriptHandlerInterna
     private ClassPath resolvedClasspath;
     private RepositoryHandler repositoryHandler;
     private DependencyHandler dependencyHandler;
-    private ConfigurationContainer configContainer;
+    private RoleBasedConfigurationContainerInternal configContainer;
     private Configuration classpathConfiguration;
     private DynamicObject dynamicObject;
 
@@ -121,16 +123,17 @@ public ConfigurationContainer getConfigurations() {
         return configContainer;
     }
 
+    @SuppressWarnings("deprecation")
     private void defineConfiguration() {
         // Defer creation and resolution of configuration until required. Short-circuit when script does not require classpath
         if (configContainer == null) {
-            configContainer = dependencyResolutionServices.getConfigurationContainer();
+            configContainer = (RoleBasedConfigurationContainerInternal) dependencyResolutionServices.getConfigurationContainer();
         }
         if (dependencyHandler == null) {
             dependencyHandler = dependencyResolutionServices.getDependencyHandler();
         }
         if (classpathConfiguration == null) {
-            classpathConfiguration = configContainer.create(CLASSPATH_CONFIGURATION);
+            classpathConfiguration = configContainer.createWithRole(CLASSPATH_CONFIGURATION, ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET);
             scriptClassPathResolver.prepareClassPath(classpathConfiguration, dependencyHandler);
         }
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ResettableConfiguration.java b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ResettableConfiguration.java
index 29a5491..22e4856 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ResettableConfiguration.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/initialization/ResettableConfiguration.java
@@ -43,6 +43,9 @@ public interface ResettableConfiguration extends Configuration {
      * resolution result. This new functionality should be applicable to all configurations -- those
      * used during buildscript classpath loading and normal configuration-time configurations.
      * By applying this functionality to all configurations, this method would no longer be required.
+     *
+     * @implSpec Usage: This method should only be called on resolvable configurations and should throw an exception if
+     * called on a configuration that does not permit this usage.
      */
     void resetResolutionState();
 
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/model/InstantiatorBackedObjectFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/model/InstantiatorBackedObjectFactory.java
index 9244655..cd882ba 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/model/InstantiatorBackedObjectFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/model/InstantiatorBackedObjectFactory.java
@@ -27,6 +27,7 @@
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.file.RegularFileProperty;
 import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.api.internal.provider.DefaultListProperty;
 import org.gradle.api.internal.provider.DefaultProperty;
 import org.gradle.api.internal.provider.PropertyHost;
 import org.gradle.api.model.ObjectFactory;
@@ -101,7 +102,7 @@ public <T> Property<T> property(Class<T> valueType) {
 
     @Override
     public <T> ListProperty<T> listProperty(Class<T> elementType) {
-        return broken();
+        return new DefaultListProperty<>(PropertyHost.NO_OP, elementType);
     }
 
     @Override
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/plugins/DefaultObjectConfigurationAction.java b/subprojects/core/src/main/java/org/gradle/api/internal/plugins/DefaultObjectConfigurationAction.java
index a604b6c..4cda091 100755
--- a/subprojects/core/src/main/java/org/gradle/api/internal/plugins/DefaultObjectConfigurationAction.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/plugins/DefaultObjectConfigurationAction.java
@@ -16,12 +16,12 @@
 
 package org.gradle.api.internal.plugins;
 
-import org.gradle.api.InvalidUserCodeException;
 import org.gradle.api.Plugin;
 import org.gradle.api.initialization.dsl.ScriptHandler;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
 import org.gradle.api.internal.initialization.ScriptHandlerFactory;
+import org.gradle.api.internal.resources.InsecureProtocolException;
 import org.gradle.api.plugins.ObjectConfigurationAction;
 import org.gradle.api.plugins.PluginAware;
 import org.gradle.api.resources.TextResourceFactory;
@@ -117,24 +117,18 @@ private HttpRedirectVerifier createHttpRedirectVerifier(URI scriptUri) {
             scriptUri,
             false,
             () -> {
-                String message =
-                    "Applying script plugins from insecure URIs, without explicit opt-in, is unsupported. " +
-                        String.format("The provided URI '%s' uses an insecure protocol (HTTP). ", scriptUri) +
-                        String.format("Use '%s' instead or try 'apply from: resources.text.fromInsecureUri(\"%s\")' to fix this. ", GUtil.toSecureUrl(scriptUri), scriptUri) +
-                        Documentation
-                            .dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)")
-                            .consultDocumentationMessage();
-                throw new InvalidUserCodeException(message);
+                throw new InsecureProtocolException(
+                    String.format("Applying script plugins from insecure URIs, without explicit opt-in, is unsupported. The provided URI '%s' uses an insecure protocol (HTTP). ", scriptUri),
+                    String.format("Use '%s' instead or try 'apply from: resources.text.fromInsecureUri(\"%s\")'. ", GUtil.toSecureUrl(scriptUri), scriptUri),
+                    Documentation.dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)").consultDocumentationMessage()
+                );
             },
             redirect -> {
-                String message =
-                    "Applying script plugins from an insecure redirect, without explicit opt-in, is unsupported. " +
-                        "Switch to HTTPS or use TextResourceFactory.fromInsecureUri(Object) to fix this. " +
-                        String.format("'%s' redirects to insecure '%s'. ", scriptUri, redirect) +
-                        Documentation
-                            .dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)")
-                            .consultDocumentationMessage();
-                throw new InvalidUserCodeException(message);
+                throw new InsecureProtocolException(
+                    String.format("Applying script plugins from an insecure redirect, without explicit opt-in, is unsupported. '%s' redirects to insecure '%s'. ", scriptUri, redirect),
+                    "Switch to HTTPS or use TextResourceFactory.fromInsecureUri(Object).",
+                    Documentation.dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)").consultDocumentationMessage()
+                );
             }
         );
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/project/DefaultProject.java b/subprojects/core/src/main/java/org/gradle/api/internal/project/DefaultProject.java
index bb218de..7765ded 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/project/DefaultProject.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/project/DefaultProject.java
@@ -55,6 +55,7 @@
 import org.gradle.api.internal.artifacts.DependencyManagementServices;
 import org.gradle.api.internal.artifacts.DependencyResolutionServices;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.artifacts.dsl.dependencies.UnknownProjectFinder;
 import org.gradle.api.internal.collections.DomainObjectCollectionFactory;
 import org.gradle.api.internal.file.DefaultProjectLayout;
@@ -73,7 +74,6 @@
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.model.ObjectFactory;
-import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.Provider;
@@ -89,6 +89,7 @@
 import org.gradle.internal.Cast;
 import org.gradle.internal.Factories;
 import org.gradle.internal.Factory;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.event.ListenerBroadcast;
 import org.gradle.internal.extensibility.ExtensibleDynamicObject;
 import org.gradle.internal.extensibility.NoConventionMapping;
@@ -204,7 +205,7 @@ public abstract class DefaultProject extends AbstractPluginAware implements Proj
 
     private DependencyHandler dependencyHandler;
 
-    private ConfigurationContainer configurationContainer;
+    private RoleBasedConfigurationContainerInternal configurationContainer;
 
     private ArtifactHandler artifactHandler;
 
@@ -571,30 +572,25 @@ public ArtifactHandler getArtifacts() {
         return artifactHandler;
     }
 
-    public void setArtifactHandler(ArtifactHandler artifactHandler) {
-        this.artifactHandler = artifactHandler;
-    }
-
     @Inject
     @Override
     public abstract RepositoryHandler getRepositories();
 
     @Override
-    public ConfigurationContainer getConfigurations() {
+    public RoleBasedConfigurationContainerInternal getConfigurations() {
         if (configurationContainer == null) {
-            configurationContainer = services.get(ConfigurationContainer.class);
+            configurationContainer = services.get(RoleBasedConfigurationContainerInternal.class);
         }
         return configurationContainer;
     }
 
-    public void setConfigurationContainer(ConfigurationContainer configurationContainer) {
-        this.configurationContainer = configurationContainer;
-    }
-
     @Deprecated
     @Override
-    public Convention getConvention() {
-        // TODO (donat) deprecate after all internal usages have been eliminated
+    public org.gradle.api.plugins.Convention getConvention() {
+        DeprecationLogger.deprecateMethod(Project.class, "getConvention()")
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "deprecated_access_to_conventions")
+            .nagUser();
         return extensibleDynamicObject.getConvention();
     }
 
@@ -1057,10 +1053,6 @@ public DependencyHandler getDependencies() {
     @Inject
     public abstract DependencyFactory getDependencyFactory();
 
-    public void setDependencyHandler(DependencyHandler dependencyHandler) {
-        this.dependencyHandler = dependencyHandler;
-    }
-
     @Override
     public ProjectEvaluationListener getProjectEvaluationBroadcaster() {
         return evaluationListener.getSource();
@@ -1128,6 +1120,7 @@ public void components(Action<? super SoftwareComponentContainer> configuration)
      *
      * @see AsmBackedClassGenerator.ClassBuilderImpl#addDynamicMethods
      */
+    @SuppressWarnings("JavadocReference")
     @Nullable
     public Object getProperty(String propertyName) {
         return property(propertyName);
@@ -1140,6 +1133,7 @@ public Object getProperty(String propertyName) {
      *
      * @see AsmBackedClassGenerator.ClassBuilderImpl#addDynamicMethods
      */
+    @SuppressWarnings("JavadocReference")
     @Nullable
     public Object invokeMethod(String name, Object args) {
         if (args instanceof Object[]) {
@@ -1414,7 +1408,7 @@ public <T> NamedDomainObjectContainer<T> container(Class<T> type, Closure factor
 
     @Override
     public ExtensionContainerInternal getExtensions() {
-        return (ExtensionContainerInternal) getConvention();
+        return (ExtensionContainerInternal) DeprecationLogger.whileDisabled(this::getConvention);
     }
 
     // Not part of the public API
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/project/ProjectInternal.java b/subprojects/core/src/main/java/org/gradle/api/internal/project/ProjectInternal.java
index afb6541..6cbd3a3 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/project/ProjectInternal.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/project/ProjectInternal.java
@@ -27,6 +27,7 @@
 import org.gradle.api.internal.DomainObjectContext;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.file.HasScriptServices;
 import org.gradle.api.internal.initialization.ClassLoaderScope;
@@ -237,6 +238,16 @@ public interface ProjectInternal extends Project, ProjectIdentifier, HasScriptSe
 
     DependencyMetaDataProvider getDependencyMetaDataProvider();
 
+    /**
+     * When we get the {@link ConfigurationContainer} from internal locations, we'll override
+     * this getter to promise to return a {@link RoleBasedConfigurationContainerInternal} instance, to avoid
+     * the need to cast the result to create role-based configurations.
+     *
+     * @return the configuration container as a {@link RoleBasedConfigurationContainerInternal}
+     */
+    @Override
+    RoleBasedConfigurationContainerInternal getConfigurations();
+
     interface DetachedResolver {
         RepositoryHandler getRepositories();
 
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskIdentity.java b/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskIdentity.java
index 0093c4c..ecb5d49 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskIdentity.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskIdentity.java
@@ -16,19 +16,13 @@
 
 package org.gradle.api.internal.project.taskfactory;
 
-import com.google.common.annotations.VisibleForTesting;
 import org.gradle.api.Task;
-import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.internal.scan.UsedByScanPlugin;
 import org.gradle.util.Path;
 
-import java.util.concurrent.atomic.AtomicLong;
-
 @UsedByScanPlugin
 public final class TaskIdentity<T extends Task> {
 
-    private static final AtomicLong SEQUENCE = new AtomicLong();
-
     public final Class<T> type;
     public final String name;
     public final Path projectPath; // path within its build (i.e. including project path)
@@ -41,7 +35,6 @@ public final class TaskIdentity<T extends Task> {
      */
     public final long uniqueId;
 
-    @VisibleForTesting
     TaskIdentity(Class<T> type, String name, Path projectPath, Path identityPath, Path buildPath, long uniqueId) {
         this.name = name;
         this.projectPath = projectPath;
@@ -51,17 +44,6 @@ public final class TaskIdentity<T extends Task> {
         this.uniqueId = uniqueId;
     }
 
-    public static <T extends Task> TaskIdentity<T> create(String name, Class<T> type, ProjectInternal project) {
-        return new TaskIdentity<>(
-            type,
-            name,
-            project.projectPath(name),
-            project.identityPath(name),
-            project.getGradle().getIdentityPath(),
-            SEQUENCE.getAndIncrement()
-        );
-    }
-
     public long getId() {
         return uniqueId;
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskIdentityFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskIdentityFactory.java
new file mode 100644
index 0000000..2682dcc
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskIdentityFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 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.api.internal.project.taskfactory;
+
+import org.gradle.api.Task;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.id.ConfigurationCacheableIdFactory;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Task identity factory that ensures that unique ids are used correctly
+ * when creating new instances and/or loading from the configuration cache.
+ */
+@ThreadSafe
+@ServiceScope(Scopes.BuildTree.class)
+public class TaskIdentityFactory {
+
+    private final ConfigurationCacheableIdFactory idFactory;
+
+    public TaskIdentityFactory(ConfigurationCacheableIdFactory idFactory) {
+        this.idFactory = idFactory;
+    }
+
+    /**
+     * Create a task identity.
+     */
+    public <T extends Task> TaskIdentity<T> create(String name, Class<T> type, ProjectInternal project) {
+        long id = idFactory.createId();
+        return doCreate(name, type, project, id);
+    }
+
+    /**
+     * Recreate a task identity.
+     * <p>
+     * Should only be used when loading from the configuration cache to preserve task ids.
+     */
+    public <T extends Task> TaskIdentity<T> recreate(String name, Class<T> type, ProjectInternal project, long uniqueId) {
+        idFactory.idRecreated();
+        return doCreate(name, type, project, uniqueId);
+    }
+
+    private static <T extends Task> TaskIdentity<T> doCreate(String name, Class<T> type, ProjectInternal project, long uniqueId) {
+        return new TaskIdentity<>(
+            type,
+            name,
+            project.projectPath(name),
+            project.identityPath(name),
+            project.getGradle().getIdentityPath(),
+            uniqueId
+        );
+    }
+
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskInstantiator.java b/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskInstantiator.java
index 3c5186d..4b9a4f2 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskInstantiator.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/project/taskfactory/TaskInstantiator.java
@@ -22,16 +22,19 @@
 
 public class TaskInstantiator implements NamedEntityInstantiator<Task> {
     private static final Object[] NO_PARAMS = new Object[0];
+
+    private final TaskIdentityFactory taskIdentityFactory;
     private final ITaskFactory taskFactory;
     private final ProjectInternal project;
 
-    public TaskInstantiator(ITaskFactory taskFactory, ProjectInternal project) {
+    public TaskInstantiator(TaskIdentityFactory taskIdentityFactory, ITaskFactory taskFactory, ProjectInternal project) {
+        this.taskIdentityFactory = taskIdentityFactory;
         this.taskFactory = taskFactory;
         this.project = project;
     }
 
     @Override
     public <S extends Task> S create(String name, Class<S> type) {
-        return taskFactory.create(TaskIdentity.create(name, type, project), NO_PARAMS);
+        return taskFactory.create(taskIdentityFactory.create(name, type, project), NO_PARAMS);
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/resources/DefaultTextResourceFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/resources/DefaultTextResourceFactory.java
index 8cca4a6..bbca615 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/resources/DefaultTextResourceFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/resources/DefaultTextResourceFactory.java
@@ -91,42 +91,19 @@ private TextResource fromUri(Object uri, boolean allowInsecureProtocol) {
         return apiTextResourcesAdapterFactory.create(rootUri, redirectVerifier);
     }
 
-    private void throwExceptionDueToInsecureProtocol(URI rootUri) {
-        String contextualAdvice =
-            String.format("The provided URI '%s' uses an insecure protocol (HTTP). ", rootUri);
-        String switchToAdvice =
-            String.format(
-                "Switch the URI to '%s' or try 'resources.text.fromInsecureUri(\"%s\")' to silence the warning. ",
-                GUtil.toSecureUrl(rootUri),
-                rootUri
-            );
-        String dslMessage =
-            Documentation
-                .dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)")
-                .consultDocumentationMessage();
-
-        String message =
-            "Loading a TextResource from an insecure URI, without explicit opt-in, is unsupported. " +
-                contextualAdvice +
-                switchToAdvice +
-                dslMessage;
-        throw new InvalidUserCodeException(message);
+    private static void throwExceptionDueToInsecureProtocol(URI rootUri) {
+        throw new InsecureProtocolException(
+            "Loading a TextResource from an insecure URI, without explicit opt-in, is unsupported. " + String.format("The provided URI '%s' uses an insecure protocol (HTTP). ", rootUri),
+            String.format("Switch the URI to '%s' or try 'resources.text.fromInsecureUri(\"%s\")' to silence the warning. ", GUtil.toSecureUrl(rootUri), rootUri),
+            Documentation.dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)").consultDocumentationMessage()
+        );
     }
 
-    private void throwExceptionDueToInsecureRedirect(Object uri, URI redirect) throws InvalidUserCodeException {
-        String contextualAdvice =
-            String.format("'%s' redirects to insecure '%s'. ", uri, redirect);
-        String switchToAdvice =
-            "Switch to HTTPS or use TextResourceFactory.fromInsecureUri(Object) to silence the warning. ";
-        String dslMessage =
-            Documentation
-                .dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)")
-                .consultDocumentationMessage();
-        String message =
-            "Loading a TextResource from an insecure redirect, without explicit opt-in, is unsupported. " +
-                contextualAdvice +
-                switchToAdvice +
-                dslMessage;
-        throw new InvalidUserCodeException(message);
+    private static void throwExceptionDueToInsecureRedirect(Object uri, URI redirect) throws InvalidUserCodeException {
+        throw new InsecureProtocolException(
+            "Loading a TextResource from an insecure redirect, without explicit opt-in, is unsupported. " + String.format("'%s' redirects to insecure '%s'.", uri, redirect),
+            "Switch to HTTPS or use TextResourceFactory.fromInsecureUri(Object) to silence the warning.",
+            Documentation.dslReference(TextResourceFactory.class, "fromInsecureUri(java.lang.Object)").consultDocumentationMessage()
+        );
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/resources/InsecureProtocolException.java b/subprojects/core/src/main/java/org/gradle/api/internal/resources/InsecureProtocolException.java
new file mode 100644
index 0000000..f8029d8
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/resources/InsecureProtocolException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.api.internal.resources;
+
+import org.gradle.api.InvalidUserCodeException;
+import org.gradle.internal.exceptions.ResolutionProvider;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class InsecureProtocolException extends InvalidUserCodeException implements ResolutionProvider {
+
+    private final List<String> resolutions;
+
+    public InsecureProtocolException(String message, String... resolutions) {
+        super(message);
+        this.resolutions = Arrays.asList(resolutions);
+    }
+
+    @Override
+    public List<String> getResolutions() {
+        return resolutions;
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultRealizableTaskCollection.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultRealizableTaskCollection.java
index b46aa04..fd1c887 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultRealizableTaskCollection.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultRealizableTaskCollection.java
@@ -18,14 +18,10 @@
 
 import groovy.lang.Closure;
 import org.gradle.api.Action;
-import org.gradle.api.DomainObjectCollection;
 import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.NamedDomainObjectCollectionSchema;
-import org.gradle.api.Namer;
-import org.gradle.api.Rule;
 import org.gradle.api.Task;
 import org.gradle.api.UnknownTaskException;
-import org.gradle.api.provider.Provider;
+import org.gradle.api.internal.DelegatingNamedDomainObjectSet;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.TaskCollection;
 import org.gradle.api.tasks.TaskProvider;
@@ -35,26 +31,19 @@
 import org.gradle.model.internal.core.MutableModelNode;
 import org.gradle.model.internal.type.ModelType;
 
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public class DefaultRealizableTaskCollection<T extends Task> implements TaskCollection<T>, TaskDependencyContainer {
+public class DefaultRealizableTaskCollection<T extends Task> extends DelegatingNamedDomainObjectSet<T> implements TaskCollection<T>, TaskDependencyContainer {
 
-    private final TaskCollection<T> delegate;
     private final Class<T> type;
     private final AtomicBoolean realized = new AtomicBoolean();
     private final MutableModelNode modelNode;
     private final Instantiator instantiator;
 
     public DefaultRealizableTaskCollection(Class<T> type, TaskCollection<T> delegate, MutableModelNode modelNode, Instantiator instantiator) {
+        super(delegate);
         assert !(delegate instanceof DefaultRealizableTaskCollection) : "Attempt to wrap already realizable task collection in realizable wrapper: " + delegate;
 
-        this.delegate = delegate;
         this.type = type;
         this.modelNode = modelNode;
         this.instantiator = instantiator;
@@ -75,242 +64,57 @@ public void visitDependencies(TaskDependencyResolveContext context) {
         }
     }
 
+    @Override
+    protected TaskCollection<T> getDelegate() {
+        return (TaskCollection<T>) super.getDelegate();
+    }
+
     private <S extends T> TaskCollection<S> realizable(Class<S> type, TaskCollection<S> collection) {
         return Cast.uncheckedCast(instantiator.newInstance(DefaultRealizableTaskCollection.class, type, collection, modelNode, instantiator));
     }
 
     @Override
     public TaskCollection<T> matching(Spec<? super T> spec) {
-        return realizable(type, delegate.matching(spec));
+        return realizable(type, getDelegate().matching(spec));
     }
 
     @Override
     public TaskCollection<T> matching(Closure closure) {
-        return realizable(type, delegate.matching(closure));
-    }
-
-    @Override
-    public T getByName(String name, Closure configureClosure) throws UnknownTaskException {
-        return delegate.getByName(name, configureClosure);
-    }
-
-    @Override
-    public T getByName(String name, Action<? super T> configureAction) throws UnknownTaskException {
-        return delegate.getByName(name, configureAction);
-    }
-
-    @Override
-    public T getByName(String name) throws UnknownTaskException {
-        return delegate.getByName(name);
+        return realizable(type, getDelegate().matching(closure));
     }
 
     @Override
     public <S extends T> TaskCollection<S> withType(Class<S> type) {
-        return realizable(type, delegate.withType(type));
+        return realizable(type, getDelegate().withType(type));
     }
 
     @Override
     public Action<? super T> whenTaskAdded(Action<? super T> action) {
-        return delegate.whenTaskAdded(action);
+        return getDelegate().whenTaskAdded(action);
     }
 
     @Override
     public void whenTaskAdded(Closure closure) {
-        delegate.whenTaskAdded(closure);
-    }
-
-    @Override
-    public T getAt(String name) throws UnknownTaskException {
-        return delegate.getAt(name);
-    }
-
-    @Override
-    public Set<T> findAll(Closure spec) {
-        return delegate.findAll(spec);
-    }
-
-    @Override
-    public boolean add(T e) {
-        return delegate.add(e);
-    }
-
-    @Override
-    public void addLater(Provider<? extends T> provider) {
-        delegate.addLater(provider);
-    }
-
-    @Override
-    public void addAllLater(Provider<? extends Iterable<T>> provider) {
-        delegate.addAllLater(provider);
-    }
-
-    @Override
-    public boolean addAll(Collection<? extends T> c) {
-        return delegate.addAll(c);
-    }
-
-    @Override
-    public Namer<T> getNamer() {
-        return delegate.getNamer();
-    }
-
-    @Override
-    public SortedMap<String, T> getAsMap() {
-        return delegate.getAsMap();
-    }
-
-    @Override
-    public SortedSet<String> getNames() {
-        return delegate.getNames();
-    }
-
-    @Override
-    public T findByName(String name) {
-        return delegate.findByName(name);
-    }
-
-    @Override
-    public Rule addRule(Rule rule) {
-        return delegate.addRule(rule);
-    }
-
-    @Override
-    public Rule addRule(String description, Closure ruleAction) {
-        return delegate.addRule(description, ruleAction);
-    }
-
-    @Override
-    public Rule addRule(String description, Action<String> ruleAction) {
-        return delegate.addRule(description, ruleAction);
-    }
-
-    @Override
-    public List<Rule> getRules() {
-        return delegate.getRules();
-    }
-
-    @Override
-    public <S extends T> DomainObjectCollection<S> withType(Class<S> type, Action<? super S> configureAction) {
-        return realizable(type, (DefaultTaskCollection<S>) delegate.withType(type, configureAction));
-    }
-
-    @Override
-    public <S extends T> DomainObjectCollection<S> withType(Class<S> type, Closure configureClosure) {
-        return realizable(type, (DefaultTaskCollection<S>) delegate.withType(type, configureClosure));
-    }
-
-    @Override
-    public Action<? super T> whenObjectAdded(Action<? super T> action) {
-        return delegate.whenObjectAdded(action);
-    }
-
-    @Override
-    public void whenObjectAdded(Closure action) {
-        delegate.whenObjectAdded(action);
-    }
-
-    @Override
-    public Action<? super T> whenObjectRemoved(Action<? super T> action) {
-        return delegate.whenObjectRemoved(action);
-    }
-
-    @Override
-    public void whenObjectRemoved(Closure action) {
-        delegate.whenObjectRemoved(action);
-    }
-
-    @Override
-    public void all(Action<? super T> action) {
-        delegate.all(action);
-    }
-
-    @Override
-    public void all(Closure action) {
-        delegate.all(action);
-    }
-
-    @Override
-    public void configureEach(Action<? super T> action) {
-        delegate.configureEach(action);
-    }
-
-    @Override
-    public int size() {
-        return delegate.size();
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return delegate.isEmpty();
-    }
-
-    @Override
-    public boolean contains(Object o) {
-        return delegate.contains(o);
-    }
-
-    @Override
-    public Iterator<T> iterator() {
-        return delegate.iterator();
-    }
-
-    @Override
-    public Object[] toArray() {
-        return delegate.toArray();
-    }
-
-    @Override
-    public <R> R[] toArray(R[] a) {
-        return delegate.toArray(a);
-    }
-
-    @Override
-    public boolean remove(Object o) {
-        return delegate.remove(o);
-    }
-
-    @Override
-    public boolean containsAll(Collection<?> c) {
-        return delegate.containsAll(c);
-    }
-
-    @Override
-    public boolean removeAll(Collection<?> c) {
-        return delegate.removeAll(c);
-    }
-
-    @Override
-    public boolean retainAll(Collection<?> c) {
-        return delegate.retainAll(c);
-    }
-
-    @Override
-    public void clear() {
-        delegate.clear();
+        getDelegate().whenTaskAdded(closure);
     }
 
     @Override
     public TaskProvider<T> named(String name) throws InvalidUserDataException {
-        return delegate.named(name);
+        return getDelegate().named(name);
     }
 
     @Override
     public TaskProvider<T> named(String name, Action<? super T> configurationAction) throws UnknownTaskException {
-        return delegate.named(name, configurationAction);
+        return getDelegate().named(name, configurationAction);
     }
 
     @Override
     public <S extends T> TaskProvider<S> named(String name, Class<S> type) throws UnknownTaskException {
-        return delegate.named(name, type);
+        return getDelegate().named(name, type);
     }
 
     @Override
     public <S extends T> TaskProvider<S> named(String name, Class<S> type, Action<? super S> configurationAction) throws UnknownTaskException {
-        return delegate.named(name, type, configurationAction);
-    }
-
-    @Override
-    public NamedDomainObjectCollectionSchema getCollectionSchema() {
-        return delegate.getCollectionSchema();
+        return getDelegate().named(name, type, configurationAction);
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskContainer.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskContainer.java
index 8178ac4..421fed6 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskContainer.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskContainer.java
@@ -17,7 +17,6 @@
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
 import groovy.lang.Closure;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.Action;
@@ -37,6 +36,7 @@
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
 import org.gradle.api.internal.project.taskfactory.TaskIdentity;
+import org.gradle.api.internal.project.taskfactory.TaskIdentityFactory;
 import org.gradle.api.internal.project.taskfactory.TaskInstantiator;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.tasks.TaskCollection;
@@ -68,6 +68,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -84,6 +85,7 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
         Task.TASK_NAME, Task.TASK_TYPE
     );
 
+    private final TaskIdentityFactory taskIdentityFactory;
     private final ITaskFactory taskFactory;
     private final NamedEntityInstantiator<Task> taskInstantiator;
     private final BuildOperationExecutor buildOperationExecutor;
@@ -93,16 +95,20 @@ public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements
 
     private MutableModelNode modelNode;
 
-    public DefaultTaskContainer(final ProjectInternal project,
-                                Instantiator instantiator,
-                                final ITaskFactory taskFactory,
-                                TaskStatistics statistics,
-                                BuildOperationExecutor buildOperationExecutor,
-                                CrossProjectConfigurator crossProjectConfigurator,
-                                CollectionCallbackActionDecorator callbackDecorator) {
+    public DefaultTaskContainer(
+        ProjectInternal project,
+        Instantiator instantiator,
+        TaskIdentityFactory taskIdentityFactory,
+        ITaskFactory taskFactory,
+        TaskStatistics statistics,
+        BuildOperationExecutor buildOperationExecutor,
+        CrossProjectConfigurator crossProjectConfigurator,
+        CollectionCallbackActionDecorator callbackDecorator
+    ) {
         super(Task.class, instantiator, project, MutationGuards.of(crossProjectConfigurator), callbackDecorator);
+        this.taskIdentityFactory = taskIdentityFactory;
         this.taskFactory = taskFactory;
-        taskInstantiator = new TaskInstantiator(taskFactory, project);
+        taskInstantiator = new TaskInstantiator(taskIdentityFactory, taskFactory, project);
         this.statistics = statistics;
         this.eagerlyCreateLazyTasks = Boolean.getBoolean(EAGERLY_CREATE_LAZY_TASKS_PROPERTY);
         this.buildOperationExecutor = buildOperationExecutor;
@@ -132,9 +138,9 @@ private Task doCreate(Map<String, ?> options, final Action<? super Task> configu
             throw new InvalidUserDataException("The task name must be provided.");
         }
 
-        final Class<? extends TaskInternal> type = Cast.uncheckedCast(actualArgs.get(Task.TASK_TYPE));
+        final Class<? extends TaskInternal> type = Objects.requireNonNull(Cast.uncheckedCast(actualArgs.get(Task.TASK_TYPE)), "Task type must not be null");
 
-        final TaskIdentity<? extends TaskInternal> identity = TaskIdentity.create(name, type, project);
+        final TaskIdentity<? extends TaskInternal> identity = taskIdentityFactory.create(name, type, project);
         return buildOperationExecutor.call(new CallableBuildOperation<Task>() {
             @Override
             public BuildOperationDescriptor.Builder description() {
@@ -165,7 +171,7 @@ public Task call(BuildOperationContext context) {
                         Action<? super Task> taskAction = Cast.uncheckedCast(action);
                         task.doFirst(taskAction);
                     } else if (action != null) {
-                        Closure closure = (Closure) action;
+                        Closure<?> closure = (Closure<?>) action;
                         task.doFirst(closure);
                     }
 
@@ -184,7 +190,7 @@ public Task call(BuildOperationContext context) {
         Object constructorArgs = args.get(Task.TASK_CONSTRUCTOR_ARGS);
         if (constructorArgs instanceof List) {
             List<?> asList = (List<?>) constructorArgs;
-            return asList.toArray(new Object[asList.size()]);
+            return asList.toArray();
         }
         if (constructorArgs instanceof Object[]) {
             return (Object[]) constructorArgs;
@@ -198,9 +204,9 @@ public Task call(BuildOperationContext context) {
     private static Map<String, ?> checkTaskArgsAndCreateDefaultValues(Map<String, ?> args) {
         validateArgs(args);
         if (!args.keySet().containsAll(MANDATORY_TASK_ARGUMENTS)) {
-            Map<String, Object> argsWithDefaults = Maps.newHashMap(args);
-            setIfNull(argsWithDefaults, Task.TASK_NAME, "");
-            setIfNull(argsWithDefaults, Task.TASK_TYPE, DefaultTask.class);
+            Map<String, Object> argsWithDefaults = new HashMap<>(args);
+            argsWithDefaults.putIfAbsent(Task.TASK_NAME, "");
+            argsWithDefaults.putIfAbsent(Task.TASK_TYPE, DefaultTask.class);
             return argsWithDefaults;
         }
         return args;
@@ -208,19 +214,13 @@ public Task call(BuildOperationContext context) {
 
     private static void validateArgs(Map<String, ?> args) {
         if (!VALID_TASK_ARGUMENTS.containsAll(args.keySet())) {
-            Map<String, Object> unknownArguments = new HashMap<String, Object>(args);
+            Map<String, Object> unknownArguments = new HashMap<>(args);
             unknownArguments.keySet().removeAll(VALID_TASK_ARGUMENTS);
             throw new InvalidUserDataException(String.format("Could not create task '%s': Unknown argument(s) in task definition: %s",
                 args.get(Task.TASK_NAME), unknownArguments.keySet()));
         }
     }
 
-    private static void setIfNull(Map<String, Object> map, String key, Object defaultValue) {
-        if (map.get(key) == null) {
-            map.put(key, defaultValue);
-        }
-    }
-
     private <T extends Task> void addTask(T task, boolean replaceExisting) {
         String name = task.getName();
 
@@ -253,7 +253,7 @@ private <T extends Task> void addTask(T task, boolean replaceExisting) {
         addInternal(task);
     }
 
-    private void failOnDuplicateTask(String task) {
+    private static void failOnDuplicateTask(String task) {
         throw new DuplicateTaskException(String.format("Cannot add task '%s' as a task with that name already exists.", task));
     }
 
@@ -288,19 +288,25 @@ public <T extends Task> T create(final String name, final Class<T> type, final O
      * @param constructorArgs null == do not invoke constructor, empty == invoke constructor with no args, non-empty = invoke constructor with args
      */
     private <T extends Task> T doCreate(final String name, final Class<T> type, @Nullable final Object[] constructorArgs, final Action<? super T> configureAction) throws InvalidUserDataException {
-        final TaskIdentity<T> identity = TaskIdentity.create(name, type, project);
+        return doCreate(taskIdentityFactory.create(name, type, project), constructorArgs, configureAction);
+    }
+
+    /**
+     * @param constructorArgs null == do not invoke constructor, empty == invoke constructor with no args, non-empty = invoke constructor with args
+     */
+    private <T extends Task> T doCreate(TaskIdentity<T> identity, @Nullable final Object[] constructorArgs, final Action<? super T> configureAction) throws InvalidUserDataException {
         return buildOperationExecutor.call(new CallableBuildOperation<T>() {
             @Override
             public T call(BuildOperationContext context) {
                 try {
                     T task = createTask(identity, constructorArgs);
-                    statistics.eagerTask(type);
+                    statistics.eagerTask(identity.type);
                     addTask(task, false);
                     configureAction.execute(task);
                     context.setResult(REALIZE_RESULT);
                     return task;
                 } catch (Throwable t) {
-                    throw taskCreationException(name, t);
+                    throw taskCreationException(identity.name, t);
                 }
             }
 
@@ -398,7 +404,7 @@ private <T extends Task> TaskProvider<T> registerTask(final String name, final C
             failOnDuplicateTask(name);
         }
 
-        final TaskIdentity<T> identity = TaskIdentity.create(name, type, project);
+        final TaskIdentity<T> identity = taskIdentityFactory.create(name, type, project);
 
         TaskProvider<T> provider = buildOperationExecutor.call(new CallableBuildOperation<TaskProvider<T>>() {
             @Override
@@ -429,7 +435,7 @@ public TaskProvider<T> call(BuildOperationContext context) {
     @Override
     public <T extends Task> T replace(final String name, final Class<T> type) {
         assertMutable("replace(String, Class)");
-        final TaskIdentity<T> identity = TaskIdentity.create(name, type, project);
+        final TaskIdentity<T> identity = taskIdentityFactory.create(name, type, project);
         return buildOperationExecutor.call(new CallableBuildOperation<T>() {
             @Override
             public T call(BuildOperationContext context) {
@@ -451,9 +457,9 @@ public BuildOperationDescriptor.Builder description() {
     }
 
     @Override
-    public <T extends Task> T createWithoutConstructor(String name, Class<T> type) {
+    public <T extends Task> T createWithoutConstructor(String name, Class<T> type, long uniqueId) {
         assertMutable("createWithoutConstructor(String, Class, Object...)");
-        return doCreate(name, type, null, Actions.doNothing());
+        return doCreate(taskIdentityFactory.recreate(name, type, project, uniqueId), null, Actions.doNothing());
     }
 
     @Override
@@ -513,7 +519,7 @@ public SortedSet<String> getNames() {
         if (modelNode == null) {
             return names;
         } else {
-            TreeSet<String> allNames = new TreeSet<String>(names);
+            TreeSet<String> allNames = new TreeSet<>(names);
             allNames.addAll(modelNode.getLinkNames());
             return allNames;
         }
@@ -565,8 +571,8 @@ public Task findByName(String name) {
         return super.findByNameWithoutRules(name);
     }
 
-    private Task realizeTask(ModelPath taskPath, ModelNode.State minState) {
-        return project.getModelRegistry().atStateOrLater(taskPath, ModelType.of(Task.class), minState);
+    private void realizeTask(ModelPath taskPath, ModelNode.State minState) {
+        project.getModelRegistry().atStateOrLater(taskPath, ModelType.of(Task.class), minState);
     }
 
     @Override
@@ -595,8 +601,8 @@ public boolean remove(Object o) {
         throw unsupportedTaskRemovalException();
     }
 
-    private boolean removeInternal(Object o) {
-        return super.remove(o);
+    private void removeInternal(Object o) {
+        super.remove(o);
     }
 
     @Deprecated
@@ -779,13 +785,6 @@ public DuplicateTaskException(String message) {
         }
     }
 
-    @Contextual
-    private static class IncompatibleTaskTypeException extends InvalidUserDataException {
-        public IncompatibleTaskTypeException(String message) {
-            super(message);
-        }
-    }
-
     private static final class RealizeDetails implements RealizeTaskBuildOperationType.Details {
 
         private final TaskIdentity<?> identity;
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java
index ba93d89..bdc8d4d 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskContainerFactory.java
@@ -22,6 +22,7 @@
 import org.gradle.api.internal.project.CrossProjectConfigurator;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.internal.project.taskfactory.TaskIdentityFactory;
 import org.gradle.api.tasks.TaskContainer;
 import org.gradle.internal.BiAction;
 import org.gradle.internal.Factory;
@@ -53,6 +54,7 @@ public class DefaultTaskContainerFactory implements Factory<TaskContainerInterna
     private static final ModelReference<Task> TASK_MODEL_REFERENCE = ModelReference.of(TASK_MODEL_TYPE);
     private static final SimpleModelRuleDescriptor COPY_TO_TASK_CONTAINER_DESCRIPTOR = new SimpleModelRuleDescriptor("copyToTaskContainer");
     private final Instantiator instantiator;
+    private final TaskIdentityFactory taskIdentityFactory;
     private final ITaskFactory taskFactory;
     private final CollectionCallbackActionDecorator callbackDecorator;
     private final ProjectInternal project;
@@ -60,14 +62,18 @@ public class DefaultTaskContainerFactory implements Factory<TaskContainerInterna
     private final BuildOperationExecutor buildOperationExecutor;
     private final CrossProjectConfigurator crossProjectConfigurator;
 
-    public DefaultTaskContainerFactory(Instantiator instantiator,
-                                       ITaskFactory taskFactory,
-                                       ProjectInternal project,
-                                       TaskStatistics statistics,
-                                       BuildOperationExecutor buildOperationExecutor,
-                                       CrossProjectConfigurator crossProjectConfigurator,
-                                       CollectionCallbackActionDecorator callbackDecorator) {
+    public DefaultTaskContainerFactory(
+        Instantiator instantiator,
+        TaskIdentityFactory taskIdentityFactory,
+        ITaskFactory taskFactory,
+        ProjectInternal project,
+        TaskStatistics statistics,
+        BuildOperationExecutor buildOperationExecutor,
+        CrossProjectConfigurator crossProjectConfigurator,
+        CollectionCallbackActionDecorator callbackDecorator
+    ) {
         this.instantiator = instantiator;
+        this.taskIdentityFactory = taskIdentityFactory;
         this.taskFactory = taskFactory;
         this.project = project;
         this.statistics = statistics;
@@ -78,7 +84,7 @@ public DefaultTaskContainerFactory(Instantiator instantiator,
 
     @Override
     public TaskContainerInternal create() {
-        DefaultTaskContainer tasks = instantiator.newInstance(DefaultTaskContainer.class, project, instantiator, taskFactory, statistics, buildOperationExecutor, crossProjectConfigurator, callbackDecorator);
+        DefaultTaskContainer tasks = instantiator.newInstance(DefaultTaskContainer.class, project, instantiator, taskIdentityFactory, taskFactory, statistics, buildOperationExecutor, crossProjectConfigurator, callbackDecorator);
         bridgeIntoSoftwareModelWhenNeeded(tasks);
         return tasks;
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskOutputs.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
index cf6e1b2..335db4a 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskOutputs.java
@@ -24,7 +24,7 @@
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.FilePropertyContainer;
 import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.api.internal.TaskOutputsEnterpriseInternal;
 import org.gradle.api.internal.file.CompositeFileCollection;
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.internal.file.FileCollectionInternal;
@@ -50,11 +50,12 @@
 import java.util.function.Consumer;
 
 @NonNullApi
-public class DefaultTaskOutputs implements TaskOutputsInternal {
+public class DefaultTaskOutputs implements TaskOutputsEnterpriseInternal {
     private final FileCollection allOutputFiles;
     private final PropertyWalker propertyWalker;
     private final FileCollectionFactory fileCollectionFactory;
     private AndSpec<TaskInternal> upToDateSpec = AndSpec.empty();
+    private boolean storeInCache = true;
     private final List<SelfDescribingSpec<TaskInternal>> cacheIfSpecs = new LinkedList<>();
     private final List<SelfDescribingSpec<TaskInternal>> doNotCacheIfSpecs = new LinkedList<>();
     private FileCollection previousOutputFiles;
@@ -97,6 +98,17 @@ public void upToDateWhen(final Spec<? super Task> spec) {
     }
 
     @Override
+    public boolean getStoreInCache() {
+        return storeInCache;
+    }
+
+    @Override
+    public void doNotStoreInCache() {
+        // Not wrapped in TaskMutator to allow calls during task execution
+        storeInCache = false;
+    }
+
+    @Override
     public void cacheIf(final Spec<? super Task> spec) {
         cacheIf("Task outputs cacheable", spec);
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskRequiredServices.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskRequiredServices.java
index 5c4173b..144ce46 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskRequiredServices.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/DefaultTaskRequiredServices.java
@@ -18,22 +18,22 @@
 
 import com.google.common.collect.ImmutableSet;
 import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.provider.DefaultProperty;
-import org.gradle.api.internal.provider.ProviderInternal;
+import org.gradle.api.internal.tasks.properties.GetServiceReferencesVisitor;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.services.BuildService;
 import org.gradle.api.services.internal.BuildServiceProvider;
+import org.gradle.api.services.internal.ConsumedBuildServiceProvider;
+import org.gradle.api.services.internal.RegisteredBuildServiceProvider;
 import org.gradle.internal.Cast;
-import org.gradle.internal.properties.PropertyValue;
-import org.gradle.internal.properties.PropertyVisitor;
 import org.gradle.internal.properties.bean.PropertyWalker;
+import org.gradle.internal.reflect.validation.TypeValidationContext;
 
 import javax.annotation.Nullable;
+import java.util.Collection;
 import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
 import java.util.Set;
-import java.util.function.Consumer;
-
-import static java.util.Collections.emptySet;
 
 public class DefaultTaskRequiredServices implements TaskRequiredServices {
     private final TaskInternal task;
@@ -44,7 +44,10 @@ public class DefaultTaskRequiredServices implements TaskRequiredServices {
      * Services registered explicitly via Task#usesService(provider).
      */
     @Nullable
-    private Set<Provider<? extends BuildService<?>>> registeredServices;
+    private Set<RegisteredBuildServiceProvider<?, ?>> registeredServices;
+
+    @Nullable
+    private Collection<? extends BuildServiceProvider<?, ?>> consumedServices;
 
     public DefaultTaskRequiredServices(TaskInternal task, TaskMutator taskMutator, PropertyWalker propertyWalker) {
         this.task = task;
@@ -54,53 +57,67 @@ public DefaultTaskRequiredServices(TaskInternal task, TaskMutator taskMutator, P
 
     @Override
     public Set<Provider<? extends BuildService<?>>> getElements() {
-        ImmutableSet.Builder<Provider<? extends BuildService<?>>> allUsedServices = ImmutableSet.builder();
-        Set<Provider<? extends BuildService<?>>> registeredServices = this.registeredServices != null ? this.registeredServices : emptySet();
-        allUsedServices.addAll(registeredServices);
-        collectRequiredServices(allUsedServices);
-        return allUsedServices.build();
+        return getElements(false);
+    }
+
+    @Override
+    public Set<Provider<? extends BuildService<?>>> searchServices() {
+        return getElements(true);
+    }
+
+    private Set<Provider<? extends BuildService<?>>> getElements(boolean search) {
+        ImmutableSet.Builder<BuildServiceProvider<?, ?>> allServicesUsed = ImmutableSet.builder();
+        if (registeredServices != null) {
+            allServicesUsed.addAll(registeredServices);
+        }
+        if (search && consumedServices == null) {
+            collectConsumedServices();
+        }
+        if (consumedServices != null) {
+            consumedServices.stream()
+                .map(it -> ((ConsumedBuildServiceProvider<?>) it).resolveIfPossible())
+                .filter(Objects::nonNull)
+                .forEach(allServicesUsed::add);
+        }
+        ImmutableSet<BuildServiceProvider<?, ?>> build = allServicesUsed.build();
+        return Cast.uncheckedCast(build);
+    }
+
+    private void collectConsumedServices() {
+        GetServiceReferencesVisitor collector = new GetServiceReferencesVisitor();
+        TaskPropertyUtils.visitAnnotatedProperties(propertyWalker, task, TypeValidationContext.NOOP, collector);
+        // this goes through task to benefit from services a task has available
+        task.acceptServiceReferences(collector.getServiceReferences());
     }
 
     @Override
     public boolean isServiceRequired(Provider<? extends BuildService<?>> toCheck) {
-        return getElements().stream().anyMatch(it -> BuildServiceProvider.isSameService(toCheck, it));
-    }
-
-    /**
-     * Collects services declared as referenced (via @ServiceReference) into the given set.
-     */
-    private void collectRequiredServices(ImmutableSet.Builder<Provider<? extends BuildService<?>>> requiredServices) {
-        visitServiceReferences(referenceProvider ->
-            requiredServices.add(asBuildServiceProvider(referenceProvider))
-        );
-    }
-
-    private Provider<? extends BuildService<?>> asBuildServiceProvider(Provider<? extends BuildService<?>> referenceProvider) {
-        if (referenceProvider instanceof DefaultProperty) {
-            DefaultProperty<?> asProperty = Cast.uncheckedNonnullCast(referenceProvider);
-            ProviderInternal<?> provider = asProperty.getProvider();
-            return Cast.uncheckedNonnullCast(provider);
-        }
-        return referenceProvider;
-    }
-
-    private void visitServiceReferences(Consumer<Provider<? extends BuildService<?>>> visitor) {
-        TaskPropertyUtils.visitProperties(propertyWalker, task, new PropertyVisitor() {
-            @Override
-            public void visitServiceReference(String propertyName, boolean optional, PropertyValue value, @Nullable String serviceName) {
-                visitor.accept(Cast.uncheckedCast(value.call()));
-            }
-        });
+        return getElements(false).stream().anyMatch(it -> BuildServiceProvider.isSameService(toCheck, it));
     }
 
     @Override
     public void registerServiceUsage(Provider<? extends BuildService<?>> service) {
         taskMutator.mutate("Task.usesService(Provider)", () -> {
-            if (registeredServices == null) {
-                registeredServices = new LinkedHashSet<>();
-            }
             // TODO:configuration-cache assert build service is from the same build as the task
-            registeredServices.add(service);
+            addRegisteredService(Cast.uncheckedNonnullCast(service));
         });
     }
+
+    private void addRegisteredService(RegisteredBuildServiceProvider<?, ?> service) {
+        if (registeredServices == null) {
+            registeredServices = new LinkedHashSet<>();
+        }
+        registeredServices.add(service);
+    }
+
+    @Override
+    public void acceptServiceReferences(List<? extends BuildServiceProvider<?, ?>> serviceReferences) {
+        // someone already collected service references for us, just remember them
+        consumedServices = serviceReferences;
+    }
+
+    @Override
+    public boolean hasServiceReferences() {
+        return consumedServices != null;
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskContainerInternal.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskContainerInternal.java
index 2332668..e191c5c 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskContainerInternal.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskContainerInternal.java
@@ -64,5 +64,5 @@ public interface TaskContainerInternal extends TaskContainer, TaskResolver, Poly
      *
      * TODO:configuration-cache - review this
      */
-    <T extends Task> T createWithoutConstructor(String name, Class<T> type);
+    <T extends Task> T createWithoutConstructor(String name, Class<T> type, long uniqueId);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskExecutionAccessChecker.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskExecutionAccessChecker.java
index e511abd..489444c 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskExecutionAccessChecker.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskExecutionAccessChecker.java
@@ -24,4 +24,5 @@
 public interface TaskExecutionAccessChecker {
     void notifyProjectAccess(TaskInternal task);
     void notifyTaskDependenciesAccess(TaskInternal task, String invocationDescription);
+    void notifyConventionAccess(TaskInternal task, String invocationDescription);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskOptionsGenerator.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskOptionsGenerator.java
new file mode 100644
index 0000000..994f929
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskOptionsGenerator.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 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.api.internal.tasks;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.gradle.api.Task;
+import org.gradle.api.internal.tasks.options.BooleanOptionElement;
+import org.gradle.api.internal.tasks.options.BuiltInOptionElement;
+import org.gradle.api.internal.tasks.options.InstanceOptionDescriptor;
+import org.gradle.api.internal.tasks.options.OptionDescriptor;
+import org.gradle.api.internal.tasks.options.OptionElement;
+import org.gradle.api.internal.tasks.options.OptionReader;
+import org.gradle.api.specs.Specs;
+import org.gradle.cli.CommandLineParser;
+import org.gradle.internal.Pair;
+import org.gradle.util.internal.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is responsible for generating the built-in options and
+ * the {@link BooleanOptionElement mutually exclusive options} of a task,
+ * based on the task options provided by the {@link OptionReader}.
+ */
+public class TaskOptionsGenerator {
+    private static final Logger LOGGER = LoggerFactory.getLogger(TaskOptionsGenerator.class);
+
+    @VisibleForTesting
+    static final List<BuiltInOptionElement> BUILT_IN_OPTIONS = Collections.singletonList(
+        new BuiltInOptionElement(
+            "Causes the task to be re-run even if up-to-date.",
+            "rerun",
+            task -> task.getOutputs().upToDateWhen(Specs.satisfyNone())
+        )
+    );
+
+    /**
+     * Builds a list of implicit built-in options available for every task.
+     */
+    private static List<OptionDescriptor> generateBuiltInOptions(Object target, Collection<String> reserved) {
+        List<OptionDescriptor> validBuiltInOptions = new ArrayList<>();
+        for (BuiltInOptionElement builtInOption : BUILT_IN_OPTIONS) {
+            OptionDescriptor optionDescriptor = new InstanceOptionDescriptor(target, builtInOption, null, reserved.contains(builtInOption.getOptionName()));
+            if (optionDescriptor.isClashing()) {
+                LOGGER.warn("Built-in option '{}' in task {} was disabled for clashing with another option of same name", optionDescriptor.getName(), target);
+            } else {
+                validBuiltInOptions.add(optionDescriptor);
+            }
+        }
+        return validBuiltInOptions;
+    }
+
+    /**
+     * Generates a map of opposite options and, based on these, adds {@link Pair pairs}
+     * of mutually exclusive options to the {@link TaskOptions#mutuallyExclusiveOptions taskOptions} argument.
+     */
+    private static Map<String, OptionDescriptor> generateOppositeOptions(Object target, Map<String, OptionDescriptor> options, TaskOptions taskOptions) {
+        Map<String, OptionDescriptor> oppositeOptions = new HashMap<>();
+        List<Pair<OptionDescriptor, OptionDescriptor>> mutuallyExclusiveOptions = new LinkedList<>();
+
+        for (OptionDescriptor option : options.values()) {
+            if (option instanceof InstanceOptionDescriptor) {
+                OptionElement optionElement = ((InstanceOptionDescriptor) option).getOptionElement();
+                if (optionElement instanceof BooleanOptionElement) {
+                    BooleanOptionElement oppositeOptionElement = BooleanOptionElement.oppositeOf((BooleanOptionElement) optionElement);
+                    String oppositeOptionName = oppositeOptionElement.getOptionName();
+                    if (options.containsKey(oppositeOptionName)) {
+                        LOGGER.warn("Opposite option '{}' in task {} was disabled for clashing with another option of same name", oppositeOptionName, target);
+                    } else {
+                        OptionDescriptor oppositeOption = new InstanceOptionDescriptor(target, oppositeOptionElement, null);
+                        oppositeOptions.put(oppositeOptionName, oppositeOption);
+                        mutuallyExclusiveOptions.add(Pair.of(option, oppositeOption));
+                    }
+                }
+            }
+        }
+        taskOptions.mutuallyExclusiveOptions = Collections.unmodifiableList(mutuallyExclusiveOptions);
+        return oppositeOptions;
+    }
+
+    /**
+     * Generates the {@link TaskOptions taskOptions} for the given task.
+     *
+     * @param task the task, must implement {@link Task}
+     * @param optionReader the optionReader
+     * @return the generated taskOptions
+     */
+    public static TaskOptions generate(Object task, OptionReader optionReader) {
+        TaskOptions taskOptions = new TaskOptions();
+        Map<String, OptionDescriptor> options = optionReader.getOptions(task);
+        options.putAll(generateOppositeOptions(task, options, taskOptions));
+        List<OptionDescriptor> sortedOptions = CollectionUtils.sort(options.values());
+        sortedOptions.addAll(generateBuiltInOptions(task, options.keySet()));
+        taskOptions.allTaskOptions = Collections.unmodifiableList(sortedOptions);
+        return taskOptions;
+    }
+
+    public static class TaskOptions {
+        private List<OptionDescriptor> allTaskOptions;
+        private List<Pair<OptionDescriptor, OptionDescriptor>> mutuallyExclusiveOptions;
+        private TaskOptions() {}
+
+        public List<OptionDescriptor> getAll() {
+            return allTaskOptions;
+        }
+
+        public void addMutualExclusions(CommandLineParser parser) {
+            mutuallyExclusiveOptions.forEach(pair -> parser.allowOneOf(pair.getLeft().getName(), pair.getRight().getName()));
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskRequiredServices.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskRequiredServices.java
index 3e61d99..268f6b7 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskRequiredServices.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/TaskRequiredServices.java
@@ -18,7 +18,9 @@
 
 import org.gradle.api.provider.Provider;
 import org.gradle.api.services.BuildService;
+import org.gradle.api.services.internal.BuildServiceProvider;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -31,6 +33,8 @@ public interface TaskRequiredServices {
      * Returns services required, be it using {@link org.gradle.api.Task#usesService} or
      * by annotating a property as {@link org.gradle.api.services.ServiceReference}.
      */
+    Set<Provider<? extends BuildService<?>>> searchServices();
+
     Set<Provider<? extends BuildService<?>>> getElements();
 
     /**
@@ -38,4 +42,8 @@ public interface TaskRequiredServices {
      * by annotating a property as {@link org.gradle.api.services.ServiceReference}.
      */
     boolean isServiceRequired(Provider<? extends BuildService<?>> toCheck);
+
+    void acceptServiceReferences(List<? extends BuildServiceProvider<?, ?>> serviceReferences);
+
+    boolean hasServiceReferences();
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/DefaultTaskCacheabilityResolver.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/DefaultTaskCacheabilityResolver.java
index e42118d..9d10739 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/DefaultTaskCacheabilityResolver.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/DefaultTaskCacheabilityResolver.java
@@ -60,7 +60,10 @@ public Optional<CachingDisabledReason> shouldDisableCaching(
                 }
             }
 
-            return Optional.of(CACHING_NOT_ENABLED);
+            return Optional.of(task.getReasonNotToTrackState()
+                .map(s -> new CachingDisabledReason(CachingDisabledReasonCategory.NOT_CACHEABLE, "Task is untracked because: " + s))
+                .orElse(CACHING_NOT_ENABLED));
+
         }
 
         if (!taskProperties.hasDeclaredOutputs()) {
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/TaskExecution.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/TaskExecution.java
index 8ba5ca4..4d915cf 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/TaskExecution.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/TaskExecution.java
@@ -21,7 +21,7 @@
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.GeneratedSubclasses;
 import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.TaskOutputsInternal;
+import org.gradle.api.internal.TaskOutputsEnterpriseInternal;
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.file.collections.LazilyInitializedFileCollection;
@@ -160,10 +160,11 @@ public WorkOutput execute(ExecutionRequest executionRequest) {
         FileCollection previousFiles = executionRequest.getPreviouslyProducedOutputs()
             .<FileCollection>map(previousOutputs -> new PreviousOutputFileCollection(task, taskDependencyFactory, fileCollectionFactory, previousOutputs))
             .orElseGet(FileCollectionFactory::empty);
-        TaskOutputsInternal outputs = task.getOutputs();
+        TaskOutputsEnterpriseInternal outputs = (TaskOutputsEnterpriseInternal) task.getOutputs();
         outputs.setPreviousOutputFiles(previousFiles);
         try {
             WorkResult didWork = executeWithPreviousOutputFiles(executionRequest.getInputChanges().orElse(null));
+            boolean storeInCache = outputs.getStoreInCache();
             return new WorkOutput() {
                 @Override
                 public WorkResult getDidWork() {
@@ -174,6 +175,11 @@ public WorkResult getDidWork() {
                 public Object getOutput() {
                     return null;
                 }
+
+                @Override
+                public boolean canStoreInCache() {
+                    return storeInCache;
+                }
             };
         } finally {
             outputs.setPreviousOutputFiles(null);
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/TaskExecutionAccessListener.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/TaskExecutionAccessListener.java
index 0597c9c..c056bf5 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/TaskExecutionAccessListener.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/execution/TaskExecutionAccessListener.java
@@ -20,6 +20,8 @@
 import org.gradle.internal.service.scopes.EventScope;
 import org.gradle.internal.service.scopes.Scopes;
 
+import javax.annotation.Nullable;
+
 
 @EventScope(Scopes.Build.class)
 public interface TaskExecutionAccessListener {
@@ -27,10 +29,16 @@ public interface TaskExecutionAccessListener {
     /**
      * Called when accessing the project during task execution.
      */
-    void onProjectAccess(String invocationDescription, TaskInternal task);
+    void onProjectAccess(String invocationDescription, TaskInternal task, @Nullable TaskInternal runningTask);
 
     /**
      * Called when accessing task dependencies during task execution.
      */
-    void onTaskDependenciesAccess(String invocationDescription, TaskInternal task);
+    void onTaskDependenciesAccess(String invocationDescription, TaskInternal task, @Nullable TaskInternal runningTask);
+
+    /**
+     * Called when accessing the convention object during task execution.
+     */
+    void onConventionAccess(String invocationDescription, TaskInternal task, @Nullable TaskInternal runningTask);
+
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/BooleanOptionElement.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/BooleanOptionElement.java
index 92f7700..e547d42 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/BooleanOptionElement.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/BooleanOptionElement.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.tasks.options;
 
+import org.gradle.api.internal.tasks.TaskOptionsGenerator;
 import org.gradle.api.tasks.options.Option;
 import org.gradle.internal.typeconversion.TypeConversionException;
 
@@ -25,8 +26,16 @@
 
 /**
  * A flag, does not take an argument.
+ *
+ * If a command line option is provided, the {@link TaskOptionsGenerator} automatically creates an opposite option.
+ * For example, {@code "--no-foo"} is created for the provided option {@code "--foo"} or {@code "--bar"} for the provided option {@code "--no-bar"}.
+ *
+ * Options whose names starts with "--no" are 'disable options' and set the option value to false.
  */
 public class BooleanOptionElement extends AbstractOptionElement {
+    private static final String DISABLE_DESC_PREFIX = "Disables option --";
+    private static final String OPPOSITE_DESC_PREFIX = "Opposite option of --";
+    private static final String DISABLE_NAME_PREFIX = "no-";
     private final PropertySetter setter;
 
     public BooleanOptionElement(String optionName, Option option, PropertySetter setter) {
@@ -34,6 +43,21 @@ public BooleanOptionElement(String optionName, Option option, PropertySetter set
         this.setter = setter;
     }
 
+    private BooleanOptionElement(String optionName, String optionDescription, PropertySetter setter) {
+        super(optionDescription, optionName, Void.TYPE);
+        this.setter = setter;
+    }
+
+    public static BooleanOptionElement oppositeOf(BooleanOptionElement optionElement) {
+        String optionName = optionElement.getOptionName();
+        return optionElement.isDisableOption() ? new BooleanOptionElement(removeDisablePrefix(optionName), OPPOSITE_DESC_PREFIX + optionName, optionElement.setter)
+            : new BooleanOptionElement(DISABLE_NAME_PREFIX + optionName, DISABLE_DESC_PREFIX + optionName, optionElement.setter);
+    }
+
+    public boolean isDisableOption() {
+        return this.getOptionName().startsWith(DISABLE_NAME_PREFIX);
+    }
+
     @Override
     public Set<String> getAvailableValues() {
         return Collections.emptySet();
@@ -41,6 +65,14 @@ public Set<String> getAvailableValues() {
 
     @Override
     public void apply(Object object, List<String> parameterValues) throws TypeConversionException {
-        setter.setValue(object, Boolean.TRUE);
+        if (isDisableOption()) {
+            setter.setValue(object, Boolean.FALSE);
+        } else {
+            setter.setValue(object, Boolean.TRUE);
+        }
+    }
+
+    private static String removeDisablePrefix(String optionName) {
+        return optionName.substring(DISABLE_NAME_PREFIX.length());
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/InstanceOptionDescriptor.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/InstanceOptionDescriptor.java
index e9ade19..1811d3c 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/InstanceOptionDescriptor.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/InstanceOptionDescriptor.java
@@ -46,6 +46,10 @@ public InstanceOptionDescriptor(Object object, OptionElement optionElement, Java
         this.clashing = clashing;
     }
 
+    public OptionElement getOptionElement() {
+        return this.optionElement;
+    }
+
     @Override
     public String getName() {
         return optionElement.getOptionName();
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/OptionReader.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/OptionReader.java
index b7efb13..ed9f3d8 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/OptionReader.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/options/OptionReader.java
@@ -16,10 +16,8 @@
 
 package org.gradle.api.internal.tasks.options;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ListMultimap;
-import org.gradle.api.specs.Specs;
 import org.gradle.api.tasks.options.Option;
 import org.gradle.api.tasks.options.OptionValues;
 import org.gradle.internal.reflect.JavaMethod;
@@ -36,43 +34,18 @@
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 public class OptionReader {
     private final ListMultimap<Class<?>, OptionElement> cachedOptionElements = ArrayListMultimap.create();
     private final Map<OptionElement, JavaMethod<Object, Collection>> cachedOptionValueMethods = new HashMap<OptionElement, JavaMethod<Object, Collection>>();
     private final OptionValueNotationParserFactory optionValueNotationParserFactory = new OptionValueNotationParserFactory();
 
-    @VisibleForTesting
-    static final Map<String, BuiltInOptionElement> BUILT_IN_OPTIONS = Stream.of(
-        new BuiltInOptionElement(
-            "Causes the task to be re-run even if up-to-date.",
-            "rerun",
-            task -> task.getOutputs().upToDateWhen(Specs.satisfyNone())
-        )
-    ).collect(Collectors.toMap(BuiltInOptionElement::getOptionName, Function.identity(), (a, b) -> a, LinkedHashMap::new));
 
-    /**
-     * Builds a list of implicit built-in options available for every task.
-     */
-    private List<OptionDescriptor> buildBuiltInOptions(Object target, Collection<String> reserved) {
-        return BUILT_IN_OPTIONS.values().stream().map(optionElement ->
-            new InstanceOptionDescriptor(target, optionElement, null, reserved.contains(optionElement.getOptionName()))
-        ).collect(Collectors.toList());
-    }
-
-    public List<OptionDescriptor> getOptions(Object target) {
-        return getOptions(target, true);
-    }
-
-    public List<OptionDescriptor> getOptions(Object target, boolean validOnly) {
+    public Map<String, OptionDescriptor> getOptions(Object target) {
         final Class<?> targetClass = target.getClass();
         Map<String, OptionDescriptor> options = new HashMap<String, OptionDescriptor>();
         if (!cachedOptionElements.containsKey(targetClass)) {
@@ -82,15 +55,7 @@ public List<OptionDescriptor> getOptions(Object target, boolean validOnly) {
             JavaMethod<Object, Collection> optionValueMethod = cachedOptionValueMethods.get(optionElement);
             options.put(optionElement.getOptionName(), new InstanceOptionDescriptor(target, optionElement, optionValueMethod));
         }
-        List<OptionDescriptor> taskOptions = CollectionUtils.sort(options.values());
-        for (OptionDescriptor builtInOption : buildBuiltInOptions(target, options.keySet())) {
-            // built-in options only enabled if they do not clash with task-declared ones
-            if (!validOnly || !builtInOption.isClashing()) {
-                taskOptions.add(builtInOption);
-            }
-        }
-
-        return taskOptions;
+        return options;
     }
 
     private void loadClassDescriptorInCache(Object target) {
@@ -255,5 +220,4 @@ private static JavaMethod<Object, Collection> getAsOptionValuesMethod(Class<?> t
                     type.getName()));
         }
     }
-
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/AbstractValidatingProperty.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/AbstractValidatingProperty.java
index e5f444c..cf9d94e 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/AbstractValidatingProperty.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/AbstractValidatingProperty.java
@@ -71,7 +71,7 @@ public void validate(PropertyValidationContext context) {
     private static boolean isPresent(@Nullable Object value) {
         if (value instanceof Provider) {
             // carefully check for presence without necessarily resolving
-            return ((Provider) value).isPresent();
+            return ((Provider<?>) value).isPresent();
         }
         return value != null;
     }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/CompositePropertyVisitor.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/CompositePropertyVisitor.java
index 182702e..96633b5 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/CompositePropertyVisitor.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/CompositePropertyVisitor.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.tasks.properties;
 
+import org.gradle.api.services.BuildService;
 import org.gradle.internal.fingerprint.DirectorySensitivity;
 import org.gradle.internal.fingerprint.FileNormalizer;
 import org.gradle.internal.fingerprint.LineEndingSensitivity;
@@ -88,9 +89,9 @@ public void visitLocalStateProperty(Object value) {
     }
 
     @Override
-    public void visitServiceReference(String propertyName, boolean optional, PropertyValue value, @Nullable String serviceName) {
+    public void visitServiceReference(String propertyName, boolean optional, PropertyValue value, @Nullable String serviceName, Class<? extends BuildService<?>> buildServiceType) {
         for (PropertyVisitor visitor : visitors) {
-            visitor.visitServiceReference(propertyName, optional, value, serviceName);
+            visitor.visitServiceReference(propertyName, optional, value, serviceName, buildServiceType);
         }
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/DefaultServiceReferenceSpec.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/DefaultServiceReferenceSpec.java
new file mode 100644
index 0000000..9bb8865
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/DefaultServiceReferenceSpec.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.properties;
+
+import org.gradle.api.services.BuildService;
+
+public class DefaultServiceReferenceSpec extends AbstractPropertySpec implements ServiceReferenceSpec {
+    private final Class<? extends BuildService<?>> buildServiceType;
+    private final String buildServiceName;
+
+    protected DefaultServiceReferenceSpec(String propertyName, Class<? extends BuildService<?>> buildServiceType, String buildServiceName) {
+        super(propertyName);
+        this.buildServiceType = buildServiceType;
+        this.buildServiceName = buildServiceName;
+    }
+
+    @Override
+    public Class<? extends BuildService<?>> getBuildServiceType() {
+        return buildServiceType;
+    }
+
+    @Override
+    public String getBuildServiceName() {
+        return buildServiceName;
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/DefaultTaskProperties.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/DefaultTaskProperties.java
index a8d74fe..8205c6d 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/DefaultTaskProperties.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/DefaultTaskProperties.java
@@ -23,6 +23,7 @@
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.internal.tasks.TaskDependencyContainer;
 import org.gradle.api.internal.tasks.TaskPropertyUtils;
+import org.gradle.api.services.BuildService;
 import org.gradle.api.tasks.TaskExecutionException;
 import org.gradle.internal.fingerprint.DirectorySensitivity;
 import org.gradle.internal.fingerprint.FileNormalizer;
@@ -46,6 +47,7 @@ public class DefaultTaskProperties implements TaskProperties {
     private final ImmutableSortedSet<InputPropertySpec> inputProperties;
     private final ImmutableSortedSet<InputFilePropertySpec> inputFileProperties;
     private final ImmutableSortedSet<OutputFilePropertySpec> outputFileProperties;
+    private final ImmutableSortedSet<ServiceReferenceSpec> serviceReferences;
     private final boolean hasDeclaredOutputs;
     private final ReplayingTypeValidationContext validationProblems;
     private final FileCollection localStateFiles;
@@ -56,6 +58,7 @@ public static TaskProperties resolve(PropertyWalker propertyWalker, FileCollecti
         String beanName = task.toString();
         GetInputPropertiesVisitor inputPropertiesVisitor = new GetInputPropertiesVisitor();
         GetInputFilesVisitor inputFilesVisitor = new GetInputFilesVisitor(beanName, fileCollectionFactory);
+        GetServiceReferencesVisitor serviceReferencesVisitor = new GetServiceReferencesVisitor();
         ValidationVisitor validationVisitor = new ValidationVisitor();
         OutputFilesCollector outputFilesCollector = new OutputFilesCollector();
         OutputUnpacker outputUnpacker = new OutputUnpacker(
@@ -75,7 +78,8 @@ public static TaskProperties resolve(PropertyWalker propertyWalker, FileCollecti
                 outputUnpacker,
                 validationVisitor,
                 destroyablesVisitor,
-                localStateVisitor
+                localStateVisitor,
+                serviceReferencesVisitor
             ));
         } catch (Exception e) {
             throw new TaskExecutionException(task, e);
@@ -85,6 +89,7 @@ public static TaskProperties resolve(PropertyWalker propertyWalker, FileCollecti
             inputPropertiesVisitor.getProperties(),
             inputFilesVisitor.getFileProperties(),
             outputFilesCollector.getFileProperties(),
+            serviceReferencesVisitor.getServiceReferences(),
             outputUnpacker.hasDeclaredOutputs(),
             localStateVisitor.getFiles(),
             destroyablesVisitor.getFiles(),
@@ -96,6 +101,7 @@ private DefaultTaskProperties(
         ImmutableSortedSet<InputPropertySpec> inputProperties,
         ImmutableSortedSet<InputFilePropertySpec> inputFileProperties,
         ImmutableSortedSet<OutputFilePropertySpec> outputFileProperties,
+        ImmutableSortedSet<ServiceReferenceSpec> serviceReferences,
         boolean hasDeclaredOutputs,
         FileCollection localStateFiles,
         FileCollection destroyableFiles,
@@ -108,6 +114,7 @@ private DefaultTaskProperties(
         this.inputProperties = inputProperties;
         this.inputFileProperties = inputFileProperties;
         this.outputFileProperties = outputFileProperties;
+        this.serviceReferences = serviceReferences;
         this.hasDeclaredOutputs = hasDeclaredOutputs;
         this.localStateFiles = localStateFiles;
         this.destroyableFiles = destroyableFiles;
@@ -129,6 +136,11 @@ public ImmutableSortedSet<InputFilePropertySpec> getInputFileProperties() {
     }
 
     @Override
+    public ImmutableSortedSet<ServiceReferenceSpec> getServiceReferences() {
+        return serviceReferences;
+    }
+
+    @Override
     public void validateType(TypeValidationContext validationContext) {
         validationProblems.replay(null, validationContext);
     }
@@ -238,7 +250,7 @@ public void visitEmptyOutputFileProperty(String propertyName, boolean optional,
         }
 
         @Override
-        public void visitServiceReference(String propertyName, boolean optional, PropertyValue value, @Nullable String serviceName) {
+        public void visitServiceReference(String propertyName, boolean optional, PropertyValue value, @Nullable String serviceName, Class<? extends BuildService<?>> buildServiceType) {
             // Service reference declared via annotation, validate it
             taskPropertySpecs.add(new DefaultValidatingProperty(propertyName, value, optional, ValidationActions.NO_OP));
         }
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/GetServiceReferencesVisitor.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/GetServiceReferencesVisitor.java
new file mode 100644
index 0000000..efc1a6a
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/GetServiceReferencesVisitor.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.properties;
+
+import com.google.common.collect.ImmutableSortedSet;
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.services.BuildService;
+import org.gradle.internal.properties.PropertyValue;
+import org.gradle.internal.properties.PropertyVisitor;
+
+import javax.annotation.Nullable;
+
+public class GetServiceReferencesVisitor implements PropertyVisitor {
+    private final ImmutableSortedSet.Builder<ServiceReferenceSpec> serviceReferences = ImmutableSortedSet.naturalOrder();
+
+    public ImmutableSortedSet<ServiceReferenceSpec> getServiceReferences() {
+        return serviceReferences.build();
+    }
+
+    @Override
+    public void visitServiceReference(String propertyName, boolean optional, PropertyValue value, @Nullable String serviceName, Class<? extends BuildService<?>> buildServiceType) {
+        serviceReferences.add(new DefaultServiceReferenceSpec(propertyName, buildServiceType, StringUtils.trimToEmpty(serviceName)));
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/ServiceReferenceSpec.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/ServiceReferenceSpec.java
new file mode 100644
index 0000000..6a0bde6
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/ServiceReferenceSpec.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.properties;
+
+import org.gradle.api.services.BuildService;
+
+public interface ServiceReferenceSpec extends PropertySpec {
+    Class<? extends BuildService<?>> getBuildServiceType();
+    String getBuildServiceName();
+}
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/TaskProperties.java b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/TaskProperties.java
index 00dca49..318aac8 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/TaskProperties.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/tasks/properties/TaskProperties.java
@@ -60,6 +60,15 @@ public interface TaskProperties {
     ImmutableSortedSet<OutputFilePropertySpec> getOutputFileProperties();
 
     /**
+     * Service reference properties (those annotated with {@link org.gradle.api.services.ServiceReference}).
+     *
+     * It is guaranteed that all the {@link ServiceReferenceSpec}s have a name and that the names are unique.
+     *
+     * @see org.gradle.api.services.ServiceReference
+     */
+    ImmutableSortedSet<ServiceReferenceSpec> getServiceReferences();
+
+    /**
      * Whether output properties have been declared.
      */
     boolean hasDeclaredOutputs();
diff --git a/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceProvider.java b/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceProvider.java
index 9a2b041..4c5c432 100644
--- a/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceProvider.java
+++ b/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceProvider.java
@@ -19,6 +19,7 @@
 import org.gradle.api.artifacts.component.BuildIdentifier;
 import org.gradle.api.internal.provider.AbstractMinimalProvider;
 import org.gradle.api.internal.provider.DefaultProperty;
+import org.gradle.api.internal.provider.ProviderInternal;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.services.BuildService;
 import org.gradle.api.services.BuildServiceParameters;
@@ -29,12 +30,25 @@
 
 import javax.annotation.Nonnull;
 
+import static org.gradle.internal.Cast.uncheckedCast;
+
 /**
  * A provider for build services that are registered or consumed.
  */
 @SuppressWarnings("rawtypes")
 public abstract class BuildServiceProvider<T extends BuildService<P>, P extends BuildServiceParameters> extends AbstractMinimalProvider<T> implements Managed {
 
+    static <T> Class<T> getProvidedType(Provider<T> provider) {
+        return ((ProviderInternal<T>) provider).getType();
+    }
+
+    static BuildServiceProvider<?, ?> asBuildServiceProvider(Provider<? extends BuildService<?>> service) {
+        if (service instanceof BuildServiceProvider) {
+            return uncheckedCast(service);
+        }
+        throw new UnsupportedOperationException("Unexpected provider for a build service: " + service);
+    }
+
     public interface Listener {
         Listener EMPTY = provider -> {
         };
diff --git a/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceProviderNagger.java b/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceProviderNagger.java
index 9f44341..a2fb7e5 100644
--- a/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceProviderNagger.java
+++ b/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceProviderNagger.java
@@ -17,7 +17,7 @@
 package org.gradle.api.services.internal;
 
 import org.gradle.api.internal.TaskInternal;
-import org.gradle.internal.execution.TaskExecutionTracker;
+import org.gradle.internal.execution.WorkExecutionTracker;
 
 import java.util.Optional;
 
@@ -25,9 +25,9 @@
 
 public class BuildServiceProviderNagger implements BuildServiceProvider.Listener {
 
-    private final TaskExecutionTracker taskExecutionTracker;
+    private final WorkExecutionTracker taskExecutionTracker;
 
-    public BuildServiceProviderNagger(TaskExecutionTracker taskExecutionTracker) {
+    public BuildServiceProviderNagger(WorkExecutionTracker taskExecutionTracker) {
         this.taskExecutionTracker = taskExecutionTracker;
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceRegistryInternal.java b/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceRegistryInternal.java
index 55570bc..770c036 100644
--- a/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceRegistryInternal.java
+++ b/subprojects/core/src/main/java/org/gradle/api/services/internal/BuildServiceRegistryInternal.java
@@ -65,5 +65,7 @@ public interface BuildServiceRegistryInternal extends BuildServiceRegistry {
     @Nullable
     BuildServiceRegistration<?, ?> findRegistration(Class<?> type, String name);
 
+    Set<BuildServiceRegistration<?, ?>> findRegistrations(Class<?> type, String name);
+
     List<ResourceLock> getSharedResources(Set<Provider<? extends BuildService<?>>> services);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/services/internal/ConsumedBuildServiceProvider.java b/subprojects/core/src/main/java/org/gradle/api/services/internal/ConsumedBuildServiceProvider.java
index dd7de44..8a056f6 100644
--- a/subprojects/core/src/main/java/org/gradle/api/services/internal/ConsumedBuildServiceProvider.java
+++ b/subprojects/core/src/main/java/org/gradle/api/services/internal/ConsumedBuildServiceProvider.java
@@ -28,6 +28,9 @@
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+import java.util.Set;
+import java.util.stream.Collectors;
+
 /**
  * A build service that is consumed.
  */
@@ -52,7 +55,7 @@ public ConsumedBuildServiceProvider(
 
     @Override
     protected Value<? extends T> calculateOwnValue(ValueConsumer consumer) {
-        RegisteredBuildServiceProvider<T, ?> resolvedProvider = resolve();
+        RegisteredBuildServiceProvider<T, ?> resolvedProvider = resolve(true);
         if (resolvedProvider == null) {
             return Value.missing();
         }
@@ -60,15 +63,30 @@ protected Value<? extends T> calculateOwnValue(ValueConsumer consumer) {
     }
 
     @Nullable
-    private RegisteredBuildServiceProvider<T, BuildServiceParameters> resolve() {
+    public RegisteredBuildServiceProvider<T, ?> resolveIfPossible() {
+        resolve(false);
+        return resolvedProvider;
+    }
+
+    @Nullable
+    private RegisteredBuildServiceProvider<T, BuildServiceParameters> resolve(boolean failIfAmbiguous) {
         if (resolvedProvider == null) {
             BuildServiceRegistry buildServiceRegistry = internalServices.get(BuildServiceRegistry.class);
-            BuildServiceRegistration<?, ?> registration = ((BuildServiceRegistryInternal) buildServiceRegistry).findRegistration(this.getType(), this.getName());
-            if (registration == null) {
+            Set<BuildServiceRegistration<?, ?>> results = ((BuildServiceRegistryInternal) buildServiceRegistry).findRegistrations(this.getType(), this.getName());
+            if (results.isEmpty()) {
                 return null;
             }
+            if (results.size() > 1) {
+                if (!failIfAmbiguous) {
+                    return null;
+                }
+                String names = results.stream()
+                    .map(it -> it.getName() + ": " + getProvidedType(it.getService()).getTypeName())
+                    .collect(Collectors.joining(", "));
+                throw new IllegalArgumentException(String.format("Cannot resolve service by type for type '%s' when there are two or more instances. Please also provide a service name. Instances found: %s.", getType().getTypeName(), names));
+            }
             // resolved, so remember it
-            resolvedProvider = Cast.uncheckedCast(registration.getService());
+            resolvedProvider = Cast.uncheckedCast(results.stream().findFirst().get().getService());
         }
         return resolvedProvider;
     }
@@ -90,18 +108,18 @@ public BuildIdentifier getBuildIdentifier() {
 
     @Override
     public BuildServiceDetails<T, BuildServiceParameters> getServiceDetails() {
-        BuildServiceProvider<T, BuildServiceParameters> resolvedProvider = resolve();
+        BuildServiceProvider<T, BuildServiceParameters> resolvedProvider = resolve(true);
         return resolvedProvider != null ? resolvedProvider.getServiceDetails() : new BuildServiceDetails<>(buildIdentifier, serviceName, serviceType);
     }
 
     @Override
     public ProviderInternal<T> withFinalValue(ValueConsumer consumer) {
-        RegisteredBuildServiceProvider<T, BuildServiceParameters> resolved = resolve();
+        RegisteredBuildServiceProvider<T, BuildServiceParameters> resolved = resolve(true);
         return resolved != null ? resolved.withFinalValue(consumer) : super.withFinalValue(consumer);
     }
 
     @Override
     public boolean calculatePresence(ValueConsumer consumer) {
-        return resolve() != null;
+        return resolve(false) != null;
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/services/internal/DefaultBuildServicesRegistry.java b/subprojects/core/src/main/java/org/gradle/api/services/internal/DefaultBuildServicesRegistry.java
index 005ff5f..308f724 100644
--- a/subprojects/core/src/main/java/org/gradle/api/services/internal/DefaultBuildServicesRegistry.java
+++ b/subprojects/core/src/main/java/org/gradle/api/services/internal/DefaultBuildServicesRegistry.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang.StringUtils;
 import org.gradle.BuildAdapter;
 import org.gradle.BuildResult;
 import org.gradle.api.Action;
@@ -26,7 +27,6 @@
 import org.gradle.api.artifacts.component.BuildIdentifier;
 import org.gradle.api.internal.collections.DomainObjectCollectionFactory;
 import org.gradle.api.internal.project.HoldsProjectState;
-import org.gradle.api.internal.provider.ProviderInternal;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.services.BuildService;
 import org.gradle.api.services.BuildServiceParameters;
@@ -53,8 +53,8 @@
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
+import static org.gradle.api.services.internal.BuildServiceProvider.asBuildServiceProvider;
 import static org.gradle.internal.Cast.uncheckedCast;
 import static org.gradle.internal.Cast.uncheckedNonnullCast;
 
@@ -129,6 +129,7 @@ public SharedResource forService(BuildServiceProvider<?, ?> service) {
         });
     }
 
+    @Nullable
     @Override
     public DefaultServiceRegistration<?, ?> findRegistration(Class<?> type, String name) {
         return uncheckedCast(!name.isEmpty() ?
@@ -138,6 +139,17 @@ public SharedResource forService(BuildServiceProvider<?, ?> service) {
     }
 
     @Override
+    public Set<BuildServiceRegistration<?, ?>> findRegistrations(Class<?> type, String name) {
+        return withRegistrations(registrations ->
+            ImmutableSet.<BuildServiceRegistration<?, ?>>builder().addAll(registrations.matching(it ->
+                type.isAssignableFrom(BuildServiceProvider.getProvidedType(it.getService()))
+                    &&
+                (StringUtils.isEmpty(name) || it.getName().equals(name))
+            )).build()
+        );
+    }
+
+    @Override
     @Nullable
     public BuildServiceRegistration<?, ?> findByName(String name) {
         return withRegistrations(registrations -> registrations.findByName(name));
@@ -146,18 +158,7 @@ public SharedResource forService(BuildServiceProvider<?, ?> service) {
     @Nullable
     @Override
     public BuildServiceRegistration<?, ?> findByType(Class<?> type) {
-        Set<BuildServiceRegistration<?, ?>> results = withRegistrations(registrations ->
-            ImmutableSet.<BuildServiceRegistration<?, ?>>builder().addAll(
-                registrations.matching(it -> type.isAssignableFrom(getProvidedType(it.getService())))
-            ).build()
-        );
-        if (results.size() > 1) {
-            String names = results.stream()
-                .map(it -> it.getName() + ": " + getProvidedType(it.getService()).getTypeName())
-                .collect(Collectors.joining(", "));
-            throw new IllegalArgumentException(String.format("Cannot resolve service by type for type '%s' when there are two or more instances. Please also provide a service name. Instances found: %s.", type.getTypeName(), names));
-        }
-        return results.stream().findFirst().orElse(null);
+        return findRegistrations(type, null).stream().findFirst().orElse(null);
     }
 
     @Override
@@ -215,13 +216,6 @@ public List<ResourceLock> getSharedResources(Set<Provider<? extends BuildService
         return locks.build();
     }
 
-    private BuildServiceProvider<?, ?> asBuildServiceProvider(Provider<? extends BuildService<?>> service) {
-        if (service instanceof BuildServiceProvider) {
-            return uncheckedCast(service);
-        }
-        throw new UnsupportedOperationException("Unexpected provider for a build service: " + service);
-    }
-
     @Nullable
     private <T extends BuildService<P>, P extends BuildServiceParameters> P instantiateParametersOf(Class<T> implementationType) {
         Class<P> parameterType = isolationScheme.parameterTypeFor(implementationType);
@@ -311,10 +305,6 @@ private void discardAll(boolean forceAll) {
         });
     }
 
-    private static <T> Class<T> getProvidedType(Provider<T> provider) {
-        return ((ProviderInternal<T>) provider).getType();
-    }
-
     private static class ServiceBackedSharedResource implements SharedResource {
         private final String name;
         private final int maxUsages;
diff --git a/subprojects/core/src/main/java/org/gradle/api/tasks/VerificationException.java b/subprojects/core/src/main/java/org/gradle/api/tasks/VerificationException.java
index 8f9edb9..3416234 100644
--- a/subprojects/core/src/main/java/org/gradle/api/tasks/VerificationException.java
+++ b/subprojects/core/src/main/java/org/gradle/api/tasks/VerificationException.java
@@ -17,6 +17,7 @@
 package org.gradle.api.tasks;
 
 import org.gradle.api.GradleException;
+import org.gradle.api.Incubating;
 
 /**
  * Signals that tests have failed. This is only thrown when tests are executed and some tests have failed execution.
@@ -27,4 +28,14 @@ public class VerificationException extends GradleException {
     public VerificationException(String message) {
         super(message);
     }
+
+    /**
+     * Allows a cause to be specified.
+     *
+     * @since 8.2
+     */
+    @Incubating
+    public VerificationException(String message, Throwable cause) {
+        super(message, cause);
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/api/tasks/WriteProperties.java b/subprojects/core/src/main/java/org/gradle/api/tasks/WriteProperties.java
index b9ca394b2..9f5e7a2 100644
--- a/subprojects/core/src/main/java/org/gradle/api/tasks/WriteProperties.java
+++ b/subprojects/core/src/main/java/org/gradle/api/tasks/WriteProperties.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import org.gradle.api.DefaultTask;
-import org.gradle.api.Incubating;
 import org.gradle.api.file.RegularFileProperty;
 import org.gradle.api.internal.file.FileOperations;
 import org.gradle.internal.IoActions;
@@ -234,7 +233,6 @@ public void setOutputFile(Object outputFile) {
      * @since 8.1
      */
     @OutputFile
-    @Incubating
     abstract public RegularFileProperty getDestinationFile();
 
     @TaskAction
diff --git a/subprojects/core/src/main/java/org/gradle/api/tasks/ant/package-info.java b/subprojects/core/src/main/java/org/gradle/api/tasks/ant/package-info.java
index 1f5c94d..88b0d6f 100644
--- a/subprojects/core/src/main/java/org/gradle/api/tasks/ant/package-info.java
+++ b/subprojects/core/src/main/java/org/gradle/api/tasks/ant/package-info.java
@@ -17,4 +17,4 @@
 /**
  * The Ant integration {@link org.gradle.api.Task} implementations.
  */
-package org.gradle.api.tasks.ant;
\ No newline at end of file
+package org.gradle.api.tasks.ant;
diff --git a/subprojects/core/src/main/java/org/gradle/caching/internal/BuildCacheServices.java b/subprojects/core/src/main/java/org/gradle/caching/internal/BuildCacheServices.java
index 5f8796a..960722c 100644
--- a/subprojects/core/src/main/java/org/gradle/caching/internal/BuildCacheServices.java
+++ b/subprojects/core/src/main/java/org/gradle/caching/internal/BuildCacheServices.java
@@ -16,16 +16,17 @@
 
 package org.gradle.caching.internal;
 
-import org.gradle.StartParameter;
 import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.StartParameterInternal;
 import org.gradle.api.internal.cache.StringInterner;
 import org.gradle.api.internal.file.temp.TemporaryFileProvider;
-import org.gradle.api.logging.configuration.ShowStacktrace;
+import org.gradle.caching.BuildCacheServiceFactory;
 import org.gradle.caching.configuration.internal.BuildCacheConfigurationInternal;
 import org.gradle.caching.configuration.internal.BuildCacheServiceRegistration;
 import org.gradle.caching.configuration.internal.DefaultBuildCacheConfiguration;
 import org.gradle.caching.configuration.internal.DefaultBuildCacheServiceRegistration;
 import org.gradle.caching.internal.controller.BuildCacheController;
+import org.gradle.caching.internal.controller.NextGenBuildCacheController;
 import org.gradle.caching.internal.controller.RootBuildCacheControllerRef;
 import org.gradle.caching.internal.origin.OriginMetadataFactory;
 import org.gradle.caching.internal.packaging.BuildCacheEntryPacker;
@@ -35,12 +36,18 @@
 import org.gradle.caching.internal.packaging.impl.TarBuildCacheEntryPacker;
 import org.gradle.caching.internal.packaging.impl.TarPackerFileSystemSupport;
 import org.gradle.caching.internal.services.BuildCacheControllerFactory;
+import org.gradle.caching.internal.services.LegacyBuildCacheControllerFactory;
+import org.gradle.caching.internal.services.NextGenBuildCacheControllerFactory;
 import org.gradle.caching.local.DirectoryBuildCache;
 import org.gradle.caching.local.internal.DirectoryBuildCacheFileStoreFactory;
 import org.gradle.caching.local.internal.DirectoryBuildCacheServiceFactory;
+import org.gradle.caching.local.internal.H2BuildCacheServiceFactory;
 import org.gradle.internal.SystemProperties;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.file.BufferProvider;
 import org.gradle.internal.file.Deleter;
 import org.gradle.internal.file.FileException;
+import org.gradle.internal.file.ThreadLocalBufferProvider;
 import org.gradle.internal.hash.ChecksumService;
 import org.gradle.internal.hash.StreamHasher;
 import org.gradle.internal.instantiation.InstantiatorFactory;
@@ -57,7 +64,6 @@
 import org.gradle.internal.service.scopes.AbstractPluginServiceRegistry;
 import org.gradle.internal.vfs.FileSystemAccess;
 import org.gradle.util.GradleVersion;
-import org.gradle.util.Path;
 
 import java.io.File;
 import java.util.List;
@@ -66,6 +72,15 @@
  * Build scoped services for build cache usage.
  */
 public final class BuildCacheServices extends AbstractPluginServiceRegistry {
+    @Override
+    public void registerGlobalServices(ServiceRegistration registration) {
+        registration.addProvider(new Object() {
+            BufferProvider createBufferProvider() {
+                // TODO Make buffer size configurable
+                return new ThreadLocalBufferProvider(64 * 1024);
+            }
+        });
+    }
 
     @Override
     public void registerBuildTreeServices(ServiceRegistration registration) {
@@ -97,7 +112,10 @@ public PathKeyFileStore createFileStore(File baseDir) {
             }
 
             BuildCacheServiceRegistration createDirectoryBuildCacheServiceRegistration() {
-                return new DefaultBuildCacheServiceRegistration(DirectoryBuildCache.class, DirectoryBuildCacheServiceFactory.class);
+                Class<? extends BuildCacheServiceFactory<?>> localCacheServiceFactory = NextGenBuildCacheController.isNextGenCachingEnabled()
+                    ? H2BuildCacheServiceFactory.class
+                    : DirectoryBuildCacheServiceFactory.class;
+                return new DefaultBuildCacheServiceRegistration(DirectoryBuildCache.class, localCacheServiceFactory);
             }
 
         });
@@ -117,10 +135,11 @@ BuildCacheEntryPacker createResultPacker(
                 TarPackerFileSystemSupport fileSystemSupport,
                 FileSystem fileSystem,
                 StreamHasher fileHasher,
-                StringInterner stringInterner
+                StringInterner stringInterner,
+                BufferProvider bufferProvider
             ) {
                 return new GZipBuildCacheEntryPacker(
-                    new TarBuildCacheEntryPacker(fileSystemSupport, new FilePermissionsAccessAdapter(fileSystem), fileHasher, stringInterner));
+                    new TarBuildCacheEntryPacker(fileSystemSupport, new FilePermissionsAccessAdapter(fileSystem), fileHasher, stringInterner, bufferProvider));
             }
 
             OriginMetadataFactory createOriginMetadataFactory(
@@ -139,19 +158,14 @@ OriginMetadataFactory createOriginMetadataFactory(
 
             BuildCacheController createBuildCacheController(
                 ServiceRegistry serviceRegistry,
-                BuildCacheConfigurationInternal buildCacheConfiguration,
-                BuildOperationExecutor buildOperationExecutor,
                 InstantiatorFactory instantiatorFactory,
                 GradleInternal gradle,
+                BuildCacheConfigurationInternal buildCacheConfiguration,
                 RootBuildCacheControllerRef rootControllerRef,
-                TemporaryFileProvider temporaryFileProvider,
-                FileSystemAccess fileSystemAccess,
-                BuildCacheEntryPacker packer,
-                OriginMetadataFactory originMetadataFactory,
-                StringInterner stringInterner
+                BuildCacheControllerFactory buildCacheControllerFactory
             ) {
                 if (isRoot(gradle) || isGradleBuildTaskRoot(rootControllerRef)) {
-                    return doCreateBuildCacheController(serviceRegistry, buildCacheConfiguration, buildOperationExecutor, instantiatorFactory, gradle, temporaryFileProvider, fileSystemAccess, packer, originMetadataFactory, stringInterner);
+                    return buildCacheControllerFactory.createController(gradle.getIdentityPath(), buildCacheConfiguration, instantiatorFactory.inject(serviceRegistry));
                 } else {
                     // must be an included build or buildSrc
                     return rootControllerRef.getForNonRootBuild();
@@ -171,33 +185,42 @@ private boolean isRoot(GradleInternal gradle) {
                 return gradle.isRootBuild();
             }
 
-            private BuildCacheController doCreateBuildCacheController(
-                ServiceRegistry serviceRegistry, BuildCacheConfigurationInternal buildCacheConfiguration, BuildOperationExecutor buildOperationExecutor, InstantiatorFactory instantiatorFactory,
-                GradleInternal gradle, TemporaryFileProvider temporaryFileProvider, FileSystemAccess fileSystemAccess, BuildCacheEntryPacker packer, OriginMetadataFactory originMetadataFactory,
-                StringInterner stringInterner
+            BuildCacheControllerFactory createBuildCacheControllerFactory(
+                StartParameterInternal startParameter,
+                BuildOperationExecutor buildOperationExecutor,
+                TemporaryFileProvider temporaryFileProvider,
+                FileSystemAccess fileSystemAccess,
+                BuildCacheEntryPacker packer,
+                OriginMetadataFactory originMetadataFactory,
+                StringInterner stringInterner,
+                Deleter deleter,
+                BuildInvocationScopeId buildInvocationScopeId,
+                ExecutorFactory executorFactory,
+                BufferProvider bufferProvider
             ) {
-                StartParameter startParameter = gradle.getStartParameter();
-                Path buildIdentityPath = gradle.getIdentityPath();
-                BuildCacheControllerFactory.BuildCacheMode buildCacheMode = startParameter.isBuildCacheEnabled() ? BuildCacheControllerFactory.BuildCacheMode.ENABLED : BuildCacheControllerFactory.BuildCacheMode.DISABLED;
-                BuildCacheControllerFactory.RemoteAccessMode remoteAccessMode = startParameter.isOffline() ? BuildCacheControllerFactory.RemoteAccessMode.OFFLINE : BuildCacheControllerFactory.RemoteAccessMode.ONLINE;
-                boolean logStackTraces = startParameter.getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS;
-                boolean emitDebugLogging = startParameter.isBuildCacheDebugLogging();
-
-                return BuildCacheControllerFactory.create(
-                    buildOperationExecutor,
-                    buildIdentityPath,
-                    temporaryFileProvider,
-                    buildCacheConfiguration,
-                    buildCacheMode,
-                    remoteAccessMode,
-                    logStackTraces,
-                    emitDebugLogging,
-                    instantiatorFactory.inject(serviceRegistry),
-                    fileSystemAccess,
-                    packer,
-                    originMetadataFactory,
-                    stringInterner
-                );
+                if (NextGenBuildCacheController.isNextGenCachingEnabled()) {
+                    return new NextGenBuildCacheControllerFactory(
+                        startParameter,
+                        buildOperationExecutor,
+                        originMetadataFactory,
+                        fileSystemAccess,
+                        stringInterner,
+                        deleter,
+                        buildInvocationScopeId,
+                        executorFactory,
+                        bufferProvider
+                    );
+                } else {
+                    return new LegacyBuildCacheControllerFactory(
+                        startParameter,
+                        buildOperationExecutor,
+                        originMetadataFactory,
+                        fileSystemAccess,
+                        stringInterner,
+                        temporaryFileProvider,
+                        packer
+                    );
+                }
             }
         });
     }
diff --git a/subprojects/core/src/main/java/org/gradle/caching/internal/services/AbstractBuildCacheControllerFactory.java b/subprojects/core/src/main/java/org/gradle/caching/internal/services/AbstractBuildCacheControllerFactory.java
new file mode 100644
index 0000000..336fe52
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/caching/internal/services/AbstractBuildCacheControllerFactory.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2017 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.caching.internal.services;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSortedMap;
+import org.gradle.StartParameter;
+import org.gradle.api.internal.GeneratedSubclasses;
+import org.gradle.api.internal.cache.StringInterner;
+import org.gradle.caching.BuildCacheService;
+import org.gradle.caching.BuildCacheServiceFactory;
+import org.gradle.caching.configuration.BuildCache;
+import org.gradle.caching.configuration.internal.BuildCacheConfigurationInternal;
+import org.gradle.caching.internal.FinalizeBuildCacheConfigurationBuildOperationType;
+import org.gradle.caching.internal.controller.BuildCacheController;
+import org.gradle.caching.internal.controller.NoOpBuildCacheController;
+import org.gradle.caching.internal.controller.service.BuildCacheServiceRole;
+import org.gradle.caching.internal.origin.OriginMetadataFactory;
+import org.gradle.caching.local.DirectoryBuildCache;
+import org.gradle.internal.Cast;
+import org.gradle.internal.instantiation.InstanceGenerator;
+import org.gradle.internal.operations.BuildOperationContext;
+import org.gradle.internal.operations.BuildOperationDescriptor;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.operations.CallableBuildOperation;
+import org.gradle.internal.vfs.FileSystemAccess;
+import org.gradle.util.Path;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public abstract class AbstractBuildCacheControllerFactory<L extends BuildCacheService> implements BuildCacheControllerFactory {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractBuildCacheControllerFactory.class);
+    protected final StartParameter startParameter;
+    protected final StringInterner stringInterner;
+    protected final BuildOperationExecutor buildOperationExecutor;
+    protected final FileSystemAccess fileSystemAccess;
+    protected final OriginMetadataFactory originMetadataFactory;
+
+    public enum BuildCacheMode {
+        ENABLED, DISABLED
+    }
+
+    public enum RemoteAccessMode {
+        ONLINE, OFFLINE
+    }
+
+    public AbstractBuildCacheControllerFactory(
+        StartParameter startParameter,
+        BuildOperationExecutor buildOperationExecutor,
+        OriginMetadataFactory originMetadataFactory,
+        FileSystemAccess fileSystemAccess,
+        StringInterner stringInterner
+    ) {
+        this.startParameter = startParameter;
+        this.buildOperationExecutor = buildOperationExecutor;
+        this.originMetadataFactory = originMetadataFactory;
+        this.fileSystemAccess = fileSystemAccess;
+        this.stringInterner = stringInterner;
+    }
+
+    abstract protected BuildCacheController doCreateController(
+        @Nullable DescribedBuildCacheService<DirectoryBuildCache, L> localDescribedService,
+        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remoteDescribedService
+    );
+
+    @Override
+    public BuildCacheController createController(Path buildIdentityPath, BuildCacheConfigurationInternal buildCacheConfiguration, InstanceGenerator instanceGenerator) {
+        BuildCacheMode buildCacheState = startParameter.isBuildCacheEnabled() ? AbstractBuildCacheControllerFactory.BuildCacheMode.ENABLED : AbstractBuildCacheControllerFactory.BuildCacheMode.DISABLED;
+        RemoteAccessMode remoteAccessMode = startParameter.isOffline() ? AbstractBuildCacheControllerFactory.RemoteAccessMode.OFFLINE : AbstractBuildCacheControllerFactory.RemoteAccessMode.ONLINE;
+
+        return buildOperationExecutor.call(new CallableBuildOperation<BuildCacheController>() {
+            @Override
+            public BuildCacheController call(BuildOperationContext context) {
+                if (buildCacheState == BuildCacheMode.DISABLED) {
+                    context.setResult(ResultImpl.disabled());
+                    return NoOpBuildCacheController.INSTANCE;
+                }
+
+                DirectoryBuildCache local = buildCacheConfiguration.getLocal();
+                BuildCache remote = buildCacheConfiguration.getRemote();
+
+                boolean localEnabled = local.isEnabled();
+                boolean remoteEnabled = remote != null && remote.isEnabled();
+
+                if (remoteEnabled && remoteAccessMode == RemoteAccessMode.OFFLINE) {
+                    remoteEnabled = false;
+                    LOGGER.warn("Remote build cache is disabled when running with --offline.");
+                }
+
+                DescribedBuildCacheService<DirectoryBuildCache, L> localDescribedService = localEnabled
+                    ? createBuildCacheService(local, BuildCacheServiceRole.LOCAL, buildIdentityPath, buildCacheConfiguration, instanceGenerator)
+                    : null;
+
+                DescribedBuildCacheService<BuildCache, BuildCacheService> remoteDescribedService = remoteEnabled
+                    ? createBuildCacheService(remote, BuildCacheServiceRole.REMOTE, buildIdentityPath, buildCacheConfiguration, instanceGenerator)
+                    : null;
+
+                context.setResult(new ResultImpl(
+                    true,
+                    local.isEnabled(),
+                    remote != null && remote.isEnabled() && remoteAccessMode == RemoteAccessMode.ONLINE,
+                    localDescribedService == null ? null : localDescribedService.description,
+                    remoteDescribedService == null ? null : remoteDescribedService.description
+                ));
+
+                if (!localEnabled && !remoteEnabled) {
+                    LOGGER.warn("Using the build cache is enabled, but no build caches are configured or enabled.");
+                    return NoOpBuildCacheController.INSTANCE;
+                } else {
+                    return doCreateController(localDescribedService, remoteDescribedService);
+                }
+            }
+
+            @Override
+            public BuildOperationDescriptor.Builder description() {
+                return BuildOperationDescriptor.displayName("Finalize build cache configuration")
+                    .details(new DetailsImpl(buildIdentityPath.getPath()));
+            }
+        });
+    }
+
+    private static <C extends BuildCache, S> DescribedBuildCacheService<C, S> createBuildCacheService(
+        C configuration,
+        BuildCacheServiceRole role,
+        Path buildIdentityPath,
+        BuildCacheConfigurationInternal buildCacheConfiguration,
+        InstanceGenerator instantiator
+    ) {
+        Class<? extends BuildCacheServiceFactory<C>> castFactoryType = Cast.uncheckedNonnullCast(
+            buildCacheConfiguration.getBuildCacheServiceFactoryType(configuration.getClass())
+        );
+
+        BuildCacheServiceFactory<C> factory = instantiator.newInstance(castFactoryType);
+        Describer describer = new Describer();
+        S service = Cast.uncheckedNonnullCast(factory.createBuildCacheService(configuration, describer));
+        ImmutableSortedMap<String, String> config = ImmutableSortedMap.copyOf(describer.configParams);
+        BuildCacheDescription description = new BuildCacheDescription(configuration, describer.type, config);
+
+        logConfig(buildIdentityPath, role, description);
+
+        return new DescribedBuildCacheService<>(configuration, service, description);
+    }
+
+    private static void logConfig(Path buildIdentityPath, BuildCacheServiceRole role, BuildCacheDescription description) {
+        if (LOGGER.isInfoEnabled()) {
+            StringBuilder config = new StringBuilder();
+            boolean pullOnly = !description.isPush();
+            if (!description.config.isEmpty() || pullOnly) {
+                Map<String, String> configMap;
+                if (pullOnly) {
+                    configMap = new LinkedHashMap<>();
+                    // Pull-only always comes first
+                    configMap.put("pull-only", null);
+                    configMap.putAll(description.config);
+                } else {
+                    configMap = description.config;
+                }
+                config.append(" (");
+                config.append(configMap.entrySet().stream().map(input -> {
+                    if (input.getValue() == null) {
+                        return input.getKey();
+                    } else {
+                        return input.getKey() + " = " + input.getValue();
+                    }
+                }).collect(Collectors.joining(", ")));
+                config.append(")");
+            }
+
+            String buildDescription;
+            if (buildIdentityPath.equals(Path.ROOT)) {
+                buildDescription = "the root build";
+            } else {
+                buildDescription = "build '" + buildIdentityPath + "'";
+            }
+
+            LOGGER.info("Using {} {} build cache for {}{}.",
+                role.getDisplayName(),
+                description.type == null ? description.className : description.type,
+                buildDescription,
+                config
+            );
+        }
+    }
+
+    private static final class BuildCacheDescription implements FinalizeBuildCacheConfigurationBuildOperationType.Result.BuildCacheDescription {
+
+        private final String className;
+        private final boolean push;
+        private final String type;
+        private final ImmutableSortedMap<String, String> config;
+
+        private BuildCacheDescription(BuildCache buildCache, String type, ImmutableSortedMap<String, String> config) {
+            this.className = GeneratedSubclasses.unpackType(buildCache).getName();
+            this.push = buildCache.isPush();
+            this.type = type;
+            this.config = config;
+        }
+
+        @Override
+        public String getClassName() {
+            return className;
+        }
+
+        @Override
+        public boolean isPush() {
+            return push;
+        }
+
+        @Override
+        public String getType() {
+            return type;
+        }
+
+        @Override
+        public Map<String, String> getConfig() {
+            return config;
+        }
+    }
+
+    private static class Describer implements BuildCacheServiceFactory.Describer {
+
+        private String type;
+        private final Map<String, String> configParams = new HashMap<>();
+
+        @Override
+        public BuildCacheServiceFactory.Describer type(String type) {
+            this.type = Preconditions.checkNotNull(type, "'type' argument cannot be null");
+            return this;
+        }
+
+        @Override
+        public BuildCacheServiceFactory.Describer config(String name, String value) {
+            Preconditions.checkNotNull(name, "'name' argument cannot be null");
+            Preconditions.checkNotNull(value, "'value' argument cannot be null");
+            configParams.put(name, value);
+            return this;
+        }
+    }
+
+    protected static class DescribedBuildCacheService<C, S> {
+        public final C config;
+        public final S service;
+        public final BuildCacheDescription description;
+
+        public DescribedBuildCacheService(C config, S service, BuildCacheDescription description) {
+            this.config = config;
+            this.service = service;
+            this.description = description;
+        }
+    }
+
+    private static class DetailsImpl implements FinalizeBuildCacheConfigurationBuildOperationType.Details {
+        private final String buildPath;
+
+        private DetailsImpl(String buildPath) {
+            this.buildPath = buildPath;
+        }
+
+        @Override
+        public String getBuildPath() {
+            return buildPath;
+        }
+    }
+
+    private static class ResultImpl implements FinalizeBuildCacheConfigurationBuildOperationType.Result {
+
+        private final boolean enabled;
+        private final boolean localEnabled;
+        private final BuildCacheDescription local;
+        private final boolean remoteEnabled;
+        private final BuildCacheDescription remote;
+
+        ResultImpl(boolean enabled, boolean localEnabled, boolean remoteEnabled, @Nullable BuildCacheDescription local, @Nullable BuildCacheDescription remote) {
+            this.enabled = enabled;
+            this.localEnabled = localEnabled;
+            this.remoteEnabled = remoteEnabled;
+            this.local = local;
+            this.remote = remote;
+        }
+
+        static FinalizeBuildCacheConfigurationBuildOperationType.Result disabled() {
+            return new ResultImpl(false, false, false, null, null);
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return enabled;
+        }
+
+        @Override
+        public boolean isLocalEnabled() {
+            return localEnabled;
+        }
+
+        @Override
+        public boolean isRemoteEnabled() {
+            return remoteEnabled;
+        }
+
+        @Override
+        @Nullable
+        public BuildCacheDescription getLocal() {
+            return local;
+        }
+
+        @Override
+        @Nullable
+        public BuildCacheDescription getRemote() {
+            return remote;
+        }
+
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/caching/internal/services/BuildCacheControllerFactory.java b/subprojects/core/src/main/java/org/gradle/caching/internal/services/BuildCacheControllerFactory.java
index a5ee135..4e6b681 100644
--- a/subprojects/core/src/main/java/org/gradle/caching/internal/services/BuildCacheControllerFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/caching/internal/services/BuildCacheControllerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 the original author or authors.
+ * Copyright 2023 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.
@@ -16,338 +16,13 @@
 
 package org.gradle.caching.internal.services;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSortedMap;
-import org.gradle.api.internal.GeneratedSubclasses;
-import org.gradle.api.internal.cache.StringInterner;
-import org.gradle.api.internal.file.temp.TemporaryFileProvider;
-import org.gradle.caching.BuildCacheService;
-import org.gradle.caching.BuildCacheServiceFactory;
-import org.gradle.caching.configuration.BuildCache;
 import org.gradle.caching.configuration.internal.BuildCacheConfigurationInternal;
-import org.gradle.caching.internal.FinalizeBuildCacheConfigurationBuildOperationType;
 import org.gradle.caching.internal.controller.BuildCacheController;
-import org.gradle.caching.internal.controller.DefaultBuildCacheController;
-import org.gradle.caching.internal.controller.NoOpBuildCacheController;
-import org.gradle.caching.internal.controller.service.BuildCacheServiceRole;
-import org.gradle.caching.internal.controller.service.BuildCacheServicesConfiguration;
-import org.gradle.caching.internal.origin.OriginMetadataFactory;
-import org.gradle.caching.internal.packaging.BuildCacheEntryPacker;
-import org.gradle.caching.local.DirectoryBuildCache;
-import org.gradle.caching.local.internal.DirectoryBuildCacheService;
-import org.gradle.internal.Cast;
-import org.gradle.internal.operations.BuildOperationContext;
-import org.gradle.internal.operations.BuildOperationDescriptor;
-import org.gradle.internal.operations.BuildOperationExecutor;
-import org.gradle.internal.operations.CallableBuildOperation;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.vfs.FileSystemAccess;
+import org.gradle.internal.instantiation.InstanceGenerator;
 import org.gradle.util.Path;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import javax.annotation.Nullable;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.stream.Collectors;
+public interface BuildCacheControllerFactory {
+    String REMOTE_CONTINUE_ON_ERROR_PROPERTY = "org.gradle.unsafe.build-cache.remote-continue-on-error";
 
-public final class BuildCacheControllerFactory {
-
-    public static final String REMOTE_CONTINUE_ON_ERROR_PROPERTY = "org.gradle.unsafe.build-cache.remote-continue-on-error";
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(BuildCacheControllerFactory.class);
-
-    public enum BuildCacheMode {
-        ENABLED, DISABLED
-    }
-
-    public enum RemoteAccessMode {
-        ONLINE, OFFLINE
-    }
-
-    public static BuildCacheController create(
-        final BuildOperationExecutor buildOperationExecutor,
-        final Path buildIdentityPath,
-        final TemporaryFileProvider temporaryFileProvider,
-        final BuildCacheConfigurationInternal buildCacheConfiguration,
-        final BuildCacheMode buildCacheState,
-        final RemoteAccessMode remoteAccessMode,
-        final boolean logStackTraces,
-        final boolean emitDebugLogging,
-        final Instantiator instantiator,
-        final FileSystemAccess fileSystemAccess,
-        final BuildCacheEntryPacker packer,
-        final OriginMetadataFactory originMetadataFactory,
-        final StringInterner stringInterner
-    ) {
-        return buildOperationExecutor.call(new CallableBuildOperation<BuildCacheController>() {
-            @Override
-            public BuildCacheController call(BuildOperationContext context) {
-                if (buildCacheState == BuildCacheMode.DISABLED) {
-                    context.setResult(ResultImpl.disabled());
-                    return NoOpBuildCacheController.INSTANCE;
-                }
-
-                DirectoryBuildCache local = buildCacheConfiguration.getLocal();
-                BuildCache remote = buildCacheConfiguration.getRemote();
-
-                boolean localEnabled = local.isEnabled();
-                boolean remoteEnabled = remote != null && remote.isEnabled();
-
-                if (remoteEnabled && remoteAccessMode == RemoteAccessMode.OFFLINE) {
-                    remoteEnabled = false;
-                    LOGGER.warn("Remote build cache is disabled when running with --offline.");
-                }
-
-                DescribedBuildCacheService<DirectoryBuildCache, DirectoryBuildCacheService> localDescribedService = localEnabled
-                    ? createBuildCacheService(local, BuildCacheServiceRole.LOCAL, buildIdentityPath, buildCacheConfiguration, instantiator)
-                    : null;
-
-                DescribedBuildCacheService<BuildCache, BuildCacheService> remoteDescribedService = remoteEnabled
-                    ? createBuildCacheService(remote, BuildCacheServiceRole.REMOTE, buildIdentityPath, buildCacheConfiguration, instantiator)
-                    : null;
-
-                context.setResult(new ResultImpl(
-                    true,
-                    local.isEnabled(),
-                    remote != null && remote.isEnabled() && remoteAccessMode == RemoteAccessMode.ONLINE,
-                    localDescribedService == null ? null : localDescribedService.description,
-                    remoteDescribedService == null ? null : remoteDescribedService.description
-                ));
-
-                if (!localEnabled && !remoteEnabled) {
-                    LOGGER.warn("Using the build cache is enabled, but no build caches are configured or enabled.");
-                    return NoOpBuildCacheController.INSTANCE;
-                } else {
-                    BuildCacheServicesConfiguration config = toConfiguration(
-                        localDescribedService,
-                        remoteDescribedService
-                    );
-
-                    return new DefaultBuildCacheController(
-                        config,
-                        buildOperationExecutor,
-                        temporaryFileProvider,
-                        logStackTraces,
-                        emitDebugLogging,
-                        !Boolean.getBoolean(REMOTE_CONTINUE_ON_ERROR_PROPERTY),
-                        fileSystemAccess,
-                        packer,
-                        originMetadataFactory,
-                        stringInterner
-                    );
-                }
-            }
-
-            @Override
-            public BuildOperationDescriptor.Builder description() {
-                return BuildOperationDescriptor.displayName("Finalize build cache configuration")
-                    .details(new DetailsImpl(buildIdentityPath.getPath()));
-            }
-        });
-    }
-
-    private static BuildCacheServicesConfiguration toConfiguration(
-        @Nullable DescribedBuildCacheService<DirectoryBuildCache, DirectoryBuildCacheService> local,
-        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remote
-    ) {
-        boolean localPush = local != null && local.config.isPush();
-        boolean remotePush = remote != null && remote.config.isPush();
-        return new BuildCacheServicesConfiguration(
-            local != null ? local.service : null, localPush,
-            remote != null ? remote.service : null, remotePush);
-    }
-
-    private static <C extends BuildCache, S> DescribedBuildCacheService<C, S> createBuildCacheService(
-        C configuration,
-        BuildCacheServiceRole role,
-        Path buildIdentityPath,
-        BuildCacheConfigurationInternal buildCacheConfiguration,
-        Instantiator instantiator
-    ) {
-        Class<? extends BuildCacheServiceFactory<C>> castFactoryType = Cast.uncheckedNonnullCast(
-            buildCacheConfiguration.getBuildCacheServiceFactoryType(configuration.getClass())
-        );
-
-        BuildCacheServiceFactory<C> factory = instantiator.newInstance(castFactoryType);
-        Describer describer = new Describer();
-        S service = Cast.uncheckedNonnullCast(factory.createBuildCacheService(configuration, describer));
-        ImmutableSortedMap<String, String> config = ImmutableSortedMap.copyOf(describer.configParams);
-        BuildCacheDescription description = new BuildCacheDescription(configuration, describer.type, config);
-
-        logConfig(buildIdentityPath, role, description);
-
-        return new DescribedBuildCacheService<>(configuration, service, description);
-    }
-
-    private static void logConfig(Path buildIdentityPath, BuildCacheServiceRole role, BuildCacheDescription description) {
-        if (LOGGER.isInfoEnabled()) {
-            StringBuilder config = new StringBuilder();
-            boolean pullOnly = !description.isPush();
-            if (!description.config.isEmpty() || pullOnly) {
-                Map<String, String> configMap;
-                if (pullOnly) {
-                    configMap = new LinkedHashMap<>();
-                    // Pull-only always comes first
-                    configMap.put("pull-only", null);
-                    configMap.putAll(description.config);
-                } else {
-                    configMap = description.config;
-                }
-                config.append(" (");
-                config.append(configMap.entrySet().stream().map(input -> {
-                    if (input.getValue() == null) {
-                        return input.getKey();
-                    } else {
-                        return input.getKey() + " = " + input.getValue();
-                    }
-                }).collect(Collectors.joining(", ")));
-                config.append(")");
-            }
-
-            String buildDescription;
-            if (buildIdentityPath.equals(Path.ROOT)) {
-                buildDescription = "the root build";
-            } else {
-                buildDescription = "build '" + buildIdentityPath + "'";
-            }
-
-            LOGGER.info("Using {} {} build cache for {}{}.",
-                role.getDisplayName(),
-                description.type == null ? description.className : description.type,
-                buildDescription,
-                config
-            );
-        }
-    }
-
-    private static final class BuildCacheDescription implements FinalizeBuildCacheConfigurationBuildOperationType.Result.BuildCacheDescription {
-
-        private final String className;
-        private final boolean push;
-        private final String type;
-        private final ImmutableSortedMap<String, String> config;
-
-        private BuildCacheDescription(BuildCache buildCache, String type, ImmutableSortedMap<String, String> config) {
-            this.className = GeneratedSubclasses.unpackType(buildCache).getName();
-            this.push = buildCache.isPush();
-            this.type = type;
-            this.config = config;
-        }
-
-        @Override
-        public String getClassName() {
-            return className;
-        }
-
-        @Override
-        public boolean isPush() {
-            return push;
-        }
-
-        @Override
-        public String getType() {
-            return type;
-        }
-
-        @Override
-        public Map<String, String> getConfig() {
-            return config;
-        }
-    }
-
-    private static class Describer implements BuildCacheServiceFactory.Describer {
-
-        private String type;
-        private Map<String, String> configParams = new HashMap<>();
-
-        @Override
-        public BuildCacheServiceFactory.Describer type(String type) {
-            this.type = Preconditions.checkNotNull(type, "'type' argument cannot be null");
-            return this;
-        }
-
-        @Override
-        public BuildCacheServiceFactory.Describer config(String name, String value) {
-            Preconditions.checkNotNull(name, "'name' argument cannot be null");
-            Preconditions.checkNotNull(value, "'value' argument cannot be null");
-            configParams.put(name, value);
-            return this;
-        }
-    }
-
-    private static class DescribedBuildCacheService<C, S> {
-        private final C config;
-        private final S service;
-        private final BuildCacheDescription description;
-
-        public DescribedBuildCacheService(C config, S service, BuildCacheDescription description) {
-            this.config = config;
-            this.service = service;
-            this.description = description;
-        }
-    }
-
-    private static class DetailsImpl implements FinalizeBuildCacheConfigurationBuildOperationType.Details {
-        private final String buildPath;
-
-        private DetailsImpl(String buildPath) {
-            this.buildPath = buildPath;
-        }
-
-        @Override
-        public String getBuildPath() {
-            return buildPath;
-        }
-    }
-
-    private static class ResultImpl implements FinalizeBuildCacheConfigurationBuildOperationType.Result {
-
-        private final boolean enabled;
-        private final boolean localEnabled;
-        private final BuildCacheDescription local;
-        private final boolean remoteEnabled;
-        private final BuildCacheDescription remote;
-
-        ResultImpl(boolean enabled, boolean localEnabled, boolean remoteEnabled, @Nullable BuildCacheDescription local, @Nullable BuildCacheDescription remote) {
-            this.enabled = enabled;
-            this.localEnabled = localEnabled;
-            this.remoteEnabled = remoteEnabled;
-            this.local = local;
-            this.remote = remote;
-        }
-
-        static FinalizeBuildCacheConfigurationBuildOperationType.Result disabled() {
-            return new ResultImpl(false, false, false, null, null);
-        }
-
-        @Override
-        public boolean isEnabled() {
-            return enabled;
-        }
-
-        @Override
-        public boolean isLocalEnabled() {
-            return localEnabled;
-        }
-
-        @Override
-        public boolean isRemoteEnabled() {
-            return remoteEnabled;
-        }
-
-        @Override
-        @Nullable
-        public BuildCacheDescription getLocal() {
-            return local;
-        }
-
-        @Override
-        @Nullable
-        public BuildCacheDescription getRemote() {
-            return remote;
-        }
-
-    }
+    BuildCacheController createController(Path buildIdentityPath, BuildCacheConfigurationInternal buildCacheConfiguration, InstanceGenerator instanceGenerator);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/caching/internal/services/LegacyBuildCacheControllerFactory.java b/subprojects/core/src/main/java/org/gradle/caching/internal/services/LegacyBuildCacheControllerFactory.java
new file mode 100644
index 0000000..587bd1d
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/caching/internal/services/LegacyBuildCacheControllerFactory.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 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.caching.internal.services;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.cache.StringInterner;
+import org.gradle.api.internal.file.temp.TemporaryFileProvider;
+import org.gradle.api.logging.configuration.ShowStacktrace;
+import org.gradle.caching.BuildCacheService;
+import org.gradle.caching.configuration.BuildCache;
+import org.gradle.caching.internal.controller.BuildCacheController;
+import org.gradle.caching.internal.controller.DefaultBuildCacheController;
+import org.gradle.caching.internal.controller.service.BuildCacheServicesConfiguration;
+import org.gradle.caching.internal.origin.OriginMetadataFactory;
+import org.gradle.caching.internal.packaging.BuildCacheEntryPacker;
+import org.gradle.caching.local.DirectoryBuildCache;
+import org.gradle.caching.local.internal.DirectoryBuildCacheService;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.vfs.FileSystemAccess;
+
+import javax.annotation.Nullable;
+
+public class LegacyBuildCacheControllerFactory extends AbstractBuildCacheControllerFactory<DirectoryBuildCacheService> {
+
+    private final TemporaryFileProvider temporaryFileProvider;
+    private final BuildCacheEntryPacker packer;
+
+    public LegacyBuildCacheControllerFactory(
+        StartParameter startParameter,
+        BuildOperationExecutor buildOperationExecutor,
+        OriginMetadataFactory originMetadataFactory,
+        FileSystemAccess fileSystemAccess,
+        StringInterner stringInterner,
+        TemporaryFileProvider temporaryFileProvider,
+        BuildCacheEntryPacker packer
+    ) {
+        super(
+            startParameter,
+            buildOperationExecutor,
+            originMetadataFactory,
+            fileSystemAccess,
+            stringInterner
+        );
+        this.temporaryFileProvider = temporaryFileProvider;
+        this.packer = packer;
+    }
+
+    @Override
+    protected BuildCacheController doCreateController(
+        @Nullable DescribedBuildCacheService<DirectoryBuildCache, DirectoryBuildCacheService> localDescribedService,
+        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remoteDescribedService
+    ) {
+        BuildCacheServicesConfiguration config = toConfiguration(
+            localDescribedService,
+            remoteDescribedService
+        );
+
+        boolean logStackTraces = startParameter.getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS;
+        boolean emitDebugLogging = startParameter.isBuildCacheDebugLogging();
+
+        return new DefaultBuildCacheController(
+            config,
+            buildOperationExecutor,
+            temporaryFileProvider,
+            logStackTraces,
+            emitDebugLogging,
+            !Boolean.getBoolean(REMOTE_CONTINUE_ON_ERROR_PROPERTY),
+            fileSystemAccess,
+            packer,
+            originMetadataFactory,
+            stringInterner
+        );
+    }
+
+    private static BuildCacheServicesConfiguration toConfiguration(
+        @Nullable DescribedBuildCacheService<DirectoryBuildCache, DirectoryBuildCacheService> local,
+        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remote
+    ) {
+        boolean localPush = local != null && local.config.isPush();
+        boolean remotePush = remote != null && remote.config.isPush();
+        return new BuildCacheServicesConfiguration(
+            local != null ? local.service : null, localPush,
+            remote != null ? remote.service : null, remotePush);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/caching/internal/services/NextGenBuildCacheControllerFactory.java b/subprojects/core/src/main/java/org/gradle/caching/internal/services/NextGenBuildCacheControllerFactory.java
new file mode 100644
index 0000000..ebe1421
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/caching/internal/services/NextGenBuildCacheControllerFactory.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2017 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.caching.internal.services;
+
+import org.gradle.StartParameter;
+import org.gradle.api.internal.cache.StringInterner;
+import org.gradle.caching.BuildCacheEntryReader;
+import org.gradle.caching.BuildCacheEntryWriter;
+import org.gradle.caching.BuildCacheException;
+import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.BuildCacheService;
+import org.gradle.caching.configuration.BuildCache;
+import org.gradle.caching.internal.NextGenBuildCacheService;
+import org.gradle.caching.internal.controller.BuildCacheController;
+import org.gradle.caching.internal.controller.DefaultNextGenBuildCacheAccess;
+import org.gradle.caching.internal.controller.GZipNextGenBuildCacheAccess;
+import org.gradle.caching.internal.controller.NextGenBuildCacheController;
+import org.gradle.caching.internal.controller.NextGenBuildCacheHandler;
+import org.gradle.caching.internal.origin.OriginMetadataFactory;
+import org.gradle.caching.local.DirectoryBuildCache;
+import org.gradle.caching.local.internal.H2BuildCacheService;
+import org.gradle.internal.concurrent.ExecutorFactory;
+import org.gradle.internal.file.BufferProvider;
+import org.gradle.internal.file.Deleter;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.scopeids.id.BuildInvocationScopeId;
+import org.gradle.internal.vfs.FileSystemAccess;
+import org.gradle.util.internal.IncubationLogger;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+
+public final class NextGenBuildCacheControllerFactory extends AbstractBuildCacheControllerFactory<H2BuildCacheService> {
+
+    private final Deleter deleter;
+    private final BuildInvocationScopeId buildInvocationScopeId;
+    private final ExecutorFactory executorFactory;
+    private final BufferProvider bufferProvider;
+
+    public NextGenBuildCacheControllerFactory(
+        StartParameter startParameter,
+        BuildOperationExecutor buildOperationExecutor,
+        OriginMetadataFactory originMetadataFactory,
+        FileSystemAccess fileSystemAccess,
+        StringInterner stringInterner,
+        Deleter deleter,
+        BuildInvocationScopeId buildInvocationScopeId,
+        ExecutorFactory executorFactory,
+        BufferProvider bufferProvider
+    ) {
+        super(
+            startParameter,
+            buildOperationExecutor,
+            originMetadataFactory,
+            fileSystemAccess,
+            stringInterner);
+        this.deleter = deleter;
+        this.buildInvocationScopeId = buildInvocationScopeId;
+        this.executorFactory = executorFactory;
+        this.bufferProvider = bufferProvider;
+    }
+
+    @Override
+    protected BuildCacheController doCreateController(
+        @Nullable DescribedBuildCacheService<DirectoryBuildCache, H2BuildCacheService> localDescribedService,
+        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remoteDescribedService
+    ) {
+        IncubationLogger.incubatingFeatureUsed("Next generation build cache");
+
+        NextGenBuildCacheHandler local = resolveService(localDescribedService);
+        NextGenBuildCacheHandler remote = resolveService(remoteDescribedService);
+
+        return new NextGenBuildCacheController(
+            buildInvocationScopeId.getId().asString(),
+            deleter,
+            fileSystemAccess,
+            bufferProvider,
+            stringInterner,
+            new GZipNextGenBuildCacheAccess(
+                new DefaultNextGenBuildCacheAccess(
+                    local,
+                    remote,
+                    bufferProvider,
+                    executorFactory
+                ),
+                bufferProvider
+            )
+        );
+    }
+
+    private static NextGenBuildCacheHandler resolveService(@Nullable DescribedBuildCacheService<? extends BuildCache, ? extends BuildCacheService> describedService) {
+        return describedService != null && describedService.config.isEnabled()
+            ? new DefaultNextGenBuildCacheHandler(makeCompatible(describedService.service), describedService.config.isPush())
+            : DISABLED_BUILD_CACHE_HANDLER;
+    }
+
+    private static NextGenBuildCacheService makeCompatible(BuildCacheService service) {
+        if (service instanceof NextGenBuildCacheService) {
+            return (NextGenBuildCacheService) service;
+        }
+        return new NextGenBuildCacheService() {
+            @Override
+            public boolean contains(BuildCacheKey key) {
+                return load(key, __ -> {});
+            }
+
+            @Override
+            public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+                return service.load(key, reader);
+            }
+
+            @Override
+            public void store(BuildCacheKey key, BuildCacheEntryWriter legacyWriter) throws BuildCacheException {
+                service.store(key, legacyWriter);
+            }
+
+            @Override
+            public void store(BuildCacheKey key, NextGenWriter writer) throws BuildCacheException {
+                service.store(key, writer);
+            }
+
+            @Override
+            public void close() throws IOException {
+                service.close();
+            }
+        };
+    }
+
+    private static final NextGenBuildCacheHandler DISABLED_BUILD_CACHE_HANDLER = new NextGenBuildCacheHandler() {
+        @Override
+        public boolean canLoad() {
+            return false;
+        }
+
+        @Override
+        public boolean canStore() {
+            return false;
+        }
+
+        @Override
+        public boolean contains(BuildCacheKey key) {
+            return false;
+        }
+
+        @Override
+        public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+            return false;
+        }
+
+        @Override
+        public void store(BuildCacheKey key, NextGenBuildCacheService.NextGenWriter writer) throws BuildCacheException {
+        }
+
+        @Override
+        public void close() {
+        }
+    };
+
+    private static class DefaultNextGenBuildCacheHandler implements NextGenBuildCacheHandler {
+        private final NextGenBuildCacheService service;
+        private final boolean pushEnabled;
+
+        public DefaultNextGenBuildCacheHandler(NextGenBuildCacheService service, boolean pushEnabled) {
+            this.service = service;
+            this.pushEnabled = pushEnabled;
+        }
+
+        @Override
+        public boolean canLoad() {
+            return true;
+        }
+
+        @Override
+        public boolean canStore() {
+            return pushEnabled;
+        }
+
+        @Override
+        public boolean contains(BuildCacheKey key) {
+            return service.contains(key);
+        }
+
+        @Override
+        public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+            return service.load(key, reader);
+        }
+
+        @Override
+        public void store(BuildCacheKey key, NextGenBuildCacheService.NextGenWriter writer) throws BuildCacheException {
+            if (pushEnabled) {
+                service.store(key, writer);
+            }
+        }
+
+        @Override
+        public void close() throws IOException {
+            service.close();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/configuration/BuildTreePreparingProjectsPreparer.java b/subprojects/core/src/main/java/org/gradle/configuration/BuildTreePreparingProjectsPreparer.java
index 0073a94..c617a87 100644
--- a/subprojects/core/src/main/java/org/gradle/configuration/BuildTreePreparingProjectsPreparer.java
+++ b/subprojects/core/src/main/java/org/gradle/configuration/BuildTreePreparingProjectsPreparer.java
@@ -26,6 +26,7 @@
 import org.gradle.initialization.buildsrc.BuildSourceBuilder;
 import org.gradle.internal.buildtree.BuildInclusionCoordinator;
 import org.gradle.internal.classpath.ClassPath;
+import org.gradle.internal.classpath.Instrumented;
 import org.gradle.internal.management.DependencyResolutionManagementInternal;
 import org.gradle.internal.service.ServiceRegistry;
 
@@ -77,6 +78,8 @@ private void generateDependenciesAccessorsAndAssignPluginVersions(ServiceRegistr
         dm.getDefaultLibrariesExtensionName().finalizeValue();
         String defaultLibrary = dm.getDefaultLibrariesExtensionName().get();
         File dependenciesFile = new File(settings.getSettingsDir(), "gradle/libs.versions.toml");
+        // Notify the CC
+        Instrumented.fileObserved(dependenciesFile, getClass().getName());
         if (dependenciesFile.exists()) {
             dm.versionCatalogs(catalogs -> {
                 VersionCatalogBuilder builder = catalogs.findByName(defaultLibrary);
diff --git a/subprojects/core/src/main/java/org/gradle/execution/BaseSelectionException.java b/subprojects/core/src/main/java/org/gradle/execution/BaseSelectionException.java
new file mode 100644
index 0000000..696b4c8
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/execution/BaseSelectionException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023 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.execution;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.internal.exceptions.FailureResolutionAware;
+
+import static org.gradle.internal.logging.text.StyledTextOutput.Style.UserInput;
+
+abstract class BaseSelectionException extends InvalidUserDataException implements FailureResolutionAware {
+    private final String taskName;
+    private final String targetName;
+
+    public BaseSelectionException(String message, String taskName, String targetName) {
+        super(message);
+        this.taskName = taskName;
+        this.targetName = targetName;
+    }
+
+    @Override
+    public void appendResolutions(Context context) {
+        context.appendResolution(output -> {
+            output.text("Run ");
+            context.getClientMetaData().describeCommand(output.withStyle(UserInput), taskName);
+            output.text(" to get a list of available " + targetName + ".");
+        });
+        context.appendDocumentationResolution("on name expansion", "command_line_interface", "sec:name_abbreviation");
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/execution/ExecutionAccessChecker.java b/subprojects/core/src/main/java/org/gradle/execution/ExecutionAccessChecker.java
new file mode 100644
index 0000000..1de531e
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/execution/ExecutionAccessChecker.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.execution;
+
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+@ServiceScope(Scopes.BuildTree.class)
+public interface ExecutionAccessChecker {
+
+    void disallowedAtExecutionInjectedServiceAccessed(Class<?> injectedServiceType, String getterName, String consumer);
+}
diff --git a/subprojects/core/src/main/java/org/gradle/execution/ExecutionAccessListener.java b/subprojects/core/src/main/java/org/gradle/execution/ExecutionAccessListener.java
new file mode 100644
index 0000000..5d6906e
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/execution/ExecutionAccessListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 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.execution;
+
+import org.gradle.internal.service.scopes.EventScope;
+import org.gradle.internal.service.scopes.Scopes;
+
+@EventScope(Scopes.BuildTree.class)
+public interface ExecutionAccessListener {
+
+    /**
+     * Called when accessing the injected service of type that disallowed during execution phase (eg. Project).
+     */
+    void disallowedAtExecutionInjectedServiceAccessed(Class<?> injectedServiceType, String getterName, String consumer);
+}
diff --git a/subprojects/core/src/main/java/org/gradle/execution/ProjectSelectionException.java b/subprojects/core/src/main/java/org/gradle/execution/ProjectSelectionException.java
index cd93b45..a1a2df7 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/ProjectSelectionException.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/ProjectSelectionException.java
@@ -18,22 +18,11 @@
 
 import org.gradle.api.internal.project.ProjectInternal;
 
-import static org.gradle.internal.logging.text.StyledTextOutput.Style.UserInput;
-
 /**
  * Thrown when the tasks to execute cannot be selected due to a problem resolving the project to select tasks from.
  */
-public class ProjectSelectionException extends TaskSelectionException {
+public class ProjectSelectionException extends BaseSelectionException {
     public ProjectSelectionException(String message) {
-        super(message);
-    }
-
-    @Override
-    public void appendResolutions(Context context) {
-        context.appendResolution(output -> {
-            output.text("Run ");
-            context.getClientMetaData().describeCommand(output.withStyle(UserInput), ProjectInternal.PROJECTS_TASK);
-            output.text(" to get a list of available projects.");
-        });
+        super(message, ProjectInternal.PROJECTS_TASK, "projects");
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/execution/TaskSelectionException.java b/subprojects/core/src/main/java/org/gradle/execution/TaskSelectionException.java
index 95f3bfe..e449d4a 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/TaskSelectionException.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/TaskSelectionException.java
@@ -15,26 +15,13 @@
  */
 package org.gradle.execution;
 
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.internal.exceptions.FailureResolutionAware;
-
-import static org.gradle.internal.logging.text.StyledTextOutput.Style.UserInput;
 
 /**
  * A {@code TaskSelectionException} is thrown when the tasks to execute cannot be selected due to some user input problem.
  */
-public class TaskSelectionException extends InvalidUserDataException implements FailureResolutionAware {
+public class TaskSelectionException extends BaseSelectionException {
     public TaskSelectionException(String message) {
-        super(message);
-    }
-
-    @Override
-    public void appendResolutions(Context context) {
-        context.appendResolution(output -> {
-            output.text("Run ");
-            context.getClientMetaData().describeCommand(output.withStyle(UserInput), ProjectInternal.TASKS_TASK);
-            output.text(" to get a list of available tasks.");
-        });
+        super(message, ProjectInternal.TASKS_TASK, "tasks");
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/execution/commandline/CommandLineTaskConfigurer.java b/subprojects/core/src/main/java/org/gradle/execution/commandline/CommandLineTaskConfigurer.java
index 4bea895..7eee99d 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/commandline/CommandLineTaskConfigurer.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/commandline/CommandLineTaskConfigurer.java
@@ -17,6 +17,8 @@
 package org.gradle.execution.commandline;
 
 import org.gradle.api.Task;
+import org.gradle.api.internal.tasks.TaskOptionsGenerator;
+import org.gradle.api.internal.tasks.TaskOptionsGenerator.TaskOptions;
 import org.gradle.api.internal.tasks.options.OptionDescriptor;
 import org.gradle.api.internal.tasks.options.OptionReader;
 import org.gradle.cli.CommandLineArgumentException;
@@ -26,19 +28,12 @@
 import org.gradle.internal.service.scopes.Scopes;
 import org.gradle.internal.service.scopes.ServiceScope;
 import org.gradle.internal.typeconversion.TypeConversionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
 
 @ServiceScope(Scopes.Gradle.class)
 public class CommandLineTaskConfigurer {
-    private static final Logger LOGGER = LoggerFactory.getLogger(CommandLineTaskConfigurer.class);
-
     private OptionReader optionReader;
 
     public CommandLineTaskConfigurer(OptionReader optionReader) {
@@ -57,16 +52,15 @@ private List<String> configureTasksNow(Collection<Task> tasks, List<String> argu
         List<String> remainingArguments = null;
         for (Task task : tasks) {
             CommandLineParser parser = new CommandLineParser();
-            Map<Boolean, List<OptionDescriptor>> allCommandLineOptions = optionReader.getOptions(task, false).stream().collect(Collectors.groupingBy(OptionDescriptor::isClashing));
-            List<OptionDescriptor> validCommandLineOptions = allCommandLineOptions.getOrDefault(false, Collections.emptyList());
-            List<OptionDescriptor> clashingOptions = allCommandLineOptions.getOrDefault(true, Collections.emptyList());
-            clashingOptions.forEach(it -> LOGGER.warn("Built-in option '{}' in task {} was disabled for clashing with another option of same name", it.getName(), task.getPath()));
-            for (OptionDescriptor optionDescriptor : validCommandLineOptions) {
+            TaskOptions taskOptions = TaskOptionsGenerator.generate(task, optionReader);
+            List<OptionDescriptor> commandLineOptions = taskOptions.getAll();
+            for (OptionDescriptor optionDescriptor : commandLineOptions) {
                 String optionName = optionDescriptor.getName();
                 org.gradle.cli.CommandLineOption option = parser.option(optionName);
                 option.hasDescription(optionDescriptor.getDescription());
                 option.hasArgument(optionDescriptor.getArgumentType());
             }
+            taskOptions.addMutualExclusions(parser);
 
             ParsedCommandLine parsed;
             try {
@@ -76,7 +70,7 @@ private List<String> configureTasksNow(Collection<Task> tasks, List<String> argu
                 throw new TaskConfigurationException(task.getPath(), "Problem configuring task " + task.getPath() + " from command line.", e);
             }
 
-            for (OptionDescriptor commandLineOptionDescriptor : validCommandLineOptions) {
+            for (OptionDescriptor commandLineOptionDescriptor : commandLineOptions) {
                 final String name = commandLineOptionDescriptor.getName();
                 if (parsed.hasOption(name)) {
                     ParsedCommandLineOption o = parsed.option(name);
diff --git a/subprojects/core/src/main/java/org/gradle/execution/plan/DefaultNodeValidator.java b/subprojects/core/src/main/java/org/gradle/execution/plan/DefaultNodeValidator.java
index 6dcb464..a1498bc 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/plan/DefaultNodeValidator.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/plan/DefaultNodeValidator.java
@@ -73,14 +73,18 @@ private void logWarnings(List<TypeValidationProblem> problems) {
     }
 
     private void reportErrors(List<TypeValidationProblem> problems, TaskInternal task, WorkValidationContext validationContext) {
-        ImmutableSet<String> uniqueErrors = problems.stream()
-                .filter(problem -> !problem.getSeverity().isWarning())
-                .map(TypeValidationProblemRenderer::renderMinimalInformationAbout)
-                .collect(ImmutableSet.toImmutableSet());
+        ImmutableSet<String> uniqueErrors = getUniqueErrors(problems);
         if (!uniqueErrors.isEmpty()) {
             throw WorkValidationException.forProblems(uniqueErrors)
-                    .withSummaryForContext(task.toString(), validationContext)
-                    .get();
+                .withSummaryForContext(task.toString(), validationContext)
+                .get();
         }
     }
+
+    private static ImmutableSet<String> getUniqueErrors(List<TypeValidationProblem> problems) {
+        return problems.stream()
+            .filter(problem -> !problem.getSeverity().isWarning())
+            .map(TypeValidationProblemRenderer::renderMinimalInformationAbout)
+            .collect(ImmutableSet.toImmutableSet());
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/execution/plan/LocalTaskNode.java b/subprojects/core/src/main/java/org/gradle/execution/plan/LocalTaskNode.java
index ab600c1..c75e2ba 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/plan/LocalTaskNode.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/plan/LocalTaskNode.java
@@ -235,6 +235,8 @@ public void resolveMutations() {
             addDestroyablesToMutations(taskProperties.getDestroyableFiles());
 
             mutations.hasFileInputs = !taskProperties.getInputFileProperties().isEmpty();
+            // piggyback on mutation resolution to declare service references as used services
+            task.acceptServiceReferences(taskProperties.getServiceReferences());
         } catch (Exception e) {
             throw new TaskExecutionException(task, e);
         }
diff --git a/subprojects/core/src/main/java/org/gradle/execution/plan/Node.java b/subprojects/core/src/main/java/org/gradle/execution/plan/Node.java
index 2f352bd..8e7b519 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/plan/Node.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/plan/Node.java
@@ -603,6 +603,10 @@ public ProjectInternal getOwningProject() {
 
     /**
      * Returns the resources which should be locked before starting this node.
+     *
+     * This operation should complete quickly,
+     * must not run user code, and
+     * should not need to acquire additional locks.
      */
     public List<? extends ResourceLock> getResourcesToLock() {
         return Collections.emptyList();
diff --git a/subprojects/core/src/main/java/org/gradle/execution/plan/PlannedNodeInternal.java b/subprojects/core/src/main/java/org/gradle/execution/plan/PlannedNodeInternal.java
new file mode 100644
index 0000000..61d246a
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/execution/plan/PlannedNodeInternal.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.execution.plan;
+
+import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType;
+import org.gradle.internal.taskgraph.NodeIdentity;
+
+import java.util.List;
+
+public interface PlannedNodeInternal extends CalculateTaskGraphBuildOperationType.PlannedNode {
+
+    /**
+     * Creates a copy of this node with the given dependencies.
+     */
+    PlannedNodeInternal withNodeDependencies(List<? extends NodeIdentity> nodeDependencies);
+
+}
diff --git a/subprojects/core/src/main/java/org/gradle/execution/plan/TaskInAnotherBuild.java b/subprojects/core/src/main/java/org/gradle/execution/plan/TaskInAnotherBuild.java
index ad72406..4081ea5 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/plan/TaskInAnotherBuild.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/plan/TaskInAnotherBuild.java
@@ -66,7 +66,7 @@ public static TaskInAnotherBuild lazy(
         BuildTreeWorkGraphController taskGraph
     ) {
         TaskIdentifier taskIdentifier = TaskIdentifier.of(targetBuild, taskPath);
-        Path taskIdentityPath = Path.path(targetBuild.getName()).append(Path.path(taskPath));
+        Path taskIdentityPath = Path.path(targetBuild.getBuildPath()).append(Path.path(taskPath));
         Lazy<IncludedBuildTaskResource> target = Lazy.unsafe().of(() -> taskGraph.locateTask(taskIdentifier));
         return new TaskInAnotherBuild(taskIdentityPath, taskPath, targetBuild) {
             @Override
diff --git a/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedNodeConverter.java b/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedNodeConverter.java
index 2794bfd..6a87ba4 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedNodeConverter.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedNodeConverter.java
@@ -16,10 +16,10 @@
 
 package org.gradle.execution.plan;
 
-import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType;
-import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.TaskIdentity;
+import org.gradle.api.NonNullApi;
 import org.gradle.internal.taskgraph.NodeIdentity;
 
+import javax.annotation.concurrent.ThreadSafe;
 import java.util.List;
 
 /**
@@ -27,7 +27,11 @@
  * <p>
  * Each implementation of this interface is responsible for nodes of {@link #getSupportedNodeType()}.
  * The converter can obtain the node identity for each node of the supported type via {@link #getNodeIdentity(Node)}.
+ * <p>
+ * Instances of this class are expected to be thread-safe.
  */
+@NonNullApi
+@ThreadSafe
 public interface ToPlannedNodeConverter {
 
     /**
@@ -36,12 +40,19 @@ public interface ToPlannedNodeConverter {
     Class<? extends Node> getSupportedNodeType();
 
     /**
+     * Node type of the planned node after conversion.
+     */
+    NodeIdentity.NodeType getConvertedNodeType();
+
+    /**
      * Provides a unique identity for the node of the {@link #getSupportedNodeType() supported type}.
      */
     NodeIdentity getNodeIdentity(Node node);
 
     /**
-     * Returns true if the given {@link Node} is not an actual executable node but only represents a node from another execution plan.
+     * Returns true if the given {@link Node} is from the same execution plan.
+     * <p>
+     * A node can be in another execution plan if it is, for instance, from an included build ({@link TaskInAnotherBuild}).
      */
     boolean isInSamePlan(Node node);
 
@@ -50,20 +61,5 @@ public interface ToPlannedNodeConverter {
      * <p>
      * Expects a node of the {@link #getSupportedNodeType() supported type} that is in the {@link #isInSamePlan(Node) same plan}.
      */
-    CalculateTaskGraphBuildOperationType.PlannedNode convert(Node node, DependencyLookup dependencyLookup);
-
-    interface DependencyLookup {
-
-        /**
-         * Finds all identifiable dependencies of the given node.
-         */
-        List<? extends NodeIdentity> findNodeDependencies(Node node);
-
-        /**
-         * Finds dependencies that are task nodes, skipping over other nodes.
-         * <p>
-         * This is required for compatibility reasons for the {@link ToPlannedTaskConverter}.
-         */
-        List<? extends TaskIdentity> findTaskDependencies(Node node);
-    }
+    PlannedNodeInternal convert(Node node, List<? extends NodeIdentity> nodeDependencies);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedNodeConverterRegistry.java b/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedNodeConverterRegistry.java
new file mode 100644
index 0000000..e77432d
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedNodeConverterRegistry.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2023 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.execution.plan;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.NonNullApi;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+import org.gradle.internal.taskgraph.NodeIdentity;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+/**
+ * A Gradle user home level registry of {@link ToPlannedNodeConverter} instances.
+ * <p>
+ * All the available converters are expected to support disjoint set of {@link Node node types}.
+ */
+@NonNullApi
+@ThreadSafe
+@ServiceScope(Scopes.UserHome.class)
+public class ToPlannedNodeConverterRegistry {
+
+    private static final ToPlannedNodeConverter MISSING_MARKER = new MissingToPlannedNodeConverter();
+
+    private final List<ToPlannedNodeConverter> converters;
+
+    private final ConcurrentMap<Class<? extends Node>, ToPlannedNodeConverter> convertersByNodeType = new ConcurrentHashMap<>();
+
+    public ToPlannedNodeConverterRegistry(List<ToPlannedNodeConverter> converters) {
+        validateConverters(converters);
+        this.converters = ImmutableList.copyOf(converters);
+
+        for (ToPlannedNodeConverter converter : this.converters) {
+            convertersByNodeType.put(converter.getSupportedNodeType(), converter);
+        }
+    }
+
+    /**
+     * Returns a set of node types that this converter registry can provide.
+     */
+    public Set<NodeIdentity.NodeType> getConvertedNodeTypes() {
+        return converters.stream()
+            .map(ToPlannedNodeConverter::getConvertedNodeType)
+            .collect(Collectors.toSet());
+    }
+
+    /**
+     * Returns a converter for the given node, or null if there is no converter for the node.
+     */
+    @Nullable
+    public ToPlannedNodeConverter getConverter(Node node) {
+        Class<? extends Node> nodeType = node.getClass();
+        ToPlannedNodeConverter converter = convertersByNodeType.computeIfAbsent(nodeType, this::findConverter);
+        return converter == MISSING_MARKER ? null : converter;
+    }
+
+    private ToPlannedNodeConverter findConverter(Class<? extends Node> nodeType) {
+        for (ToPlannedNodeConverter converterCandidate : converters) {
+            Class<? extends Node> supportedNodeType = converterCandidate.getSupportedNodeType();
+            if (supportedNodeType.isAssignableFrom(nodeType)) {
+                return converterCandidate;
+            }
+        }
+
+        return MISSING_MARKER;
+    }
+
+    private static void validateConverters(List<ToPlannedNodeConverter> converters) {
+        int converterCount = converters.size();
+        for (int i = 0; i < converterCount; i++) {
+            ToPlannedNodeConverter converter1 = converters.get(i);
+            for (int j = i + 1; j < converterCount; j++) {
+                ToPlannedNodeConverter converter2 = converters.get(j);
+                checkOverlappingConverters(converter1, converter2);
+            }
+        }
+    }
+
+    private static void checkOverlappingConverters(ToPlannedNodeConverter converter1, ToPlannedNodeConverter converter2) {
+        Class<? extends Node> supportedNodeType1 = converter1.getSupportedNodeType();
+        Class<? extends Node> supportedNodeType2 = converter2.getSupportedNodeType();
+        if (supportedNodeType1.isAssignableFrom(supportedNodeType2) || supportedNodeType2.isAssignableFrom(supportedNodeType1)) {
+            throw new IllegalStateException("Converter " + converter1 + " overlaps by supported node type with converter " + converter2);
+        }
+    }
+
+    private static final class MissingToPlannedNodeConverter implements ToPlannedNodeConverter {
+        @Override
+        public Class<? extends Node> getSupportedNodeType() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public NodeIdentity.NodeType getConvertedNodeType() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public NodeIdentity getNodeIdentity(Node node) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isInSamePlan(Node node) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public PlannedNodeInternal convert(Node node, List<? extends NodeIdentity> nodeDependencies) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+}
diff --git a/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedTaskConverter.java b/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedTaskConverter.java
index 82efa42..bf3b797 100644
--- a/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedTaskConverter.java
+++ b/subprojects/core/src/main/java/org/gradle/execution/plan/ToPlannedTaskConverter.java
@@ -16,21 +16,23 @@
 
 package org.gradle.execution.plan;
 
+import org.gradle.api.NonNullApi;
 import org.gradle.initialization.DefaultPlannedTask;
-import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType;
 import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.TaskIdentity;
 import org.gradle.internal.taskgraph.NodeIdentity;
 
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 /**
  * A {@link ToPlannedTaskConverter} for {@link TaskNode}s.
  * <p>
- * Only can convert {@link LocalTaskNode}s to {@link org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.PlannedTask}s.
+ * Only can convert {@link LocalTaskNode}s to planned tasks.
  */
+@NonNullApi
 public class ToPlannedTaskConverter implements ToPlannedNodeConverter {
 
     @Override
@@ -39,30 +41,64 @@ public Class<? extends Node> getSupportedNodeType() {
     }
 
     @Override
+    public NodeIdentity.NodeType getConvertedNodeType() {
+        return NodeIdentity.NodeType.TASK;
+    }
+
+    @Override
     public TaskIdentity getNodeIdentity(Node node) {
         TaskNode taskNode = (TaskNode) node;
-        org.gradle.api.internal.project.taskfactory.TaskIdentity<?> delegate = taskNode.getTask().getTaskIdentity();
-        return new TaskIdentity() {
-            @Override
-            public String getBuildPath() {
-                return delegate.getBuildPath();
-            }
+        return new PlannedTaskIdentity(taskNode.getTask().getTaskIdentity());
+    }
 
-            @Override
-            public String getTaskPath() {
-                return delegate.getTaskPath();
-            }
+    private static class PlannedTaskIdentity implements TaskIdentity {
+        private final org.gradle.api.internal.project.taskfactory.TaskIdentity<?> delegate;
 
-            @Override
-            public long getTaskId() {
-                return delegate.getId();
-            }
+        public PlannedTaskIdentity(org.gradle.api.internal.project.taskfactory.TaskIdentity<?> delegate) {
+            this.delegate = delegate;
+        }
 
-            @Override
-            public String toString() {
-                return "Task " + delegate.getTaskPath();
+        @Override
+        public NodeIdentity.NodeType getNodeType() {
+            return NodeIdentity.NodeType.TASK;
+        }
+
+        @Override
+        public String getBuildPath() {
+            return delegate.getBuildPath();
+        }
+
+        @Override
+        public String getTaskPath() {
+            return delegate.getTaskPath();
+        }
+
+        @Override
+        public long getTaskId() {
+            return delegate.getId();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
             }
-        };
+            if (!(o instanceof PlannedTaskIdentity)) {
+                return false;
+            }
+            PlannedTaskIdentity that = (PlannedTaskIdentity) o;
+            return delegate.equals(that.delegate);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(delegate);
+        }
+
+        @Override
+        public String toString() {
+            return "Task " + delegate.getIdentityPath();
+        }
     }
 
     @Override
@@ -71,19 +107,15 @@ public boolean isInSamePlan(Node node) {
     }
 
     @Override
-    public CalculateTaskGraphBuildOperationType.PlannedTask convert(Node node, DependencyLookup dependencyLookup) {
+    public DefaultPlannedTask convert(Node node, List<? extends NodeIdentity> nodeDependencies) {
         if (!isInSamePlan(node)) {
             throw new IllegalArgumentException("Cannot convert task from another plan: " + node);
         }
 
         LocalTaskNode taskNode = (LocalTaskNode) node;
-        List<? extends NodeIdentity> nodeDependencies = dependencyLookup.findNodeDependencies(taskNode);
-        @SuppressWarnings("unchecked")
-        List<TaskIdentity> taskDependencies = (List<TaskIdentity>) dependencyLookup.findTaskDependencies(taskNode);
         return new DefaultPlannedTask(
             getNodeIdentity(taskNode),
             nodeDependencies,
-            taskDependencies,
             getTaskIdentities(taskNode.getMustSuccessors()),
             getTaskIdentities(taskNode.getShouldSuccessors()),
             getTaskIdentities(taskNode.getFinalizers())
@@ -101,4 +133,9 @@ private List<TaskIdentity> getTaskIdentities(Collection<Node> nodes) {
             .map(this::getNodeIdentity)
             .collect(Collectors.toList());
     }
+
+    @Override
+    public String toString() {
+        return "ToPlannedTaskConverter(" + getSupportedNodeType().getSimpleName() + ")";
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java
index a12f024..f5bc2ec 100644
--- a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java
+++ b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/FileCacheBackedScriptClassCompiler.java
@@ -26,6 +26,7 @@
 import org.gradle.internal.Pair;
 import org.gradle.internal.classanalysis.AsmConstants;
 import org.gradle.internal.classpath.CachedClasspathTransformer;
+import org.gradle.internal.classpath.ClassData;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.classpath.ClasspathEntryVisitor;
 import org.gradle.internal.classpath.DefaultClassPath;
@@ -141,7 +142,7 @@ public void applyConfigurationTo(Hasher hasher) {
             }
 
             @Override
-            public Pair<RelativePath, ClassVisitor> apply(ClasspathEntryVisitor.Entry entry, ClassVisitor visitor) throws IOException {
+            public Pair<RelativePath, ClassVisitor> apply(ClasspathEntryVisitor.Entry entry, ClassVisitor visitor, ClassData classData) throws IOException {
                 String renamed = entry.getPath().getLastName();
                 if (renamed.startsWith(RemappingScriptSource.MAPPED_SCRIPT)) {
                     renamed = className + renamed.substring(RemappingScriptSource.MAPPED_SCRIPT.length());
diff --git a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/InitialPassStatementTransformer.java b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/InitialPassStatementTransformer.java
index a0e2862..b15d4a6 100644
--- a/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/InitialPassStatementTransformer.java
+++ b/subprojects/core/src/main/java/org/gradle/groovy/scripts/internal/InitialPassStatementTransformer.java
@@ -148,9 +148,9 @@ private String failureMessageForPluginManagementBlock() {
 
     private String makePluginManagementError(String failureMessage) {
         return String.format(
-            "%s%n%nSee %s for information on the pluginManagement {} block%n%n",
+            "%s%n%n%s%n%n",
             failureMessage,
-            documentationRegistry.getDocumentationFor("plugins", "sec:plugin_management"));
+            documentationRegistry.getDocumentationRecommendationFor("information on the pluginManagement {} block", "plugins", "sec:plugin_management"));
     }
 
 }
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradleProperties.java b/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradleProperties.java
index e036dd0..06e5312 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradleProperties.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradleProperties.java
@@ -16,17 +16,17 @@
 
 package org.gradle.initialization;
 
-import com.google.common.collect.ImmutableMap;
-import org.gradle.api.internal.properties.GradleProperties;
+import org.gradle.initialization.properties.MutableGradleProperties;
 
 import javax.annotation.Nullable;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
-class DefaultGradleProperties implements GradleProperties {
+public class DefaultGradleProperties implements MutableGradleProperties {
     private final Map<String, Object> defaultProperties;
     private final Map<String, Object> overrideProperties;
-    private final ImmutableMap<String, Object> gradleProperties;
+    private final Map<String, Object> gradleProperties;
 
     public DefaultGradleProperties(
         Map<String, Object> defaultProperties,
@@ -34,7 +34,7 @@ public DefaultGradleProperties(
     ) {
         this.defaultProperties = defaultProperties;
         this.overrideProperties = overrideProperties;
-        gradleProperties = immutablePropertiesWith(ImmutableMap.of());
+        this.gradleProperties = mergePropertiesWith(Collections.emptyMap());
     }
 
     @Nullable
@@ -47,14 +47,22 @@ public Object find(String propertyName) {
     public Map<String, Object> mergeProperties(Map<String, Object> properties) {
         return properties.isEmpty()
             ? gradleProperties
-            : immutablePropertiesWith(properties);
+            : mergePropertiesWith(properties);
     }
 
-    ImmutableMap<String, Object> immutablePropertiesWith(Map<String, Object> properties) {
-        return ImmutableMap.copyOf(mergePropertiesWith(properties));
+    @Override
+    public void updateOverrideProperties(Map<String, Object> properties) {
+        overrideProperties.putAll(properties);
+        gradleProperties.clear();
+        gradleProperties.putAll(mergePropertiesWith(Collections.emptyMap()));
     }
 
-    Map<String, Object> mergePropertiesWith(Map<String, Object> properties) {
+    @Override
+    public Map<String, Object> getProperties() {
+        return new HashMap<>(gradleProperties);
+    }
+
+    private Map<String, Object> mergePropertiesWith(Map<String, Object> properties) {
         Map<String, Object> result = new HashMap<>();
         result.putAll(defaultProperties);
         result.putAll(properties);
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradlePropertiesController.java b/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradlePropertiesController.java
index 7d815f2..a629e14 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradlePropertiesController.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradlePropertiesController.java
@@ -17,6 +17,9 @@
 package org.gradle.initialization;
 
 import org.gradle.api.internal.properties.GradleProperties;
+import org.gradle.initialization.properties.MutableGradleProperties;
+import org.gradle.initialization.properties.ProjectPropertiesLoader;
+import org.gradle.initialization.properties.SystemPropertiesInstaller;
 
 import javax.annotation.Nullable;
 import java.io.File;
@@ -27,9 +30,13 @@ public class DefaultGradlePropertiesController implements GradlePropertiesContro
     private State state = new NotLoaded();
     private final GradleProperties sharedGradleProperties = new SharedGradleProperties();
     private final IGradlePropertiesLoader propertiesLoader;
+    private final SystemPropertiesInstaller systemPropertiesInstaller;
+    private final ProjectPropertiesLoader projectPropertiesLoader;
 
-    public DefaultGradlePropertiesController(IGradlePropertiesLoader propertiesLoader) {
+    public DefaultGradlePropertiesController(IGradlePropertiesLoader propertiesLoader, SystemPropertiesInstaller systemPropertiesInstaller, ProjectPropertiesLoader projectPropertiesLoader) {
         this.propertiesLoader = propertiesLoader;
+        this.systemPropertiesInstaller = systemPropertiesInstaller;
+        this.projectPropertiesLoader = projectPropertiesLoader;
     }
 
     @Override
@@ -38,13 +45,13 @@ public GradleProperties getGradleProperties() {
     }
 
     @Override
-    public void loadGradlePropertiesFrom(File settingsDir) {
-        state = state.loadGradlePropertiesFrom(settingsDir);
+    public void loadGradlePropertiesFrom(File settingsDir, boolean setSystemProperties) {
+        state = state.loadGradlePropertiesFrom(settingsDir, setSystemProperties);
     }
 
     @Override
     public void unloadGradleProperties() {
-       state = new NotLoaded();
+        state = new NotLoaded();
     }
 
     public void overrideWith(GradleProperties gradleProperties) {
@@ -64,6 +71,11 @@ public Map<String, Object> mergeProperties(Map<String, Object> properties) {
             return gradleProperties().mergeProperties(properties);
         }
 
+        @Override
+        public Map<String, Object> getProperties() {
+            return gradleProperties().getProperties();
+        }
+
         private GradleProperties gradleProperties() {
             return state.gradleProperties();
         }
@@ -73,7 +85,7 @@ private interface State {
 
         GradleProperties gradleProperties();
 
-        State loadGradlePropertiesFrom(File settingsDir);
+        State loadGradlePropertiesFrom(File settingsDir, boolean setSystemProperties);
 
         State overrideWith(GradleProperties gradleProperties);
     }
@@ -86,11 +98,16 @@ public GradleProperties gradleProperties() {
         }
 
         @Override
-        public State loadGradlePropertiesFrom(File settingsDir) {
-            return new Loaded(
-                propertiesLoader.loadGradleProperties(settingsDir),
-                settingsDir
-            );
+        public State loadGradlePropertiesFrom(File settingsDir, boolean setSystemProperties) {
+            MutableGradleProperties loadedProperties = propertiesLoader.loadGradleProperties(settingsDir);
+
+            if (setSystemProperties) {
+                systemPropertiesInstaller.setSystemPropertiesFrom(loadedProperties);
+            }
+
+            Map<String, Object> projectProperties = projectPropertiesLoader.loadProjectProperties();
+            loadedProperties.updateOverrideProperties(projectProperties);
+            return new Loaded(loadedProperties, settingsDir);
         }
 
         @Override
@@ -104,7 +121,7 @@ private static class Loaded implements State {
         private final GradleProperties gradleProperties;
         private final File propertiesDir;
 
-        public Loaded(GradleProperties gradleProperties, File propertiesDir) {
+        public Loaded(MutableGradleProperties gradleProperties, File propertiesDir) {
             this.gradleProperties = gradleProperties;
             this.propertiesDir = propertiesDir;
         }
@@ -115,7 +132,7 @@ public GradleProperties gradleProperties() {
         }
 
         @Override
-        public State loadGradlePropertiesFrom(File settingsDir) {
+        public State loadGradlePropertiesFrom(File settingsDir, boolean setSystemProperties) {
             if (!propertiesDir.equals(settingsDir)) {
                 throw new IllegalStateException(
                     String.format(
@@ -147,7 +164,7 @@ public GradleProperties gradleProperties() {
         }
 
         @Override
-        public State loadGradlePropertiesFrom(File settingsDir) {
+        public State loadGradlePropertiesFrom(File settingsDir, boolean setSystemProperties) {
             throw new IllegalStateException();
         }
 
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradlePropertiesLoader.java b/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradlePropertiesLoader.java
index d5a20e2..767ccda 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradlePropertiesLoader.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/DefaultGradlePropertiesLoader.java
@@ -15,44 +15,32 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.api.Project;
-import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.StartParameterInternal;
-import org.gradle.api.internal.properties.GradleProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.gradle.initialization.properties.MutableGradleProperties;
 
 import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
 
 import static org.gradle.api.Project.GRADLE_PROPERTIES;
-import static org.gradle.internal.Cast.uncheckedNonnullCast;
 
 public class DefaultGradlePropertiesLoader implements IGradlePropertiesLoader {
-    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultGradlePropertiesLoader.class);
 
     private final StartParameterInternal startParameter;
 
     private final Environment environment;
 
-    private final EnvironmentChangeTracker environmentChangeTracker;
-
-    private final GradleInternal gradleInternal;
-
-    public DefaultGradlePropertiesLoader(StartParameterInternal startParameter, Environment environment, EnvironmentChangeTracker environmentChangeTracker, GradleInternal gradleInternal) {
+    public DefaultGradlePropertiesLoader(StartParameterInternal startParameter, Environment environment) {
         this.startParameter = startParameter;
         this.environment = environment;
-        this.environmentChangeTracker = environmentChangeTracker;
-        this.gradleInternal = gradleInternal;
     }
 
     @Override
-    public GradleProperties loadGradleProperties(File rootDir) {
+    public MutableGradleProperties loadGradleProperties(File rootDir) {
         return loadProperties(rootDir);
     }
 
-    GradleProperties loadProperties(File rootDir) {
+    private MutableGradleProperties loadProperties(File rootDir) {
         Map<String, Object> defaultProperties = new HashMap<>();
         Map<String, Object> overrideProperties = new HashMap<>();
 
@@ -60,76 +48,13 @@ GradleProperties loadProperties(File rootDir) {
         addGradlePropertiesFrom(rootDir, defaultProperties);
         addGradlePropertiesFrom(startParameter.getGradleUserHomeDir(), overrideProperties);
 
-        // TODO:configuration-cache What happens when a system property is set from a Gradle property and
-        //    that same system property is then used to set a Gradle property from an included build?
-        //    e.g., included-build/gradle.properties << systemProp.org.gradle.project.fromSystemProp=42
-        setSystemPropertiesFromGradleProperties(defaultProperties);
-        setSystemPropertiesFromGradleProperties(overrideProperties);
-        setSystemPropertiesFromStartParameter(startParameter.getSystemPropertiesArgs());
-
-        overrideProperties.putAll(projectPropertiesFromEnvironmentVariables());
-        overrideProperties.putAll(projectPropertiesFromSystemProperties());
-        overrideProperties.putAll(startParameter.getProjectProperties());
-
         return new DefaultGradleProperties(defaultProperties, overrideProperties);
     }
 
-    private void setSystemPropertiesFromStartParameter(Map<String, String> systemPropertiesArgs) {
-        for (String key : systemPropertiesArgs.keySet()) {
-            environmentChangeTracker.systemPropertyOverridden(key);
-        }
-
-        System.getProperties().putAll(startParameter.getSystemPropertiesArgs());
-    }
-
     private void addGradlePropertiesFrom(File dir, Map<String, Object> target) {
         Map<String, String> propertiesFile = environment.propertiesFile(new File(dir, GRADLE_PROPERTIES));
         if (propertiesFile != null) {
             target.putAll(propertiesFile);
         }
     }
-
-    private Map<String, String> projectPropertiesFromSystemProperties() {
-        Map<String, String> systemProjectProperties = byPrefix(SYSTEM_PROJECT_PROPERTIES_PREFIX, environment.getSystemProperties());
-        LOGGER.debug("Found system project properties: {}", systemProjectProperties.keySet());
-        return systemProjectProperties;
-    }
-
-    private Map<String, String> projectPropertiesFromEnvironmentVariables() {
-        Map<String, String> envProjectProperties = byPrefix(ENV_PROJECT_PROPERTIES_PREFIX, environment.getVariables());
-        LOGGER.debug("Found env project properties: {}", envProjectProperties.keySet());
-        return envProjectProperties;
-    }
-
-    private Map<String, String> byPrefix(String prefix, Environment.Properties properties) {
-        return mapKeysRemovingPrefix(prefix, properties.byNamePrefix(prefix));
-    }
-
-    private void setSystemPropertiesFromGradleProperties(Map<String, Object> properties) {
-        if (properties.isEmpty()) {
-            return;
-        }
-        String prefix = Project.SYSTEM_PROP_PREFIX + '.';
-        int prefixLength = prefix.length();
-        for (String key : properties.keySet()) {
-            if (key.length() > prefixLength && key.startsWith(prefix)) {
-                String systemPropertyKey = key.substring(prefixLength);
-                if (!gradleInternal.isRootBuild()) {
-                    environmentChangeTracker.systemPropertyLoaded(systemPropertyKey, properties.get(key), System.getProperty(systemPropertyKey));
-                }
-                System.setProperty(systemPropertyKey, uncheckedNonnullCast(properties.get(key)));
-            }
-        }
-    }
-
-    private static Map<String, String> mapKeysRemovingPrefix(String prefix, Map<String, String> mapWithPrefix) {
-        Map<String, String> mapWithoutPrefix = new HashMap<>(mapWithPrefix.size());
-        for (Map.Entry<String, String> entry : mapWithPrefix.entrySet()) {
-            mapWithoutPrefix.put(
-                entry.getKey().substring(prefix.length()),
-                entry.getValue()
-            );
-        }
-        return mapWithoutPrefix;
-    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/DefaultPlannedTask.java b/subprojects/core/src/main/java/org/gradle/initialization/DefaultPlannedTask.java
index 6a51668..9d3b176 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/DefaultPlannedTask.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/DefaultPlannedTask.java
@@ -16,20 +16,21 @@
 
 package org.gradle.initialization;
 
+import org.gradle.execution.plan.PlannedNodeInternal;
 import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.PlannedTask;
 import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.TaskIdentity;
 import org.gradle.internal.taskgraph.NodeIdentity;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Default implementation of {@link PlannedTask}.
  */
-public class DefaultPlannedTask implements PlannedTask {
+public class DefaultPlannedTask implements PlannedTask, PlannedNodeInternal {
 
     private final TaskIdentity taskIdentity;
     private final List<? extends NodeIdentity> nodeDependencies;
-    private final List<TaskIdentity> taskDependencies;
     private final List<TaskIdentity> mustRunAfter;
     private final List<TaskIdentity> shouldRunAfter;
     private final List<TaskIdentity> finalizers;
@@ -37,14 +38,12 @@ public class DefaultPlannedTask implements PlannedTask {
     public DefaultPlannedTask(
         TaskIdentity taskIdentity,
         List<? extends NodeIdentity> nodeDependencies,
-        List<TaskIdentity> taskDependencies,
         List<TaskIdentity> mustRunAfter,
         List<TaskIdentity> shouldRunAfter,
         List<TaskIdentity> finalizers
     ) {
         this.taskIdentity = taskIdentity;
         this.nodeDependencies = nodeDependencies;
-        this.taskDependencies = taskDependencies;
         this.mustRunAfter = mustRunAfter;
         this.shouldRunAfter = shouldRunAfter;
         this.finalizers = finalizers;
@@ -55,8 +54,17 @@ public TaskIdentity getTask() {
         return taskIdentity;
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public List<TaskIdentity> getDependencies() {
+        if (!nodeDependencies.stream().allMatch(TaskIdentity.class::isInstance)) {
+            List<? extends NodeIdentity> nonTaskDependencies = nodeDependencies.stream().filter(it -> !(it instanceof TaskIdentity)).collect(Collectors.toList());
+            throw new IllegalStateException("Task-only dependencies are available only for task plans." +
+                " '" + taskIdentity + "' from the requested execution plan has dependencies with higher detail level: " + nonTaskDependencies);
+        }
+
+        @SuppressWarnings("unchecked")
+        List<TaskIdentity> taskDependencies = (List<TaskIdentity>) nodeDependencies;
         return taskDependencies;
     }
 
@@ -89,4 +97,9 @@ public List<? extends NodeIdentity> getNodeDependencies() {
     public String toString() {
         return taskIdentity.toString();
     }
+
+    @Override
+    public DefaultPlannedTask withNodeDependencies(List<? extends NodeIdentity> nodeDependencies) {
+        return new DefaultPlannedTask(taskIdentity, nodeDependencies, mustRunAfter, shouldRunAfter, finalizers);
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/GradlePropertiesController.java b/subprojects/core/src/main/java/org/gradle/initialization/GradlePropertiesController.java
index a1cae9a..7f84abd 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/GradlePropertiesController.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/GradlePropertiesController.java
@@ -35,16 +35,19 @@ public interface GradlePropertiesController {
     GradleProperties getGradleProperties();
 
     /**
-     * Loads the immutable set of {@link GradleProperties} from the given directory and
+     * Loads the set of {@link GradleProperties} from the given directory and
      * makes it available to the build.
      *
+     * Optionally sets a system properties after load.
+     *
      * This method should be called only once per build but multiple calls with the
      * same argument are allowed.
      *
      * @param settingsDir directory where to look for the {@code gradle.properties} file
+     * @param setSystemProperties should system properties be set or not
      * @throws IllegalStateException if called with a different argument in the same build
      */
-    void loadGradlePropertiesFrom(File settingsDir);
+    void loadGradlePropertiesFrom(File settingsDir, boolean setSystemProperties);
 
     /**
      * Unloads the properties so the next call to {@link #loadGradlePropertiesFrom(File)} would reload them and
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/GradlePropertiesHandlingSettingsLoader.java b/subprojects/core/src/main/java/org/gradle/initialization/GradlePropertiesHandlingSettingsLoader.java
index 0b9a842..42b15bb 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/GradlePropertiesHandlingSettingsLoader.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/GradlePropertiesHandlingSettingsLoader.java
@@ -34,7 +34,7 @@ public GradlePropertiesHandlingSettingsLoader(SettingsLoader delegate, BuildLayo
     @Override
     public SettingsState findAndLoadSettings(GradleInternal gradle) {
         SettingsLocation settingsLocation = buildLayoutFactory.getLayoutFor(new BuildLayoutConfiguration(gradle.getStartParameter()));
-        gradlePropertiesController.loadGradlePropertiesFrom(settingsLocation.getSettingsDir());
+        gradlePropertiesController.loadGradlePropertiesFrom(settingsLocation.getSettingsDir(), true);
         return delegate.findAndLoadSettings(gradle);
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/IGradlePropertiesLoader.java b/subprojects/core/src/main/java/org/gradle/initialization/IGradlePropertiesLoader.java
index e983ae7..3945620 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/IGradlePropertiesLoader.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/IGradlePropertiesLoader.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.initialization;
 
-import org.gradle.api.internal.properties.GradleProperties;
+import org.gradle.initialization.properties.MutableGradleProperties;
 
 import java.io.File;
 
@@ -26,9 +26,9 @@ public interface IGradlePropertiesLoader {
     String ENV_PROJECT_PROPERTIES_PREFIX = "ORG_GRADLE_PROJECT_";
 
     /**
-     * Loads the immutable set of Gradle properties.
+     * Loads the set of Gradle properties.
      *
      * @since 6.2
      */
-    GradleProperties loadGradleProperties(File rootDir);
+    MutableGradleProperties loadGradleProperties(File rootDir);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/SettingsFactory.java b/subprojects/core/src/main/java/org/gradle/initialization/SettingsFactory.java
index 5dfeb51..c143e45 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/SettingsFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/SettingsFactory.java
@@ -34,8 +34,6 @@
 import java.io.File;
 import java.util.Map;
 
-import static java.util.Collections.emptyMap;
-
 public class SettingsFactory {
     private final Instantiator instantiator;
     private final ServiceRegistry buildScopeServices;
@@ -68,7 +66,7 @@ public SettingsState createSettings(
             settingsScript,
             startParameter
         );
-        Map<String, Object> properties = gradleProperties.mergeProperties(emptyMap());
+        Map<String, Object> properties = gradleProperties.getProperties();
         DynamicObject dynamicObject = ((DynamicObjectAware) settings).getAsDynamicObject();
         ((ExtensibleDynamicObject) dynamicObject).addProperties(properties);
         return new SettingsState(settings, serviceRegistryFactory.services);
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 a2f5ada..b8cc7fd 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/StartParameterBuildOptions.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/StartParameterBuildOptions.java
@@ -75,11 +75,11 @@ public class StartParameterBuildOptions extends BuildOptionSet<StartParameterInt
         options.add(new ExportKeysOption());
         options.add(new ConfigurationCacheProblemsOption());
         options.add(new ConfigurationCacheOption());
-        options.add(new IsolatedProjectsOption());
         options.add(new ConfigurationCacheMaxProblemsOption());
         options.add(new ConfigurationCacheDebugOption());
         options.add(new ConfigurationCacheRecreateOption());
         options.add(new ConfigurationCacheQuietOption());
+        options.add(new IsolatedProjectsOption());
         StartParameterBuildOptions.options = Collections.unmodifiableList(options);
     }
 
@@ -89,8 +89,10 @@ public List<? extends BuildOption<? super StartParameterInternal>> getAllOptions
     }
 
     public static class ProjectCacheDirOption extends StringBuildOption<StartParameterInternal> {
+        public static final String PROPERTY_NAME = "org.gradle.projectcachedir";
+        
         public ProjectCacheDirOption() {
-            super(null, CommandLineOptionConfiguration.create("project-cache-dir", "Specify the project-specific cache directory. Defaults to .gradle in the root project directory."));
+            super(PROPERTY_NAME, CommandLineOptionConfiguration.create("project-cache-dir", "Specify the project-specific cache directory. Defaults to .gradle in the root project directory."));
         }
 
         @Override
@@ -122,16 +124,25 @@ public void applyTo(StartParameterInternal settings, Origin origin) {
         }
     }
 
-    public static class ContinueOption extends EnabledOnlyBooleanBuildOption<StartParameterInternal> {
+    public static class ContinueOption extends BooleanBuildOption<StartParameterInternal> {
         public static final String LONG_OPTION = "continue";
 
+        public static final String PROPERTY_NAME = "org.gradle.continue";
+
         public ContinueOption() {
-            super(null, CommandLineOptionConfiguration.create(LONG_OPTION, "Continue task execution after a task failure."));
+            super(
+                PROPERTY_NAME,
+                BooleanCommandLineOptionConfiguration.create(
+                    LONG_OPTION,
+                    "Continue task execution after a task failure.",
+                    "Stop task execution after a task failure."
+                )
+            );
         }
 
         @Override
-        public void applyTo(StartParameterInternal settings, Origin origin) {
-            settings.setContinueOnFailure(true);
+        public void applyTo(boolean value, StartParameterInternal settings, Origin origin) {
+            settings.setContinueOnFailure(value);
         }
     }
 
@@ -437,7 +448,7 @@ public void applyTo(StartParameterInternal settings, Origin origin) {
 
     public static class ExportKeysOption extends EnabledOnlyBooleanBuildOption<StartParameterInternal> {
 
-        private static final String LONG_OPTION = "export-keys";
+        public static final String LONG_OPTION = "export-keys";
 
         public ExportKeysOption() {
             super(null,
@@ -452,17 +463,19 @@ public void applyTo(StartParameterInternal settings, Origin origin) {
 
     public static class ConfigurationCacheOption extends BooleanBuildOption<StartParameterInternal> {
 
-        public static final String PROPERTY_NAME = "org.gradle.unsafe.configuration-cache";
+        public static final String PROPERTY_NAME = "org.gradle.configuration-cache";
+        public static final String DEPRECATED_PROPERTY_NAME = "org.gradle.unsafe.configuration-cache";
         public static final String LONG_OPTION = "configuration-cache";
 
         public ConfigurationCacheOption() {
             super(
                 PROPERTY_NAME,
+                DEPRECATED_PROPERTY_NAME,
                 BooleanCommandLineOptionConfiguration.create(
                     LONG_OPTION,
                     "Enables the configuration cache. Gradle will try to reuse the build configuration from previous builds.",
                     "Disables the configuration cache."
-                ).incubating()
+                )
             );
         }
 
@@ -486,7 +499,8 @@ public void applyTo(boolean value, StartParameterInternal settings, Origin origi
     }
 
     public static class ConfigurationCacheProblemsOption extends EnumBuildOption<ConfigurationCacheProblemsOption.Value, StartParameterInternal> {
-        public static final String PROPERTY_NAME = "org.gradle.unsafe.configuration-cache-problems";
+        public static final String PROPERTY_NAME = "org.gradle.configuration-cache.problems";
+        public static final String DEPRECATED_PROPERTY_NAME = "org.gradle.unsafe.configuration-cache-problems";
         public static final String LONG_OPTION = "configuration-cache-problems";
 
         public enum Value {
@@ -499,10 +513,11 @@ public ConfigurationCacheProblemsOption() {
                 Value.class,
                 Value.values(),
                 PROPERTY_NAME,
+                DEPRECATED_PROPERTY_NAME,
                 CommandLineOptionConfiguration.create(
                     LONG_OPTION,
                     "Configures how the configuration cache handles problems (fail or warn). Defaults to fail."
-                ).incubating()
+                )
             );
         }
 
@@ -514,10 +529,11 @@ public void applyTo(Value value, StartParameterInternal settings, Origin origin)
 
     public static class ConfigurationCacheMaxProblemsOption extends IntegerBuildOption<StartParameterInternal> {
 
-        public static final String PROPERTY_NAME = "org.gradle.unsafe.configuration-cache.max-problems";
+        public static final String PROPERTY_NAME = "org.gradle.configuration-cache.max-problems";
+        public static final String DEPRECATED_PROPERTY_NAME = "org.gradle.unsafe.configuration-cache.max-problems";
 
         public ConfigurationCacheMaxProblemsOption() {
-            super(PROPERTY_NAME);
+            super(PROPERTY_NAME, DEPRECATED_PROPERTY_NAME);
         }
 
         @Override
@@ -528,10 +544,11 @@ public void applyTo(int value, StartParameterInternal settings, Origin origin) {
 
     public static class ConfigurationCacheDebugOption extends BooleanBuildOption<StartParameterInternal> {
 
-        public static final String PROPERTY_NAME = "org.gradle.unsafe.configuration-cache.debug";
+        public static final String PROPERTY_NAME = "org.gradle.configuration-cache.internal.debug";
+        public static final String DEPRECATED_PROPERTY_NAME = "org.gradle.unsafe.configuration-cache.debug";
 
         public ConfigurationCacheDebugOption() {
-            super(PROPERTY_NAME);
+            super(PROPERTY_NAME, DEPRECATED_PROPERTY_NAME);
         }
 
         @Override
@@ -542,24 +559,27 @@ public void applyTo(boolean value, StartParameterInternal settings, Origin origi
 
     public static class ConfigurationCacheRecreateOption extends BooleanBuildOption<StartParameterInternal> {
 
-        public static final String PROPERTY_NAME = "org.gradle.unsafe.configuration-cache.recreate-cache";
+        public static final String PROPERTY_NAME = "org.gradle.configuration-cache.internal.recreate-cache";
+        public static final String DEPRECATED_PROPERTY_NAME = "org.gradle.unsafe.configuration-cache.recreate-cache";
 
         public ConfigurationCacheRecreateOption() {
-            super(PROPERTY_NAME);
+            super(PROPERTY_NAME, DEPRECATED_PROPERTY_NAME);
         }
 
         @Override
         public void applyTo(boolean value, StartParameterInternal settings, Origin origin) {
             settings.setConfigurationCacheRecreateCache(value);
         }
+
     }
 
     public static class ConfigurationCacheQuietOption extends BooleanBuildOption<StartParameterInternal> {
 
-        public static final String PROPERTY_NAME = "org.gradle.unsafe.configuration-cache.quiet";
+        public static final String PROPERTY_NAME = "org.gradle.configuration-cache.internal.quiet";
+        public static final String DEPRECATED_PROPERTY_NAME = "org.gradle.unsafe.configuration-cache.quiet";
 
         public ConfigurationCacheQuietOption() {
-            super(PROPERTY_NAME);
+            super(PROPERTY_NAME, DEPRECATED_PROPERTY_NAME);
         }
 
         @Override
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java b/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java
index 9f67191..3da38a7 100644
--- a/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/initialization/buildsrc/BuildSrcBuildListenerFactory.java
@@ -80,8 +80,7 @@ public void projectsLoaded(Gradle gradle) {
         @Override
         public void applyTasksTo(Context context, ExecutionPlan plan) {
             rootProjectState.applyToMutableState(rootProject -> {
-                classpathConfiguration = rootProject.getConfigurations().create("buildScriptClasspath");
-                classpathConfiguration.setCanBeConsumed(false);
+                classpathConfiguration = rootProject.getConfigurations().resolvableBucket("buildScriptClasspath");
                 resolver.prepareClassPath(classpathConfiguration, rootProject.getDependencies());
                 classpathConfiguration.getDependencies().add(rootProject.getDependencies().create(rootProject));
                 plan.addEntryTasks(TaskDependencyUtil.getDependenciesForInternalUse(classpathConfiguration.getBuildDependencies(), null));
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/internal/InternalBuildFinishedListener.java b/subprojects/core/src/main/java/org/gradle/initialization/internal/InternalBuildFinishedListener.java
deleted file mode 100644
index 1e2c009..0000000
--- a/subprojects/core/src/main/java/org/gradle/initialization/internal/InternalBuildFinishedListener.java
+++ /dev/null
@@ -1,33 +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.initialization.internal;
-
-import org.gradle.api.internal.GradleInternal;
-import org.gradle.internal.service.scopes.EventScope;
-import org.gradle.internal.service.scopes.Scopes;
-
-@EventScope(Scopes.Build.class)
-public interface InternalBuildFinishedListener {
-    /**
-     * Called after all user buildFinished hooks have been executed, but before
-     * services are shutdown.
-     * @param gradle the Gradle instance being finalized
-     * @param failed
-     */
-    default void buildFinished(GradleInternal gradle, boolean failed) {
-
-    }
-}
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/properties/DefaultProjectPropertiesLoader.java b/subprojects/core/src/main/java/org/gradle/initialization/properties/DefaultProjectPropertiesLoader.java
new file mode 100644
index 0000000..008e075
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/initialization/properties/DefaultProjectPropertiesLoader.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 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.initialization.properties;
+
+import org.gradle.api.internal.StartParameterInternal;
+import org.gradle.initialization.Environment;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.gradle.initialization.IGradlePropertiesLoader.ENV_PROJECT_PROPERTIES_PREFIX;
+import static org.gradle.initialization.IGradlePropertiesLoader.SYSTEM_PROJECT_PROPERTIES_PREFIX;
+
+public class DefaultProjectPropertiesLoader implements ProjectPropertiesLoader {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultProjectPropertiesLoader.class);
+
+    private final Environment environment;
+
+    private final StartParameterInternal startParameter;
+
+    public DefaultProjectPropertiesLoader(StartParameterInternal startParameter, Environment environment) {
+        this.environment = environment;
+        this.startParameter = startParameter;
+    }
+
+    @Override
+    public Map<String, Object> loadProjectProperties() {
+        Map<String, Object> properties = new HashMap<>();
+
+        properties.putAll(projectPropertiesFromEnvironmentVariables());
+        properties.putAll(projectPropertiesFromSystemProperties());
+        properties.putAll(startParameter.getProjectProperties());
+
+        return properties;
+    }
+
+    private Map<String, String> projectPropertiesFromSystemProperties() {
+        Map<String, String> systemProjectProperties = byPrefix(SYSTEM_PROJECT_PROPERTIES_PREFIX, environment.getSystemProperties());
+        LOGGER.debug("Found system project properties: {}", systemProjectProperties.keySet());
+        return systemProjectProperties;
+    }
+
+    private Map<String, String> projectPropertiesFromEnvironmentVariables() {
+        Map<String, String> envProjectProperties = byPrefix(ENV_PROJECT_PROPERTIES_PREFIX, environment.getVariables());
+        LOGGER.debug("Found env project properties: {}", envProjectProperties.keySet());
+        return envProjectProperties;
+    }
+
+    private static Map<String, String> byPrefix(String prefix, Environment.Properties properties) {
+        return mapKeysRemovingPrefix(prefix, properties.byNamePrefix(prefix));
+    }
+
+    private static Map<String, String> mapKeysRemovingPrefix(String prefix, Map<String, String> mapWithPrefix) {
+        Map<String, String> mapWithoutPrefix = new HashMap<>(mapWithPrefix.size());
+        for (Map.Entry<String, String> entry : mapWithPrefix.entrySet()) {
+            mapWithoutPrefix.put(
+                entry.getKey().substring(prefix.length()),
+                entry.getValue()
+            );
+        }
+        return mapWithoutPrefix;
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/properties/DefaultSystemPropertiesInstaller.java b/subprojects/core/src/main/java/org/gradle/initialization/properties/DefaultSystemPropertiesInstaller.java
new file mode 100644
index 0000000..18d87d6
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/initialization/properties/DefaultSystemPropertiesInstaller.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 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.initialization.properties;
+
+import org.gradle.api.Project;
+import org.gradle.api.internal.GradleInternal;
+import org.gradle.api.internal.StartParameterInternal;
+import org.gradle.api.internal.properties.GradleProperties;
+import org.gradle.initialization.EnvironmentChangeTracker;
+
+import java.util.Map;
+
+import static org.gradle.internal.Cast.uncheckedNonnullCast;
+
+public class DefaultSystemPropertiesInstaller implements SystemPropertiesInstaller {
+
+    private final EnvironmentChangeTracker environmentChangeTracker;
+
+    private final StartParameterInternal startParameter;
+
+    private final GradleInternal gradleInternal;
+
+    public DefaultSystemPropertiesInstaller(
+        EnvironmentChangeTracker environmentChangeTracker,
+        StartParameterInternal startParameter,
+        GradleInternal gradleInternal
+    ) {
+        this.environmentChangeTracker = environmentChangeTracker;
+        this.startParameter = startParameter;
+        this.gradleInternal = gradleInternal;
+    }
+
+    @Override
+    public void setSystemPropertiesFrom(GradleProperties gradleProperties) {
+        // TODO:configuration-cache What happens when a system property is set from a Gradle property and
+        //    that same system property is then used to set a Gradle property from an included build?
+        //    e.g., included-build/gradle.properties << systemProp.org.gradle.project.fromSystemProp=42
+        setSystemPropertiesFromGradleProperties(gradleProperties.getProperties());
+        setSystemPropertiesFromStartParameter();
+    }
+
+    private void setSystemPropertiesFromStartParameter() {
+        Map<String, String> systemPropertiesArgs = startParameter.getSystemPropertiesArgs();
+
+        for (String key : systemPropertiesArgs.keySet()) {
+            environmentChangeTracker.systemPropertyOverridden(key);
+        }
+
+        System.getProperties().putAll(systemPropertiesArgs);
+    }
+
+    private void setSystemPropertiesFromGradleProperties(Map<String, Object> properties) {
+        if (properties.isEmpty()) {
+            return;
+        }
+        String prefix = Project.SYSTEM_PROP_PREFIX + '.';
+        int prefixLength = prefix.length();
+        for (String key : properties.keySet()) {
+            if (key.length() > prefixLength && key.startsWith(prefix)) {
+                String systemPropertyKey = key.substring(prefixLength);
+                if (!gradleInternal.isRootBuild()) {
+                    environmentChangeTracker.systemPropertyLoaded(systemPropertyKey, properties.get(key), System.getProperty(systemPropertyKey));
+                }
+                System.setProperty(systemPropertyKey, uncheckedNonnullCast(properties.get(key)));
+            }
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/properties/MutableGradleProperties.java b/subprojects/core/src/main/java/org/gradle/initialization/properties/MutableGradleProperties.java
new file mode 100644
index 0000000..aada77d
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/initialization/properties/MutableGradleProperties.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.initialization.properties;
+
+import org.gradle.api.internal.properties.GradleProperties;
+
+import java.util.Map;
+
+public interface MutableGradleProperties extends GradleProperties {
+
+    void updateOverrideProperties(Map<String, Object> properties);
+}
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/properties/ProjectPropertiesLoader.java b/subprojects/core/src/main/java/org/gradle/initialization/properties/ProjectPropertiesLoader.java
new file mode 100644
index 0000000..1b4ea55
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/initialization/properties/ProjectPropertiesLoader.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 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.initialization.properties;
+
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+import java.util.Map;
+
+@ServiceScope(Scopes.Build.class)
+public interface ProjectPropertiesLoader {
+
+    Map<String, Object> loadProjectProperties();
+}
diff --git a/subprojects/core/src/main/java/org/gradle/initialization/properties/SystemPropertiesInstaller.java b/subprojects/core/src/main/java/org/gradle/initialization/properties/SystemPropertiesInstaller.java
new file mode 100644
index 0000000..f0c3f4c
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/initialization/properties/SystemPropertiesInstaller.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.initialization.properties;
+
+import org.gradle.api.internal.properties.GradleProperties;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+@ServiceScope(Scopes.Build.class)
+public interface SystemPropertiesInstaller {
+
+    void setSystemPropertiesFrom(GradleProperties gradleProperties);
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/agents/AgentControl.java b/subprojects/core/src/main/java/org/gradle/internal/agents/AgentControl.java
new file mode 100644
index 0000000..763a540
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/agents/AgentControl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 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.agents;
+
+import org.gradle.internal.UncheckedException;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Provides methods to interact with the Java agent shipped with Gradle. Because of the different class loaders, it is hard to query the Agent class directly.
+ * <p>
+ * The agent class must follow a special protocol to be recognized properly: the class should have a method:
+ * <pre>
+ *     public static boolean isApplied()
+ * </pre>
+ * that returns if the agent is applied, i.e. if one of its {@code premain} or {@code agentmain} entry methods was called.
+ * <p>
+ * It is possible to have an agent in the classpath without actually applying it, so checking for the availability of the class is not enough.
+ */
+class AgentControl {
+    private static final String INSTRUMENTATION_AGENT_CLASS_NAME = "org.gradle.instrumentation.agent.Agent";
+
+    private AgentControl() {}
+
+    /**
+     * Checks if the instrumentation agent class is applied to the current JVM.
+     *
+     * @return {@code true} if the agent was applied
+     */
+    public static boolean isInstrumentationAgentApplied() {
+        Method isAppliedMethod = findAgentMethod(INSTRUMENTATION_AGENT_CLASS_NAME, "isApplied");
+        if (isAppliedMethod == null) {
+            return false;
+        }
+        return callStaticAgentMethod(isAppliedMethod);
+    }
+
+    public static boolean installTransformer(ClassFileTransformer transformer) {
+        Method installTransformer = findAgentMethod(INSTRUMENTATION_AGENT_CLASS_NAME, "installTransformer", ClassFileTransformer.class);
+        if (installTransformer == null) {
+            return false;
+        }
+        return callStaticAgentMethod(installTransformer, transformer);
+    }
+
+    @Nullable
+    private static Class<?> tryLoadAgentClass(String agentClassName) {
+        try {
+            return ClassLoader.getSystemClassLoader().loadClass(agentClassName);
+        } catch (ClassNotFoundException e) {
+            // This typically means that the agent is not loaded at all.
+            // For now, this happens when running in a no-daemon mode, or when the Gradle distribution is not available.
+            LoggerFactory.getLogger(AgentControl.class).debug("Agent {} is not loaded", agentClassName);
+        }
+        return null;
+    }
+
+    @Nullable
+    private static Method findAgentMethod(String agentClassName, String methodName, Class<?>... args) {
+        Class<?> agentClass = tryLoadAgentClass(agentClassName);
+        if (agentClass == null) {
+            return null;
+        }
+        try {
+            Method method = agentClass.getMethod(methodName, args);
+            method.setAccessible(true);
+            return method;
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T callStaticAgentMethod(Method method, Object... args) {
+        try {
+            return (T) method.invoke(null, args);
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+        } catch (InvocationTargetException e) {
+            throw UncheckedException.unwrapAndRethrow(e);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/agents/AgentInitializer.java b/subprojects/core/src/main/java/org/gradle/internal/agents/AgentInitializer.java
new file mode 100644
index 0000000..ec7b912
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/agents/AgentInitializer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.agents;
+
+import org.gradle.internal.service.scopes.Scope;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+/**
+ * Initializes the instrumenting agent.
+ */
+@ServiceScope(Scope.Global.class)
+public class AgentInitializer {
+    private final AgentStatus agentStatus;
+
+    public AgentInitializer(AgentStatus agentStatus) {
+        this.agentStatus = agentStatus;
+    }
+
+    /**
+     * Sets up the agent-based instrumentation if it is enabled for the process.
+     */
+    public void maybeConfigureInstrumentationAgent() {
+        if (agentStatus.isAgentInstrumentationEnabled()) {
+            DefaultClassFileTransformer.tryInstall();
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/agents/AgentStatus.java b/subprojects/core/src/main/java/org/gradle/internal/agents/AgentStatus.java
new file mode 100644
index 0000000..2ab6e05
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/agents/AgentStatus.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2022 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.agents;
+
+import org.gradle.internal.service.scopes.Scope;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+/**
+ * A build service to query the status of the Gradle's Java agents. Prefer using this service to accessing the {@link AgentControl} directly.
+ */
+@ServiceScope(Scope.Global.class)
+public interface AgentStatus {
+    /**
+     * Checks if the agent-based bytecode instrumentation is enabled for the current JVM process.
+     *
+     * @return {@code true} if the agent instrumentation should be used
+     */
+    boolean isAgentInstrumentationEnabled();
+
+    /**
+     * Returns an AgentStatus instance that enables instrumentation if the agent is available.
+     */
+    static AgentStatus allowed() {
+        return new DefaultAgentStatus();
+    }
+
+    /**
+     * Returns an AgentStatus instance that disables instrumentation regardless of the agent's availability.
+     */
+    static AgentStatus disabled() {
+        return new DisabledAgentStatus();
+    }
+
+    /**
+     * Returns {@link #allowed()} or {@link #disabled()} AgentStatus according to the value of the flag.
+     *
+     * @param agentInstrumentationAllowed if {@code true} then the returned status allows instrumentation
+     */
+    static AgentStatus of(boolean agentInstrumentationAllowed) {
+        return agentInstrumentationAllowed ? allowed() : disabled();
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/agents/AgentUtils.java b/subprojects/core/src/main/java/org/gradle/internal/agents/AgentUtils.java
new file mode 100644
index 0000000..2483287
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/agents/AgentUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 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.agents;
+
+/**
+ * Common utilities used for Gradle's own Java agents.
+ */
+public final class AgentUtils {
+    private AgentUtils() {}
+
+    public static final String AGENT_MODULE_NAME = "gradle-instrumentation-agent";
+
+    /**
+     * Checks if the command-line argument looks like JVM switch that applies gradle instrumentation agent.
+     * If the returned value is {@code true} then the argument is definitely a java agent application.
+     * However, only the name of the agent jar is checked, so it is possible to have false positives and false negatives.
+     *
+     * @param jvmArg the argument to check
+     * @return {@code true} if the argument looks like a switch, {@code false} otherwise
+     */
+    public static boolean isGradleInstrumentationAgentSwitch(String jvmArg) {
+        return isJavaAgentSwitch(jvmArg) && jvmArg.contains(AGENT_MODULE_NAME);
+    }
+
+    public static boolean isJavaAgentSwitch(String jvmArg) {
+        return jvmArg.startsWith("-javaagent:");
+    }
+
+    public static boolean isThirdPartyJavaAgentSwitch(String jvmArg) {
+        return isJavaAgentSwitch(jvmArg) && !jvmArg.contains(AGENT_MODULE_NAME);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/agents/DefaultAgentStatus.java b/subprojects/core/src/main/java/org/gradle/internal/agents/DefaultAgentStatus.java
new file mode 100644
index 0000000..1cc78a8
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/agents/DefaultAgentStatus.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.agents;
+
+class DefaultAgentStatus implements AgentStatus {
+    @Override
+    public boolean isAgentInstrumentationEnabled() {
+        return AgentControl.isInstrumentationAgentApplied();
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/agents/DefaultClassFileTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/agents/DefaultClassFileTransformer.java
new file mode 100644
index 0000000..f6cc3bd
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/agents/DefaultClassFileTransformer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 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.agents;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.security.ProtectionDomain;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+class DefaultClassFileTransformer implements ClassFileTransformer {
+    private static final AtomicBoolean INSTALLED = new AtomicBoolean();
+
+    @Override
+    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
+        if (!(loader instanceof InstrumentingClassLoader)) {
+            return null;
+        }
+        InstrumentingClassLoader instrumentingLoader = (InstrumentingClassLoader) loader;
+        try {
+            return instrumentingLoader.instrumentClass(className, protectionDomain, classfileBuffer);
+        } catch (Throwable th) {
+            // Throwing exception from the ClassFileTransformer has no effect - if it happens, the class is loaded unchanged silently.
+            // This is not something we want, so we notify the class loader about this.
+            instrumentingLoader.transformFailed(className, th);
+            return null;
+        }
+    }
+
+    public static boolean tryInstall() {
+        // Installing the same transformer multiple times is very problematic, so additional correctness check is worth it.
+        if (!INSTALLED.compareAndSet(false, true)) {
+            throw new IllegalStateException("The transformer is already installed in " + DefaultClassFileTransformer.class.getClassLoader());
+        }
+        return AgentControl.installTransformer(new DefaultClassFileTransformer());
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/agents/DisabledAgentStatus.java b/subprojects/core/src/main/java/org/gradle/internal/agents/DisabledAgentStatus.java
new file mode 100644
index 0000000..d7aca32
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/agents/DisabledAgentStatus.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.agents;
+
+class DisabledAgentStatus implements AgentStatus {
+    @Override
+    public boolean isAgentInstrumentationEnabled() {
+        return false;
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/AbstractBuildState.java b/subprojects/core/src/main/java/org/gradle/internal/build/AbstractBuildState.java
index d964024..6f2e2e6 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/AbstractBuildState.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/AbstractBuildState.java
@@ -18,7 +18,6 @@
 
 import org.gradle.api.internal.BuildDefinition;
 import org.gradle.api.internal.GradleInternal;
-import org.gradle.api.internal.project.HoldsProjectState;
 import org.gradle.api.internal.project.ProjectStateRegistry;
 import org.gradle.initialization.IncludedBuildSpec;
 import org.gradle.internal.Describables;
@@ -72,13 +71,20 @@ public String toString() {
     }
 
     @Override
-    public void resetLifecycle() {
+    public void resetModel() {
         projectStateRegistry.get().discardProjectsFor(this);
         workGraphController.get().resetState();
-        buildLifecycleController.get().resetLifecycle();
-        for (HoldsProjectState service : buildLifecycleController.get().getGradle().getServices().getAll(HoldsProjectState.class)) {
-            service.discardAll();
-        }
+        buildLifecycleController.get().resetModel();
+    }
+
+    @Override
+    public ExecutionResult<Void> beforeModelReset() {
+        return getBuildController().beforeModelReset();
+    }
+
+    @Override
+    public ExecutionResult<Void> beforeModelDiscarded(boolean failed) {
+        return getBuildController().beforeModelDiscarded(failed);
     }
 
     @Override
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildLayoutValidator.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildLayoutValidator.java
index 2508c2d..e00cf4d 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/BuildLayoutValidator.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/BuildLayoutValidator.java
@@ -65,20 +65,18 @@ public void validate(StartParameterInternal startParameter) {
             }
         }
 
-        StringBuilder message = new StringBuilder();
-        message.append("Directory '");
-        message.append(startParameter.getCurrentDir());
-        message.append("' does not contain a Gradle build.\n\n");
-        message.append("A Gradle build should contain a 'settings.gradle' or 'settings.gradle.kts' file in its root directory. ");
-        message.append("It may also contain a 'build.gradle' or 'build.gradle.kts' file.\n\n");
-        message.append("To create a new Gradle build in this directory run '");
+        StringBuilder message = new StringBuilder("Directory '");
+        message.append(startParameter.getCurrentDir())
+            .append("' does not contain a Gradle build.\n\n")
+            .append("A Gradle build should contain a 'settings.gradle' or 'settings.gradle.kts' file in its root directory. ")
+            .append("It may also contain a 'build.gradle' or 'build.gradle.kts' file.\n\n")
+            .append("To create a new Gradle build in this directory run '");
         clientMetaData.describeCommand(message, "init");
-        message.append("'\n\n");
-        message.append("For more detail on the 'init' task see ");
-        message.append(documentationRegistry.getDocumentationFor("build_init_plugin"));
-        message.append("\n\n");
-        message.append("For more detail on creating a Gradle build see ");
-        message.append(documentationRegistry.getDocumentationFor("tutorial_using_tasks")); // this is the "build script basics" chapter, we're missing some kind of "how to write a Gradle build chapter"
+        message.append("'\n\n")
+            .append(documentationRegistry.getDocumentationRecommendationFor("information about the 'init' task", "build_init_plugin"))
+            .append("\n\n")
+            // this is the "build script basics" chapter, we're missing some kind of "how to write a Gradle build chapter"
+            .append(documentationRegistry.getDocumentationRecommendationFor("details on creating a Gradle build", "tutorial_using_tasks"));
         throw new BuildLayoutException(message.toString());
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildLifecycleController.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildLifecycleController.java
index 6a90c43..daf4310 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/BuildLifecycleController.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/BuildLifecycleController.java
@@ -124,7 +124,17 @@ public interface BuildLifecycleController {
     /**
      * Restarts the lifecycle of this build.
      */
-    void resetLifecycle();
+    void resetModel();
+
+    /**
+     * Runs whatever work is required prior to discarding the model for this build. This is called prior to calling {@link #resetModel()}.
+     */
+    ExecutionResult<Void> beforeModelReset();
+
+    /**
+     * Runs whatever work is required prior to discarding the model for this build. This is called at the end of the build, after {@link #finishBuild(Throwable)}.
+     */
+    ExecutionResult<Void> beforeModelDiscarded(boolean failed);
 
     interface WorkGraphBuilder {
         /**
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelLifecycleListener.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelLifecycleListener.java
new file mode 100644
index 0000000..3ac224d
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/BuildModelLifecycleListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.EventScope;
+import org.gradle.internal.service.scopes.Scopes;
+
+@EventScope(Scopes.Build.class)
+public interface BuildModelLifecycleListener {
+    /**
+     * Called immediately prior to discarding the build model.
+     */
+    default void beforeModelDiscarded(GradleInternal model, boolean buildFailed) {
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildOperationFiringBuildWorkPreparer.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildOperationFiringBuildWorkPreparer.java
index a66f483..54f1e99 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/BuildOperationFiringBuildWorkPreparer.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/BuildOperationFiringBuildWorkPreparer.java
@@ -18,53 +18,43 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import org.gradle.api.NonNullApi;
 import org.gradle.api.Task;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.execution.plan.ExecutionPlan;
 import org.gradle.execution.plan.FinalizedExecutionPlan;
-import org.gradle.execution.plan.Node;
 import org.gradle.execution.plan.QueryableExecutionPlan;
-import org.gradle.execution.plan.ToPlannedNodeConverter;
 import org.gradle.internal.operations.BuildOperationContext;
 import org.gradle.internal.operations.BuildOperationDescriptor;
 import org.gradle.internal.operations.BuildOperationExecutor;
 import org.gradle.internal.operations.RunnableBuildOperation;
 import org.gradle.internal.operations.trace.CustomOperationTraceSerialization;
-import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.TaskIdentity;
+import org.gradle.execution.plan.ToPlannedNodeConverterRegistry;
 import org.gradle.internal.taskgraph.NodeIdentity;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
-import static java.util.Collections.newSetFromMap;
 import static org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.Details;
 import static org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.PlannedNode;
 import static org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.PlannedTask;
 import static org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.Result;
 
+@NonNullApi
 public class BuildOperationFiringBuildWorkPreparer implements BuildWorkPreparer {
     private final BuildOperationExecutor buildOperationExecutor;
     private final BuildWorkPreparer delegate;
-    private final ConverterRegistry converterRegistry;
+    private final ToPlannedNodeConverterRegistry converterRegistry;
 
-    public BuildOperationFiringBuildWorkPreparer(BuildOperationExecutor buildOperationExecutor, BuildWorkPreparer delegate, List<ToPlannedNodeConverter> converters) {
+    public BuildOperationFiringBuildWorkPreparer(BuildOperationExecutor buildOperationExecutor, BuildWorkPreparer delegate, ToPlannedNodeConverterRegistry converterRegistry) {
         this.buildOperationExecutor = buildOperationExecutor;
         this.delegate = delegate;
-        this.converterRegistry = new ConverterRegistry(converters);
+        this.converterRegistry = converterRegistry;
     }
 
     @Override
@@ -87,16 +77,14 @@ private static class PopulateWorkGraph implements RunnableBuildOperation {
         private final GradleInternal gradle;
         private final ExecutionPlan plan;
         private final Consumer<? super ExecutionPlan> action;
-        private final ConverterRegistry converterRegistry;
-        private final NodeDependencyLookup dependencyLookup;
+        private final ToPlannedNodeConverterRegistry converterRegistry;
 
-        public PopulateWorkGraph(BuildWorkPreparer delegate, GradleInternal gradle, ExecutionPlan plan, Consumer<? super ExecutionPlan> action, ConverterRegistry converterRegistry) {
+        public PopulateWorkGraph(BuildWorkPreparer delegate, GradleInternal gradle, ExecutionPlan plan, Consumer<? super ExecutionPlan> action, ToPlannedNodeConverterRegistry converterRegistry) {
             this.delegate = delegate;
             this.gradle = gradle;
             this.plan = plan;
             this.action = action;
             this.converterRegistry = converterRegistry;
-            this.dependencyLookup = new NodeDependencyLookup(converterRegistry);
         }
 
         @Override
@@ -109,9 +97,9 @@ public void run(BuildOperationContext buildOperationContext) {
             Set<Task> filteredTasks = contents.getFilteredTasks();
             QueryableExecutionPlan.ScheduledNodes scheduledWork = contents.getScheduledNodes();
 
-            List<PlannedNode> plannedNodes = toPlannedNodes(scheduledWork);
+            PlannedNodeGraph plannedNodeGraph = computePlannedNodeGraph(scheduledWork);
 
-            buildOperationContext.setResult(new CalculateTaskGraphResult(requestedTasks, filteredTasks, plannedNodes));
+            buildOperationContext.setResult(new CalculateTaskGraphResult(requestedTasks, filteredTasks, plannedNodeGraph));
         }
 
         void populateTaskGraph() {
@@ -131,59 +119,50 @@ public String getBuildPath() {
                 });
         }
 
-        private List<PlannedNode> toPlannedNodes(QueryableExecutionPlan.ScheduledNodes scheduledWork) {
-            List<PlannedNode> plannedNodes = new ArrayList<>();
-            scheduledWork.visitNodes(nodes -> {
-                for (Node node : nodes) {
-                    ToPlannedNodeConverter converter = converterRegistry.getConverter(node);
-                    if (converter != null && converter.isInSamePlan(node)) {
-                        PlannedNode plannedNode = converter.convert(node, dependencyLookup);
-                        plannedNodes.add(plannedNode);
-                    }
-                }
-            });
-            return plannedNodes;
+        private PlannedNodeGraph computePlannedNodeGraph(QueryableExecutionPlan.ScheduledNodes scheduledWork) {
+            PlannedNodeGraph.Collector collector = new PlannedNodeGraph.Collector(converterRegistry);
+            scheduledWork.visitNodes(collector::collectNodes);
+            return collector.getGraph();
         }
 
-
         private static class CalculateTaskGraphResult implements Result, CustomOperationTraceSerialization {
 
             private final Set<Task> requestedTasks;
             private final Set<Task> filteredTasks;
-            private final List<PlannedNode> plannedNodes;
+            private final PlannedNodeGraph plannedNodeGraph;
 
-            public CalculateTaskGraphResult(Set<Task> requestedTasks, Set<Task> filteredTasks, List<PlannedNode> plannedNodes) {
+            public CalculateTaskGraphResult(Set<Task> requestedTasks, Set<Task> filteredTasks, PlannedNodeGraph plannedNodeGraph) {
                 this.requestedTasks = requestedTasks;
                 this.filteredTasks = filteredTasks;
-                this.plannedNodes = plannedNodes;
+                this.plannedNodeGraph = plannedNodeGraph;
             }
 
             @Override
             public List<String> getRequestedTaskPaths() {
-                return toSortedTaskPaths(requestedTasks);
+                return toUniqueSortedTaskPaths(requestedTasks);
             }
 
             @Override
             public List<String> getExcludedTaskPaths() {
-                return toSortedTaskPaths(filteredTasks);
+                return toUniqueSortedTaskPaths(filteredTasks);
             }
 
             @Override
             public List<PlannedTask> getTaskPlan() {
-                return plannedNodes.stream()
-                    .filter(PlannedTask.class::isInstance)
-                    .map(PlannedTask.class::cast)
-                    .collect(Collectors.toList());
+                @SuppressWarnings("unchecked")
+                List<? extends PlannedTask> taskPlan = (List<? extends PlannedTask>) plannedNodeGraph.getNodes(PlannedNodeGraph.DetailLevel.LEVEL1_TASKS);
+                return ImmutableList.copyOf(taskPlan);
             }
 
             @Override
             public List<PlannedNode> getExecutionPlan(Set<NodeIdentity.NodeType> types) {
-                if (EnumSet.allOf(NodeIdentity.NodeType.class).equals(types)) {
-                    return plannedNodes;
+                if (types.isEmpty()) {
+                    return Collections.emptyList();
                 }
-                return plannedNodes.stream()
-                    .filter(node -> types.contains(node.getNodeIdentity().getNodeType()))
-                    .collect(Collectors.toList());
+
+                @SuppressWarnings("unchecked")
+                List<PlannedNode> plan = (List<PlannedNode>) plannedNodeGraph.getNodes(PlannedNodeGraph.DetailLevel.from(types));
+                return plan;
             }
 
             @Override
@@ -195,134 +174,11 @@ public Object getCustomOperationTraceSerializableModel() {
                 builder.put("executionPlan", getExecutionPlan(EnumSet.allOf(NodeIdentity.NodeType.class)));
                 return builder.build();
             }
-        }
-    }
 
-    private static List<String> toSortedTaskPaths(Set<Task> tasks) {
-        return tasks.stream().map(Task::getPath).distinct().sorted().collect(Collectors.toList());
-    }
-
-    private static class NodeDependencyLookup implements ToPlannedNodeConverter.DependencyLookup {
-
-        private final ConverterRegistry converterRegistry;
-
-        private NodeDependencyLookup(ConverterRegistry converterRegistry) {
-            this.converterRegistry = converterRegistry;
-        }
-
-        @Override
-        public List<? extends NodeIdentity> findNodeDependencies(Node node) {
-            return findDependencies(node, Node::getDependencySuccessors, it -> true).collect(Collectors.toList());
-        }
-
-        @Override
-        public List<? extends TaskIdentity> findTaskDependencies(Node node) {
-            return findDependencies(node, Node::getDependencySuccessors, it -> it instanceof TaskIdentity)
-                .map(TaskIdentity.class::cast)
-                .collect(Collectors.toList());
-        }
-
-        private Stream<? extends NodeIdentity> findDependencies(
-            Node node,
-            Function<? super Node, ? extends Collection<Node>> traverser,
-            Predicate<NodeIdentity> isDependencyNode
-        ) {
-            return findIdentifiedNodes(node, traverser, isDependencyNode, newSetFromMap(new IdentityHashMap<>()));
-        }
-
-        private Stream<NodeIdentity> findIdentifiedNodes(
-            Node curNode,
-            Function<? super Node, ? extends Collection<Node>> traverser,
-            Predicate<NodeIdentity> isDependencyNode,
-            Set<Node> seen
-        ) {
-            Collection<Node> nodes = traverser.apply(curNode);
-            if (nodes.isEmpty()) {
-                return Stream.empty();
-            }
-
-            return nodes.stream()
-                .filter(seen::add)
-                .flatMap(node -> {
-                    NodeIdentity nodeIdentity = identifyAsDependencyNode(node, isDependencyNode);
-                    return nodeIdentity != null
-                        ? Stream.of(nodeIdentity)
-                        : findIdentifiedNodes(node, traverser, isDependencyNode, seen);
-                });
-        }
-
-        private NodeIdentity identifyAsDependencyNode(Node node, Predicate<NodeIdentity> isDependencyNode) {
-            ToPlannedNodeConverter converter = converterRegistry.getConverter(node);
-            if (converter == null) {
-                return null;
-            }
-            NodeIdentity nodeIdentity = converter.getNodeIdentity(node);
-            return isDependencyNode.test(nodeIdentity) ? nodeIdentity : null;
-        }
-    }
-
-    private static class ConverterRegistry {
-
-        private final List<ToPlannedNodeConverter> converters;
-
-        private final Map<Class<?>, ToPlannedNodeConverter> convertersByNodeType = new HashMap<>();
-        private final Set<Class<?>> unsupportedNodeTypes = new HashSet<>();
-
-        private ConverterRegistry(List<ToPlannedNodeConverter> converters) {
-            validateConverters(converters);
-            this.converters = ImmutableList.copyOf(converters);
-
-            for (ToPlannedNodeConverter converter : this.converters) {
-                convertersByNodeType.put(converter.getSupportedNodeType(), converter);
-            }
-        }
-
-        /**
-         * Returns a converter for the given node, or null if there is no converter for the node.
-         */
-        @Nullable
-        public ToPlannedNodeConverter getConverter(Node node) {
-            Class<? extends Node> nodeType = node.getClass();
-            ToPlannedNodeConverter converter = convertersByNodeType.get(nodeType);
-            if (converter != null) {
-                return converter;
-            }
-
-            if (unsupportedNodeTypes.contains(nodeType)) {
-                return null;
-            }
-
-            for (ToPlannedNodeConverter converterCandidate : converters) {
-                Class<? extends Node> supportedNodeType = converterCandidate.getSupportedNodeType();
-                if (supportedNodeType.isAssignableFrom(nodeType)) {
-                    converter = converterCandidate;
-                    break;
-                }
-            }
-
-            if (converter != null) {
-                convertersByNodeType.put(nodeType, converter);
-            } else {
-                unsupportedNodeTypes.add(nodeType);
-            }
-
-            return converter;
-        }
-
-        private static void validateConverters(List<ToPlannedNodeConverter> converters) {
-            for (ToPlannedNodeConverter converter1 : converters) {
-                Class<? extends Node> supportedNodeType1 = converter1.getSupportedNodeType();
-                for (ToPlannedNodeConverter converter2 : converters) {
-                    if (converter1 == converter2) {
-                        continue;
-                    }
-
-                    Class<? extends Node> supportedNodeType2 = converter2.getSupportedNodeType();
-                    if (supportedNodeType1.isAssignableFrom(supportedNodeType2) || supportedNodeType2.isAssignableFrom(supportedNodeType1)) {
-                        throw new IllegalStateException("Converter " + converter1 + " is not compatible with converter " + converter2);
-                    }
-                }
+            private static List<String> toUniqueSortedTaskPaths(Set<Task> tasks) {
+                return tasks.stream().map(Task::getPath).distinct().sorted().collect(Collectors.toList());
             }
         }
     }
+
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/BuildState.java b/subprojects/core/src/main/java/org/gradle/internal/build/BuildState.java
index 0916df7..7336608 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/BuildState.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/BuildState.java
@@ -54,11 +54,6 @@ public interface BuildState {
     boolean isImportableBuild();
 
     /**
-     * Note: may change value over the lifetime of this build, as this is often a function of the name of the root project in the build and this is not known until the settings have been configured. A temporary value will be returned when child builds need to create projects for some reason.
-     */
-    Path getCurrentPrefixForProjectsInChildBuilds();
-
-    /**
      * Calculates the identity path for a project in this build.
      */
     Path calculateIdentityPathForProject(Path projectPath) throws IllegalStateException;
@@ -95,7 +90,7 @@ public interface BuildState {
     File getBuildRootDir();
 
     /**
-     * Returns the current state of the mutable model of this build. Try to use {@link #withState(Transformer)} instead.
+     * Returns the current state of the mutable model of this build. Try to avoid using the model directly.
      */
     GradleInternal getMutableModel();
 
@@ -110,7 +105,17 @@ public interface BuildState {
     <T> T withToolingModels(Function<? super BuildToolingModelController, T> action);
 
     /**
-     * Restarts the lifecycle for this build, discarding all present model state.
+     * Runs whatever work is required prior to discarding the model for this build. Called prior to {@link #resetModel()}.
      */
-    void resetLifecycle();
+    ExecutionResult<Void> beforeModelReset();
+
+    /**
+     * Restarts the lifecycle of the model of this build, discarding all current model state.
+     */
+    void resetModel();
+
+    /**
+     * Runs whatever work is required prior to discarding the model for this build. Called at the end of the build.
+     */
+    ExecutionResult<Void> beforeModelDiscarded(boolean failed);
 }
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 32aa292..8a4e066 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
@@ -15,11 +15,13 @@
  */
 package org.gradle.internal.build;
 
+import com.google.common.collect.ImmutableList;
 import org.gradle.BuildListener;
 import org.gradle.BuildResult;
 import org.gradle.api.Task;
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.SettingsInternal;
+import org.gradle.api.internal.project.HoldsProjectState;
 import org.gradle.api.specs.Spec;
 import org.gradle.execution.BuildWorkExecutor;
 import org.gradle.execution.EntryTaskSelector;
@@ -29,7 +31,6 @@
 import org.gradle.execution.plan.LocalTaskNode;
 import org.gradle.execution.plan.Node;
 import org.gradle.initialization.exception.ExceptionAnalyser;
-import org.gradle.initialization.internal.InternalBuildFinishedListener;
 import org.gradle.internal.Describables;
 import org.gradle.internal.model.StateTransitionController;
 import org.gradle.internal.model.StateTransitionControllerFactory;
@@ -48,13 +49,17 @@ private enum State implements StateTransitionController.State {
         // Scheduling tasks for execution
         TaskSchedule,
         ReadyToRun,
+        BuildFinishHooks,
+        ReadyToReset,
         // build has finished and should do no further work
         Finished
     }
 
+    public static final ImmutableList<State> CONFIGURATION_STATES = ImmutableList.of(State.Configure, State.TaskSchedule, State.ReadyToRun);
+
     private final ExceptionAnalyser exceptionAnalyser;
     private final BuildListener buildListener;
-    private final InternalBuildFinishedListener buildFinishedListener;
+    private final BuildModelLifecycleListener buildModelLifecycleListener;
     private final BuildWorkPreparer workPreparer;
     private final BuildWorkExecutor workExecutor;
     private final BuildToolingModelControllerFactory toolingModelControllerFactory;
@@ -62,13 +67,14 @@ private enum State implements StateTransitionController.State {
     private final StateTransitionController<State> state;
     private final GradleInternal gradle;
     private boolean hasTasks;
+    private boolean hasFiredBeforeModelDiscarded;
 
     public DefaultBuildLifecycleController(
         GradleInternal gradle,
         BuildModelController buildModelController,
         ExceptionAnalyser exceptionAnalyser,
         BuildListener buildListener,
-        InternalBuildFinishedListener buildFinishedListener,
+        BuildModelLifecycleListener buildModelLifecycleListener,
         BuildWorkPreparer workPreparer,
         BuildWorkExecutor workExecutor,
         BuildToolingModelControllerFactory toolingModelControllerFactory,
@@ -80,7 +86,7 @@ public DefaultBuildLifecycleController(
         this.buildListener = buildListener;
         this.workPreparer = workPreparer;
         this.workExecutor = workExecutor;
-        this.buildFinishedListener = buildFinishedListener;
+        this.buildModelLifecycleListener = buildModelLifecycleListener;
         this.toolingModelControllerFactory = toolingModelControllerFactory;
         this.state = controllerFactory.newController(Describables.of("state of", gradle.getOwner().getDisplayName()), State.Configure);
     }
@@ -117,8 +123,32 @@ public <T> T withProjectsConfigured(Function<? super GradleInternal, T> action)
     }
 
     @Override
-    public void resetLifecycle() {
-        state.restart(State.Configure, () -> gradle.resetState());
+    public void resetModel() {
+        state.restart(State.ReadyToReset, State.Configure, () -> {
+            gradle.resetState();
+            for (HoldsProjectState service : gradle.getServices().getAll(HoldsProjectState.class)) {
+                service.discardAll();
+            }
+        });
+    }
+
+    @Override
+    public ExecutionResult<Void> beforeModelReset() {
+        return state.transition(CONFIGURATION_STATES, State.ReadyToReset, failures -> fireBeforeModelDiscarded(false));
+    }
+
+    @Override
+    public ExecutionResult<Void> beforeModelDiscarded(boolean failed) {
+        return state.transition(State.BuildFinishHooks, State.Finished, failures -> fireBeforeModelDiscarded(failed));
+    }
+
+    private ExecutionResult<Void> fireBeforeModelDiscarded(boolean failed) {
+        if (hasFiredBeforeModelDiscarded) {
+            return ExecutionResult.succeeded();
+        } else {
+            hasFiredBeforeModelDiscarded = true;
+            return ExecutionResult.maybeFailing(() -> buildModelLifecycleListener.beforeModelDiscarded(gradle, failed));
+        }
     }
 
     @Override
@@ -187,7 +217,7 @@ public <T> T withToolingModels(Function<? super BuildToolingModelController, T>
 
     @Override
     public ExecutionResult<Void> finishBuild(@Nullable Throwable failure) {
-        return state.finish(State.Finished, stageFailures -> {
+        return state.transition(CONFIGURATION_STATES, State.BuildFinishHooks, stageFailures -> {
             // Fire the build finished events even if nothing has happened to this build, because quite a lot of internal infrastructure
             // adds listeners and expects to see a build finished event. Infrastructure should not be using the public listener types
             // In addition, they almost all should be using a build tree scoped event instead of a build scoped event
@@ -197,15 +227,7 @@ public ExecutionResult<Void> finishBuild(@Nullable Throwable failure) {
                 reportableFailure = exceptionAnalyser.transform(stageFailures.getFailures());
             }
             BuildResult buildResult = new BuildResult(hasTasks ? "Build" : "Configure", gradle, reportableFailure);
-            ExecutionResult<Void> finishResult;
-            try {
-                buildListener.buildFinished(buildResult);
-                buildFinishedListener.buildFinished((GradleInternal) buildResult.getGradle(), buildResult.getFailure() != null);
-                finishResult = ExecutionResult.succeeded();
-            } catch (Throwable t) {
-                finishResult = ExecutionResult.failed(t);
-            }
-            return finishResult;
+            return ExecutionResult.maybeFailing(() -> buildListener.buildFinished(buildResult));
         });
     }
 
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 9c6961e..7cd9352 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
@@ -21,7 +21,6 @@
 import org.gradle.api.internal.GradleInternal;
 import org.gradle.execution.BuildWorkExecutor;
 import org.gradle.initialization.exception.ExceptionAnalyser;
-import org.gradle.initialization.internal.InternalBuildFinishedListener;
 import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.deprecation.DeprecationMessageBuilder;
 import org.gradle.internal.event.ListenerManager;
@@ -70,7 +69,7 @@ public BuildLifecycleController newInstance(BuildDefinition buildDefinition, Bui
             buildModelController,
             exceptionAnalyser,
             gradle.getBuildListenerBroadcaster(),
-            listenerManager.getBroadcaster(InternalBuildFinishedListener.class),
+            listenerManager.getBroadcaster(BuildModelLifecycleListener.class),
             gradle.getServices().get(BuildWorkPreparer.class),
             gradle.getServices().get(BuildWorkExecutor.class),
             buildToolingModelControllerFactory,
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/ExecutionResult.java b/subprojects/core/src/main/java/org/gradle/internal/build/ExecutionResult.java
index 6124a27..20aa479 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/ExecutionResult.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/ExecutionResult.java
@@ -52,6 +52,11 @@ public Void getValue() {
     public abstract RuntimeException getFailure();
 
     /**
+     * Returns a single exception object that contains all failures in this result, or null if the operation was successful.
+     */
+    public abstract RuntimeException getFailureOrNull();
+
+    /**
      * Returns the value or rethrows the failures of this result.
      */
     public abstract T getValueOrRethrow();
@@ -84,6 +89,15 @@ public static ExecutionResult<Void> succeeded() {
         return SUCCESS;
     }
 
+    public static ExecutionResult<Void> maybeFailing(Runnable action) {
+        try {
+            action.run();
+            return SUCCESS;
+        } catch (Throwable t) {
+            return failed(t);
+        }
+    }
+
     public static <T> ExecutionResult<T> failed(Throwable failure) {
         return new Failure<>(ImmutableList.of(failure));
     }
@@ -150,6 +164,11 @@ public void rethrow() {
         }
 
         @Override
+        public RuntimeException getFailureOrNull() {
+            return null;
+        }
+
+        @Override
         public RuntimeException getFailure() {
             throw new IllegalArgumentException("Cannot get the failure of a successful result.");
         }
@@ -184,6 +203,11 @@ public List<Throwable> getFailures() {
         }
 
         @Override
+        public RuntimeException getFailureOrNull() {
+            return getFailure();
+        }
+
+        @Override
         public RuntimeException getFailure() {
             if (failures.size() == 1 && failures.get(0) instanceof RuntimeException) {
                 return (RuntimeException) failures.get(0);
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/IncludedBuildFactory.java b/subprojects/core/src/main/java/org/gradle/internal/build/IncludedBuildFactory.java
index 13aa36e..1c92249 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/build/IncludedBuildFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/IncludedBuildFactory.java
@@ -20,9 +20,8 @@
 import org.gradle.api.internal.BuildDefinition;
 import org.gradle.internal.service.scopes.Scopes;
 import org.gradle.internal.service.scopes.ServiceScope;
-import org.gradle.util.Path;
 
 @ServiceScope(Scopes.BuildTree.class)
 public interface IncludedBuildFactory {
-    IncludedBuildState createBuild(BuildIdentifier buildIdentifier, Path identityPath, BuildDefinition buildDefinition, boolean isImplicit, BuildState owner);
+    IncludedBuildState createBuild(BuildIdentifier buildIdentifier, BuildDefinition buildDefinition, boolean isImplicit, BuildState owner);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/build/PlannedNodeGraph.java b/subprojects/core/src/main/java/org/gradle/internal/build/PlannedNodeGraph.java
new file mode 100644
index 0000000..0244e37
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/build/PlannedNodeGraph.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2023 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 com.google.common.collect.ImmutableList;
+import org.gradle.execution.plan.Node;
+import org.gradle.execution.plan.PlannedNodeInternal;
+import org.gradle.execution.plan.ToPlannedNodeConverter;
+import org.gradle.execution.plan.ToPlannedNodeConverterRegistry;
+import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.PlannedNode;
+import org.gradle.internal.taskgraph.NodeIdentity;
+import org.gradle.internal.taskgraph.NodeIdentity.NodeType;
+
+import javax.annotation.Nullable;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.function.Predicate;
+
+/**
+ * A graph of planned nodes that supports extracting sub-graphs of different {@link DetailLevel levels of detail}
+ * depending on what node types are supported by the consumer.
+ *
+ * @see Collector
+ */
+public class PlannedNodeGraph {
+
+    private final DetailLevel detailLevel;
+    private final List<PlannedNodeInternal> plannedNodes;
+
+    public PlannedNodeGraph(DetailLevel detailLevel, List<PlannedNodeInternal> plannedNodes) {
+        this.detailLevel = detailLevel;
+        this.plannedNodes = ImmutableList.copyOf(plannedNodes);
+    }
+
+    /**
+     * Returns the nodes from a subgraph that are of the given or lower detail level
+     * (lower if the requested detail level is higher than the available detail level).
+     */
+    public List<? extends PlannedNode> getNodes(DetailLevel detailLevel) {
+        if (detailLevel.level >= this.detailLevel.level) {
+            return plannedNodes;
+        }
+
+        return computePlan(detailLevel);
+    }
+
+    private List<PlannedNodeInternal> computePlan(DetailLevel detailLevel) {
+        Map<NodeIdentity, List<? extends NodeIdentity>> plannedNodeDependenciesByIdentity = new HashMap<>();
+        for (PlannedNodeInternal plannedNode : plannedNodes) {
+            plannedNodeDependenciesByIdentity.put(plannedNode.getNodeIdentity(), plannedNode.getNodeDependencies());
+        }
+
+        Predicate<NodeIdentity> inDetailLevel = id -> detailLevel.contains(id.getNodeType());
+        IdentityProvider<NodeIdentity> identityProvider = id -> inDetailLevel.test(id) ? id : null;
+        DependencyTraverser<NodeIdentity> traverser = id -> {
+            List<? extends NodeIdentity> deps = plannedNodeDependenciesByIdentity.get(id);
+            if (deps == null) {
+                throw new IllegalStateException("No dependencies for node: " + id);
+            }
+            return deps;
+        };
+
+        List<PlannedNodeInternal> newPlannedNodes = new ArrayList<>();
+        for (PlannedNodeInternal plannedNode : plannedNodes) {
+            NodeIdentity nodeIdentity = plannedNode.getNodeIdentity();
+            if (!inDetailLevel.test(nodeIdentity)) {
+                continue;
+            }
+
+            List<? extends NodeIdentity> nodeDependencies = plannedNode.getNodeDependencies();
+            if (nodeDependencies.isEmpty() || nodeDependencies.stream().allMatch(inDetailLevel)) {
+                newPlannedNodes.add(plannedNode);
+            } else {
+                List<NodeIdentity> newNodeDependencies = computeDependencies(traverser, identityProvider, nodeIdentity);
+                newPlannedNodes.add(plannedNode.withNodeDependencies(newNodeDependencies));
+            }
+        }
+
+        return newPlannedNodes;
+    }
+
+    public enum DetailLevel {
+        LEVEL1_TASKS(1, NodeType.TASK),
+        LEVEL2_TRANSFORM_STEPS(2, NodeType.TASK, NodeType.TRANSFORM_STEP);
+
+        private final int level;
+        private final Set<NodeType> nodeTypes;
+
+        DetailLevel(int level, NodeType... nodeTypes) {
+            this.level = level;
+            this.nodeTypes = EnumSet.copyOf(Arrays.asList(nodeTypes));
+        }
+
+        public int getLevel() {
+            return level;
+        }
+
+        public boolean contains(NodeType nodeType) {
+            return nodeTypes.contains(nodeType);
+        }
+
+        public static DetailLevel from(Set<NodeType> nodeTypes) {
+            for (DetailLevel detailLevel : values()) {
+                if (detailLevel.nodeTypes.equals(nodeTypes)) {
+                    return detailLevel;
+                }
+            }
+
+            throw new IllegalStateException("Unknown detail level for node types: " + nodeTypes);
+        }
+    }
+
+    /**
+     * {@link #collectNodes(Collection) Collects} and converts nodes to planned nodes
+     * resolving their dependencies with the highest available {@link DetailLevel detail level}.
+     */
+    public static class Collector {
+
+        private final ToPlannedNodeConverterRegistry converterRegistry;
+        private final DetailLevel detailLevel;
+
+        private final List<PlannedNodeInternal> plannedNodes = new ArrayList<>();
+        private final Map<Node, NodeIdentity> nodeIdentityCache = new IdentityHashMap<>();
+
+        public Collector(ToPlannedNodeConverterRegistry converterRegistry) {
+            this.converterRegistry = converterRegistry;
+            this.detailLevel = DetailLevel.from(converterRegistry.getConvertedNodeTypes());
+        }
+
+        public DetailLevel getDetailLevel() {
+            return detailLevel;
+        }
+
+        public void collectNodes(Collection<Node> nodes) {
+            for (Node node : nodes) {
+                ToPlannedNodeConverter converter = converterRegistry.getConverter(node);
+                if (converter != null && converter.isInSamePlan(node)) {
+                    List<? extends NodeIdentity> nodeDependencies = findNodeDependencies(node);
+                    PlannedNodeInternal plannedNode = converter.convert(node, nodeDependencies);
+                    plannedNodes.add(plannedNode);
+                }
+            }
+        }
+
+        public PlannedNodeGraph getGraph() {
+            return new PlannedNodeGraph(detailLevel, plannedNodes);
+        }
+
+        private List<? extends NodeIdentity> findNodeDependencies(Node node) {
+            return computeDependencies(Node::getDependencySuccessors, this::getNodeIdentityOrNull, node);
+        }
+
+        private NodeIdentity getNodeIdentityOrNull(Node node) {
+            ToPlannedNodeConverter converter = converterRegistry.getConverter(node);
+            if (converter == null) {
+                return null;
+            }
+
+            return nodeIdentityCache.computeIfAbsent(node, converter::getNodeIdentity);
+        }
+    }
+
+    /**
+     * Computes dependencies of a node in breadth-first order, stopping at each dependency for which identity is provided.
+     */
+    private static <T> List<NodeIdentity> computeDependencies(
+        DependencyTraverser<T> traverser,
+        IdentityProvider<T> identityProvider,
+        T start
+    ) {
+        List<NodeIdentity> resultDependencies = new ArrayList<>();
+
+        Queue<T> queue = new ArrayDeque<>(traverser.getDependencies(start));
+        Set<T> seen = new HashSet<>();
+
+        while (!queue.isEmpty()) {
+            T node = queue.remove();
+            if (!seen.add(node)) {
+                continue;
+            }
+
+            NodeIdentity identity = identityProvider.get(node);
+            if (identity == null) {
+                // skip the node and look at its dependencies
+                queue.addAll(traverser.getDependencies(node));
+            } else {
+                resultDependencies.add(identity);
+            }
+        }
+
+        return resultDependencies;
+    }
+
+    private interface DependencyTraverser<T> {
+        Collection<? extends T> getDependencies(T node);
+    }
+
+    private interface IdentityProvider<T> {
+        @Nullable
+        NodeIdentity get(T node);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/buildevents/BuildExceptionReporter.java b/subprojects/core/src/main/java/org/gradle/internal/buildevents/BuildExceptionReporter.java
index 96257f8..32029d6 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/buildevents/BuildExceptionReporter.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/buildevents/BuildExceptionReporter.java
@@ -15,20 +15,25 @@
  */
 package org.gradle.internal.buildevents;
 
+import com.google.common.collect.ImmutableList;
 import org.gradle.BuildResult;
 import org.gradle.api.Action;
+import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.configuration.LoggingConfiguration;
 import org.gradle.api.logging.configuration.ShowStacktrace;
 import org.gradle.execution.MultipleBuildFailures;
 import org.gradle.initialization.BuildClientMetaData;
-import org.gradle.initialization.StartParameterBuildOptions;
 import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager;
+import org.gradle.internal.exceptions.CompilationFailedIndicator;
 import org.gradle.internal.exceptions.ContextAwareException;
 import org.gradle.internal.exceptions.ExceptionContextVisitor;
 import org.gradle.internal.exceptions.FailureResolutionAware;
+import org.gradle.internal.exceptions.MultiCauseException;
+import org.gradle.internal.exceptions.NonGradleCause;
+import org.gradle.internal.exceptions.NonGradleCauseExceptionsHolder;
+import org.gradle.internal.exceptions.ResolutionProvider;
 import org.gradle.internal.exceptions.StyledException;
-import org.gradle.internal.logging.LoggingConfigurationBuildOptions;
 import org.gradle.internal.logging.text.BufferingStyledTextOutput;
 import org.gradle.internal.logging.text.LinePrefixingStyledTextOutput;
 import org.gradle.internal.logging.text.StyledTextOutput;
@@ -38,6 +43,16 @@
 import java.util.List;
 import java.util.function.Consumer;
 
+import static com.google.common.collect.ImmutableList.builder;
+import static com.google.common.collect.ImmutableList.of;
+import static java.lang.String.join;
+import static org.apache.commons.lang.StringUtils.repeat;
+import static org.gradle.api.logging.LogLevel.DEBUG;
+import static org.gradle.api.logging.LogLevel.INFO;
+import static org.gradle.initialization.StartParameterBuildOptions.BuildScanOption.LONG_OPTION;
+import static org.gradle.internal.logging.LoggingConfigurationBuildOptions.LogLevelOption.DEBUG_LONG_OPTION;
+import static org.gradle.internal.logging.LoggingConfigurationBuildOptions.LogLevelOption.INFO_LONG_OPTION;
+import static org.gradle.internal.logging.LoggingConfigurationBuildOptions.StacktraceOption.STACKTRACE_LONG_OPTION;
 import static org.gradle.internal.logging.text.StyledTextOutput.Style.Failure;
 import static org.gradle.internal.logging.text.StyledTextOutput.Style.Info;
 import static org.gradle.internal.logging.text.StyledTextOutput.Style.Normal;
@@ -47,6 +62,10 @@
  * Reports the build exception, if any.
  */
 public class BuildExceptionReporter implements Action<Throwable> {
+
+    public static final String RESOLUTION_LINE_PREFIX = "> ";
+    public static final String LINE_PREFIX_LENGTH_SPACES = repeat(" ", RESOLUTION_LINE_PREFIX.length());
+
     private enum ExceptionStyle {
         NONE, FULL
     }
@@ -107,7 +126,6 @@ private void renderMultipleBuildExceptions(MultipleBuildFailures failure) {
 
             output.println("==============================================================================");
         }
-        writeGeneralTips(output);
     }
 
     private void renderSingleBuildException(Throwable failure) {
@@ -120,8 +138,24 @@ private void renderSingleBuildException(Throwable failure) {
         output.println();
 
         writeFailureDetails(output, details);
+    }
 
-        writeGeneralTips(output);
+    private static boolean hasCauseAncestry(Throwable failure, Class<?> type) {
+        Throwable cause = failure.getCause();
+        while (cause != null) {
+            if (hasCause(cause, type)) {
+                return true;
+            }
+            cause = cause.getCause();
+        }
+        return false;
+    }
+
+    private static boolean hasCause(Throwable cause, Class<?> type) {
+        if (cause instanceof NonGradleCauseExceptionsHolder) {
+            return ((NonGradleCauseExceptionsHolder) cause).hasCause(type);
+        }
+        return false;
     }
 
     private ExceptionStyle getShowStackTraceOption() {
@@ -185,53 +219,93 @@ public void endChildren() {
 
         private LinePrefixingStyledTextOutput getLinePrefixingStyledTextOutput(FailureDetails details) {
             details.details.format("%n");
-            StringBuilder prefix = new StringBuilder();
-            for (int i = 1; i < depth; i++) {
-                prefix.append("   ");
-            }
+            StringBuilder prefix = new StringBuilder(repeat("   ", depth - 1));
             details.details.text(prefix);
             prefix.append("  ");
-            details.details.style(Info).text("> ").style(Normal);
+            details.details.style(Info).text(RESOLUTION_LINE_PREFIX).style(Normal);
 
             return new LinePrefixingStyledTextOutput(details.details, prefix, false);
         }
     }
 
     private void fillInFailureResolution(FailureDetails details) {
-        BufferingStyledTextOutput resolution = details.resolution;
-        ContextImpl context = new ContextImpl(resolution);
+        ContextImpl context = new ContextImpl(details.resolution);
         if (details.failure instanceof FailureResolutionAware) {
             ((FailureResolutionAware) details.failure).appendResolutions(context);
         }
-        if (details.exceptionStyle == ExceptionStyle.NONE) {
-            context.appendResolution(output -> {
-                resolution.text("Run with ");
-                resolution.withStyle(UserInput).format("--%s", LoggingConfigurationBuildOptions.StacktraceOption.STACKTRACE_LONG_OPTION);
-                resolution.text(" option to get the stack trace.");
-            });
+        getResolutions(details.failure).stream()
+            .distinct()
+            .forEach(resolution ->
+                context.appendResolution(output ->
+                    output.text(join("\n " + LINE_PREFIX_LENGTH_SPACES, resolution.split("\n"))))
+            );
+        boolean hasNonGradleSpecificCauseInAncestry = hasCauseAncestry(details.failure, NonGradleCause.class);
+        if (details.exceptionStyle == ExceptionStyle.NONE && !hasNonGradleSpecificCauseInAncestry) {
+            context.appendResolution(output ->
+                runtWithOption(output, STACKTRACE_LONG_OPTION, " option to get the stack trace.")
+            );
         }
-        if (loggingConfiguration.getLogLevel() != LogLevel.DEBUG) {
+
+        boolean hasCompileError = hasNonGradleSpecificCauseInAncestry &&
+            hasCauseAncestry(details.failure, CompilationFailedIndicator.class);
+        LogLevel logLevel = loggingConfiguration.getLogLevel();
+        boolean isLessThanInfo = logLevel.ordinal() > INFO.ordinal();
+        if (hasCompileError && isLessThanInfo) {
+            context.appendResolution(output ->
+                runtWithOption(output, INFO_LONG_OPTION, " option to get more log output.")
+            );
+        } else if (logLevel != DEBUG && !hasNonGradleSpecificCauseInAncestry) {
             context.appendResolution(output -> {
-                resolution.text("Run with ");
-                if (loggingConfiguration.getLogLevel() != LogLevel.INFO) {
-                    resolution.withStyle(UserInput).format("--%s", LoggingConfigurationBuildOptions.LogLevelOption.INFO_LONG_OPTION);
-                    resolution.text(" or ");
+                output.text("Run with ");
+                if (isLessThanInfo) {
+                    output.withStyle(UserInput).format("--%s", INFO_LONG_OPTION);
+                    output.text(" or ");
                 }
-                resolution.withStyle(UserInput).format("--%s", LoggingConfigurationBuildOptions.LogLevelOption.DEBUG_LONG_OPTION);
-                resolution.text(" option to get more log output.");
+                output.withStyle(UserInput).format("--%s", DEBUG_LONG_OPTION);
+                output.text(" option to get more log output.");
             });
         }
 
         if (!context.missingBuild && !isGradleEnterprisePluginApplied()) {
             addBuildScanMessage(context);
         }
+
+        if (!hasNonGradleSpecificCauseInAncestry) {
+            context.appendResolution(this::writeGeneralTips);
+        }
+    }
+
+    private static void runtWithOption(StyledTextOutput output, String optionName, String text) {
+        output.text("Run with ");
+        output.withStyle(UserInput).format("--%s", optionName);
+        output.text(text);
+    }
+
+    private static List<String> getResolutions(Throwable throwable) {
+        ImmutableList.Builder<String> resolutions = builder();
+
+        if (throwable instanceof ResolutionProvider) {
+            resolutions.addAll(((ResolutionProvider) throwable).getResolutions());
+        }
+
+        for (Throwable cause : getCauses(throwable)) {
+            resolutions.addAll(getResolutions(cause));
+        }
+
+        return resolutions.build();
+    }
+
+    private static List<? extends Throwable> getCauses(Throwable cause) {
+        if (cause instanceof MultiCauseException) {
+            return ((MultiCauseException) cause).getCauses();
+        }
+        Throwable nextCause = cause.getCause();
+        return nextCause == null ? of() : of(nextCause);
     }
 
     private void addBuildScanMessage(ContextImpl context) {
         context.appendResolution(output -> {
-            output.text("Run with ");
-            output.withStyle(UserInput).format("--%s", StartParameterBuildOptions.BuildScanOption.LONG_OPTION);
-            output.text(" to get full insights.");
+            runtWithOption(output, LONG_OPTION, " to get full insights.");
         });
     }
 
@@ -240,10 +314,9 @@ private boolean isGradleEnterprisePluginApplied() {
     }
 
     private void writeGeneralTips(StyledTextOutput resolution) {
-        resolution.println();
-        resolution.text("* Get more help at ");
+        resolution.text("Get more help at ");
         resolution.withStyle(UserInput).text("https://help.gradle.org");
-        resolution.println();
+        resolution.text(".");
     }
 
     private static String getMessage(Throwable throwable) {
@@ -259,31 +332,17 @@ private static String getMessage(Throwable throwable) {
     }
 
     private void writeFailureDetails(StyledTextOutput output, FailureDetails details) {
-        if (details.location.getHasContent()) {
-            output.println();
-            output.println("* Where:");
-            details.location.writeTo(output);
-            output.println();
-        }
+        writeSection(details.location, output, "* Where:");
+        writeSection(details.details, output, "* What went wrong:");
+        writeSection(details.resolution, output, "* Try:");
+        writeSection(details.stackTrace, output, "* Exception is:");
+    }
 
-        if (details.details.getHasContent()) {
+    private static void writeSection(BufferingStyledTextOutput textOutput, StyledTextOutput output, String sectionTitle) {
+        if (textOutput.getHasContent()) {
             output.println();
-            output.println("* What went wrong:");
-            details.details.writeTo(output);
-            output.println();
-        }
-
-        if (details.resolution.getHasContent()) {
-            output.println();
-            output.println("* Try:");
-            details.resolution.writeTo(output);
-            output.println();
-        }
-
-        if (details.stackTrace.getHasContent()) {
-            output.println();
-            output.println("* Exception is:");
-            details.stackTrace.writeTo(output);
+            output.println(sectionTitle);
+            textOutput.writeTo(output);
             output.println();
         }
     }
@@ -327,6 +386,9 @@ static void renderStyledError(Throwable failure, StyledTextOutput details) {
 
     private class ContextImpl implements FailureResolutionAware.Context {
         private final BufferingStyledTextOutput resolution;
+
+        private final DocumentationRegistry documentationRegistry = new DocumentationRegistry();
+
         private boolean missingBuild;
 
         public ContextImpl(BufferingStyledTextOutput resolution) {
@@ -348,8 +410,13 @@ public void appendResolution(Consumer<StyledTextOutput> resolutionProducer) {
             if (resolution.getHasContent()) {
                 resolution.println();
             }
-            resolution.style(Info).text("> ").style(Normal);
+            resolution.style(Info).text(RESOLUTION_LINE_PREFIX).style(Normal);
             resolutionProducer.accept(resolution);
         }
+
+        @Override
+        public void appendDocumentationResolution(String prefix, String userGuideId, String userGuideSection) {
+            appendResolution(output -> output.text(documentationRegistry.getDocumentationRecommendationFor(prefix, userGuideId, userGuideSection)));
+        }
     }
 }
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
index 0c8e119..9d6483c 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildModelParameters.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/BuildModelParameters.java
@@ -16,6 +16,7 @@
 
 package org.gradle.internal.buildtree;
 
+import org.gradle.api.logging.LogLevel;
 import org.gradle.internal.service.scopes.Scopes;
 import org.gradle.internal.service.scopes.ServiceScope;
 
@@ -28,6 +29,7 @@ public class BuildModelParameters {
     private final boolean intermediateModelCache;
     private final boolean parallelToolingApiActions;
     private final boolean invalidateCoupledProjects;
+    private final LogLevel configurationCacheLogLevel;
 
     public BuildModelParameters(
         boolean configureOnDemand,
@@ -36,7 +38,8 @@ public BuildModelParameters(
         boolean requiresBuildModel,
         boolean intermediateModelCache,
         boolean parallelToolingApiActions,
-        boolean invalidateCoupledProjects
+        boolean invalidateCoupledProjects,
+        LogLevel configurationCacheLogLevel
     ) {
         this.configureOnDemand = configureOnDemand;
         this.configurationCache = configurationCache;
@@ -45,6 +48,7 @@ public BuildModelParameters(
         this.intermediateModelCache = intermediateModelCache;
         this.parallelToolingApiActions = parallelToolingApiActions;
         this.invalidateCoupledProjects = invalidateCoupledProjects;
+        this.configurationCacheLogLevel = configurationCacheLogLevel;
     }
 
     /**
@@ -64,6 +68,10 @@ public boolean isConfigurationCache() {
         return configurationCache;
     }
 
+    public LogLevel getConfigurationCacheLogLevel() {
+        return configurationCacheLogLevel;
+    }
+
     public boolean isIsolatedProjects() {
         return isolatedProjects;
     }
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 9abc46e..f09cc7f 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
@@ -21,11 +21,13 @@
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.internal.file.collections.FileCollectionObservationListener;
 import org.gradle.api.internal.project.DefaultProjectStateRegistry;
+import org.gradle.api.internal.project.taskfactory.TaskIdentityFactory;
 import org.gradle.api.internal.provider.DefaultConfigurationTimeBarrier;
 import org.gradle.api.logging.configuration.LoggingConfiguration;
 import org.gradle.api.logging.configuration.ShowStacktrace;
 import org.gradle.cache.internal.DecompressionCacheFactory;
 import org.gradle.cache.scopes.BuildTreeScopedCacheBuilderFactory;
+import org.gradle.internal.id.ConfigurationCacheableIdFactory;
 import org.gradle.execution.DefaultTaskSelector;
 import org.gradle.execution.ProjectConfigurer;
 import org.gradle.execution.TaskNameResolver;
@@ -83,6 +85,8 @@ protected void configure(ServiceRegistration registration, List<PluginServiceReg
         registration.add(DefaultProblemLocationAnalyzer.class);
         registration.add(DefaultExceptionAnalyser.class);
         registration.add(ScriptUsageLocationReporter.class);
+        registration.add(ConfigurationCacheableIdFactory.class);
+        registration.add(TaskIdentityFactory.class);
         modelServices.applyServicesTo(registration);
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/internal/buildtree/DefaultBuildTreeFinishExecutor.java b/subprojects/core/src/main/java/org/gradle/internal/buildtree/DefaultBuildTreeFinishExecutor.java
index c2b6a80..171cf74 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/buildtree/DefaultBuildTreeFinishExecutor.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/buildtree/DefaultBuildTreeFinishExecutor.java
@@ -44,15 +44,31 @@ public DefaultBuildTreeFinishExecutor(
     @Override
     @Nullable
     public RuntimeException finishBuildTree(List<Throwable> failures) {
-        List<Throwable> allFailures = new ArrayList<>(failures);
+        List<Throwable> finishNestedBuildsFailures = new ArrayList<>(failures);
+
         buildStateRegistry.visitBuilds(buildState -> {
             if (buildState instanceof NestedBuildState) {
                 ExecutionResult<Void> result = ((NestedBuildState) buildState).finishBuild();
-                allFailures.addAll(result.getFailures());
+                finishNestedBuildsFailures.addAll(result.getFailures());
             }
         });
-        RuntimeException reportableFailure = exceptionAnalyser.transform(allFailures);
+
+        RuntimeException reportableFailure = exceptionAnalyser.transform(finishNestedBuildsFailures);
         ExecutionResult<Void> finishResult = buildLifecycleController.finishBuild(reportableFailure);
-        return exceptionAnalyser.transform(ExecutionResult.maybeFailed(reportableFailure).withFailures(finishResult).getFailures());
+
+        List<Throwable> finishFailures = new ArrayList<>();
+        if (reportableFailure != null) {
+            finishFailures.add(reportableFailure);
+        }
+        finishFailures.addAll(finishResult.getFailures());
+        boolean failed = reportableFailure != null;
+
+        // These should run concurrently
+        buildStateRegistry.visitBuilds(buildState -> {
+            ExecutionResult<Void> result = buildState.beforeModelDiscarded(failed);
+            finishFailures.addAll(result.getFailures());
+        });
+
+        return exceptionAnalyser.transform(finishFailures);
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/CachedClasspathTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/CachedClasspathTransformer.java
index b40eeae..f6ad2db 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/classpath/CachedClasspathTransformer.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/CachedClasspathTransformer.java
@@ -54,6 +54,6 @@ enum StandardTransform {
     interface Transform {
         void applyConfigurationTo(Hasher hasher);
 
-        Pair<RelativePath, ClassVisitor> apply(ClasspathEntryVisitor.Entry entry, ClassVisitor visitor) throws IOException;
+        Pair<RelativePath, ClassVisitor> apply(ClasspathEntryVisitor.Entry entry, ClassVisitor visitor, ClassData classData) throws IOException;
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/ClassData.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/ClassData.java
new file mode 100644
index 0000000..cf05f7e
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/ClassData.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import org.gradle.internal.lazy.Lazy;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+
+public class ClassData {
+    ClassData(ClassReader reader) {
+        lazyClassNode = Lazy.unsafe().of(() -> {
+            ClassNode classNode = new ClassNode();
+            reader.accept(classNode, 0);
+            return classNode;
+        });
+    }
+
+    private final Lazy<ClassNode> lazyClassNode;
+
+    public ClassNode readClassAsNode() {
+        return lazyClassNode.get();
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/CommonTypes.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/CommonTypes.java
new file mode 100644
index 0000000..7ff1aea
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/CommonTypes.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import org.gradle.api.NonNullApi;
+import org.objectweb.asm.Type;
+
+import static org.objectweb.asm.Type.getType;
+
+/**
+ * A collection of common types and method descriptors used when instrumenting bytecode.
+ */
+@NonNullApi
+final class CommonTypes {
+    public static final Type OBJECT_TYPE = getType(Object.class);
+    public static final Type STRING_TYPE = getType(String.class);
+    public static final String[] NO_EXCEPTIONS = new String[0];
+
+    private CommonTypes() {
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/CompositeTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/CompositeTransformer.java
index e27f0b4..34aca45 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/classpath/CompositeTransformer.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/CompositeTransformer.java
@@ -39,7 +39,7 @@ public void applyConfigurationTo(Hasher hasher) {
     }
 
     @Override
-    public Pair<RelativePath, ClassVisitor> apply(ClasspathEntryVisitor.Entry entry, ClassVisitor visitor) throws IOException {
-        return first.apply(entry, second.apply(entry, visitor).right);
+    public Pair<RelativePath, ClassVisitor> apply(ClasspathEntryVisitor.Entry entry, ClassVisitor visitor, ClassData classData) throws IOException {
+        return first.apply(entry, second.apply(entry, visitor, classData).right, classData);
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/DefaultCachedClasspathTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/DefaultCachedClasspathTransformer.java
index 661e6ce..b8b97c9 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/classpath/DefaultCachedClasspathTransformer.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/DefaultCachedClasspathTransformer.java
@@ -22,7 +22,7 @@
 import org.gradle.cache.PersistentCache;
 import org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory;
 import org.gradle.internal.Either;
-import org.gradle.internal.agents.AgentControl;
+import org.gradle.internal.agents.AgentStatus;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.concurrent.ManagedExecutor;
@@ -59,6 +59,7 @@ public class DefaultCachedClasspathTransformer implements CachedClasspathTransfo
     private final FileSystemAccess fileSystemAccess;
     private final GlobalCacheLocations globalCacheLocations;
     private final FileLockManager fileLockManager;
+    private final AgentStatus agentStatus;
     private final ManagedExecutor executor;
 
     public DefaultCachedClasspathTransformer(
@@ -70,13 +71,15 @@ public DefaultCachedClasspathTransformer(
         FileSystemAccess fileSystemAccess,
         ExecutorFactory executorFactory,
         GlobalCacheLocations globalCacheLocations,
-        FileLockManager fileLockManager
+        FileLockManager fileLockManager,
+        AgentStatus agentStatus
     ) {
         this.classpathWalker = classpathWalker;
         this.classpathBuilder = classpathBuilder;
         this.fileSystemAccess = fileSystemAccess;
         this.globalCacheLocations = globalCacheLocations;
         this.fileLockManager = fileLockManager;
+        this.agentStatus = agentStatus;
         this.cache = classpathTransformerCacheFactory.createCache(cacheBuilderFactory, fileAccessTimeJournal);
         this.fileAccessTracker = classpathTransformerCacheFactory.createFileAccessTracker(cache, fileAccessTimeJournal);
         this.executor = executorFactory.create("jar transforms", Runtime.getRuntime().availableProcessors());
@@ -106,7 +109,7 @@ private TransformPipeline transformPipelineFor(StandardTransform transform) {
             case None:
                 return copyingPipeline();
             case BuildLogic:
-                if (!AgentControl.isInstrumentationAgentApplied()) {
+                if (!agentStatus.isAgentInstrumentationEnabled()) {
                     return instrumentingPipeline(InstrumentingClasspathFileTransformer.instrumentForLoadingWithClassLoader());
                 }
                 return agentInstrumentingPipeline(copyingPipeline(), instrumentingPipeline(InstrumentingClasspathFileTransformer.instrumentForLoadingWithAgent()));
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/FileUtils.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/FileUtils.java
new file mode 100644
index 0000000..666e458
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/FileUtils.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import org.gradle.internal.Cast;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileAttribute;
+import java.util.Set;
+
+public class FileUtils {
+    /**
+     * Checks if the channel created with the given options will have read access of the file content.
+     * See the contract of {@link Files#newByteChannel(Path, Set, FileAttribute[])}.
+     */
+    public static boolean optionsAllowReading(OpenOption[] options) {
+        boolean hasNonReadMode = false;
+
+        for (OpenOption option : options) {
+            if (option == StandardOpenOption.READ) {
+                return true;
+            }
+            if (option == StandardOpenOption.APPEND || option == StandardOpenOption.WRITE) {
+                hasNonReadMode = true;
+            }
+        }
+
+        return !hasNonReadMode;
+    }
+
+    public static boolean optionsAllowReading(Set<?> options) {
+        return optionsAllowReading(Cast.<Set<OpenOption>>uncheckedNonnullCast(options).toArray(new OpenOption[0]));
+    }
+
+    public static void tryReportFileOpened(Path path, String consumer) {
+        File file = toFileIfAvailable(path);
+        if (file != null) {
+            Instrumented.fileOpened(file, consumer);
+        }
+    }
+
+    public static void tryReportDirectoryContentObserved(Path path, String consumer) {
+        File file = toFileIfAvailable(path);
+        if (file != null) {
+            Instrumented.directoryContentObserved(file, consumer);
+        }
+    }
+
+    public static void tryReportFileSystemEntryObserved(Path path, String consumer) {
+        File file = toFileIfAvailable(path);
+        if (file != null) {
+            Instrumented.fileSystemEntryObserved(file, consumer);
+        }
+    }
+
+    /**
+     * Returns the result of {@link Path#toFile()} if that will not throw an exception, null otherwise.
+     */
+    @Nullable
+    private static File toFileIfAvailable(Path path) {
+        if (path.getFileSystem() == FileSystems.getDefault()) {
+            return path.toFile();
+        }
+        return null;
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/GroovyCallInterceptorsProvider.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/GroovyCallInterceptorsProvider.java
new file mode 100644
index 0000000..67d4c0d
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/GroovyCallInterceptorsProvider.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 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.classpath;
+
+import org.gradle.api.NonNullApi;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAMES;
+
+@NonNullApi
+public interface GroovyCallInterceptorsProvider {
+
+    GroovyCallInterceptorsProvider DEFAULT = () -> Stream.concat(
+        Stream.of(Instrumented.class.getName()),
+        GROOVY_INTERCEPTORS_GENERATED_CLASS_NAMES.stream()
+    ).collect(Collectors.toList());
+
+    List<String> getInterceptorProviderClassNames();
+
+    default GroovyCallInterceptorsProvider plus(GroovyCallInterceptorsProvider other) {
+        return () -> Stream.concat(getInterceptorProviderClassNames().stream(), other.getInterceptorProviderClassNames().stream()).collect(Collectors.toList());
+    }
+
+    static GroovyCallInterceptorsProvider fromClass(Class<?> theClass) {
+        return () -> Collections.singletonList(theClass.getName());
+    }
+
+    static GroovyCallInterceptorsProvider fromClassName(String className) {
+        return () -> Collections.singletonList(className);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/GroovyCallInterceptorsProvisionTools.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/GroovyCallInterceptorsProvisionTools.java
new file mode 100644
index 0000000..cf2098e
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/GroovyCallInterceptorsProvisionTools.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2020 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.classpath;
+
+import org.gradle.api.NonNullApi;
+import org.gradle.internal.Cast;
+import org.gradle.internal.classpath.intercept.CallInterceptor;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@NonNullApi
+public class GroovyCallInterceptorsProvisionTools {
+    private GroovyCallInterceptorsProvisionTools() {
+    }
+
+    static List<CallInterceptor> getInterceptorsFromProvider(GroovyCallInterceptorsProvider provider) {
+        return provider.getInterceptorProviderClassNames().stream().flatMap(it -> getInterceptorsFromClass(it).stream()).collect(Collectors.toList());
+    }
+
+    /**
+     * @param className the class name for the class that provides the call interceptors, as per {@link Class#getName}
+     */
+    static List<CallInterceptor> getInterceptorsFromClass(String className) {
+        try {
+            return getInterceptorsFromClass(Class.forName(className));
+        } catch (ClassNotFoundException e) {
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * @param interceptorsProviderClass the class providing the Groovy call interceptors.
+     * It must have a method that follows the pattern: {@code public static List<CallInterceptor> getCallInterceptors()}
+     */
+    static List<CallInterceptor> getInterceptorsFromClass(Class<?> interceptorsProviderClass) {
+        Method getCallInterceptors;
+        try {
+            getCallInterceptors = interceptorsProviderClass.getDeclaredMethod("getCallInterceptors");
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException("The provider class does not have the expected getCallInterceptors method", e);
+        }
+
+        List<CallInterceptor> result;
+        try {
+            result = Cast.uncheckedNonnullCast(getCallInterceptors.invoke(null));
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException("Cannot access the getCallInterceptors method in the provider class", e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+
+        return result;
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/Instrumented.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/Instrumented.java
index 9e88a0f..31784fc 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/classpath/Instrumented.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/Instrumented.java
@@ -22,9 +22,11 @@
 import org.codehaus.groovy.runtime.callsite.CallSite;
 import org.codehaus.groovy.runtime.callsite.CallSiteArray;
 import org.codehaus.groovy.vmplugin.v8.IndyInterface;
+import org.gradle.api.NonNullApi;
 import org.gradle.internal.SystemProperties;
 import org.gradle.internal.classpath.intercept.CallInterceptor;
 import org.gradle.internal.classpath.intercept.CallInterceptorsSet;
+import org.gradle.internal.classpath.intercept.CallSiteDecorator;
 import org.gradle.internal.classpath.intercept.ClassBoundCallInterceptor;
 import org.gradle.internal.classpath.intercept.InterceptScope;
 import org.gradle.internal.classpath.intercept.Invocation;
@@ -33,7 +35,6 @@
 import javax.annotation.Nullable;
 import java.io.File;
 import java.io.FileFilter;
-import java.io.FileInputStream;
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.lang.invoke.MethodHandle;
@@ -49,11 +50,10 @@
 import java.util.Optional;
 import java.util.Properties;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.BiFunction;
 import java.util.stream.Collectors;
 
-import static org.gradle.internal.classpath.InstrumentedUtil.findStaticOrThrowError;
-import static org.gradle.internal.classpath.InstrumentedUtil.kotlinDefaultMethodType;
+import static org.gradle.internal.classpath.MethodHandleUtils.findStaticOrThrowError;
+import static org.gradle.internal.classpath.MethodHandleUtils.lazyKotlinStaticDefaultHandle;
 
 public class Instrumented {
     private static final Listener NO_OP = new Listener() {
@@ -108,34 +108,53 @@ public static void discardListener() {
         setListener(NO_OP);
     }
 
-    private static final CallInterceptorsSet CALL_INTERCEPTORS = new CallInterceptorsSet(
-        new SystemGetPropertyInterceptor(),
-        new SystemSetPropertyInterceptor(),
-        new SystemGetPropertiesInterceptor(),
-        new SystemSetPropertiesInterceptor(),
-        new SystemClearPropertyInterceptor(),
-        new IntegerGetIntegerInterceptor(),
-        new LongGetLongInterceptor(),
-        new BooleanGetBooleanInterceptor(),
-        new SystemGetenvInterceptor(),
-        new RuntimeExecInterceptor(),
-        new FileCheckInterceptor.FileExistsInterceptor(),
-        new FileCheckInterceptor.FileIsFileInterceptor(),
-        new FileCheckInterceptor.FileIsDirectoryInterceptor(),
-        new FileListFilesInterceptor(),
-        new FilesReadStringInterceptor(),
-        new FileTextInterceptor(),
-        new ProcessGroovyMethodsExecuteInterceptor(),
-        new ProcessBuilderStartInterceptor(),
-        new ProcessBuilderStartPipelineInterceptor(),
-        new FileInputStreamConstructorInterceptor());
+    /**
+     * This API follows the requirements in {@link GroovyCallInterceptorsProvisionTools}.
+     * @deprecated This should not be called from the sources.
+     */
+    @SuppressWarnings("unused")
+    @Deprecated
+    public static List<CallInterceptor> getCallInterceptors() {
+        return Arrays.asList(
+            new SystemGetPropertyInterceptor(),
+            new SystemSetPropertyInterceptor(),
+            new SystemGetPropertiesInterceptor(),
+            new SystemSetPropertiesInterceptor(),
+            new SystemClearPropertyInterceptor(),
+            new IntegerGetIntegerInterceptor(),
+            new LongGetLongInterceptor(),
+            new BooleanGetBooleanInterceptor(),
+            new SystemGetenvInterceptor(),
+            new RuntimeExecInterceptor(),
+            new ProcessGroovyMethodsExecuteInterceptor(),
+            new ProcessBuilderStartInterceptor(),
+            new ProcessBuilderStartPipelineInterceptor()
+        );
+    }
 
+    private static volatile CallSiteDecorator currentCallDecorator = new CallInterceptorsSet(
+        GroovyCallInterceptorsProvisionTools.getInterceptorsFromProvider(GroovyCallInterceptorsProvider.DEFAULT).stream()
+    );
+
+    // TODO: We can support getting the interceptors in the instrumented code from different locations, not just `Instrumented.currentCallDecorator`,
+    //       so that replacing the call interceptors for tests would instead work by embedding a different location
+    //       than this one in the instrumented code. Doing that adds much more complexity in the instrumentation.
+    @NonNullApi
+    public static class GroovyCallInterceptorInternalTesting {
+        static CallSiteDecorator getCurrentGroovyCallSiteDecorator() {
+            return currentCallDecorator;
+        }
+
+        static void setCurrentGroovyCallSiteDecorator(CallSiteDecorator interceptorsSet) {
+            currentCallDecorator = interceptorsSet;
+        }
+    }
 
     // Called by generated code
     @SuppressWarnings("unused")
     public static void groovyCallSites(CallSiteArray array) {
         for (CallSite callSite : array.array) {
-            array.array[callSite.getIndex()] = CALL_INTERCEPTORS.maybeDecorateGroovyCallSite(callSite);
+            array.array[callSite.getIndex()] = currentCallDecorator.maybeDecorateGroovyCallSite(callSite);
         }
     }
 
@@ -153,7 +172,7 @@ public static void groovyCallSites(CallSiteArray array) {
      * @see IndyInterface
      */
     public static java.lang.invoke.CallSite bootstrap(MethodHandles.Lookup caller, String callType, MethodType type, String name, int flags) {
-        return CALL_INTERCEPTORS.maybeDecorateIndyCallSite(
+        return currentCallDecorator.maybeDecorateIndyCallSite(
             IndyInterface.bootstrap(caller, callType, type, name, flags), caller, callType, name, flags);
     }
 
@@ -353,33 +372,41 @@ public static Process start(ProcessBuilder builder, String consumer) throws IOEx
         return builder.start();
     }
 
-    public static boolean fileExists(File file, String consumer) {
+    public static void fileSystemEntryObserved(File file, String consumer) {
         listener().fileSystemEntryObserved(file, consumer);
+    }
+
+    public static boolean fileExists(File file, String consumer) {
+        fileSystemEntryObserved(file, consumer);
         return file.exists();
     }
 
     public static boolean fileIsFile(File file, String consumer) {
-        listener().fileSystemEntryObserved(file, consumer);
+        fileSystemEntryObserved(file, consumer);
         return file.isFile();
     }
 
     public static boolean fileIsDirectory(File file, String consumer) {
-        listener().fileSystemEntryObserved(file, consumer);
+        fileSystemEntryObserved(file, consumer);
         return file.isDirectory();
     }
 
-    public static File[] fileListFiles(File file, String consumer) {
+    public static void directoryContentObserved(File file, String consumer) {
         listener().directoryContentObserved(file, consumer);
+    }
+
+    public static File[] fileListFiles(File file, String consumer) {
+        directoryContentObserved(file, consumer);
         return file.listFiles();
     }
 
     public static File[] fileListFiles(File file, FileFilter fileFilter, String consumer) {
-        listener().directoryContentObserved(file, consumer);
+        directoryContentObserved(file, consumer);
         return file.listFiles(fileFilter);
     }
 
     public static File[] fileListFiles(File file, FilenameFilter fileFilter, String consumer) {
-        listener().directoryContentObserved(file, consumer);
+        directoryContentObserved(file, consumer);
         return file.listFiles(fileFilter);
     }
 
@@ -394,7 +421,7 @@ public static String kotlinIoFilesKtReadTextDefault(File receiver, Charset chars
     }
 
     private static final Lazy<MethodHandle> FILESKT_READ_TEXT_DEFAULT =
-        Lazy.locking().of(() -> findStaticOrThrowError(FilesKt.class, "readText$default", kotlinDefaultMethodType(String.class, File.class, Charset.class)));
+        lazyKotlinStaticDefaultHandle(FilesKt.class, "readText", String.class, File.class, Charset.class);
 
     public static String filesReadString(Path file, String consumer) throws Throwable {
         listener().fileOpened(file.toFile(), consumer);
@@ -402,7 +429,7 @@ public static String filesReadString(Path file, String consumer) throws Throwabl
     }
 
     public static String filesReadString(Path file, Charset charset, String consumer) throws Throwable {
-        listener().fileOpened(file.toFile(), consumer);
+        FileUtils.tryReportFileOpened(file, consumer);
         return (String) FILES_READ_STRING_PATH_CHARSET.get().invokeExact(file, charset);
     }
 
@@ -788,122 +815,6 @@ private Optional<Process> tryCallExec(Object runtimeArg, Object commandArg, @Nul
         }
     }
 
-    private static abstract class FileCheckInterceptor extends CallInterceptor {
-        private final BiFunction<File, String, Boolean> invokeWhenIntercepted;
-
-        public FileCheckInterceptor(String methodName, BiFunction<File, String, Boolean> invokeWhenIntercepted) {
-            super(InterceptScope.methodsNamed(methodName));
-            this.invokeWhenIntercepted = invokeWhenIntercepted;
-        }
-
-        @Override
-        protected Object doIntercept(Invocation invocation, String consumer) throws Throwable {
-            if (invocation.getArgsCount() == 0) {
-                Object receiver = invocation.getReceiver();
-                if (receiver instanceof File) {
-                    File fileReceiver = (File) receiver;
-                    return invokeWhenIntercepted.apply(fileReceiver, consumer);
-                }
-            }
-            return invocation.callOriginal();
-        }
-
-        static class FileExistsInterceptor extends FileCheckInterceptor {
-            public FileExistsInterceptor() {
-                super("exists", Instrumented::fileExists);
-            }
-        }
-
-        static class FileIsFileInterceptor extends FileCheckInterceptor {
-            public FileIsFileInterceptor() {
-                super("isFile", Instrumented::fileIsFile);
-            }
-        }
-
-        static class FileIsDirectoryInterceptor extends FileCheckInterceptor {
-            public FileIsDirectoryInterceptor() {
-                super("isDirectory", Instrumented::fileIsDirectory);
-            }
-        }
-    }
-
-    private static class FileListFilesInterceptor extends CallInterceptor {
-        public FileListFilesInterceptor() {
-            super(InterceptScope.methodsNamed("listFiles"));
-        }
-
-        @Override
-        protected Object doIntercept(Invocation invocation, String consumer) throws Throwable {
-            if (invocation.getArgsCount() <= 1) {
-                Object receiver = invocation.getReceiver();
-                if (receiver instanceof File) {
-                    File fileReceiver = (File) receiver;
-                    if (invocation.getArgsCount() == 0) {
-                        return Instrumented.fileListFiles(fileReceiver, consumer);
-                    } else if (invocation.getArgsCount() == 1) {
-                        Object arg0 = invocation.getArgument(0);
-                        if (arg0 instanceof FileFilter) {
-                            return Instrumented.fileListFiles(fileReceiver, (FileFilter) arg0, consumer);
-                        } else if (arg0 instanceof FilenameFilter) {
-                            return Instrumented.fileListFiles(fileReceiver, (FilenameFilter) arg0, consumer);
-                        }
-                    }
-                }
-            }
-            return invocation.callOriginal();
-        }
-    }
-
-    private static class FilesReadStringInterceptor extends ClassBoundCallInterceptor {
-        public FilesReadStringInterceptor() {
-            super(Files.class, InterceptScope.methodsNamed("readString"));
-        }
-
-        @Override
-        protected Object doInterceptSafe(Invocation invocation, String consumer) throws Throwable {
-            if (invocation.getArgsCount() >= 1 && invocation.getArgsCount() <= 2) {
-                Object arg0 = invocation.getArgument(0);
-                if (arg0 instanceof Path) {
-                    Path arg0Path = (Path) arg0;
-                    if (invocation.getArgsCount() == 1) {
-                        return Instrumented.filesReadString(arg0Path, consumer);
-                    } else if (invocation.getArgsCount() == 2) {
-                        Object arg1 = invocation.getArgument(1);
-                        if (arg1 instanceof Charset) {
-                            Charset arg1Charset = (Charset) arg1;
-                            return Instrumented.filesReadString(arg0Path, arg1Charset, consumer);
-                        }
-                    }
-                }
-            }
-            return invocation.callOriginal();
-        }
-    }
-
-    private static class FileTextInterceptor extends CallInterceptor {
-        public FileTextInterceptor() {
-            super(InterceptScope.readsOfPropertiesNamed("text"), InterceptScope.methodsNamed("getText"));
-        }
-
-        @Override
-        protected Object doIntercept(Invocation invocation, String consumer) throws Throwable {
-            Object receiver = invocation.getReceiver();
-            if (receiver instanceof File) {
-                File receiverFile = (File) receiver;
-                if (invocation.getArgsCount() == 0) {
-                    return groovyFileGetText(receiverFile, consumer);
-                } else if (invocation.getArgsCount() == 1) {
-                    Object arg0 = invocation.getArgument(0);
-                    if (arg0 instanceof String) {
-                        String arg0String = (String) arg0;
-                        return groovyFileGetText(receiverFile, arg0String, consumer);
-                    }
-                }
-            }
-            return invocation.callOriginal();
-        }
-    }
-
     /**
      * The interceptor for Groovy's {@code String.execute}, {@code String[].execute}, and {@code List.execute}. This also handles {@code ProcessGroovyMethods.execute}.
      */
@@ -1006,51 +917,4 @@ protected Object doInterceptSafe(Invocation invocation, String consumer) throws
             return invocation.callOriginal();
         }
     }
-
-    /**
-     * The interceptor for {@link FileInputStream#FileInputStream(File)} and {@link FileInputStream#FileInputStream(String)}.
-     */
-    private static class FileInputStreamConstructorInterceptor extends CallInterceptor {
-        public FileInputStreamConstructorInterceptor() {
-            super(InterceptScope.constructorsOf(FileInputStream.class));
-        }
-
-        @Override
-        protected Object doIntercept(Invocation invocation, String consumer) throws Throwable {
-            if (invocation.getArgsCount() == 1) {
-                Object argument = invocation.getArgument(0);
-                if (argument instanceof CharSequence) {
-                    String path = convertToString(argument);
-                    fileOpened(path, consumer);
-                    return new FileInputStream(path);
-                } else if (argument instanceof File) {
-                    File file = (File) argument;
-                    fileOpened(file, consumer);
-                    return new FileInputStream(file);
-                }
-            }
-            return invocation.callOriginal();
-
-        }
-    }
 }
-
-class InstrumentedUtil {
-    static MethodHandle findStaticOrThrowError(Class<?> refc, String name, MethodType methodType) {
-        try {
-            return MethodHandles.lookup().findStatic(refc, name, methodType);
-        } catch (NoSuchMethodException e) {
-            throw new NoSuchMethodError(e.getMessage());
-        } catch (IllegalAccessException e) {
-            throw new IllegalAccessError(e.getMessage());
-        }
-    }
-
-    static MethodType kotlinDefaultMethodType(Class<?> returnType, Class<?>... parameterTypes) {
-        Class<?>[] defaultParameterTypes = Arrays.copyOf(parameterTypes, parameterTypes.length + 2);
-        defaultParameterTypes[parameterTypes.length] = int.class; // default value mask
-        defaultParameterTypes[parameterTypes.length + 1] = Object.class; // default signature marker
-        return MethodType.methodType(returnType, defaultParameterTypes);
-    }
-}
-
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentedExecutionAccess.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentedExecutionAccess.java
new file mode 100644
index 0000000..7f7a900
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentedExecutionAccess.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import org.gradle.api.NonNullApi;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * As {@link Instrumented} is using to augment calls at configuration time, this class is using
+ * to augment calls at execution time.
+ */
+@NonNullApi
+public class InstrumentedExecutionAccess {
+
+    private static final Listener NO_OP = new Listener() {
+
+        @Override
+        public void disallowedAtExecutionInjectedServiceAccessed(Class<?> injectedServiceType, String propertyName, String consumer) {
+        }
+    };
+
+    private static final AtomicReference<Listener> LISTENER = new AtomicReference<>(NO_OP);
+
+    private static Listener listener() {
+        return LISTENER.get();
+    }
+
+    public static void setListener(Listener listener) {
+        LISTENER.set(listener);
+    }
+
+    public static void discardListener() {
+        LISTENER.set(NO_OP);
+    }
+
+    /**
+     * Called by generated code
+     *
+     * Invoked when the code accesses the injected service of type that disallowed at execution time (eg. Project)
+     *
+     * @param injectedServiceType the type of the injected service that was accessed
+     * @param consumer class name of consumer that contains the code accesses the injected service
+     */
+    public static void disallowedAtExecutionInjectedServiceAccessed(
+        Class<?> injectedServiceType,
+        String getterName,
+        String consumer
+    ) {
+        listener().disallowedAtExecutionInjectedServiceAccessed(injectedServiceType, getterName, consumer);
+    }
+
+    @NonNullApi
+    public interface Listener {
+        void disallowedAtExecutionInjectedServiceAccessed(Class<?> injectedServiceType, String propertyName, String consumer);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingClasspathFileTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingClasspathFileTransformer.java
index 2264ea3..11f63df 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingClasspathFileTransformer.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingClasspathFileTransformer.java
@@ -37,8 +37,13 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nullable;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
 
 import static java.lang.String.format;
 import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode;
@@ -46,6 +51,10 @@
 class InstrumentingClasspathFileTransformer implements ClasspathFileTransformer {
     private static final Logger LOGGER = LoggerFactory.getLogger(InstrumentingClasspathFileTransformer.class);
     private static final int CACHE_FORMAT = 5;
+    private static final int AGENT_INSTRUMENTATION_VERSION = 2;
+
+    // We cannot use Attributes.Name.MULTI_RELEASE as it is only available since Java 9;
+    private static final String MULTI_RELEASE_ATTRIBUTE = "Multi-Release";
 
     private final FileLockManager fileLockManager;
     private final ClasspathWalker classpathWalker;
@@ -54,11 +63,43 @@ class InstrumentingClasspathFileTransformer implements ClasspathFileTransformer
     private final CachedClasspathTransformer.Transform transform;
     private final HashCode configHash;
 
+    /**
+     * Instrumentation policy. There are some differences when instrumenting classes to be loaded by the instrumenting agent, this interface encapsulates them.
+     */
     public interface Policy {
+        /**
+         * Modifies JAR content hash according to the algorithm implemented by this policy.
+         *
+         * @param hasher the hasher to modify
+         */
         void applyConfigurationTo(Hasher hasher);
 
+        /**
+         * Checks if the file/directory should be instrumented. For example, legacy instrumentation doesn't support signed JARs.
+         *
+         * @param file the file/directory to check
+         * @return {@code true} if the file/directory should be instrumented.
+         */
         boolean instrumentFile(File file);
 
+        /**
+         * Processes manifest of the instrumented JAR file. The implementation may return the manifest unprocessed.
+         * If the return value is {@code null}, then the manifest should not be added to the resulting instrumented JAR at all.
+         *
+         * @param source the file/directory that contributes the manifest
+         * @param entry the entry of the manifest
+         * @return the contents of the manifest to put, or {@code null} if the resulting JAR should have no manifest at all
+         * @throws IOException if reading and parsing the manifest fail
+         */
+        @Nullable
+        byte[] processManifest(File source, ClasspathEntryVisitor.Entry entry) throws IOException;
+
+        /**
+         * Checks if the entry should be included into the resulting instrumented JAR
+         *
+         * @param entry the entry to check
+         * @return {@code true} if the entry should be included.
+         */
         boolean includeEntry(ClasspathEntryVisitor.Entry entry);
     }
 
@@ -162,13 +203,18 @@ private void visitEntries(File source, ClasspathBuilder.EntryBuilder builder) th
                 if (!policy.includeEntry(entry)) {
                     return;
                 }
-                if (entry.getName().endsWith(".class")) {
+                if (isClassFile(entry)) {
                     ClassReader reader = new ClassReader(entry.getContent());
                     ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-                    Pair<RelativePath, ClassVisitor> chain = transform.apply(entry, classWriter);
+                    Pair<RelativePath, ClassVisitor> chain = transform.apply(entry, classWriter, new ClassData(reader));
                     reader.accept(chain.right, 0);
                     byte[] bytes = classWriter.toByteArray();
                     builder.put(chain.left.getPathString(), bytes, entry.getCompressionMethod());
+                } else if (isManifest(entry)) {
+                    byte[] processedManifest = policy.processManifest(source, entry);
+                    if (processedManifest != null) {
+                        builder.put(entry.getName(), processedManifest, entry.getCompressionMethod());
+                    }
                 } else {
                     builder.put(entry.getName(), entry.getContent(), entry.getCompressionMethod());
                 }
@@ -210,6 +256,12 @@ public boolean instrumentFile(File file) {
             }
 
             @Override
+            public byte[] processManifest(File source, ClasspathEntryVisitor.Entry entry) throws IOException {
+                // No need to modify manifest, let's put the original as is.
+                return entry.getContent();
+            }
+
+            @Override
             public boolean includeEntry(ClasspathEntryVisitor.Entry entry) {
                 // include everything
                 return true;
@@ -221,7 +273,7 @@ public static Policy instrumentForLoadingWithAgent() {
         return new Policy() {
             @Override
             public void applyConfigurationTo(Hasher hasher) {
-                hasher.putInt(1);
+                hasher.putInt(AGENT_INSTRUMENTATION_VERSION);
             }
 
             @Override
@@ -230,10 +282,63 @@ public boolean instrumentFile(File file) {
             }
 
             @Override
+            public byte[] processManifest(File source, ClasspathEntryVisitor.Entry entry) throws IOException {
+                try {
+                    Manifest parsedManifest = new Manifest(new ByteArrayInputStream(entry.getContent()));
+                    if (isMultiReleaseJarManifest(parsedManifest)) {
+                        // We want processed JAR to also be a proper multi-release JAR.
+                        // To do so it must have the "Multi-Release: true" attribute.
+                        // "Manifest-Version" attribute is also required.
+                        // For everything else (classpath, sealed, etc.) classloader will check the original JAR, so no need to copy it.
+                        Manifest processedManifest = new Manifest();
+                        copyManifestMainAttribute(parsedManifest, processedManifest, Attributes.Name.MANIFEST_VERSION);
+                        setManifestMainAttribute(processedManifest, MULTI_RELEASE_ATTRIBUTE, "true");
+                        return toByteArray(processedManifest);
+                    }
+
+                    return null;
+                } catch (IOException e) {
+                    LOGGER.debug("Failed to parse Manifest from JAR " + source);
+                    throw e;
+                }
+            }
+
+            @Nullable
+            private String getManifestMainAttribute(Manifest manifest, String name) {
+                return manifest.getMainAttributes().getValue(name);
+            }
+
+            private void copyManifestMainAttribute(Manifest source, Manifest destination, Attributes.Name name) {
+                destination.getMainAttributes().put(name, source.getMainAttributes().getValue(name));
+            }
+
+            private void setManifestMainAttribute(Manifest manifest, String name, String value) {
+                manifest.getMainAttributes().putValue(name, value);
+            }
+
+            private boolean isMultiReleaseJarManifest(Manifest manifest) {
+                return Boolean.parseBoolean(getManifestMainAttribute(manifest, MULTI_RELEASE_ATTRIBUTE));
+            }
+
+            private byte[] toByteArray(Manifest manifest) throws IOException {
+                ByteArrayOutputStream manifestOutput = new ByteArrayOutputStream(512);
+                manifest.write(manifestOutput);
+                return manifestOutput.toByteArray();
+            }
+
+            @Override
             public boolean includeEntry(ClasspathEntryVisitor.Entry entry) {
                 // Only include classes in the result, resources will be loaded from the original JAR by the class loader.
-                return entry.getName().endsWith(".class");
+                return isClassFile(entry) || isManifest(entry);
             }
         };
     }
+
+    private static boolean isClassFile(ClasspathEntryVisitor.Entry entry) {
+        return entry.getName().endsWith(".class");
+    }
+
+    private static boolean isManifest(ClasspathEntryVisitor.Entry entry) {
+        return entry.getName().equals("META-INF/MANIFEST.MF");
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingTransformer.java
index ac787d6..89de296 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingTransformer.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingTransformer.java
@@ -16,46 +16,40 @@
 
 package org.gradle.internal.classpath;
 
-import kotlin.io.FilesKt;
 import org.codehaus.groovy.runtime.ProcessGroovyMethods;
-import org.codehaus.groovy.runtime.ResourceGroovyMethods;
 import org.codehaus.groovy.runtime.callsite.CallSiteArray;
 import org.codehaus.groovy.vmplugin.v8.IndyInterface;
-import org.gradle.api.Action;
-import org.gradle.api.Transformer;
 import org.gradle.api.file.RelativePath;
-import org.gradle.api.specs.Spec;
 import org.gradle.internal.Pair;
 import org.gradle.internal.hash.Hasher;
+import org.gradle.internal.instrumentation.api.jvmbytecode.JvmBytecodeCallInterceptor;
+import org.gradle.internal.instrumentation.api.metadata.InstrumentationMetadata;
+import org.gradle.internal.lazy.Lazy;
 import org.gradle.model.internal.asm.MethodVisitorScope;
-import org.gradle.process.CommandLineArgumentProvider;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.Handle;
-import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
 
 import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FilenameFilter;
 import java.lang.invoke.CallSite;
-import java.lang.invoke.LambdaMetafactory;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
-import java.lang.invoke.SerializedLambda;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Properties;
+import java.util.function.Supplier;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
 import static org.gradle.internal.classanalysis.AsmConstants.ASM_LEVEL;
-import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
+import static org.gradle.internal.classpath.CommonTypes.NO_EXCEPTIONS;
+import static org.gradle.internal.classpath.CommonTypes.STRING_TYPE;
 import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
 import static org.objectweb.asm.Opcodes.ACC_STATIC;
 import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
@@ -70,22 +64,20 @@
 
 class InstrumentingTransformer implements CachedClasspathTransformer.Transform {
 
+    private final JvmBytecodeInterceptorSet externalInterceptors;
+
     /**
      * Decoration format. Increment this when making changes.
      */
-    private static final int DECORATION_FORMAT = 22;
+    private static final int DECORATION_FORMAT = 30;
 
     private static final Type SYSTEM_TYPE = getType(System.class);
-    private static final Type STRING_TYPE = getType(String.class);
     private static final Type INTEGER_TYPE = getType(Integer.class);
     private static final Type INSTRUMENTED_TYPE = getType(Instrumented.class);
-    private static final Type OBJECT_TYPE = getType(Object.class);
-    private static final Type SERIALIZED_LAMBDA_TYPE = getType(SerializedLambda.class);
     private static final Type LONG_TYPE = getType(Long.class);
     private static final Type BOOLEAN_TYPE = getType(Boolean.class);
     public static final Type PROPERTIES_TYPE = getType(Properties.class);
 
-    private static final String RETURN_STRING = getMethodDescriptor(STRING_TYPE);
     private static final String RETURN_STRING_FROM_STRING = getMethodDescriptor(STRING_TYPE, STRING_TYPE);
     private static final String RETURN_STRING_FROM_STRING_STRING = getMethodDescriptor(STRING_TYPE, STRING_TYPE, STRING_TYPE);
     private static final String RETURN_STRING_FROM_STRING_STRING_STRING = getMethodDescriptor(STRING_TYPE, STRING_TYPE, STRING_TYPE, STRING_TYPE);
@@ -103,16 +95,12 @@ class InstrumentingTransformer implements CachedClasspathTransformer.Transform {
     private static final String RETURN_LONG_FROM_STRING_LONG_STRING = getMethodDescriptor(LONG_TYPE, STRING_TYPE, LONG_TYPE, STRING_TYPE);
     private static final String RETURN_PRIMITIVE_BOOLEAN_FROM_STRING = getMethodDescriptor(Type.BOOLEAN_TYPE, STRING_TYPE);
     private static final String RETURN_PRIMITIVE_BOOLEAN_FROM_STRING_STRING = getMethodDescriptor(Type.BOOLEAN_TYPE, STRING_TYPE, STRING_TYPE);
-    private static final String RETURN_OBJECT_FROM_INT = getMethodDescriptor(OBJECT_TYPE, Type.INT_TYPE);
-    private static final String RETURN_BOOLEAN = getMethodDescriptor(Type.BOOLEAN_TYPE);
-    private static final String RETURN_BOOLEAN_FROM_OBJECT = getMethodDescriptor(Type.BOOLEAN_TYPE, OBJECT_TYPE);
     private static final String RETURN_PROPERTIES = getMethodDescriptor(PROPERTIES_TYPE);
     private static final String RETURN_PROPERTIES_FROM_STRING = getMethodDescriptor(PROPERTIES_TYPE, STRING_TYPE);
     private static final String RETURN_VOID_FROM_PROPERTIES = getMethodDescriptor(Type.VOID_TYPE, PROPERTIES_TYPE);
     private static final String RETURN_VOID_FROM_PROPERTIES_STRING = getMethodDescriptor(Type.VOID_TYPE, PROPERTIES_TYPE, STRING_TYPE);
     private static final String RETURN_CALL_SITE_ARRAY = getMethodDescriptor(getType(CallSiteArray.class));
     private static final String RETURN_VOID_FROM_CALL_SITE_ARRAY = getMethodDescriptor(Type.VOID_TYPE, getType(CallSiteArray.class));
-    private static final String RETURN_OBJECT_FROM_SERIALIZED_LAMBDA = getMethodDescriptor(OBJECT_TYPE, SERIALIZED_LAMBDA_TYPE);
     private static final String RETURN_MAP = getMethodDescriptor(getType(Map.class));
     private static final String RETURN_MAP_FROM_STRING = getMethodDescriptor(getType(Map.class), STRING_TYPE);
 
@@ -122,34 +110,8 @@ class InstrumentingTransformer implements CachedClasspathTransformer.Transform {
     private static final Type PROCESS_GROOVY_METHODS_TYPE = getType(ProcessGroovyMethods.class);
     private static final Type STRING_ARRAY_TYPE = getType(String[].class);
     private static final Type FILE_TYPE = getType(File.class);
-    private static final Type FILE_ARRAY_TYPE = getType(File[].class);
-    private static final Type PATH_TYPE = getType(Path.class);
-    private static final Type CHARSET_TYPE = getType(Charset.class);
     private static final Type LIST_TYPE = getType(List.class);
 
-    private static final Type KOTLIN_IO_FILES_TYPE = getType(FilesKt.class);
-    // readText(File, Charset) -> kotlinIoFilesKtReadText(File, Charset, String)
-    private static final String RETURN_STRING_FROM_FILE_CHARSET = getMethodDescriptor(STRING_TYPE, FILE_TYPE, getType(Charset.class));
-    private static final String RETURN_STRING_FROM_FILE_CHARSET_STRING = getMethodDescriptor(STRING_TYPE, FILE_TYPE, getType(Charset.class), STRING_TYPE);
-    // readText$default(File, Charset, int, Object) -> kotlinIoFilesKtReadText(File, Charset, String)
-    private static final String RETURN_STRING_FROM_FILE_CHARSET_INT_OBJECT = getMethodDescriptor(STRING_TYPE, FILE_TYPE, getType(Charset.class), INT_TYPE, OBJECT_TYPE);
-    private static final String RETURN_STRING_FROM_FILE_CHARSET_INT_OBJECT_STRING = getMethodDescriptor(STRING_TYPE, FILE_TYPE, getType(Charset.class), INT_TYPE, OBJECT_TYPE, STRING_TYPE);
-
-    private static final Type RESOURCE_GROOVY_METHODS_TYPE = getType(ResourceGroovyMethods.class);
-    // file.text -(Groovy compiler)-> ResourceGroovyMethods.getText(File) -> groovyFileGetText(File, String)
-    private static final String RETURN_STRING_FROM_FILE = getMethodDescriptor(STRING_TYPE, FILE_TYPE);
-    private static final String RETURN_STRING_FROM_FILE_STRING = getMethodDescriptor(STRING_TYPE, FILE_TYPE, STRING_TYPE);
-    // file.getText(String charset) -(Groovy compiler)-> ResourceGroovyMethods.getText(File, String) -> groovyFileGetText(File, String, String)
-    private static final String RETURN_STRING_FROM_FILE_STRING_STRING = getMethodDescriptor(STRING_TYPE, FILE_TYPE, STRING_TYPE, STRING_TYPE);
-
-    private static final Type FILES_TYPE = getType(Files.class);
-    // readString(Path) -> filesReadString(Path, String)
-    private static final String RETURN_STRING_FROM_PATH = getMethodDescriptor(STRING_TYPE, PATH_TYPE);
-    private static final String RETURN_STRING_FROM_PATH_STRING = getMethodDescriptor(STRING_TYPE, PATH_TYPE, STRING_TYPE);
-    // readString(Path, Charset) -> filesReadString(Path, Charset, String)
-    private static final String RETURN_STRING_FROM_PATH_CHARSET = getMethodDescriptor(STRING_TYPE, PATH_TYPE, CHARSET_TYPE);
-    private static final String RETURN_STRING_FROM_PATH_CHARSET_STRING = getMethodDescriptor(STRING_TYPE, PATH_TYPE, CHARSET_TYPE, STRING_TYPE);
-
     // ProcessBuilder().start() -> start(ProcessBuilder, String)
     private static final String RETURN_PROCESS = getMethodDescriptor(PROCESS_TYPE);
     private static final String RETURN_PROCESS_FROM_PROCESS_BUILDER_STRING = getMethodDescriptor(PROCESS_TYPE, PROCESS_BUILDER_TYPE, STRING_TYPE);
@@ -199,28 +161,6 @@ class InstrumentingTransformer implements CachedClasspathTransformer.Transform {
     private static final String RETURN_PROCESS_FROM_LIST_LIST_FILE = getMethodDescriptor(PROCESS_TYPE, LIST_TYPE, LIST_TYPE, FILE_TYPE);
     private static final String RETURN_PROCESS_FROM_LIST_LIST_FILE_STRING = getMethodDescriptor(PROCESS_TYPE, LIST_TYPE, LIST_TYPE, FILE_TYPE, STRING_TYPE);
 
-    private static final Type FILE_INPUT_STREAM_TYPE = getType(FileInputStream.class);
-    // FileInputStream(File) -> fileOpened(File, String)
-    private static final String RETURN_VOID_FROM_FILE = getMethodDescriptor(Type.VOID_TYPE, FILE_TYPE);
-    private static final String RETURN_VOID_FROM_FILE_STRING = getMethodDescriptor(Type.VOID_TYPE, FILE_TYPE, STRING_TYPE);
-    // FileInputStream(String) -> fileOpened(String, String)
-    private static final String RETURN_VOID_FROM_STRING = getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE);
-    private static final String RETURN_VOID_FROM_STRING_STRING = getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, STRING_TYPE);
-    // File.exists() -> fileExists(File, String)
-    private static final String RETURN_BOOLEAN_FROM_FILE_STRING = getMethodDescriptor(Type.BOOLEAN_TYPE, FILE_TYPE, STRING_TYPE);
-    // File.listFiles() -> fileListFiles(File, String)
-    private static final String RETURN_FILE_ARRAY = getMethodDescriptor(FILE_ARRAY_TYPE);
-    private static final String RETURN_FILE_ARRAY_FROM_FILE_STRING = getMethodDescriptor(FILE_ARRAY_TYPE, FILE_TYPE, STRING_TYPE);
-    // File.listFiles(FileFilter) -> fileListFiles(File, FileFilter, String)
-    private static final String RETURN_FILE_ARRAY_FROM_FILEFILTER = getMethodDescriptor(FILE_ARRAY_TYPE, getType(FileFilter.class));
-    private static final String RETURN_FILE_ARRAY_FROM_FILE_FILEFILTER_STRING = getMethodDescriptor(FILE_ARRAY_TYPE, FILE_TYPE, getType(FileFilter.class), STRING_TYPE);
-    // File.listFiles(FileFilter) -> fileListFiles(File, FileFilter, String)
-    private static final String RETURN_FILE_ARRAY_FROM_FILENAMEFILTER = getMethodDescriptor(FILE_ARRAY_TYPE, getType(FilenameFilter.class));
-    private static final String RETURN_FILE_ARRAY_FROM_FILE_FILENAMEFILTER_STRING = getMethodDescriptor(FILE_ARRAY_TYPE, FILE_TYPE, getType(FilenameFilter.class), STRING_TYPE);
-
-    private static final String LAMBDA_METAFACTORY_TYPE = getType(LambdaMetafactory.class).getInternalName();
-    private static final String LAMBDA_METAFACTORY_METHOD_DESCRIPTOR = getMethodDescriptor(getType(CallSite.class), getType(MethodHandles.Lookup.class), STRING_TYPE, getType(MethodType.class), getType(Object[].class));
-
     private static final String GROOVY_INDY_INTERFACE_TYPE = getType(IndyInterface.class).getInternalName();
 
     @SuppressWarnings("deprecation")
@@ -229,10 +169,6 @@ class InstrumentingTransformer implements CachedClasspathTransformer.Transform {
 
     private static final String INSTRUMENTED_CALL_SITE_METHOD = "$instrumentedCallSiteArray";
     private static final String CREATE_CALL_SITE_ARRAY_METHOD = "$createCallSiteArray";
-    private static final String DESERIALIZE_LAMBDA = "$deserializeLambda$";
-    private static final String RENAMED_DESERIALIZE_LAMBDA = "$renamedDeserializeLambda$";
-
-    private static final String[] NO_EXCEPTIONS = new String[0];
 
     @Override
     public void applyConfigurationTo(Hasher hasher) {
@@ -240,43 +176,54 @@ public void applyConfigurationTo(Hasher hasher) {
         hasher.putInt(DECORATION_FORMAT);
     }
 
+    public InstrumentingTransformer() {
+        this(JvmBytecodeInterceptorSet.DEFAULT);
+    }
+
+    /**
+     * This constructor can be used in tests with a set of call interceptors complemented by ones generated
+     * specifically for the tests.
+     */
+    public InstrumentingTransformer(JvmBytecodeInterceptorSet externalInterceptors) {
+        this.externalInterceptors = externalInterceptors;
+    }
+
     @Override
-    public Pair<RelativePath, ClassVisitor> apply(ClasspathEntryVisitor.Entry entry, ClassVisitor visitor) {
-        return Pair.of(entry.getPath(), new InstrumentingVisitor(new InstrumentingBackwardsCompatibilityVisitor(visitor)));
+    public Pair<RelativePath, ClassVisitor> apply(ClasspathEntryVisitor.Entry entry, ClassVisitor visitor, ClassData classData) {
+        return Pair.of(entry.getPath(), new InstrumentingVisitor(new LambdaSerializationTransformer(new InstrumentingBackwardsCompatibilityVisitor(visitor)), classData::readClassAsNode, externalInterceptors.interceptorClassNames()));
     }
 
     private static class InstrumentingVisitor extends ClassVisitor {
         String className;
-        private final List<LambdaFactoryDetails> lambdaFactories = new ArrayList<>();
+        private final Supplier<ClassNode> classAsNode;
         private boolean hasGroovyCallSites;
-        private boolean hasDeserializeLambda;
-        private boolean isInterface;
+        private final List<String> generatedInterceptorClassNames;
 
-        public InstrumentingVisitor(ClassVisitor visitor) {
+        public InstrumentingVisitor(ClassVisitor visitor, Supplier<ClassNode> classAsNode, List<String> generatedInterceptorClassNames) {
             super(ASM_LEVEL, visitor);
-        }
-
-        public void addSerializedLambda(LambdaFactoryDetails lambdaFactoryDetails) {
-            lambdaFactories.add(lambdaFactoryDetails);
+            this.classAsNode = classAsNode;
+            this.generatedInterceptorClassNames = generatedInterceptorClassNames;
         }
 
         @Override
         public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
             super.visit(version, access, name, signature, superName, interfaces);
             this.className = name;
-            this.isInterface = (access & ACC_INTERFACE) != 0;
         }
 
         @Override
         public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
             if (name.equals(CREATE_CALL_SITE_ARRAY_METHOD) && descriptor.equals(RETURN_CALL_SITE_ARRAY)) {
                 hasGroovyCallSites = true;
-            } else if (name.equals(DESERIALIZE_LAMBDA) && descriptor.equals(RETURN_OBJECT_FROM_SERIALIZED_LAMBDA)) {
-                hasDeserializeLambda = true;
-                return super.visitMethod(access, RENAMED_DESERIALIZE_LAMBDA, descriptor, signature, exceptions);
             }
             MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
-            return new InstrumentingMethodVisitor(this, methodVisitor);
+            Lazy<MethodNode> asMethodNode = Lazy.unsafe().of(() -> {
+                Optional<MethodNode> methodNode = classAsNode.get().methods.stream().filter(method ->
+                    Objects.equals(method.name, name) && Objects.equals(method.desc, descriptor) && Objects.equals(method.signature, signature)
+                ).findFirst();
+                return methodNode.orElseThrow(() -> new IllegalStateException("could not find method " + name + " with descriptor " + descriptor));
+            });
+            return new InstrumentingMethodVisitor(this, methodVisitor, asMethodNode, generatedInterceptorClassNames);
         }
 
         @Override
@@ -284,61 +231,9 @@ public void visitEnd() {
             if (hasGroovyCallSites) {
                 generateCallSiteFactoryMethod();
             }
-            if (!lambdaFactories.isEmpty() || hasDeserializeLambda) {
-                generateLambdaDeserializeMethod();
-            }
             super.visitEnd();
         }
 
-        private void generateLambdaDeserializeMethod() {
-            new MethodVisitorScope(visitStaticPrivateMethod(DESERIALIZE_LAMBDA, RETURN_OBJECT_FROM_SERIALIZED_LAMBDA)) {{
-                Label next = null;
-                for (LambdaFactoryDetails factory : lambdaFactories) {
-                    if (next != null) {
-                        visitLabel(next);
-                        _F_SAME();
-                    }
-                    next = new Label();
-                    Handle implHandle = (Handle) factory.bootstrapMethodArguments.get(1);
-
-                    _ALOAD(0);
-                    _INVOKEVIRTUAL(SERIALIZED_LAMBDA_TYPE, "getImplMethodName", RETURN_STRING);
-                    _LDC(implHandle.getName());
-                    _INVOKEVIRTUAL(OBJECT_TYPE, "equals", RETURN_BOOLEAN_FROM_OBJECT);
-                    _IFEQ(next);
-
-                    _ALOAD(0);
-                    _INVOKEVIRTUAL(SERIALIZED_LAMBDA_TYPE, "getImplMethodSignature", RETURN_STRING);
-                    _LDC(implHandle.getDesc());
-                    _INVOKEVIRTUAL(OBJECT_TYPE, "equals", RETURN_BOOLEAN_FROM_OBJECT);
-                    _IFEQ(next);
-
-                    Type[] argumentTypes = Type.getArgumentTypes(factory.descriptor);
-                    for (int i = 0; i < argumentTypes.length; i++) {
-                        _ALOAD(0);
-                        _LDC(i);
-                        _INVOKEVIRTUAL(SERIALIZED_LAMBDA_TYPE, "getCapturedArg", RETURN_OBJECT_FROM_INT);
-                        _UNBOX(argumentTypes[i]);
-                    }
-                    _INVOKEDYNAMIC(factory.name, factory.descriptor, factory.bootstrapMethodHandle, factory.bootstrapMethodArguments);
-                    _ARETURN();
-                }
-                if (next != null) {
-                    visitLabel(next);
-                    _F_SAME();
-                }
-                if (hasDeserializeLambda) {
-                    _ALOAD(0);
-                    _INVOKESTATIC(className, RENAMED_DESERIALIZE_LAMBDA, RETURN_OBJECT_FROM_SERIALIZED_LAMBDA, isInterface);
-                } else {
-                    _ACONST_NULL();
-                }
-                _ARETURN();
-                visitMaxs(0, 0);
-                visitEnd();
-            }};
-        }
-
         private void generateCallSiteFactoryMethod() {
             new MethodVisitorScope(visitStaticPrivateMethod(INSTRUMENTED_CALL_SITE_METHOD, RETURN_CALL_SITE_ARRAY)) {{
                 _INVOKESTATIC(className, CREATE_CALL_SITE_ARRAY_METHOD, RETURN_CALL_SITE_ARRAY);
@@ -358,11 +253,33 @@ private MethodVisitor visitStaticPrivateMethod(String name, String descriptor) {
     private static class InstrumentingMethodVisitor extends MethodVisitorScope {
         private final InstrumentingVisitor owner;
         private final String className;
+        private final Lazy<MethodNode> asNode;
+        private final List<JvmBytecodeCallInterceptor> externalInterceptors;
 
-        public InstrumentingMethodVisitor(InstrumentingVisitor owner, MethodVisitor methodVisitor) {
+        public InstrumentingMethodVisitor(InstrumentingVisitor owner, MethodVisitor methodVisitor, Lazy<MethodNode> asNode, List<String> externalInterceptors) {
             super(methodVisitor);
             this.owner = owner;
             this.className = owner.className;
+            this.asNode = asNode;
+            this.externalInterceptors = externalInterceptors.stream()
+                .map(className -> newInterceptor(className, methodVisitor))
+                .filter(Optional::isPresent)
+                .map(Optional::get)
+                .collect(toImmutableList());
+        }
+
+        private static Optional<JvmBytecodeCallInterceptor> newInterceptor(String className, MethodVisitor methodVisitor) {
+            try {
+                //noinspection Convert2MethodRef
+                InstrumentationMetadata metadata = (type, superType) -> type.equals(superType); // TODO implement properly
+                Constructor<?> constructor = Class.forName(className).getConstructor(MethodVisitor.class, InstrumentationMetadata.class);
+                return Optional.of((JvmBytecodeCallInterceptor) constructor.newInstance(methodVisitor, metadata));
+            } catch (ClassNotFoundException e) {
+                // No interceptor definition for this class
+                return Optional.empty();
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+                throw new RuntimeException(e);
+            }
         }
 
         @Override
@@ -376,6 +293,12 @@ public void visitMethodInsn(int opcode, String owner, String name, String descri
             if (opcode == INVOKESPECIAL && visitINVOKESPECIAL(owner, name, descriptor)) {
                 return;
             }
+
+            for (JvmBytecodeCallInterceptor generatedInterceptor : externalInterceptors) {
+                if (generatedInterceptor.visitMethodInsn(className, opcode, owner, name, descriptor, isInterface, asNode)) {
+                    return;
+                }
+            }
             super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
         }
 
@@ -466,38 +389,6 @@ private boolean visitINVOKESTATIC(String owner, String name, String descriptor)
                 _LDC(binaryClassNameOf(className));
                 _INVOKESTATIC(INSTRUMENTED_TYPE, "execute", instrumentedDescriptor.get());
                 return true;
-            } else if (owner.equals(KOTLIN_IO_FILES_TYPE.getInternalName())) {
-                if (name.equals("readText") && descriptor.equals(RETURN_STRING_FROM_FILE_CHARSET)) {
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, "kotlinIoFilesKtReadText", RETURN_STRING_FROM_FILE_CHARSET_STRING);
-                    return true;
-                }
-                if (name.equals("readText$default") && descriptor.equals(RETURN_STRING_FROM_FILE_CHARSET_INT_OBJECT)) {
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, "kotlinIoFilesKtReadTextDefault", RETURN_STRING_FROM_FILE_CHARSET_INT_OBJECT_STRING);
-                    return true;
-                }
-            } else if (owner.equals(FILES_TYPE.getInternalName())) {
-                if (name.equals("readString") && descriptor.equals(RETURN_STRING_FROM_PATH)) {
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, "filesReadString", RETURN_STRING_FROM_PATH_STRING);
-                    return true;
-                }
-                if (name.equals("readString") && descriptor.equals(RETURN_STRING_FROM_PATH_CHARSET)) {
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, "filesReadString", RETURN_STRING_FROM_PATH_CHARSET_STRING);
-                    return true;
-                }
-            } else if (owner.equals(RESOURCE_GROOVY_METHODS_TYPE.getInternalName())) {
-                if (name.equals("getText") && descriptor.equals(RETURN_STRING_FROM_FILE)) {
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, "groovyFileGetText", RETURN_STRING_FROM_FILE_STRING);
-                    return true;
-                } else if (name.equals("getText") && descriptor.equals(RETURN_STRING_FROM_FILE_STRING)) {
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, "groovyFileGetText", RETURN_STRING_FROM_FILE_STRING_STRING);
-                    return true;
-                }
             }
             if (owner.equals(PROCESS_BUILDER_TYPE.getInternalName()) && name.equals("startPipeline") && descriptor.equals(RETURN_LIST_FROM_LIST)) {
                 _LDC(binaryClassNameOf(className));
@@ -528,34 +419,6 @@ private boolean visitINVOKEVIRTUAL(String owner, String name, String descriptor)
                     return true;
                 }
             }
-            if (owner.equals(FILE_TYPE.getInternalName())) {
-                if ((name.equals("exists") || name.equals("isFile") || name.equals("isDirectory"))
-                    && descriptor.equals(RETURN_BOOLEAN)
-                ) {
-                    String instrumentedMethodName =
-                        name.equals("exists") ? "fileExists" :
-                            name.equals("isFile") ? "fileIsFile" :
-                                "fileIsDirectory";
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, instrumentedMethodName, RETURN_BOOLEAN_FROM_FILE_STRING);
-                    return true;
-                }
-
-                if (name.equals("listFiles") &&
-                    (descriptor.equals(RETURN_FILE_ARRAY) ||
-                        descriptor.equals(RETURN_FILE_ARRAY_FROM_FILEFILTER) ||
-                        descriptor.equals(RETURN_FILE_ARRAY_FROM_FILENAMEFILTER))
-                ) {
-                    String instrumentedDescriptor =
-                        descriptor.equals(RETURN_FILE_ARRAY) ? RETURN_FILE_ARRAY_FROM_FILE_STRING  :
-                            descriptor.equals(RETURN_FILE_ARRAY_FROM_FILEFILTER) ? RETURN_FILE_ARRAY_FROM_FILE_FILEFILTER_STRING :
-                                RETURN_FILE_ARRAY_FROM_FILE_FILENAMEFILTER_STRING;
-
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, "fileListFiles", instrumentedDescriptor);
-                    return true;
-                }
-            }
             return false;
         }
 
@@ -611,47 +474,12 @@ private Optional<String> getInstrumentedDescriptorForRuntimeExecDescriptor(Strin
         }
 
         private boolean visitINVOKESPECIAL(String owner, String name, String descriptor) {
-            if (owner.equals(FILE_INPUT_STREAM_TYPE.getInternalName()) && name.equals("<init>")) {
-                Optional<String> instrumentedDescriptor = getInstrumentedDescriptorForFileInputStreamConstructor(descriptor);
-                if (instrumentedDescriptor.isPresent()) {
-                    // We are still calling the original constructor instead of replacing it with an instrumented method. The instrumented method is merely a notifier
-                    // there.
-                    _DUP();
-                    _LDC(binaryClassNameOf(className));
-                    _INVOKESTATIC(INSTRUMENTED_TYPE, "fileOpened", instrumentedDescriptor.get());
-                    _INVOKESPECIAL(owner, name, descriptor);
-                    return true;
-                }
-            }
             return false;
         }
 
-        private Optional<String> getInstrumentedDescriptorForFileInputStreamConstructor(String descriptor) {
-            if (descriptor.equals(RETURN_VOID_FROM_FILE)) {
-                return Optional.of(RETURN_VOID_FROM_FILE_STRING);
-            } else if (descriptor.equals(RETURN_VOID_FROM_STRING)) {
-                return Optional.of(RETURN_VOID_FROM_STRING_STRING);
-            }
-            // It is some signature of FileInputStream.<init> that we don't support.
-            return Optional.empty();
-        }
-
         @Override
         public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
-            if (bootstrapMethodHandle.getOwner().equals(LAMBDA_METAFACTORY_TYPE) && bootstrapMethodHandle.getName().equals("metafactory")) {
-                Handle altMethod = new Handle(
-                    H_INVOKESTATIC,
-                    LAMBDA_METAFACTORY_TYPE,
-                    "altMetafactory",
-                    LAMBDA_METAFACTORY_METHOD_DESCRIPTOR,
-                    false
-                );
-                List<Object> args = new ArrayList<>(bootstrapMethodArguments.length + 1);
-                Collections.addAll(args, bootstrapMethodArguments);
-                args.add(LambdaMetafactory.FLAG_SERIALIZABLE);
-                super.visitInvokeDynamicInsn(name, descriptor, altMethod, args.toArray());
-                owner.addSerializedLambda(new LambdaFactoryDetails(name, descriptor, altMethod, args));
-            } else if (isGroovyIndyCallsite(descriptor, bootstrapMethodHandle)) {
+            if (isGroovyIndyCallsite(bootstrapMethodHandle)) {
                 Handle interceptor = new Handle(
                     H_INVOKESTATIC,
                     INSTRUMENTED_TYPE.getInternalName(),
@@ -665,41 +493,15 @@ public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootst
             }
         }
 
-        private boolean isGradleLambdaDescriptor(String descriptor) {
-            return descriptor.endsWith(ACTION_LAMBDA_SUFFIX)
-                || descriptor.endsWith(SPEC_LAMBDA_SUFFIX)
-                || descriptor.endsWith(TRANSFORMER_LAMBDA_SUFFIX)
-                || descriptor.endsWith(COMMAND_LINE_ARGUMENT_PROVIDER_LAMBDA_SUFFIX);
-        }
-
         private String binaryClassNameOf(String className) {
             return getObjectType(className).getClassName();
         }
 
-        private boolean isGroovyIndyCallsite(String descriptor, Handle bootstrapMethodHandle) {
+        private boolean isGroovyIndyCallsite(Handle bootstrapMethodHandle) {
             return (bootstrapMethodHandle.getOwner().equals(GROOVY_INDY_INTERFACE_TYPE) ||
                 bootstrapMethodHandle.getOwner().equals(GROOVY_INDY_INTERFACE_V7_TYPE)) &&
                 bootstrapMethodHandle.getName().equals("bootstrap") &&
                 bootstrapMethodHandle.getDesc().equals(GROOVY_INDY_INTERFACE_BOOTSTRAP_METHOD_DESCRIPTOR);
         }
-
-        private static final String ACTION_LAMBDA_SUFFIX = ")" + getType(Action.class).getDescriptor();
-        private static final String SPEC_LAMBDA_SUFFIX = ")" + getType(Spec.class).getDescriptor();
-        private static final String TRANSFORMER_LAMBDA_SUFFIX = ")" + getType(Transformer.class).getDescriptor();
-        private static final String COMMAND_LINE_ARGUMENT_PROVIDER_LAMBDA_SUFFIX = ")" + getType(CommandLineArgumentProvider.class).getDescriptor();
-    }
-
-    private static class LambdaFactoryDetails {
-        final String name;
-        final String descriptor;
-        final Handle bootstrapMethodHandle;
-        final List<?> bootstrapMethodArguments;
-
-        public LambdaFactoryDetails(String name, String descriptor, Handle bootstrapMethodHandle, List<?> bootstrapMethodArguments) {
-            this.name = name;
-            this.descriptor = descriptor;
-            this.bootstrapMethodHandle = bootstrapMethodHandle;
-            this.bootstrapMethodArguments = bootstrapMethodArguments;
-        }
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/JvmBytecodeInterceptorSet.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/JvmBytecodeInterceptorSet.java
new file mode 100644
index 0000000..96a118d
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/JvmBytecodeInterceptorSet.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import org.gradle.api.NonNullApi;
+import org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Provides a set of implementation classes for call interception in JVM bytecode, specified by the full class name as in {@link Class#getName()}.
+ * The referenced classes should implement {@link org.gradle.internal.instrumentation.api.jvmbytecode.JvmBytecodeCallInterceptor}.
+ */
+@NonNullApi
+public interface JvmBytecodeInterceptorSet {
+    JvmBytecodeInterceptorSet DEFAULT = () -> InterceptorDeclaration.JVM_BYTECODE_GENERATED_CLASS_NAMES;
+
+    List<String> interceptorClassNames();
+
+    default JvmBytecodeInterceptorSet plus(JvmBytecodeInterceptorSet other) {
+        return () -> Stream.of(this, other).flatMap(it -> it.interceptorClassNames().stream()).distinct().collect(Collectors.toList());
+    }
+}
+
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/LambdaSerializationTransformer.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/LambdaSerializationTransformer.java
new file mode 100644
index 0000000..31981a5
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/LambdaSerializationTransformer.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.PeekingIterator;
+import org.gradle.api.InvalidUserCodeException;
+import org.gradle.api.NonNullApi;
+import org.gradle.internal.classanalysis.AsmConstants;
+import org.gradle.model.internal.asm.MethodVisitorScope;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.CodeSizeEvaluator;
+
+import javax.annotation.Nullable;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.SerializedLambda;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.gradle.internal.classpath.CommonTypes.NO_EXCEPTIONS;
+import static org.gradle.internal.classpath.CommonTypes.OBJECT_TYPE;
+import static org.gradle.internal.classpath.CommonTypes.STRING_TYPE;
+import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
+import static org.objectweb.asm.Type.getMethodDescriptor;
+import static org.objectweb.asm.Type.getType;
+
+/**
+ * A class visitor that makes all lambdas in code serializable.
+ * It adds {@link LambdaMetafactory#FLAG_SERIALIZABLE} to all bootstrap methods that create lambdas and generate synthetic deserialization method in the processed class.
+ * If deserialization method is already present, it is renamed and the new implementation falls back to it.
+ */
+@NonNullApi
+class LambdaSerializationTransformer extends ClassVisitor {
+    // The method's bytecode must be less than MAX_CODE_SIZE bytes in length.
+    private static final int MAX_CODE_SIZE = 65536;
+
+    private static final Type SERIALIZED_LAMBDA_TYPE = getType(SerializedLambda.class);
+
+    private static final String RETURN_OBJECT_FROM_SERIALIZED_LAMBDA = getMethodDescriptor(OBJECT_TYPE, SERIALIZED_LAMBDA_TYPE);
+    private static final String RETURN_STRING = getMethodDescriptor(CommonTypes.STRING_TYPE);
+    private static final String RETURN_BOOLEAN_FROM_OBJECT = getMethodDescriptor(Type.BOOLEAN_TYPE, OBJECT_TYPE);
+    private static final String RETURN_OBJECT_FROM_INT = getMethodDescriptor(OBJECT_TYPE, Type.INT_TYPE);
+
+    private static final String LAMBDA_METAFACTORY_TYPE = getType(LambdaMetafactory.class).getInternalName();
+    private static final String LAMBDA_METAFACTORY_METHOD_DESCRIPTOR = getMethodDescriptor(
+        getType(CallSite.class),
+        getType(MethodHandles.Lookup.class),
+        STRING_TYPE,
+        getType(MethodType.class),
+        getType(Object[].class));
+
+    private static final String DESERIALIZE_LAMBDA = "$deserializeLambda$";
+    private static final String RENAMED_DESERIALIZE_LAMBDA = "$renamedDeserializeLambda$";
+
+
+    private final List<LambdaFactoryDetails> lambdaFactories = new ArrayList<>();
+    private String className;
+    private boolean hasDeserializeLambda;
+    private boolean isInterface;
+
+    protected LambdaSerializationTransformer(ClassVisitor classVisitor) {
+        super(AsmConstants.ASM_LEVEL, classVisitor);
+    }
+
+    @Override
+    public void visit(int version, int access, String name, @Nullable String signature, @Nullable String superName, @Nullable String[] interfaces) {
+        super.visit(version, access, name, signature, superName, interfaces);
+        this.className = name;
+        this.isInterface = (access & ACC_INTERFACE) != 0;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String descriptor, @Nullable String signature, @Nullable String[] exceptions) {
+        if (name.equals(DESERIALIZE_LAMBDA) && descriptor.equals(RETURN_OBJECT_FROM_SERIALIZED_LAMBDA)) {
+            hasDeserializeLambda = true;
+            return super.visitMethod(access, RENAMED_DESERIALIZE_LAMBDA, descriptor, signature, exceptions);
+        }
+        return new LambdaFactoryCallRewriter(super.visitMethod(access, name, descriptor, signature, exceptions));
+    }
+
+    @Override
+    public void visitEnd() {
+        if (!lambdaFactories.isEmpty() || hasDeserializeLambda) {
+            generateLambdaDeserializeMethod();
+        }
+        super.visitEnd();
+    }
+
+    private void generateLambdaDeserializeMethod() {
+        // There may be so many lambdas, the resulting deserialization method wouldn't fit into the JVM size limits.
+        // If this happens, we split the generated method into a chain of multiple.
+        // The first split is always $deserializeLambda$. It processes as many lambdas as possible,
+        // and if there are too many, it calls another split method $deserializeLambda0$ as its last operation.
+        // $deserializeLambda0$ may call $deserializeLambda1$ if needed, and so on.
+        PeekingIterator<LambdaFactoryDetails> factoriesIterator = Iterators.peekingIterator(lambdaFactories.iterator());
+        int nextSplitMethodIndex = 0;
+        String currentSplitMethodName = DESERIALIZE_LAMBDA;
+        do {
+            String nextSplitMethodName = String.format("$deserializeLambda%d$", nextSplitMethodIndex);
+            generateSplitLambdaDeserializeMethod(currentSplitMethodName, nextSplitMethodName, factoriesIterator);
+            currentSplitMethodName = nextSplitMethodName;
+            ++nextSplitMethodIndex;
+        } while (factoriesIterator.hasNext());
+    }
+
+    private void generateSplitLambdaDeserializeMethod(String methodName, String nextSplitMethodName, PeekingIterator<LambdaFactoryDetails> factoriesIterator) {
+        CodeSizeEvaluator sizeEvaluator = new CodeSizeEvaluator(visitStaticPrivateMethod(methodName, RETURN_OBJECT_FROM_SERIALIZED_LAMBDA));
+        new MethodVisitorScope(sizeEvaluator) {{
+            Label next = null;
+            while (factoriesIterator.hasNext()) {
+                LambdaFactoryDetails factory = factoriesIterator.peek();
+                Type[] argumentTypes = Type.getArgumentTypes(factory.descriptor);
+
+                int codeSizeSoFar = sizeEvaluator.getMaxSize();
+                if (codeSizeSoFar + getEstimatedSingleLambdaHandlingCodeLength(argumentTypes) + getEstimatedEpilogueLength() >= MAX_CODE_SIZE) {
+                    // In theory, it is possible to have a lambda so big, its handling won't fit in a single method, and such a lambda will cause an infinite loop here.
+                    // However, the number of captured lambda variables is limited by the max number of method arguments allowed by the JVM.
+                    // This limit is 255 arguments as of Java 20. Deserializing that many is not a problem for the current implementation.
+                    // The check is here as a future-proofing measure, in case the limit is relaxed or the generated code size grows.
+                    if (codeSizeSoFar == 0) {
+                        // This is the first lambda to process in this method, but it doesn't fit already - cannot proceed.
+                        throw new InvalidUserCodeException(
+                            "Cannot generate the deserialization method for class " + className
+                                + " because lambda implementation " + factory.name + " has too many captured arguments (" + argumentTypes.length + ")");
+                    }
+                    break;
+                }
+
+                // Current lambda seems to fit, remove it from the sequence.
+                factoriesIterator.next();
+
+                if (next != null) {
+                    visitLabel(next);
+                    _F_SAME();
+                }
+                next = new Label();
+                Handle implHandle = (Handle) factory.bootstrapMethodArguments.get(1);
+
+                // Handling of a single lambda.
+                // Let's estimate the generated bytecode size. When changing the code, don't forget to update the estimation in
+                // getEstimatedSingleLambdaHandlingCodeLength.
+                // * Each ALOAD_0 is 1 byte.
+                // * INVOKEVIRTUAL and INVOKESTATIC are 3 bytes.
+                // * INVOKEDYNAMIC is 5.
+                // * LDC can be 2 or 3 bytes (in which case it is actually LDC_W, but ASM hides this from us).
+                //   It depends on how big is the argument, and we have no control over this, so let's be pessimistic.
+                // * IFEQ itself is 3 bytes. However, ASM's CodeSizeEvaluator gives an upper bound of 8, because offsets
+                //   that don't fit in SHORT type have to be encoded differently. We're unlikely to encounter such offsets in this
+                //   code, but it is better to be consistent with the CodeSizeEvaluator.
+                // * _UNBOX takes 3 bytes if the target type is a reference and 6 if it is a primitive.
+                // * ARETURN is 1 byte
+                // * ACONST_NULL is one byte
+                // Labels aren't represented in the code.
+
+                _ALOAD(0);
+                _INVOKEVIRTUAL(SERIALIZED_LAMBDA_TYPE, "getImplMethodName", RETURN_STRING);
+                _LDC(implHandle.getName());
+                _INVOKEVIRTUAL(OBJECT_TYPE, "equals", RETURN_BOOLEAN_FROM_OBJECT);
+                _IFEQ(next);
+
+                _ALOAD(0);
+                _INVOKEVIRTUAL(SERIALIZED_LAMBDA_TYPE, "getImplMethodSignature", RETURN_STRING);
+                _LDC(implHandle.getDesc());
+                _INVOKEVIRTUAL(OBJECT_TYPE, "equals", RETURN_BOOLEAN_FROM_OBJECT);
+                _IFEQ(next);
+                // SerializedLambda check takes at most 36 bytes.
+
+                // Primitive argument handling takes 13 bytes per argument, reference takes 10 bytes.
+                // When changing this, update getEstimatedArgumentHandlingCodeLength.
+                for (int i = 0; i < argumentTypes.length; i++) {
+                    _ALOAD(0);
+                    _LDC(i);
+                    _INVOKEVIRTUAL(SERIALIZED_LAMBDA_TYPE, "getCapturedArg", RETURN_OBJECT_FROM_INT);
+                    _UNBOX(argumentTypes[i]);
+                }
+
+                _INVOKEDYNAMIC(factory.name, factory.descriptor, factory.bootstrapMethodHandle, factory.bootstrapMethodArguments);
+                _ARETURN();
+                // Creating the lambda and returning it take 6 bytes more, so 42 bytes + argument handling cost in total for this lambda.
+            }
+            if (next != null) {
+                visitLabel(next);
+                _F_SAME();
+            }
+            // Epilogue, its estimation is in getEstimatedEpilogueLength.
+            if (factoriesIterator.hasNext()) {
+                // We failed to fit all remaining lambdas in this method, a new split has to be generated,
+                // and this method must delegate to it.
+                _ALOAD(0);
+                _INVOKESTATIC(className, nextSplitMethodName, RETURN_OBJECT_FROM_SERIALIZED_LAMBDA, isInterface);
+            } else if (hasDeserializeLambda) {
+                _ALOAD(0);
+                _INVOKESTATIC(className, RENAMED_DESERIALIZE_LAMBDA, RETURN_OBJECT_FROM_SERIALIZED_LAMBDA, isInterface);
+            } else {
+                _ACONST_NULL();
+            }
+            _ARETURN();
+            // Epilogue takes 5 bytes if we need to call the other method (next split or original), or 2 bytes to just return null.
+            visitMaxs(0, 0);
+            visitEnd();
+        }};
+    }
+
+    int getEstimatedDeserializationPrologueLength() {
+        return 0;
+    }
+
+    // Estimated length of the $deserializeLambda*$ method's epilogue that calls the renamed original $deserializeLambda$ or the next split method.
+    int getEstimatedEpilogueLength() {
+        return hasDeserializeLambda ? 5 : 2;
+    }
+
+    int getEstimatedSingleLambdaHandlingCodeLength(Type[] arguments) {
+        // See the generateSplitLambdaDeserializeMethod to find where this number comes from.
+        int nonArgumentCodeSize = 42;
+        int argumentHandlingLength = 0;
+        for (Type arg : arguments) {
+            argumentHandlingLength += getEstimatedArgumentHandlingCodeLength(arg);
+        }
+        return nonArgumentCodeSize + argumentHandlingLength;
+    }
+
+    private static int getEstimatedArgumentHandlingCodeLength(Type argument) {
+        int loadSize = 7;  // size of SerializedLambda.getCapturedArg(<n>) call
+        // unboxing of a primitive adds "invokevirtual" to "checkcast".
+        int unboxingSize = isPrimitiveArgument(argument) ? 6 : 3;
+        return loadSize + unboxingSize;
+    }
+
+    private static boolean isPrimitiveArgument(Type argument) {
+        switch (argument.getSort()) {
+            case Type.BOOLEAN:
+            case Type.CHAR:
+            case Type.SHORT:
+            case Type.INT:
+            case Type.FLOAT:
+            case Type.LONG:
+            case Type.DOUBLE:
+                return true;
+        }
+        return false;
+    }
+
+    private MethodVisitor visitStaticPrivateMethod(String name, String descriptor) {
+        return super.visitMethod(ACC_STATIC | ACC_SYNTHETIC | ACC_PRIVATE, name, descriptor, null, NO_EXCEPTIONS);
+    }
+
+    private void addSerializedLambda(LambdaFactoryDetails lambdaFactoryDetails) {
+        lambdaFactories.add(lambdaFactoryDetails);
+    }
+
+    @NonNullApi
+    private class LambdaFactoryCallRewriter extends MethodVisitorScope {
+
+        public LambdaFactoryCallRewriter(@Nullable MethodVisitor methodVisitor) {
+            super(methodVisitor);
+        }
+
+        @Override
+        public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
+            if (bootstrapMethodHandle.getOwner().equals(LAMBDA_METAFACTORY_TYPE) && bootstrapMethodHandle.getName().equals("metafactory")) {
+                Handle altMethod = new Handle(
+                    H_INVOKESTATIC,
+                    LAMBDA_METAFACTORY_TYPE,
+                    "altMetafactory",
+                    LAMBDA_METAFACTORY_METHOD_DESCRIPTOR,
+                    false
+                );
+                List<Object> args = new ArrayList<>(bootstrapMethodArguments.length + 1);
+                Collections.addAll(args, bootstrapMethodArguments);
+                args.add(LambdaMetafactory.FLAG_SERIALIZABLE);
+                super.visitInvokeDynamicInsn(name, descriptor, altMethod, args.toArray());
+                addSerializedLambda(new LambdaFactoryDetails(name, descriptor, altMethod, args));
+            } else {
+                super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
+            }
+        }
+    }
+
+    @NonNullApi
+    private static class LambdaFactoryDetails {
+        final String name;
+        final String descriptor;
+        final Handle bootstrapMethodHandle;
+        final List<?> bootstrapMethodArguments;
+
+        public LambdaFactoryDetails(String name, String descriptor, Handle bootstrapMethodHandle, List<?> bootstrapMethodArguments) {
+            this.name = name;
+            this.descriptor = descriptor;
+            this.bootstrapMethodHandle = bootstrapMethodHandle;
+            this.bootstrapMethodArguments = bootstrapMethodArguments;
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/MethodHandleUtils.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/MethodHandleUtils.java
new file mode 100644
index 0000000..7a61c51
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/MethodHandleUtils.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import org.gradle.internal.Cast;
+import org.gradle.internal.lazy.Lazy;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+
+public class MethodHandleUtils {
+    public static MethodHandle findStaticOrThrowError(Class<?> refc, String name, MethodType methodType) {
+        try {
+            return MethodHandles.lookup().findStatic(refc, name, methodType);
+        } catch (NoSuchMethodException e) {
+            throw new NoSuchMethodError(e.getMessage());
+        } catch (IllegalAccessException e) {
+            throw new IllegalAccessError(e.getMessage());
+        }
+    }
+
+    public static <T> T invokeKotlinStaticDefault(Lazy<MethodHandle> handle, int mask, Object... args) throws Throwable {
+        Object[] argsWithDefaultMaskAndFlag = Arrays.copyOf(args, args.length + 2);
+        argsWithDefaultMaskAndFlag[args.length] = mask;
+        argsWithDefaultMaskAndFlag[args.length + 1] = null; // it's already there, but let's be clear
+        return Cast.uncheckedCast(handle.get().invokeWithArguments(argsWithDefaultMaskAndFlag));
+    }
+
+    public static Lazy<MethodHandle> lazyKotlinStaticDefaultHandle(
+        Class<?> owner, String name, Class<?> returnType, Class<?>... parameterTypes
+    ) {
+        return Lazy.locking().of(() -> findStaticOrThrowError(owner, name + "$default", kotlinDefaultMethodType(returnType, parameterTypes)));
+    }
+
+    private static MethodType kotlinDefaultMethodType(Class<?> returnType, Class<?>... parameterTypes) {
+        Class<?>[] defaultParameterTypes = Arrays.copyOf(parameterTypes, parameterTypes.length + 2);
+        defaultParameterTypes[parameterTypes.length] = int.class; // default value mask
+        defaultParameterTypes[parameterTypes.length + 1] = Object.class; // default signature marker
+        return MethodType.methodType(returnType, defaultParameterTypes);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/FileInputStreamInterceptorsDeclaration.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/FileInputStreamInterceptorsDeclaration.java
new file mode 100644
index 0000000..b524710
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/FileInputStreamInterceptorsDeclaration.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 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.classpath.declarations;
+
+import org.gradle.internal.classpath.Instrumented;
+import org.gradle.internal.instrumentation.api.annotations.SpecificGroovyCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.SpecificJvmCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind.AfterConstructor;
+import org.gradle.internal.instrumentation.api.annotations.InterceptCalls;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.CallerClassName;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.Receiver;
+import org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+@SpecificJvmCallInterceptors(generatedClassName = InterceptorDeclaration.JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+@SpecificGroovyCallInterceptors(generatedClassName = InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+public class FileInputStreamInterceptorsDeclaration {
+
+    @InterceptCalls
+    @AfterConstructor
+    public static void interceptFileInputStreamConstructor(
+        @Receiver FileInputStream receiver,
+        File file,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.fileOpened(file, consumer);
+    }
+
+    @InterceptCalls
+    @AfterConstructor
+    public static void interceptFileInputStreamConstructor(
+        @Receiver FileInputStream receiver,
+        String path,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.fileOpened(path, consumer);
+    }
+}
+
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/FileInterceptorsDeclaration.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/FileInterceptorsDeclaration.java
new file mode 100644
index 0000000..7c7df0e
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/FileInterceptorsDeclaration.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2023 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.classpath.declarations;
+
+import org.gradle.internal.classpath.Instrumented;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind.InstanceMethod;
+import org.gradle.internal.instrumentation.api.annotations.SpecificGroovyCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.SpecificJvmCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.InterceptCalls;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.CallerClassName;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.Receiver;
+import org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FilenameFilter;
+
+@SuppressWarnings("NewMethodNamingConvention")
+@SpecificJvmCallInterceptors(generatedClassName = InterceptorDeclaration.JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+@SpecificGroovyCallInterceptors(generatedClassName = InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+public class FileInterceptorsDeclaration {
+    @InterceptCalls
+    @InstanceMethod
+    public static File[] intercept_listFiles(
+        @Receiver File thisFile,
+        @CallerClassName String consumer
+    ) {
+        return Instrumented.fileListFiles(thisFile, consumer);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static File[] intercept_listFiles(
+        @Receiver File thisFile,
+        FileFilter fileFilter,
+        @CallerClassName String consumer
+    ) {
+        return Instrumented.fileListFiles(thisFile, fileFilter, consumer);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static File[] intercept_listFiles(
+        @Receiver File thisFile,
+        FilenameFilter fileFilter,
+        @CallerClassName String consumer
+    ) {
+        return Instrumented.fileListFiles(thisFile, fileFilter, consumer);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static boolean intercept_exists(
+        @Receiver File thisFile,
+        @CallerClassName String consumer
+    ) {
+        return Instrumented.fileExists(thisFile, consumer);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static boolean intercept_isFile(
+        @Receiver File thisFile,
+        @CallerClassName String consumer
+    ) {
+        return Instrumented.fileIsFile(thisFile, consumer);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static boolean intercept_isDirectory(
+        @Receiver File thisFile,
+        @CallerClassName String consumer
+    ) {
+        return Instrumented.fileIsDirectory(thisFile, consumer);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static String[] intercept_list(
+        @Receiver File thisFile,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.directoryContentObserved(thisFile, consumer);
+        return thisFile.list();
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static String[] intercept_list(
+        @Receiver File thisFile,
+        FilenameFilter filenameFilter,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.directoryContentObserved(thisFile, consumer);
+        return thisFile.list(filenameFilter);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static long intercept_length(
+        @Receiver File thisFile,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.fileOpened(thisFile, consumer);
+        return thisFile.length();
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/GroovyFileInterceptors.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/GroovyFileInterceptors.java
new file mode 100644
index 0000000..062ec74
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/GroovyFileInterceptors.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright 2023 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.classpath.declarations;
+
+import groovy.io.FileType;
+import groovy.lang.Closure;
+import org.codehaus.groovy.runtime.ResourceGroovyMethods;
+import org.gradle.internal.classpath.Instrumented;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind.GroovyProperty;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind.InstanceMethod;
+import org.gradle.internal.instrumentation.api.annotations.InterceptGroovyCalls;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.CallerClassName;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.Receiver;
+import org.gradle.internal.instrumentation.api.annotations.SpecificGroovyCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.SpecificJvmCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.features.withstaticreference.WithExtensionReferences;
+import org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.util.List;
+import java.util.regex.Pattern;
+
+@SuppressWarnings("NewMethodNamingConvention")
+@SpecificJvmCallInterceptors(generatedClassName = InterceptorDeclaration.JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+@SpecificGroovyCallInterceptors(generatedClassName = InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+public class GroovyFileInterceptors {
+    @InterceptGroovyCalls
+    @GroovyProperty
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static String intercept_text(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) throws IOException {
+        return Instrumented.groovyFileGetText(self, consumer);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static String intercept_getText(
+        @Receiver File self,
+        String charset,
+        @CallerClassName String consumer
+    ) throws IOException {
+        return Instrumented.groovyFileGetText(self, charset, consumer);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachByte(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        ResourceGroovyMethods.eachByte(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachByte(
+        @Receiver File self,
+        int bufferLen,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        ResourceGroovyMethods.eachByte(self, bufferLen, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachDir(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.directoryContentObserved(self, consumer);
+        ResourceGroovyMethods.eachDir(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachDirMatch(
+        @Receiver File self,
+        Object nameFilter,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.directoryContentObserved(self, consumer);
+        ResourceGroovyMethods.eachDirMatch(self, nameFilter, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachFile(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.directoryContentObserved(self, consumer);
+        ResourceGroovyMethods.eachFile(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachFile(
+        @Receiver File self,
+        FileType fileType,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.directoryContentObserved(self, consumer);
+        ResourceGroovyMethods.eachFile(self, fileType, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachFileMatch(
+        @Receiver File self,
+        Object nameFilter,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.directoryContentObserved(self, consumer);
+        ResourceGroovyMethods.eachFileMatch(self, nameFilter, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachFileMatch(
+        @Receiver File self,
+        FileType fileType,
+        Object nameFilter,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.directoryContentObserved(self, consumer);
+        ResourceGroovyMethods.eachFileMatch(self, fileType, nameFilter, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachLine(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        ResourceGroovyMethods.eachLine(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachLine(
+        @Receiver File self,
+        String charset,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        ResourceGroovyMethods.eachLine(self, charset, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachLine(
+        @Receiver File self,
+        int firstLine,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        ResourceGroovyMethods.eachLine(self, firstLine, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachLine(
+        @Receiver File self,
+        String charset,
+        int firstLine,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        ResourceGroovyMethods.eachLine(self, charset, firstLine, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static void intercept_eachObject(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException, ClassNotFoundException {
+        Instrumented.fileOpened(self, consumer);
+        ResourceGroovyMethods.eachObject(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @GroovyProperty
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static byte[] intercept_bytes(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.getBytes(self);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static InputStream intercept_newInputStream(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.newInputStream(self);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static DataInputStream intercept_newDataInputStream(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.newDataInputStream(self);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static ObjectInputStream intercept_newObjectInputStream(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.newObjectInputStream(self);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static ObjectInputStream intercept_newObjectInputStream(
+        @Receiver File self,
+        ClassLoader classLoader,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.newObjectInputStream(self, classLoader);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static BufferedReader intercept_newReader(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.newReader(self);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static BufferedReader intercept_newReader(
+        @Receiver File self,
+        String charset,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.newReader(self, charset);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static byte[] intercept_readBytes(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.readBytes(self);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static List<String> intercept_readLines(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.readLines(self);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static List<String> intercept_readLines(
+        @Receiver File self,
+        String charset,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.readLines(self, charset);
+    }
+
+    @InterceptGroovyCalls
+    @GroovyProperty
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static long intercept_size(
+        @Receiver File self,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.size(self);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_splitEachLine(
+        @Receiver File self,
+        Pattern pattern,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.splitEachLine(self, pattern, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_splitEachLine(
+        @Receiver File self,
+        Pattern pattern,
+        String charset,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.splitEachLine(self, pattern, charset, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_splitEachLine(
+        @Receiver File self,
+        String regex,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.splitEachLine(self, regex, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_splitEachLine(
+        @Receiver File self,
+        String regex,
+        String charset,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.splitEachLine(self, regex, charset, closure);
+    }
+
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_withInputStream(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.withInputStream(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_withDataInputStream(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.withDataInputStream(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_withObjectInputStream(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.withObjectInputStream(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_withObjectInputStream(
+        @Receiver File self,
+        ClassLoader classLoader,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.withObjectInputStream(self, classLoader, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_withReader(
+        @Receiver File self,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.withReader(self, closure);
+    }
+
+    @InterceptGroovyCalls
+    @InstanceMethod
+    @WithExtensionReferences(toClass = ResourceGroovyMethods.class)
+    public static Object intercept_withReader(
+        @Receiver File self,
+        String charset,
+        Closure<?> closure,
+        @CallerClassName String consumer
+    ) throws IOException {
+        Instrumented.fileOpened(self, consumer);
+        return ResourceGroovyMethods.withReader(self, charset, closure);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/KotlinFileExtensionsInterceptorsDeclaration.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/KotlinFileExtensionsInterceptorsDeclaration.java
new file mode 100644
index 0000000..52d38c0
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/KotlinFileExtensionsInterceptorsDeclaration.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 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.classpath.declarations;
+
+import kotlin.io.FilesKt;
+import org.gradle.internal.classpath.Instrumented;
+import org.gradle.internal.instrumentation.api.annotations.SpecificGroovyCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.SpecificJvmCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind.StaticMethod;
+import org.gradle.internal.instrumentation.api.annotations.InterceptCalls;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.CallerClassName;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.KotlinDefaultMask;
+import org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+@SuppressWarnings("NewMethodNamingConvention")
+@SpecificJvmCallInterceptors(generatedClassName = InterceptorDeclaration.JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+@SpecificGroovyCallInterceptors(generatedClassName = InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+public class KotlinFileExtensionsInterceptorsDeclaration {
+
+    @InterceptCalls
+    @StaticMethod(ofClass = FilesKt.class)
+    public static String intercept_readText(
+        File thisFile,
+        Charset charset,
+        @KotlinDefaultMask int mask,
+        @CallerClassName String consumer
+    ) throws Throwable {
+        return mask != 0 ?
+            Instrumented.kotlinIoFilesKtReadTextDefault(thisFile, charset, mask, null, consumer) :
+            Instrumented.kotlinIoFilesKtReadText(thisFile, charset, consumer);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/KotlinStdlibFileInterceptors.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/KotlinStdlibFileInterceptors.java
new file mode 100644
index 0000000..7f6a95f
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/KotlinStdlibFileInterceptors.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2023 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.classpath.declarations;
+
+import kotlin.io.FilesKt;
+import kotlin.jvm.functions.Function1;
+import kotlin.jvm.functions.Function2;
+import org.gradle.internal.Cast;
+import org.gradle.internal.classpath.Instrumented;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind.StaticMethod;
+import org.gradle.internal.instrumentation.api.annotations.InterceptCalls;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.CallerClassName;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.KotlinDefaultMask;
+import org.gradle.internal.instrumentation.api.annotations.SpecificGroovyCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.SpecificJvmCallInterceptors;
+import org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration;
+import org.gradle.internal.lazy.Lazy;
+
+import java.io.File;
+import java.lang.invoke.MethodHandle;
+import java.nio.charset.Charset;
+import java.util.List;
+
+import static org.gradle.internal.classpath.MethodHandleUtils.invokeKotlinStaticDefault;
+import static org.gradle.internal.classpath.MethodHandleUtils.lazyKotlinStaticDefaultHandle;
+import static org.gradle.internal.classpath.declarations.Handles.FOR_EACH_LINE_DEFAULT;
+import static org.gradle.internal.classpath.declarations.Handles.READ_LINES_DEFAULT;
+import static org.gradle.internal.classpath.declarations.Handles.USE_LINES_DEFAULT;
+
+@SuppressWarnings("NewMethodNamingConvention")
+@SpecificJvmCallInterceptors(generatedClassName = InterceptorDeclaration.JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+@SpecificGroovyCallInterceptors(generatedClassName = InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+public class KotlinStdlibFileInterceptors {
+    @InterceptCalls
+    @StaticMethod(ofClass = FilesKt.class)
+    public static byte[] intercept_readBytes(
+        File self,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.fileOpened(self, consumer);
+        return FilesKt.readBytes(self);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = FilesKt.class)
+    public static void intercept_forEachBlock(
+        File self,
+        Function2<?, ?, ?> action,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.fileOpened(self, consumer);
+        FilesKt.forEachBlock(self, Cast.uncheckedNonnullCast(action));
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = FilesKt.class)
+    public static void intercept_forEachBlock(
+        File self,
+        int blockSize,
+        Function2<?, ?, ?> action,
+        @CallerClassName String consumer
+    ) {
+        Instrumented.fileOpened(self, consumer);
+        FilesKt.forEachBlock(self, blockSize, Cast.uncheckedNonnullCast(action));
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = FilesKt.class)
+    public static void intercept_forEachLine(
+        File self,
+        Charset charset,
+        Function1<?, ?> action,
+        @KotlinDefaultMask int mask,
+        @CallerClassName String consumer
+    ) throws Throwable {
+        Instrumented.fileOpened(self, consumer);
+        if (mask == 0) {
+            FilesKt.forEachLine(self, charset, Cast.uncheckedNonnullCast(action));
+        } else {
+            invokeKotlinStaticDefault(FOR_EACH_LINE_DEFAULT, mask, self, charset, action);
+        }
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = FilesKt.class)
+    public static List<String> intercept_readLines(
+        File self,
+        Charset charset,
+        @KotlinDefaultMask int mask,
+        @CallerClassName String consumer
+    ) throws Throwable {
+        Instrumented.fileOpened(self, consumer);
+        return mask == 0
+            ? FilesKt.readLines(self, charset)
+            : invokeKotlinStaticDefault(READ_LINES_DEFAULT, mask, self, charset);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = FilesKt.class)
+    public static Object intercept_useLines(
+        File self,
+        Charset charset,
+        Function1<?, ?> block,
+        @KotlinDefaultMask int mask,
+        @CallerClassName String consumer
+    ) throws Throwable {
+        Instrumented.fileOpened(self, consumer);
+        return mask == 0
+            ? FilesKt.useLines(self, charset, Cast.uncheckedNonnullCast(block))
+            : invokeKotlinStaticDefault(USE_LINES_DEFAULT, mask, self, charset, block);
+    }
+
+}
+
+class Handles {
+    public static final Lazy<MethodHandle> FOR_EACH_LINE_DEFAULT =
+        lazyKotlinStaticDefaultHandle(FilesKt.class, "forEachLine", void.class, File.class, Charset.class, Function1.class);
+
+    public static final Lazy<MethodHandle> READ_LINES_DEFAULT =
+        lazyKotlinStaticDefaultHandle(FilesKt.class, "readLines", List.class, File.class, Charset.class);
+
+    public static final Lazy<MethodHandle> USE_LINES_DEFAULT =
+        lazyKotlinStaticDefaultHandle(FilesKt.class, "useLines", Object.class, File.class, Charset.class, Function1.class);
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/NioFileInterceptors.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/NioFileInterceptors.java
new file mode 100644
index 0000000..d61011c
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/declarations/NioFileInterceptors.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2023 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.classpath.declarations;
+
+import org.gradle.internal.Cast;
+import org.gradle.internal.classpath.Instrumented;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind.InstanceMethod;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind.StaticMethod;
+import org.gradle.internal.instrumentation.api.annotations.InterceptCalls;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.CallerClassName;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.Receiver;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind.VarargParameter;
+import org.gradle.internal.instrumentation.api.annotations.SpecificGroovyCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.SpecificJvmCallInterceptors;
+import org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.spi.FileSystemProvider;
+import java.nio.file.spi.FileTypeDetector;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static org.gradle.internal.classpath.FileUtils.optionsAllowReading;
+import static org.gradle.internal.classpath.FileUtils.tryReportDirectoryContentObserved;
+import static org.gradle.internal.classpath.FileUtils.tryReportFileOpened;
+import static org.gradle.internal.classpath.FileUtils.tryReportFileSystemEntryObserved;
+
+@SuppressWarnings("NewMethodNamingConvention")
+@SpecificJvmCallInterceptors(generatedClassName = InterceptorDeclaration.JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+@SpecificGroovyCallInterceptors(generatedClassName = InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE)
+public class NioFileInterceptors {
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static boolean intercept_isRegularFile(
+        Path path,
+        @VarargParameter LinkOption[] options,
+        @CallerClassName String consumer
+    ) {
+        tryReportFileSystemEntryObserved(path, consumer);
+        return Files.isRegularFile(path, options);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static boolean intercept_isDirectory(
+        Path path,
+        @VarargParameter LinkOption[] options,
+        @CallerClassName String consumer
+    ) {
+        tryReportFileSystemEntryObserved(path, consumer);
+        return Files.isDirectory(path, options);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static boolean intercept_exists(
+        Path path,
+        @VarargParameter LinkOption[] options,
+        @CallerClassName String consumer
+    ) {
+        tryReportFileSystemEntryObserved(path, consumer);
+        return Files.exists(path, options);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static boolean intercept_notExists(
+        Path path,
+        @VarargParameter LinkOption[] options,
+        @CallerClassName String consumer
+    ) {
+        tryReportFileSystemEntryObserved(path, consumer);
+        return Files.notExists(path, options);
+    }
+
+    // TODO: handle varargs in Groovy
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static SeekableByteChannel intercept_newByteChannel(
+        Path path,
+        @VarargParameter OpenOption[] options,
+        @CallerClassName String consumer
+    ) throws IOException {
+        if (optionsAllowReading(options)) {
+            tryReportFileOpened(path, consumer);
+        }
+        return Files.newByteChannel(path, options);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static SeekableByteChannel intercept_newByteChannel(
+        Path path,
+        Set<?> options, // todo: use a proper type argument here once the tool supports it
+        @VarargParameter FileAttribute<?>[] attrs,
+        @CallerClassName String consumer
+    ) throws IOException {
+        if (optionsAllowReading(options)) {
+            tryReportFileOpened(path, consumer);
+        }
+        return Files.newByteChannel(path, Cast.uncheckedNonnullCast(options), attrs);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static BufferedReader intercept_newBufferedReader(
+        Path path,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return Files.newBufferedReader(path);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static BufferedReader intercept_newBufferedReader(
+        Path path,
+        Charset charset,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return Files.newBufferedReader(path, charset);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static InputStream intercept_newInputStream(
+        Path path,
+        @VarargParameter OpenOption[] options,
+        @CallerClassName String consumer
+    ) throws IOException {
+        if (optionsAllowReading(options)) {
+            tryReportFileOpened(path, consumer);
+        }
+        return Files.newInputStream(path, options);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static String intercept_readString(
+        Path path,
+        @CallerClassName String consumer
+    ) throws Throwable {
+        return Instrumented.filesReadString(path, consumer);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static String intercept_readString(
+        Path path,
+        Charset charset,
+        @CallerClassName String consumer
+    ) throws Throwable {
+        return Instrumented.filesReadString(path, charset, consumer);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static byte[] intercept_readAllBytes(
+        Path path,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return Files.readAllBytes(path);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static List<String> intercept_readAllLines(
+        Path path,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return Files.readAllLines(path);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static List<String> intercept_readAllLines(
+        Path path,
+        Charset charset,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return Files.readAllLines(path, charset);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static Stream<String> intercept_lines(
+        Path path,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return Files.lines(path);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static Stream<String> intercept_lines(
+        Path path,
+        Charset charset,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return Files.lines(path, charset);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static DirectoryStream<Path> intercept_newDirectoryStream(
+        Path path,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportDirectoryContentObserved(path, consumer);
+        return Files.newDirectoryStream(path);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static DirectoryStream<Path> intercept_newDirectoryStream(
+        Path path,
+        String glob,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportDirectoryContentObserved(path, consumer);
+        return Files.newDirectoryStream(path, glob);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static DirectoryStream<Path> intercept_newDirectoryStream(
+        Path path,
+        DirectoryStream.Filter<?> filter,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportDirectoryContentObserved(path, consumer);
+        return Files.newDirectoryStream(path, Cast.<DirectoryStream.Filter<? super Path>>uncheckedNonnullCast(filter));
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static Stream<Path> intercept_list(
+        Path path,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportDirectoryContentObserved(path, consumer);
+        return Files.list(path);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = Files.class)
+    public static String intercept_probeContentType(
+        Path path,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return Files.probeContentType(path);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static String intercept_probeContentType(
+        @Receiver FileTypeDetector self,
+        Path path,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportFileOpened(path, consumer);
+        return self.probeContentType(path);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static SeekableByteChannel intercept_newByteChannel(
+        @Receiver FileSystemProvider self,
+        Path path,
+        Set<?> options,
+        @VarargParameter FileAttribute<?>[] attrs,
+        @CallerClassName String consumer
+    ) throws IOException {
+        if (optionsAllowReading(options)) {
+            tryReportFileOpened(path, consumer);
+        }
+        return self.newByteChannel(path, Cast.uncheckedCast(options), attrs);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static InputStream intercept_newInputStream(
+        @Receiver FileSystemProvider self,
+        Path path,
+        @VarargParameter OpenOption[] options,
+        @CallerClassName String consumer
+    ) throws IOException {
+        if (optionsAllowReading(options)) {
+            tryReportFileOpened(path, consumer);
+        }
+        return self.newInputStream(path, options);
+    }
+
+    @InterceptCalls
+    @InstanceMethod
+    public static DirectoryStream<Path> intercept_newDirectoryStream(@Receiver FileSystemProvider self,
+        Path path,
+        DirectoryStream.Filter<?> filter,
+        @CallerClassName String consumer
+    ) throws IOException {
+        tryReportDirectoryContentObserved(path, consumer);
+        return self.newDirectoryStream(path, Cast.uncheckedCast(filter));
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = FileChannel.class)
+    public static FileChannel intercept_open(
+        Path path,
+        @VarargParameter OpenOption[] options,
+        @CallerClassName String consumer
+    ) throws IOException {
+        if (optionsAllowReading(options)) {
+            tryReportFileOpened(path, consumer);
+        }
+        return FileChannel.open(path, options);
+    }
+
+    @InterceptCalls
+    @StaticMethod(ofClass = FileChannel.class)
+    public static FileChannel intercept_open(
+        Path path,
+        Set<?> options,
+        @VarargParameter FileAttribute<?>[] attrs,
+        @CallerClassName String consumer
+    ) throws IOException {
+        if (optionsAllowReading(options)) {
+            tryReportFileOpened(path, consumer);
+        }
+        return FileChannel.open(path, Cast.uncheckedCast(options), attrs);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/generated/package-info.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/generated/package-info.java
new file mode 100644
index 0000000..566c040
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/generated/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * A package containing instrumentation generated classes. Classes are generated via annotation processor.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.internal.classpath.generated;
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/intercept/CallInterceptorsSet.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/intercept/CallInterceptorsSet.java
index 2583e5b..41f56b4 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/classpath/intercept/CallInterceptorsSet.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/intercept/CallInterceptorsSet.java
@@ -20,6 +20,7 @@
 import org.codehaus.groovy.runtime.callsite.CallSite;
 import org.codehaus.groovy.vmplugin.v8.CacheableCallSite;
 import org.gradle.api.GradleException;
+import org.gradle.api.NonNullApi;
 
 import javax.annotation.Nullable;
 import java.lang.invoke.MethodHandle;
@@ -28,11 +29,13 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Stream;
 
 /**
  * Holds a collection of interceptors and can decorate a Groovy CallSite if it is within a scope of a registered interceptor.
  */
-public class CallInterceptorsSet {
+@NonNullApi
+public class CallInterceptorsSet implements CallSiteDecorator {
     private final Map<InterceptScope, CallInterceptor> interceptors = new HashMap<>();
     private final Set<String> interceptedCallSiteNames = new HashSet<>();
 
@@ -55,12 +58,10 @@ protected Object doIntercept(Invocation invocation, String consumer) throws Thro
     };
 
     /**
-     * Creates the interceptor set out of provided interceptors.
+     * Creates the interceptor set, collecting the interceptors from the stream.
      */
-    public CallInterceptorsSet(CallInterceptor... interceptors) {
-        for (CallInterceptor interceptor : interceptors) {
-            addInterceptor(interceptor);
-        }
+    public CallInterceptorsSet(Stream<CallInterceptor> interceptors) {
+        interceptors.forEach(this::addInterceptor);
     }
 
     private void addInterceptor(CallInterceptor interceptor) {
@@ -75,6 +76,7 @@ private void addInterceptor(CallInterceptor interceptor) {
         }
     }
 
+    @Override
     public java.lang.invoke.CallSite maybeDecorateIndyCallSite(java.lang.invoke.CallSite originalCallSite, MethodHandles.Lookup caller, String callType, String name, int flags) {
         CacheableCallSite ccs = toGroovyCacheableCallSite(originalCallSite);
         switch (callType) {
@@ -115,6 +117,7 @@ private static CacheableCallSite toGroovyCacheableCallSite(java.lang.invoke.Call
      * @param originalCallSite the CallSite to decorate
      * @return the new CallSite capable of intercepting calls or the original CallSite if interception not neccessary.
      */
+    @Override
     public CallSite maybeDecorateGroovyCallSite(CallSite originalCallSite) {
         if (shouldDecorate(originalCallSite)) {
             return new DecoratingCallSite(originalCallSite);
diff --git a/subprojects/core/src/main/java/org/gradle/internal/classpath/intercept/CallSiteDecorator.java b/subprojects/core/src/main/java/org/gradle/internal/classpath/intercept/CallSiteDecorator.java
new file mode 100644
index 0000000..0495793
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/classpath/intercept/CallSiteDecorator.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.classpath.intercept;
+
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import org.gradle.api.NonNullApi;
+
+import java.lang.invoke.MethodHandles;
+
+/**
+ * A handler for Groovy call sites, including Indy ones, which is used to replace the call sites of some calls at runtime,
+ * in order to alter their behavior.
+ */
+@NonNullApi
+public interface CallSiteDecorator {
+    CallSite maybeDecorateGroovyCallSite(CallSite originalCallSite);
+
+    java.lang.invoke.CallSite maybeDecorateIndyCallSite(java.lang.invoke.CallSite originalCallSite, MethodHandles.Lookup caller, String callType, String name, int flags);
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/exceptions/FailureResolutionAware.java b/subprojects/core/src/main/java/org/gradle/internal/exceptions/FailureResolutionAware.java
index 8e66c80..ab89eee 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/exceptions/FailureResolutionAware.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/exceptions/FailureResolutionAware.java
@@ -37,5 +37,16 @@ interface Context {
         void doNotSuggestResolutionsThatRequireBuildDefinition();
 
         void appendResolution(Consumer<StyledTextOutput> resolution);
+
+        /**
+         * Adds a resolution pointing to the user guide. The output matches the following pattern:
+         * <code>
+         *     ${prefix} http://docs.gradle.org/${currentGradleVersion}/userguide/${userGuideId}.html#${userGuideSection}.
+         * </code>
+         * @param prefix The string prepended to the documentation URL.
+         * @param userGuideId The user guide chapter.
+         * @param userGuideSection The user guide section.
+         */
+        void appendDocumentationResolution(String prefix, String userGuideId, String userGuideSection);
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/execution/DefaultTaskExecutionTracker.java b/subprojects/core/src/main/java/org/gradle/internal/execution/DefaultTaskExecutionTracker.java
deleted file mode 100644
index da96221..0000000
--- a/subprojects/core/src/main/java/org/gradle/internal/execution/DefaultTaskExecutionTracker.java
+++ /dev/null
@@ -1,106 +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.execution;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.api.internal.tasks.execution.ExecuteTaskBuildOperationDetails;
-import org.gradle.api.internal.tasks.execution.ExecuteTaskBuildOperationType;
-import org.gradle.internal.operations.BuildOperationAncestryTracker;
-import org.gradle.internal.operations.BuildOperationDescriptor;
-import org.gradle.internal.operations.BuildOperationListener;
-import org.gradle.internal.operations.BuildOperationListenerManager;
-import org.gradle.internal.operations.CurrentBuildOperationRef;
-import org.gradle.internal.operations.OperationFinishEvent;
-import org.gradle.internal.operations.OperationIdentifier;
-import org.gradle.internal.operations.OperationProgressEvent;
-import org.gradle.internal.operations.OperationStartEvent;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static java.lang.String.format;
-
-public class DefaultTaskExecutionTracker implements TaskExecutionTracker, Closeable {
-
-    private final BuildOperationAncestryTracker buildOperationAncestryTracker;
-    private final BuildOperationListenerManager buildOperationListenerManager;
-    private final CurrentBuildOperationRef currentBuildOperationRef = CurrentBuildOperationRef.instance();
-    private final OperationListener operationListener = new OperationListener();
-
-    public DefaultTaskExecutionTracker(
-        BuildOperationAncestryTracker buildOperationAncestryTracker,
-        BuildOperationListenerManager buildOperationListenerManager
-    ) {
-        this.buildOperationAncestryTracker = buildOperationAncestryTracker;
-        this.buildOperationListenerManager = buildOperationListenerManager;
-        buildOperationListenerManager.addListener(operationListener);
-    }
-
-    @Override
-    public Optional<TaskInternal> getCurrentTask() {
-        return buildOperationAncestryTracker
-            .findClosestExistingAncestor(
-                currentBuildOperationRef.getId(),
-                operationListener.runningTasks::get
-            );
-    }
-
-    @Override
-    public void close() throws IOException {
-        buildOperationListenerManager.removeListener(operationListener);
-        assert operationListener.runningTasks.isEmpty();
-    }
-
-    private static class OperationListener implements BuildOperationListener {
-
-        final Map<OperationIdentifier, TaskInternal> runningTasks = new ConcurrentHashMap<>();
-
-        @Override
-        public void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) {
-            Object details = buildOperation.getDetails();
-            if (details instanceof ExecuteTaskBuildOperationDetails) {
-                runningTasks.put(mandatoryIdOf(buildOperation), ((ExecuteTaskBuildOperationDetails) details).getTask());
-            }
-        }
-
-        @Override
-        public void finished(BuildOperationDescriptor buildOperation, OperationFinishEvent finishEvent) {
-            Object details = buildOperation.getDetails();
-            if (details instanceof ExecuteTaskBuildOperationType.Details) {
-                Object removed = runningTasks.remove(mandatoryIdOf(buildOperation));
-                if (removed == null) {
-                    throw new IllegalStateException(format("Task build operation %s was finished without being started.", buildOperation));
-                }
-            }
-        }
-
-        private OperationIdentifier mandatoryIdOf(BuildOperationDescriptor buildOperation) {
-            OperationIdentifier id = buildOperation.getId();
-            if (id == null) {
-                throw new IllegalStateException(format("Task build operation %s has no valid id", buildOperation));
-            }
-            return id;
-        }
-
-        @Override
-        public void progress(OperationIdentifier operationIdentifier, OperationProgressEvent progressEvent) {
-        }
-    }
-}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/execution/DefaultWorkExecutionTracker.java b/subprojects/core/src/main/java/org/gradle/internal/execution/DefaultWorkExecutionTracker.java
new file mode 100644
index 0000000..0a073f3
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/execution/DefaultWorkExecutionTracker.java
@@ -0,0 +1,132 @@
+/*
+ * 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.execution;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.api.internal.tasks.execution.ExecuteTaskBuildOperationDetails;
+import org.gradle.api.internal.tasks.execution.ExecuteTaskBuildOperationType;
+import org.gradle.internal.operations.BuildOperationAncestryTracker;
+import org.gradle.internal.operations.BuildOperationDescriptor;
+import org.gradle.internal.operations.BuildOperationListener;
+import org.gradle.internal.operations.BuildOperationListenerManager;
+import org.gradle.internal.operations.CurrentBuildOperationRef;
+import org.gradle.internal.operations.OperationFinishEvent;
+import org.gradle.internal.operations.OperationIdentifier;
+import org.gradle.internal.operations.OperationProgressEvent;
+import org.gradle.internal.operations.OperationStartEvent;
+import org.gradle.internal.operations.UncategorizedBuildOperations;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static java.lang.String.format;
+
+public class DefaultWorkExecutionTracker implements WorkExecutionTracker, Closeable {
+
+    private final BuildOperationAncestryTracker buildOperationAncestryTracker;
+    private final BuildOperationListenerManager buildOperationListenerManager;
+    private final CurrentBuildOperationRef currentBuildOperationRef = CurrentBuildOperationRef.instance();
+    private final OperationListener operationListener = new OperationListener();
+
+    public DefaultWorkExecutionTracker(
+        BuildOperationAncestryTracker buildOperationAncestryTracker,
+        BuildOperationListenerManager buildOperationListenerManager
+    ) {
+        this.buildOperationAncestryTracker = buildOperationAncestryTracker;
+        this.buildOperationListenerManager = buildOperationListenerManager;
+        buildOperationListenerManager.addListener(operationListener);
+    }
+
+    @Override
+    public Optional<TaskInternal> getCurrentTask() {
+        return buildOperationAncestryTracker
+            .findClosestExistingAncestor(
+                currentBuildOperationRef.getId(),
+                operationListener.runningTasks::get
+            );
+    }
+
+    @Override
+    public boolean isExecutingTransformAction() {
+        return buildOperationAncestryTracker.findClosestMatchingAncestor(
+            currentBuildOperationRef.getId(), operationListener.runningTransformActions::contains
+        ).isPresent();
+    }
+
+    @Override
+    public void close() throws IOException {
+        buildOperationListenerManager.removeListener(operationListener);
+        assert !operationListener.hasRunningWork();
+    }
+
+    private static class OperationListener implements BuildOperationListener {
+
+        final Map<OperationIdentifier, TaskInternal> runningTasks = new ConcurrentHashMap<>();
+        final Set<OperationIdentifier> runningTransformActions = ConcurrentHashMap.newKeySet();
+
+        @Override
+        public void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) {
+            if (isTransformAction(buildOperation)) {
+                runningTransformActions.add(mandatoryIdOf(buildOperation));
+            } else {
+                Object details = buildOperation.getDetails();
+                if (details instanceof ExecuteTaskBuildOperationDetails) {
+                    runningTasks.put(mandatoryIdOf(buildOperation), ((ExecuteTaskBuildOperationDetails) details).getTask());
+                }
+            }
+        }
+
+        @Override
+        public void finished(BuildOperationDescriptor buildOperation, OperationFinishEvent finishEvent) {
+            if (isTransformAction(buildOperation)) {
+                runningTransformActions.remove(mandatoryIdOf(buildOperation));
+            } else {
+                Object details = buildOperation.getDetails();
+                if (details instanceof ExecuteTaskBuildOperationType.Details) {
+                    Object removed = runningTasks.remove(mandatoryIdOf(buildOperation));
+                    if (removed == null) {
+                        throw new IllegalStateException(format("Task build operation %s was finished without being started.", buildOperation));
+                    }
+                }
+            }
+        }
+
+        private OperationIdentifier mandatoryIdOf(BuildOperationDescriptor buildOperation) {
+            OperationIdentifier id = buildOperation.getId();
+            if (id == null) {
+                throw new IllegalStateException(format("Build operation %s has no valid id", buildOperation));
+            }
+            return id;
+        }
+
+        @Override
+        public void progress(OperationIdentifier operationIdentifier, OperationProgressEvent progressEvent) {
+        }
+
+        public boolean hasRunningWork() {
+            return !runningTasks.isEmpty() || !runningTransformActions.isEmpty();
+        }
+
+        private static boolean isTransformAction(BuildOperationDescriptor buildOperation) {
+            return UncategorizedBuildOperations.TRANSFORM_ACTION.equals(buildOperation.getMetadata());
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/execution/TaskExecutionTracker.java b/subprojects/core/src/main/java/org/gradle/internal/execution/TaskExecutionTracker.java
deleted file mode 100644
index 492587f..0000000
--- a/subprojects/core/src/main/java/org/gradle/internal/execution/TaskExecutionTracker.java
+++ /dev/null
@@ -1,35 +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.execution;
-
-import org.gradle.api.internal.TaskInternal;
-import org.gradle.internal.service.scopes.Scope;
-import org.gradle.internal.service.scopes.ServiceScope;
-
-import java.util.Optional;
-
-/**
- * Provides access to the task executing on the current thread.
- */
-@ServiceScope(Scope.Global.class)
-public interface TaskExecutionTracker {
-
-    /**
-     * Finds the task executing on the current thread, if any.
-     */
-    Optional<TaskInternal> getCurrentTask();
-}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/execution/WorkExecutionTracker.java b/subprojects/core/src/main/java/org/gradle/internal/execution/WorkExecutionTracker.java
new file mode 100644
index 0000000..f7250b4
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/execution/WorkExecutionTracker.java
@@ -0,0 +1,42 @@
+/*
+ * 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.execution;
+
+import org.gradle.api.internal.TaskInternal;
+import org.gradle.internal.service.scopes.Scope;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+import java.util.Optional;
+
+/**
+ * Provides access to the work executing on the current thread.
+ */
+@ServiceScope(Scope.Global.class)
+public interface WorkExecutionTracker {
+
+    /**
+     * Finds the task executing on the current thread, if any.
+     */
+    Optional<TaskInternal> getCurrentTask();
+
+    /**
+     * Checks if the current thread is executing a {@link org.gradle.api.artifacts.transform.TransformAction}.
+     *
+     * @return {@code true} if the current thread is executing a transform action
+     */
+    boolean isExecutingTransformAction();
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/file/DefaultReservedFileSystemLocationRegistry.java b/subprojects/core/src/main/java/org/gradle/internal/file/DefaultReservedFileSystemLocationRegistry.java
index ef67db7..5f61163 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/file/DefaultReservedFileSystemLocationRegistry.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/file/DefaultReservedFileSystemLocationRegistry.java
@@ -16,7 +16,7 @@
 
 package org.gradle.internal.file;
 
-import org.gradle.internal.watch.registry.impl.Combiners;
+import org.gradle.internal.Combiners;
 
 import java.io.File;
 import java.util.List;
diff --git a/subprojects/core/src/main/java/org/gradle/internal/id/ConfigurationCacheableIdFactory.java b/subprojects/core/src/main/java/org/gradle/internal/id/ConfigurationCacheableIdFactory.java
new file mode 100644
index 0000000..83b2b41
--- /dev/null
+++ b/subprojects/core/src/main/java/org/gradle/internal/id/ConfigurationCacheableIdFactory.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 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.id;
+
+
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Creates unique ids for objects created during configuration.
+ * <p>
+ * Usage of this factory in object factories ensures that ids are reused consistently when loading from the configuration cache.
+ */
+@ThreadSafe
+@ServiceScope(Scopes.BuildTree.class)
+public class ConfigurationCacheableIdFactory {
+
+    private static final long USED_ASSIGNED_ID_MARKER = -1;
+
+    private final AtomicLong sequence = new AtomicLong(0);
+
+    /**
+     * Creates a new unique id.
+     * <p>
+     * New ids can only be created if no ids have been loaded from the configuration cache.
+     * When re-creating an object due to loading from the configuration cache,
+     * {@link #idRecreated()} must be called before the object is re-created to make sure the consistency of the ids.
+     *
+     * @throws IllegalStateException if an id has already been loaded.
+     */
+    public long createId() {
+        long newId = sequence.updateAndGet(it -> it == USED_ASSIGNED_ID_MARKER ? it : it + 1);
+        if (newId == USED_ASSIGNED_ID_MARKER) {
+            throw new IllegalStateException("Cannot create a new id after one has been loaded");
+        }
+
+        return newId;
+    }
+
+    /**
+     * This method must be called whenever an object is about to be re-created with the previously assigned id
+     * due to loading from the configuration cache.
+     */
+    public void idRecreated() {
+        sequence.set(USED_ASSIGNED_ID_MARKER);
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/internal/model/StateTransitionController.java b/subprojects/core/src/main/java/org/gradle/internal/model/StateTransitionController.java
index 0738500..1f801e8 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/model/StateTransitionController.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/model/StateTransitionController.java
@@ -23,6 +23,7 @@
 
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.ThreadSafe;
+import java.util.List;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
@@ -45,18 +46,6 @@ public StateTransitionController(DisplayName displayName, T initialState, Synchr
     }
 
     /**
-     * Verifies that the current state is the given state. Ignores any transition in progress and failures of previous operations.
-     *
-     * <p>You should try to not use this method, as it does not provide any thread safety for the code that follows the call.</p>
-     */
-    public void assertInState(T expected) {
-        CurrentState<T> current = state;
-        if (current.state != expected) {
-            throw new IllegalStateException(displayName.getCapitalizedDisplayName() + " should be in state " + expected + " but is in " + current.state + ".");
-        }
-    }
-
-    /**
      * Verifies that the current state is not the given state. Ignores any transition in progress and failures of previous operations.
      *
      * <p>You should try to not use this method, as it does not provide any thread safety for the code that follows the call.</p>
@@ -67,17 +56,6 @@ public void assertNotInState(T forbidden) {
         }
     }
 
-    public void restart(T target, Runnable action) {
-        synchronizer.withLock(() -> {
-            action.run();
-            state = new InState<>(displayName, target, null);
-        });
-    }
-
-    public boolean isInStateOrLater(T expected) {
-        return state.hasSeenStateIgnoringTransitions(expected);
-    }
-
     /**
      * Verifies that the current state is the given state or some later state. Ignores any transition in progress and failures of previous operations.
      *
@@ -112,7 +90,7 @@ public <S> S notInStateIgnoreOtherThreads(T forbidden, Supplier<S> supplier) {
     }
 
     /**
-     * Runs the given action.
+     * Runs the given action, verifying the current state is the expected state.
      * Fails if the current state is not the given state or a previous operation has failed.
      * Blocks until other operations are complete.
      */
@@ -160,6 +138,20 @@ public <S> S notInState(T forbidden, Supplier<S> action) {
     }
 
     /**
+     * Resets the state to the given state.
+     * Fails if the current state is not the given state, ignores failures from previous operations.
+     * Blocks until other operations are complete.
+     */
+    public void restart(T fromState, T toState, Runnable action) {
+        synchronizer.withLock(() -> {
+            CurrentState<T> current = state;
+            current.assertCanTransition(fromState, toState, true);
+            action.run();
+            state = new InState<>(displayName, toState, null);
+        });
+    }
+
+    /**
      * Transitions to the given "to" state.
      * Fails if the current state is not the given "from" state, the current thread is transitioning the state, or a previous operation has failed.
      * Blocks until other operations are complete.
@@ -225,23 +217,39 @@ public void transitionIfNotPreviously(T fromState, T toState, Runnable action) {
     /**
      * Transitions to a final state, taking any failures from previous transitions and transforming them.
      */
-    public ExecutionResult<Void> finish(T toState, Function<ExecutionResult<Void>, ExecutionResult<Void>> action) {
+    public ExecutionResult<Void> transition(T fromState, T toState, Function<ExecutionResult<Void>, ExecutionResult<Void>> action) {
         return synchronizer.withLock(() -> {
             CurrentState<T> current = state;
-            if (current.state == toState) {
-                // Don't rethrow the failure
-                return ExecutionResult.succeeded();
-            }
-            ExecutionResult<Void> result = current.asResult();
-            state = current.transitioningTo(toState);
-            try {
-                return action.apply(result);
-            } finally {
-                state = state.nextState(toState);
-            }
+            current.assertCanTransition(fromState, toState, true);
+            return doTransitionWithFailures(toState, action, current);
         });
     }
 
+    public ExecutionResult<Void> transition(List<T> fromStates, T toState, Function<ExecutionResult<Void>, ExecutionResult<Void>> action) {
+        return synchronizer.withLock(() -> {
+            CurrentState<T> current = state;
+            current.assertCanTransition(fromStates, toState, true);
+            return doTransitionWithFailures(toState, action, current);
+        });
+    }
+
+    private ExecutionResult<Void> doTransitionWithFailures(T toState, Function<ExecutionResult<Void>, ExecutionResult<Void>> action, CurrentState<T> current) {
+        ExecutionResult<Void> currentResult = current.asResult();
+        state = current.transitioningTo(toState);
+        ExecutionResult<Void> result;
+        try {
+            result = action.apply(currentResult);
+        } catch (Throwable t) {
+            result = ExecutionResult.failed(t);
+        }
+        if (!result.getFailures().isEmpty()) {
+            state = state.failed(result);
+        } else {
+            state = state.nextState(toState);
+        }
+        return result;
+    }
+
     private void doTransition(T fromState, T toState, Runnable action) {
         doTransition(fromState, toState, () -> {
             action.run();
@@ -280,7 +288,13 @@ public CurrentState(DisplayName displayName, T state) {
 
         public abstract void assertNotInState(T forbidden);
 
-        public abstract void assertCanTransition(T fromState, T toState);
+        public void assertCanTransition(T fromState, T toState) {
+            assertCanTransition(fromState, toState, false);
+        }
+
+        public abstract void assertCanTransition(T fromState, T toState, boolean ignoreFailures);
+
+        public abstract void assertCanTransition(List<T> fromStates, T toState, boolean ignoreFailures);
 
         public abstract boolean inStateAndNotTransitioning(T toState);
 
@@ -338,13 +352,20 @@ public void assertNotInState(T forbidden) {
         }
 
         @Override
-        public void assertCanTransition(T fromState, T toState) {
+        public void assertCanTransition(T fromState, T toState, boolean ignoreFailures) {
             if (state != fromState) {
                 throw new IllegalStateException("Can only transition " + displayName.getCapitalizedDisplayName() + " to state " + toState + " from state " + fromState + " however it is currently in state " + state + ".");
             }
         }
 
         @Override
+        public void assertCanTransition(List<T> fromStates, T toState, boolean ignoreFailures) {
+            if (!fromStates.contains(state)) {
+                throw new IllegalStateException("Can only transition " + displayName.getCapitalizedDisplayName() + " to state " + toState + " from states " + fromStates + " however it is currently in state " + state + ".");
+            }
+        }
+
+        @Override
         public boolean inStateAndNotTransitioning(T toState) {
             return state == toState;
         }
@@ -413,7 +434,16 @@ public void assertNotInState(T forbidden) {
         }
 
         @Override
-        public void assertCanTransition(T fromState, T toState) {
+        public void assertCanTransition(T fromState, T toState, boolean ignoreFailures) {
+            failDueToTransition(toState);
+        }
+
+        @Override
+        public void assertCanTransition(List<T> fromStates, T toState, boolean ignoreFailures) {
+            failDueToTransition(toState);
+        }
+
+        private void failDueToTransition(T toState) {
             if (targetState == toState) {
                 throw new IllegalStateException("Cannot transition " + displayName.getDisplayName() + " to state " + toState + " as already transitioning to this state.");
             } else {
@@ -463,8 +493,17 @@ public void assertNotInState(T forbidden) {
         }
 
         @Override
-        public void assertCanTransition(T fromState, T toState) {
-            throwFailure();
+        public void assertCanTransition(List<T> fromStates, T toState, boolean ignoreFailures) {
+            if (!ignoreFailures) {
+                throwFailure();
+            }
+        }
+
+        @Override
+        public void assertCanTransition(T fromState, T toState, boolean ignoreFailures) {
+            if (!ignoreFailures) {
+                throwFailure();
+            }
         }
 
         @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 5f019d6..d912d31 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
@@ -109,7 +109,7 @@
 import org.gradle.execution.plan.TaskDependencyResolver;
 import org.gradle.execution.plan.TaskNodeDependencyResolver;
 import org.gradle.execution.plan.TaskNodeFactory;
-import org.gradle.execution.plan.ToPlannedNodeConverter;
+import org.gradle.execution.plan.ToPlannedNodeConverterRegistry;
 import org.gradle.execution.plan.WorkNodeDependencyResolver;
 import org.gradle.execution.selection.BuildTaskSelector;
 import org.gradle.groovy.scripts.DefaultScriptCompilerFactory;
@@ -154,6 +154,10 @@
 import org.gradle.initialization.layout.BuildLayoutConfiguration;
 import org.gradle.initialization.layout.BuildLayoutFactory;
 import org.gradle.initialization.layout.ResolvedBuildLayout;
+import org.gradle.initialization.properties.DefaultProjectPropertiesLoader;
+import org.gradle.initialization.properties.DefaultSystemPropertiesInstaller;
+import org.gradle.initialization.properties.ProjectPropertiesLoader;
+import org.gradle.initialization.properties.SystemPropertiesInstaller;
 import org.gradle.internal.actor.ActorFactory;
 import org.gradle.internal.actor.internal.DefaultActorFactory;
 import org.gradle.internal.authentication.AuthenticationSchemeRegistry;
@@ -177,7 +181,7 @@
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.event.DefaultListenerManager;
 import org.gradle.internal.event.ListenerManager;
-import org.gradle.internal.execution.TaskExecutionTracker;
+import org.gradle.internal.execution.WorkExecutionTracker;
 import org.gradle.internal.file.Deleter;
 import org.gradle.internal.file.RelativeFilePathResolver;
 import org.gradle.internal.file.Stat;
@@ -352,21 +356,30 @@ protected GradleProperties createGradleProperties(
     }
 
     protected GradlePropertiesController createGradlePropertiesController(
-        IGradlePropertiesLoader propertiesLoader
+        IGradlePropertiesLoader propertiesLoader,
+        SystemPropertiesInstaller systemPropertiesInstaller,
+        ProjectPropertiesLoader projectPropertiesLoader
     ) {
-        return new DefaultGradlePropertiesController(propertiesLoader);
+        return new DefaultGradlePropertiesController(propertiesLoader, systemPropertiesInstaller, projectPropertiesLoader);
+    }
+
+    protected ProjectPropertiesLoader createProjectPropertiesLoader(
+        Environment environment
+    ) {
+        return new DefaultProjectPropertiesLoader((StartParameterInternal) get(StartParameter.class), environment);
     }
 
     protected IGradlePropertiesLoader createGradlePropertiesLoader(
-        Environment environment,
+        Environment environment
+    ) {
+        return new DefaultGradlePropertiesLoader((StartParameterInternal) get(StartParameter.class), environment);
+    }
+
+    protected SystemPropertiesInstaller createSystemPropertiesInstaller(
         EnvironmentChangeTracker environmentChangeTracker,
         GradleInternal gradleInternal
     ) {
-        return new DefaultGradlePropertiesLoader(
-            (StartParameterInternal) get(StartParameter.class),
-            environment,
-            environmentChangeTracker,
-            gradleInternal);
+        return new DefaultSystemPropertiesInstaller(environmentChangeTracker, (StartParameterInternal) get(StartParameter.class), gradleInternal);
     }
 
     protected ValueSourceProviderFactory createValueSourceProviderFactory(
@@ -560,13 +573,13 @@ protected ProjectsPreparer createBuildConfigurer(
             buildOperationExecutor);
     }
 
-    protected BuildWorkPreparer createWorkPreparer(BuildOperationExecutor buildOperationExecutor, ExecutionPlanFactory executionPlanFactory, List<ToPlannedNodeConverter> converters) {
+    protected BuildWorkPreparer createWorkPreparer(BuildOperationExecutor buildOperationExecutor, ExecutionPlanFactory executionPlanFactory, ToPlannedNodeConverterRegistry converterRegistry) {
         return new BuildOperationFiringBuildWorkPreparer(
             buildOperationExecutor,
             new DefaultBuildWorkPreparer(
                 executionPlanFactory
             ),
-            converters
+            converterRegistry
         );
     }
 
@@ -687,7 +700,7 @@ protected DefaultBuildServicesRegistry createSharedServiceRegistry(
             isolatableFactory,
             sharedResourceLeaseRegistry,
             featureFlags.isEnabled(FeaturePreviews.Feature.STABLE_CONFIGURATION_CACHE)
-                ? new BuildServiceProviderNagger(services.get(TaskExecutionTracker.class))
+                ? new BuildServiceProviderNagger(services.get(WorkExecutionTracker.class))
                 : BuildServiceProvider.Listener.EMPTY
         );
     }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/ExecutionGlobalServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/ExecutionGlobalServices.java
index dfe6733..b044643 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/ExecutionGlobalServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/ExecutionGlobalServices.java
@@ -65,8 +65,8 @@
 import org.gradle.api.tasks.options.OptionValues;
 import org.gradle.cache.internal.CrossBuildInMemoryCacheFactory;
 import org.gradle.internal.event.ListenerManager;
-import org.gradle.internal.execution.DefaultTaskExecutionTracker;
-import org.gradle.internal.execution.TaskExecutionTracker;
+import org.gradle.internal.execution.DefaultWorkExecutionTracker;
+import org.gradle.internal.execution.WorkExecutionTracker;
 import org.gradle.internal.execution.WorkInputListeners;
 import org.gradle.internal.execution.model.annotations.DisableCachingByDefaultTypeAnnotationHandler;
 import org.gradle.internal.execution.model.annotations.InputDirectoryPropertyAnnotationHandler;
@@ -124,8 +124,8 @@ public class ExecutionGlobalServices {
         ReplacedBy.class
     );
 
-    TaskExecutionTracker createTaskExecutionTracker(BuildOperationAncestryTracker ancestryTracker, BuildOperationListenerManager operationListenerManager) {
-        return new DefaultTaskExecutionTracker(ancestryTracker, operationListenerManager);
+    WorkExecutionTracker createWorkExecutionTracker(BuildOperationAncestryTracker ancestryTracker, BuildOperationListenerManager operationListenerManager) {
+        return new DefaultWorkExecutionTracker(ancestryTracker, operationListenerManager);
     }
 
     AnnotationHandlerRegistar createAnnotationRegistry(List<AnnotationHandlerRegistration> registrations) {
diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GlobalScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GlobalScopeServices.java
index 28077fa7..3f75697 100755
--- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GlobalScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GlobalScopeServices.java
@@ -56,6 +56,8 @@
 import org.gradle.initialization.LegacyTypesSupport;
 import org.gradle.initialization.layout.BuildLayoutFactory;
 import org.gradle.internal.Factory;
+import org.gradle.internal.agents.AgentInitializer;
+import org.gradle.internal.agents.AgentStatus;
 import org.gradle.internal.classloader.ClassLoaderFactory;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.concurrent.ExecutorFactory;
@@ -115,13 +117,15 @@
 public class GlobalScopeServices extends WorkerSharedGlobalScopeServices {
 
     private final GradleBuildEnvironment environment;
+    private final AgentStatus agentStatus;
 
-    public GlobalScopeServices(final boolean longLiving) {
-        this(longLiving, ClassPath.EMPTY);
+    public GlobalScopeServices(final boolean longLiving, AgentStatus agentStatus) {
+        this(longLiving, agentStatus, ClassPath.EMPTY);
     }
 
-    public GlobalScopeServices(final boolean longLiving, ClassPath additionalModuleClassPath) {
+    public GlobalScopeServices(final boolean longLiving, AgentStatus agentStatus, ClassPath additionalModuleClassPath) {
         super(additionalModuleClassPath);
+        this.agentStatus = agentStatus;
         this.environment = () -> longLiving;
     }
 
@@ -301,4 +305,12 @@ OverlappingOutputDetector createOverlappingOutputDetector() {
     DefaultWorkValidationWarningRecorder createValidationWarningReporter() {
         return new DefaultWorkValidationWarningRecorder();
     }
+
+    AgentStatus createAgentStatus() {
+        return agentStatus;
+    }
+
+    AgentInitializer createAgentInitializer() {
+        return new AgentInitializer(agentStatus);
+    }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GradleUserHomeScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GradleUserHomeScopeServices.java
index 08eb241..dcbe394 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GradleUserHomeScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/GradleUserHomeScopeServices.java
@@ -45,6 +45,7 @@
 import org.gradle.cache.internal.scopes.DefaultGlobalScopedCacheBuilderFactory;
 import org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory;
 import org.gradle.execution.plan.ToPlannedNodeConverter;
+import org.gradle.execution.plan.ToPlannedNodeConverterRegistry;
 import org.gradle.execution.plan.ToPlannedTaskConverter;
 import org.gradle.groovy.scripts.internal.CrossBuildInMemoryCachingScriptClassCache;
 import org.gradle.groovy.scripts.internal.DefaultScriptSourceHasher;
@@ -115,6 +116,10 @@ public void configure(ServiceRegistration registration) {
         }
     }
 
+    ToPlannedNodeConverterRegistry createToPlannedNodeConverterRegistry(List<ToPlannedNodeConverter> converters) {
+        return new ToPlannedNodeConverterRegistry(converters);
+    }
+
     ToPlannedNodeConverter createToPlannedTransformConverter() {
         return new ToPlannedTaskConverter();
     }
diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/ProjectScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/ProjectScopeServices.java
index f09dcf3..873edb4 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/ProjectScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/ProjectScopeServices.java
@@ -55,6 +55,7 @@
 import org.gradle.api.internal.project.ProjectStateRegistry;
 import org.gradle.api.internal.project.ant.DefaultAntLoggingAdapterFactory;
 import org.gradle.api.internal.project.taskfactory.ITaskFactory;
+import org.gradle.api.internal.project.taskfactory.TaskIdentityFactory;
 import org.gradle.api.internal.project.taskfactory.TaskInstantiator;
 import org.gradle.api.internal.provider.PropertyHost;
 import org.gradle.api.internal.resources.ApiTextResourceAdapter;
@@ -191,13 +192,14 @@ protected ITaskFactory createTaskFactory(ITaskFactory parentFactory, TaskScheme
         return parentFactory.createChild(project, taskScheme.getInstantiationScheme().withServices(this));
     }
 
-    protected TaskInstantiator createTaskInstantiator(ITaskFactory taskFactory) {
-        return new TaskInstantiator(taskFactory, project);
+    protected TaskInstantiator createTaskInstantiator(TaskIdentityFactory taskIdentityFactory, ITaskFactory taskFactory) {
+        return new TaskInstantiator(taskIdentityFactory, taskFactory, project);
     }
 
     protected TaskContainerInternal createTaskContainerInternal(TaskStatistics taskStatistics, BuildOperationExecutor buildOperationExecutor, CrossProjectConfigurator crossProjectConfigurator, CollectionCallbackActionDecorator decorator) {
         return new DefaultTaskContainerFactory(
             get(Instantiator.class),
+            get(TaskIdentityFactory.class),
             get(ITaskFactory.class),
             project,
             taskStatistics,
diff --git a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/VirtualFileSystemServices.java b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/VirtualFileSystemServices.java
index 75fb533..2f0b242 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/service/scopes/VirtualFileSystemServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/service/scopes/VirtualFileSystemServices.java
@@ -213,7 +213,7 @@ BuildLifecycleAwareVirtualFileSystem createVirtualFileSystem(
                 .<BuildLifecycleAwareVirtualFileSystem>map(watcherRegistryFactory -> new WatchingVirtualFileSystem(
                     watcherRegistryFactory,
                     root,
-                    sectionId -> documentationRegistry.getDocumentationFor("gradle_daemon", sectionId),
+                    sectionId -> documentationRegistry.getDocumentationRecommendationFor("details", "gradle_daemon", sectionId),
                     locationsWrittenByCurrentBuild,
                     watchableFileSystemDetector,
                     fileChangeListeners
diff --git a/subprojects/core/src/main/java/org/gradle/internal/session/BuildSessionScopeServices.java b/subprojects/core/src/main/java/org/gradle/internal/session/BuildSessionScopeServices.java
index 6e0e553..dda910f 100644
--- a/subprojects/core/src/main/java/org/gradle/internal/session/BuildSessionScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/internal/session/BuildSessionScopeServices.java
@@ -18,7 +18,6 @@
 
 import org.gradle.StartParameter;
 import org.gradle.api.internal.StartParameterInternal;
-import org.gradle.api.internal.attributes.DefaultImmutableAttributesFactory;
 import org.gradle.api.internal.cache.StringInterner;
 import org.gradle.api.internal.changedetection.state.BuildSessionScopeFileTimeStampInspector;
 import org.gradle.api.internal.changedetection.state.CrossBuildFileHashCache;
@@ -26,7 +25,6 @@
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.internal.file.FileLookup;
 import org.gradle.api.internal.file.FileResolver;
-import org.gradle.api.internal.model.NamedObjectInstantiator;
 import org.gradle.api.internal.project.BuildOperationCrossProjectConfigurator;
 import org.gradle.api.internal.project.CrossProjectConfigurator;
 import org.gradle.api.internal.tasks.userinput.DefaultUserInputHandler;
@@ -35,9 +33,9 @@
 import org.gradle.api.internal.tasks.userinput.UserInputHandler;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.cache.UnscopedCacheBuilderFactory;
-import org.gradle.cache.internal.CleanupActionDecorator;
-import org.gradle.cache.internal.BuildScopeCacheDir;
 import org.gradle.cache.internal.BuildOperationCleanupActionDecorator;
+import org.gradle.cache.internal.BuildScopeCacheDir;
+import org.gradle.cache.internal.CleanupActionDecorator;
 import org.gradle.cache.internal.InMemoryCacheDecoratorFactory;
 import org.gradle.cache.internal.scopes.DefaultBuildTreeScopedCacheBuilderFactory;
 import org.gradle.cache.scopes.BuildTreeScopedCacheBuilderFactory;
@@ -62,7 +60,6 @@
 import org.gradle.internal.file.Deleter;
 import org.gradle.internal.hash.ChecksumService;
 import org.gradle.internal.hash.DefaultChecksumService;
-import org.gradle.internal.isolation.IsolatableFactory;
 import org.gradle.internal.jvm.JavaModuleDetector;
 import org.gradle.internal.logging.progress.ProgressLoggerFactory;
 import org.gradle.internal.logging.sink.OutputEventListenerManager;
@@ -176,10 +173,6 @@ ScriptSourceHasher createScriptSourceHasher() {
         return new DefaultScriptSourceHasher();
     }
 
-    DefaultImmutableAttributesFactory createImmutableAttributesFactory(IsolatableFactory isolatableFactory, NamedObjectInstantiator instantiator) {
-        return new DefaultImmutableAttributesFactory(isolatableFactory, instantiator);
-    }
-
     UserScopeId createUserScopeId(PersistentScopeIdLoader persistentScopeIdLoader) {
         return persistentScopeIdLoader.getUser();
     }
diff --git a/subprojects/core/src/main/java/org/gradle/invocation/DefaultGradle.java b/subprojects/core/src/main/java/org/gradle/invocation/DefaultGradle.java
index e31a6bf..928f25b 100644
--- a/subprojects/core/src/main/java/org/gradle/invocation/DefaultGradle.java
+++ b/subprojects/core/src/main/java/org/gradle/invocation/DefaultGradle.java
@@ -25,6 +25,8 @@
 import org.gradle.api.Project;
 import org.gradle.api.ProjectEvaluationListener;
 import org.gradle.api.UnknownDomainObjectException;
+import org.gradle.api.artifacts.DependencyResolutionListener;
+import org.gradle.api.execution.TaskExecutionGraphListener;
 import org.gradle.api.initialization.IncludedBuild;
 import org.gradle.api.initialization.Settings;
 import org.gradle.api.internal.BuildScopeListenerRegistrationListener;
@@ -49,6 +51,7 @@
 import org.gradle.initialization.ClassLoaderScopeRegistry;
 import org.gradle.initialization.SettingsState;
 import org.gradle.internal.Cast;
+import org.gradle.internal.DeprecatedInGradleScope;
 import org.gradle.internal.InternalBuildAdapter;
 import org.gradle.internal.InternalListener;
 import org.gradle.internal.MutableActionSet;
@@ -60,6 +63,7 @@
 import org.gradle.internal.event.ListenerManager;
 import org.gradle.internal.installation.CurrentGradleInstallation;
 import org.gradle.internal.installation.GradleInstallation;
+import org.gradle.internal.reflect.JavaPropertyReflectionUtil;
 import org.gradle.internal.resource.TextUriResourceLoader;
 import org.gradle.internal.service.ServiceRegistry;
 import org.gradle.internal.service.scopes.ServiceRegistryFactory;
@@ -411,13 +415,27 @@ private void addListener(String registrationPoint, Object listener) {
     }
 
     private void notifyListenerRegistration(String registrationPoint, Object listener) {
-        if (listener instanceof InternalListener || listener instanceof ProjectEvaluationListener) {
+        if (isListenerSupportedWithConfigurationCache(listener)) {
             return;
         }
         getListenerManager().getBroadcaster(BuildScopeListenerRegistrationListener.class)
             .onBuildScopeListenerRegistration(listener, registrationPoint, this);
     }
 
+    private boolean isListenerSupportedWithConfigurationCache(Object listener) {
+        if (listener instanceof InternalListener) {
+            // Internal listeners are always allowed: we know their lifecycle and ensure there are no problems when configuration cache is reused.
+            return true;
+        }
+        if (JavaPropertyReflectionUtil.getAnnotation(listener.getClass(), DeprecatedInGradleScope.class) != null) {
+            // Explicitly unsupported Listener types are disallowed.
+            return false;
+        }
+        // We had to check for unsupported first to reject a listener that implements both allowed and disallowed interfaces.
+        // Just reject everything we don't know.
+        return listener instanceof ProjectEvaluationListener || listener instanceof TaskExecutionGraphListener || listener instanceof DependencyResolutionListener;
+    }
+
     @Override
     public void removeListener(Object listener) {
         // do same decoration as in addListener to remove correctly
@@ -426,6 +444,7 @@ public void removeListener(Object listener) {
 
     @Override
     public void useLogger(Object logger) {
+        notifyListenerRegistration("Gradle.useLogger", logger);
         getListenerManager().useLogger(logger);
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/plugin/management/internal/autoapply/AutoAppliedGradleEnterprisePlugin.java b/subprojects/core/src/main/java/org/gradle/plugin/management/internal/autoapply/AutoAppliedGradleEnterprisePlugin.java
index be4ec9e..92fa077 100644
--- a/subprojects/core/src/main/java/org/gradle/plugin/management/internal/autoapply/AutoAppliedGradleEnterprisePlugin.java
+++ b/subprojects/core/src/main/java/org/gradle/plugin/management/internal/autoapply/AutoAppliedGradleEnterprisePlugin.java
@@ -28,7 +28,7 @@ public final class AutoAppliedGradleEnterprisePlugin {
 
     public static final String GROUP = "com.gradle";
     public static final String NAME = "gradle-enterprise-gradle-plugin";
-    public static final String VERSION = "3.12.3";
+    public static final String VERSION = "3.13.2";
 
     public static final PluginId ID = new DefaultPluginId("com.gradle.enterprise");
     public static final PluginId BUILD_SCAN_PLUGIN_ID = new DefaultPluginId("com.gradle.build-scan");
diff --git a/subprojects/core/src/main/java/org/gradle/plugin/use/internal/PluginUseScriptBlockMetadataCompiler.java b/subprojects/core/src/main/java/org/gradle/plugin/use/internal/PluginUseScriptBlockMetadataCompiler.java
index dd20945..d31eb67 100644
--- a/subprojects/core/src/main/java/org/gradle/plugin/use/internal/PluginUseScriptBlockMetadataCompiler.java
+++ b/subprojects/core/src/main/java/org/gradle/plugin/use/internal/PluginUseScriptBlockMetadataCompiler.java
@@ -34,6 +34,7 @@
 import org.gradle.groovy.scripts.internal.ScriptBlock;
 
 import static org.gradle.groovy.scripts.internal.AstUtils.hasSingleConstantArgOfType;
+import static org.gradle.groovy.scripts.internal.AstUtils.hasSingleConstantStringArg;
 import static org.gradle.groovy.scripts.internal.AstUtils.hasSinglePropertyExpressionArgument;
 import static org.gradle.groovy.scripts.internal.AstUtils.isOfType;
 
@@ -99,7 +100,7 @@ public void visitMethodCallExpression(MethodCallExpression call) {
                                 restrict(call, formatErrorMessage(DISALLOWED_ALIAS_NOTATION));
                                 return;
                             case "id":
-                                ConstantExpression argumentExpression = hasSingleConstantArgOfType(call, String.class);
+                                ConstantExpression argumentExpression = hasSingleConstantStringArg(call);
                                 if (argumentExpression == null) {
                                     restrict(call, formatErrorMessage(NEED_LITERAL_STRING));
                                     return;
@@ -170,7 +171,7 @@ public void visitExpressionStatement(ExpressionStatement statement) {
      * b) A GString expression containing only variable expressions
      */
     private static boolean hasSimpleInterpolatedStringType(MethodCallExpression call) {
-        if (hasSingleConstantArgOfType(call, String.class) != null) {
+        if (hasSingleConstantStringArg(call) != null) {
             return true;
         }
 
@@ -192,6 +193,6 @@ private static boolean hasSimpleInterpolatedStringType(MethodCallExpression call
     }
 
     public String formatErrorMessage(String message) {
-        return String.format("%s%n%nSee %s for information on the plugins {} block%n%n", message, documentationRegistry.getDocumentationFor("plugins", "sec:plugins_block"));
+        return String.format("%s%n%n%s%n%n", message, documentationRegistry.getDocumentationRecommendationFor("information on the plugins {} block", "plugins", "sec:plugins_block"));
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/process/internal/CurrentProcess.java b/subprojects/core/src/main/java/org/gradle/process/internal/CurrentProcess.java
index 495a30d..b655f79 100644
--- a/subprojects/core/src/main/java/org/gradle/process/internal/CurrentProcess.java
+++ b/subprojects/core/src/main/java/org/gradle/process/internal/CurrentProcess.java
@@ -17,10 +17,13 @@
 package org.gradle.process.internal;
 
 import org.gradle.api.internal.file.FileCollectionFactory;
+import org.gradle.internal.agents.AgentUtils;
 import org.gradle.internal.jvm.JavaInfo;
 import org.gradle.internal.jvm.Jvm;
 
 import java.lang.management.ManagementFactory;
+import java.util.List;
+import java.util.stream.Collectors;
 
 public class CurrentProcess {
     private final JavaInfo jvm;
@@ -47,7 +50,11 @@ private static JvmOptions inferJvmOptions(FileCollectionFactory fileCollectionFa
         // Try to infer the effective jvm options for the currently running process.
         // We only care about 'managed' jvm args, anything else is unimportant to the running build
         JvmOptions jvmOptions = new JvmOptions(fileCollectionFactory);
-        jvmOptions.setAllJvmArgs(ManagementFactory.getRuntimeMXBean().getInputArguments());
+        List<String> arguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
+        // TODO(mlopatkin) figure out a nicer way of handling the presence of agent in the foreground daemon.
+        //  Currently it is hard to have a proper "-javaagent:/path/to/jar" in clients that start the daemon, so all code deals with a boolean flag shouldApplyAgent instead.
+        //  It is also possible to have the agent attached at runtime, without the flag, so flag checking is preferred.
+        jvmOptions.setAllJvmArgs(arguments.stream().filter(arg -> !AgentUtils.isGradleInstrumentationAgentSwitch(arg)).collect(Collectors.toList()));
         return jvmOptions;
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/process/internal/DefaultExecActionFactory.java b/subprojects/core/src/main/java/org/gradle/process/internal/DefaultExecActionFactory.java
index e093ca6..5fd04ac 100644
--- a/subprojects/core/src/main/java/org/gradle/process/internal/DefaultExecActionFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/process/internal/DefaultExecActionFactory.java
@@ -627,5 +627,15 @@ public JavaForkOptions copyTo(JavaForkOptions options) {
         public boolean isCompatibleWith(JavaForkOptions options) {
             return delegate.isCompatibleWith(options);
         }
+
+        @Override
+        public void setExtraJvmArgs(Iterable<?> jvmArgs) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void checkDebugConfiguration(Iterable<?> arguments) {
+            throw new UnsupportedOperationException();
+        }
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/process/internal/DefaultJavaExecSpec.java b/subprojects/core/src/main/java/org/gradle/process/internal/DefaultJavaExecSpec.java
index 351ee51..2193d29 100644
--- a/subprojects/core/src/main/java/org/gradle/process/internal/DefaultJavaExecSpec.java
+++ b/subprojects/core/src/main/java/org/gradle/process/internal/DefaultJavaExecSpec.java
@@ -21,6 +21,7 @@
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.jvm.ModularitySpec;
 import org.gradle.api.model.ObjectFactory;
+import org.gradle.api.provider.ListProperty;
 import org.gradle.api.provider.Property;
 import org.gradle.internal.file.PathToFileResolver;
 import org.gradle.internal.jvm.DefaultModularitySpec;
@@ -45,6 +46,7 @@ public class DefaultJavaExecSpec extends DefaultJavaForkOptions implements JavaE
     private final Property<String> mainClass;
     private final Property<String> mainModule;
     private final ModularitySpec modularity;
+    private final ListProperty<String> jvmArguments;
 
     private final FileCollectionFactory fileCollectionFactory;
     private ConfigurableFileCollection classpath;
@@ -56,6 +58,7 @@ public DefaultJavaExecSpec(
         FileCollectionFactory fileCollectionFactory
     ) {
         super(resolver, fileCollectionFactory, objectFactory.newInstance(DefaultJavaDebugOptions.class));
+        this.jvmArguments = objectFactory.listProperty(String.class);
         this.mainClass = objectFactory.property(String.class);
         this.mainModule = objectFactory.property(String.class);
         this.modularity = objectFactory.newInstance(DefaultModularitySpec.class);
@@ -180,6 +183,11 @@ public JavaExecSpec setErrorOutput(OutputStream errorOutput) {
     }
 
     @Override
+    public ListProperty<String> getJvmArguments() {
+        return jvmArguments;
+    }
+
+    @Override
     public Property<String> getMainClass() {
         return mainClass;
     }
diff --git a/subprojects/core/src/main/java/org/gradle/process/internal/DefaultJavaForkOptions.java b/subprojects/core/src/main/java/org/gradle/process/internal/DefaultJavaForkOptions.java
index 3308fd3..68e5e66 100755
--- a/subprojects/core/src/main/java/org/gradle/process/internal/DefaultJavaForkOptions.java
+++ b/subprojects/core/src/main/java/org/gradle/process/internal/DefaultJavaForkOptions.java
@@ -235,6 +235,16 @@ && containsAll(getEnvironment(), options.getEnvironment())
             && getBootstrapClasspath().getFiles().containsAll(options.getBootstrapClasspath().getFiles());
     }
 
+    @Override
+    public void checkDebugConfiguration(Iterable<?> arguments) {
+        options.checkDebugConfiguration(arguments);
+    }
+
+    @Override
+    public void setExtraJvmArgs(Iterable<?> arguments) {
+        options.setExtraJvmArgs(arguments);
+    }
+
     private static boolean hasJvmArgumentProviders(JavaForkOptions forkOptions) {
         return forkOptions instanceof DefaultJavaForkOptions
             && hasJvmArgumentProviders((DefaultJavaForkOptions) forkOptions);
diff --git a/subprojects/core/src/main/java/org/gradle/process/internal/JavaExecHandleBuilder.java b/subprojects/core/src/main/java/org/gradle/process/internal/JavaExecHandleBuilder.java
index 943ee85..b4e27d2 100644
--- a/subprojects/core/src/main/java/org/gradle/process/internal/JavaExecHandleBuilder.java
+++ b/subprojects/core/src/main/java/org/gradle/process/internal/JavaExecHandleBuilder.java
@@ -25,6 +25,7 @@
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.model.ObjectFactory;
+import org.gradle.api.provider.ListProperty;
 import org.gradle.api.provider.Property;
 import org.gradle.initialization.BuildCancellationToken;
 import org.gradle.internal.jvm.DefaultModularitySpec;
@@ -63,6 +64,7 @@ public class JavaExecHandleBuilder extends AbstractExecHandleBuilder implements
     private final JavaModuleDetector javaModuleDetector;
     private final Property<String> mainModule;
     private final Property<String> mainClass;
+    private final ListProperty<String> jvmArguments;
     private ConfigurableFileCollection classpath;
     private final JavaForkOptions javaOptions;
     private final ProcessArgumentsSpec applicationArgsSpec = new ProcessArgumentsSpec(this);
@@ -85,6 +87,7 @@ public JavaExecHandleBuilder(
         this.classpath = fileCollectionFactory.configurableFiles("classpath");
         this.mainModule = objectFactory.property(String.class);
         this.mainClass = objectFactory.property(String.class);
+        this.jvmArguments = objectFactory.listProperty(String.class);
         this.javaOptions = javaOptions;
         this.modularity = new DefaultModularitySpec(objectFactory);
         executable(javaOptions.getExecutable());
@@ -186,6 +189,11 @@ public JavaExecHandleBuilder jvmArgs(Object... arguments) {
     }
 
     @Override
+    public ListProperty<String> getJvmArguments() {
+        return jvmArguments;
+    }
+
+    @Override
     public Map<String, Object> getSystemProperties() {
         return javaOptions.getSystemProperties();
     }
diff --git a/subprojects/core/src/main/java/org/gradle/process/internal/JavaForkOptionsInternal.java b/subprojects/core/src/main/java/org/gradle/process/internal/JavaForkOptionsInternal.java
index 0dd582b..8c84758 100644
--- a/subprojects/core/src/main/java/org/gradle/process/internal/JavaForkOptionsInternal.java
+++ b/subprojects/core/src/main/java/org/gradle/process/internal/JavaForkOptionsInternal.java
@@ -25,4 +25,14 @@ public interface JavaForkOptionsInternal extends JavaForkOptions {
      */
     boolean isCompatibleWith(JavaForkOptions options);
 
+    /**
+     * Sets extra JVM arguments to a Java process without checking debug configuration.
+     */
+    void setExtraJvmArgs(Iterable<?> jvmArgs);
+
+
+    /**
+     * Checks supplied JVM arguments with purpose to ignore debug configuration in favor of the supplied arguments.
+     */
+    void checkDebugConfiguration(Iterable<?> arguments);
 }
diff --git a/subprojects/core/src/main/java/org/gradle/process/internal/JvmOptions.java b/subprojects/core/src/main/java/org/gradle/process/internal/JvmOptions.java
index 7066376..5420bf2 100644
--- a/subprojects/core/src/main/java/org/gradle/process/internal/JvmOptions.java
+++ b/subprojects/core/src/main/java/org/gradle/process/internal/JvmOptions.java
@@ -199,7 +199,46 @@ public void setJvmArgs(Iterable<?> arguments) {
         jvmArgs(arguments);
     }
 
+    public void setExtraJvmArgs(Iterable<?> arguments) {
+        extraJvmArgs.clear();
+        addExtraJvmArgs(arguments);
+    }
+
+    public void checkDebugConfiguration(Iterable<?> arguments) {
+        List<String> debugArgs = collectDebugArgs(arguments);
+        if (!debugArgs.isEmpty() && debugOptions.getEnabled().get()) {
+            LOGGER.warn("Debug configuration ignored in favor of the supplied JVM arguments: " + debugArgs);
+            debugOptions.getEnabled().set(false);
+        }
+    }
+
+    private static List<String> collectDebugArgs(Iterable<?> arguments) {
+        List<String> debugArgs = new ArrayList<>();
+        for (Object extraJvmArg : arguments) {
+            String extraJvmArgString = extraJvmArg.toString();
+            if (isDebugArg(extraJvmArgString)) {
+                debugArgs.add(extraJvmArgString);
+            }
+        }
+        return debugArgs;
+    }
+
+    private static boolean isDebugArg(String extraJvmArgString) {
+        return extraJvmArgString.equals("-Xdebug")
+            || extraJvmArgString.startsWith("-Xrunjdwp")
+            || extraJvmArgString.startsWith("-agentlib:jdwp");
+    }
+
     public void jvmArgs(Iterable<?> arguments) {
+        addExtraJvmArgs(arguments);
+        checkDebugConfiguration(extraJvmArgs);
+    }
+
+    public void jvmArgs(Object... arguments) {
+        jvmArgs(Arrays.asList(arguments));
+    }
+
+    private void addExtraJvmArgs(Iterable<?> arguments) {
         for (Object argument : arguments) {
             String argStr = argument.toString();
 
@@ -226,22 +265,6 @@ public void jvmArgs(Iterable<?> arguments) {
                 extraJvmArgs.add(argument);
             }
         }
-
-        List<String> debugArgs = new ArrayList<>();
-        for (Object extraJvmArg : extraJvmArgs) {
-            String extraJvmArgString = extraJvmArg.toString();
-            if (extraJvmArgString.equals("-Xdebug") || extraJvmArgString.startsWith("-Xrunjdwp") || extraJvmArgString.startsWith("-agentlib:jdwp")) {
-                debugArgs.add(extraJvmArgString);
-            }
-        }
-        if (!debugArgs.isEmpty() && debugOptions.getEnabled().get()) {
-            LOGGER.warn("Debug configuration ignored in favor of the supplied JVM arguments: " + debugArgs);
-            debugOptions.getEnabled().set(false);
-        }
-    }
-
-    public void jvmArgs(Object... arguments) {
-        jvmArgs(Arrays.asList(arguments));
     }
 
     public Map<String, Object> getMutableSystemProperties() {
diff --git a/subprojects/core/src/main/java/org/gradle/reporting/ReportRenderer.java b/subprojects/core/src/main/java/org/gradle/reporting/ReportRenderer.java
index d3142c8..07ab410 100644
--- a/subprojects/core/src/main/java/org/gradle/reporting/ReportRenderer.java
+++ b/subprojects/core/src/main/java/org/gradle/reporting/ReportRenderer.java
@@ -23,4 +23,4 @@ public abstract class ReportRenderer<T, E> {
      * Renders the report for the given model to the given output.
      */
     public abstract void render(T model, E output) throws IOException;
-}
\ No newline at end of file
+}
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 3db025b..d9af611 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
@@ -258,11 +258,6 @@ public boolean isImplicitBuild() {
         }
 
         @Override
-        public Path getCurrentPrefixForProjectsInChildBuilds() {
-            return Path.ROOT;
-        }
-
-        @Override
         public Path calculateIdentityPathForProject(Path projectPath) {
             return projectPath;
         }
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 e92a754..f383e7c 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
@@ -30,6 +30,7 @@
 
 import javax.annotation.Nullable;
 import java.io.File;
+import java.util.Collections;
 import java.util.Map;
 
 public class TestBuildScopeServices extends BuildScopeServices {
@@ -71,5 +72,10 @@ public Object find(String propertyName) {
         public Map<String, Object> mergeProperties(Map<String, Object> properties) {
             return properties;
         }
+
+        @Override
+        public Map<String, Object> getProperties() {
+            return Collections.emptyMap();
+        }
     }
 }
diff --git a/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestGlobalScopeServices.java b/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestGlobalScopeServices.java
index 1d07666..52896a8 100644
--- a/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestGlobalScopeServices.java
+++ b/subprojects/core/src/main/java/org/gradle/testfixtures/internal/TestGlobalScopeServices.java
@@ -18,6 +18,7 @@
 import org.gradle.cache.FileLockManager;
 import org.gradle.cache.internal.CacheFactory;
 import org.gradle.internal.Factory;
+import org.gradle.internal.agents.AgentStatus;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.logging.LoggingManagerInternal;
 import org.gradle.internal.logging.progress.ProgressLoggerFactory;
@@ -29,7 +30,7 @@
 
 public class TestGlobalScopeServices extends GlobalScopeServices {
     public TestGlobalScopeServices() {
-        super(false);
+        super(false, AgentStatus.disabled());
     }
 
     @Override
diff --git a/subprojects/core/src/main/java/org/gradle/util/VersionNumber.java b/subprojects/core/src/main/java/org/gradle/util/VersionNumber.java
index 1544711..cb03511 100644
--- a/subprojects/core/src/main/java/org/gradle/util/VersionNumber.java
+++ b/subprojects/core/src/main/java/org/gradle/util/VersionNumber.java
@@ -109,7 +109,7 @@ public VersionNumber getBaseVersion() {
 
     @Override
     public int compareTo(VersionNumber other) {
-        logDeprecation(8);
+        // TODO log deprecation once intellij/studio are fixed
         if (major != other.major) {
             return major - other.major;
         }
@@ -171,7 +171,7 @@ public static Scheme withPatchNumber() {
     }
 
     public static VersionNumber parse(String versionString) {
-        logDeprecation(8);
+        // TODO log deprecation once intellij/studio are fixed
         return DEFAULT_SCHEME.parse(versionString);
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/util/internal/DistributionLocator.java b/subprojects/core/src/main/java/org/gradle/util/internal/DistributionLocator.java
index bbe0443..c51b089 100644
--- a/subprojects/core/src/main/java/org/gradle/util/internal/DistributionLocator.java
+++ b/subprojects/core/src/main/java/org/gradle/util/internal/DistributionLocator.java
@@ -23,10 +23,14 @@
 
 public class DistributionLocator {
 
-    public static final String SERVICES_GRADLE_BASE_URL = "https://services.gradle.org";
+    private static final String SERVICES_GRADLE_BASE_URL_PROPERTY = "org.gradle.internal.services.base.url";
+    private static final String SERVICES_GRADLE_BASE_URL = "https://services.gradle.org";
+    private static final String RELEASE_REPOSITORY = "/distributions";
+    private static final String SNAPSHOT_REPOSITORY = "/distributions-snapshots";
 
-    public static final String RELEASE_REPOSITORY = SERVICES_GRADLE_BASE_URL + "/distributions";
-    private static final String SNAPSHOT_REPOSITORY = SERVICES_GRADLE_BASE_URL + "/distributions-snapshots";
+    public static String getBaseUrl() {
+        return System.getProperty(SERVICES_GRADLE_BASE_URL_PROPERTY, SERVICES_GRADLE_BASE_URL);
+    }
 
     public URI getDistributionFor(GradleVersion version) {
         return getDistributionFor(version, "bin");
@@ -38,9 +42,9 @@ public URI getDistributionFor(GradleVersion version, String type) {
 
     private String getDistributionRepository(GradleVersion version) {
         if (version.isSnapshot()) {
-            return SNAPSHOT_REPOSITORY;
+            return getBaseUrl() + SNAPSHOT_REPOSITORY;
         } else {
-            return RELEASE_REPOSITORY;
+            return getBaseUrl() + RELEASE_REPOSITORY;
         }
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/file/FileCollectionSymlinkTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/file/FileCollectionSymlinkTest.groovy
index 8f8e43b..0b3e7b8 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/file/FileCollectionSymlinkTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/file/FileCollectionSymlinkTest.groovy
@@ -18,15 +18,15 @@
 import org.gradle.api.Project
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.ProjectBuilder
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.gradle.util.UsesNativeServices
 import org.junit.ClassRule
 import spock.lang.Shared
 import spock.lang.Specification
 
-@Requires(TestPrecondition.SYMLINKS)
+@Requires(UnitTestPreconditions.Symlinks)
 @UsesNativeServices
 class FileCollectionSymlinkTest extends Specification {
     @Shared Project project = ProjectBuilder.builder().build()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultTaskTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultTaskTest.groovy
index db7cab9..70923ac 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultTaskTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/DefaultTaskTest.groovy
@@ -22,7 +22,7 @@
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.Project
 import org.gradle.api.Task
-import org.gradle.api.internal.project.taskfactory.TaskIdentity
+import org.gradle.api.internal.project.taskfactory.TestTaskIdentities
 import org.gradle.api.internal.tasks.InputChangesAwareTaskAction
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.tasks.AbstractTaskTest
@@ -59,7 +59,7 @@
 
     def "default task"() {
         given:
-        def identity = TaskIdentity.create(TEST_TASK_NAME, Task, project)
+        def identity = TestTaskIdentities.create(TEST_TASK_NAME, Task, project)
         Task task = AbstractTask.injectIntoNewInstance(project, identity, { TestUtil.newInstance(DefaultTask) } as Callable)
 
         expect:
@@ -75,7 +75,7 @@
 
     def "can inject values into task when using no-args constructor"() {
         given:
-        def identity = TaskIdentity.create(TEST_TASK_NAME, Task, project)
+        def identity = TestTaskIdentities.create(TEST_TASK_NAME, Task, project)
         def task = AbstractTask.injectIntoNewInstance(project, identity, { TestUtil.newInstance(DefaultTask) } as Callable)
 
         expect:
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultBuildIdentifierTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultBuildIdentifierTest.groovy
index e8dc33a..262308b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultBuildIdentifierTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultBuildIdentifierTest.groovy
@@ -17,23 +17,24 @@
 package org.gradle.api.internal.artifacts
 
 import org.gradle.util.Matchers
+import org.gradle.util.Path
 import spock.lang.Specification
 
 
 class DefaultBuildIdentifierTest extends Specification {
     def "create build from name"() {
         expect:
-        def id = new DefaultBuildIdentifier('thing')
-        id.name == 'thing'
+        def id = new DefaultBuildIdentifier(Path.path(":thing"))
+        id.buildPath == ':thing'
         id.currentBuild
-        id.toString() == "build 'thing'"
+        id.toString() == "build ':thing'"
     }
 
     def "has equals"() {
         expect:
-        def id = new DefaultBuildIdentifier('one')
-        def same = new DefaultBuildIdentifier('one')
-        def different = new DefaultBuildIdentifier('two')
+        def id = new DefaultBuildIdentifier(Path.path(":one"))
+        def same = new DefaultBuildIdentifier(Path.path(":one"))
+        def different = new DefaultBuildIdentifier(Path.path(":two"))
 
         Matchers.strictlyEquals(id, same)
         id != different
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultProjectComponentIdentifierTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultProjectComponentIdentifierTest.groovy
index 94a2361..2b5521b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultProjectComponentIdentifierTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/artifacts/DefaultProjectComponentIdentifierTest.groovy
@@ -48,15 +48,11 @@
     }
 
     private static newProjectId(String path) {
-        newProjectId(buildId(":"), path)
+        newProjectId(DefaultBuildIdentifier.ROOT, path)
     }
 
     private static newProjectId(BuildIdentifier build, String path) {
         new DefaultProjectComponentIdentifier(build, Path.path(path), Path.path(path), "name")
     }
 
-    private static buildId(String name) {
-        return new DefaultBuildIdentifier(name)
-    }
-
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/catalog/parser/TomlCatalogFileParserTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/catalog/parser/TomlCatalogFileParserTest.groovy
index 4fb4123..525fc21 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/catalog/parser/TomlCatalogFileParserTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/catalog/parser/TomlCatalogFileParserTest.groovy
@@ -33,6 +33,7 @@
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
+import java.nio.file.Paths
 import java.util.function.Supplier
 
 class TomlCatalogFileParserTest extends Specification implements VersionCatalogErrorMessages {
@@ -349,7 +350,7 @@
 
         then:
         InvalidUserDataException ex = thrown()
-        ex.message == "On library declaration 'guava' expected to find any of 'group', 'module', 'name' or 'version' but found unexpected ${error}."
+        ex.message == "On library declaration 'guava' expected to find any of 'group', 'module', 'name', or 'version' but found unexpected ${error}."
 
         where:
         i | error
@@ -364,7 +365,7 @@
 
         then:
         InvalidUserDataException ex = thrown()
-        ex.message == "On version declaration of alias 'guava' expected to find any of 'prefer', 'ref', 'reject', 'rejectAll', 'require' or 'strictly' but found unexpected ${error}."
+        ex.message == "On version declaration of alias 'guava' expected to find any of 'prefer', 'ref', 'reject', 'rejectAll', 'require', or 'strictly' but found unexpected ${error}."
 
         where:
         i | error
@@ -412,19 +413,15 @@
     }
 
     private void parse(String name) {
-        TomlCatalogFileParser.parse(toml(name), builder)
+        def tomlResource = getClass().getResource("/org/gradle/api/internal/catalog/parser/${name}.toml").toURI()
+        // Paths might be unusual, but we need it because of 1.8
+        def tomlPath = Paths.get(tomlResource)
+
+        TomlCatalogFileParser.parse(tomlPath, builder)
         model = builder.build()
         assert model != null: "Expected model to be generated but it wasn't"
     }
 
-    private static InputStream toml(String name) {
-        return TomlCatalogFileParserTest.class.getResourceAsStream("${name}.toml").withReader("utf-8") {
-            String text = it.text
-            // we're using an in-memory input stream to make sure we don't accidentally leak descriptors in tests
-            return new ByteArrayInputStream(text.getBytes("utf-8"))
-        }
-    }
-
     @CompileStatic
     private static class DependencySpec {
         private final DependencyModel model
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionModeResolverTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionModeResolverTest.groovy
index ef76348..38f0c4f 100755
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionModeResolverTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/changedetection/changes/DefaultTaskExecutionModeResolverTest.groovy
@@ -20,18 +20,11 @@
 import org.gradle.api.internal.TaskInputsInternal
 import org.gradle.api.internal.TaskInternal
 import org.gradle.api.internal.TaskOutputsInternal
-import org.gradle.api.internal.changedetection.TaskExecutionMode
 import org.gradle.api.internal.project.taskfactory.IncrementalTaskAction
 import org.gradle.api.internal.tasks.properties.TaskProperties
 import org.gradle.api.specs.AndSpec
 import spock.lang.Specification
 
-import static org.gradle.api.internal.changedetection.TaskExecutionMode.INCREMENTAL
-import static org.gradle.api.internal.changedetection.TaskExecutionMode.NO_OUTPUTS
-import static org.gradle.api.internal.changedetection.TaskExecutionMode.RERUN_TASKS_ENABLED
-import static org.gradle.api.internal.changedetection.TaskExecutionMode.UNTRACKED
-import static org.gradle.api.internal.changedetection.TaskExecutionMode.UP_TO_DATE_WHEN_FALSE
-
 class DefaultTaskExecutionModeResolverTest extends Specification {
 
     def startParameter = new StartParameter()
@@ -50,20 +43,20 @@
 
     def "untracked"() {
         when:
-        TaskExecutionMode state = repository.getExecutionMode(task, taskProperties)
+        def state = repository.getExecutionMode(task, taskProperties)
 
         then:
-        state == UNTRACKED
+        state == DefaultTaskExecutionMode.untracked("For testing")
         _ * task.getReasonNotToTrackState() >> Optional.of("For testing")
         0 * _
     }
 
     def "no outputs"() {
         when:
-        TaskExecutionMode state = repository.getExecutionMode(task, taskProperties)
+        def state = repository.getExecutionMode(task, taskProperties)
 
         then:
-        state == NO_OUTPUTS
+        state == DefaultTaskExecutionMode.noOutputs()
         1 * taskProperties.hasDeclaredOutputs() >> false
         1 * upToDateSpec.isEmpty() >> true
         _ * task.getTaskActions() >> []
@@ -72,10 +65,10 @@
 
     def "default"() {
         when:
-        TaskExecutionMode state = repository.getExecutionMode(task, taskProperties)
+        def state = repository.getExecutionMode(task, taskProperties)
 
         then:
-        state == INCREMENTAL
+        state == DefaultTaskExecutionMode.incremental()
         1 * taskProperties.hasDeclaredOutputs() >> true
         1 * upToDateSpec.isSatisfiedBy(task) >> true
         0 * _
@@ -87,7 +80,7 @@
         def state = repository.getExecutionMode(task, taskProperties)
 
         then:
-        state == RERUN_TASKS_ENABLED
+        state == DefaultTaskExecutionMode.rerunTasksEnabled()
         1 * taskProperties.hasDeclaredOutputs() >> false
         1 * upToDateSpec.empty >> false
         0 * _
@@ -98,7 +91,7 @@
         def state = repository.getExecutionMode(task, taskProperties)
 
         then:
-        state == UP_TO_DATE_WHEN_FALSE
+        state == DefaultTaskExecutionMode.upToDateWhenFalse()
         1 * taskProperties.hasDeclaredOutputs() >> true
         1 * upToDateSpec.isSatisfiedBy(task) >> false
         0 * _
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverSpec.groovy
index 6fc7203..af31213 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverSpec.groovy
@@ -20,8 +20,8 @@
 import org.gradle.internal.typeconversion.UnsupportedNotationException
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.UsesNativeServices
 import org.junit.Assume
 import org.junit.Rule
@@ -36,7 +36,7 @@
     @Rule
     TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass())
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "normalizes absolute path which points to an absolute link"() {
         def target = createFile(new File(tmpDir.testDirectory, 'target.txt'))
         def file = new File(tmpDir.testDirectory, 'a/other.txt')
@@ -47,7 +47,7 @@
         normalize(file) == file
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "normalizes absolute path which points to a relative link"() {
         def target = createFile(new File(tmpDir.testDirectory, 'target.txt'))
         def file = new File(tmpDir.testDirectory, 'a/other.txt')
@@ -58,7 +58,7 @@
         normalize(file) == file
     }
 
-    @Requires(TestPrecondition.CASE_INSENSITIVE_FS)
+    @Requires(UnitTestPreconditions.CaseInsensitiveFs)
     def "does not normalize case"() {
         def file = createFile(new File(tmpDir.testDirectory, 'dir/file.txt'))
         def path = new File(tmpDir.testDirectory, 'dir/FILE.txt')
@@ -68,7 +68,7 @@
         normalize(path) == path
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "normalizes path which points to a link to something that does not exist"() {
         def file = new File(tmpDir.testDirectory, 'a/other.txt')
         createLink(file, 'unknown.txt')
@@ -78,7 +78,7 @@
         normalize(file) == file
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "normalizes path when ancestor is an absolute link"() {
         def target = createFile(new File(tmpDir.testDirectory, 'target/file.txt'))
         def file = new File(tmpDir.testDirectory, 'a/b/file.txt')
@@ -105,7 +105,7 @@
         normalize(".", baseDir) == baseDir
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "normalizes relative path when base dir is a link"() {
         createFile(new File(tmpDir.testDirectory, 'target/file.txt'))
         def baseDir = new File(tmpDir.testDirectory, 'base')
@@ -117,7 +117,7 @@
         normalize('file.txt', baseDir) == file
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "does not normalize windows 8.3 names"() {
         createFile(new File(tmpDir.testDirectory, 'dir/file-with-long-name.txt'))
         def path = new File(tmpDir.testDirectory, 'dir/FILE-W~1.TXT')
@@ -135,7 +135,7 @@
         root << getFsRoots().collect { it.absolutePath }
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "normalizes non-existent file system root"() {
         def file = nonexistentFsRoot()
         assert !file.exists()
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverTest.groovy
index 4c9228d..caa6e19 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/file/BaseDirFileResolverTest.groovy
@@ -18,7 +18,7 @@
 import org.gradle.api.InvalidUserDataException
 import org.gradle.api.PathValidation
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.PreconditionVerifier
+import org.gradle.test.precondition.PreconditionVerifier
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -26,8 +26,8 @@
 import java.util.concurrent.Callable
 
 import static org.hamcrest.CoreMatchers.equalTo
-import static org.junit.Assert.assertEquals
 import static org.hamcrest.MatcherAssert.assertThat
+import static org.junit.Assert.assertEquals
 import static org.junit.Assert.fail
 
 class BaseDirFileResolverTest {
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerTest.groovy
index d677457..2c65ba3 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/initialization/DefaultScriptHandlerTest.groovy
@@ -15,13 +15,12 @@
  */
 package org.gradle.api.internal.initialization
 
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.artifacts.DependencyConstraintSet
-import org.gradle.api.artifacts.dsl.DependencyConstraintHandler
+
 import org.gradle.api.artifacts.dsl.DependencyHandler
 import org.gradle.api.artifacts.dsl.RepositoryHandler
 import org.gradle.api.internal.artifacts.DependencyResolutionServices
-import org.gradle.api.internal.attributes.AttributeContainerInternal
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import org.gradle.api.internal.attributes.AttributesSchemaInternal
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.internal.classloader.ClasspathUtil
@@ -32,9 +31,7 @@
 class DefaultScriptHandlerTest extends Specification {
     def repositoryHandler = Mock(RepositoryHandler)
     def dependencyHandler = Mock(DependencyHandler)
-    def dependencyConstraintHandler = Mock(DependencyConstraintHandler)
-    def dependencyConstraintSet = Mock(DependencyConstraintSet)
-    def configurationContainer = Mock(ConfigurationContainer)
+    def configurationContainer = Mock(RoleBasedConfigurationContainerInternal)
     def configuration = Mock(ResettableConfiguration)
     def scriptSource = Stub(ScriptSource)
     def depMgmtServices = Mock(DependencyResolutionServices) {
@@ -46,7 +43,6 @@
     }
     def classpathResolver = Mock(ScriptClassPathResolver)
     def handler = new DefaultScriptHandler(scriptSource, depMgmtServices, classLoaderScope, classpathResolver)
-    def attributes = Mock(AttributeContainerInternal)
 
     def "adds classpath configuration when configuration container is queried"() {
         when:
@@ -56,7 +52,7 @@
         then:
         1 * depMgmtServices.configurationContainer >> configurationContainer
         1 * depMgmtServices.dependencyHandler >> dependencyHandler
-        1 * configurationContainer.create('classpath') >> configuration
+        1 * configurationContainer.createWithRole('classpath', ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET) >> configuration
         1 * classpathResolver.prepareClassPath(configuration, dependencyHandler)
         0 * configurationContainer._
         0 * depMgmtServices._
@@ -70,7 +66,7 @@
         then:
         1 * depMgmtServices.configurationContainer >> configurationContainer
         1 * depMgmtServices.dependencyHandler >> dependencyHandler
-        1 * configurationContainer.create('classpath') >> configuration
+        1 * configurationContainer.createWithRole('classpath', ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET) >> configuration
         1 * classpathResolver.prepareClassPath(configuration, dependencyHandler)
         0 * configurationContainer._
         0 * depMgmtServices._
@@ -101,7 +97,7 @@
         and:
         1 * depMgmtServices.configurationContainer >> configurationContainer
         1 * depMgmtServices.dependencyHandler >> dependencyHandler
-        1 * configurationContainer.create('classpath') >> configuration
+        1 * configurationContainer.createWithRole('classpath', ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET) >> configuration
         1 * classpathResolver.prepareClassPath(configuration, dependencyHandler)
         1 * classpathResolver.resolveClassPath(configuration) >> classpath
     }
@@ -137,7 +133,7 @@
         then:
         1 * depMgmtServices.dependencyHandler >> dependencyHandler
         1 * depMgmtServices.configurationContainer >> configurationContainer
-        1 * configurationContainer.create('classpath') >> configuration
+        1 * configurationContainer.createWithRole('classpath', ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET) >> configuration
         1 * classpathResolver.prepareClassPath(configuration, dependencyHandler)
         1 * dependencyHandler.add('config', 'dep')
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
index b396536..611f9e8 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/DefaultProjectTest.groovy
@@ -26,7 +26,6 @@
 import org.gradle.api.ProjectEvaluationListener
 import org.gradle.api.Task
 import org.gradle.api.UnknownProjectException
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.dsl.ArtifactHandler
 import org.gradle.api.artifacts.dsl.ComponentMetadataHandler
 import org.gradle.api.artifacts.dsl.DependencyFactory
@@ -42,6 +41,7 @@
 import org.gradle.api.internal.artifacts.Module
 import org.gradle.api.internal.artifacts.ProjectBackedModule
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import org.gradle.api.internal.collections.DomainObjectCollectionFactory
 import org.gradle.api.internal.file.DefaultProjectLayout
 import org.gradle.api.internal.file.FileCollectionFactory
@@ -136,7 +136,7 @@
     Factory<AntBuilder> antBuilderFactoryMock = Stub(Factory)
     AntBuilder testAntBuilder
 
-    ConfigurationContainer configurationContainerMock = Stub(ConfigurationContainer)
+    RoleBasedConfigurationContainerInternal configurationContainerMock = Stub(RoleBasedConfigurationContainerInternal)
     RepositoryHandler repositoryHandlerMock = Stub(RepositoryHandler)
     DependencyHandler dependencyHandlerMock = Stub(DependencyHandler)
     DependencyFactory dependencyFactoryMock = Stub(DependencyFactory)
@@ -193,7 +193,7 @@
         serviceRegistryMock.get(TaskContainerInternal) >> taskContainerMock
         taskContainerMock.getTasksAsDynamicObject() >> new BeanDynamicObject(new TaskContainerDynamicObject(someTask: testTask))
         serviceRegistryMock.get((Type) RepositoryHandler) >> repositoryHandlerMock
-        serviceRegistryMock.get(ConfigurationContainer) >> configurationContainerMock
+        serviceRegistryMock.get(RoleBasedConfigurationContainerInternal) >> configurationContainerMock
         serviceRegistryMock.get(ArtifactHandler) >> Stub(ArtifactHandler)
         serviceRegistryMock.get(DependencyHandler) >> dependencyHandlerMock
         serviceRegistryMock.get(DependencyFactory) >> dependencyFactoryMock
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/NewDefaultProjectTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/NewDefaultProjectTest.groovy
index a998eac..81c9beb 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/NewDefaultProjectTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/NewDefaultProjectTest.groovy
@@ -17,53 +17,11 @@
 package org.gradle.api.internal.project
 
 import org.gradle.api.InvalidUserDataException
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.artifacts.dsl.ArtifactHandler
-import org.gradle.api.artifacts.dsl.DependencyHandler
 import org.gradle.test.fixtures.AbstractProjectBuilderSpec
 
 import static org.gradle.util.TestUtil.createChildProject
 
 class NewDefaultProjectTest extends AbstractProjectBuilderSpec {
-
-    void "delegates to artifacts handler"() {
-        def handler = Mock(ArtifactHandler)
-        project.artifactHandler = handler
-
-        when:
-        project.artifacts {
-            add('conf', 'art')
-        }
-
-        then:
-        1 * handler.add('conf', 'art')
-    }
-
-    void "delegates to dependency handler"() {
-        def handler = Mock(DependencyHandler)
-        project.dependencyHandler = handler
-
-        when:
-        project.dependencies {
-            add('conf', 'dep')
-        }
-
-        then:
-        1 * handler.add('conf', 'dep')
-    }
-
-    void "delegates to configuration container"() {
-        Closure cl = {}
-        def container = Mock(ConfigurationContainer)
-        project.configurationContainer = container
-
-        when:
-        project.configurations cl
-
-        then:
-        1 * container.configure(cl)
-    }
-
     def "provides all tasks recursively"() {
         def a = createChildProject(project, "a")
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactoryTest.groovy
index 41c6204..7a50fcf 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/project/taskfactory/AnnotationProcessingTaskFactoryTest.groovy
@@ -928,7 +928,8 @@
 
     private <T extends TaskInternal> T expectTaskCreated(final Class<T> type, final Object... params) {
         final String name = "task"
-        T task = AbstractTask.injectIntoNewInstance(project, TaskIdentity.create(name, type, project), new Callable<T>() {
+        def taskIdentity = TestTaskIdentities.create(name, type, project)
+        T task = AbstractTask.injectIntoNewInstance(project, taskIdentity, new Callable<T>() {
             T call() throws Exception {
                 if (params.length > 0) {
                     // TODO: This should be using objectFactory too because that more closely matches what the production code does.
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerTest.groovy
index 86e2a33..4561489 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/DefaultTaskContainerTest.groovy
@@ -35,6 +35,7 @@
 import org.gradle.api.internal.project.taskfactory.TaskFactory
 import org.gradle.api.internal.project.taskfactory.TaskIdentity
 import org.gradle.api.internal.project.taskfactory.TaskInstantiator
+import org.gradle.api.internal.project.taskfactory.TestTaskIdentities
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.provider.Provider
 import org.gradle.api.tasks.TaskDependency
@@ -47,6 +48,7 @@
 
 class DefaultTaskContainerTest extends AbstractPolymorphicDomainObjectContainerSpec<Task> {
 
+    private taskIdentityFactory = TestTaskIdentities.factory()
     private taskFactory = Mock(ITaskFactory)
     private project = Mock(ProjectInternal, name: "<project>") {
         identityPath(_) >> { String name ->
@@ -65,12 +67,12 @@
         getServices() >> Mock(ServiceRegistry)
         getTaskDependencyFactory() >> TestFiles.taskDependencyFactory()
         getObjects() >> Stub(ObjectFactory)
-    }
-    private taskCount = 1
+    } as ProjectInternal
     private container = new DefaultTaskContainerFactory(
         DirectInstantiator.INSTANCE,
+        taskIdentityFactory,
         taskFactory,
-        project,
+        project as ProjectInternal,
         new TaskStatistics(),
         buildOperationExecutor,
         new BuildOperationCrossProjectConfigurator(buildOperationExecutor),
@@ -1464,7 +1466,7 @@
         thrown(UnsupportedOperationException)
     }
 
-    def factory = new TaskInstantiator(new TaskFactory().createChild(project, TestUtil.instantiatorFactory().decorateScheme()), project)
+    def factory = new TaskInstantiator(taskIdentityFactory, new TaskFactory().createChild(project, TestUtil.instantiatorFactory().decorateScheme()), project)
     SomeTask a = factory.create("a", SomeTask)
     SomeTask b = factory.create("b", SomeTask)
     SomeTask c = factory.create("c", SomeTask)
@@ -1622,11 +1624,12 @@
     }
 
     private <U extends TaskInternal> U task(final String name, Class<U> type) {
-        Mock(type, name: "[task" + taskCount++ + "]") {
+        def taskId = taskIdentityFactory.create(name, type, project)
+        Mock(type, name: "[task" + taskId.id + "]") {
             getName() >> name
             getTaskDependency() >> Mock(TaskDependency)
-            getTaskIdentity() >> TaskIdentity.create(name, type, project)
-        }
+            getTaskIdentity() >> taskId
+        } as U
     }
 
     private Task addTask(String name) {
@@ -1648,6 +1651,4 @@
     interface CustomTask extends TaskInternal {}
 
     interface MyCustomTask extends CustomTask {}
-
-    interface AnotherCustomTask extends TaskInternal {}
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/SnapshotTaskInputsBuildOperationResultTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/SnapshotTaskInputsBuildOperationResultTest.groovy
index fe45d88..ed31ed4 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/SnapshotTaskInputsBuildOperationResultTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/SnapshotTaskInputsBuildOperationResultTest.groovy
@@ -47,8 +47,8 @@
 import org.gradle.internal.fingerprint.impl.DefaultFileSystemLocationFingerprint
 import org.gradle.internal.hash.TestHashCodes
 import org.gradle.internal.snapshot.TestSnapshotFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Specification
 
 import static org.gradle.api.internal.tasks.SnapshotTaskInputsBuildOperationResult.FilePropertyAttribute.FINGERPRINTING_STRATEGY_ABSOLUTE_PATH
@@ -103,7 +103,7 @@
         def t = thrown(IllegalStateException)
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "properly visits structure when ignoring directories"() {
         given:
         def visitor = Mock(SnapshotTaskInputsBuildOperationType.Result.InputFilePropertyVisitor)
@@ -168,7 +168,7 @@
         0 * visitor._
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "properly visits structure when ignoring only the root directory"() {
         given:
         def visitor = Mock(SnapshotTaskInputsBuildOperationType.Result.InputFilePropertyVisitor)
@@ -220,7 +220,7 @@
         0 * visitor._
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "properly visits structure when not ignoring directories"() {
         given:
         def visitor = Mock(SnapshotTaskInputsBuildOperationType.Result.InputFilePropertyVisitor)
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuterTest.groovy
index efb6808..e87a051 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ExecuteActionsTaskExecuterTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.internal.TaskInternal
-import org.gradle.api.internal.TaskOutputsInternal
-import org.gradle.api.internal.changedetection.TaskExecutionMode
+import org.gradle.api.internal.TaskOutputsEnterpriseInternal
+import org.gradle.api.internal.changedetection.changes.DefaultTaskExecutionMode
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.tasks.InputChangesAwareTaskAction
@@ -103,7 +103,7 @@
 class ExecuteActionsTaskExecuterTest extends Specification {
     private final DocumentationRegistry documentationRegistry = new DocumentationRegistry()
     def task = Mock(TaskInternal)
-    def taskOutputs = Mock(TaskOutputsInternal)
+    def taskOutputs = Mock(TaskOutputsEnterpriseInternal)
     def action1 = Mock(InputChangesAwareTaskAction) {
         getActionImplementation(_ as ClassLoaderHierarchyHasher) >> ImplementationSnapshot.of("Action1", TestHashCodes.hashCodeFrom(1234))
     }
@@ -212,7 +212,7 @@
         taskOutputs.setPreviousOutputFiles(_ as FileCollection)
         project.getBuildScriptSource() >> scriptSource
         task.getStandardOutputCapture() >> standardOutputCapture
-        executionContext.getTaskExecutionMode() >> TaskExecutionMode.INCREMENTAL
+        executionContext.getTaskExecutionMode() >> DefaultTaskExecutionMode.incremental()
         executionContext.getTaskProperties() >> taskProperties
         executionContext.getValidationContext() >> validationContext
         executionContext.getValidationAction() >> { { c -> } as TaskExecutionContext.ValidationAction }
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ResolveTaskExecutionModeExecuterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ResolveTaskExecutionModeExecuterTest.groovy
index 64f139e..8e4cfde 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ResolveTaskExecutionModeExecuterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/execution/ResolveTaskExecutionModeExecuterTest.groovy
@@ -17,8 +17,8 @@
 package org.gradle.api.internal.tasks.execution
 
 import org.gradle.api.internal.TaskInternal
-import org.gradle.api.internal.changedetection.TaskExecutionMode
 import org.gradle.api.internal.changedetection.TaskExecutionModeResolver
+import org.gradle.api.internal.changedetection.changes.DefaultTaskExecutionMode
 import org.gradle.api.internal.tasks.TaskExecuter
 import org.gradle.api.internal.tasks.TaskExecuterResult
 import org.gradle.api.internal.tasks.TaskExecutionContext
@@ -35,7 +35,7 @@
     final taskState = Mock(TaskStateInternal)
     final taskContext = Mock(TaskExecutionContext)
     final repository = Mock(TaskExecutionModeResolver)
-    final executionMode = TaskExecutionMode.INCREMENTAL
+    final executionMode = DefaultTaskExecutionMode.incremental()
 
     final executer = new ResolveTaskExecutionModeExecuter(repository, delegate)
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/options/OptionReaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/options/OptionReaderTest.groovy
index 1f18076..abbd1b1 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/options/OptionReaderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/internal/tasks/options/OptionReaderTest.groovy
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.tasks.options
 
-import org.gradle.api.Project
+import org.gradle.api.internal.tasks.TaskOptionsGenerator
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.options.Option
 import org.gradle.api.tasks.options.OptionValues
@@ -26,17 +26,16 @@
 class OptionReaderTest extends Specification {
 
     OptionReader reader
-    Project project
     int builtInOptionCount
 
     def setup() {
         reader = new OptionReader()
-        builtInOptionCount = OptionReader.BUILT_IN_OPTIONS.size();
+        builtInOptionCount = TaskOptionsGenerator.BUILT_IN_OPTIONS.size();
     }
 
     def "can read options linked to setter methods of a task"() {
         when:
-        List<InstanceOptionDescriptor> options = reader.getOptions(new TestClassWithSetters())
+        List<InstanceOptionDescriptor> options = TaskOptionsGenerator.generate(new TestClassWithSetters(), reader).getAll()
         then:
         options[0].name == "aFlag"
         options[0].description == "simple flag"
@@ -58,20 +57,30 @@
         options[3].argumentType == List
         options[3].availableValues == [] as Set
 
-        options[4].name == "objectValue"
-        options[4].description == "object value"
-        options[4].argumentType == String
+        options[4].name == "no-aFlag"
+        options[4].description == "Disables option --aFlag"
+        options[4].argumentType == Void.TYPE
         options[4].availableValues == [] as Set
 
-        options[5].name == "stringValue"
-        options[5].description == "string value"
-        options[5].argumentType == String
-        options[5].availableValues == ["dynValue1", "dynValue2"] as Set
+        options[5].name == "no-booleanValue"
+        options[5].description == "Disables option --booleanValue"
+        options[5].argumentType == Void.TYPE
+        options[5].availableValues == [] as Set
+
+        options[6].name == "objectValue"
+        options[6].description == "object value"
+        options[6].argumentType == String
+        options[6].availableValues == [] as Set
+
+        options[7].name == "stringValue"
+        options[7].description == "string value"
+        options[7].argumentType == String
+        options[7].availableValues == ["dynValue1", "dynValue2"] as Set
     }
 
     def "can read options linked to property getter methods of a task"() {
         when:
-        List<InstanceOptionDescriptor> options = reader.getOptions(new TestClassWithProperties())
+        List<InstanceOptionDescriptor> options = TaskOptionsGenerator.generate(new TestClassWithProperties(), reader).getAll()
         then:
         options[0].name == "booleanValue"
         options[0].description == "boolean value"
@@ -83,26 +92,32 @@
         options[1].argumentType == String
         options[1].availableValues == ["ABC", "DEF"] as Set
 
-        options[2].name == "objectValue"
-        options[2].description == "object value"
-        options[2].argumentType == String
+        options[2].name == "no-booleanValue"
+        options[2].description == "Disables option --booleanValue"
+        options[2].argumentType == Void.TYPE
         options[2].availableValues == [] as Set
 
-        options[3].name == "stringValue"
-        options[3].description == "string value"
+        options[3].name == "objectValue"
+        options[3].description == "object value"
         options[3].argumentType == String
-        options[3].availableValues == ["dynValue1", "dynValue2"] as Set
+        options[3].availableValues == [] as Set
+
+        options[4].name == "stringValue"
+        options[4].description == "string value"
+        options[4].argumentType == String
+        options[4].availableValues == ["dynValue1", "dynValue2"] as Set
     }
 
     def "built-in options appear last"() {
         when:
-        List<InstanceOptionDescriptor> options = reader.getOptions(new TestClassWithProperties())
-        int ownOptions = 4
+        List<OptionDescriptor> options = TaskOptionsGenerator.generate(new TestClassWithProperties(), reader).getAll()
+        int ownOptions = 5
         then:
-        options.size() == ownOptions + OptionReader.BUILT_IN_OPTIONS.size()
-        OptionReader.BUILT_IN_OPTIONS.values().eachWithIndex { BuiltInOptionElement entry, int i ->
-            assert options[ownOptions + i].name == entry.optionName
-            assert options[ownOptions + i].description == entry.description
+        options.forEach {it -> System.out.println(it.name + " " + it.description)}
+
+        TaskOptionsGenerator.BUILT_IN_OPTIONS.eachWithIndex { BuiltInOptionElement optionElement, int i ->
+            assert options[ownOptions + i].name == optionElement.optionName
+            assert options[ownOptions + i].description == optionElement.description
             assert options[ownOptions + i].argumentType == Void.TYPE
             assert options[ownOptions + i].availableValues == [] as Set
         }
@@ -110,20 +125,34 @@
 
     def "task own options shadow built-in options"() {
         when:
-        List<InstanceOptionDescriptor> options = reader.getOptions(new TestClassWithOptionNameClashing())
+        List<InstanceOptionDescriptor> options = TaskOptionsGenerator.generate(new TestClassWithOptionNameClashing(), reader).getAll()
         int ownOptions = 2
         List<String> clashingOptions = ["rerun"]
         then:
-        options.size() == ownOptions + OptionReader.BUILT_IN_OPTIONS.size() - clashingOptions.size()
+        options.size() == ownOptions + builtInOptionCount - clashingOptions.size()
         options[0].name == "rerun"
         options[0].description == "custom clashing option"
         options[1].name == "unique"
         options[1].description == "custom unique option"
     }
 
+    def "task own options shadow generated opposite options"() {
+        when:
+        List<InstanceOptionDescriptor> options = TaskOptionsGenerator.generate(new TestClassWithOppositeOptionNameClashing(), reader).getAll()
+        int ownOptions = 2
+        then:
+        options.size() == ownOptions + builtInOptionCount
+        options[0].name == "my-option"
+        options[0].description == "Option to trigger creation of opposite option"
+        options[1].name == "no-my-option"
+        options[1].description == "Option clashing with opposite option"
+        options[2].name == "rerun"
+        options[2].description == "Causes the task to be re-run even if up-to-date."
+    }
+
     def "fail when multiple methods define same option"() {
         when:
-        reader.getOptions(new TestClass2())
+        TaskOptionsGenerator.generate(new TestClass2(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "@Option 'stringValue' linked to multiple elements in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass2'."
@@ -131,7 +160,7 @@
 
     def "fail when multiple methods from different types define same option"() {
         when:
-        reader.getOptions(new WithDuplicateOptionInAnotherInterface())
+        TaskOptionsGenerator.generate(new WithDuplicateOptionInAnotherInterface(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "@Option 'stringValue' linked to multiple elements in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$WithDuplicateOptionInAnotherInterface'."
@@ -139,7 +168,7 @@
 
     def "fails on static methods"() {
         when:
-        reader.getOptions(new TestClass31())
+        TaskOptionsGenerator.generate(new TestClass31(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "@Option on static method 'setStaticString' not supported in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass31'."
@@ -147,7 +176,7 @@
 
     def "fails on static fields"() {
         when:
-        reader.getOptions(new TestClass32())
+        TaskOptionsGenerator.generate(new TestClass32(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "@Option on static field 'staticField' not supported in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass32'."
@@ -155,7 +184,7 @@
 
     def "fail when parameter cannot be converted from the command-line"() {
         when:
-        reader.getOptions(new TestClass5())
+        TaskOptionsGenerator.generate(new TestClass5(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "Option 'fileValue' cannot be cast to type 'java.io.File' in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass5'."
@@ -163,7 +192,7 @@
 
     def "fails when method has > 1 parameter"() {
         when:
-        reader.getOptions(new TestClass4());
+        TaskOptionsGenerator.generate(new TestClass4(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "Option 'stringValue' on method cannot take multiple parameters in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass4#setStrings'."
@@ -171,7 +200,7 @@
 
     def "handles field options"() {
         when:
-        List<InstanceOptionDescriptor> options = reader.getOptions(new TestClassWithFields())
+        List<InstanceOptionDescriptor> options = TaskOptionsGenerator.generate(new TestClassWithFields(), reader).getAll()
         then:
         options[0].name == "customOptionName"
         options[0].description == "custom description"
@@ -200,7 +229,7 @@
 
     def "handles property field options"() {
         when:
-        List<InstanceOptionDescriptor> options = reader.getOptions(new TestClassWithPropertyField())
+        List<InstanceOptionDescriptor> options = TaskOptionsGenerator.generate(new TestClassWithPropertyField(), reader).getAll()
         then:
         options[0].name == "customOptionName"
         options[0].description == "custom description"
@@ -225,13 +254,13 @@
 
     def "throws decent error when description not set"() {
         when:
-        reader.getOptions(new TestClass7());
+        TaskOptionsGenerator.generate(new TestClass7(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "No description set on option 'aValue' at for class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass7'."
 
         when:
-        reader.getOptions(new TestClass8());
+        TaskOptionsGenerator.generate(new TestClass8(), reader).getAll()
         then:
         e = thrown(OptionValidationException)
         e.message == "No description set on option 'field' at for class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass8'."
@@ -239,7 +268,7 @@
 
     def "throws decent error when method annotated without option name set"() {
         when:
-        reader.getOptions(new TestClass9());
+        TaskOptionsGenerator.generate(new TestClass9(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "No option name set on 'setStrings' in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass9'."
@@ -247,7 +276,7 @@
 
     def "throws decent error when private field is annotated as option and no setter declared"() {
         when:
-        reader.getOptions(new TestClass10())
+        TaskOptionsGenerator.generate(new TestClass10(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "No setter for Option annotated field 'field' in class 'class org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass10'."
@@ -255,13 +284,13 @@
 
     def "throws decent error for invalid OptionValues annotated methods"() {
         when:
-        reader.getOptions(new WithInvalidSomeOptionMethod());
+        TaskOptionsGenerator.generate(new WithInvalidSomeOptionMethod(), reader).getAll()
         then:
         def e = thrown(OptionValidationException)
         e.message == "@OptionValues annotation not supported on method 'getValues' in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$WithInvalidSomeOptionMethod'. Supported method must be non-static, return a Collection<String> and take no parameters.";
 
         when:
-        reader.getOptions(new TestClass8());
+        TaskOptionsGenerator.generate(new TestClass8(), reader).getAll()
         then:
         e = thrown(OptionValidationException)
         e.message == "No description set on option 'field' at for class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass8'."
@@ -270,12 +299,12 @@
     @Issue("https://github.com/gradle/gradle/issues/18496")
     def "handles abstract classes with interfaces"() {
         when:
-        List<OptionDescriptor> options = reader.getOptions(new AbstractTestClassWithInterface() {
+        List<OptionDescriptor> options = TaskOptionsGenerator.generate((new AbstractTestClassWithInterface() {
             @Override
             Property<String> getStringValue() {
                 throw new UnsupportedOperationException()
             }
-        })
+        }), reader).getAll()
         then:
         options.size() == 2 + builtInOptionCount
 
@@ -293,12 +322,12 @@
     @Issue("https://github.com/gradle/gradle/issues/18496")
     def "handles abstract classes with interfaces with same method but different option names"() {
         when:
-        List<OptionDescriptor> options = reader.getOptions(new AbstractTestClassWithTwoInterfacesWithSameMethod() {
+        List<OptionDescriptor> options = TaskOptionsGenerator.generate((new AbstractTestClassWithTwoInterfacesWithSameMethod() {
             @Override
             Property<String> getStringValue() {
                 throw new UnsupportedOperationException()
             }
-        })
+        }), reader).getAll()
         then:
         options.size() == 3 + builtInOptionCount
 
@@ -320,7 +349,7 @@
 
     def "class that defines option when parent class and interface do has uses the sub-class option"() {
         when:
-        List<OptionDescriptor> options = reader.getOptions(new OverrideCheckSubClassDefinesOption())
+        List<OptionDescriptor> options = TaskOptionsGenerator.generate(new OverrideCheckSubClassDefinesOption(), reader).getAll()
         then:
         options.size() == 1 + builtInOptionCount
 
@@ -330,7 +359,7 @@
 
     def "class that has an option defined in parent class and interface uses the parent class option"() {
         when:
-        List<OptionDescriptor> options = reader.getOptions(new OverrideCheckSubClassSaysNothing())
+        List<OptionDescriptor> options = TaskOptionsGenerator.generate(new OverrideCheckSubClassSaysNothing(), reader).getAll()
         then:
         options.size() == 1 + builtInOptionCount
 
@@ -340,7 +369,7 @@
 
     def "class that defines option when parent class which impls interface do has uses the sub-class option"() {
         when:
-        List<OptionDescriptor> options = reader.getOptions(new OverrideCheckSubClassImplInterfaceDefinesOption())
+        List<OptionDescriptor> options = TaskOptionsGenerator.generate(new OverrideCheckSubClassImplInterfaceDefinesOption(), reader).getAll()
         then:
         options.size() == 1 + builtInOptionCount
 
@@ -350,7 +379,7 @@
 
     def "class that has an option defined in parent class which impls interface uses the parent class option"() {
         when:
-        List<OptionDescriptor> options = reader.getOptions(new OverrideCheckSubClassImplInterfaceSaysNothing())
+        List<OptionDescriptor> options = TaskOptionsGenerator.generate(new OverrideCheckSubClassImplInterfaceSaysNothing(), reader).getAll()
         then:
         options.size() == 1 + builtInOptionCount
 
@@ -523,6 +552,13 @@
         String field2
     }
 
+    public static class TestClassWithOppositeOptionNameClashing {
+        @Option(option = 'my-option', description = "Option to trigger creation of opposite option")
+        Boolean field1
+        @Option(option = 'no-my-option', description = "Option clashing with opposite option")
+        Boolean field2
+    }
+
     public static class TestClassWithFields {
         @Option(option = 'customOptionName', description = "custom description")
         String field1
diff --git a/subprojects/core/src/test/groovy/org/gradle/api/tasks/DeleteTest.groovy b/subprojects/core/src/test/groovy/org/gradle/api/tasks/DeleteTest.groovy
index 020b280..a2bd45d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/api/tasks/DeleteTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/api/tasks/DeleteTest.groovy
@@ -17,8 +17,8 @@
 package org.gradle.api.tasks
 
 import org.gradle.api.internal.ConventionTask
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.WrapUtil
 
 import static org.gradle.api.internal.file.TestFiles.fileSystem
@@ -72,7 +72,7 @@
         delete.getTargetFiles().getFiles() == getProject().files(delete.getDelete()).getFiles()
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "can follow symlinks"() {
         given:
         def keepTxt = temporaryFolder.createFile("originalDir", "keep.txt")
@@ -91,7 +91,7 @@
         !keepTxt.exists()
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "will not follow symlinks by default"() {
         given:
         def keepTxt = temporaryFolder.createFile("originalDir", "keep.txt")
diff --git a/subprojects/core/src/test/groovy/org/gradle/caching/internal/controller/BuildCacheControllerFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/caching/internal/controller/BuildCacheControllerFactoryTest.groovy
deleted file mode 100644
index 7825b26..0000000
--- a/subprojects/core/src/test/groovy/org/gradle/caching/internal/controller/BuildCacheControllerFactoryTest.groovy
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright 2017 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.caching.internal.controller
-
-import org.gradle.api.Action
-import org.gradle.api.internal.cache.StringInterner
-import org.gradle.caching.BuildCacheEntryReader
-import org.gradle.caching.BuildCacheEntryWriter
-import org.gradle.caching.BuildCacheException
-import org.gradle.caching.BuildCacheKey
-import org.gradle.caching.BuildCacheService
-import org.gradle.caching.BuildCacheServiceFactory
-import org.gradle.caching.configuration.AbstractBuildCache
-import org.gradle.caching.configuration.internal.DefaultBuildCacheConfiguration
-import org.gradle.caching.configuration.internal.DefaultBuildCacheServiceRegistration
-import org.gradle.caching.internal.FinalizeBuildCacheConfigurationBuildOperationType
-import org.gradle.caching.internal.origin.OriginMetadataFactory
-import org.gradle.caching.internal.packaging.BuildCacheEntryPacker
-import org.gradle.caching.internal.services.BuildCacheControllerFactory
-import org.gradle.caching.local.DirectoryBuildCache
-import org.gradle.caching.local.internal.LocalBuildCacheService
-import org.gradle.internal.operations.TestBuildOperationExecutor
-import org.gradle.internal.vfs.FileSystemAccess
-import org.gradle.util.Path
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-import static org.gradle.caching.internal.services.BuildCacheControllerFactory.BuildCacheMode.DISABLED
-import static org.gradle.caching.internal.services.BuildCacheControllerFactory.BuildCacheMode.ENABLED
-import static org.gradle.caching.internal.services.BuildCacheControllerFactory.RemoteAccessMode.ONLINE
-
-class BuildCacheControllerFactoryTest extends Specification {
-
-    def buildCacheEnabled = true
-    def buildOperationExecuter = new TestBuildOperationExecutor()
-    def config = new DefaultBuildCacheConfiguration(TestUtil.instantiatorFactory().inject(), [
-        new DefaultBuildCacheServiceRegistration(DirectoryBuildCache, TestDirectoryBuildCacheServiceFactory),
-        new DefaultBuildCacheServiceRegistration(TestOtherRemoteBuildCache, TestOtherRemoteBuildCacheServiceFactory),
-        new DefaultBuildCacheServiceRegistration(TestRemoteBuildCache, TestRemoteBuildCacheServiceFactory),
-    ])
-
-    boolean logStacktraces
-    boolean emitDebugLogging
-
-    private DefaultBuildCacheController createController() {
-        createController(DefaultBuildCacheController)
-    }
-
-    private <T extends BuildCacheController> T createController(Class<T> controllerType) {
-        def controller = BuildCacheControllerFactory.create(
-            buildOperationExecuter,
-            Path.path("test"),
-            null,
-            config,
-            buildCacheEnabled ? ENABLED : DISABLED,
-            ONLINE,
-            logStacktraces,
-            emitDebugLogging,
-            TestUtil.instantiatorFactory().inject(),
-            Stub(FileSystemAccess),
-            Stub(BuildCacheEntryPacker),
-            Stub(OriginMetadataFactory),
-            Stub(StringInterner)
-        )
-        assert controllerType.isInstance(controller)
-        controllerType.cast(controller)
-    }
-
-    private FinalizeBuildCacheConfigurationBuildOperationType.Result buildOpResult() {
-        buildOperationExecuter.log.mostRecentResult(FinalizeBuildCacheConfigurationBuildOperationType)
-    }
-
-    def 'local cache service is created when remote is not configured'() {
-        when:
-        def c = createController()
-
-        then:
-        c.remote.service == null
-        c.local.service != null
-
-        and:
-        with(buildOpResult()) {
-            local.type == "directory"
-            local.className == DirectoryBuildCache.name
-            remote == null
-        }
-    }
-
-    def 'local cache service is created when remote is disabled'() {
-        config.remote(TestRemoteBuildCache).enabled = false
-
-        when:
-        def c = createController()
-
-        then:
-        c.local.service != null
-        c.remote.service == null
-        with(buildOpResult()) {
-            local.type == "directory"
-            local.className == DirectoryBuildCache.name
-            remote == null
-        }
-    }
-
-    def 'remote cache service is created when local is disabled'() {
-        config.local.enabled = false
-        config.remote(TestRemoteBuildCache)
-
-        when:
-        def c = createController()
-
-        then:
-        c.local.service == null
-        c.remote.service instanceof TestRemoteBuildCacheService
-        with(buildOpResult()) {
-            local == null
-            remote.type == "remote"
-            remote.className == TestRemoteBuildCache.name
-        }
-    }
-
-    def 'can enable both'() {
-        config.remote(TestRemoteBuildCache)
-
-        when:
-        def c = createController()
-
-        then:
-        c.local.service != null
-        c.remote.service != null
-        with(buildOpResult()) {
-            local.type == "directory"
-            remote.type == "remote"
-        }
-    }
-
-    def "respects debug logging setting - #setting"() {
-        when:
-        emitDebugLogging = setting
-        config.remote(TestRemoteBuildCache)
-        def c = createController()
-
-        then:
-        c.emitDebugLogging == setting
-
-        where:
-        setting << [true, false]
-    }
-
-    def 'when caching is disabled no services are created'() {
-        buildCacheEnabled = false
-
-        expect:
-        createController(NoOpBuildCacheController)
-        with(buildOpResult()) {
-            local == null
-            remote == null
-        }
-    }
-
-    static class TestRemoteBuildCache extends AbstractBuildCache {
-        String value
-    }
-
-    static class TestRemoteBuildCacheServiceFactory implements BuildCacheServiceFactory<TestRemoteBuildCache> {
-        @Override
-        BuildCacheService createBuildCacheService(TestRemoteBuildCache configuration, Describer describer) {
-            def chain = describer.type("remote")
-            if (configuration.value != null) {
-                chain.config("value", configuration.value)
-            }
-            new TestRemoteBuildCacheService()
-        }
-    }
-
-    static class TestOtherRemoteBuildCache extends AbstractBuildCache {
-        String value
-    }
-
-    static class TestOtherRemoteBuildCacheServiceFactory implements BuildCacheServiceFactory<TestOtherRemoteBuildCache> {
-        @Override
-        BuildCacheService createBuildCacheService(TestOtherRemoteBuildCache configuration, Describer describer) {
-            def chain = describer.type("other-remote")
-            if (configuration.value != null) {
-                chain.config("value", configuration.value)
-            }
-            new TestRemoteBuildCacheService()
-        }
-    }
-
-    static class TestRemoteBuildCacheService implements BuildCacheService {
-
-        @Override
-        boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
-            return false
-        }
-
-        @Override
-        void store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException {
-
-        }
-
-        @Override
-        void close() throws IOException {
-
-        }
-    }
-
-    static class TestDirectoryBuildCacheServiceFactory implements BuildCacheServiceFactory<DirectoryBuildCache> {
-        @Override
-        BuildCacheService createBuildCacheService(DirectoryBuildCache configuration, Describer describer) {
-            def chain = describer.type("directory")
-            if (configuration.directory != null) {
-                chain.config("location", configuration.directory.toString())
-            }
-
-            new TestLocalBuildCacheService()
-        }
-    }
-
-    static class TestLocalBuildCacheService implements LocalBuildCacheService, BuildCacheService {
-        @Override
-        void storeLocally(BuildCacheKey key, File file) {
-
-        }
-
-        @Override
-        void loadLocally(BuildCacheKey key, Action<? super File> reader) {
-
-        }
-
-        @Override
-        boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
-            return false
-        }
-
-        @Override
-        void store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException {
-
-        }
-
-        @Override
-        void close() {
-
-        }
-
-        @Override
-        void withTempFile(BuildCacheKey key, Action<? super File> action) {
-
-        }
-    }
-}
diff --git a/subprojects/core/src/test/groovy/org/gradle/caching/internal/controller/LegacyBuildCacheControllerFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/caching/internal/controller/LegacyBuildCacheControllerFactoryTest.groovy
new file mode 100644
index 0000000..0623463
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/caching/internal/controller/LegacyBuildCacheControllerFactoryTest.groovy
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017 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.caching.internal.controller
+
+import org.gradle.api.Action
+import org.gradle.api.internal.StartParameterInternal
+import org.gradle.api.internal.cache.StringInterner
+import org.gradle.api.internal.file.temp.TemporaryFileProvider
+import org.gradle.caching.BuildCacheEntryReader
+import org.gradle.caching.BuildCacheEntryWriter
+import org.gradle.caching.BuildCacheException
+import org.gradle.caching.BuildCacheKey
+import org.gradle.caching.BuildCacheService
+import org.gradle.caching.BuildCacheServiceFactory
+import org.gradle.caching.configuration.AbstractBuildCache
+import org.gradle.caching.configuration.internal.DefaultBuildCacheConfiguration
+import org.gradle.caching.configuration.internal.DefaultBuildCacheServiceRegistration
+import org.gradle.caching.internal.FinalizeBuildCacheConfigurationBuildOperationType
+import org.gradle.caching.internal.origin.OriginMetadataFactory
+import org.gradle.caching.internal.packaging.BuildCacheEntryPacker
+import org.gradle.caching.internal.services.LegacyBuildCacheControllerFactory
+import org.gradle.caching.local.DirectoryBuildCache
+import org.gradle.caching.local.internal.LocalBuildCacheService
+import org.gradle.internal.operations.TestBuildOperationExecutor
+import org.gradle.internal.vfs.FileSystemAccess
+import org.gradle.util.Path
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+
+class LegacyBuildCacheControllerFactoryTest extends Specification {
+
+    def buildCacheEnabled = true
+    def buildOperationExecuter = new TestBuildOperationExecutor()
+    def config = new DefaultBuildCacheConfiguration(TestUtil.instantiatorFactory().inject(), [
+        new DefaultBuildCacheServiceRegistration(DirectoryBuildCache, TestDirectoryBuildCacheServiceFactory),
+        new DefaultBuildCacheServiceRegistration(TestOtherRemoteBuildCache, TestOtherRemoteBuildCacheServiceFactory),
+        new DefaultBuildCacheServiceRegistration(TestRemoteBuildCache, TestRemoteBuildCacheServiceFactory),
+    ])
+
+    boolean emitDebugLogging
+
+    private DefaultBuildCacheController createController() {
+        createController(DefaultBuildCacheController)
+    }
+
+    private <T extends BuildCacheController> T createController(Class<T> controllerType) {
+        def controller = new LegacyBuildCacheControllerFactory(
+            Stub(StartParameterInternal) {
+                isBuildCacheEnabled() >> buildCacheEnabled
+                isBuildCacheDebugLogging() >> emitDebugLogging
+            },
+            buildOperationExecuter,
+            Stub(OriginMetadataFactory),
+            Stub(FileSystemAccess),
+            Stub(StringInterner),
+            Stub(TemporaryFileProvider),
+            Stub(BuildCacheEntryPacker)
+        ).createController(Path.path("test"), config, TestUtil.instantiatorFactory().inject())
+        assert controllerType.isInstance(controller)
+        controllerType.cast(controller)
+    }
+
+    private FinalizeBuildCacheConfigurationBuildOperationType.Result buildOpResult() {
+        buildOperationExecuter.log.mostRecentResult(FinalizeBuildCacheConfigurationBuildOperationType)
+    }
+
+    def 'local cache service is created when remote is not configured'() {
+        when:
+        def c = createController()
+
+        then:
+        c.remote.service == null
+        c.local.service != null
+
+        and:
+        with(buildOpResult()) {
+            local.type == "directory"
+            local.className == DirectoryBuildCache.name
+            remote == null
+        }
+    }
+
+    def 'local cache service is created when remote is disabled'() {
+        config.remote(TestRemoteBuildCache).enabled = false
+
+        when:
+        def c = createController()
+
+        then:
+        c.local.service != null
+        c.remote.service == null
+        with(buildOpResult()) {
+            local.type == "directory"
+            local.className == DirectoryBuildCache.name
+            remote == null
+        }
+    }
+
+    def 'remote cache service is created when local is disabled'() {
+        config.local.enabled = false
+        config.remote(TestRemoteBuildCache)
+
+        when:
+        def c = createController()
+
+        then:
+        c.local.service == null
+        c.remote.service instanceof TestRemoteBuildCacheService
+        with(buildOpResult()) {
+            local == null
+            remote.type == "remote"
+            remote.className == TestRemoteBuildCache.name
+        }
+    }
+
+    def 'can enable both'() {
+        config.remote(TestRemoteBuildCache)
+
+        when:
+        def c = createController()
+
+        then:
+        c.local.service != null
+        c.remote.service != null
+        with(buildOpResult()) {
+            local.type == "directory"
+            remote.type == "remote"
+        }
+    }
+
+    def "respects debug logging setting - #setting"() {
+        when:
+        emitDebugLogging = setting
+        config.remote(TestRemoteBuildCache)
+        def c = createController()
+
+        then:
+        c.emitDebugLogging == setting
+
+        where:
+        setting << [true, false]
+    }
+
+    def 'when caching is disabled no services are created'() {
+        buildCacheEnabled = false
+
+        expect:
+        createController(NoOpBuildCacheController)
+        with(buildOpResult()) {
+            local == null
+            remote == null
+        }
+    }
+
+    static class TestRemoteBuildCache extends AbstractBuildCache {
+        String value
+    }
+
+    static class TestRemoteBuildCacheServiceFactory implements BuildCacheServiceFactory<TestRemoteBuildCache> {
+        @Override
+        BuildCacheService createBuildCacheService(TestRemoteBuildCache configuration, Describer describer) {
+            def chain = describer.type("remote")
+            if (configuration.value != null) {
+                chain.config("value", configuration.value)
+            }
+            new TestRemoteBuildCacheService()
+        }
+    }
+
+    static class TestOtherRemoteBuildCache extends AbstractBuildCache {
+        String value
+    }
+
+    static class TestOtherRemoteBuildCacheServiceFactory implements BuildCacheServiceFactory<TestOtherRemoteBuildCache> {
+        @Override
+        BuildCacheService createBuildCacheService(TestOtherRemoteBuildCache configuration, Describer describer) {
+            def chain = describer.type("other-remote")
+            if (configuration.value != null) {
+                chain.config("value", configuration.value)
+            }
+            new TestRemoteBuildCacheService()
+        }
+    }
+
+    static class TestRemoteBuildCacheService implements BuildCacheService {
+
+        @Override
+        boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+            return false
+        }
+
+        @Override
+        void store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException {
+
+        }
+
+        @Override
+        void close() throws IOException {
+
+        }
+    }
+
+    static class TestDirectoryBuildCacheServiceFactory implements BuildCacheServiceFactory<DirectoryBuildCache> {
+        @Override
+        BuildCacheService createBuildCacheService(DirectoryBuildCache configuration, Describer describer) {
+            def chain = describer.type("directory")
+            if (configuration.directory != null) {
+                chain.config("location", configuration.directory.toString())
+            }
+
+            new TestLocalBuildCacheService()
+        }
+    }
+
+    static class TestLocalBuildCacheService implements LocalBuildCacheService, BuildCacheService {
+        @Override
+        void storeLocally(BuildCacheKey key, File file) {
+
+        }
+
+        @Override
+        void loadLocally(BuildCacheKey key, Action<? super File> reader) {
+
+        }
+
+        @Override
+        boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+            return false
+        }
+
+        @Override
+        void store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException {
+
+        }
+
+        @Override
+        void close() {
+
+        }
+
+        @Override
+        void withTempFile(BuildCacheKey key, Action<? super File> action) {
+
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/plan/AbstractExecutionPlanSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/plan/AbstractExecutionPlanSpec.groovy
index 8d386f2..df0a303 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/plan/AbstractExecutionPlanSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/plan/AbstractExecutionPlanSpec.groovy
@@ -23,7 +23,7 @@
 import org.gradle.api.internal.TaskOutputsInternal
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.project.ProjectState
-import org.gradle.api.internal.project.taskfactory.TaskIdentity
+import org.gradle.api.internal.project.taskfactory.TestTaskIdentities
 import org.gradle.api.internal.tasks.TaskContainerInternal
 import org.gradle.api.internal.tasks.TaskDependencyContainerInternal
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext
@@ -99,7 +99,7 @@
         task.localState >> emptyTaskLocalState()
         task.inputs >> emptyTaskInputs()
         task.requiredServices >> emptyTaskRequiredServices()
-        task.taskIdentity >> TaskIdentity.create(name, DefaultTask, project)
+        task.taskIdentity >> TestTaskIdentities.create(name, DefaultTask, project)
         return task
     }
 
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/plan/DefaultExecutionPlanParallelTest.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/plan/DefaultExecutionPlanParallelTest.groovy
index aa28dd6..7973866 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/plan/DefaultExecutionPlanParallelTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/plan/DefaultExecutionPlanParallelTest.groovy
@@ -21,7 +21,8 @@
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.internal.TaskInternal
-import org.gradle.api.internal.project.taskfactory.TaskIdentity
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.taskfactory.TestTaskIdentities
 import org.gradle.api.internal.tasks.NodeExecutionContext
 import org.gradle.api.internal.tasks.TaskStateInternal
 import org.gradle.api.tasks.Destroys
@@ -36,9 +37,9 @@
 import org.gradle.internal.file.Stat
 import org.gradle.internal.operations.TestBuildOperationExecutor
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.Path
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.gradle.util.internal.ToBeImplemented
 import spock.lang.Issue
 
@@ -72,7 +73,7 @@
         _ * task.shouldRunAfter >> taskDependencyResolvingTo(task, options.shouldRunAfter ?: [])
         _ * task.mustRunAfter >> taskDependencyResolvingTo(task, options.mustRunAfter ?: [])
         _ * task.sharedResources >> (options.resources ?: [])
-        _ * task.taskIdentity >> TaskIdentity.create(name, DefaultTask, project)
+        _ * task.taskIdentity >> TestTaskIdentities.create(name, DefaultTask, project as ProjectInternal)
         TaskStateInternal state = Mock()
         _ * task.state >> state
         if (options.failure != null) {
@@ -1127,7 +1128,7 @@
     }
 
     @ToBeImplemented("When we support symlinks in the VFS, we should implement this as well")
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "a task that writes into a symlink that overlaps with output of currently running task is not started"() {
         given:
         def taskOutput = file("outputDir").createDir()
@@ -1147,7 +1148,7 @@
     }
 
     @ToBeImplemented("When we support symlinks in the VFS, we should implement this as well")
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "a task that writes into a symlink of a shared output dir of currently running task is not started"() {
         given:
         def taskOutput = file("outputDir").createDir()
@@ -1169,7 +1170,7 @@
     }
 
     @ToBeImplemented("When we support symlinks in the VFS, we should implement this as well")
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "a task that stores local state into a symlink of a shared output dir of currently running task is not started"() {
         given:
         def taskOutput = file("outputDir").createDir()
diff --git a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionGraphSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionGraphSpec.groovy
index 0527eda..7eaeb9e 100644
--- a/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionGraphSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/execution/taskgraph/DefaultTaskExecutionGraphSpec.groovy
@@ -31,7 +31,7 @@
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.project.ProjectStateRegistry
-import org.gradle.api.internal.project.taskfactory.TaskIdentity
+import org.gradle.api.internal.project.taskfactory.TestTaskIdentities
 import org.gradle.api.internal.tasks.NodeExecutionContext
 import org.gradle.api.internal.tasks.TaskDependencyFactory
 import org.gradle.api.internal.tasks.TaskDependencyInternal
@@ -658,7 +658,7 @@
         _ * mock.destroyables >> Stub(TaskDestroyablesInternal)
         _ * mock.localState >> Stub(TaskLocalStateInternal)
         _ * mock.path >> ":${name}"
-        _ * mock.taskIdentity >> TaskIdentity.create(name, DefaultTask, project as ProjectInternal)
+        _ * mock.taskIdentity >> TestTaskIdentities.create(name, DefaultTask, project as ProjectInternal)
         return mock
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesControllerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesControllerTest.groovy
index fdb514de..50f527d 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesControllerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesControllerTest.groovy
@@ -16,18 +16,54 @@
 
 package org.gradle.initialization
 
-import org.gradle.api.internal.properties.GradleProperties
+import org.gradle.api.Project
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.StartParameterInternal
+import org.gradle.initialization.properties.DefaultProjectPropertiesLoader
+import org.gradle.initialization.properties.DefaultSystemPropertiesInstaller
+import org.gradle.initialization.properties.MutableGradleProperties
+import org.gradle.initialization.properties.ProjectPropertiesLoader
+import org.gradle.initialization.properties.SystemPropertiesInstaller
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
 import spock.lang.Specification
 
+import static java.util.Collections.emptyMap
+import static org.gradle.initialization.IGradlePropertiesLoader.ENV_PROJECT_PROPERTIES_PREFIX
+import static org.gradle.initialization.IGradlePropertiesLoader.SYSTEM_PROJECT_PROPERTIES_PREFIX
+
 class DefaultGradlePropertiesControllerTest extends Specification {
 
+    private Map<String, String> prefixedEnvironmentVariables = emptyMap()
+    private Map<String, String> prefixedSystemProperties = emptyMap()
+    private Map<String, String> projectPropertiesArgs = emptyMap()
+    private Map<String, String> systemPropertiesArgs = emptyMap()
+    private File gradleUserHome = new File("gradleUserHome")
+
+    private final Environment environment = Mock(Environment) {
+        getSystemProperties() >> Mock(Environment.Properties) {
+            byNamePrefix(SYSTEM_PROJECT_PROPERTIES_PREFIX) >> { prefixedSystemProperties }
+        }
+        getVariables() >> Mock(Environment.Properties) {
+            byNamePrefix(ENV_PROJECT_PROPERTIES_PREFIX) >> { prefixedEnvironmentVariables }
+        }
+    }
+
+    private final StartParameterInternal startParameter = Mock(StartParameterInternal) {
+        getProjectProperties() >> { projectPropertiesArgs }
+        getSystemPropertiesArgs() >> { systemPropertiesArgs }
+        getGradleUserHomeDir() >> gradleUserHome
+    }
+
+    @Rule
+    public SetSystemProperties sysProp = new SetSystemProperties()
+
     def "attached GradleProperties #method fails before loading"() {
 
         given:
-        def propertiesLoader = Mock(IGradlePropertiesLoader)
-        def subject = new DefaultGradlePropertiesController(propertiesLoader)
-        def properties = subject.gradleProperties
-        0 * propertiesLoader.loadGradleProperties(_)
+        def controller = new DefaultGradlePropertiesController(Mock(IGradlePropertiesLoader), Mock(SystemPropertiesInstaller), Mock(ProjectPropertiesLoader))
+        def properties = controller.gradleProperties
+        0 * controller.loadGradleProperties(_)
 
         when:
         switch (method) {
@@ -46,17 +82,17 @@
     def "attached GradleProperties methods succeed after loading"() {
 
         given:
+        def loader = Mock(IGradlePropertiesLoader)
+        def controller = new DefaultGradlePropertiesController(loader, Mock(SystemPropertiesInstaller), Mock(ProjectPropertiesLoader))
         def settingsDir = new File('.')
-        def propertiesLoader = Mock(IGradlePropertiesLoader)
-        def subject = new DefaultGradlePropertiesController(propertiesLoader)
-        def properties = subject.gradleProperties
-        def loadedProperties = Mock(GradleProperties)
-        1 * propertiesLoader.loadGradleProperties(settingsDir) >> loadedProperties
+        def properties = controller.gradleProperties
+        def loadedProperties = Mock(MutableGradleProperties)
+        1 * loader.loadGradleProperties(settingsDir) >> loadedProperties
         1 * loadedProperties.mergeProperties(_) >> [property: '42']
         1 * loadedProperties.find(_) >> '42'
 
         when:
-        subject.loadGradlePropertiesFrom(settingsDir)
+        controller.loadGradlePropertiesFrom(settingsDir, true)
 
         then:
         properties.find("property") == '42'
@@ -68,32 +104,163 @@
         given:
         // use a different File instance for each call to ensure it is compared by value
         def currentDir = { new File('.') }
-        def propertiesLoader = Mock(IGradlePropertiesLoader)
-        def subject = new DefaultGradlePropertiesController(propertiesLoader)
-        def loadedProperties = Mock(GradleProperties)
+        def loader = Mock(IGradlePropertiesLoader)
+        def controller = new DefaultGradlePropertiesController(loader, Mock(SystemPropertiesInstaller), Mock(ProjectPropertiesLoader))
+        def loadedProperties = Mock(MutableGradleProperties)
 
         when: "calling the method multiple times with the same value"
-        subject.loadGradlePropertiesFrom(currentDir())
-        subject.loadGradlePropertiesFrom(currentDir())
+        controller.loadGradlePropertiesFrom(currentDir(), true)
+        controller.loadGradlePropertiesFrom(currentDir(), true)
 
         then:
-        1 * propertiesLoader.loadGradleProperties(currentDir()) >> loadedProperties
+        1 * loader.loadGradleProperties(currentDir()) >> loadedProperties
     }
 
     def "loadGradlePropertiesFrom fails when called with different argument"() {
 
         given:
         def settingsDir = new File('a')
-        def propertiesLoader = Mock(IGradlePropertiesLoader)
-        def subject = new DefaultGradlePropertiesController(propertiesLoader)
-        def loadedProperties = Mock(GradleProperties)
-        1 * propertiesLoader.loadGradleProperties(settingsDir) >> loadedProperties
+        def loader = Mock(IGradlePropertiesLoader)
+        def controller = new DefaultGradlePropertiesController(loader, Mock(SystemPropertiesInstaller), Mock(ProjectPropertiesLoader))
+        def loadedProperties = Mock(MutableGradleProperties)
+        1 * loader.loadGradleProperties(settingsDir) >> loadedProperties
 
         when:
-        subject.loadGradlePropertiesFrom(settingsDir)
-        subject.loadGradlePropertiesFrom(new File('b'))
+        controller.loadGradlePropertiesFrom(settingsDir, true)
+        controller.loadGradlePropertiesFrom(new File('b'), true)
 
         then:
         thrown(IllegalStateException)
     }
+
+    def "environment variables have precedence over project properties"() {
+        given:
+        def settingsDir = new File("settingsDir")
+        def gradleUserHomePropertiesFile = propertiesFileFromDir(gradleUserHome)
+        def settingsPropertiesFile = propertiesFileFromDir(settingsDir)
+
+        1 * environment.propertiesFile(gradleUserHomePropertiesFile) >> ["prop": "user value"]
+        1 * environment.propertiesFile(settingsPropertiesFile) >> ["prop": "settings value"]
+        prefixedEnvironmentVariables = [(ENV_PROJECT_PROPERTIES_PREFIX + "prop"): "env value"]
+
+        def projectProperties = ["prop": "project value"]
+        def gradlePropertiesLoader = new DefaultGradlePropertiesLoader(startParameter, environment)
+        def projectPropertiesLoader = new DefaultProjectPropertiesLoader(startParameter, environment)
+        def controller = new DefaultGradlePropertiesController(gradlePropertiesLoader, Mock(SystemPropertiesInstaller), projectPropertiesLoader)
+        def properties = controller.gradleProperties
+
+        when:
+        controller.loadGradlePropertiesFrom(settingsDir, true)
+
+        then:
+        "env value" == properties.mergeProperties(projectProperties)["prop"]
+    }
+
+    def "system properties have precedence over environment variables"() {
+        given:
+        def settingsDir = new File("settingsDir")
+        def gradleUserHomePropertiesFile = propertiesFileFromDir(gradleUserHome)
+        def settingsPropertiesFile = propertiesFileFromDir(settingsDir)
+
+        1 * environment.propertiesFile(gradleUserHomePropertiesFile) >> ["prop": "user value"]
+        1 * environment.propertiesFile(settingsPropertiesFile) >> ["prop": "settings value"]
+        prefixedEnvironmentVariables = [(ENV_PROJECT_PROPERTIES_PREFIX + "prop"): "env value"]
+        prefixedSystemProperties = [(SYSTEM_PROJECT_PROPERTIES_PREFIX + "prop"): "system value"]
+
+        def projectProperties = ["prop": "project value"]
+        def gradlePropertiesLoader = new DefaultGradlePropertiesLoader(startParameter, environment)
+        def projectPropertiesLoader = new DefaultProjectPropertiesLoader(startParameter, environment)
+        def controller = new DefaultGradlePropertiesController(gradlePropertiesLoader, Mock(SystemPropertiesInstaller), projectPropertiesLoader)
+        def properties = controller.gradleProperties
+
+        when:
+        controller.loadGradlePropertiesFrom(settingsDir, true)
+
+        then:
+        "system value" == properties.mergeProperties(projectProperties)["prop"]
+    }
+
+    def "start parameter properties have precedence over system properties"() {
+        given:
+        def settingsDir = new File("settingsDir")
+        def gradleUserHomePropertiesFile = propertiesFileFromDir(gradleUserHome)
+        def settingsPropertiesFile = propertiesFileFromDir(settingsDir)
+
+        1 * environment.propertiesFile(gradleUserHomePropertiesFile) >> ["prop": "user value"]
+        1 * environment.propertiesFile(settingsPropertiesFile) >> ["prop": "settings value"]
+        prefixedEnvironmentVariables = [(ENV_PROJECT_PROPERTIES_PREFIX + "prop"): "env value"]
+        prefixedSystemProperties = [(SYSTEM_PROJECT_PROPERTIES_PREFIX + "prop"): "system value"]
+        projectPropertiesArgs = ["prop": "param value"]
+
+        def projectProperties = ["prop": "project value"]
+        def gradlePropertiesLoader = new DefaultGradlePropertiesLoader(startParameter, environment)
+        def projectPropertiesLoader = new DefaultProjectPropertiesLoader(startParameter, environment)
+        def controller = new DefaultGradlePropertiesController(gradlePropertiesLoader, Mock(SystemPropertiesInstaller), projectPropertiesLoader)
+        def properties = controller.gradleProperties
+
+        when:
+        controller.loadGradlePropertiesFrom(settingsDir, true)
+
+        then:
+        "param value" == properties.mergeProperties(projectProperties)["prop"]
+    }
+
+    def "load sets system properties"() {
+        given:
+        def settingsDir = new File("settingsDir")
+        def gradleUserHomePropertiesFile = propertiesFileFromDir(gradleUserHome)
+        def settingsPropertiesFile = propertiesFileFromDir(settingsDir)
+
+        1 * environment.propertiesFile(gradleUserHomePropertiesFile) >> [
+            (Project.SYSTEM_PROP_PREFIX + ".userSystemProp"): "userSystemValue"
+        ]
+        1 * environment.propertiesFile(settingsPropertiesFile) >> [
+            (Project.SYSTEM_PROP_PREFIX + ".userSystemProp"): "settingsSystemValue",
+            (Project.SYSTEM_PROP_PREFIX + ".settingsSystemProp2"): "settingsSystemValue2"
+        ]
+        systemPropertiesArgs = ["systemPropArgKey": "systemPropArgValue"]
+
+        def gradlePropertiesLoader = new DefaultGradlePropertiesLoader(startParameter, environment)
+        def projectPropertiesLoader = new DefaultProjectPropertiesLoader(startParameter, environment)
+        def systemPropertiesInstaller = new DefaultSystemPropertiesInstaller(Mock(EnvironmentChangeTracker), startParameter, Mock(GradleInternal))
+        def controller = new DefaultGradlePropertiesController(gradlePropertiesLoader, systemPropertiesInstaller, projectPropertiesLoader)
+
+        when:
+        controller.loadGradlePropertiesFrom(settingsDir, setSystemProperties)
+
+        then:
+        (setSystemProperties ? "userSystemValue" : null) == System.getProperty("userSystemProp")
+        (setSystemProperties ? "settingsSystemValue2" : null) == System.getProperty("settingsSystemProp2")
+        (setSystemProperties ? "systemPropArgValue" : null) == System.getProperty("systemPropArgKey")
+
+        where:
+        setSystemProperties << [true, false]
+    }
+
+    def "start parameter system properties have precedence over properties files"() {
+        given:
+        def settingsDir = new File("settingsDir")
+        def gradleUserHomePropertiesFile = propertiesFileFromDir(gradleUserHome)
+        def settingsPropertiesFile = propertiesFileFromDir(settingsDir)
+
+        1 * environment.propertiesFile(gradleUserHomePropertiesFile) >> ["prop": "user value"]
+        1 * environment.propertiesFile(settingsPropertiesFile) >> ["prop": "settings value"]
+        prefixedSystemProperties = [:]
+        systemPropertiesArgs = ["prop": "commandline value"]
+
+        def gradlePropertiesLoader = new DefaultGradlePropertiesLoader(startParameter, environment)
+        def projectPropertiesLoader = new DefaultProjectPropertiesLoader(startParameter, environment)
+        def systemPropertiesInstaller = new DefaultSystemPropertiesInstaller(Mock(EnvironmentChangeTracker), startParameter, Mock(GradleInternal))
+        def controller = new DefaultGradlePropertiesController(gradlePropertiesLoader, systemPropertiesInstaller, projectPropertiesLoader)
+
+        when:
+        controller.loadGradlePropertiesFrom(settingsDir, true)
+
+        then:
+        "commandline value" == System.getProperty("prop")
+    }
+
+    private static File propertiesFileFromDir(File dir) {
+        new File(dir, Project.GRADLE_PROPERTIES)
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesLoaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesLoaderTest.groovy
index a17664a..4891dcf 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesLoaderTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultGradlePropertiesLoaderTest.groovy
@@ -16,44 +16,26 @@
 package org.gradle.initialization
 
 import org.gradle.api.Project
-import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.StartParameterInternal
 import org.gradle.api.internal.properties.GradleProperties
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.SetSystemProperties
 import org.junit.Rule
 import spock.lang.Specification
 
 import static java.util.Collections.emptyMap
-import static org.gradle.api.Project.SYSTEM_PROP_PREFIX
-import static org.gradle.initialization.IGradlePropertiesLoader.ENV_PROJECT_PROPERTIES_PREFIX
-import static org.gradle.initialization.IGradlePropertiesLoader.SYSTEM_PROJECT_PROPERTIES_PREFIX
 
 class DefaultGradlePropertiesLoaderTest extends Specification {
 
-    private final StartParameterInternal startParameter = Mock(StartParameterInternal)
     private final Environment environment = Mock(Environment)
-    private final EnvironmentChangeTracker environmentChangeTracker = Mock(EnvironmentChangeTracker)
-    private final GradleInternal gradleInternal = Mock(GradleInternal)
-    private final DefaultGradlePropertiesLoader gradlePropertiesLoader = new DefaultGradlePropertiesLoader(
-        startParameter,
-        environment,
-        environmentChangeTracker,
-        gradleInternal
-    )
+    private final StartParameterInternal startParameter = Mock(StartParameterInternal)
+    private final DefaultGradlePropertiesLoader gradlePropertiesLoader = new DefaultGradlePropertiesLoader(startParameter, environment)
 
     private File gradleUserHomeDir
     private File settingsDir
     private File gradleInstallationHomeDir
-    private Map<String, String> prefixedSystemProperties = emptyMap()
-    private Map<String, String> prefixedEnvironmentVariables = emptyMap()
-    private Map<String, String> systemPropertiesArgs = emptyMap()
-    private Map<String, String> projectPropertiesArgs = emptyMap()
 
     @Rule
     public TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass())
-    @Rule
-    public SetSystemProperties sysProp = new SetSystemProperties()
 
     def setup() {
         gradleUserHomeDir = tmpDir.createDir("gradleUserHome")
@@ -61,14 +43,6 @@
         gradleInstallationHomeDir = tmpDir.createDir("gradleInstallationHome")
         _ * startParameter.gradleUserHomeDir >> gradleUserHomeDir
         _ * startParameter.gradleHomeDir >> gradleInstallationHomeDir
-        _ * startParameter.projectProperties >> { projectPropertiesArgs }
-        _ * startParameter.systemPropertiesArgs >> { systemPropertiesArgs }
-        _ * environment.systemProperties >> Mock(Environment.Properties) {
-            _ * it.byNamePrefix(SYSTEM_PROJECT_PROPERTIES_PREFIX) >> { prefixedSystemProperties }
-        }
-        _ * environment.variables >> Mock(Environment.Properties) {
-            _ * it.byNamePrefix(ENV_PROJECT_PROPERTIES_PREFIX) >> { prefixedEnvironmentVariables }
-        }
     }
 
     private static File fromDir(File dir) {
@@ -114,43 +88,6 @@
         "settings value" == properties["settingsProp"]
     }
 
-    def mergeAddsPropertiesFromEnvironmentVariablesWithPrefix() {
-        given:
-        prefixedEnvironmentVariables = [
-            (ENV_PROJECT_PROPERTIES_PREFIX + "envProp"): "env value"
-        ]
-
-        when:
-        def properties = loadAndMergePropertiesWith(emptyMap())
-
-        then:
-        "env value" == properties["envProp"]
-    }
-
-    def mergeAddsPropertiesFromSystemPropertiesWithPrefix() {
-        given:
-        prefixedSystemProperties = [
-            (SYSTEM_PROJECT_PROPERTIES_PREFIX + "systemProp"): "system value"
-        ]
-
-        when:
-        def properties = loadAndMergePropertiesWith(emptyMap())
-
-        then:
-        "system value" == properties["systemProp"]
-    }
-
-    def mergeAddsPropertiesFromStartParameter() {
-        given:
-        projectPropertiesArgs = ["paramProp": "param value"]
-
-        when:
-        def properties = loadAndMergePropertiesWith(emptyMap())
-
-        then:
-        "param value" == properties["paramProp"]
-    }
-
     def projectPropertiesHavePrecedenceOverInstallationPropertiesFile() {
         given:
         1 * environment.propertiesFile(fromDir(gradleInstallationHomeDir)) >> ["prop": "settings value"]
@@ -200,71 +137,6 @@
         "user value" == properties["prop"]
     }
 
-    def environmentVariablesHavePrecedenceOverProjectProperties() {
-        given:
-        1 * environment.propertiesFile(fromDir(gradleUserHomeDir)) >> ["prop": "user value"]
-        1 * environment.propertiesFile(fromDir(settingsDir)) >> ["prop": "settings value"]
-        prefixedEnvironmentVariables = [(ENV_PROJECT_PROPERTIES_PREFIX + "prop"): "env value"]
-        def projectProperties = ["prop": "project value"]
-
-        when:
-        def properties = loadAndMergePropertiesWith(projectProperties)
-
-        then:
-        "env value" == properties["prop"]
-    }
-
-    def systemPropertiesHavePrecedenceOverEnvironmentVariables() {
-        given:
-        1 * environment.propertiesFile(fromDir(gradleUserHomeDir)) >> ["prop": "user value"]
-        1 * environment.propertiesFile(fromDir(settingsDir)) >> ["prop": "settings value"]
-        prefixedEnvironmentVariables = [(ENV_PROJECT_PROPERTIES_PREFIX + "prop"): "env value"]
-        prefixedSystemProperties = [(SYSTEM_PROJECT_PROPERTIES_PREFIX + "prop"): "system value"]
-        def projectProperties = ["prop": "project value"]
-
-        when:
-        def properties = loadAndMergePropertiesWith(projectProperties)
-
-        then:
-        "system value" == properties["prop"]
-    }
-
-    def startParameterPropertiesHavePrecedenceOverSystemProperties() {
-        given:
-        1 * environment.propertiesFile(fromDir(gradleUserHomeDir)) >> ["prop": "user value"]
-        1 * environment.propertiesFile(fromDir(settingsDir)) >> ["prop": "settings value"]
-        projectPropertiesArgs = ["prop": "param value"]
-        prefixedEnvironmentVariables = [(ENV_PROJECT_PROPERTIES_PREFIX + "prop"): "env value"]
-        prefixedSystemProperties = [(SYSTEM_PROJECT_PROPERTIES_PREFIX + "prop"): "system value"]
-        def projectProperties = ["prop": "project value"]
-
-        when:
-        def properties = loadAndMergePropertiesWith(projectProperties)
-
-        then:
-        "param value" == properties["prop"]
-    }
-
-    def loadSetsSystemProperties() {
-        given:
-        systemPropertiesArgs = ["systemPropArgKey": "systemPropArgValue"]
-        1 * environment.propertiesFile(fromDir(gradleUserHomeDir)) >> [
-            (SYSTEM_PROP_PREFIX + ".userSystemProp"): "userSystemValue"
-        ]
-        1 * environment.propertiesFile(fromDir(settingsDir)) >> [
-            (SYSTEM_PROP_PREFIX + ".userSystemProp"): "settingsSystemValue",
-            (SYSTEM_PROP_PREFIX + ".settingsSystemProp2"): "settingsSystemValue2"
-        ]
-
-        when:
-        loadProperties()
-
-        then:
-        "userSystemValue" == System.getProperty("userSystemProp")
-        "settingsSystemValue2" == System.getProperty("settingsSystemProp2")
-        "systemPropArgValue" == System.getProperty("systemPropArgKey")
-    }
-
     def loadPropertiesWithNoExceptionForNonexistentGradleInstallationHomeAndUserHomeAndSettingsDir() {
         given:
         tmpDir.getTestDirectory().deleteDir()
@@ -299,36 +171,13 @@
         "value" == properties["prop2"]
 
         when:
-        properties = loadPropertiesFrom(otherSettingsDir).mergeProperties(emptyMap())
+        properties = loadPropertiesFrom(otherSettingsDir).getProperties()
 
         then:
         "otherValue" == properties["prop1"]
         properties["prop2"].is null
     }
 
-    def buildSystemProperties() {
-        given:
-        System.setProperty("gradle-loader-test", "value")
-
-        expect:
-        System.getProperties().containsKey("gradle-loader-test")
-        "value" == System.getProperties().get("gradle-loader-test")
-    }
-
-    def startParameterSystemPropertiesHavePrecedenceOverPropertiesFiles() {
-        given:
-        1 * environment.propertiesFile(fromDir(gradleUserHomeDir)) >> ["systemProp.prop": "user value"]
-        1 * environment.propertiesFile(fromDir(settingsDir)) >> ["systemProp.prop": "settings value"]
-        systemPropertiesArgs = ["prop": "commandline value"]
-        prefixedSystemProperties = [:]
-
-        when:
-        loadProperties()
-
-        then:
-        "commandline value" == System.getProperty("prop")
-    }
-
     private Map<String, String> loadAndMergePropertiesWith(Map<String, String> projectProperties) {
         return loadProperties().mergeProperties(projectProperties)
     }
@@ -338,6 +187,6 @@
     }
 
     private GradleProperties loadPropertiesFrom(File settingsDir) {
-        return gradlePropertiesLoader.loadProperties(settingsDir)
+        return gradlePropertiesLoader.loadGradleProperties(settingsDir)
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectPropertiesLoaderTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectPropertiesLoaderTest.groovy
new file mode 100644
index 0000000..6e6a234
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultProjectPropertiesLoaderTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 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.initialization
+
+import org.gradle.api.internal.StartParameterInternal
+import org.gradle.initialization.properties.DefaultProjectPropertiesLoader
+import org.gradle.initialization.properties.ProjectPropertiesLoader
+import spock.lang.Specification
+
+import static java.util.Collections.emptyMap
+import static org.gradle.initialization.IGradlePropertiesLoader.ENV_PROJECT_PROPERTIES_PREFIX
+import static org.gradle.initialization.IGradlePropertiesLoader.SYSTEM_PROJECT_PROPERTIES_PREFIX
+
+class DefaultProjectPropertiesLoaderTest extends Specification {
+
+    private Map<String, String> prefixedEnvironmentVariables = emptyMap()
+    private Map<String, String> prefixedSystemProperties = emptyMap()
+    private Map<String, String> projectPropertiesArgs = emptyMap()
+
+    private final StartParameterInternal startParameter = Mock(StartParameterInternal) {
+        getProjectProperties() >> { projectPropertiesArgs }
+    }
+
+    private final Environment environment = Mock(Environment) {
+        getSystemProperties() >> Mock(Environment.Properties) {
+            byNamePrefix(SYSTEM_PROJECT_PROPERTIES_PREFIX) >> { prefixedSystemProperties }
+        }
+        getVariables() >> Mock(Environment.Properties) {
+            byNamePrefix(ENV_PROJECT_PROPERTIES_PREFIX) >> { prefixedEnvironmentVariables }
+        }
+    }
+
+    private final ProjectPropertiesLoader projectPropertiesLoader = new DefaultProjectPropertiesLoader(startParameter, environment)
+
+    def "load properties from environment variables with prefix"() {
+        given:
+        prefixedEnvironmentVariables = [
+            (ENV_PROJECT_PROPERTIES_PREFIX + "envProp"): "env value"
+        ]
+
+        when:
+        def properties = projectPropertiesLoader.loadProjectProperties()
+
+        then:
+        "env value" == properties["envProp"]
+    }
+
+    def "load properties from system properties with prefix"() {
+        given:
+        prefixedSystemProperties = [
+            (SYSTEM_PROJECT_PROPERTIES_PREFIX + "systemProp"): "system value"
+        ]
+
+        when:
+        def properties = projectPropertiesLoader.loadProjectProperties()
+
+        then:
+        "system value" == properties["systemProp"]
+    }
+
+    def "load properties from start parameter"() {
+        given:
+        projectPropertiesArgs = ["paramProp": "param value"]
+
+        when:
+        def properties = projectPropertiesLoader.loadProjectProperties()
+
+        then:
+        "param value" == properties["paramProp"]
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSystemPropertiesInstallerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSystemPropertiesInstallerTest.groovy
new file mode 100644
index 0000000..cc2589f
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/DefaultSystemPropertiesInstallerTest.groovy
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2023 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.initialization
+
+import org.gradle.api.Project
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.StartParameterInternal
+import org.gradle.api.internal.properties.GradleProperties
+import org.gradle.initialization.properties.DefaultSystemPropertiesInstaller
+import org.gradle.initialization.properties.SystemPropertiesInstaller
+import org.gradle.util.SetSystemProperties
+import org.junit.Rule
+import spock.lang.Specification
+
+import static java.util.Collections.emptyMap
+
+class DefaultSystemPropertiesInstallerTest extends Specification {
+
+    private GradleProperties loadedGradleProperties = Mock(GradleProperties) {
+        getProperties() >> [
+            (Project.SYSTEM_PROP_PREFIX + ".userSystemProp"): "userSystemValue",
+            (Project.SYSTEM_PROP_PREFIX + ".userSystemProp2"): "userSystemValue2",
+        ]
+    }
+    private EnvironmentChangeTracker environmentChangeTracker = Mock(EnvironmentChangeTracker)
+    private StartParameterInternal startParameter = Mock(StartParameterInternal) {
+        systemPropertiesArgs >> { startParameterSystemProperties }
+    }
+    private GradleInternal gradle = Mock(GradleInternal)
+
+    private Map<String, String> startParameterSystemProperties = emptyMap()
+
+    private SystemPropertiesInstaller systemPropertiesInstaller = new DefaultSystemPropertiesInstaller(environmentChangeTracker, startParameter, gradle)
+
+    @Rule
+    public SetSystemProperties sysProp = new SetSystemProperties()
+
+    def "set system properties"() {
+        when:
+        systemPropertiesInstaller.setSystemPropertiesFrom(loadedGradleProperties)
+
+        then:
+        "userSystemValue" == System.getProperty("userSystemProp")
+        "userSystemValue2" == System.getProperty("userSystemProp2")
+    }
+
+    def "track loaded properties"() {
+        given:
+        gradle.isRootBuild() >> isRootBuild
+
+        when:
+        systemPropertiesInstaller.setSystemPropertiesFrom(loadedGradleProperties)
+
+        then:
+        if (isRootBuild) {
+            0 * environmentChangeTracker.systemPropertyLoaded(_)
+        } else {
+            1 * environmentChangeTracker.systemPropertyLoaded("userSystemProp", "userSystemValue", null)
+            1 * environmentChangeTracker.systemPropertyLoaded("userSystemProp2", "userSystemValue2", null)
+        }
+
+        where:
+        isRootBuild << [true, false]
+    }
+
+    def "track override properties"() {
+        given:
+        startParameterSystemProperties = [("overrideSystemProp"): "overrideSystemValue"]
+
+        when:
+        systemPropertiesInstaller.setSystemPropertiesFrom(loadedGradleProperties)
+
+        then:
+        1 * environmentChangeTracker.systemPropertyOverridden("overrideSystemProp")
+    }
+
+    def "build system properties"() {
+        given:
+        System.setProperty("gradle-loader-test", "value")
+
+        expect:
+        System.getProperties().containsKey("gradle-loader-test")
+        "value" == System.getProperties().get("gradle-loader-test")
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy
index 7ab02c9..f2018f7 100644
--- a/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/initialization/LayoutCommandLineConverterTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.internal.SystemProperties
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Issue
 import spock.lang.Specification
@@ -35,7 +35,7 @@
         converter.convert(Arrays.asList(args), new BuildLayoutParameters())
     }
 
-    @Requires(TestPrecondition.NOT_EC2_AGENT)
+    @Requires(UnitTestPreconditions.NotEC2Agent)
     @Issue('https://github.com/gradle/gradle-private/issues/2876')
     def "has reasonable defaults"() {
         expect:
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/build/BuildOperationFiringBuildWorkPreparerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/build/BuildOperationFiringBuildWorkPreparerTest.groovy
new file mode 100644
index 0000000..5f80691
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/build/BuildOperationFiringBuildWorkPreparerTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2023 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.Task
+import org.gradle.api.internal.GradleInternal
+import org.gradle.api.internal.TaskInternal
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.taskfactory.TestTaskIdentities
+import org.gradle.execution.plan.ExecutionPlan
+import org.gradle.execution.plan.LocalTaskNode
+import org.gradle.execution.plan.QueryableExecutionPlan
+import org.gradle.execution.plan.ToPlannedNodeConverterRegistry
+import org.gradle.execution.plan.ToPlannedTaskConverter
+import org.gradle.internal.operations.TestBuildOperationExecutor
+import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType
+import org.gradle.internal.taskgraph.NodeIdentity
+import org.gradle.util.Path
+import spock.lang.Specification
+
+import java.util.function.Consumer
+
+class BuildOperationFiringBuildWorkPreparerTest extends Specification {
+
+    def "build operation provides execution plan when queries with all node types"() {
+        def ti1 = TestTaskIdentities.create("t1", Task, Stub(ProjectInternal) {
+            projectPath(_) >> { args -> Path.path(":" + args[0]) }
+        })
+        def t = Stub(LocalTaskNode) {
+            getTask() >> Stub(TaskInternal) {
+                getTaskIdentity() >> ti1
+            }
+        }
+        List<Node> nodes = [t]
+
+        def scheduledNodesStub = Stub(QueryableExecutionPlan.ScheduledNodes) {
+            visitNodes(_) >> { Consumer<List<Node>> consumer ->
+                consumer.accept(nodes)
+            }
+        }
+
+        def executionPlan = Stub(ExecutionPlan) {
+            getContents() >> Stub(QueryableExecutionPlan) {
+                getScheduledNodes() >> scheduledNodesStub
+            }
+        }
+
+        def buildOperatorsExecutor = new TestBuildOperationExecutor()
+        def converterRegistry = new ToPlannedNodeConverterRegistry([new ToPlannedTaskConverter()])
+        def preparer = new BuildOperationFiringBuildWorkPreparer(buildOperatorsExecutor, Stub(BuildWorkPreparer), converterRegistry)
+
+        when:
+        preparer.populateWorkGraph(Stub(GradleInternal), executionPlan, {})
+        then:
+        def result = buildOperatorsExecutor.log.mostRecentResult(CalculateTaskGraphBuildOperationType)
+        result != null
+        def plannedNodes = result.getExecutionPlan(EnumSet.allOf(NodeIdentity.NodeType))
+        plannedNodes.size() == 1
+        with(plannedNodes[0]) {
+            nodeIdentity.nodeType == NodeIdentity.NodeType.TASK
+            (nodeIdentity as CalculateTaskGraphBuildOperationType.TaskIdentity).taskPath == ":t1"
+        }
+    }
+}
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 df5635a..4738873 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
@@ -24,7 +24,6 @@
 import org.gradle.execution.plan.FinalizedExecutionPlan
 import org.gradle.execution.taskgraph.TaskExecutionGraphInternal
 import org.gradle.initialization.exception.ExceptionAnalyser
-import org.gradle.initialization.internal.InternalBuildFinishedListener
 import org.gradle.internal.execution.BuildOutputCleanupRegistry
 import org.gradle.internal.service.DefaultServiceRegistry
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
@@ -35,7 +34,8 @@
 import java.util.function.Function
 
 class DefaultBuildLifecycleControllerTest extends Specification {
-    def buildBroadcaster = Mock(BuildListener)
+    def buildListener = Mock(BuildListener)
+    def buildModelLifecycleListener = Mock(BuildModelLifecycleListener)
     def workExecutor = Mock(BuildWorkExecutor)
     def workPreparer = Mock(BuildWorkPreparer)
 
@@ -44,7 +44,6 @@
 
     def buildModelController = Mock(BuildModelController)
     def exceptionAnalyser = Mock(ExceptionAnalyser)
-    def buildFinishedListener = Mock(InternalBuildFinishedListener.class)
     def executionPlan = Mock(ExecutionPlan)
     def finalizedPlan = Mock(FinalizedExecutionPlan)
     def toolingControllerFactory = Mock(BuildToolingModelControllerFactory)
@@ -64,8 +63,8 @@
     }
 
     DefaultBuildLifecycleController controller() {
-        return new DefaultBuildLifecycleController(gradleMock, buildModelController, exceptionAnalyser, buildBroadcaster,
-            buildFinishedListener, workPreparer, workExecutor, toolingControllerFactory, TestUtil.stateTransitionControllerFactory())
+        return new DefaultBuildLifecycleController(gradleMock, buildModelController, exceptionAnalyser, buildListener,
+            buildModelLifecycleListener, workPreparer, workExecutor, toolingControllerFactory, TestUtil.stateTransitionControllerFactory())
     }
 
     void testCanFinishBuildWhenNothingHasBeenDone() {
@@ -76,6 +75,9 @@
 
         def finishResult = controller.finishBuild(null)
         finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
     }
 
     void testScheduleAndRunRequestedTasks() {
@@ -95,6 +97,9 @@
 
         def finishResult = controller.finishBuild(null)
         finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
     }
 
     void testScheduleAndRunRequestedTasksMultipleTimes() {
@@ -123,6 +128,43 @@
 
         def finishResult = controller.finishBuild(null)
         finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
+    }
+
+    void testResetModelAfterSchedulingTasks() {
+        expect:
+        expectRequestedTasksScheduled()
+        expectTasksRun()
+        expectTasksScheduled()
+        expectModelReset()
+        expectBuildFinished()
+
+        def controller = controller()
+
+        controller.prepareToScheduleTasks()
+        def plan1 = controller.newWorkGraph()
+        controller.populateWorkGraph(plan1) { b -> b.addRequestedTasks() }
+        controller.finalizeWorkGraph(plan1)
+
+        def resetResult = controller.beforeModelReset()
+        resetResult.failures.empty
+
+        controller.resetModel()
+
+        controller.prepareToScheduleTasks()
+        def plan2 = controller.newWorkGraph()
+        controller.populateWorkGraph(plan2) {}
+        controller.finalizeWorkGraph(plan2)
+        def executionResult = controller.executeTasks(plan2)
+        executionResult.failures.empty
+
+        def finishResult = controller.finishBuild(null)
+        finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
     }
 
     void testLoadSettings() {
@@ -135,6 +177,9 @@
 
         def finishResult = controller.finishBuild(null)
         finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
     }
 
     void testWithSettings() {
@@ -156,6 +201,9 @@
         expectBuildFinished("Configure")
         def finishResult = controller.finishBuild(null)
         finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
     }
 
     void testNotifiesListenerOnLoadSettingsFailure() {
@@ -176,8 +224,15 @@
 
         then:
         1 * exceptionAnalyser.transform([failure]) >> transformedException
-        1 * buildBroadcaster.buildFinished({ it.failure == transformedException })
+        1 * buildListener.buildFinished({ it.failure == transformedException })
         finishResult.failures.empty
+
+        when:
+        def discardResult = controller.beforeModelDiscarded(true)
+
+        then:
+        1 * buildModelLifecycleListener.beforeModelDiscarded(gradleMock, true)
+        discardResult.failures.empty
     }
 
     void testLoadSettingsRethrowsPreviousFailure() {
@@ -221,6 +276,9 @@
         expectBuildFinished("Configure")
         def finishResult = controller.finishBuild(null)
         finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
     }
 
     void testWithConfiguredBuild() {
@@ -238,6 +296,9 @@
         expectBuildFinished("Configure")
         def finishResult = controller.finishBuild(null)
         finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
     }
 
     void testGetConfiguredBuild() {
@@ -254,6 +315,9 @@
         expectBuildFinished("Configure")
         def finishResult = controller.finishBuild(null)
         finishResult.failures.empty
+
+        def discardResult = controller.beforeModelDiscarded(false)
+        discardResult.failures.empty
     }
 
     void testNotifiesListenerOnConfigureBuildFailure() {
@@ -274,8 +338,15 @@
 
         then:
         1 * exceptionAnalyser.transform([failure]) >> transformedException
-        1 * buildBroadcaster.buildFinished({ it.failure == transformedException })
+        1 * buildListener.buildFinished({ it.failure == transformedException })
         finishResult.failures.empty
+
+        when:
+        def discardResult = controller.beforeModelDiscarded(true)
+
+        then:
+        1 * buildModelLifecycleListener.beforeModelDiscarded(gradleMock, true)
+        discardResult.failures.empty
     }
 
     void testConfigureBuildRethrowsPreviousFailure() {
@@ -319,8 +390,15 @@
         def finishResult = controller.finishBuild(null)
 
         then:
-        1 * buildBroadcaster.buildFinished({ it.failure == null })
+        1 * buildListener.buildFinished({ it.failure == null })
         finishResult.failures.empty
+
+        when:
+        def discardResult = controller.beforeModelDiscarded(false)
+
+        then:
+        1 * buildModelLifecycleListener.beforeModelDiscarded(gradleMock, false)
+        discardResult.failures.empty
     }
 
     void testNotifiesListenerOnTaskSchedulingFailure() {
@@ -344,8 +422,15 @@
 
         then:
         1 * exceptionAnalyser.transform([failure]) >> transformedException
-        1 * buildBroadcaster.buildFinished({ it.failure == transformedException && it.action == "Build" })
+        1 * buildListener.buildFinished({ it.failure == transformedException && it.action == "Build" })
         finishResult.failures.empty
+
+        when:
+        def discardResult = controller.beforeModelDiscarded(true)
+
+        then:
+        1 * buildModelLifecycleListener.beforeModelDiscarded(gradleMock, true)
+        discardResult.failures.empty
     }
 
     void testNotifiesListenerOnTaskExecutionFailure() {
@@ -369,8 +454,15 @@
 
         then:
         1 * exceptionAnalyser.transform([failure]) >> transformedException
-        1 * buildBroadcaster.buildFinished({ it.failure == transformedException })
+        1 * buildListener.buildFinished({ it.failure == transformedException })
         finishResult.failures.empty
+
+        when:
+        def discardResult = controller.beforeModelDiscarded(true)
+
+        then:
+        1 * buildModelLifecycleListener.beforeModelDiscarded(gradleMock, true)
+        discardResult.failures.empty
     }
 
     void testNotifiesListenerOnBuildCompleteWithMultipleFailures() {
@@ -396,8 +488,15 @@
 
         then:
         1 * exceptionAnalyser.transform([failure, failure2]) >> transformedException
-        1 * buildBroadcaster.buildFinished({ it.failure == transformedException })
+        1 * buildListener.buildFinished({ it.failure == transformedException })
         finishResult.failures.empty
+
+        when:
+        def discardResult = controller.beforeModelDiscarded(true)
+
+        then:
+        1 * buildModelLifecycleListener.beforeModelDiscarded(gradleMock, true)
+        discardResult.failures.empty
     }
 
     void testTransformsBuildFinishedListenerFailure() {
@@ -417,8 +516,15 @@
         def finishResult = controller.finishBuild(null)
 
         then:
-        1 * buildBroadcaster.buildFinished({ it.failure == null }) >> { throw failure }
+        1 * buildListener.buildFinished({ it.failure == null }) >> { throw failure }
         finishResult.failures == [failure]
+
+        when:
+        def discardResult = controller.beforeModelDiscarded(true)
+
+        then:
+        1 * buildModelLifecycleListener.beforeModelDiscarded(gradleMock, true)
+        discardResult.failures.empty
     }
 
     void testNotifiesListenersOnMultipleBuildFailuresAndBuildListenerFailure() {
@@ -447,14 +553,22 @@
 
         then:
         1 * exceptionAnalyser.transform([failure, failure2]) >> transformedException
-        1 * buildBroadcaster.buildFinished({ it.failure == transformedException }) >> { throw failure3 }
+        1 * buildListener.buildFinished({ it.failure == transformedException }) >> { throw failure3 }
         finishResult.failures == [failure3]
+
+        when:
+        def discardResult = controller.beforeModelDiscarded(true)
+
+        then:
+        1 * buildModelLifecycleListener.beforeModelDiscarded(gradleMock, true)
+        discardResult.failures.empty
     }
 
     void testCannotGetModelAfterFinished() {
         given:
         def controller = controller()
         controller.finishBuild(null)
+        controller.beforeModelDiscarded(false)
 
         when:
         controller.gradle
@@ -467,6 +581,7 @@
         given:
         def controller = controller()
         controller.finishBuild(null)
+        controller.beforeModelDiscarded(false)
 
         when:
         controller.loadSettings()
@@ -479,6 +594,7 @@
         given:
         def controller = controller()
         controller.finishBuild(null)
+        controller.beforeModelDiscarded(false)
 
         when:
         controller.configureProjects()
@@ -491,6 +607,7 @@
         given:
         def controller = controller()
         controller.finishBuild(null)
+        controller.beforeModelDiscarded(false)
 
         when:
         controller.prepareToScheduleTasks()
@@ -507,6 +624,10 @@
         1 * buildModelController.loadedSettings >> { throw failure }
     }
 
+    private void expectModelReset() {
+        1 * gradleMock.resetState()
+    }
+
     private void expectRequestedTasksScheduled() {
         1 * workPreparer.newExecutionPlan() >> executionPlan
         1 * buildModelController.prepareToScheduleTasks()
@@ -532,7 +653,7 @@
     }
 
     private void expectBuildFinished(String action = "Build") {
-        1 * buildBroadcaster.buildFinished({ it.failure == null && it.action == action })
-        1 * buildFinishedListener.buildFinished(_, false)
+        1 * buildListener.buildFinished({ it.failure == null && it.action == action })
+        1 * buildModelLifecycleListener.beforeModelDiscarded(_, false)
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/build/ExecutionResultTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/build/ExecutionResultTest.groovy
index 80e6be6..056ee9c 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/build/ExecutionResultTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/build/ExecutionResultTest.groovy
@@ -28,6 +28,7 @@
         expect:
         result.value == 12
         result.valueOrRethrow == 12
+        result.failureOrNull == null
         result.failures.empty
         result.rethrow()
 
@@ -50,6 +51,7 @@
         expect:
         result.value == null
         result.valueOrRethrow == null
+        result.failureOrNull == null
         result.failures.empty
         result.rethrow()
     }
@@ -61,6 +63,7 @@
         expect:
         result.failures == [failure]
         result.failure == failure
+        result.failureOrNull == failure
 
         when:
         result.valueOrRethrow
@@ -92,6 +95,8 @@
         result.failures == [failure1, failure2]
         result.failure instanceof MultipleBuildFailures
         result.failure.causes == [failure1, failure2]
+        result.failureOrNull instanceof MultipleBuildFailures
+        result.failureOrNull.causes == [failure1, failure2]
 
         when:
         result.valueOrRethrow
@@ -120,6 +125,7 @@
         expect:
         result.value == null
         result.valueOrRethrow == null
+        result.failureOrNull == null
         result.failures.empty
         result.rethrow()
     }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/build/PlannedNodeGraphTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/build/PlannedNodeGraphTest.groovy
new file mode 100644
index 0000000..707c7b6
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/build/PlannedNodeGraphTest.groovy
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2023 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.execution.plan.Node
+import org.gradle.execution.plan.PlannedNodeInternal
+import org.gradle.execution.plan.TaskDependencyResolver
+import org.gradle.execution.plan.ToPlannedNodeConverter
+import org.gradle.execution.plan.ToPlannedNodeConverterRegistry
+import org.gradle.internal.taskgraph.NodeIdentity
+import spock.lang.Specification
+
+import static org.gradle.internal.taskgraph.NodeIdentity.*
+
+class PlannedNodeGraphTest extends Specification {
+
+    // Anonymous classes to use as supported node types for converters (the count should be equal to the number of NodeIdentity.NodeType values)
+    private static List<Class<? extends Node>> anonymousNodeTypes = [new TestNode("") {}, new TestNode("") {}]*.class
+
+    def "can create collectors with different level of detail"() {
+        def taskConverter = stubConverter(anonymousNodeTypes[0], NodeType.TASK)
+        def transformStepConverter = stubConverter(anonymousNodeTypes[1], NodeType.TRANSFORM_STEP)
+
+        when:
+        def collector = new PlannedNodeGraph.Collector(new ToPlannedNodeConverterRegistry([taskConverter]))
+        then:
+        collector.detailLevel == PlannedNodeGraph.DetailLevel.LEVEL1_TASKS
+
+        when:
+        collector = new PlannedNodeGraph.Collector(new ToPlannedNodeConverterRegistry([taskConverter, transformStepConverter]))
+        then:
+        collector.detailLevel == PlannedNodeGraph.DetailLevel.LEVEL2_TRANSFORM_STEPS
+
+        when:
+        collector = new PlannedNodeGraph.Collector(new ToPlannedNodeConverterRegistry(NodeType.values().toList().withIndex().collect { nodeType, index ->
+            stubConverter(anonymousNodeTypes[index], nodeType)
+        }))
+        then:
+        collector.detailLevel == PlannedNodeGraph.DetailLevel.values().max { it.level }
+
+        when:
+        new PlannedNodeGraph.Collector(new ToPlannedNodeConverterRegistry([]))
+        then:
+        def e1 = thrown(IllegalStateException)
+        e1.message == "Unknown detail level for node types: []"
+
+        when:
+        new PlannedNodeGraph.Collector(new ToPlannedNodeConverterRegistry([transformStepConverter]))
+        then:
+        def e2 = thrown(IllegalStateException)
+        e2.message == "Unknown detail level for node types: [TRANSFORM_STEP]"
+    }
+
+    def "plan node dependencies include transitively closest identifiable nodes"() {
+        def taskConverter = new ToTestPlannedNodeConverter(TestTaskNode, NodeType.TASK)
+        def collector = new PlannedNodeGraph.Collector(new ToPlannedNodeConverterRegistry([taskConverter]))
+
+        def task1 = new TestTaskNode("task1")
+        def task2 = new TestTaskNode("task2")
+        def task3 = new TestTaskNode("task3")
+        def task4 = new TestTaskNode("task4")
+        def transform1 = new TestTransformStepNode("transform1")
+
+        dependsOn(task1, [task2, transform1])
+        dependsOn(task2, [task3])
+        dependsOn(task3, [task4])
+        dependsOn(transform1, [task4])
+
+        when:
+        collector.collectNodes([task1, task2, task3, task4, transform1])
+        def graph = collector.getGraph()
+        def nodes = graph.getNodes(PlannedNodeGraph.DetailLevel.LEVEL1_TASKS) as List<TestPlannedNode>
+        then:
+        nodes.size() == 4
+        nodes*.nodeIdentity*.nodeType =~ [NodeType.TASK]
+        verifyAll {
+            nodes[0].nodeIdentity.name == "task1"
+            nodes[0].nodeDependencies*.name == ["task2", "task4"] // does not include task3, because it is "behind" task2, but includes task4, because it is "behind" non-identifiable transform1
+            nodes[1].nodeIdentity.name == "task2"
+            nodes[1].nodeDependencies*.name == ["task3"]
+            nodes[2].nodeIdentity.name == "task3"
+            nodes[2].nodeDependencies*.name == ["task4"]
+            nodes[3].nodeIdentity.name == "task4"
+            nodes[3].nodeDependencies*.name == []
+        }
+
+        when:
+        def nextLevelNodes = graph.getNodes(PlannedNodeGraph.DetailLevel.LEVEL2_TRANSFORM_STEPS)
+        then:
+        // Requesting higher-than-available detail level should return the same nodes
+        nextLevelNodes == nodes
+    }
+
+    def "can obtain a plan with lower detail level"() {
+        def taskConverter = new ToTestPlannedNodeConverter(TestTaskNode, NodeType.TASK)
+        def transformStepConverter = new ToTestPlannedNodeConverter(TestTransformStepNode, NodeType.TRANSFORM_STEP)
+        def collector = new PlannedNodeGraph.Collector(new ToPlannedNodeConverterRegistry([taskConverter, transformStepConverter]))
+
+        def task1 = new TestTaskNode("task1")
+        def task2 = new TestTaskNode("task2")
+        def task3 = new TestTaskNode("task3")
+        def task4 = new TestTaskNode("task4")
+        def transformStep1 = new TestTransformStepNode("transformStep1")
+        def transformStep2 = new TestTransformStepNode("transformStep2")
+        def other1 = new TestNode("other1")
+        def other2 = new TestNode("other2")
+
+        dependsOn(task1, [other1])
+        dependsOn(other1, [transformStep1])
+        dependsOn(transformStep1, [task2, other2])
+        dependsOn(other2, [task3])
+        dependsOn(task4, [transformStep2])
+
+        when:
+        collector.collectNodes([task1, task2, task3, task4, transformStep1, transformStep2, other1, other2])
+        def graph = collector.getGraph()
+        def nodes = graph.getNodes(PlannedNodeGraph.DetailLevel.LEVEL2_TRANSFORM_STEPS) as List<TestPlannedNode>
+        then:
+        nodes.size() == 6
+        nodes*.nodeIdentity*.nodeType =~ [NodeType.TASK, NodeType.TRANSFORM_STEP]
+        verifyAll {
+            nodes[0].nodeIdentity.name == "task1"
+            nodes[0].nodeDependencies*.name == ["transformStep1"]
+            nodes[1].nodeIdentity.name == "task2"
+            nodes[1].nodeDependencies*.name == []
+            nodes[2].nodeIdentity.name == "task3"
+            nodes[2].nodeDependencies*.name == []
+            nodes[3].nodeIdentity.name == "task4"
+            nodes[3].nodeDependencies*.name == ["transformStep2"]
+            nodes[4].nodeIdentity.name == "transformStep1"
+            nodes[4].nodeDependencies*.name == ["task2", "task3"]
+            nodes[5].nodeIdentity.name == "transformStep2"
+            nodes[5].nodeDependencies*.name == []
+        }
+
+        when:
+        nodes = graph.getNodes(PlannedNodeGraph.DetailLevel.LEVEL1_TASKS) as List<TestPlannedNode>
+        then:
+        nodes.size() == 4
+        nodes*.nodeIdentity*.nodeType =~ [NodeType.TASK]
+        verifyAll {
+            nodes[0].nodeIdentity.name == "task1"
+            nodes[0].nodeDependencies*.name == ["task2", "task3"]
+            nodes[1].nodeIdentity.name == "task2"
+            nodes[1].nodeDependencies*.name == []
+            nodes[2].nodeIdentity.name == "task3"
+            nodes[2].nodeDependencies*.name == []
+            nodes[3].nodeIdentity.name == "task4"
+            nodes[3].nodeDependencies*.name == []
+        }
+    }
+
+    def dependsOn(TestNode node, List<TestNode> dependencies) {
+        dependencies.forEach { node.addDependencySuccessor(it) }
+    }
+
+    def stubConverter(Class<? extends Node> supportedNodeType, NodeType nodeType) {
+        Stub(ToPlannedNodeConverter) {
+            getSupportedNodeType() >> supportedNodeType
+            getConvertedNodeType() >> nodeType
+        }
+    }
+
+    static class ToTestPlannedNodeConverter implements ToPlannedNodeConverter {
+
+        private final Class<? extends Node> supportedNodeType
+        private final NodeType convertedNodeType
+
+        ToTestPlannedNodeConverter(Class<? extends Node> supportedNodeType, NodeType convertedNodeType) {
+            this.supportedNodeType = supportedNodeType
+            this.convertedNodeType = convertedNodeType
+        }
+
+        @Override
+        Class<? extends Node> getSupportedNodeType() {
+            return supportedNodeType
+        }
+
+        @Override
+        NodeType getConvertedNodeType() {
+            return convertedNodeType
+        }
+
+        @Override
+        TestNodeIdentity getNodeIdentity(Node node) {
+            return new TestNodeIdentity(convertedNodeType, (node as TestNode).name)
+        }
+
+        @Override
+        boolean isInSamePlan(Node node) {
+            return true
+        }
+
+        @Override
+        PlannedNodeInternal convert(Node node, List<? extends NodeIdentity> nodeDependencies) {
+            return new TestPlannedNode(getNodeIdentity(node), nodeDependencies)
+        }
+    }
+
+    static class TestNodeIdentity implements NodeIdentity {
+        NodeType nodeType
+        String name
+
+        TestNodeIdentity(NodeType nodeType, String name) {
+            this.nodeType = nodeType
+            this.name = name
+        }
+
+        @Override
+        NodeType getNodeType() {
+            return nodeType
+        }
+
+        @Override
+        String toString() {
+            return name
+        }
+
+        @Override
+        boolean equals(o) {
+            if (this.is(o)) {
+                return true
+            }
+            if (!(o instanceof TestNodeIdentity)) {
+                return false
+            }
+
+            TestNodeIdentity that = (TestNodeIdentity) o
+
+            if (name != that.name) {
+                return false
+            }
+            if (nodeType != that.nodeType) {
+                return false
+            }
+
+            return true
+        }
+
+        @Override
+        int hashCode() {
+            int result
+            result = nodeType.hashCode()
+            result = 31 * result + name.hashCode()
+            return result
+        }
+    }
+
+    static class TestPlannedNode implements PlannedNodeInternal {
+
+        private final TestNodeIdentity nodeIdentity
+        private final List<? extends NodeIdentity> dependencies
+
+        TestPlannedNode(TestNodeIdentity nodeIdentity, List<? extends NodeIdentity> dependencies) {
+            this.nodeIdentity = nodeIdentity
+            this.dependencies = dependencies
+        }
+
+        @Override
+        PlannedNodeInternal withNodeDependencies(List<? extends NodeIdentity> nodeDependencies) {
+            return new TestPlannedNode(nodeIdentity, nodeDependencies)
+        }
+
+        @Override
+        TestNodeIdentity getNodeIdentity() {
+            return nodeIdentity
+        }
+
+        @Override
+        List<? extends NodeIdentity> getNodeDependencies() {
+            return dependencies
+        }
+
+        @Override
+        String toString() {
+            return "TestPlannedNode($nodeIdentity)"
+        }
+    }
+
+    static class TestNode extends Node {
+        String name
+
+        TestNode(String name) {
+            this.name = name
+        }
+
+        @Override
+        Throwable getNodeFailure() {
+            return null
+        }
+
+        @Override
+        void resolveDependencies(TaskDependencyResolver dependencyResolver) {}
+
+        @Override
+        String toString() {
+            return "TestNode($name)"
+        }
+    }
+
+    static class TestTaskNode extends TestNode {
+        TestTaskNode(String name) {
+            super(name)
+        }
+    }
+
+    static class TestTransformStepNode extends TestNode {
+        TestTransformStepNode(String name) {
+            super(name)
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/BuildExceptionReporterTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/BuildExceptionReporterTest.groovy
index f4896c3..46e090e8 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/BuildExceptionReporterTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/BuildExceptionReporterTest.groovy
@@ -23,6 +23,7 @@
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.logging.configuration.LoggingConfiguration
 import org.gradle.api.logging.configuration.ShowStacktrace
+import org.gradle.api.tasks.TaskExecutionException
 import org.gradle.execution.MultipleBuildFailures
 import org.gradle.initialization.BuildClientMetaData
 import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager
@@ -42,6 +43,17 @@
     final LoggingConfiguration configuration = new DefaultLoggingConfiguration()
     final BuildExceptionReporter reporter = new BuildExceptionReporter(factory, configuration, clientMetaData, gradleEnterprisePluginManager)
 
+
+    static final String MESSAGE = "<message>"
+    static final String FAILURE = '<failure>'
+    static final String LOCATION = "<location>"
+    static final String STACKTRACE = "{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace."
+    static final String INFO_OR_DEBUG = "{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output."
+    static final String INFO = "{info}> {normal}Run with {userinput}--info{normal} option to get more log output."
+    static final String SCAN = "{info}> {normal}Run with {userinput}--scan{normal} to get full insights."
+    static final String GET_HELP = "{info}> {normal}Get more help at {userinput}https://help.gradle.org{normal}."
+
+
     def setup() {
         factory.create(BuildExceptionReporter.class, LogLevel.ERROR) >> output
         clientMetaData.describeCommand(!null, !null) >> { args -> args[0].append("[gradle ${args[1].join(' ')}]") }
@@ -54,27 +66,26 @@
     }
 
     def reportsBuildFailure() {
-        GradleException exception = new GradleException("<message>");
+        GradleException exception = new GradleException(MESSAGE);
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * What went wrong:
-<message>
+$MESSAGE
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
+"""
     }
 
     def "does not suggest to use --scan if option was on command line"() {
-        GradleException exception = new GradleException("<message>");
+        GradleException exception = new GradleException(MESSAGE);
 
         def result = result(exception)
         result.gradle >> Mock(Gradle) {
@@ -87,22 +98,21 @@
 
         expect:
         reporter.buildFinished(result)
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * What went wrong:
-<message>
+$MESSAGE
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$GET_HELP
+"""
     }
 
     def "does not suggest to use --scan if --no-scan is on command line"() {
-        GradleException exception = new GradleException("<message>");
+        GradleException exception = new GradleException(MESSAGE);
 
         def result = result(exception)
         result.gradle >> Mock(Gradle) {
@@ -115,18 +125,17 @@
 
         expect:
         reporter.buildFinished(result)
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * What went wrong:
-<message>
+$MESSAGE
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$GET_HELP
+"""
     }
 
     def reportsBuildFailureWhenFailureHasNoMessage() {
@@ -134,109 +143,105 @@
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * What went wrong:
 org.gradle.api.GradleException (no error message)
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
+"""
     }
 
     def reportsLocationAwareException() {
-        Throwable exception = new LocationAwareException(new RuntimeException("<message>", new RuntimeException("<cause>")), "<location>", 42)
+        Throwable exception = new LocationAwareException(new RuntimeException(MESSAGE, new RuntimeException("<cause>")), LOCATION, 42)
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * Where:
-<location> line: 42
+$LOCATION line: 42
 
 * What went wrong:
-<message>
+$MESSAGE
 {info}> {normal}<cause>
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
+"""
     }
 
     def reportsLocationAwareExceptionWithNoMessage() {
-        Throwable exception = new LocationAwareException(new RuntimeException(new IOException()), "<location>", 42)
+        Throwable exception = new LocationAwareException(new RuntimeException(new IOException()), LOCATION, 42)
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * Where:
-<location> line: 42
+$LOCATION line: 42
 
 * What went wrong:
 java.io.IOException
 {info}> {normal}java.io.IOException (no error message)
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
+"""
     }
 
     def reportsLocationAwareExceptionWithMultipleCauses() {
-        Throwable exception = new LocationAwareException(new DefaultMultiCauseException("<message>", new RuntimeException("<cause1>"), new RuntimeException("<cause2>")), "<location>", 42)
+        Throwable exception = new LocationAwareException(new DefaultMultiCauseException(MESSAGE, new RuntimeException("<cause1>"), new RuntimeException("<cause2>")), LOCATION, 42)
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * Where:
-<location> line: 42
+$LOCATION line: 42
 
 * What went wrong:
-<message>
+$MESSAGE
 {info}> {normal}<cause1>
 {info}> {normal}<cause2>
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
+"""
     }
 
     def reportsLocationAwareExceptionWithMultipleNestedCauses() {
         def cause1 = new DefaultMultiCauseException("<cause1>", new RuntimeException("<cause1.1>"), new RuntimeException("<cause1.2>"))
         def cause2 = new DefaultMultiCauseException("<cause2>", new RuntimeException("<cause2.1>"))
-        Throwable exception = new LocationAwareException(new DefaultMultiCauseException("<message>", cause1, cause2), "<location>", 42)
+        Throwable exception = new LocationAwareException(new DefaultMultiCauseException(MESSAGE, cause1, cause2), LOCATION, 42)
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * Where:
-<location> line: 42
+$LOCATION line: 42
 
 * What went wrong:
-<message>
+$MESSAGE
 {info}> {normal}<cause1>
    {info}> {normal}<cause1.1>
    {info}> {normal}<cause1.2>
@@ -244,103 +249,102 @@
    {info}> {normal}<cause2.1>
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
+"""
     }
 
     def reportsLocationAwareExceptionWhenCauseHasNoMessage() {
-        Throwable exception = new LocationAwareException(new RuntimeException("<message>", new RuntimeException()), "<location>", 42)
+        Throwable exception = new LocationAwareException(new RuntimeException(MESSAGE, new RuntimeException()), LOCATION, 42)
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * Where:
-<location> line: 42
+$LOCATION line: 42
 
 * What went wrong:
-<message>
+$MESSAGE
 {info}> {normal}java.lang.RuntimeException (no error message)
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
+"""
     }
 
     def showsStacktraceOfCauseOfLocationAwareException() {
         configuration.showStacktrace = ShowStacktrace.ALWAYS
 
-        Throwable exception = new LocationAwareException(new GradleException("<message>", new GradleException('<failure>')), "<location>", 42)
+        Throwable exception = new LocationAwareException(new GradleException(MESSAGE, new GradleException(FAILURE)), LOCATION, 42)
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * Where:
-<location> line: 42
+$LOCATION line: 42
 
 * What went wrong:
-<message>
-{info}> {normal}<failure>
+$MESSAGE
+{info}> {normal}$FAILURE
 
 * Try:
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
 
 * Exception is:
-org.gradle.api.GradleException: <message>
+org.gradle.api.GradleException: $MESSAGE
 {stacktrace}
-Caused by: org.gradle.api.GradleException: <failure>
+Caused by: org.gradle.api.GradleException: $FAILURE
 {stacktrace}
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+"""
     }
 
-    def reportsMultipleBuildFailures() {
-        def failure1 = new LocationAwareException(new RuntimeException("<message>", new RuntimeException("<cause>")), "<location>", 42)
-        def failure2 = new GradleException("<failure>")
+    def "report multiple failures and skip help link for NonGradleCauseException"() {
+        def failure1 = new LocationAwareException(new TaskExecutionException(null, new TestNonGradleCauseException()), LOCATION, 42)
+        def failure2 = new LocationAwareException(new TaskExecutionException(null, new TestCompilationFailureException()), LOCATION, 42)
         def failure3 = new RuntimeException("<error>")
         Throwable exception = new MultipleBuildFailures([failure1, failure2, failure3])
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: Build completed with 3 failures.{normal}
 
 {failure}1: {normal}{failure}Task failed with an exception.{normal}
 -----------
 * Where:
-<location> line: 42
+$LOCATION line: 42
 
 * What went wrong:
-<message>
-{info}> {normal}<cause>
+Execution failed for null.
+{info}> {normal}org.gradle.internal.buildevents.TestNonGradleCauseException (no error message)
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
+$SCAN
 ==============================================================================
 
 {failure}2: {normal}{failure}Task failed with an exception.{normal}
 -----------
+* Where:
+$LOCATION line: 42
+
 * What went wrong:
-<failure>
+Execution failed for null.
+{info}> {normal}org.gradle.internal.buildevents.TestCompilationFailureException (no error message)
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
+$INFO
+$SCAN
 ==============================================================================
 
 {failure}3: {normal}{failure}Task failed with an exception.{normal}
@@ -349,61 +353,60 @@
 <error>
 
 * Try:
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
 ==============================================================================
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-''';
+""";
     }
 
     def reportsBuildFailureWhenShowStacktraceEnabled() {
         configuration.showStacktrace = ShowStacktrace.ALWAYS
 
-        GradleException exception = new GradleException('<message>')
+        GradleException exception = new GradleException(MESSAGE)
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * What went wrong:
-<message>
+$MESSAGE
 
 * Try:
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
 
 * Exception is:
-org.gradle.api.GradleException: <message>
+org.gradle.api.GradleException: $MESSAGE
 {stacktrace}
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+"""
     }
 
     def reportsBuildFailureWhenShowFullStacktraceEnabled() {
         configuration.showStacktrace = ShowStacktrace.ALWAYS_FULL
 
-        GradleException exception = new GradleException('<message>')
+        GradleException exception = new GradleException(MESSAGE)
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * What went wrong:
-<message>
+$MESSAGE
 
 * Try:
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
 
 * Exception is:
-org.gradle.api.GradleException: <message>
+org.gradle.api.GradleException: $MESSAGE
 {stacktrace}
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+"""
     }
 
     def includesResolutionsFromExceptionWhenItImplementsFailureResolutionAware() {
@@ -417,21 +420,20 @@
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * What went wrong:
-<message>
+$MESSAGE
 
 * Try:
 {info}> {normal}resolution 1.
 {info}> {normal}resolution 2.
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-{info}> {normal}Run with {userinput}--scan{normal} to get full insights.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$SCAN
+$GET_HELP
+"""
     }
 
     def doesNotSuggestGeneratingAScanWhenTheBuildIsMissing() {
@@ -445,19 +447,18 @@
 
         expect:
         reporter.buildFinished(result(exception))
-        output.value == '''
+        output.value == """
 {failure}FAILURE: {normal}{failure}Build failed with an exception.{normal}
 
 * What went wrong:
-<message>
+$MESSAGE
 
 * Try:
 {info}> {normal}resolution 1.
-{info}> {normal}Run with {userinput}--stacktrace{normal} option to get the stack trace.
-{info}> {normal}Run with {userinput}--info{normal} or {userinput}--debug{normal} option to get more log output.
-
-* Get more help at {userinput}https://help.gradle.org{normal}
-'''
+$STACKTRACE
+$INFO_OR_DEBUG
+$GET_HELP
+"""
     }
 
     def result(Throwable failure) {
@@ -468,7 +469,7 @@
 
     abstract class TestException extends GradleException implements FailureResolutionAware {
         TestException() {
-            super("<message>")
+            super(MESSAGE)
         }
     }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/TestCompilationFailureException.java b/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/TestCompilationFailureException.java
new file mode 100644
index 0000000..c0ee667
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/TestCompilationFailureException.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 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.buildevents;
+
+import org.gradle.internal.exceptions.CompilationFailedIndicator;
+
+class TestCompilationFailureException extends Exception implements CompilationFailedIndicator {
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/TestNonGradleCauseException.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/TestNonGradleCauseException.groovy
new file mode 100644
index 0000000..c9c2af4
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/buildevents/TestNonGradleCauseException.groovy
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 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.buildevents
+
+import org.gradle.internal.exceptions.NonGradleCause
+
+class TestNonGradleCauseException extends Exception implements NonGradleCause {
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/AbstractCallInterceptionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/AbstractCallInterceptionTest.groovy
new file mode 100644
index 0000000..7c7659f7
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/AbstractCallInterceptionTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 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.classpath
+
+
+import spock.lang.Specification
+
+import java.util.function.Predicate
+
+abstract class AbstractCallInterceptionTest extends Specification {
+    protected abstract Predicate<String> shouldInstrumentAndReloadClassByName()
+
+    protected abstract JvmBytecodeInterceptorSet jvmBytecodeInterceptorSet()
+
+    protected abstract GroovyCallInterceptorsProvider groovyCallInterceptors()
+
+    protected InstrumentedClasses instrumentedClasses
+
+    private GroovyInterceptorsSubstitution groovyInterceptorsSubstitution
+
+    def setup() {
+        instrumentedClasses = new InstrumentedClasses(
+            getClass().classLoader,
+            shouldInstrumentAndReloadClassByName(),
+            jvmBytecodeInterceptorSet()
+        )
+        groovyInterceptorsSubstitution = new GroovyInterceptorsSubstitution(groovyCallInterceptors())
+        groovyInterceptorsSubstitution.setupForCurrentThread()
+    }
+
+    def cleanup() {
+        groovyInterceptorsSubstitution.cleanupForCurrentThread()
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/BasicCallInterceptionTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/BasicCallInterceptionTest.groovy
new file mode 100644
index 0000000..a0a20ed
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/BasicCallInterceptionTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 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.classpath
+
+import groovy.transform.stc.ClosureParams
+import groovy.transform.stc.SimpleType
+
+import java.util.function.Predicate
+
+import static java.util.function.Predicate.isEqual
+import static org.gradle.internal.classpath.InstrumentedClasses.nestedClassesOf
+
+class BasicCallInterceptionTest extends AbstractCallInterceptionTest {
+    @Override
+    protected Predicate<String> shouldInstrumentAndReloadClassByName() {
+        nestedClassesOf(BasicCallInterceptionTest) | isEqual(JavaCallerForBasicCallInterceptorTest.class.name)
+    }
+
+    @Override
+    protected JvmBytecodeInterceptorSet jvmBytecodeInterceptorSet() {
+        return { [BasicCallInterceptionTestInterceptorsDeclaration.JVM_BYTECODE_GENERATED_CLASS] }
+    }
+
+    @Override
+    protected GroovyCallInterceptorsProvider groovyCallInterceptors() {
+        return { [BasicCallInterceptionTestInterceptorsDeclaration.GROOVY_GENERATED_CLASS] }
+    }
+
+    String interceptedWhen(@ClosureParams(value = SimpleType, options = "InterceptorTestReceiver") Closure<?> call) {
+        def receiver = new InterceptorTestReceiver()
+        instrumentedClasses.instrumentedClosure(call).call(receiver)
+        receiver.intercepted
+    }
+
+    def 'intercepts a basic instance call with $name'() {
+        when:
+        def intercepted = interceptedWhen(invocation)
+
+        then:
+        intercepted == expected
+
+        where:
+        // TODO: the set of the test cases should be extended; the ones listed currently are an example
+        name                       | invocation                                                    | expected
+        "no argument from Java"    | { JavaCallerForBasicCallInterceptorTest.doCallNoArg(it) }     | "call()"
+        "one argument from Java"   | { JavaCallerForBasicCallInterceptorTest.doCallSingleArg(it) } | "call(InterceptorTestReceiver)"
+        "vararg from Java"         | { JavaCallerForBasicCallInterceptorTest.doCallVararg(it) }    | "callVararg(Object...)"
+
+        "no argument from Groovy"  | { it.call() }                                                 | "call()"
+        "one argument from Groovy" | { it.call(it) }                                               | "call(InterceptorTestReceiver)"
+        "vararg from Groovy"       | { it.callVararg(it, it, it) }                                 | "callVararg(Object...)"
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClassWithCapturelessLambda.java b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClassWithCapturelessLambda.java
new file mode 100644
index 0000000..7ddfded
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClassWithCapturelessLambda.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import java.util.function.Consumer;
+
+public class ClassWithCapturelessLambda {
+    public static Consumer<StringBuilder> action() {
+        return s -> {
+            s.append("value");
+        };
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClassWithObjectCapturingLambda.java b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClassWithObjectCapturingLambda.java
new file mode 100644
index 0000000..6d31d78
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClassWithObjectCapturingLambda.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import java.util.function.Consumer;
+
+public class ClassWithObjectCapturingLambda {
+    public Consumer<StringBuilder> action() {
+        String value = "value";
+        return s -> {
+            s.append(value);
+        };
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClasspathWalkerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClasspathWalkerTest.groovy
index 15a9e77..e782b66 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClasspathWalkerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/ClasspathWalkerTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.internal.file.FileException
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -39,7 +39,7 @@
         0 * visitor._
     }
 
-    @Requires(TestPrecondition.JDK11_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk11OrLater)
     def "throws FileException on badly formed JAR"() {
         def visitor = Mock(ClasspathEntryVisitor)
         def file = tmpDir.file("broken").createFile()
@@ -54,7 +54,7 @@
     }
 
     // This documents current behaviour, not desired behaviour
-    @Requires(TestPrecondition.JDK10_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk10OrEarlier)
     def "ignores badly formed JAR"() {
         def visitor = Mock(ClasspathEntryVisitor)
         def file = tmpDir.file("broken").createFile()
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/DefaultCachedClasspathTransformerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/DefaultCachedClasspathTransformerTest.groovy
index c57010c..f6c53da 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/DefaultCachedClasspathTransformerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/DefaultCachedClasspathTransformerTest.groovy
@@ -20,12 +20,13 @@
 import org.gradle.api.internal.cache.CacheConfigurationsInternal
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.cache.CacheBuilder
-import org.gradle.cache.internal.CleanupActionDecorator
 import org.gradle.cache.FileLockManager
 import org.gradle.cache.GlobalCacheLocations
+import org.gradle.cache.internal.CleanupActionDecorator
 import org.gradle.cache.internal.UsedGradleVersions
 import org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory
 import org.gradle.internal.Pair
+import org.gradle.internal.agents.AgentStatus
 import org.gradle.internal.classloader.FilteringClassLoader
 import org.gradle.internal.file.FileAccessTimeJournal
 import org.gradle.internal.hash.Hasher
@@ -70,6 +71,10 @@
     def fileSystemAccess = TestFiles.fileSystemAccess()
     def globalCacheLocations = Stub(GlobalCacheLocations)
     def fileLockManager = Stub(FileLockManager)
+    def agentStatus = Stub(AgentStatus) {
+        // TODO(mlopatkin) Invent a way to test this with agent-based instrumentation
+        isAgentInstrumentationEnabled() >> false
+    }
     URLClassLoader testClassLoader = null
 
     @Subject
@@ -82,7 +87,8 @@
         fileSystemAccess,
         executorFactory,
         globalCacheLocations,
-        fileLockManager
+        fileLockManager,
+        agentStatus
     )
 
     def cleanup() {
@@ -241,7 +247,7 @@
         def file = testDir.file("thing.jar")
         jar(file)
         def classpath = DefaultClassPath.of(file)
-        def cachedFile = testDir.file("cached/073ad98e99cb900c3eda1074690cf327/thing.jar")
+        def cachedFile = testDir.file("cached/01e416160778d051d40336af60a1dca7/thing.jar")
 
         when:
         def cachedClasspath = transformer.transform(classpath, BuildLogic)
@@ -269,7 +275,7 @@
         def dir = testDir.file("thing.dir")
         classesDir(dir)
         def classpath = DefaultClassPath.of(dir)
-        def cachedFile = testDir.file("cached/178e7de4384d207e79ca0da5482e9dbd/thing.dir.jar")
+        def cachedFile = testDir.file("cached/3adb41d26ed4b3fc7a3c0e70dd612e24/thing.dir.jar")
 
         when:
         def cachedClasspath = transformer.transform(classpath, BuildLogic)
@@ -299,8 +305,8 @@
         def file = testDir.file("thing.jar")
         jar(file)
         def classpath = DefaultClassPath.of(dir, file)
-        def cachedDir = testDir.file("cached/178e7de4384d207e79ca0da5482e9dbd/thing.dir.jar")
-        def cachedFile = testDir.file("cached/073ad98e99cb900c3eda1074690cf327/thing.jar")
+        def cachedDir = testDir.file("cached/3adb41d26ed4b3fc7a3c0e70dd612e24/thing.dir.jar")
+        def cachedFile = testDir.file("cached/01e416160778d051d40336af60a1dca7/thing.jar")
 
         when:
         def cachedClasspath = transformer.transform(classpath, BuildLogic)
@@ -357,8 +363,8 @@
         def file3 = testDir.file("thing3.jar")
         jar(file3)
         def classpath = DefaultClassPath.of(dir, file, dir2, file2, dir3, file3)
-        def cachedDir = testDir.file("cached/178e7de4384d207e79ca0da5482e9dbd/thing.dir.jar")
-        def cachedFile = testDir.file("cached/073ad98e99cb900c3eda1074690cf327/thing.jar")
+        def cachedDir = testDir.file("cached/3adb41d26ed4b3fc7a3c0e70dd612e24/thing.dir.jar")
+        def cachedFile = testDir.file("cached/01e416160778d051d40336af60a1dca7/thing.jar")
 
         when:
         def cachedClasspath = transformer.transform(classpath, BuildLogic)
@@ -378,7 +384,7 @@
         def file = testDir.file("thing.jar")
         jar(file)
         def classpath = DefaultClassPath.of(file)
-        def cachedFile = testDir.file("cached/d78c58860acf6b3e0967e6f37f3cb2fa/thing.jar")
+        def cachedFile = testDir.file("cached/5dc4b6748a3b200dfeacab5756530be3/thing.jar")
 
         when:
         def cachedClasspath = transformer.transform(classpath, BuildLogic, transform)
@@ -388,7 +394,7 @@
 
         and:
         1 * transform.applyConfigurationTo(_) >> { Hasher hasher -> hasher.putInt(123) }
-        1 * transform.apply(_, _) >> { entry, visitor ->
+        1 * transform.apply(_, _, _) >> { entry, visitor, data ->
             assert entry.name == "a.class"
             Pair.of(entry.path, visitor)
         }
@@ -412,7 +418,7 @@
         def file = testDir.file("thing.jar")
         jarWithStoredResource(file)
         def classpath = DefaultClassPath.of(file)
-        def cachedFile = testDir.file("cached/54d492d97b2c57616a51af37f14507b7/thing.jar")
+        def cachedFile = testDir.file("cached/24e26f6ec0b84c21d9101cab1617c298/thing.jar")
 
         when:
         def cachedClasspath = transformer.transform(classpath, BuildLogic)
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/JavaCallerForBasicCallInterceptorTest.java b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/JavaCallerForBasicCallInterceptorTest.java
new file mode 100644
index 0000000..7ceade3
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/JavaCallerForBasicCallInterceptorTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.classpath;
+
+public class JavaCallerForBasicCallInterceptorTest {
+    public static void doCallNoArg(InterceptorTestReceiver receiver) {
+        receiver.call();
+    }
+
+    public static void doCallSingleArg(InterceptorTestReceiver receiver) {
+        receiver.call(receiver);
+    }
+
+    public static void doCallVararg(InterceptorTestReceiver receiver) {
+        receiver.callVararg(receiver, receiver, receiver);
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/classpath/LambdaSerializationTransformerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/LambdaSerializationTransformerTest.groovy
new file mode 100644
index 0000000..94b72a8
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/classpath/LambdaSerializationTransformerTest.groovy
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 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.classpath
+
+
+import org.gradle.internal.classanalysis.AsmConstants
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.ClassWriter
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Type
+import org.objectweb.asm.commons.CodeSizeEvaluator
+import spock.lang.Specification
+
+class LambdaSerializationTransformerTest extends Specification {
+    def "transformer estimates deserialization code size of #cl correctly"() {
+        when:
+        def classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS)
+
+        LambdaSerializationTransformer transformer = acceptClass(cl, new LambdaSerializationTransformer(classWriter))
+
+        def estimatedDeserializationMethodSize = (
+            transformer.estimatedDeserializationPrologueLength
+                + transformer.estimatedEpilogueLength
+                + transformer.getEstimatedSingleLambdaHandlingCodeLength(lambdaArgs as Type[])
+        )
+
+        then:
+        // ASM has no API to figure out the exact method size, so we have to rely on estimations.
+        estimatedDeserializationMethodSize == getMaxEvaluatedDeserializationMethodSize(classWriter.toByteArray())
+
+        where:
+        cl                             | lambdaArgs
+        ClassWithActionLambda          | [Type.getType(int.class)]
+        ClassWithCapturelessLambda     | []
+        ClassWithObjectCapturingLambda | [Type.getType(String)]
+    }
+
+    int getMaxEvaluatedDeserializationMethodSize(byte[] classData) {
+        int maxMethodSize = 0;
+        def deserializeLambdaSizeEvaluator = new ClassVisitor(AsmConstants.ASM_LEVEL) {
+            @Override
+            MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
+                if ('$deserializeLambda$' == name) {
+                    return new CodeSizeEvaluator(super.visitMethod(access, name, descriptor, signature, exceptions)) {
+                        @Override
+                        void visitEnd() {
+                            maxMethodSize = getMaxSize()
+                            super.visitEnd()
+                        }
+                    }
+                }
+                return super.visitMethod(access, name, descriptor, signature, exceptions)
+            }
+        }
+
+        acceptClass(classData, deserializeLambdaSizeEvaluator)
+
+        return maxMethodSize
+    }
+
+    <T extends ClassVisitor> T acceptClass(Class cl, T visitor) {
+        def fileName = cl.name.replace('.', '/') + ".class"
+        byte[] classData = cl.classLoader.getResource(fileName).bytes
+        acceptClass(classData, visitor)
+        return visitor
+    }
+
+    <T extends ClassVisitor> T acceptClass(byte[] classData, T visitor) {
+        new ClassReader(classData).accept(visitor, 0)
+        return visitor
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/id/ConfigurationCacheableIdFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/id/ConfigurationCacheableIdFactoryTest.groovy
new file mode 100644
index 0000000..cfd1401
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/id/ConfigurationCacheableIdFactoryTest.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2023 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.id
+
+import spock.lang.Specification
+
+class ConfigurationCacheableIdFactoryTest extends Specification {
+
+    def "creating ids after loading is not allowed"() {
+        def factory = new ConfigurationCacheableIdFactory()
+
+        when:
+        factory.idRecreated()
+        factory.idRecreated()
+        then:
+        noExceptionThrown()
+
+        when:
+        factory.createId()
+        then:
+        def e = thrown(IllegalStateException)
+        e.message == "Cannot create a new id after one has been loaded"
+
+        // repeating creation still throws an exception
+        when:
+        factory.createId()
+        then:
+        def e2 = thrown(IllegalStateException)
+        e2.message == "Cannot create a new id after one has been loaded"
+    }
+
+    def "creating ids again after loading is not allowed"() {
+        def factory = new ConfigurationCacheableIdFactory()
+
+        when:
+        def id1 = factory.createId()
+        def id2 = factory.createId()
+        then:
+        id1 != id2
+
+        when:
+        factory.idRecreated()
+        factory.idRecreated()
+        then:
+        noExceptionThrown()
+
+        when:
+        factory.createId()
+        then:
+        def e = thrown(IllegalStateException)
+        e.message == "Cannot create a new id after one has been loaded"
+    }
+
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/model/StateTransitionControllerTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/model/StateTransitionControllerTest.groovy
index eff2e4d..434cf37 100644
--- a/subprojects/core/src/test/groovy/org/gradle/internal/model/StateTransitionControllerTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/model/StateTransitionControllerTest.groovy
@@ -18,11 +18,13 @@
 
 import org.gradle.internal.Describables
 import org.gradle.internal.Factory
+import org.gradle.internal.build.ExecutionResult
 import org.gradle.internal.concurrent.DefaultParallelismConfiguration
 import org.gradle.internal.resources.DefaultResourceLockCoordinationService
 import org.gradle.internal.work.DefaultWorkerLeaseService
 import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 
+import java.util.function.Function
 import java.util.function.Supplier
 
 class StateTransitionControllerTest extends ConcurrentSpec {
@@ -693,6 +695,88 @@
         noExceptionThrown()
     }
 
+    def "can transition to new state and take previous failure as input when nothing has failed"() {
+        def action = Mock(Function)
+        def controller = controller(TestState.A)
+
+        when:
+        def result = asWorker {
+            controller.transition(TestState.A, TestState.B, action)
+        }
+
+        then:
+        1 * action.apply(_) >> { ExecutionResult r ->
+            assert r.failures.empty
+            ExecutionResult.succeeded()
+        }
+        0 * action._
+        result.failures.empty
+    }
+
+    def "can transition to new state and take previous failure as input"() {
+        def action = Mock(Function)
+        def controller = controller(TestState.A)
+
+        given:
+        asWorker {
+            makeBroken(controller)
+        }
+
+        when:
+        def result = asWorker {
+            controller.transition(TestState.A, TestState.B, action)
+        }
+
+        then:
+        1 * action.apply(_) >> { ExecutionResult r ->
+            assert r.failures.size() == 1
+            ExecutionResult.succeeded()
+        }
+        0 * action._
+        result.failures.empty
+    }
+
+    def "retains failure in action that takes previous failure as input"() {
+        def action = Mock(Function)
+        def failure = new RuntimeException()
+        def controller = controller(TestState.A)
+
+        when:
+        def result = asWorker {
+            controller.transition(TestState.A, TestState.B, action)
+        }
+
+        then:
+        1 * action.apply(_) >> { ExecutionResult r ->
+            throw failure
+        }
+        0 * action._
+        result.failures == [failure]
+
+        when:
+        asWorker {
+            controller.transition(TestState.B, TestState.A) {}
+        }
+
+        then:
+        def e = thrown(RuntimeException)
+        e == failure
+    }
+
+    def "transition that takes previous failure as input fails when in unexpected state"() {
+        def action = Mock(Function)
+        def controller = controller(TestState.A)
+
+        when:
+        asWorker {
+            controller.transition(TestState.B, TestState.C, action)
+        }
+
+        then:
+        def e = thrown(IllegalStateException)
+        e.message == 'Can only transition <state> to state C from state B however it is currently in state A.'
+    }
+
     def "can reset state to initial state"() {
         def action = Mock(Runnable)
         def resetAction = Mock(Runnable)
@@ -705,7 +789,7 @@
 
         when:
         asWorker {
-            controller.restart(TestState.A, resetAction)
+            controller.restart(TestState.B, TestState.A, resetAction)
         }
 
         then:
@@ -722,4 +806,61 @@
         0 * _
     }
 
+    def "can reset state to initial state and discard collected failure"() {
+        def action = Mock(Runnable)
+        def resetAction = Mock(Runnable)
+        def controller = controller(TestState.A)
+
+        given:
+        asWorker {
+            makeBroken(controller)
+        }
+
+        when:
+        asWorker {
+            controller.restart(TestState.B, TestState.A, resetAction)
+        }
+
+        then:
+        1 * resetAction.run()
+        0 * _
+
+        when:
+        asWorker {
+            controller.transition(TestState.A, TestState.B, action)
+        }
+
+        then:
+        1 * action.run()
+        0 * _
+    }
+
+    def "reset state fails when not in expected from state"() {
+        def resetAction = Mock(Runnable)
+        def controller = controller(TestState.A)
+
+        given:
+        asWorker {
+            controller.transition(TestState.A, TestState.C) {}
+        }
+
+        when:
+        asWorker {
+            controller.restart(TestState.B, TestState.A, resetAction)
+        }
+
+        then:
+        def e = thrown(IllegalStateException)
+        e.message == 'Can only transition <state> to state A from state B however it is currently in state C.'
+    }
+
+    private makeBroken(StateTransitionController<TestState> controller) {
+        try {
+            controller.transition(TestState.A, TestState.B) {
+                throw new RuntimeException("broken")
+            }
+        } catch (RuntimeException e) {
+            // ignore
+        }
+    }
 }
diff --git a/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/ToPlannedNodeConverterRegistryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/ToPlannedNodeConverterRegistryTest.groovy
new file mode 100644
index 0000000..ed0a40e
--- /dev/null
+++ b/subprojects/core/src/test/groovy/org/gradle/internal/service/scopes/ToPlannedNodeConverterRegistryTest.groovy
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2023 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.service.scopes
+
+import org.gradle.execution.plan.Node
+import org.gradle.execution.plan.TaskDependencyResolver
+import org.gradle.execution.plan.TaskNode
+import org.gradle.execution.plan.ToPlannedNodeConverter
+import org.gradle.execution.plan.ToPlannedNodeConverterRegistry
+import org.gradle.execution.plan.ToPlannedTaskConverter
+import spock.lang.Specification
+
+class ToPlannedNodeConverterRegistryTest extends Specification {
+
+    def "can be empty"() {
+        def registry = new ToPlannedNodeConverterRegistry([])
+        expect:
+        registry.getConverter(Stub(Node)) == null
+    }
+
+    def "finds converter matching type"() {
+        def testConverter = Stub(ToPlannedNodeConverter) {
+            getSupportedNodeType() >> TestNode
+        }
+        def registry = new ToPlannedNodeConverterRegistry([testConverter])
+
+        when:
+        def foundConverter = registry.getConverter(new TestNode())
+        then:
+        foundConverter.is(testConverter)
+
+        // verify caching works
+        when:
+        foundConverter = registry.getConverter(new TestNode())
+        then:
+        foundConverter.is(testConverter)
+    }
+
+    def "finds converter for node of sub type"() {
+        def testConverter = Stub(ToPlannedNodeConverter) {
+            getSupportedNodeType() >> Node
+        }
+        def registry = new ToPlannedNodeConverterRegistry([testConverter])
+
+        when:
+        def foundConverter = registry.getConverter(new TestNode())
+        then:
+        foundConverter.is(testConverter)
+    }
+
+    def "does not find converter for node of parent type"() {
+        def testConverter = Stub(ToPlannedNodeConverter) {
+            getSupportedNodeType() >> TestNode
+        }
+        def registry = new ToPlannedNodeConverterRegistry([testConverter])
+
+        when:
+        def foundConverter = registry.getConverter(Stub(Node))
+        then:
+        foundConverter == null
+
+        // Verify caching works
+        when:
+        foundConverter = registry.getConverter(Stub(Node))
+        then:
+        foundConverter == null
+    }
+
+    def "finds converter matching type among multiple"() {
+        def testConverter = Stub(ToPlannedNodeConverter) {
+            getSupportedNodeType() >> TestNode
+        }
+        def taskConverter = new ToPlannedTaskConverter()
+        def registry = new ToPlannedNodeConverterRegistry([taskConverter, testConverter])
+
+        when:
+        def foundConverter = registry.getConverter(new TestNode())
+        then:
+        foundConverter.is(testConverter)
+
+        when:
+        foundConverter = registry.getConverter(Stub(TaskNode))
+        then:
+        foundConverter.is(taskConverter)
+    }
+
+    def "validates converters support disjoint set of types"() {
+        def testConverter1 = Stub(ToPlannedNodeConverter) {
+            getSupportedNodeType() >> TestNode
+            toString() >> "testConverter1"
+        }
+        def testConverter2 = Stub(ToPlannedNodeConverter) {
+            getSupportedNodeType() >> TestNode
+            toString() >> "testConverter2"
+        }
+
+        when:
+        new ToPlannedNodeConverterRegistry([testConverter1, testConverter2])
+        then:
+        def e = thrown(IllegalStateException)
+        e.message == "Converter testConverter1 overlaps by supported node type with converter testConverter2"
+    }
+
+    static class TestNode extends Node {
+        @Override
+        Throwable getNodeFailure() {
+            return null
+        }
+
+        @Override
+        void resolveDependencies(TaskDependencyResolver dependencyResolver) {}
+
+        @Override
+        String toString() {
+            "TestNode"
+        }
+    }
+}
diff --git a/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleSpec.groovy b/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleSpec.groovy
index 2188332..6acf043 100644
--- a/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleSpec.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/invocation/DefaultGradleSpec.groovy
@@ -17,7 +17,13 @@
 package org.gradle.invocation
 
 import org.gradle.api.Action
+import org.gradle.api.Project
+import org.gradle.api.ProjectEvaluationListener
+import org.gradle.api.ProjectState
+import org.gradle.api.Task
+import org.gradle.api.execution.TaskExecutionListener
 import org.gradle.api.initialization.dsl.ScriptHandler
+import org.gradle.api.internal.BuildScopeListenerRegistrationListener
 import org.gradle.api.internal.GradleInternal
 import org.gradle.api.internal.SettingsInternal
 import org.gradle.api.internal.StartParameterInternal
@@ -25,6 +31,7 @@
 import org.gradle.api.internal.project.CrossProjectConfigurator
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.tasks.TaskContainerInternal
+import org.gradle.api.tasks.TaskState
 import org.gradle.configuration.internal.ListenerBuildOperationDecorator
 import org.gradle.configuration.internal.TestListenerBuildOperationDecorator
 import org.gradle.execution.taskgraph.TaskExecutionGraphInternal
@@ -420,6 +427,39 @@
         gradle.toString() == "build 'rootProject'"
     }
 
+    @SuppressWarnings("deprecation")
+    interface UnsupportedDescendant extends TaskExecutionListener, ProjectEvaluationListener {}
+
+    def "notifies observers when a descendant of unsupported listener interface is added"() {
+        given:
+        def registrationListener = Mock(BuildScopeListenerRegistrationListener)
+        listenerManager.addListener(registrationListener)
+        when:
+        gradle.addListener(new UnsupportedDescendant() {
+            @Override
+            void beforeEvaluate(Project project) {
+            }
+
+            @Override
+            void afterEvaluate(Project project, ProjectState state) {
+            }
+
+            @Override
+            void beforeExecute(Task task) {
+            }
+
+            @Override
+            void afterExecute(Task task, TaskState state) {
+            }
+        })
+
+        then:
+        1 * registrationListener.onBuildScopeListenerRegistration(_, _, _)
+
+        cleanup:
+        listenerManager.removeListener(registrationListener)
+    }
+
     private ProjectInternal project(String name) {
         def project = Mock(ProjectInternal)
         _ * project.name >> name
diff --git a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecActionFactoryTest.groovy b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecActionFactoryTest.groovy
index c888281..8cb544b 100644
--- a/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecActionFactoryTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/process/internal/DefaultExecActionFactoryTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.process.ExecResult
 import org.gradle.test.fixtures.concurrent.ConcurrentSpec
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.TestUtil
 import org.junit.Rule
 
@@ -78,7 +78,7 @@
         result.exitValue != 0
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def exec() {
         File testFile = tmpDir.file("someFile")
 
@@ -94,7 +94,7 @@
         result.exitValue == 0
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def execWithNonZeroExitValueShouldThrowException() {
         when:
         factory.exec { spec ->
@@ -107,7 +107,7 @@
         thrown(ExecException)
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def execWithNonZeroExitValueAndIgnoreExitValueShouldNotThrowException() {
         when:
         ExecResult result = factory.exec { spec ->
diff --git a/subprojects/core/src/test/groovy/org/gradle/util/internal/NameValidatorTest.groovy b/subprojects/core/src/test/groovy/org/gradle/util/internal/NameValidatorTest.groovy
index 2908d44..e8bbbe4 100644
--- a/subprojects/core/src/test/groovy/org/gradle/util/internal/NameValidatorTest.groovy
+++ b/subprojects/core/src/test/groovy/org/gradle/util/internal/NameValidatorTest.groovy
@@ -26,8 +26,10 @@
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.project.taskfactory.TaskFactory
+import org.gradle.api.internal.project.taskfactory.TaskIdentityFactory
 import org.gradle.api.internal.project.taskfactory.TaskInstantiator
 import org.gradle.api.internal.tasks.DefaultSourceSetContainer
+import org.gradle.internal.id.ConfigurationCacheableIdFactory
 import org.gradle.internal.instantiation.InstantiationScheme
 import org.gradle.nativeplatform.internal.DefaultFlavorContainer
 import org.gradle.util.AttributeTestUtil
@@ -68,7 +70,7 @@
                 getIdentityPath() >> Path.path(":build:foo:bar")
             }
         }
-        new TaskInstantiator(new TaskFactory(project, Mock(InstantiationScheme)), project).create(name, DefaultTask)
+        new TaskInstantiator(new TaskIdentityFactory(new ConfigurationCacheableIdFactory()), new TaskFactory(project, Mock(InstantiationScheme)), project).create(name, DefaultTask)
 
         then:
         def exception = thrown(InvalidUserDataException)
diff --git a/subprojects/core/src/test/java/org/gradle/internal/classpath/BasicCallInterceptionTestInterceptorsDeclaration.java b/subprojects/core/src/test/java/org/gradle/internal/classpath/BasicCallInterceptionTestInterceptorsDeclaration.java
new file mode 100644
index 0000000..efbf84f
--- /dev/null
+++ b/subprojects/core/src/test/java/org/gradle/internal/classpath/BasicCallInterceptionTestInterceptorsDeclaration.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import org.gradle.internal.instrumentation.api.annotations.CallableKind;
+import org.gradle.internal.instrumentation.api.annotations.InterceptCalls;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind;
+import org.gradle.internal.instrumentation.api.annotations.SpecificGroovyCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.SpecificJvmCallInterceptors;
+
+@SuppressWarnings("NewMethodNamingConvention")
+@SpecificJvmCallInterceptors(generatedClassName = BasicCallInterceptionTestInterceptorsDeclaration.JVM_BYTECODE_GENERATED_CLASS)
+@SpecificGroovyCallInterceptors(generatedClassName = BasicCallInterceptionTestInterceptorsDeclaration.GROOVY_GENERATED_CLASS)
+public class BasicCallInterceptionTestInterceptorsDeclaration {
+    public static final String JVM_BYTECODE_GENERATED_CLASS = "org.gradle.internal.classpath.Test_interceptors_jvmbytecode_generated";
+    public static final String GROOVY_GENERATED_CLASS = "org.gradle.internal.classpath.Test_interceptors_groovy_generated";
+
+    @InterceptCalls
+    @CallableKind.InstanceMethod
+    public static void intercept_call(
+        @ParameterKind.Receiver InterceptorTestReceiver self,
+        @ParameterKind.CallerClassName String consumer
+    ) {
+        self.intercepted = "call()";
+        self.call();
+    }
+
+    @InterceptCalls
+    @CallableKind.InstanceMethod
+    public static void intercept_call(
+        @ParameterKind.Receiver InterceptorTestReceiver self,
+        InterceptorTestReceiver arg0,
+        @ParameterKind.CallerClassName String consumer
+    ) {
+        self.intercepted = "call(InterceptorTestReceiver)";
+        self.call(arg0);
+    }
+
+    @InterceptCalls
+    @CallableKind.InstanceMethod
+    public static void intercept_callVararg(
+        @ParameterKind.Receiver InterceptorTestReceiver self,
+        @ParameterKind.VarargParameter Object[] arg,
+        @ParameterKind.CallerClassName String consumer
+    ) {
+        self.intercepted = "callVararg(Object...)";
+        self.callVararg(arg);
+    }
+}
diff --git a/subprojects/core/src/test/java/org/gradle/internal/classpath/InterceptorTestReceiver.java b/subprojects/core/src/test/java/org/gradle/internal/classpath/InterceptorTestReceiver.java
new file mode 100644
index 0000000..e444226
--- /dev/null
+++ b/subprojects/core/src/test/java/org/gradle/internal/classpath/InterceptorTestReceiver.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.classpath;
+
+public class InterceptorTestReceiver {
+    public void call() {}
+    public void call(InterceptorTestReceiver arg) {}
+    public void callVararg(Object... arg) {}
+
+    public String intercepted = null;
+}
diff --git a/subprojects/core/src/test/resources/org/gradle/testfixtures/ProjectBuilderTest.gradle b/subprojects/core/src/test/resources/org/gradle/testfixtures/ProjectBuilderTest.gradle
index 5b89c33..f8c65f8 100644
--- a/subprojects/core/src/test/resources/org/gradle/testfixtures/ProjectBuilderTest.gradle
+++ b/subprojects/core/src/test/resources/org/gradle/testfixtures/ProjectBuilderTest.gradle
@@ -1 +1 @@
-task hello { doLast { println 'hello' } }
\ No newline at end of file
+task hello { doLast { println 'hello' } }
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/RecordingAntBuildListener.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/api/RecordingAntBuildListener.groovy
index dbd797c..b3cdefc 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/RecordingAntBuildListener.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/RecordingAntBuildListener.groovy
@@ -57,4 +57,4 @@
         messageLogged << event
     }
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/catalog/problems/VersionCatalogErrorMessages.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/catalog/problems/VersionCatalogErrorMessages.groovy
index d7852d6..181e658 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/catalog/problems/VersionCatalogErrorMessages.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/catalog/problems/VersionCatalogErrorMessages.groovy
@@ -20,6 +20,7 @@
 import org.gradle.api.internal.DocumentationRegistry
 
 import static org.gradle.problems.internal.RenderingUtils.oxfordListOf
+import static org.gradle.util.internal.TextUtil.getPluralEnding
 import static org.gradle.util.internal.TextUtil.normaliseLineSeparators
 
 @CompileStatic
@@ -125,7 +126,7 @@
         }
 
         String getDocumentation() {
-            "Please refer to ${doc.getDocumentationFor("version_catalog_problems", section)} for more details about this problem."
+            doc.getDocumentationRecommendationFor("information","version_catalog_problems", section)
         }
 
         abstract String build()
@@ -147,7 +148,7 @@
 
         @Override
         String build() {
-            """${intro}  - Problem: In version catalog $catalog, parsing failed with ${errors.size()} error${errors.size() > 1 ? "s" : ""}.
+            """${intro}  - Problem: In version catalog $catalog, parsing failed with ${errors.size()} error${getPluralEnding(errors)}.
 
     Reason: ${errors.join('\n    ')}.
 
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/project/taskfactory/TestTaskIdentities.java b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/project/taskfactory/TestTaskIdentities.java
new file mode 100644
index 0000000..3f3549e
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/internal/project/taskfactory/TestTaskIdentities.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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.api.internal.project.taskfactory;
+
+import org.gradle.api.Task;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.id.ConfigurationCacheableIdFactory;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A test fixture to create {@link TaskIdentityFactory}'es or {@link TaskIdentity}'es directly.
+ * <p>
+ * It exists mainly because {@code TaskIdentity} is a final class and cannot be mocked.
+ */
+public class TestTaskIdentities {
+
+    private static final TaskIdentityFactory DEFAULT_FACTORY = factory();
+
+    public static <T extends Task> TaskIdentity<T> create(String name, Class<T> type, ProjectInternal project) {
+        return DEFAULT_FACTORY.create(name, type, project);
+    }
+
+    public static TaskIdentityFactory factory() {
+        AtomicLong idCounter = new AtomicLong(0);
+        ConfigurationCacheableIdFactory idFactory = new ConfigurationCacheableIdFactory() {
+            @Override
+            public long createId() {
+                return idCounter.incrementAndGet();
+            }
+
+            @Override
+            public void idRecreated() {
+            }
+        };
+        return new TaskIdentityFactory(idFactory);
+    }
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractSpockTaskTest.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractSpockTaskTest.groovy
index 6919aba..ca7a12e 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractSpockTaskTest.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/api/tasks/AbstractSpockTaskTest.groovy
@@ -26,9 +26,11 @@
 import org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory
 import org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore
 import org.gradle.api.internal.project.taskfactory.TaskFactory
+import org.gradle.api.internal.project.taskfactory.TaskIdentityFactory
 import org.gradle.api.internal.project.taskfactory.TaskInstantiator
 import org.gradle.api.specs.Spec
 import org.gradle.cache.internal.TestCrossBuildInMemoryCacheFactory
+import org.gradle.internal.id.ConfigurationCacheableIdFactory
 import org.gradle.internal.Actions
 import org.gradle.internal.MutableBoolean
 import org.gradle.internal.reflect.DirectInstantiator
@@ -41,6 +43,7 @@
     public static final String TEST_TASK_NAME = "taskname"
 
     def taskClassInfoStore = new DefaultTaskClassInfoStore(new TestCrossBuildInMemoryCacheFactory())
+    def taskIdentityFactory = new TaskIdentityFactory(new ConfigurationCacheableIdFactory())
     def taskFactory = new AnnotationProcessingTaskFactory(DirectInstantiator.INSTANCE, taskClassInfoStore, new TaskFactory())
 
     abstract DefaultTask getTask()
@@ -54,7 +57,7 @@
     }
 
     def <T extends DefaultTask> T createTask(Class<T> type, ProjectInternal project, String name) {
-        Task task = new TaskInstantiator(taskFactory.createChild(project, TestUtil.instantiatorFactory().decoratingLenientScheme), project).create(name, type)
+        Task task = new TaskInstantiator(taskIdentityFactory, taskFactory.createChild(project, TestUtil.instantiatorFactory().decoratingLenientScheme), project).create(name, type)
         assert type.isAssignableFrom(task.getClass())
         return type.cast(task)
     }
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/internal/classpath/GroovyInterceptorsSubstitution.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/internal/classpath/GroovyInterceptorsSubstitution.groovy
new file mode 100644
index 0000000..4534599
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/internal/classpath/GroovyInterceptorsSubstitution.groovy
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 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.classpath
+
+import org.codehaus.groovy.runtime.callsite.CallSite
+import org.gradle.internal.classpath.intercept.CallInterceptorsSet
+import org.gradle.internal.classpath.intercept.CallSiteDecorator
+
+import javax.annotation.Nullable
+import java.lang.invoke.MethodHandles
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * Provides support for replacing the Groovy interceptors for the current thread.
+ * Make sure that the call sites that should be affected by the change are only reached after the interceptors have been substituted.
+ *
+ * The interceptors are substituted per-thread because other threads may be executing unrelated tests.
+ */
+class GroovyInterceptorsSubstitution {
+    /**
+     * Ensures that the global Groovy call site decorator is replaced with an implementation that maintains call site decorators per thread.
+     * Then sets up the call site decorator for the current thread only.
+     */
+    void setupForCurrentThread() {
+        synchronized (Instrumented.class) {
+            maybeSetGlobalCallSiteDecorator()
+                .substituteForCurrentThread(
+                    new CallInterceptorsSet(GroovyCallInterceptorsProvisionTools.getInterceptorsFromProvider(substitutionProvider).stream())
+                )
+        }
+    }
+
+    /**
+     * Cancels the Groovy call site decorator substitution for the current thread.
+     * If the global implementation does not have any other active callsite decorators in the other threads, reverts the global call site
+     * decorator implementation.
+     */
+    static void cleanupForCurrentThread() {
+        synchronized (Instrumented.class) {
+            substitutionIfPresent()?.cancelSubstitutionForCurrentThread()
+            maybeRevertGlobalCallSiteDecorator()
+        }
+    }
+
+    private final GroovyCallInterceptorsProvider substitutionProvider
+
+    GroovyInterceptorsSubstitution(GroovyCallInterceptorsProvider substitutionProvider) {
+        this.substitutionProvider = substitutionProvider
+    }
+
+    @Nullable
+    private static ThreadLocalCallSiteDecoratorSubstitution substitutionIfPresent() {
+        def decorator = Instrumented.GroovyCallInterceptorInternalTesting.currentGroovyCallSiteDecorator
+        if (decorator instanceof ThreadLocalCallSiteDecoratorSubstitution) {
+            return decorator as ThreadLocalCallSiteDecoratorSubstitution
+        }
+        return null
+    }
+
+    private static ThreadLocalCallSiteDecoratorSubstitution maybeSetGlobalCallSiteDecorator() {
+        def decorator = Instrumented.GroovyCallInterceptorInternalTesting.currentGroovyCallSiteDecorator
+        if (decorator !instanceof ThreadLocalCallSiteDecoratorSubstitution) {
+            decorator = new ThreadLocalCallSiteDecoratorSubstitution(decorator)
+            Instrumented.GroovyCallInterceptorInternalTesting.currentGroovyCallSiteDecorator = decorator
+        }
+        return decorator as ThreadLocalCallSiteDecoratorSubstitution
+    }
+
+    private static void maybeRevertGlobalCallSiteDecorator() {
+        def decorator = Instrumented.GroovyCallInterceptorInternalTesting.currentGroovyCallSiteDecorator
+        if (decorator instanceof ThreadLocalCallSiteDecoratorSubstitution) {
+            if (decorator.isEmpty()) {
+                Instrumented.GroovyCallInterceptorInternalTesting.currentGroovyCallSiteDecorator = decorator.original
+            }
+        }
+    }
+
+    private static class ThreadLocalCallSiteDecoratorSubstitution implements CallSiteDecorator {
+
+        public final CallSiteDecorator original
+
+        private final AtomicInteger substitutions = new AtomicInteger(0)
+        private final ThreadLocal<CallSiteDecorator> threadLocalDecorators = ThreadLocal.withInitial { original }
+
+        boolean isEmpty() {
+            substitutions.get() == 0
+        }
+
+        ThreadLocalCallSiteDecoratorSubstitution(CallSiteDecorator original) {
+            this.original = original
+        }
+
+        void substituteForCurrentThread(CallSiteDecorator decorator) {
+            if (threadLocalDecorators.get() != original) {
+                throw new IllegalStateException("already substituted for the current thread; proper cleanup might have been missed")
+            }
+            substitutions.incrementAndGet()
+            threadLocalDecorators.set(decorator)
+        }
+
+        void cancelSubstitutionForCurrentThread() {
+            if (threadLocalDecorators.get() == original) {
+                throw new IllegalStateException("there was no substitution for the current thread")
+            }
+            substitutions.decrementAndGet()
+            threadLocalDecorators.remove()
+        }
+
+        @Override
+        CallSite maybeDecorateGroovyCallSite(CallSite originalCallSite) {
+            threadLocalDecorators.get().maybeDecorateGroovyCallSite(originalCallSite)
+        }
+
+        @Override
+        java.lang.invoke.CallSite maybeDecorateIndyCallSite(java.lang.invoke.CallSite originalCallSite, MethodHandles.Lookup caller, String callType, String name, int flags) {
+            threadLocalDecorators.get().maybeDecorateIndyCallSite(originalCallSite, caller, callType, name, flags)
+        }
+    }
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/internal/classpath/InstrumentedClasses.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/internal/classpath/InstrumentedClasses.groovy
new file mode 100644
index 0000000..06374c6
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/internal/classpath/InstrumentedClasses.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 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.classpath
+
+
+import java.util.function.Predicate
+
+class InstrumentedClasses {
+
+    private final Predicate<String> shouldInstrumentClassByName
+
+    private final TestInstrumentedClassLoader loader
+
+    InstrumentedClasses(
+        ClassLoader source,
+        Predicate<String> shouldInstrumentClassByName,
+        JvmBytecodeInterceptorSet interceptors
+    ) {
+        this.shouldInstrumentClassByName = shouldInstrumentClassByName
+        loader = new TestInstrumentedClassLoader(
+            source,
+            shouldInstrumentClassByName,
+            new InstrumentingTransformer(interceptors)
+        )
+    }
+
+    static Predicate<String> nestedClassesOf(Class<?> theClass) {
+        return { className -> className.startsWith(theClass.name + "\$")}
+    }
+
+    Class<?> instrumentedClass(Class<?> originalClass) {
+        if (!shouldInstrumentClassByName.test(originalClass.name)) {
+            throw new IllegalArgumentException(originalClass.name + " is not instrumented")
+        }
+        loader.loadClass(originalClass.name)
+    }
+
+    Closure<?> instrumentedClosure(Closure<?> originalClosure) {
+        def capturedParams = originalClosure.class.declaredConstructors[0].parameters.drop(2)
+        if (capturedParams.size() != 0) {
+            // TODO support captured args in some way?
+            throw new IllegalArgumentException("closures with captured arguments are not supported yet; please use the arguments and return value")
+        }
+        instrumentedClass(originalClosure.class).getDeclaredConstructor(Object, Object).newInstance(originalClosure.thisObject, originalClosure.owner) as Closure<?>
+    }
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/internal/progress/NoOpProgressLoggerFactory.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/internal/progress/NoOpProgressLoggerFactory.groovy
index 90b96f3..8204edf 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/internal/progress/NoOpProgressLoggerFactory.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/internal/progress/NoOpProgressLoggerFactory.groovy
@@ -22,11 +22,11 @@
 
 class NoOpProgressLoggerFactory implements ProgressLoggerFactory {
     ProgressLogger newOperation(String loggerCategory) {
-        throw new UnsupportedOperationException()
+        return new Logger()
     }
 
     ProgressLogger newOperation(Class<?> loggerCategory) {
-        throw new UnsupportedOperationException()
+        return new Logger()
     }
 
     ProgressLogger newOperation(Class<?> loggerCategory, BuildOperationDescriptor buildOperationDescriptor) {
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/internal/progress/RecordingProgressLoggerFactory.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/internal/progress/RecordingProgressLoggerFactory.groovy
new file mode 100644
index 0000000..ba4d045
--- /dev/null
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/internal/progress/RecordingProgressLoggerFactory.groovy
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 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.progress
+
+import org.gradle.internal.logging.progress.ProgressLogger
+import org.gradle.internal.logging.progress.ProgressLoggerFactory
+import org.gradle.internal.operations.BuildOperationDescriptor
+
+class RecordingProgressLoggerFactory implements ProgressLoggerFactory {
+    private List<String> messages = []
+
+    ProgressLogger newOperation(String loggerCategory) {
+        return new Logger(messages)
+    }
+
+    ProgressLogger newOperation(Class<?> loggerCategory) {
+        return new Logger(messages)
+    }
+
+    ProgressLogger newOperation(Class<?> loggerCategory, BuildOperationDescriptor buildOperationDescriptor) {
+        return new Logger(messages)
+    }
+
+    ProgressLogger newOperation(Class<?> loggerClass, ProgressLogger parent) {
+        return new Logger(messages)
+    }
+
+    List<String> getRecordedMessages() {
+        return messages
+    }
+
+    class Logger implements ProgressLogger {
+        private List<String> messages
+
+        String description
+
+        Logger(List<String> messages) {
+            this.messages = messages
+        }
+
+        String getDescription() { description }
+
+        ProgressLogger setDescription(String description) {
+            this.description = description
+            this
+        }
+
+        ProgressLogger start(String description, String status) {
+            setDescription(description)
+            started()
+            this
+        }
+
+        void started() {}
+
+        void started(String status) {
+            messages.add(status)
+        }
+
+        void progress(String status) {
+            messages.add(status)
+        }
+
+        void progress(String status, boolean failing) {
+            messages.add(status)
+        }
+
+        void completed() {}
+
+        void completed(String status, boolean failed) {
+            messages.add(status)
+        }
+    }
+}
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/test/fixtures/AbstractProjectBuilderSpec.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/test/fixtures/AbstractProjectBuilderSpec.groovy
index 6ffc163..fc6b9df 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/test/fixtures/AbstractProjectBuilderSpec.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/test/fixtures/AbstractProjectBuilderSpec.groovy
@@ -26,6 +26,7 @@
 import org.gradle.api.internal.tasks.execution.DefaultTaskExecutionContext
 import org.gradle.api.internal.tasks.properties.DefaultTaskProperties
 import org.gradle.execution.ProjectExecutionServices
+import org.gradle.execution.plan.LocalTaskNode
 import org.gradle.internal.execution.BuildOutputCleanupRegistry
 import org.gradle.internal.execution.WorkValidationContext
 import org.gradle.internal.execution.impl.DefaultWorkValidationContext
@@ -85,10 +86,11 @@
     }
 
     void execute(Task task) {
+        def workValidationContext = new DefaultWorkValidationContext(documentationRegistry, WorkValidationContext.TypeOriginInspector.NO_OP)
         def taskExecutionContext = new DefaultTaskExecutionContext(
-            null,
+            new LocalTaskNode(task as TaskInternal, workValidationContext, { null }),
             DefaultTaskProperties.resolve(executionServices.get(PropertyWalker), executionServices.get(FileCollectionFactory), task as TaskInternal),
-            new DefaultWorkValidationContext(documentationRegistry, WorkValidationContext.TypeOriginInspector.NO_OP),
+            workValidationContext,
             { context -> }
         )
         project.gradle.services.get(BuildOutputCleanupRegistry).resolveOutputs()
diff --git a/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestTask.groovy b/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestTask.groovy
index 1c799d5..3f90aa5 100644
--- a/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestTask.groovy
+++ b/subprojects/core/src/testFixtures/groovy/org/gradle/util/TestTask.groovy
@@ -30,4 +30,4 @@
     List list2
     @Internal
     Map map1
-}
\ No newline at end of file
+}
diff --git a/subprojects/core/src/testFixtures/java/org/gradle/internal/classpath/TestInstrumentedClassLoader.java b/subprojects/core/src/testFixtures/java/org/gradle/internal/classpath/TestInstrumentedClassLoader.java
new file mode 100644
index 0000000..a1b3a90
--- /dev/null
+++ b/subprojects/core/src/testFixtures/java/org/gradle/internal/classpath/TestInstrumentedClassLoader.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 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.classpath;
+
+import org.gradle.api.file.RelativePath;
+import org.gradle.internal.Pair;
+import org.gradle.internal.classloader.TransformingClassLoader;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.function.Predicate;
+
+public class TestInstrumentedClassLoader extends TransformingClassLoader {
+    private final CachedClasspathTransformer.Transform transform;
+    private final Predicate<String> shouldLoadTransformedClass;
+    private final ClassLoader source;
+
+    TestInstrumentedClassLoader(
+        ClassLoader source,
+        Predicate<String> shouldLoadTransformedClass,
+        CachedClasspathTransformer.Transform transform
+    ) {
+        super("test-transformed-loader", source, Collections.emptyList());
+        this.shouldLoadTransformedClass = shouldLoadTransformedClass;
+        this.transform = transform;
+        this.source = source;
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (shouldLoadTransformedClass.test(name)) {
+            Class<?> result = findLoadedClass(name);
+            if (result == null) {
+                result = findClass(name);
+            }
+            if (resolve) {
+                resolveClass(result);
+            }
+            return result;
+        }
+        return super.loadClass(name, resolve);
+    }
+
+    @Override
+    public URL findResource(String name) {
+        return source.getResource(name);
+    }
+
+    @Override
+    protected @Nonnull byte[] transform(String className, @Nonnull byte[] bytes) {
+        String path = className.replace(".", "/") + ".class";
+        ClasspathEntryVisitor.Entry classEntry = new ClasspathEntryVisitor.Entry() {
+            @Override
+            public String getName() {
+                return className;
+            }
+
+            @Override
+            public RelativePath getPath() {
+                return RelativePath.parse(true, path);
+            }
+
+            @Override
+            public ClasspathEntryVisitor.Entry.CompressionMethod getCompressionMethod() {
+                return ClasspathEntryVisitor.Entry.CompressionMethod.STORED;
+            }
+
+            @Override
+            public byte[] getContent() {
+                return bytes;
+            }
+        };
+        ClassReader originalReader = new ClassReader(bytes);
+        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+        Pair<RelativePath, ClassVisitor> pathAndVisitor;
+        try {
+            pathAndVisitor = transform.apply(classEntry, writer, new ClassData(originalReader));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        originalReader.accept(pathAndVisitor.right(), ClassReader.EXPAND_FRAMES);
+
+        return writer.toByteArray();
+    }
+}
diff --git a/subprojects/dependency-management/build.gradle.kts b/subprojects/dependency-management/build.gradle.kts
index 6384bd4..39f8861 100644
--- a/subprojects/dependency-management/build.gradle.kts
+++ b/subprojects/dependency-management/build.gradle.kts
@@ -46,6 +46,7 @@
     implementation(libs.ant)
     implementation(libs.ivy)
     implementation(libs.maven3SettingsBuilder)
+    implementation(libs.fastutil)
 
     testImplementation(project(":process-services"))
     testImplementation(project(":diagnostics"))
@@ -131,8 +132,3 @@
         }.visit { this.file.setWritable(true) }
     }
 }
-
-// Remove as part of fixing https://github.com/gradle/configuration-cache/issues/585
-tasks.configCacheIntegTest {
-    systemProperties["org.gradle.configuration-cache.internal.test-disable-load-after-store"] = "true"
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/AbstractVersionRangeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/AbstractVersionRangeResolveIntegrationTest.groovy
deleted file mode 100644
index f22fda1..0000000
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/AbstractVersionRangeResolveIntegrationTest.groovy
+++ /dev/null
@@ -1,148 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
-import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
-import spock.lang.IgnoreIf
-
-/**
- * A comprehensive test of dependency resolution of a single module version, given a set of input selectors.
- * This integration test validates all scenarios in {@link VersionRangeResolveTestScenarios}, as well as some adhoc scenarios.
- */
-@IgnoreIf({
-    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
-    // infrastructure to simulate everything done here, so we're only going to execute this test in
-    // embedded mode
-    !GradleContextualExecuter.embedded
-})
-abstract class AbstractVersionRangeResolveIntegrationTest extends AbstractDependencyResolutionTest {
-
-    def baseBuild
-    def baseSettings
-    def resolve = new ResolveTestFixture(buildFile, "conf").expectDefaultConfiguration("runtime")
-
-    def setup() {
-        (9..13).each {
-            mavenRepo.module("org", "foo", "${it}").publish()
-        }
-
-        settingsFile << "rootProject.name = 'test'"
-        buildFile << """
-            repositories {
-                maven { url '${mavenRepo.uri}' }
-            }
-            configurations {
-                conf
-            }
-"""
-        resolve.prepare()
-        baseBuild = buildFile.text
-        baseSettings = settingsFile.text
-    }
-
-    void checkScenarioResolution(String expectedSingle, String expectedMulti, VersionRangeResolveTestScenarios.RenderableVersion... versions) {
-        checkScenarioResolution(expectedSingle, expectedMulti, versions as List)
-    }
-
-    void checkScenarioResolution(String expectedSingle, String expectedMulti, List<VersionRangeResolveTestScenarios.RenderableVersion> versions) {
-        settingsFile.text = baseSettings
-
-        def singleProjectConfs = []
-        def singleProjectDeps = []
-        versions.eachWithIndex { VersionRangeResolveTestScenarios.RenderableVersion version, int i ->
-            singleProjectConfs << "single${i}"
-            singleProjectDeps << "single${i} " + version.render()
-        }
-
-        buildFile.text = baseBuild + """
-            allprojects {
-                configurations { conf }
-            }
-
-            configurations {
-                ${singleProjectConfs.join('\n')}
-                single {
-                    extendsFrom(${singleProjectConfs.join(',')})
-                }
-            }
-
-            dependencies {
-                conf 'org:foo'
-                conf project(path: ':p1', configuration: 'conf')
-                ${singleProjectDeps.join('\n')}
-            }
-
-            task resolveMultiProject(type: Sync) {
-                from configurations.conf
-                into 'libs-multi'
-            }
-
-            task resolveSingleProject(type: Sync) {
-                from configurations.single
-                into 'libs-single'
-            }
-"""
-        for (int i = 1; i <= versions.size(); i++) {
-            VersionRangeResolveTestScenarios.RenderableVersion version = versions.get(i - 1);
-            def nextProjectDependency = i < versions.size() ? "conf project(path: ':p${i + 1}', configuration: 'conf')" : ""
-            buildFile << """
-                project('p${i}') {
-                    dependencies {
-                        conf ${version.render()}
-                        ${nextProjectDependency}
-                    }
-                }
-"""
-            settingsFile << """
-                include ':p${i}'
-"""
-        }
-
-        boolean expectFailureSingle = expectedSingle == VersionRangeResolveTestScenarios.REJECTED || expectedSingle == VersionRangeResolveTestScenarios.FAILED
-        boolean expectFailureMulti = expectedMulti == VersionRangeResolveTestScenarios.REJECTED || expectedMulti == VersionRangeResolveTestScenarios.FAILED
-        if (expectFailureMulti) {
-            fails 'resolveMultiProject'
-        }
-        if (expectFailureSingle) {
-            fails 'resolveSingleProject'
-        }
-
-        if (!expectFailureMulti) {
-            run 'resolveMultiProject'
-            def multiProjectResolve = file('libs-multi').list() as List
-            assert parseResolvedVersion(multiProjectResolve) == expectedMulti
-        }
-
-        if (!expectFailureSingle) {
-            run 'resolveSingleProject'
-            def singleProjectResolve = file('libs-single').list() as List
-            assert parseResolvedVersion(singleProjectResolve) == expectedSingle
-        }
-    }
-
-    def parseResolvedVersion(resolvedFiles) {
-        assert resolvedFiles.size() == 1
-        def resolvedFile = resolvedFiles.get(0)
-        assert resolvedFile.startsWith('foo-')
-        assert resolvedFile.endsWith('.jar')
-        def resolvedVersion = (resolvedFile =~ /\d+/).getAt(0)
-        resolvedVersion
-    }
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
index 0d78c09..1aca066 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest.groovy
@@ -15,11 +15,11 @@
  */
 package org.gradle.integtests.resolve
 
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
 import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
-import org.gradle.test.fixtures.file.TestFile
+import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 import org.junit.Rule
 import spock.lang.Issue
 
@@ -30,33 +30,146 @@
  * These tests should be migrated to live with the rest of the coverage over time.
  */
 @FluidDependenciesResolveTest
-class ArtifactDependenciesIntegrationTest extends AbstractIntegrationSpec {
+class ArtifactDependenciesIntegrationTest extends AbstractDependencyResolutionTest {
+    public final resolve = new ResolveTestFixture(buildFile)
     @Rule
     public final TestResources testResources = new TestResources(testDirectoryProvider)
 
-    def setup() {
-        executer.requireOwnGradleUserHomeDir()
-    }
-
     void canHaveConfigurationHierarchy() {
-        expect:
-        executer.run()
+        given:
+        resolve.prepare {
+            config("compile")
+            config("runtime")
+        }
+
+        when:
+        run("checkCompile")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("test:projectA:1.2") {
+                    configuration("api")
+                    module("test:projectB:1.5") {
+                        artifact(name: "projectB-api")
+                    }
+                }
+            }
+        }
+
+        when:
+        run("checkRuntime")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("test:projectA:1.2") {
+                    configuration("api")
+                    configuration("default")
+                    module("test:projectB:1.5") {
+                        configuration("extraRuntime")
+                        artifact()
+                        artifact(name: "projectB-api")
+                        artifact(name: "projectB-extraRuntime")
+                    }
+                    module("test:projectB:1.5")
+                }
+                module("test:projectA:1.2")
+                module("test:projectB:1.5")
+            }
+        }
     }
 
     void dependencyReportWithConflicts() {
-        expect:
-        executer.run()
-        executer.withDependencyList().run()
+        given:
+        resolve.prepare {
+            config("evictedTransitive")
+            config("evictedDirect")
+            config("multiProject")
+        }
+
+        when:
+        run(":checkEvictedTransitive")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("test:projectA:1.2") {
+                    edge("test:projectB:1.5", "test:projectB:2.1.5")
+                }
+                module("test:projectB:2.1.5") {
+                    byConflictResolution("between versions 2.1.5 and 1.5")
+                }
+            }
+        }
+
+        when:
+        run(":checkEvictedDirect")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("test:projectA:2.0") {
+                    module("test:projectB:2.1.5") {
+                        byConflictResolution("between versions 2.1.5 and 1.5")
+                    }
+                }
+                edge("test:projectB:1.5", "test:projectB:2.1.5")
+            }
+        }
+
+        when:
+        run(":checkMultiProject")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("test:projectA:1.2", "test:projectA:2.0") {
+                    byConflictResolution("between versions 2.0 and 1.2")
+                    module("test:projectB:2.1.5")
+                }
+                project(":subproject", "test:subproject:") {
+                    noArtifacts()
+                    module("test:projectA:2.0")
+                }
+            }
+        }
     }
 
-    void canHaveCycleInDependencyGraph() throws IOException {
-        expect:
-        executer.run()
+    void canHaveCycleInDependencyGraph() {
+        given:
+        resolve.prepare("compile")
+
+        when:
+        run(":checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("test:projectA:1.2") {
+                    module("test:projectB:1.5") {
+                        module("test:projectA:1.2")
+                    }
+                }
+            }
+        }
     }
 
-    void canUseDynamicVersions() throws IOException {
-        expect:
-        executer.run()
+    void canUseDynamicVersions() {
+        given:
+        resolve.prepare("compile")
+
+        when:
+        run(":checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("test:projectA:1.+", "test:projectA:1.2") {
+                    edge("test:projectB:latest.release", "test:projectB:1.5")
+                }
+            }
+        }
     }
 
     void resolutionFailsWhenProjectHasNoRepositoriesEvenWhenArtifactIsCachedLocally() {
@@ -67,7 +180,10 @@
     configurations {
         compile
     }
-    task listDeps { doLast { configurations.compile.each { } } }
+    task listDeps {
+        def files = configurations.compile
+        doLast { files.files }
+    }
 }
 project(':a') {
     repositories {
@@ -85,9 +201,9 @@
 """
         repo.module('org.gradle.test', 'external1', '1.0').publish()
 
-        inTestDirectory().withTasks('a:listDeps').run()
-        def result = inTestDirectory().withTasks('b:listDeps').runWithFailure()
-        result.assertHasCause('Cannot resolve external dependency org.gradle.test:external1:1.0 because no repositories are defined.')
+        succeeds('a:listDeps')
+        fails('b:listDeps')
+        failure.assertHasCause('Cannot resolve external dependency org.gradle.test:external1:1.0 because no repositories are defined.')
     }
 
     void resolutionFailsForMissingArtifact() {
@@ -104,27 +220,36 @@
     missingExt "org.gradle.test:lib:1.0@zip"
     missingClassifier "org.gradle.test:lib:1.0:classifier1"
 }
-task listJar { doLast { configurations.compile.each { } } }
-task listMissingExt { doLast { configurations.missingExt.each { } } }
-task listMissingClassifier { doLast { configurations.missingClassifier.each { } } }
+task listJar {
+    def files = configurations.compile
+    doLast { files.each { } }
+}
+task listMissingExt {
+    def files = configurations.missingExt
+    doLast { files.each { } }
+}
+task listMissingClassifier {
+    def files = configurations.missingClassifier
+    doLast { files.each { } }
+}
 """
 
         def module = repo.module('org.gradle.test', 'lib', '1.0')
         module.publish()
 
-        inTestDirectory().withTasks('listJar').run()
+        succeeds('listJar')
 
-        def result = inTestDirectory().withTasks('listMissingExt').runWithFailure()
+        fails('listMissingExt')
 
-        result.assertHasCause("""Could not find lib-1.0.zip (org.gradle.test:lib:1.0).
+        failure.assertHasCause("""Could not find lib-1.0.zip (org.gradle.test:lib:1.0).
 Searched in the following locations:
     ${module.artifactFile(type: 'zip').toURL()}""")
 
         when:
-        result = inTestDirectory().withTasks('listMissingClassifier').runWithFailure()
+        fails('listMissingClassifier')
 
         then:
-        result.assertHasCause("""Could not find lib-1.0-classifier1.jar (org.gradle.test:lib:1.0).
+        failure.assertHasCause("""Could not find lib-1.0-classifier1.jar (org.gradle.test:lib:1.0).
 Searched in the following locations:
     ${module.artifactFile(classifier: 'classifier1').toURL()}""")
     }
@@ -142,7 +267,10 @@
     configurations {
         compile
     }
-    task listDeps { doLast { configurations.compile.each { } } }
+    task listDeps {
+        def files = configurations.compile
+        doLast { files.each { } }
+    }
 }
 project(':a') {
     repositories {
@@ -162,9 +290,9 @@
 }
 """
 
-        inTestDirectory().withTasks('a:listDeps').run()
-        def result = inTestDirectory().withTasks('b:listDeps').runWithFailure()
-        result.assertThatCause(containsString('Could not find org.gradle.test:external1:1.0.'))
+        succeeds('a:listDeps')
+        fails('b:listDeps')
+        failure.assertThatCause(containsString('Could not find org.gradle.test:external1:1.0.'))
     }
 
     void artifactFilesPreserveFixedOrder() {
@@ -190,15 +318,17 @@
                 compile "org:middle2:1.0", "org:middle1:1.0"
             }
             task test {
+                def files = configurations.compile
                 doLast {
-                    assert configurations.compile.files.collect { it.name } == ['middle2-1.0.jar', 'middle1-1.0.jar', 'leaf3-1.0.jar', 'leaf4-1.0.jar', 'leaf1-1.0.jar', 'leaf2-1.0.jar']
+                    assert files.collect { it.name } == ['middle2-1.0.jar', 'middle1-1.0.jar', 'leaf3-1.0.jar', 'leaf4-1.0.jar', 'leaf1-1.0.jar', 'leaf2-1.0.jar']
                 }
             }
         """
 
-        executer.withTasks("test").run()
+        succeeds("test")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     void exposesMetaDataAboutResolvedArtifactsInAFixedOrder() {
         expect:
         def module = repo.module('org.gradle.test', 'lib', '1.0')
@@ -245,18 +375,21 @@
 }
 """
 
-        inTestDirectory().withTasks('test').run()
+        succeeds('test')
     }
 
     @Issue("GRADLE-1567")
     void resolutionDifferentiatesBetweenArtifactsThatDifferOnlyInClassifier() {
         expect:
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.artifact(classifier: 'classifier1')
-        module.artifact(classifier: 'classifier2')
-        module.publish()
+        def lib = repo.module('org.gradle.test', 'external1', '1.0')
+        lib.artifact(classifier: 'classifier1')
+        lib.artifact(classifier: 'classifier2')
+        lib.publish()
 
-        file('settings.gradle') << 'include "a", "b", "c"'
+        file('settings.gradle') << """
+            rootProject.name = "test"
+            include "a", "b", "c"
+        """
         file('build.gradle') << """
 subprojects {
     repositories {
@@ -289,9 +422,31 @@
     }
 }
 """
+        resolve.prepare("compile")
 
-        inTestDirectory().withTasks('a:test').run()
-        inTestDirectory().withTasks('b:test').run()
+        when:
+        succeeds("a:checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":a", "test:a:") {
+                module("org.gradle.test:external1:1.0") {
+                    artifact(classifier: "classifier1")
+                }
+            }
+        }
+
+        when:
+        succeeds("b:checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":b", "test:b:") {
+                module("org.gradle.test:external1:1.0") {
+                    artifact(classifier: "classifier2")
+                }
+            }
+        }
     }
 
     @Issue("GRADLE-739")
@@ -330,23 +485,26 @@
     rawExtended 'org.gradle.test:external1:1.0:extendedClassifier'
 }
 
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
 task test {
+    def base = configurations.base
+    def extendedWithOther = configurations.extendedWithOther
+    def extendedWithClassifier = configurations.extendedWithClassifier
+    def justDefault = configurations.justDefault
+    def justClassifier = configurations.justClassifier
+    def rawBase = configurations.rawBase
+    def rawExtended = configurations.rawExtended
     doLast {
-        checkDeps configurations.base, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar']
-        checkDeps configurations.extendedWithOther, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar', 'other-1.0.jar']
-        checkDeps configurations.extendedWithClassifier, ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
-        checkDeps configurations.justDefault, ['external1-1.0.jar']
-        checkDeps configurations.justClassifier, ['external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
-        checkDeps configurations.rawBase, ['external1-1.0.jar']
-        checkDeps configurations.rawExtended, ['external1-1.0.jar', 'external1-1.0-extendedClassifier.jar']
+        assert base*.name == ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar']
+        assert extendedWithOther*.name == ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar', 'other-1.0.jar']
+        assert extendedWithClassifier*.name == ['external1-1.0.jar', 'external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
+        assert justDefault*.name == ['external1-1.0.jar']
+        assert justClassifier*.name == ['external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
+        assert rawBase*.name == ['external1-1.0.jar']
+        assert rawExtended*.name == ['external1-1.0.jar', 'external1-1.0-extendedClassifier.jar']
     }
 }
 """
-        inTestDirectory().withTasks('test').run()
+        succeeds('test')
     }
 
     @Issue("GRADLE-739")
@@ -378,20 +536,20 @@
     extendedWithType 'org.gradle.test:external1:1.0@txt'
 }
 
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
 task test {
+    def base = configurations.base
+    def extended = configurations.extended
+    def extendedWithClassifier = configurations.extendedWithClassifier
+    def extendedWithType = configurations.extendedWithType
     doLast {
-        checkDeps configurations.base, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar']
-        checkDeps configurations.extended, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'other-1.0.jar']
-        checkDeps configurations.extendedWithClassifier, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
-        checkDeps configurations.extendedWithType, ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'external1-1.0.txt']
+        assert base*.name == ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar']
+        assert extended*.name == ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'other-1.0.jar']
+        assert extendedWithClassifier*.name == ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'external1-1.0-extendedClassifier.jar']
+        assert extendedWithType*.name == ['external1-1.0.zip', 'external1-1.0-baseClassifier.jar', 'external1-1.0.txt']
     }
 }
 """
-        inTestDirectory().withTasks('test').run()
+        succeeds('test')
     }
 
     @Issue("GRADLE-739")
@@ -419,30 +577,30 @@
     extended2 'org.gradle.test:external1:1.0:classifier@bin'
 }
 
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
 task test {
+    def base = configurations.base
+    def extended = configurations.extended
+    def extended2 = configurations.extended2
     doLast {
-        checkDeps configurations.base, ['external1-1.0.jar', 'external1-1.0.zip']
-        checkDeps configurations.extended, ['external1-1.0.jar', 'external1-1.0.zip', 'external1-1.0-classifier.jar']
-        checkDeps configurations.extended2, ['external1-1.0.jar', 'external1-1.0.zip', 'external1-1.0-classifier.bin']
+        assert base*.name == ['external1-1.0.jar', 'external1-1.0.zip']
+        assert extended*.name == ['external1-1.0.jar', 'external1-1.0.zip', 'external1-1.0-classifier.jar']
+        assert extended2*.name == ['external1-1.0.jar', 'external1-1.0.zip', 'external1-1.0-classifier.bin']
     }
 }
 """
-        inTestDirectory().withTasks('test').run()
+        succeeds('test')
     }
 
     void nonTransitiveDependenciesAreNotRetrieved() {
         expect:
         repo.module('org.gradle.test', 'one', '1.0').publish()
         repo.module('org.gradle.test', 'two', '1.0').publish()
-        def module = repo.module('org.gradle.test', 'external1', '1.0')
-        module.dependsOn('org.gradle.test', 'one', '1.0')
-        module.artifact(classifier: 'classifier')
-        module.publish()
+        def lib = repo.module('org.gradle.test', 'external1', '1.0')
+        lib.dependsOn('org.gradle.test', 'one', '1.0')
+        lib.artifact(classifier: 'classifier')
+        lib.publish()
 
+        file('settings.gradle') << "rootProject.name = 'test'"
         file('build.gradle') << """
 repositories {
     maven { url '${repo.uri}' }
@@ -461,22 +619,72 @@
     mergedNonTransitive 'org.gradle.test:external1:1.0', {transitive = false }
     mergedNonTransitive 'org.gradle.test:external1:1.0:classifier', { transitive = false }
 }
-
-def checkDeps(config, expectedDependencies) {
-    assert config.collect({ it.name }) as Set == expectedDependencies as Set
-}
-
-task test {
-    doLast {
-        checkDeps configurations.transitive, ['external1-1.0.jar', 'one-1.0.jar']
-        checkDeps configurations.nonTransitive, ['external1-1.0.jar']
-        checkDeps configurations.extendedNonTransitive, ['external1-1.0.jar', 'two-1.0.jar']
-        checkDeps configurations.extendedBoth, ['external1-1.0.jar', 'one-1.0.jar']
-        checkDeps configurations.mergedNonTransitive, ['external1-1.0.jar', 'external1-1.0-classifier.jar']
-    }
-}
 """
-        inTestDirectory().withTasks('test').run()
+        resolve.prepare {
+            config("transitive")
+            config("nonTransitive")
+            config("extendedNonTransitive")
+            config("extendedBoth")
+            config("mergedNonTransitive")
+        }
+
+        when:
+        succeeds("checkTransitive")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.gradle.test:external1:1.0") {
+                    module("org.gradle.test:one:1.0")
+                }
+            }
+        }
+
+        when:
+        succeeds("checkNonTransitive")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.gradle.test:external1:1.0")
+            }
+        }
+
+        when:
+        succeeds("checkExtendedNonTransitive")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.gradle.test:external1:1.0")
+                module("org.gradle.test:two:1.0")
+            }
+        }
+
+        when:
+        succeeds("checkExtendedBoth")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.gradle.test:external1:1.0") {
+                    module("org.gradle.test:one:1.0")
+                }
+            }
+        }
+
+        when:
+        succeeds("checkMergedNonTransitive")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.gradle.test:external1:1.0") {
+                    artifact()
+                    artifact(classifier: "classifier")
+                }
+            }
+        }
     }
 
     void "configuration transitive = false overrides dependency transitive flag"() {
@@ -498,13 +706,14 @@
 }
 
 task test {
+    def files = configurations.override
     doLast {
-        assert configurations.override.collect { it.name } == ['external1-1.0.jar']
+        assert files.collect { it.name } == ['external1-1.0.jar']
     }
 }
 """
 
-        inTestDirectory().withTasks('test').run()
+        succeeds('test')
     }
 
     /*
@@ -535,40 +744,45 @@
 }
 
 task test {
+    def a = configurations.a
+    def b = configurations.b
     doLast {
-        checkDeps configurations.a, ['external1-1.0.jar']
-        checkDeps configurations.b, ['external1-1.0-withClassifier.jar', 'external1-1.0.jar']
+        assert a*.name == ['external1-1.0.jar']
+        assert b*.name == ['external1-1.0.jar', 'external1-1.0-withClassifier.jar']
     }
 }
 """
-        inTestDirectory().withTasks('test').run()
+        succeeds('test')
     }
 
     void projectCanDependOnItself() {
-        expect:
-        TestFile buildFile = file("build.gradle")
-        buildFile << '''
+        given:
+        file("settings.gradle") << "rootProject.name = 'test'"
+        file("build.gradle") << '''
+            group = 'org.test'
+            version = '1.2'
             configurations { compile; create('default') }
             dependencies { compile project(':') }
             task jar1(type: Jar) { destinationDirectory = buildDir; archiveBaseName = '1' }
             task jar2(type: Jar) { destinationDirectory = buildDir; archiveBaseName = '2' }
             artifacts { compile jar1; 'default' jar2 }
-            task listJars {
-                doLast {
-                    assert configurations.compile.collect { it.name } == ['2.jar']
-                }
-            }
 '''
+        resolve.prepare("compile")
 
-        inTestDirectory().withTasks("listJars").run()
+        when:
+        succeeds("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", "org.test:test:1.2") {
+                project(":", "org.test:test:1.2")
+                artifact(name: '2', fileName: '2.jar')
+            }
+        }
     }
 
     def getRepo() {
         return maven(file('repo'))
     }
-
-    private GradleExecuter inTestDirectory() {
-        return inDirectory(testDirectory)
-    }
 }
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy
index a899f48..3547cda 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy
@@ -19,6 +19,7 @@
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
+import org.gradle.integtests.fixtures.resolve.ResolveFailureTestFixture
 
 @FluidDependenciesResolveTest
 class ArtifactSelectionIntegrationTest extends AbstractHttpDependencyResolutionTest {
@@ -129,28 +130,32 @@
                 }
 
                 task resolve {
-                    inputs.files configurations.compile.incoming.artifactView {
-                        attributes { it.attribute(artifactType, 'jar') }
-                    }.files
-                    doLast {
-                        // Get a view specifying the default type
-                        def defaultView = configurations.compile.incoming.artifactView {
-                            attributes {
-                                it.attribute(artifactType, 'jar')
-                            }
+                    // Get a view specifying the default type
+                    def defaultView = configurations.compile.incoming.artifactView {
+                        attributes {
+                            it.attribute(artifactType, 'jar')
                         }
-                        assert defaultView.files.collect { it.name } == ['lib.jar', 'lib-util.jar', 'ui.jar', 'some-jar-1.0.jar']
-                        assert defaultView.artifacts.collect { it.id.displayName }  == ['lib.jar (project :lib)', 'lib-util.jar', 'ui.jar (project :ui)', 'some-jar-1.0.jar (org:test:1.0)']
+                    }
+                    def defaultFiles = defaultView.files
+                    def defaultArtifacts = defaultView.artifacts
 
-                        // Get a view with additional optional attribute
-                        def optionalAttributeView =  configurations.compile.incoming.artifactView {
-                            attributes {
-                                it.attribute(artifactType, 'jar')
-                                it.attribute(otherAttributeOptional, 'anything')
-                            }
+                    // Get a view with additional optional attribute
+                    def optionalAttributeView = configurations.compile.incoming.artifactView {
+                        attributes {
+                            it.attribute(artifactType, 'jar')
+                            it.attribute(otherAttributeOptional, 'anything')
                         }
-                        assert optionalAttributeView.files.collect { it.name } == ['lib.jar', 'lib-util.jar', 'ui.jar', 'some-jar-1.0.jar']
-                        assert optionalAttributeView.artifacts.collect { it.id.displayName }  == ['lib.jar (project :lib)', 'lib-util.jar', 'ui.jar (project :ui)', 'some-jar-1.0.jar (org:test:1.0)']
+                    }
+                    def optionalFiles = optionalAttributeView.files
+                    def optionalArtifacts = optionalAttributeView.artifacts
+
+                    inputs.files defaultFiles
+                    doLast {
+                        assert defaultFiles.collect { it.name } == ['lib.jar', 'lib-util.jar', 'ui.jar', 'some-jar-1.0.jar']
+                        assert defaultArtifacts.collect { it.id.displayName }  == ['lib.jar (project :lib)', 'lib-util.jar', 'ui.jar (project :ui)', 'some-jar-1.0.jar (org:test:1.0)']
+
+                        assert optionalFiles.collect { it.name } == ['lib.jar', 'lib-util.jar', 'ui.jar', 'some-jar-1.0.jar']
+                        assert optionalArtifacts.collect { it.id.displayName }  == ['lib.jar (project :lib)', 'lib-util.jar', 'ui.jar (project :ui)', 'some-jar-1.0.jar (org:test:1.0)']
                     }
                 }
             }
@@ -301,10 +306,10 @@
 
 task show {
     inputs.files configurations.compile
+    def artifacts = configurations.compile.incoming.artifactView {
+        attributes { it.attribute(buildType, 'debug') }
+    }.artifacts
     doLast {
-        def artifacts = configurations.compile.incoming.artifactView {
-            attributes { it.attribute(buildType, 'debug') }
-        }.artifacts
         println "files: " + artifacts.collect { it.file.name }
         println "variants: " + artifacts.collect { it.variant.attributes }
     }
@@ -439,10 +444,10 @@
 
 task show {
     inputs.files configurations.compile
+    def artifacts = configurations.compile.incoming.artifactView {
+        attributes { it.attribute(buildType, 'debug') }
+    }.artifacts
     doLast {
-        def artifacts = configurations.compile.incoming.artifactView {
-            attributes { it.attribute(buildType, 'debug') }
-        }.artifacts
         println "files: " + artifacts.collect { it.file.name }
         println "variants: " + artifacts.collect { it.variant.attributes }
     }
@@ -898,6 +903,8 @@
     }
 
     def "reports failure to match attributes during selection"() {
+        def resolve = new ResolveFailureTestFixture(buildFile)
+
         buildFile << """
             project(':lib') {
                 def attr = Attribute.of('attr', Boolean)
@@ -956,12 +963,13 @@
                 }
 
                 task resolve {
+                    def files = configurations.compile.incoming.artifactView {
+                        attributes {
+                            it.attribute(attr, 'jar')
+                        }
+                    }.files
                     doLast {
-                        configurations.compile.incoming.artifactView {
-                            attributes {
-                                it.attribute(attr, 'jar')
-                            }
-                        }.files.each { println it }
+                        files.each { println it }
                     }
                 }
             }
@@ -969,7 +977,7 @@
 
         expect:
         fails(":app:resolve")
-        failure.assertHasDescription("Execution failed for task ':app:resolve'.")
+        resolve.assertFailurePresent(failure)
         failure.assertHasCause("Could not resolve all files for configuration ':app:compile'.")
         failure.assertHasCause("Could not select a variant of project :lib that matches the consumer attributes.")
         failure.assertHasCause("Unexpected type for attribute 'attr' provided. Expected a value of type java.lang.String but found a value of type java.lang.Boolean.")
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/BeforeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/BeforeResolveIntegrationTest.groovy
index 689569e..79be0fc 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/BeforeResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/BeforeResolveIntegrationTest.groovy
@@ -17,8 +17,8 @@
 
 import org.gradle.api.internal.artifacts.configurations.MutationValidator
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 class BeforeResolveIntegrationTest extends AbstractDependencyResolutionTest {
@@ -179,10 +179,12 @@
                 testImplementation('org.test:module2:1.0')
             }
 
-            configurations.all { configuration ->
-                configuration.incoming.beforeResolve { resolvableDependencies ->
-                    resolvableDependencies.dependencies.each { dependency ->
-                        dependency.exclude module: 'excluded-dep'
+            configurations.all { configuration -> 
+                if (configuration.canBeResolved) {
+                    configuration.incoming.beforeResolve { resolvableDependencies ->
+                        resolvableDependencies.dependencies.each { dependency ->
+                            dependency.exclude module: 'excluded-dep'
+                        }
                     }
                 }
             }
@@ -201,7 +203,7 @@
         succeeds 'resolveDependencies'
     }
 
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     // This emulates the behaviour of the Spring Dependency Management plugin when applying dependency excludes from a BOM
     def "can use beforeResolve hook to modify excludes for a dependency shared with an already-resolved configuration"() {
         given: "3 modules, where there are dependency relations such that module1 depends on module2 and module2 depends on module3"
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy
index daff708..8aa9cc7 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/CacheResolveIntegrationTest.groovy
@@ -25,7 +25,7 @@
 
 class CacheResolveIntegrationTest extends AbstractHttpDependencyResolutionTest implements CachingIntegrationFixture {
 
-    @ToBeFixedForConfigurationCache
+    @ToBeFixedForConfigurationCache(because = "CC does not check for deleted or modified artifacts in local cache")
     void "cache handles manual deletion of cached artifacts"() {
         given:
         def module = ivyHttpRepo.module('group', 'projectA', '1.2').publish()
@@ -40,8 +40,9 @@
 configurations { compile }
 dependencies { compile 'group:projectA:1.2' }
 task listJars {
+    def files = configurations.compile
     doLast {
-        assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+        assert files.collect { it.name } == ['projectA-1.2.jar']
     }
 }
 task deleteCacheFiles(type: Delete) {
@@ -131,8 +132,9 @@
 configurations { compile }
 dependencies { compile 'group:projectA:1.2' }
 task listJars {
+    def files = configurations.compile
     doLast {
-        assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+        assert files.collect { it.name } == ['projectA-1.2.jar']
     }
 }
 """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyHandlerExtensionAwareDSLSpec.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyHandlerExtensionAwareDSLSpec.groovy
index 4e2590d..db50310 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyHandlerExtensionAwareDSLSpec.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyHandlerExtensionAwareDSLSpec.groovy
@@ -20,6 +20,7 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class DependencyHandlerExtensionAwareDSLSpec extends AbstractIntegrationSpec {
+    @ToBeFixedForConfigurationCache(because = "task uses DependencyHandler API")
     def "can type-safely use DependencyHandler ExtensionAware with the Groovy DSL"() {
         buildFile << """
             dependencies {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyManagementResultsAsInputsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyManagementResultsAsInputsIntegrationTest.groovy
index d6e14a6..a2a1fa3 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyManagementResultsAsInputsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyManagementResultsAsInputsIntegrationTest.groovy
@@ -25,12 +25,11 @@
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
 import org.gradle.internal.Describables
+import org.gradle.internal.component.external.model.DefaultImmutableCapability
 import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactIdentifier
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
-import org.gradle.internal.component.external.model.DefaultImmutableCapability
 import org.gradle.internal.component.local.model.DefaultLibraryComponentSelector
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.GradleVersion
 import spock.lang.Issue
 
 class DependencyManagementResultsAsInputsIntegrationTest extends AbstractHttpDependencyResolutionTest {
@@ -198,7 +197,7 @@
         failureDescriptionContains("2. Extract artifact files and annotate with @InputFiles.")
 
         // Documentation
-        failureDescriptionContains("Please refer to https://docs.gradle.org/${GradleVersion.current().version}/userguide/validation_problems.html#unsupported_value_type for more details about this problem.")
+        failureDescriptionContains(documentationRegistry.getDocumentationRecommendationFor("information", "validation_problems", "unsupported_value_type"))
 
         where:
         annotation    | _
@@ -649,7 +648,7 @@
             if (Boolean.getBoolean("externalDependency")) {
                 dependencies { implementation 'org.external:external-tool:1.0' }
             }
-            configurations.runtimeClasspath.returnAllVariants = true
+            configurations.runtimeClasspath.resolutionStrategy.returnAllVariants = true
         """
 
         when: "Task without changes is executed & not skipped"
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
index 9cbccd7..27dc844 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyNotationIntegrationSpec.groovy
@@ -83,6 +83,7 @@
         succeeds 'checkDeps'
     }
 
+    @ToBeFixedForConfigurationCache(because = "Task uses the Configuration API")
     def "understands project notations"() {
         when:
         settingsFile << "include 'otherProject'"
@@ -120,6 +121,7 @@
         succeeds 'checkDeps'
     }
 
+    @ToBeFixedForConfigurationCache(because = "Task uses the Configuration API")
     def "understands client module notation with dependencies"() {
         when:
         buildFile <<  """
@@ -214,6 +216,7 @@
         failure.assertThatCause(CoreMatchers.startsWith("Cannot convert a null value to an object of type Dependency"))
     }
 
+    @ToBeFixedForConfigurationCache(because = "Task uses the Configuration API")
     @Issue("https://issues.gradle.org/browse/GRADLE-3271")
     def "gradleApi dependency implements contentEquals"() {
         when:
@@ -240,6 +243,7 @@
         succeeds "check"
     }
 
+    @ToBeFixedForConfigurationCache(because = "Task uses the Configuration API")
     def "dependencies block supports provider dependencies"() {
         when:
         buildFile << """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy
index 2423dce..a7bb19c 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencySubstitutionRulesIntegrationTest.groovy
@@ -260,7 +260,11 @@
             root(":", ":depsub:") {
                 edge("org.utils:impl:1.3", "org.utils:impl:1.3") {
                     selectedByRule()
-                    module("org.utils:api:1.3").selectedByRule()
+                    forced()
+                    module("org.utils:api:1.3") {
+                        selectedByRule()
+                        forced()
+                    }
                 }
             }
         }
@@ -661,6 +665,7 @@
             root(":impl", "depsub:impl:") {
                 edge("org.utils:api:1.5", ":api", "depsub:api:") {
                     variant("default")
+                    forced()
                     selectedByRule()
                 }
             }
@@ -861,12 +866,16 @@
         then:
         resolve.expectGraph {
             root(":impl", "depsub:impl:") {
-                edge("org.utils:dep1:1.5", "org.utils:dep1:2.0").byConflictResolution("between versions 1.6 and 2.0")
+                edge("org.utils:dep1:1.5", "org.utils:dep1:2.0") {
+                    byConflictResolution("between versions 1.6 and 2.0")
+                    selectedByRule()
+                }
                 edge("org.utils:dep1:2.0", "org.utils:dep1:2.0")
 
                 edge("org.utils:dep2:1.5", ":dep2", "org.utils:dep2:3.0") {
                     variant "default"
-                    selectedByRule().byConflictResolution("between versions 3.0 and 2.0")
+                    selectedByRule()
+                    byConflictResolution("between versions 3.0 and 2.0")
                 }
                 edge("org.utils:dep2:2.0", "org.utils:dep2:3.0")
 
@@ -932,7 +941,10 @@
                 module("org.utils:b:1.3") {
                     module("org.utils:a:1.3")
                 }
-                edge("org.utils:a:1.2", "org.utils:a:1.3").byConflictResolution("between versions 1.3 and 1.2.1")
+                edge("org.utils:a:1.2", "org.utils:a:1.3") {
+                    selectedByRule()
+                    byConflictResolution("between versions 1.3 and 1.2.1")
+                }
             }
         }
     }
@@ -1349,6 +1361,7 @@
         resolve.expectGraph {
             root(":", ":depsub:") {
                 edge("org:a:1.0", "org:c:2.0") {
+                    selectedByRule()
                     byConflictResolution("between versions 2.0 and 1.1")
                 }
                 module("org:a:2.0") {
@@ -1467,6 +1480,7 @@
             root(":", ":depsub:") {
                 project(':sub', 'org.test:sub:0.0.1') {
                     configuration = 'default'
+                    selectedByRule()
                 }
                 edge('foo:bar:1', 'org.test:sub:0.0.1')
             }
@@ -1537,6 +1551,7 @@
             root(":", ":depsub:") {
                 edge('org:lib:1.0', 'org:lib:1.1') {
                     selectedByRule()
+                    byConflictResolution("between versions 1.1 and 1.0")
                 }
                 module('org:other:1.0') {
                     module('org:lib:1.1')
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyUnresolvedModuleIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyUnresolvedModuleIntegrationTest.groovy
index baaf251..c749535 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyUnresolvedModuleIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DependencyUnresolvedModuleIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.keystore.TestKeyStore
 import org.gradle.test.fixtures.maven.MavenFileRepository
@@ -132,6 +133,7 @@
         protocol << ['http', 'https']
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "prevents using repository in later resolution within the same build on HTTP timeout"() {
         given:
         MavenHttpModule moduleB = publishMavenModule(mavenHttpRepo, 'b')
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy
index 1bd1c08..a093c15 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/DetachedConfigurationsIntegrationTest.groovy
@@ -16,17 +16,21 @@
 
 package org.gradle.integtests.resolve
 
+
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 import spock.lang.Issue
 
+import static org.gradle.api.internal.DocumentationRegistry.BASE_URL
+
 @FluidDependenciesResolveTest
 class DetachedConfigurationsIntegrationTest extends AbstractIntegrationSpec {
 
     @Issue("GRADLE-2889")
     @ToBeFixedForConfigurationCache(because = "Task.getProject() during execution")
     def "detached configurations may have separate dependencies"() {
+        given:
         settingsFile << "include 'a', 'b'"
         mavenRepo.module("org", "foo").publish()
         mavenRepo.module("org", "bar").publish()
@@ -65,4 +69,91 @@
         expect:
         run "checkDependencies"
     }
+
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
+    def "detached configurations may have dependencies on other projects"() {
+        given:
+        settingsFile << "include 'other'"
+        buildFile << """
+            plugins {
+                id 'java-library'
+            }
+
+            def detached = project.configurations.detachedConfiguration()
+            detached.dependencies.add(project.dependencies.create(project(':other')))
+
+            task checkDependencies {
+                doLast {
+                    assert detached.resolvedConfiguration.getFirstLevelModuleDependencies().moduleName.contains('other')
+                    assert detached.resolvedConfiguration.resolvedArtifacts.collect { it.file.name }.contains("other.jar")
+                }
+            }
+        """
+
+        file("other/build.gradle") << """
+            plugins {
+                id 'java-library'
+            }
+        """
+
+        expect:
+        run "checkDependencies"
+    }
+
+    // This behavior will be removed in Gradle 9.0
+    @Deprecated
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
+    def "detached configurations can contain artifacts and resolve them during a self-dependency scenario"() {
+        given:
+        settingsFile << """
+            rootProject.name = 'test'
+        """
+
+        buildFile << """
+            plugins {
+                id 'java-library'
+            }
+
+            def detached = project.configurations.detachedConfiguration()
+            detached.attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
+            detached.dependencies.add(project.dependencies.create(project))
+
+            task makeArtifact(type: Zip) {
+                archiveFileName = "artifact.zip"
+                from "artifact.txt"
+            }
+
+            detached.outgoing.artifact(tasks.makeArtifact)
+
+            task checkDependencies {
+                doLast {
+                    assert detached.resolvedConfiguration.getFirstLevelModuleDependencies().moduleName.contains('test')
+                    assert detached.resolvedConfiguration.resolvedArtifacts.collect { it.file.name }.contains("artifact.zip")
+                }
+            }
+        """
+
+        file("artifact.txt") << "sample artifact"
+
+        expect:
+        run "checkDependencies"
+    }
+
+    def "configurations container reserves name #name for detached configurations"() {
+        given:
+        buildFile << """
+            configurations {
+                $name
+            }
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("Creating a configuration with a name that starts with 'detachedConfiguration' has been deprecated. " +
+            "This is scheduled to be removed in Gradle 9.0. Use a different name for the configuration '$name'. " +
+            "Consult the upgrading guide for further information: ${BASE_URL}/userguide/upgrading_version_8.html#reserved_configuration_names")
+        succeeds "help"
+
+        where:
+        name << ["detachedConfiguration", "detachedConfiguration1", "detachedConfiguration22902"]
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FileDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FileDependencyResolveIntegrationTest.groovy
index c37e77a..69108cb 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FileDependencyResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FileDependencyResolveIntegrationTest.groovy
@@ -17,7 +17,6 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 
@@ -116,7 +115,6 @@
         }
     }
 
-    @ToBeFixedForConfigurationCache(because = "serializes multiple copies of file collection for task")
     def "files are requested once only when dependency is resolved"() {
         buildFile << '''
             def jarFile = file("jar-1.jar")
@@ -140,6 +138,12 @@
 '''
 
         when:
+        run ":help"
+
+        then:
+        outputDoesNotContain("FILES REQUESTED")
+
+        when:
         run ":checkFiles"
 
         then:
@@ -232,4 +236,46 @@
         }
     }
 
+    def "can select directory using artifact type and compatibility rule"() {
+        settingsFile << "rootProject.name='main'"
+        file("someDir").createDir()
+        buildFile << '''
+            allprojects {
+                configurations {
+                    compile {
+                        attributes {
+                            attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, "thing")
+                        }
+                    }
+                }
+                dependencies {
+                    attributesSchema {
+                        attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE) {
+                            compatibilityRules.add(DirectoryIsOk)
+                        }
+                    }
+                }
+            }
+            dependencies {
+                compile files("someDir")
+            }
+            class DirectoryIsOk implements AttributeCompatibilityRule {
+                void execute(CompatibilityCheckDetails<String> details) {
+                    if (details.producerValue == ArtifactTypeDefinition.DIRECTORY_TYPE) {
+                        details.compatible()
+                    }
+                }
+            }
+'''
+
+        when:
+        run ":checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":main:") {
+                files << "someDir"
+            }
+        }
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FilteredConfigurationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FilteredConfigurationIntegrationTest.groovy
index e3eef0a..1406040 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FilteredConfigurationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FilteredConfigurationIntegrationTest.groovy
@@ -17,10 +17,12 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 
 @FluidDependenciesResolveTest
 class FilteredConfigurationIntegrationTest extends AbstractDependencyResolutionTest {
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "can query files for filtered first level dependencies"() {
         mavenRepo.module("group", "test1", "1.0").publish()
         mavenRepo.module("group", "test2", "1.0").publish()
@@ -93,6 +95,7 @@
         outputContains("child1-dependencies artifacts: [child1.jar, test2-1.0.jar]")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "can query files for filtered first level dependencies when there is a cycle in the dependency graph"() {
         mavenRepo.module("group", "test1", "1.0").publish()
         mavenRepo.module("group", "test2", "1.0").publish()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy
index 4fe89d8..fe70dc6 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/FlatDirJvmLibraryArtifactResolutionIntegrationTest.groovy
@@ -16,7 +16,6 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class FlatDirJvmLibraryArtifactResolutionIntegrationTest extends AbstractDependencyResolutionTest {
     JvmLibraryArtifactResolveTestFixture fixture
@@ -50,7 +49,6 @@
         file("sources/some-artifact-1.0-sources.jar").assertHasChangedSince(snapshot)
     }
 
-    @ToBeFixedForConfigurationCache(because = "locations are missing from exception")
     def "resolves artifacts of non-existing component"() {
         def location1 = file("repo/some-artifact-1.0.jar").toURL()
         def location2 = file("repo/some-artifact.jar").toURL()
@@ -85,7 +83,6 @@
         succeeds("verify")
     }
 
-    @ToBeFixedForConfigurationCache(because = "locations are missing from exception")
     def "can only resolve component if main artifact exists"() {
         file("repo/some-artifact-1.0-sources.jar").createFile()
         file("repo/some-artifact-1.0-javadoc.jar").createFile()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
index 45977f7..1bc759f 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ForcedModulesIntegrationTest.groovy
@@ -19,9 +19,10 @@
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 
 class ForcedModulesIntegrationTest extends AbstractIntegrationSpec {
+    private ResolveTestFixture resolve = new ResolveTestFixture(buildFile)
 
     def setup() {
-        new ResolveTestFixture(buildFile).addDefaultVariantDerivationStrategy()
+        resolve.addDefaultVariantDerivationStrategy()
     }
 
     void "can force the version of a particular module"() {
@@ -162,19 +163,6 @@
 		implementation project(':api')
 		implementation project(':impl')
 	}
-    task checkDeps {
-        def runtimeClasspath = configurations.runtimeClasspath
-        doLast {
-            assert runtimeClasspath*.name == ['api-1.0.jar', 'impl-1.0.jar', 'foo-1.5.5.jar']
-            def metadata = configurations.runtimeClasspath.resolvedConfiguration
-            def api = metadata.firstLevelModuleDependencies.find { it.moduleName == 'api' }
-            assert api.children.size() == 1
-            assert api.children.find { it.moduleName == 'foo' && it.moduleVersion == '1.5.5' }
-            def impl = metadata.firstLevelModuleDependencies.find { it.moduleName == 'impl' }
-            assert impl.children.size() == 1
-            assert impl.children.find { it.moduleName == 'foo' && it.moduleVersion == '1.5.5' }
-        }
-    }
 }
 
 allprojects {
@@ -187,9 +175,23 @@
 }
 
 """
+        resolve.expectDefaultConfiguration("runtimeElements")
+        resolve.prepare("runtimeClasspath")
 
         expect:
         run(":tool:checkDeps")
+        resolve.expectGraph {
+            root(":tool", "org.foo.unittests:tool:1.0") {
+                project(":api", "org.foo.unittests:api:1.0") {
+                    edge("org:foo:1.4.4", "org:foo:1.5.5") {
+                        forced()
+                    }
+                }
+                project(":impl", "org.foo.unittests:impl:1.0") {
+                    edge("org:foo:1.3.3", "org:foo:1.5.5")
+                }
+            }
+        }
     }
 
     void "latest strategy respects forced modules"() {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/GradlePluginPortalDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/GradlePluginPortalDependencyResolveIntegrationTest.groovy
index 951ec08..52735ed 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/GradlePluginPortalDependencyResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/GradlePluginPortalDependencyResolveIntegrationTest.groovy
@@ -16,10 +16,10 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 class GradlePluginPortalDependencyResolveIntegrationTest extends AbstractDependencyResolutionTest {
 
     def gradlePluginPortalRepository = """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/LazyDownloadsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/LazyDownloadsIntegrationTest.groovy
index bdf404d..2c7f578 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/LazyDownloadsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/LazyDownloadsIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class LazyDownloadsIntegrationTest extends AbstractHttpDependencyResolutionTest {
     def module = mavenHttpRepo.module("test", "test", "1.0").publish()
@@ -66,6 +67,7 @@
         succeeds("graph")
     }
 
+    @ToBeFixedForConfigurationCache(because = "Uses Configuration API in task action")
     def "downloads only the metadata when resolved artifacts are queried"() {
         given:
         buildFile << """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/LocalExcludeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/LocalExcludeResolveIntegrationTest.groovy
index 6f844e6..f147714 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/LocalExcludeResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/LocalExcludeResolveIntegrationTest.groovy
@@ -69,8 +69,9 @@
 }
 
 task check {
+    def files = configurations.compile
     doLast {
-        assert configurations.compile.collect { it.name } == [${resolvedJars.collect { "'$it'" }.join(", ")}]
+        assert files.collect { it.name } == [${resolvedJars.collect { "'$it'" }.join(", ")}]
     }
 }
 """
@@ -115,14 +116,12 @@
     excluded 'org.gradle.test:direct:1.0'
 }
 
-def checkDeps(config, expectedDependencies) {
-    assert config*.name as Set == expectedDependencies as Set
-}
-
 task test {
+    def excluded = configurations.excluded
+    def extendedExcluded = configurations.extendedExcluded
     doLast {
-        checkDeps configurations.excluded, ['external-1.0.jar']
-        checkDeps configurations.extendedExcluded, ['external-1.0.jar']
+        assert excluded*.name == ['external-1.0.jar']
+        assert extendedExcluded*.name == ['external-1.0.jar']
     }
 }
 """
@@ -161,8 +160,9 @@
 }
 
 task check {
+    def files = configurations.compile
     doLast {
-        assert configurations.compile.collect { it.name } == [${expectedJars.collect { "'${it}.jar'" }.join(", ")}]
+        assert files.collect { it.name } == [${expectedJars.collect { "'${it}.jar'" }.join(", ")}]
     }
 }
 """
@@ -256,8 +256,9 @@
             }
 
             task resolve() {
+                def files = configurations.foo
                 doLast {
-                    configurations.foo.resolve()
+                    files.files
                 }
             }
         """
@@ -293,8 +294,9 @@
 }
 
 task test {
+    def files = configurations.excluded
     doLast {
-        assert configurations.excluded.collect { it.name } == ['external-1.0.jar']
+        assert files.collect { it.name } == ['external-1.0.jar']
     }
 }
 """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ModuleDependencyExcludeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ModuleDependencyExcludeResolveIntegrationTest.groovy
index 8ee4bf4..73ea3d0 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ModuleDependencyExcludeResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ModuleDependencyExcludeResolveIntegrationTest.groovy
@@ -439,6 +439,7 @@
                     edge("b:b:1.0", "b:b:2.0")
                     module("c:c:1.0") {
                         module("b:b:2.0") {
+                            byConflictResolution("between versions 2.0 and 1.0")
                             // 'd' is NOT excluded, because the exclude in 'a:a:1.0 --depends-on--> b:b:1.0' only excludes 'e'
                             module("d:d:1.0")
                         }
@@ -527,7 +528,9 @@
                 module('org.test:depD:1.0') {
                     module('org.test:depC:1.0') {
                         module('org.test:depB:1.0') {
-                            module('org.test:depA:1.0')
+                            module('org.test:depA:1.0') {
+                                byConstraint()
+                            }
                         }
                     }
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/OutgoingVariantsMutationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/OutgoingVariantsMutationIntegrationTest.groovy
index 3260569..176266f 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/OutgoingVariantsMutationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/OutgoingVariantsMutationIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class OutgoingVariantsMutationIntegrationTest extends AbstractIntegrationSpec {
     def setup() {
@@ -34,6 +35,7 @@
         """
     }
 
+    @ToBeFixedForConfigurationCache(because = "Task uses the Configuration API")
     def "cannot mutate outgoing variants after configuration is resolved"() {
         given:
         buildFile << """
@@ -88,6 +90,7 @@
         failure.assertHasCause "Cannot change attributes of dependency configuration ':compile' after it has been resolved"
     }
 
+    @ToBeFixedForConfigurationCache(because = "Task uses the Configuration API")
     def "cannot add outgoing variants after configuration is resolved"() {
         given:
         buildFile << """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy
index b73c5cf..b69673e 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependenciesIntegrationTest.groovy
@@ -19,6 +19,7 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 import spock.lang.Issue
 
@@ -55,6 +56,7 @@
         outputContains "Resolved at configuration time: [impl.jar, foo-1.0.jar]"
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses dependencies API")
     def "configuring project dependencies by map is validated"() {
         settingsFile << "include 'impl'"
         buildFile << """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
index f8cf043..13f9bc0 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ProjectDependencyResolveIntegrationTest.groovy
@@ -138,9 +138,11 @@
         resolve.expectGraph {
             root(":b", "test:b:") {
                 project(":a", 'test:a:') {
+                    notRequested()
                     byReason('can provide a dependency reason for project dependencies too')
                     variant('runtime')
                     module('org.other:externalA:1.2') {
+                        notRequested()
                         byReason('also check dependency reasons')
                         variant('runtime', ['org.gradle.status': 'release', 'org.gradle.category': 'library', 'org.gradle.usage': 'java-runtime', 'org.gradle.libraryelements': 'jar'])
                     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/PublishedRichVersionConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/PublishedRichVersionConstraintsIntegrationTest.groovy
index 5973642..bf6fe31 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/PublishedRichVersionConstraintsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/PublishedRichVersionConstraintsIntegrationTest.groovy
@@ -105,7 +105,9 @@
                 edge("org:foo:{strictly [1.0,1.2]}", "org:foo:1.2")
                 edge('org:bar:1.0', 'org:bar:1.0') {
                     edge("org:foo:{strictly [1.1,1.3]}", "org:foo:1.2") {
+                        notRequested()
                         byAncestor()
+                        byReason("didn't match version 1.3")
                     }
                 }
             }
@@ -162,7 +164,9 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module('org:bar:1') {
-                    edge("org:foo:{prefer 1.1}", "org:foo:1.1")
+                    edge("org:foo:{prefer 1.1}", "org:foo:1.1") {
+                        byReason("didn't match version 2.0")
+                    }
                 }
                 module('org:baz:1') {
                     edge("org:foo:{prefer 1.0}", "org:foo:1.1")
@@ -208,7 +212,9 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge('org:foo:{strictly 17}', 'org:foo:17')
+                edge('org:foo:{strictly 17}', 'org:foo:17') {
+                    byAncestor()
+                }
                 module('org:bar:1.0') {
                     edge("org:foo:{strictly 15}", "org:foo:17")
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RemoteDependencyResolveConsoleIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RemoteDependencyResolveConsoleIntegrationTest.groovy
index e0ac370..9b4e000e 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RemoteDependencyResolveConsoleIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RemoteDependencyResolveConsoleIntegrationTest.groovy
@@ -19,6 +19,7 @@
 import org.gradle.api.logging.configuration.ConsoleOutput
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
 import org.gradle.integtests.fixtures.RichConsoleStyling
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.executer.GradleHandle
 import org.gradle.integtests.fixtures.executer.LogContent
 import org.gradle.test.fixtures.ConcurrentTestUtil
@@ -33,6 +34,7 @@
         server.start()
     }
 
+    @ToBeFixedForConfigurationCache(because = "Dependencies are not resolved during task execution")
     def "shows work-in-progress during graph and file resolution"() {
         def m1 = mavenRepo.module("test", "one", "1.2").publish()
         def m2 = mavenRepo.module("test", "two", "1.2").publish()
@@ -47,8 +49,9 @@
                 compile "test:two:1.2"
             }
             task resolve {
+                def files = configurations.compile
                 doLast {
-                    configurations.compile.each { println it.name }
+                    files.each { println it.name }
                 }
             }
 """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RepositoriesDeclaredInSettingsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RepositoriesDeclaredInSettingsIntegrationTest.groovy
index 1df760b..9e8fce1 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RepositoriesDeclaredInSettingsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RepositoriesDeclaredInSettingsIntegrationTest.groovy
@@ -18,14 +18,20 @@
 
 import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
 import org.gradle.integtests.fixtures.RequiredFeature
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.test.fixtures.plugin.PluginBuilder
 import org.gradle.test.fixtures.server.http.MavenHttpPluginRepository
-import org.gradle.util.GradleVersion
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.ToBeImplemented
-import spock.lang.IgnoreIf
 import spock.lang.Issue
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 // Restrict the number of combinations because that's not really what we want to test
 @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
 @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true")
@@ -515,6 +521,7 @@
         }
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses dependency resolution API")
     def "mutation of settings repositories after settings have been evaluated is disallowed"() {
 
         buildFile << """
@@ -565,7 +572,7 @@
     }
 
     // fails to delete directory under Windows otherwise
-    @IgnoreIf({ TestPrecondition.WINDOWS.fulfilled })
+    @Requires(UnitTestPreconditions.NotWindows)
     def "can use a published settings plugin which will apply to both the main build and buildSrc"() {
         def pluginPortal = MavenHttpPluginRepository.asGradlePluginPortal(executer, mavenRepo)
         pluginPortal.start()
@@ -703,7 +710,7 @@
     }
 
     // fails to delete directory under Windows otherwise
-    @IgnoreIf({ TestPrecondition.WINDOWS.fulfilled })
+    @Requires(UnitTestPreconditions.NotWindows)
     void "repositories declared in settings shouldn't be used to resolve plugins"() {
         def pluginPortal = MavenHttpPluginRepository.asGradlePluginPortal(executer, mavenRepo)
         pluginPortal.start()
@@ -757,10 +764,15 @@
         fails ':checkDeps'
 
         then:
-        failure.assertHasCause("""Could not resolve all dependencies for configuration ':conf'.
-The project declares repositories, effectively ignoring the repositories you have declared in the settings.
-You can figure out how project repositories are declared by configuring your build to fail on project repositories.
-See https://docs.gradle.org/${GradleVersion.current().version}/userguide/declaring_repositories.html#sub:fail_build_on_project_repositories for details.""")
+        failure.assertHasCause("""Could not resolve all dependencies for configuration ':conf'.""")
+            .assertHasResolutions("""The project declares repositories, effectively ignoring the repositories you have declared in the settings.
+   You can figure out how project repositories are declared by configuring your build to fail on project repositories.
+   ${documentationRegistry.getDocumentationRecommendationFor("information", "declaring_repositories", "sub:fail_build_on_project_repositories")}""",
+                repositoryHint("Maven POM"),
+                STACKTRACE_MESSAGE,
+                INFO_DEBUG,
+                SCAN,
+                GET_HELP)
     }
 
     @Issue("https://github.com/gradle/gradle/issues/15772")
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RepositoryContentFilteringIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RepositoryContentFilteringIntegrationTest.groovy
index 4e16248..6601794 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RepositoryContentFilteringIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RepositoryContentFilteringIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 import org.gradle.test.fixtures.file.TestFile
 
@@ -302,6 +303,7 @@
         ]
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "two configurations can use the same repositories with filtering and do not interfere with each other"() {
         def mod = mavenHttpRepo.module('org', 'foo', '1.0').publish()
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationDependenciesBuildOperationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationDependenciesBuildOperationIntegrationTest.groovy
index b645135..707468e 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationDependenciesBuildOperationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationDependenciesBuildOperationIntegrationTest.groovy
@@ -27,6 +27,7 @@
 import org.gradle.test.fixtures.server.http.AuthScheme
 import org.gradle.test.fixtures.server.http.MavenHttpModule
 import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.junit.Test
 
 class ResolveConfigurationDependenciesBuildOperationIntegrationTest extends AbstractHttpDependencyResolutionTest {
     def failedResolve = new ResolveFailureTestFixture(buildFile, "compile")
@@ -403,7 +404,7 @@
         def op = operations.first(ResolveConfigurationDependenciesBuildOperationType)
         op.details.configurationName == "compile"
         op.failure == "org.gradle.api.artifacts.ResolveException: Could not resolve all dependencies for configuration ':compile'."
-        failure.assertHasCause("""Conflict(s) found for the following module(s):
+        failure.assertHasCause("""Conflict found for the following module:
   - org:leaf between versions 2.0 and 1.0""")
         op.result != null
         op.result.resolvedDependenciesCount == 2
@@ -501,17 +502,19 @@
             def ops = operations.all(ResolveConfigurationDependenciesBuildOperationType)
             assert ops.size() == 1
             def op = ops[0]
+            def maven1Id = repoId('maven1', op.details)
+            def maven2Id = repoId('maven2', op.details)
             assert op.result.resolvedDependenciesCount == 3
             def resolvedComponents = op.result.components
             assert resolvedComponents.size() == 8
-            assert resolvedComponents.'project :'.repoName == null
-            assert resolvedComponents.'org.foo:direct1:1.0'.repoName == 'maven1'
-            assert resolvedComponents.'org.foo:direct2:1.0'.repoName == 'maven2'
-            assert resolvedComponents.'org.foo:transitive1:1.0'.repoName == 'maven1'
-            assert resolvedComponents.'org.foo:transitive2:1.0'.repoName == 'maven2'
-            assert resolvedComponents.'project :child'.repoName == null
-            assert resolvedComponents.'org.foo:child-transitive1:1.0'.repoName == 'maven1'
-            assert resolvedComponents.'org.foo:child-transitive2:1.0'.repoName == 'maven2'
+            assert resolvedComponents.'project :'.repoId == null
+            assert resolvedComponents.'org.foo:direct1:1.0'.repoId == maven1Id
+            assert resolvedComponents.'org.foo:direct2:1.0'.repoId == maven2Id
+            assert resolvedComponents.'org.foo:transitive1:1.0'.repoId == maven1Id
+            assert resolvedComponents.'org.foo:transitive2:1.0'.repoId == maven2Id
+            assert resolvedComponents.'project :child'.repoId == null
+            assert resolvedComponents.'org.foo:child-transitive1:1.0'.repoId == maven1Id
+            assert resolvedComponents.'org.foo:child-transitive2:1.0'.repoId == maven2Id
             return true
         }
 
@@ -570,12 +573,13 @@
         then:
         failedResolve.assertFailurePresent(failure)
         def op = operations.first(ResolveConfigurationDependenciesBuildOperationType)
+        def repoId = repoId('maven1', op.details)
         def resolvedComponents = op.result.components
         resolvedComponents.size() == 4
-        resolvedComponents.'project :'.repoName == null
-        resolvedComponents.'project :child'.repoName == null
-        resolvedComponents.'org.foo:direct1:1.0'.repoName == 'maven1'
-        resolvedComponents.'org.foo:transitive1:1.0'.repoName == 'maven1'
+        resolvedComponents.'project :'.repoId == null
+        resolvedComponents.'project :child'.repoId == null
+        resolvedComponents.'org.foo:direct1:1.0'.repoId == repoId
+        resolvedComponents.'org.foo:transitive1:1.0'.repoId == repoId
     }
 
     @ToBeFixedForConfigurationCache(because = "Dependency resolution does not run for a from-cache build")
@@ -616,9 +620,10 @@
 
         then:
         def op = operations.first(ResolveConfigurationDependenciesBuildOperationType)
+        def repo1Id = repoId('withCreds', op.details)
         def resolvedComponents = op.result.components
         resolvedComponents.size() == 2
-        resolvedComponents.'org.foo:good:1.0'.repoName == 'withCreds'
+        resolvedComponents.'org.foo:good:1.0'.repoId == repo1Id
 
         when:
         server.resetExpectations()
@@ -627,9 +632,80 @@
         then:
         // This demonstrates a bug in Gradle, where we ignore the requirement for credentials when retrieving from the cache
         def op2 = operations.first(ResolveConfigurationDependenciesBuildOperationType)
+        def repo2Id = repoId('withoutCreds', op.details)
         def resolvedComponents2 = op2.result.components
         resolvedComponents2.size() == 2
-        resolvedComponents2.'org.foo:good:1.0'.repoName == 'withoutCreds'
+        resolvedComponents2.'org.foo:good:1.0'.repoId == repo2Id
+    }
+
+    def "resolved components contain their source repository id, even when the repository definitions are modified"() {
+        mavenRepo.module('org.foo', 'good').publish()
+
+        setup:
+        buildFile << """
+            apply plugin: "java"
+            repositories {
+                maven {
+                    name 'one'
+                    url '${mavenRepo.uri}'
+                }
+            }
+            configurations {
+                compileClasspath {
+                    incoming.afterResolve {
+                        project.repositories.clear()
+                        project.repositories {
+                            maven {
+                                name 'two'
+                                url '${mavenRepo.uri}'
+                            }
+                            mavenCentral()
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                implementation 'org.foo:good:1.0'
+                testImplementation 'junit:junit:4.11'
+            }
+
+            task resolve1(type: Sync) {
+                from configurations.compileClasspath
+                into 'out1'
+            }
+            task resolve2(type: Sync) {
+                from configurations.testCompileClasspath
+                into 'out2'
+                mustRunAfter(tasks.resolve1)
+            }
+        """
+        file("src/main/java/Thing.java") << "public class Thing { }"
+        file("src/test/java/ThingTest.java") << """
+            import ${Test.name};
+            public class ThingTest {
+                @Test
+                public void ok() { }
+            }
+        """
+
+        when:
+        succeeds 'resolve1', 'resolve2'
+
+        then:
+        def ops = operations.all(ResolveConfigurationDependenciesBuildOperationType)
+        def op = ops[0]
+        op.details.configurationName == 'compileClasspath'
+        def repo1Id = repoId('one', op.details)
+        def resolvedComponents = op.result.components
+        resolvedComponents.size() == 2
+        resolvedComponents.'org.foo:good:1.0'.repoId == repo1Id
+        def op2 = ops[1]
+        op2.details.configurationName == 'testCompileClasspath'
+        def repo2Id = repoId('two', op2.details)
+        def resolvedComponents2 = op2.result.components
+        resolvedComponents2.size() == 4
+        resolvedComponents2.'org.foo:good:1.0'.repoId == repo2Id
     }
 
     def "resolved component op includes configuration requested attributes"() {
@@ -638,25 +714,36 @@
 
         settingsFile << "include 'fixtures'"
         buildFile << """
-            allprojects {
-                apply plugin: "java"
-                apply plugin: "java-test-fixtures"
-                repositories {
-                    maven { url '${mavenHttpRepo.uri}' }
-                }
+            plugins {
+                id 'java-library'
             }
-            dependencies {
-                testImplementation(testFixtures(project(':fixtures')))
-            }
-
-            project(':fixtures') {
+            ${mavenCentralRepository()}
+            repositories { maven { url '${mavenHttpRepo.uri}' } }
+            testing.suites.test {
+                useJUnit()
                 dependencies {
-                    testFixturesApi('org.foo:stuff:1.0')
+                    implementation testFixtures(project(':fixtures'))
                 }
             }
         """
+        file("fixtures/build.gradle") << """
+            plugins {
+                id 'java-library'
+                id 'java-test-fixtures'
+            }
+            repositories { maven { url '${mavenHttpRepo.uri}' } }
+            dependencies {
+                testFixturesApi('org.foo:stuff:1.0')
+            }
+        """
         file("fixtures/src/testFixtures/java/SomeClass.java") << "class SomeClass {}"
-        file("src/test/java/SomeTest.java") << "class SomeClass {}"
+        file("src/test/java/SomeTest.java") <<
+            """
+            public class SomeTest {
+                @org.junit.Test
+                public void test() { }
+            }
+            """
 
         when:
         succeeds ':test'
@@ -675,4 +762,8 @@
             it.requestedAttributes.find { it.name == 'org.gradle.libraryelements' }.value == 'jar'
         }
     }
+
+    private String repoId(String repoName, Map<String, ?> details) {
+        return details.repositories.find { it.name == repoName }.id
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationRepositoriesBuildOperationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationRepositoriesBuildOperationIntegrationTest.groovy
index e8c0af9..273c033 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationRepositoriesBuildOperationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolveConfigurationRepositoriesBuildOperationIntegrationTest.groovy
@@ -56,7 +56,9 @@
         op.details.buildPath == ":"
         def repos = op.details.repositories
         repos.size() == 1
-        repos.first() == augmentMapWithProperties(expectedRepo, [
+        def repo1 = repos.first()
+        repo1.remove('id')
+        repo1 == augmentMapWithProperties(expectedRepo, [
             URL: expectedRepo.name == 'MavenLocal' ? m2.mavenRepo().uri.toString() : mavenHttpRepo.uri.toString(),
             DIRS: [buildFile.parentFile.file('fooDir').absolutePath]
         ])
@@ -434,7 +436,6 @@
 
     private static Map expectedMavenRepo() {
         [
-            id: 'maven',
             name: 'maven',
             type: 'MAVEN',
             properties: [
@@ -471,7 +472,6 @@
 
     private static Map expectedIvyRepo() {
         [
-            id: 'ivy',
             name: 'ivy',
             type: 'IVY',
             properties: [
@@ -493,7 +493,6 @@
 
     private static Map expectedIvyRepoNoUrl() {
         [
-            id: 'ivy',
             name: 'ivy',
             type: 'IVY',
             properties: [
@@ -517,7 +516,6 @@
 
     private static Map expectedFlatDirRepo() {
         [
-            id: 'flatDir',
             name: 'flatDir',
             type: 'FLAT_DIR',
             properties: [
@@ -532,7 +530,6 @@
 
     private static Map expectedMavenLocalRepo() {
         [
-            id: 'MavenLocal',
             name: 'MavenLocal',
             type: 'MAVEN',
             properties: [
@@ -551,7 +548,6 @@
 
     private static Map expectedMavenCentralRepo() {
         [
-            id: 'MavenRepo',
             name: 'MavenRepo',
             type: 'MAVEN',
             properties: [
@@ -570,7 +566,6 @@
 
     private static Map expectedJcenterRepo() {
         [
-            id: 'BintrayJCenter',
             name: 'BintrayJCenter',
             type: 'MAVEN',
             properties: [
@@ -589,7 +584,6 @@
 
     private static Map expectedGoogleRepo() {
         [
-            id: 'Google',
             name: 'Google',
             type: 'MAVEN',
             properties: [
@@ -608,7 +602,6 @@
 
     private static Map expectedGradlePluginPortalRepo() {
         [
-            id: 'Gradle Central Plugin Repository',
             name: 'Gradle Central Plugin Repository',
             type: 'MAVEN',
             properties: [
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
index 557c1ad..cb6f468 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedConfigurationIntegrationTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 import spock.lang.Issue
 
@@ -32,6 +33,7 @@
         """
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "resolves strictly for dependency resolve failures when #expression is used"() {
         settingsFile << "include 'child'"
         def m1 = mavenHttpRepo.module('org.foo', 'hiphop').publish()
@@ -81,6 +83,7 @@
         "resolvedArtifacts"                        | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "resolves strictly for artifact resolve failures when #expression is used"() {
         settingsFile << "include 'child'"
         def m1 = mavenHttpRepo.module('org.foo', 'hiphop').publish()
@@ -133,6 +136,7 @@
         "files"                                    | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "resolves leniently for dependency resolve failures"() {
         settingsFile << "include 'child'"
         def m1 = mavenHttpRepo.module('org.foo', 'hiphop').publish()
@@ -198,6 +202,7 @@
         succeeds "validate"
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "doesn't reintegrate evicted edges into graph"() {
         def a1 = mavenHttpRepo.module('org.foo', 'a')
         def b1 = mavenHttpRepo.module('org.foo', 'b')
@@ -276,6 +281,7 @@
         succeeds "validate"
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "lenient for both dependency and artifact resolve and download failures"() {
         settingsFile << "include 'child'"
         def m1 = mavenHttpRepo.module('org.foo', 'hiphop').publish()
@@ -342,6 +348,7 @@
         succeeds "validate"
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "resolves leniently from mixed confs"() {
         def m1 = mavenHttpRepo.module('org.foo', 'hiphop').publish()
         def m2 = mavenHttpRepo.module('org.foo', 'unknown')
@@ -389,6 +396,7 @@
     }
 
     @Issue("gradle/gradle#3401")
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "first-level dependencies should include modules selected by agreement between selectors"() {
         def foo1 = mavenHttpRepo.module('org', 'foo', '1').publish()
         def foo2 = mavenHttpRepo.module('org', 'foo', '2').publish()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedFileOrderingIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedFileOrderingIntegrationTest.groovy
index b1a5c0c..2035e55 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedFileOrderingIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ResolvedFileOrderingIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class ResolvedFileOrderingIntegrationTest extends AbstractDependencyResolutionTest {
     def setup() {
@@ -40,6 +41,7 @@
 """
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "result includes files from local and external components and file dependencies in a fixed order and with duplicates removed"() {
         mavenRepo.module("org", "test", "1.0")
             .artifact(classifier: 'from-main')
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RichVersionConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RichVersionConstraintsIntegrationTest.groovy
deleted file mode 100644
index 665cff7..0000000
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/RichVersionConstraintsIntegrationTest.groovy
+++ /dev/null
@@ -1,1015 +0,0 @@
-/*
- * Copyright 2017 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.integtests.resolve
-
-import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
-import org.gradle.integtests.fixtures.RequiredFeature
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.test.fixtures.ivy.IvyModule
-import spock.lang.IgnoreIf
-import spock.lang.Issue
-
-@IgnoreIf({
-    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
-    // infrastructure to simulate everything done here, so we're only going to execute this test in
-    // embedded mode
-    !GradleContextualExecuter.embedded
-})
-class RichVersionConstraintsIntegrationTest extends AbstractModuleDependencyResolveTest {
-
-    void "can declare a strict dependency onto an external component"() {
-        given:
-        repository {
-            'org:foo:1.0'()
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:1.0!!')
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo:{strictly 1.0}", "org:foo:1.0")
-            }
-        }
-    }
-
-    void "can declare a strict dependency constraint onto an external component"() {
-        given:
-        repository {
-            'org:foo:1.0'()
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo')
-                constraints {
-                    conf('org:foo:1.0!!')
-                }
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo", "org:foo:1.0")
-                constraint("org:foo:{strictly 1.0}", "org:foo:1.0")
-            }
-        }
-    }
-
-    @Issue("gradle/gradle#4186")
-    def "should choose highest when multiple prefer versions disagree"() {
-        repository {
-            'org:foo' {
-                '1.0.0'()
-                '1.1.0'()
-                '1.2.0'()
-                '2.0.0'()
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                constraints {
-                    conf('org:foo') {
-                        version { prefer '1.1.0' }
-                    }
-                    conf('org:foo') {
-                        version { prefer '1.0.0' }
-                    }
-                }
-                conf 'org:foo:[1.0.0,2.0.0)'
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                expectVersionListing()
-                '1.1.0' {
-                    expectGetMetadata()
-                    expectGetArtifact()
-                }
-                '1.2.0' {
-                    expectGetMetadata()
-                }
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                constraint("org:foo:{prefer 1.0.0}", "org:foo:1.1.0")
-                constraint("org:foo:{prefer 1.1.0}", "org:foo:1.1.0")
-                edge("org:foo:[1.0.0,2.0.0)", "org:foo:1.1.0") {
-                    byConstraint()
-                    byReason("didn't match version 2.0.0")
-                }
-            }
-        }
-    }
-
-    def "can combine required and preferred version in single dependency definition"() {
-        repository {
-            'org:foo' {
-                '1.0.0'()
-                '1.1.0'()
-                '1.2.0'()
-                '2.0.0'()
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:0.9')
-                conf('org:foo:[1.0.0,2.0.0)') {
-                    version {
-                        prefer '1.1.0'
-                    }
-                }
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                expectVersionListing()
-                '1.1.0' {
-                    expectGetMetadata()
-                    expectGetArtifact()
-                }
-                '1.2.0' {
-                    expectGetMetadata()
-                }
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo:{require [1.0.0,2.0.0); prefer 1.1.0}", "org:foo:1.1.0")
-                edge("org:foo:0.9", "org:foo:1.1.0").byConflictResolution("between versions 0.9 and 1.1.0")
-            }
-        }
-    }
-
-    def "can combine strict and preferred version in single dependency definition"() {
-        repository {
-            'org:foo' {
-                '1.0.0'()
-                '1.1.0'()
-                '1.2.0'()
-                '2.0.0'()
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:[1.0.0,2.0.0)!!1.1.0')
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                expectVersionListing()
-                '1.1.0' {
-                    expectGetMetadata()
-                    expectGetArtifact()
-                }
-                '1.2.0' {
-                    expectGetMetadata()
-                }
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo:{strictly [1.0.0,2.0.0); prefer 1.1.0}", "org:foo:1.1.0")
-            }
-        }
-    }
-
-    void "a strict dependency version takes precedence over a higher transitive version"() {
-        given:
-        repository {
-            'org:foo' {
-                '1.0'()
-                '1.1'()
-            }
-            'org:bar:1.0' {
-                dependsOn('org:foo:1.1')
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo') {
-                    version {
-                       strictly '1.0'
-                    }
-                }
-                conf('org:bar:1.0')
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                '1.0' {
-                    expectResolve()
-                }
-            }
-            'org:bar:1.0' {
-                expectResolve()
-            }
-        }
-        succeeds ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge('org:foo:{strictly 1.0}', 'org:foo:1.0')
-                module('org:bar:1.0') {
-                    edge('org:foo:1.1', 'org:foo:1.0') {
-                        byAncestor()
-                    }
-                }
-            }
-        }
-
-    }
-
-    void "should pass if transitive dependency version matches exactly the strict dependency version"() {
-        given:
-        repository {
-            'org:foo:1.0'()
-            'org:bar:1.0' {
-                dependsOn 'org:foo:1.0'
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:1.0!!')
-                conf('org:bar:1.0')
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org:bar:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge "org:foo:{strictly 1.0}", "org:foo:1.0"
-                edge("org:bar:1.0", "org:bar:1.0") {
-                    edge "org:foo:1.0", "org:foo:1.0"
-                }
-            }
-        }
-    }
-
-    void "can upgrade a non-strict dependency"() {
-        given:
-        repository {
-            'org:foo' {
-                '1.0'()
-                '1.1'()
-            }
-            'org:bar:1.0' {
-                dependsOn 'org:foo:1.0'
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo') {
-                    version {
-                       strictly '1.1'
-                    }
-                }
-                conf('org:bar:1.0')
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo:1.1' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org:bar:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge "org:foo:{strictly 1.1}", "org:foo:1.1"
-                edge("org:bar:1.0", "org:bar:1.0") {
-                    edge("org:foo:1.0", "org:foo:1.1").byAncestor()
-                }
-            }
-        }
-    }
-
-    void "should pass if transitive dependency version (#transitiveDependencyVersion) matches a strict dependency version (#directDependencyVersion)"() {
-        given:
-        repository {
-            'org:foo' {
-                '1.0'()
-                '1.1'()
-                '1.2'()
-                '1.3'()
-            }
-            'org:bar:1.0' {
-                dependsOn("org:foo:$transitiveDependencyVersion")
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo') {
-                    version {
-                       strictly '$directDependencyVersion'
-                    }
-                }
-                conf('org:bar:1.0')
-            }
-
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                if (listVersions) {
-                    expectVersionListing()
-                }
-                "$resolvedVersion" {
-                    expectResolve()
-                }
-            }
-            'org:bar:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        noExceptionThrown()
-
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo:{strictly $directDependencyVersion}", "org:foo:$resolvedVersion")
-                edge("org:bar:1.0", "org:bar:1.0") {
-                    edge("org:foo:$transitiveDependencyVersion", "org:foo:$resolvedVersion") {
-                        if (transitiveDependencyVersion != resolvedVersion) {
-                            byAncestor()
-                        }
-                    }
-                }
-            }
-        }
-
-        where:
-        directDependencyVersion | transitiveDependencyVersion | listVersions | resolvedVersion
-        '[1.0,1.3]'             | '1.2'                       | true         | '1.3' // should probably choose 1.2 instead
-        '1.2'                   | '[1.0,1.3]'                 | false        | '1.2'
-        '[1.0,1.2]'             | '[1.0, 1.3]'                | true         | '1.2'
-        '[1.0,1.3]'             | '[1.0,1.2]'                 | true         | '1.3' // should probably choose 1.2 instead
-    }
-
-    def "should not downgrade dependency version when a transitive dependency has strict version"() {
-        given:
-        repository {
-            'org:foo:15'()
-            'org:foo:17'()
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:17')
-                conf project(path: 'other', configuration: 'conf')
-            }
-        """
-        file("other/build.gradle") << """
-            $repositoryDeclaration
-
-            configurations {
-                conf
-            }
-            dependencies {
-                conf('org:foo') {
-                    version {
-                        strictly '15'
-                    }
-                }
-            }
-        """
-        settingsFile << "\ninclude 'other'"
-
-        when:
-        repositoryInteractions {
-            'org:foo:15' {
-                maybeGetMetadata()
-            }
-            'org:foo:17' {
-                expectGetMetadata()
-            }
-        }
-        fails ':checkDeps'
-
-        then:
-        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
-   Dependency path ':test:unspecified' --> 'org:foo:17'
-   Dependency path ':test:unspecified' --> 'test:other:unspecified' (conf) --> 'org:foo:{strictly 15}'""")
-
-    }
-
-    def "should fail if 2 1st-level strict versions disagree"() {
-        given:
-        repository {
-            'org:foo:15'()
-            'org:foo:17'()
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo') {
-                    version {
-                        strictly '17'
-                    }
-                }
-                conf('org:foo') {
-                    version {
-                        strictly '15'
-                    }
-                }
-            }
-        """
-        settingsFile << "\ninclude 'other'"
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                '15' {
-                    maybeGetMetadata()
-                }
-                '17' {
-                    expectGetMetadata()
-                }
-            }
-        }
-        fails ':checkDeps'
-
-        then:
-        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
-   Dependency path ':test:unspecified' --> 'org:foo:{strictly 17}'
-   Dependency path ':test:unspecified' --> 'org:foo:{strictly 15}'""")
-
-    }
-
-
-    @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true")
-    def "should fail if 1st-level version disagrees with transitive strict version"() {
-        given:
-        repository {
-            'org:foo:15'()
-            'org:foo:17'()
-            'org:bar:1' {
-                dependsOn(group: 'org', artifact: 'foo', strictly: '15')
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:17')
-                conf('org:bar:1')
-            }
-        """
-        settingsFile << "\ninclude 'other'"
-
-        when:
-        repositoryInteractions {
-            'org:bar:1' {
-                expectGetMetadata()
-            }
-            'org:foo' {
-                '15' {
-                    maybeGetMetadata()
-                }
-                '17' {
-                    expectGetMetadata()
-                }
-            }
-        }
-        fails ':checkDeps'
-
-        then:
-        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
-   Dependency path ':test:unspecified' --> 'org:foo:17'
-   Dependency path ':test:unspecified' --> 'org:bar:1' (runtime) --> 'org:foo:{strictly 15}'""")
-
-    }
-
-    def "strict range defined as 1st level dependency wins over transitive one"() {
-        given:
-        repository {
-            'org:foo:15'()
-            'org:foo:16'()
-            'org:foo:17'()
-            'org:foo:18'()
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo') {
-                    version {
-                        strictly '[15,16]'
-                    }
-                }
-                conf project(path: 'other', configuration: 'conf')
-            }
-        """
-        file("other/build.gradle") << """
-            $repositoryDeclaration
-
-            configurations {
-                conf
-            }
-            dependencies {
-                conf('org:foo') {
-                    version {
-                        strictly '[17,18]'
-                    }
-                }
-            }
-        """
-        settingsFile << "\ninclude 'other'"
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                expectVersionListing()
-                '16' {
-                    expectResolve()
-                }
-            }
-        }
-        succeeds ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge('org:foo:{strictly [15,16]}', 'org:foo:16')
-                project(':other', 'test:other:') {
-                    configuration 'conf'
-                    edge('org:foo:{strictly [17,18]}', 'org:foo:16') {
-                        byAncestor()
-                    }
-                    noArtifacts()
-                }
-            }
-        }
-
-    }
-
-    def "should fail and include path to unresolvable strict version range"() {
-        given:
-        repository {
-            'org:foo:15'()
-        }
-
-        buildFile << """
-            dependencies {
-                conf 'org:foo:15'
-                conf('org:foo') {
-                    version {
-                        strictly '[0,1]'
-                    }
-                }
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                expectVersionListing()
-                '15' {
-                    expectGetMetadata()
-                }
-            }
-        }
-        fails ':checkDeps'
-
-        then:
-        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
-   Dependency path ':test:unspecified' --> 'org:foo:15'
-   Dependency path ':test:unspecified' --> 'org:foo:{strictly [0,1]}'""")
-    }
-
-    void "can reject dependency versions of an external component"() {
-        given:
-        repository {
-            'org:foo:1.0'()
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:1.0') {
-                   version {
-                      reject '1.1'
-                   }
-                }
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo:{require 1.0; reject 1.1}", "org:foo:1.0")
-            }
-        }
-    }
-
-    void "honors rejection using dynamic versions using dependency notation #notation"() {
-        given:
-        repository {
-            'org:foo:1.0' {
-                withModule(IvyModule) {
-                    withStatus('release')
-                }
-            }
-            'org:foo:1.1' {
-                withModule(IvyModule) {
-                    withStatus('release')
-                }
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:$notation') {
-                   version {
-                      reject '1.1'
-                   }
-                }
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                expectVersionListing()
-                if (requiresMetadata) {
-                    '1.1' {
-                        expectGetMetadata()
-                    }
-                }
-                '1.0' {
-                    expectResolve()
-                }
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo:{require $notation; reject 1.1}", "org:foo:1.0").byReason("rejected version 1.1")
-            }
-        }
-
-        where:
-        notation         | requiresMetadata
-        '1+'             | false
-        '1.+'            | false
-        '[1.0,)'         | false
-        'latest.release' | true
-    }
-
-    def "should fail during conflict resolution when one module rejects version"() {
-        given:
-        repository {
-            'org:foo' {
-                '1.0'()
-                '1.1'()
-            }
-            'org:bar:1.0' {
-                dependsOn('org:foo:1.1') // transitive dependency on rejected version
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:1.0') {
-                   version {
-                      reject '1.1'
-                   }
-                }
-                conf 'org:bar:1.0'
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                '1.0' {
-                    expectGetMetadata()
-                }
-                '1.1' {
-                    expectGetMetadata()
-                }
-            }
-            'org:bar:1.0' {
-                expectGetMetadata()
-            }
-        }
-        fails ':checkDeps'
-
-        then:
-        def selected = GradleMetadataResolveRunner.gradleMetadataPublished || GradleMetadataResolveRunner.useMaven() ? 'runtime' : 'default'
-        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
-   Dependency path ':test:unspecified' --> 'org:foo:{require 1.0; reject 1.1}'
-   Dependency path ':test:unspecified' --> 'org:bar:1.0' ($selected) --> 'org:foo:1.1'""")
-    }
-
-    def "can reject a version range"() {
-        given:
-        repository {
-            (0..5).each {
-                "org:foo:1.$it"()
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:[1.0,)') {
-                   version {
-                      reject '[1.2, 1.5]'
-                   }
-                }
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                expectVersionListing()
-                '1.1' {
-                    expectResolve()
-                }
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo:{require [1.0,); reject [1.2, 1.5]}", "org:foo:1.1").byReason("rejected versions 1.5, 1.4, 1.3, 1.2")
-            }
-        }
-    }
-
-    void "can reject multiple versions of an external component"() {
-        given:
-        repository {
-            'org:foo:1.0'()
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:1.0') {
-                   version {
-                      reject '1.1', '1.2'
-                   }
-                }
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:foo:{require 1.0; reject 1.1 & 1.2}", "org:foo:1.0")
-            }
-        }
-    }
-
-    void "honors multiple rejections #rejects using dynamic versions using dependency notation #notation"() {
-        given:
-        repository {
-            (0..5).each {
-                "org:foo:1.$it" {
-                    withModule(IvyModule) {
-                        withStatus('release')
-                    }
-                }
-            }
-        }
-
-        def rejectString = rejects.collect({ "'${it}'" }).join(', ')
-        buildFile << """
-            dependencies {
-                conf('org:foo:$notation') {
-                   version {
-                      reject $rejectString
-                   }
-                }
-            }
-        """
-
-        when:
-        repositoryInteractions {
-            'org:foo' {
-                expectVersionListing()
-                if (requiresMetadata) {
-                    5.downto(selected + 1) {
-                        "1.$it" {
-                            expectGetMetadata()
-                        }
-                    }
-                }
-                "1.$selected" {
-                    expectResolve()
-                }
-            }
-        }
-        run ':checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                String rejectedVersions = (selected + 1..5).collect { "1.${it}" }.reverse().join(", ")
-                edge("org:foo:{require $notation; reject ${rejects.join(' & ')}}", "org:foo:1.$selected").byReason("rejected versions ${rejectedVersions}")
-            }
-        }
-
-        where:
-        notation         | rejects                      | selected | requiresMetadata
-        '1+'             | ['1.4', '1.5']               | 3        | false
-        '1.+'            | ['1.4', '1.5']               | 3        | false
-        '[1.0,)'         | ['1.4', '1.5']               | 3        | false
-        'latest.release' | ['1.4', '1.5']               | 3        | true
-
-        '1+'             | ['[1.2,)', '1.5']            | 1        | false
-        '1.+'            | ['[1.2,)', '1.5']            | 1        | false
-        '[1.0,)'         | ['[1.2,)', '1.5']            | 1        | false
-        'latest.release' | ['[1.2,)', '1.5']            | 1        | true
-
-        '1+'             | ['1.5', '[1.1, 1.3]', '1.4'] | 0        | false
-        '1.+'            | ['1.5', '[1.1, 1.3]', '1.4'] | 0        | false
-        '[1.0,)'         | ['1.5', '[1.1, 1.3]', '1.4'] | 0        | false
-        'latest.release' | ['1.5', '[1.1, 1.3]', '1.4'] | 0        | true
-    }
-
-    def "adding rejectAll on a dependency is pointless and make it fail"() {
-        given:
-        repository {
-            'org:foo' {
-                '1.0'()
-            }
-        }
-
-        buildFile << """
-            dependencies {
-                conf('org:foo:1.0') {
-                   version {
-                      rejectAll()
-                   }
-                }
-            }
-        """
-
-        when:
-        fails ':checkDeps'
-
-        then:
-        failure.assertHasCause("Module 'org:foo' has been rejected")
-    }
-
-    @Issue("gradle/gradle#4608")
-    def "conflict resolution should consider all constraints for each candidate"() {
-        repository {
-            'org:foo:2' {
-                dependsOn("org:bar:2")
-            }
-            'org:bar:1'()
-            'org:bar:2'()
-        }
-
-        buildFile << """
-            dependencies {
-                constraints {
-                    conf('org:bar') {
-                        version {
-                            strictly '1'
-                        }
-                    }
-                }
-                conf 'org:bar:1'
-                conf 'org:foo:2' // Brings in org:bar:2, which is chosen over org:bar:1 in conflict resolution
-            }
-"""
-        when:
-        repositoryInteractions {
-            'org:bar' {
-                '1' {
-                    expectResolve()
-                }
-            }
-            'org:foo:2' {
-                expectResolve()
-            }
-        }
-
-        succeeds ":checkDeps"
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                constraint('org:bar:{strictly 1}', 'org:bar:1')
-                module('org:bar:1') {
-                    byConstraint()
-                }
-                module('org:foo:2') {
-                    edge('org:bar:2', 'org:bar:1') {
-                        byAncestor()
-                    }
-                }
-            }
-        }
-    }
-
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ScriptDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ScriptDependencyResolveIntegrationTest.groovy
index 6b4e81f..1632672 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ScriptDependencyResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ScriptDependencyResolveIntegrationTest.groovy
@@ -17,11 +17,20 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.test.fixtures.file.LeaksFileHandles
 import spock.lang.Issue
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+
 class ScriptDependencyResolveIntegrationTest extends AbstractDependencyResolutionTest {
+
+
     @LeaksFileHandles("Puts gradle user home in integration test dir")
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "root component identifier has the correct type when resolving a script classpath"() {
         given:
         def module = mavenRepo().module("org.gradle", "test", "1.45")
@@ -87,7 +96,13 @@
 """
         expect:
         fails "help"
-        failureHasCause("Conflict(s) found for the following module(s):")
+        failureHasCause("Conflict found for the following module:")
+        failure.assertHasResolutions("Run with :dependencyInsight --configuration classpath " +
+            "--dependency org.gradle:test to get more insight on how to solve the conflict.",
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
     }
 
     @Issue("gradle/gradle#19300")
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/SelfResolvingDependencyIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/SelfResolvingDependencyIntegrationTest.groovy
index 3e45671..b0a013d 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/SelfResolvingDependencyIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/SelfResolvingDependencyIntegrationTest.groovy
@@ -17,12 +17,14 @@
 package org.gradle.integtests.resolve
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class SelfResolvingDependencyIntegrationTest extends AbstractDependencyResolutionTest {
+    @ToBeFixedForConfigurationCache(because = "Task uses the Configuration API")
     def "can query file dependency for its files"() {
         buildFile << """
 allprojects {
-    configurations { 
+    configurations {
         compile
     }
 }
@@ -38,7 +40,7 @@
     doLast {
         def dep = configurations.compile.dependencies.find { it instanceof FileCollectionDependency }
         println "files: " + dep.resolve().collect { it.name }
-        println "files-not-transitive: " + dep.resolve(false).collect { it.name } 
+        println "files-not-transitive: " + dep.resolve(false).collect { it.name }
         println "files-transitive: " + dep.resolve(true).collect { it.name }
     }
 }
@@ -54,6 +56,7 @@
     }
 
     // This test documents existing behaviour rather than desired behaviour
+    @ToBeFixedForConfigurationCache(because = "Task uses the Configuration API")
     def "can query project dependency for its files"() {
         mavenRepo.module("group", "test1", "1.0").publish()
         mavenRepo.module("group", "test2", "1.0").publish()
@@ -67,7 +70,7 @@
     repositories {
         maven { url '${mavenRepo.uri}' }
     }
-    configurations { 
+    configurations {
         compile
         create('default') { extendsFrom compile }
     }
@@ -112,7 +115,7 @@
     doLast {
         def dep = configurations.compile.dependencies.find { it instanceof ProjectDependency }
         println "files: " + dep.resolve().collect { it.name }
-        println "files-not-transitive: " + dep.resolve(false).collect { it.name } 
+        println "files-not-transitive: " + dep.resolve(false).collect { it.name }
         println "files-transitive: " + dep.resolve(true).collect { it.name }
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/UnsafeConfigurationResolutionDeprecationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/UnsafeConfigurationResolutionDeprecationIntegrationTest.groovy
index f0c548c..985f5db 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/UnsafeConfigurationResolutionDeprecationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/UnsafeConfigurationResolutionDeprecationIntegrationTest.groovy
@@ -18,7 +18,6 @@
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
 import org.gradle.util.GradleVersion
 import spock.lang.Ignore
 
@@ -61,7 +60,7 @@
         result.assertHasErrorOutput("Resolution of the configuration :bar:bar was attempted from a context different than the project context. See: https://docs.gradle.org/${GradleVersion.current().version}/userguide/viewing_debugging_dependencies.html#sub:resolving-unsafe-configuration-resolution-errors for more information.")
     }
 
-    @UnsupportedWithConfigurationCache(because = "resolves configuration from background thread")
+    @ToBeFixedForConfigurationCache(because = "uses Configuration API at runtime")
     def "exception when non-gradle thread resolves dependency graph"() {
         mavenRepo.module("test", "test-jar", "1.0").publish()
 
@@ -127,6 +126,7 @@
         ]
     }
 
+    @ToBeFixedForConfigurationCache(because = "uses Configuration API at runtime")
     def "no exception when non-gradle thread iterates over dependency artifacts that were declared as task inputs"() {
         mavenRepo.module("test", "test-jar", "1.0").publish()
 
@@ -213,12 +213,13 @@
 
             task resolve {
                 def configuration = configurations.bar
+                def outFile = file('bar')
                 doFirst {
                     configuration.files
                     def failure = null
                     def thread = new Thread({
                         try {
-                            file('bar') << configuration.files
+                            outFile << configuration.files
                         } catch(Throwable t) {
                             failure = t
                         }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy
deleted file mode 100644
index 8956ec8..0000000
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionConflictResolutionIntegrationTest.groovy
+++ /dev/null
@@ -1,2372 +0,0 @@
-/*
- * Copyright 2012 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.integtests.resolve
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
-import spock.lang.Issue
-
-import static org.hamcrest.CoreMatchers.containsString
-
-class VersionConflictResolutionIntegrationTest extends AbstractIntegrationSpec {
-    private ResolveTestFixture resolve = new ResolveTestFixture(buildFile, "compile")
-
-    def setup() {
-        settingsFile << """
-            rootProject.name = 'test'
-        """
-        resolve.expectDefaultConfiguration("runtime")
-        resolve.addDefaultVariantDerivationStrategy()
-    }
-
-    void "strict conflict resolution should fail due to conflict"() {
-        mavenRepo.module("org", "foo", '1.3.3').publish()
-        mavenRepo.module("org", "foo", '1.4.4').publish()
-
-        settingsFile << "include 'api', 'impl', 'tool'"
-
-        buildFile << """
-allprojects {
-	apply plugin: 'java'
-	repositories {
-		maven { url "${mavenRepo.uri}" }
-	}
-}
-
-project(':api') {
-	dependencies {
-		implementation (group: 'org', name: 'foo', version:'1.3.3')
-	}
-}
-
-project(':impl') {
-	dependencies {
-		implementation (group: 'org', name: 'foo', version:'1.4.4')
-	}
-}
-
-project(':tool') {
-	dependencies {
-		implementation project(':api')
-		implementation project(':impl')
-	}
-
-	configurations.runtimeClasspath.resolutionStrategy.failOnVersionConflict()
-}
-"""
-
-        expect:
-        runAndFail("tool:dependencies")
-        failure.assertThatCause(containsString('Conflict(s) found for the following module(s):'))
-    }
-
-    void "strict conflict resolution should pass when no conflicts"() {
-        mavenRepo.module("org", "foo", '1.3.3').publish()
-
-        settingsFile << "include 'api', 'impl', 'tool'"
-
-        buildFile << """
-allprojects {
-	apply plugin: 'java'
-	repositories {
-		maven { url "${mavenRepo.uri}" }
-	}
-}
-
-project(':api') {
-	dependencies {
-		implementation (group: 'org', name: 'foo', version:'1.3.3')
-	}
-}
-
-project(':impl') {
-	dependencies {
-		implementation (group: 'org', name: 'foo', version:'1.3.3')
-	}
-}
-
-project(':tool') {
-	dependencies {
-		implementation project(':api')
-		implementation project(':impl')
-	}
-
-	configurations.all { resolutionStrategy.failOnVersionConflict() }
-}
-"""
-
-        expect:
-        run("tool:dependencies")
-    }
-
-    void "resolves module version conflicts to the latest version by default"() {
-        mavenRepo.module("org", "foo", '1.3.3').publish()
-        mavenRepo.module("org", "foo", '1.4.4').publish()
-
-        settingsFile << """
-include 'api', 'impl', 'tool'
-"""
-
-        buildFile << """
-allprojects {
-	apply plugin: 'java'
-	repositories {
-		maven { url "${mavenRepo.uri}" }
-	}
-}
-
-project(':api') {
-	dependencies {
-		implementation (group: 'org', name: 'foo', version:'1.3.3')
-	}
-}
-
-project(':impl') {
-	dependencies {
-		implementation (group: 'org', name: 'foo', version:'1.4.4')
-	}
-}
-
-project(':tool') {
-	dependencies {
-		implementation project(':api')
-		implementation project(':impl')
-	}
-}
-"""
-
-        def resolve = new ResolveTestFixture(buildFile)
-        resolve.prepare()
-
-        when:
-        run("tool:checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":tool", "test:tool:") {
-                project(":api", "test:api:") {
-                    configuration = "runtimeElements"
-                    edge("org:foo:1.3.3", "org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3")
-                }
-                project(":impl", "test:impl:") {
-                    configuration = "runtimeElements"
-                    module("org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3")
-                }
-            }
-        }
-    }
-
-    void "resolves transitive module version conflicts to the latest version by default"() {
-        def foo133 = mavenRepo.module("org", "foo", '1.3.3').publish()
-        def foo144 = mavenRepo.module("org", "foo", '1.4.4').publish()
-        mavenRepo.module("org", "bar", "1.0").dependsOn(foo133).publish()
-        mavenRepo.module("org", "baz", "1.0").dependsOn(foo144).publish()
-
-        buildFile << """
-apply plugin: 'java'
-group = 'org'
-version = '1.0'
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-dependencies {
-    implementation (group: 'org', name: 'bar', version:'1.0')
-    implementation (group: 'org', name: 'baz', version:'1.0')
-}
-
-task resolve {
-    doLast {
-        println configurations.compile.files
-    }
-}
-"""
-
-        def resolve = new ResolveTestFixture(buildFile).expectDefaultConfiguration("runtime")
-        resolve.prepare()
-
-        when:
-        run(":checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", "org:test:1.0") {
-                module("org:bar:1.0") {
-                    edge("org:foo:1.3.3", "org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3")
-                }
-                module("org:baz:1.0") {
-                    module("org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3")
-                }
-            }
-        }
-    }
-
-    void "re-selects target version for previously resolved then evicted selector"() {
-        def depOld = mavenRepo.module("org", "dep", "2.0").publish()
-        def depNew = mavenRepo.module("org", "dep", "2.5").publish()
-
-        def controlOld = mavenRepo.module("org", "control", "1.0").dependsOn(depNew).publish()
-        def controlNew = mavenRepo.module("org", "control", "1.2").dependsOn(depNew).publish()
-        def controlNewBringer = mavenRepo.module("org", "control-1.2-bringer", "1.0").dependsOn(controlNew).publish()
-
-        mavenRepo.module("org", "one", "1.0").dependsOn(controlOld).publish()
-
-        def depOldBringer = mavenRepo.module("org", "dep-2.0-bringer", "1.0").dependsOn(depOld).publish()
-        // Note: changing the order of the following dependencies makes the test pass
-        mavenRepo.module("org", "two", "1.0").dependsOn(controlNewBringer).dependsOn(depOldBringer).publish()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:one:1.0'
-    compile 'org:two:1.0'
-}
-"""
-
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:one:1.0") {
-                    edge("org:control:1.0", "org:control:1.2") {
-                        edge("org:dep:2.5", "org:dep:2.5")
-                    }
-                }
-                module("org:two:1.0") {
-                    module("org:dep-2.0-bringer:1.0") {
-                        edge("org:dep:2.0", "org:dep:2.5").byConflictResolution("between versions 2.5 and 2.0")
-                    }
-                    module("org:control-1.2-bringer:1.0") {
-                        module("org:control:1.2")
-                    }
-                }
-            }
-        }
-    }
-
-    void "does not attempt to resolve an evicted dependency"() {
-        mavenRepo.module("org", "external", "1.2").publish()
-        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.0").publish()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:external:1.2'
-    compile 'org:dep:2.2'
-}
-"""
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:external:1.2").byConflictResolution("between versions 1.2 and 1.0")
-                module("org:dep:2.2") {
-                    edge("org:external:1.0", "org:external:1.2")
-                }
-            }
-        }
-    }
-
-    @Issue("GRADLE-2890")
-    void "selects latest from multiple conflicts"() {
-        mavenRepo.module("org", "child", '1').publish()
-        mavenRepo.module("org", "child", '2').publish()
-        mavenRepo.module("org", "parent", '1').dependsOn("org", "child", "1").publish()
-        mavenRepo.module("org", "parent", '2').dependsOn("org", "child", "2").publish()
-        mavenRepo.module("org", "dep", '2').dependsOn("org", "parent", "2").publish()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-configurations {
-    compile
-}
-dependencies {
-    compile 'org:parent:1'
-    compile 'org:child:2'
-    compile 'org:dep:2'
-}
-"""
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:child:2")
-                edge("org:parent:1", "org:parent:2") {
-                    module("org:child:2")
-                }
-                module("org:dep:2") {
-                    module("org:parent:2")
-                }
-            }
-        }
-    }
-
-    void "resolves dynamic dependency before resolving conflict"() {
-        mavenRepo.module("org", "external", "1.2").publish()
-        mavenRepo.module("org", "external", "1.4").publish()
-        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "[1.3,)").publish()
-
-        def buildFile = file("build.gradle")
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:external:1.2'
-    compile 'org:dep:2.2'
-}
-"""
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:external:1.2", "org:external:1.4")
-                module("org:dep:2.2") {
-                    edge("org:external:[1.3,)", "org:external:1.4")
-                }
-            }
-        }
-    }
-
-    void "fails when version selected by conflict resolution does not exist"() {
-        mavenRepo.module("org", "external", "1.2").publish()
-        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.4").publish()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:external:1.2'
-    compile 'org:dep:2.2'
-}
-
-task checkDeps {
-    def files = configurations.compile
-    doLast {
-        files.files
-    }
-}
-"""
-
-        expect:
-        runAndFail("checkDeps")
-        failure.assertHasCause("Could not find org:external:1.4.")
-    }
-
-    void "does not fail when evicted version does not exist"() {
-        mavenRepo.module("org", "external", "1.4").publish()
-        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.4").publish()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-configurations { compile }
-
-dependencies {
-    compile 'org:external:1.2'
-    compile 'org:dep:2.2'
-}
-"""
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:external:1.2", "org:external:1.4")
-                module("org:dep:2.2") {
-                    module("org:external:1.4")
-                }
-            }
-        }
-    }
-
-    void "takes newest dynamic version when dynamic version forced"() {
-        mavenRepo.module("org", "foo", '1.3.0').publish()
-
-        mavenRepo.module("org", "foo", '1.4.1').publish()
-        mavenRepo.module("org", "foo", '1.4.4').publish()
-        mavenRepo.module("org", "foo", '1.4.9').publish()
-
-        mavenRepo.module("org", "foo", '1.6.0').publish()
-
-        settingsFile << "include 'api', 'impl', 'tool'"
-
-        buildFile << """
-allprojects {
-	apply plugin: 'java'
-	repositories {
-		maven { url "${mavenRepo.uri}" }
-	}
-}
-
-project(':api') {
-	dependencies {
-		implementation 'org:foo:1.4.4'
-	}
-}
-
-project(':impl') {
-	dependencies {
-		implementation 'org:foo:1.4.1'
-	}
-}
-
-project(':tool') {
-
-	dependencies {
-		implementation project(':api'), project(':impl'), 'org:foo:1.3.0'
-	}
-
-	configurations.all {
-	    resolutionStrategy {
-	        force 'org:foo:[1.4, 1.5)'
-	        failOnVersionConflict()
-	    }
-	}
-}
-
-"""
-        resolve.prepare("runtimeClasspath")
-
-        when:
-        run("tool:checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":tool", "test:tool:") {
-                project(":api", "test:api:") {
-                    edge("org:foo:1.4.4", "org:foo:1.4.9")
-                }
-                project(":impl", "test:impl:") {
-                    edge("org:foo:1.4.1", "org:foo:1.4.9")
-                }
-                edge("org:foo:1.3.0", "org:foo:1.4.9")
-            }
-        }
-    }
-
-    void "parent pom does not participate in forcing mechanism"() {
-        mavenRepo.module("org", "foo", '1.3.0').publish()
-        mavenRepo.module("org", "foo", '2.4.0').publish()
-
-        def parent = mavenRepo.module("org", "someParent", "1.0")
-        parent.type = 'pom'
-        parent.dependsOn("org", "foo", "1.3.0")
-        parent.publish()
-
-        def otherParent = mavenRepo.module("org", "someParent", "2.0")
-        otherParent.type = 'pom'
-        otherParent.dependsOn("org", "foo", "2.4.0")
-        otherParent.publish()
-
-        def dep = mavenRepo.module("org", "someArtifact", '1.0')
-        dep.parent("org", "someParent", "1.0")
-        dep.publish()
-
-        buildFile << """
-apply plugin: 'java'
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-dependencies {
-    implementation 'org:someArtifact:1.0'
-}
-
-configurations.all {
-    resolutionStrategy {
-        force 'org:someParent:2.0'
-        failOnVersionConflict()
-    }
-}
-"""
-        resolve.prepare("runtimeClasspath")
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:someArtifact:1.0") {
-                    module("org:foo:1.3.0")
-                }
-            }
-        }
-    }
-
-    void "previously evicted nodes should contain correct target version"() {
-        /*
-        a1->b1
-        a2->b2->a1
-
-        resolution process:
-
-        1. stop resolution, resolve conflict a1 vs a2
-        2. select a2, restart resolution
-        3. stop, resolve b1 vs b2
-        4. select b2, restart
-        5. resolve b2 dependencies, a1 has been evicted previously but it should show correctly on the report
-           ('dependencies' report pre 1.2 would not show the a1 dependency leaf for this scenario)
-        */
-
-        ivyRepo.module("org", "b", '1.0').publish()
-        ivyRepo.module("org", "a", '1.0').dependsOn("org", "b", '1.0').publish()
-        ivyRepo.module("org", "b", '2.0').dependsOn("org", "a", "1.0").publish()
-        ivyRepo.module("org", "a", '2.0').dependsOn("org", "b", '2.0').publish()
-
-        buildFile << """
-            repositories {
-                ivy { url "${ivyRepo.uri}" }
-            }
-
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:a:2.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:a:1.0", "org:a:2.0")
-                module("org:a:2.0") {
-                    configuration = "default"
-                    module("org:b:2.0") {
-                        edge("org:a:1.0", "org:a:2.0")
-                    }
-                }
-            }
-        }
-    }
-
-    @Issue("GRADLE-2555")
-    void "can deal with transitive with parent in conflict"() {
-        /*
-            Graph looks like…
-
-            \--- org:a:1.0
-                 \--- org:in-conflict:1.0 -> 2.0
-                      \--- org:target:1.0
-                           \--- org:target-child:1.0
-            \--- org:b:1.0
-                 \--- org:b-child:1.0
-                      \--- org:in-conflict:2.0 (*)
-
-            This is the simplest structure I could boil it down to that produces the error.
-            - target *must* have a child
-            - Having "b" depend directly on "in-conflict" does not produce the error, needs to go through "b-child"
-         */
-
-        mavenRepo.module("org", "target-child", "1.0").publish()
-        mavenRepo.module("org", "target", "1.0").dependsOn("org", "target-child", "1.0").publish()
-        mavenRepo.module("org", "in-conflict", "1.0").dependsOn("org", "target", "1.0").publish()
-        mavenRepo.module("org", "in-conflict", "2.0").dependsOn("org", "target", "1.0").publish()
-
-        mavenRepo.module("org", "a", '1.0').dependsOn("org", "in-conflict", "1.0").publish()
-
-        mavenRepo.module("org", "b-child", '1.0').dependsOn("org", "in-conflict", "2.0").publish()
-
-        mavenRepo.module("org", "b", '1.0').dependsOn("org", "b-child", "1.0").publish()
-
-        buildFile << """
-            repositories { maven { url "${mavenRepo.uri}" } }
-
-            configurations { compile }
-
-            dependencies {
-                compile "org:a:1.0", "org:b:1.0"
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:in-conflict:1.0", "org:in-conflict:2.0")
-                }
-                module("org:b:1.0") {
-                    module("org:b-child:1.0") {
-                        module("org:in-conflict:2.0") {
-                            module("org:target:1.0") {
-                                module("org:target-child:1.0")
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @Issue("GRADLE-2555")
-    void "batched up conflicts with conflicted parent and child"() {
-        /*
-        Dependency tree:
-
-        a->c1
-        b->c2->x1
-        d->x1
-        f->x2
-
-        Everything is resolvable but not x2
-
-        Scenario:
-         - We have batched up conflicts
-         - root of one conflicted version is also conflicted
-         - conflicted root is positioned on the conflicts queue after the conflicted child (the order of declaring dependencies matters)
-         - winning root depends on a child that previously was evicted
-         - finally, the winning child is an unresolved dependency
-        */
-        mavenRepo.module("org", "c", '1.0').publish()
-        mavenRepo.module("org", "x", '1.0').publish()
-        mavenRepo.module("org", "c", '2.0').dependsOn("org", "x", '1.0').publish()
-        mavenRepo.module("org", "a").dependsOn("org", "c", "1.0").publish()
-        mavenRepo.module("org", "b").dependsOn("org", "c", "2.0").publish()
-        mavenRepo.module("org", "d").dependsOn("org", "x", "1.0").publish()
-        mavenRepo.module("org", "f").dependsOn("org", "x", "2.0").publish()
-
-        buildFile << """
-            repositories { maven { url "${mavenRepo.uri}" } }
-            configurations {
-                childFirst
-                parentFirst
-            }
-            dependencies {
-                //conflicted child is resolved first
-                childFirst 'org:d:1.0', 'org:f:1.0', 'org:a:1.0', 'org:b:1.0'
-                //conflicted parent is resolved first
-                parentFirst 'org:a:1.0', 'org:b:1.0', 'org:d:1.0', 'org:f:1.0'
-            }
-        """
-
-        when:
-        run("dependencies")
-
-        then:
-        output.contains """
-childFirst
-+--- org:d:1.0
-|    \\--- org:x:1.0 -> 2.0 FAILED
-+--- org:f:1.0
-|    \\--- org:x:2.0 FAILED
-+--- org:a:1.0
-|    \\--- org:c:1.0 -> 2.0
-|         \\--- org:x:1.0 -> 2.0 FAILED
-\\--- org:b:1.0
-     \\--- org:c:2.0 (*)
-
-parentFirst
-+--- org:a:1.0
-|    \\--- org:c:1.0 -> 2.0
-|         \\--- org:x:1.0 -> 2.0 FAILED
-+--- org:b:1.0
-|    \\--- org:c:2.0 (*)
-+--- org:d:1.0
-|    \\--- org:x:1.0 -> 2.0 FAILED
-\\--- org:f:1.0
-     \\--- org:x:2.0 FAILED"""
-    }
-
-    @Issue("GRADLE-2752")
-    void "selects root module when earlier version of module requested"() {
-        mavenRepo.module("org", "test", "1.2").publish()
-        mavenRepo.module("org", "other", "1.7").dependsOn("org", "test", "1.2").publish()
-
-        buildFile << """
-apply plugin: 'java'
-
-group "org"
-version "1.3"
-
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-dependencies {
-    implementation "org:other:1.7"
-}
-"""
-
-        def resolve = new ResolveTestFixture(buildFile).expectDefaultConfiguration("runtime")
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", "org:test:1.3") {
-                module("org:other:1.7") {
-                    edge("org:test:1.2", "org:test:1.3")
-                }
-            }
-        }
-    }
-
-    @Issue("GRADLE-2920")
-    void "selects later version of root module when requested"() {
-        mavenRepo.module("org", "test", "2.1").publish()
-        mavenRepo.module("org", "other", "1.7").dependsOn("org", "test", "2.1").publish()
-
-        buildFile << """
-apply plugin: 'java'
-
-group "org"
-version "1.3"
-
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-
-dependencies {
-    implementation "org:other:1.7"
-}
-"""
-
-        def resolve = new ResolveTestFixture(buildFile).expectDefaultConfiguration("runtime")
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", "org:test:1.3") {
-                module("org:other:1.7") {
-                    module("org:test:2.1").byConflictResolution("between versions 2.1 and 1.3")
-                }
-            }
-        }
-    }
-
-    void "module is required only by selected conflicting version and in turn requires evicted conflicting version"() {
-        /*
-            a2 -> b1 -> c1
-            a1
-            c2
-         */
-        mavenRepo.module("org", "a", "1").publish()
-        mavenRepo.module("org", "a", "2").dependsOn("org", "b", "1").publish()
-        mavenRepo.module("org", "b", "1").dependsOn("org", "c", "1").publish()
-        mavenRepo.module("org", "c", "1").publish()
-        mavenRepo.module("org", "c", "2").publish()
-
-        buildFile << """
-repositories {
-    maven { url "${mavenRepo.uri}" }
-}
-configurations {
-    compile
-}
-dependencies {
-    compile "org:a:2"
-    compile "org:a:1"
-    compile "org:c:2"
-}
-"""
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:2")
-                edge("org:a:1", "org:a:2") {
-                    module("org:b:1") {
-                        edge("org:c:1", "org:c:2")
-                    }
-                }
-                module("org:c:2")
-            }
-        }
-    }
-
-    @Issue("GRADLE-2738")
-    def "resolution fails when any selector cannot be resolved"() {
-        given:
-        //only 1.5 published:
-        mavenRepo.module("org", "leaf", "1.5").publish()
-
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "(,1.0)").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "1.0").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[1.5,1.9]").publish()
-        mavenRepo.module("org", "d", "1.0").dependsOn("org", "leaf", "2.0+").publish()
-
-        buildFile << """
-            version = 12
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                conf
-            }
-            dependencies {
-                conf 'org:a:1.0', 'org:b:1.0', 'org:c:1.0', 'org:d:1.0'
-            }
-            task resolve {
-                def files = configurations.conf
-                doLast {
-                    files.files
-                }
-            }
-            task checkGraph {
-                def result = configurations.conf.incoming.resolutionResult.rootComponent
-                doLast {
-                    def root = result.get()
-                    def components = root.dependencies.collect { it.selected }
-                    def a = components.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'a' }
-                    def b = components.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'b' }
-                    def c = components.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'c' }
-                    def d = components.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'd' }
-
-                    a.dependencies.each {
-                        assert it instanceof UnresolvedDependencyResult
-                        assert it.requested.toString() == 'org:leaf:(,1.0)'
-                        assert it.failure.getMessage().startsWith('Could not find any version that matches org:leaf:(,1.0).')
-                    }
-                    b.dependencies.each {
-                        assert it instanceof ResolvedDependencyResult
-                        assert it.requested.toString() == 'org:leaf:1.0'
-                        assert it.selected.id.module == 'leaf'
-                    }
-                    c.dependencies.each {
-                        assert it instanceof ResolvedDependencyResult
-                        assert it.requested.toString() == 'org:leaf:[1.5,1.9]'
-                        assert it.selected.id.module == 'leaf'
-                    }
-                    d.dependencies.each {
-                        assert it instanceof UnresolvedDependencyResult
-                        assert it.requested.toString() == 'org:leaf:2.0+'
-                        assert it.failure.getMessage().startsWith('Could not find any version that matches org:leaf:2.0+.')
-                    }
-                }
-            }
-        """
-
-        when:
-        succeeds "checkGraph"
-
-        and:
-        runAndFail "resolve"
-
-        then:
-        failure.assertResolutionFailure(":conf").assertFailedDependencyRequiredBy("project : > org:d:1.0")
-    }
-
-    def "chooses highest version that is included in both ranges"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,6]", "org:leaf:6")
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[4,8]", "org:leaf:6")
-                }
-            }
-        }
-    }
-
-    def "chooses highest version that is included in both ranges, with the highest version in the intersection missing"() {
-        given:
-        (1..10).findAll {
-            // We skip v6, as we test what happens when the top version of the intersection is missing
-            it != 6
-        }.each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,6]", "org:leaf:5")
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[4,8]", "org:leaf:5")
-                }
-            }
-        }
-    }
-
-    def "chooses highest version that is included in both ranges when fail on conflict is set"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile {
-                    resolutionStrategy {
-                        failOnVersionConflict()
-                    }
-                }
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,6]", "org:leaf:6")
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[4,8]", "org:leaf:6")
-                }
-            }
-        }
-    }
-
-    def "chooses highest version that is included in all ranges"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[3,5]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,6]", "org:leaf:5")
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[4,8]", "org:leaf:5")
-                }
-                module("org:c:1.0") {
-                    edge("org:leaf:[3,5]", "org:leaf:5")
-                }
-            }
-        }
-    }
-
-    def "chooses highest version that is included in all ranges, when dependencies are included at different transitivity levels"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        // top level
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-
-        // b will include 'leaf' through a transitive dependency
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "b2", "1.0").publish()
-        mavenRepo.module("org", "b2", "1.0").dependsOn("org", "leaf", "[3,5]").publish()
-
-        // c will include 'leaf' through a 2d level transitive dependency
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "c2", "1.0").publish()
-        mavenRepo.module("org", "c2", "1.0").dependsOn("org", "c3", "1.0").publish()
-        mavenRepo.module("org", "c3", "1.0").dependsOn("org", "leaf", "[3,5]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,6]", "org:leaf:5") {
-                        byReason("didn't match versions 10, 9, 8, 7")
-                    }
-                }
-                module("org:b:1.0") {
-                    module("org:b2:1.0") {
-                        edge("org:leaf:[3,5]", "org:leaf:5") {
-                            byReason("didn't match versions 10, 9, 8, 7, 6")
-                        }
-                    }
-                }
-                module("org:c:1.0") {
-                    module("org:c2:1.0") {
-                        module("org:c3:1.0") {
-                            edge("org:leaf:[3,5]", "org:leaf:5")
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    def "upgrades version when ranges are disjoint"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,3]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[5,8]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,3]", "org:leaf:8") {
-                        byReason("didn't match versions 10, 9, 8, 7, 6, 5, 4")
-                        byReason("conflict resolution: between versions 3 and 8")
-                    }
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[5,8]", "org:leaf:8") {
-                        byReason("didn't match versions 10, 9")
-                    }
-                }
-            }
-        }
-    }
-
-    def "fail when ranges are disjoint and no top range artifact is present"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[1,5]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[11,15]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                conf
-            }
-            dependencies {
-                conf 'org:a:1.0', 'org:b:1.0'
-            }
-            task checkDeps {
-                def files = configurations.conf
-                doLast {
-                    files.files
-                }
-            }
-        """
-
-        when:
-        fails 'checkDeps'
-
-        then:
-        failure.assertThatCause(containsString("Could not find any version that matches org:leaf:[11,15]."))
-    }
-
-    def "upgrades version when ranges are disjoint unless failOnVersionConflict is set"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,3]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[5,8]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                conf {
-                    resolutionStrategy.failOnVersionConflict()
-                }
-            }
-            dependencies {
-                conf 'org:a:1.0', 'org:b:1.0'
-            }
-            task checkDeps {
-                def files = configurations.conf
-                doLast {
-                    files.files
-                }
-            }
-        """
-
-        when:
-        fails 'checkDeps'
-
-        then:
-        failure.assertThatCause(containsString('Conflict(s) found for the following module(s):'))
-    }
-
-    def "upgrades version when one of the ranges is disjoint"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[3,4]").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[7,8]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,6]", "org:leaf:8") {
-                        byReason("didn't match versions 10, 9, 8, 7")
-                        byReason("conflict resolution: between versions 8 and 4")
-                    }
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[3,4]", "org:leaf:8") {
-                        byReason("didn't match versions 10, 9, 8, 7, 6, 5")
-                    }
-                }
-                module("org:c:1.0") {
-                    edge("org:leaf:[7,8]", "org:leaf:8") {
-                        byReason("didn't match versions 10, 9")
-                    }
-                }
-            }
-        }
-    }
-
-    def "fails when one of the ranges is disjoint and fail on conflict is set"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[3,4]").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[7,8]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                conf {
-                    resolutionStrategy {
-                        failOnVersionConflict()
-                    }
-                }
-            }
-            dependencies {
-                conf 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
-            }
-            task checkDeps {
-                def files = configurations.conf
-                doLast {
-                    files.files
-                }
-            }
-        """
-
-        when:
-        fails 'checkDeps'
-
-        then:
-        failure.assertThatCause(containsString('Conflict(s) found for the following module(s):'))
-    }
-
-    def "chooses highest version of all versions fully included within range"() {
-        given:
-        (1..12).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[1,12]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[3,8]").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[2,10]").publish()
-        mavenRepo.module("org", "d", "1.0").dependsOn("org", "leaf", "[4,7]").publish()
-        mavenRepo.module("org", "e", "1.0").dependsOn("org", "leaf", "[4,11]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0', 'org:d:1.0', 'org:e:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[1,12]", "org:leaf:7")
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[3,8]", "org:leaf:7")
-                }
-                module("org:c:1.0") {
-                    edge("org:leaf:[2,10]", "org:leaf:7")
-                }
-                module("org:d:1.0") {
-                    edge("org:leaf:[4,7]", "org:leaf:7")
-                }
-                module("org:e:1.0") {
-                    edge("org:leaf:[4,11]", "org:leaf:7")
-                }
-            }
-        }
-    }
-
-    def "selects the minimal version when in there's an open range"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[5,)").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,6]", "org:leaf:6")
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[5,)", "org:leaf:6")
-                }
-            }
-        }
-    }
-
-    def "range selector should not win over sub-version selector"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "1.$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[1.2,1.6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "1.+").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[1.2,1.6]", "org:leaf:1.10")
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:1.+", "org:leaf:1.10")
-                }
-            }
-        }
-    }
-
-    def "range conflict resolution not interfering between distinct configurations"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[3,5]").publish()
-        mavenRepo.module("org", "d", "1.0").dependsOn("org", "leaf", "8").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                conf
-                conf2
-                conf3
-                conf4
-            }
-            dependencies {
-                conf 'org:a:1.0', 'org:b:1.0'
-                conf2 'org:a:1.0', 'org:c:1.0'
-                conf3 'org:b:1.0', 'org:c:1.0'
-                conf4 'org:b:1.0', 'org:c:1.0', 'org:d:1.0'
-            }
-            task checkDeps {
-                def files1 = configurations.conf
-                def files2 = configurations.conf2
-                def files3 = configurations.conf3
-                def files4 = configurations.conf4
-                doLast {
-                    def files = files1*.name.sort()
-                    assert files == ['a-1.0.jar', 'b-1.0.jar', 'leaf-6.jar']
-                    files = files2*.name.sort()
-                    assert files == ['a-1.0.jar', 'c-1.0.jar', 'leaf-5.jar']
-                    files = files3*.name.sort()
-                    assert files == ['b-1.0.jar', 'c-1.0.jar', 'leaf-5.jar']
-                    files = files4*.name.sort()
-                    assert files == ['b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'leaf-8.jar']
-                }
-            }
-        """
-
-        when:
-        run 'checkDeps'
-
-        then:
-        noExceptionThrown()
-    }
-
-    def "conflict resolution on different dependencies are handled separately"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "leaf", "$it").publish()
-            mavenRepo.module("org", "leaf2", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf2", "[3,4]").publish()
-        mavenRepo.module("org", "d", "1.0").dependsOn("org", "leaf2", "[1,7]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0', 'org:d:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[2,6]", "org:leaf:6")
-                }
-                module("org:b:1.0") {
-                    edge("org:leaf:[4,8]", "org:leaf:6")
-                }
-                module("org:c:1.0") {
-                    edge("org:leaf2:[3,4]", "org:leaf2:4")
-                }
-                module("org:d:1.0") {
-                    edge("org:leaf2:[1,7]", "org:leaf2:4")
-                }
-            }
-        }
-    }
-
-    def "previously selected transitive dependency is not used when it becomes orphaned because of selection of a different version of its dependent module"() {
-        given:
-        (1..10).each {
-            def dep = mavenRepo.module('org', 'zdep', "$it").publish()
-            mavenRepo.module("org", "leaf", "$it").dependsOn(dep).publish()
-        }
-        mavenRepo.module("org", "a", "1.0")
-            .dependsOn("org", "c", "1.0")
-            .dependsOn('org', 'leaf', '[1,8]')
-            .publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "c", "1.1").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[3,4]").publish()
-        mavenRepo.module("org", "c", "1.1").dependsOn("org", "leaf", "[4,6]").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1.0") {
-                    edge("org:leaf:[1,8]", "org:leaf:6") {
-                        byReason("didn't match versions 10, 9")
-                    }
-                    edge("org:c:1.0", "org:c:1.1") {
-                        edge("org:leaf:[4,6]", "org:leaf:6") {
-                            byReason("didn't match versions 10, 9, 8, 7")
-                            module("org:zdep:6")
-                        }
-                    }
-                }
-                module("org:b:1.0") {
-                    module("org:c:1.1")
-                }
-            }
-        }
-    }
-
-    def "evicted version removes range constraint from transitive dependency"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "e", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "e", "[3,6]").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "e", "[1,10]").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "a", "2.0").publish()
-        mavenRepo.module("org", "a", "2.0").dependsOn("org", "e", "[4,8]").publish()
-
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:a:1.0", "org:a:2.0") {
-                    edge("org:e:[4,8]", "org:e:8")
-                }
-                module("org:b:1.0") {
-                    edge("org:e:[1,10]", "org:e:8")
-                }
-                module("org:c:1.0") {
-                    module("org:a:2.0")
-                }
-            }
-        }
-    }
-
-    def "orphan node can be re-selected later with a non short-circuiting selector"() {
-        given:
-        (1..10).each {
-            mavenRepo.module("org", "e", "$it").publish()
-        }
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "e", "10").publish()
-        mavenRepo.module("org", "b", "1.0").dependsOn("org", "a", "2.0").publish()
-        mavenRepo.module("org", "a", "2.0").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn("org", "d", "1.0").publish()
-        mavenRepo.module("org", "d", "1.0").dependsOn("org", "e", "latest.release").publish()
-
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:a:1.0", "org:a:2.0")
-                module("org:b:1.0") {
-                    module("org:a:2.0")
-                }
-                module("org:c:1.0") {
-                    module("org:d:1.0") {
-                        edge("org:e:latest.release", "org:e:10")
-                    }
-                }
-            }
-        }
-    }
-
-    def "presence of evicted and orphan node for a module do not fail selection"() {
-        given:
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "b", "1.0").publish()
-        mavenRepo.module("org", "b", "1.0").publish()
-        mavenRepo.module("org", "b", "2.0").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn('org', 'b', '2.0').publish()
-        mavenRepo.module("org", "d", "1.0").dependsOn("org", 'e', '1.0').publish()
-        mavenRepo.module("org", "e", "1.0").dependsOn("org", 'a', '2.0').dependsOn('org', 'c', '1.0').publish()
-        mavenRepo.module("org", "a", "2.0").dependsOn("org", 'c', '2.0').publish()
-        mavenRepo.module('org', 'c', '2.0').publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:c:1.0', 'org:d:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:a:1.0", "org:a:2.0") {
-                    module("org:c:2.0")
-                }
-                edge("org:c:1.0", "org:c:2.0")
-                module("org:d:1.0") {
-                    module("org:e:1.0") {
-                        module("org:a:2.0")
-                        edge("org:c:1.0", "org:c:2.0")
-                    }
-                }
-            }
-        }
-    }
-
-    def "can have a dependency on evicted node"() {
-        given:
-        mavenRepo.module("org", "a", "1.0").dependsOn("org", "b", "1.0").publish()
-        mavenRepo.module("org", "b", "1.0").publish()
-        mavenRepo.module("org", "c", "1.0").dependsOn('org', 'a', '1.0').publish()
-        mavenRepo.module('org', 'd', '1.0').dependsOn('org', 'a', '2.0').publish()
-        mavenRepo.module('org', 'a', '2.0').publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1.0', 'org:c:1.0', 'org:d:1.0'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:a:1.0", "org:a:2.0")
-                module("org:c:1.0") {
-                    edge("org:a:1.0", "org:a:2.0")
-                }
-                module("org:d:1.0") {
-                    module("org:a:2.0")
-                }
-            }
-        }
-    }
-
-    def "evicted hard dependency shouldn't add constraint on range"() {
-        given:
-        4.times { mavenRepo.module("org", "e", "${it + 1}").publish() }
-        4.times { mavenRepo.module("org", "a", "${it + 1}").dependsOn('org', 'e', "${it + 1}").publish() }
-        mavenRepo.module("org", "b", "1").dependsOn('org', 'a', '4').publish() // this will be evicted
-        mavenRepo.module('org', 'c', '1').dependsOn('org', 'd', '1').publish()
-        mavenRepo.module('org', 'd', '1').dependsOn('org', 'b', '2').publish()
-        mavenRepo.module('org', 'b', '2').publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:[1,3]', 'org:b:1', 'org:c:1'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:a:[1,3]", "org:a:3") {
-                    byReason("didn't match version 4")
-                    module("org:e:3")
-                }
-                edge("org:b:1", "org:b:2")
-                module("org:c:1") {
-                    module("org:d:1") {
-                        module("org:b:2")
-                    }
-                }
-            }
-        }
-    }
-
-    def "evicted hard dependency shouldn't add constraint on version"() {
-        given:
-        mavenRepo.module("org", "a", "1").publish()
-        mavenRepo.module("org", "a", "4").publish()
-        mavenRepo.module("org", "b", "1").dependsOn('org', 'a', '4').publish() // this will be evicted
-        mavenRepo.module('org', 'b', '2').publish()
-        mavenRepo.module('org', 'c', '1').dependsOn('org', 'd', '1').publish()
-        mavenRepo.module('org', 'd', '1').dependsOn('org', 'b', '2').publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1', 'org:b:1', 'org:c:1'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module("org:a:1")
-                edge("org:b:1", "org:b:2")
-                module("org:c:1") {
-                    module("org:d:1") {
-                        module("org:b:2")
-                    }
-                }
-            }
-        }
-    }
-
-    def "doesn't include evicted version from branch which has been deselected"() {
-        given:
-        mavenRepo.module('org', 'a', '1').dependsOn('org', 'b', '2').publish()
-        mavenRepo.module('org', 'b', '1').publish()
-        mavenRepo.module('org', 'b', '2').publish()
-        mavenRepo.module('org', 'c', '1').dependsOn('org', 'a', '2').publish()
-        mavenRepo.module('org', 'a', '2').publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                compile
-            }
-            dependencies {
-                compile 'org:a:1', 'org:b:1', 'org:c:1'
-            }
-        """
-        resolve.prepare()
-
-        when:
-        run("checkDeps")
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                edge("org:a:1", "org:a:2")
-                module("org:b:1")
-                module("org:c:1") {
-                    module("org:a:2")
-                }
-            }
-        }
-    }
-
-    def 'order of dependency declaration does not effect transitive dependency versions'() {
-        given:
-        def foo11 = mavenRepo.module('org', 'foo', '1.1').publish()
-        def foo12 = mavenRepo.module('org', 'foo', '1.2').publish()
-        def baz11 = mavenRepo.module('org', 'baz', '1.1').dependsOn(foo11).publish()
-        mavenRepo.module('org', 'baz', '1.2').dependsOn(foo12).publish()
-        mavenRepo.module('org', 'bar', '1.1').dependsOn(baz11).publish()
-
-        ResolveTestFixture resolve = new ResolveTestFixture(buildFile, "conf").expectDefaultConfiguration("runtime")
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                conf
-            }
-            dependencies {
-                if ($barFirst) {
-                    conf 'org:bar:1.1' // WORKS IF THIS DEPENDENCY IS FIRST
-                }
-                conf 'org:baz:[1.0,2.0)'
-                if (!$barFirst) {
-                    conf 'org:bar:1.1' // FAILED IF HERE
-                }
-                conf 'org:foo:[1.0,2.0)'
-            }
-"""
-        resolve.prepare()
-
-        when:
-        run 'dependencies', 'checkDeps'
-
-        then:
-        resolve.expectGraph {
-            root(":", ":test:") {
-                module('org:bar:1.1') {
-                    module('org:baz:1.1') {
-                        module('org:foo:1.1')
-                    }
-                }
-                edge("org:foo:[1.0,2.0)", 'org:foo:1.1')
-                edge('org:baz:[1.0,2.0)', 'org:baz:1.1')
-            }
-        }
-
-        where:
-        barFirst << [false, true]
-    }
-
-    @Issue("gradle/gradle-private#1268")
-    def "shouldn't fail if root component is also added through cycle, and that failOnVersionConflict() is used"() {
-        settingsFile << """
-            include "testlib", "common"
-        """
-
-        buildFile << """
-            subprojects {
-                apply plugin: 'java-library'
-                configurations.all {
-                   resolutionStrategy.failOnVersionConflict()
-                }
-            }
-        """
-
-        file("testlib/build.gradle") << """
-            dependencies {
-                api project(':common') // cycle causes resolution to fail, but shouldn't
-            }
-        """
-
-        file("common/build.gradle") << """
-            dependencies {
-                testImplementation project(':testlib')
-            }
-        """
-
-        when:
-        run 'common:dependencies', '--configuration', 'testCompileClasspath'
-
-        then:
-        noExceptionThrown()
-    }
-
-    @Issue("gradle/gradle#6403")
-    def "shouldn't fail when forcing a dynamic version in resolution strategy"() {
-
-        given:
-        mavenRepo.module("org", "moduleA", "1.1").publish()
-
-        buildFile << """
-            repositories {
-                maven { url "${mavenRepo.uri}" }
-            }
-            configurations {
-                conf {
-                   resolutionStrategy {
-                      force "org:moduleA:1.+"
-                      failOnVersionConflict()
-                   }
-                }
-            }
-
-            dependencies {
-               conf("org:moduleA:1.+")
-               conf("org:moduleA:1.1")
-            }
-        """
-
-        when:
-        run 'dependencies', '--configuration', 'conf'
-
-        then:
-        noExceptionThrown()
-
-
-    }
-
-    def 'optional dependency marked as no longer pending reverts to pending if hard edge disappears (remover has constraint: #dependsOptional, root has constraint: #constraintsOptional)'() {
-        given:
-        def optional = mavenRepo.module('org', 'optional', '1.0').publish()
-        def main = mavenRepo.module('org', 'main', '1.0').dependsOn(optional, optional: true).publish()
-        mavenRepo.module('org.a', 'root', '1.0').dependsOn(main).dependsOn(optional).publish()
-        def root11 = mavenRepo.module('org.a', 'root', '1.1').dependsOn(main).publish()
-        def bom = mavenRepo.module("org", "bom", "1.0")
-        bom.hasPackaging('pom')
-        bom.dependencyConstraint(root11)
-        if (dependsOptional) {
-            bom.dependencyConstraint(optional)
-        }
-        bom.publish()
-
-        buildFile << """
-apply plugin: 'java'
-
-repositories {
-    maven {
-        name 'repo'
-        url '${mavenRepo.uri}'
-    }
-}
-
-dependencies {
-    implementation 'org.a:root'
-    implementation platform('org:bom:1.0')
-    constraints {
-        implementation 'org.a:root:1.0'
-        if ($constraintsOptional) {
-            implementation 'org:optional:1.0'
-        }
-    }
-}
-"""
-        when:
-        succeeds 'dependencies', '--configuration', 'compileClasspath'
-
-        then:
-        outputDoesNotContain('org:optional')
-
-        where:
-        dependsOptional | constraintsOptional
-        true            | true
-        true            | false
-        false           | true
-        false           | false
-    }
-
-    @Issue("gradle/gradle#8944")
-    def 'verify that cleaning up constraints no longer causes a ConcurrentModificationException'() {
-        given:
-        // Direct dependency with transitive to be substituted by project
-        def project = mavenRepo.module('org', 'project', '1.0')
-        mavenRepo.module('org', 'direct', '1.0').dependsOn(project).publish()
-
-        // Updated version no longer depends on project
-        def updated = mavenRepo.module('org', 'direct', '1.1').publish()
-
-        // Chain of deps to make sure upgrade happens after substituting and finding deps
-        def b = mavenRepo.module('org', 'b', '1.0').dependsOn(updated).publish()
-        mavenRepo.module('org', 'a', '1.0').dependsOn(b).publish()
-
-        mavenRepo.module('org', 'lib', '1.0').publish()
-        mavenRepo.module('org', 'other', '1.0').publish()
-
-        settingsFile << """
-include 'sub'
-"""
-
-        buildFile << """
-apply plugin: 'java'
-
-repositories {
-    maven {
-        name 'repo'
-        url '${mavenRepo.uri}'
-    }
-}
-
-configurations.all {
-    resolutionStrategy.dependencySubstitution {
-        substitute module('org:project') using project(':sub')
-    }
-}
-
-dependencies {
-    implementation 'org:direct:1.0'
-    implementation 'org:a:1.0'
-}
-
-project(':sub') {
-    apply plugin: 'java'
-
-    group = 'org'
-    version = '1.0'
-
-    dependencies {
-        constraints {
-            implementation 'org:lib:1.0'
-        }
-
-        implementation 'org:lib'
-        implementation 'org:other:1.0'
-    }
-}
-"""
-        expect:
-        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
-    }
-
-    @Issue("gradle/gradle#11844")
-    def 'does not fail serialization in recursive error case'() {
-        // org:lib:1.0 -> org:between:1.0 -> org:lib:1.1
-        //
-        //  - org:lib:1.1 is selected
-        //  - removes org:between:1.0
-        //  - org:lib:1.1 stays selected (because of cycle), still internal state is updated partially and org:lib:1.1 selector is removed in some places
-
-        given:
-        def libUpdated = mavenRepo.module('org', 'lib', '1.1')
-        // depend on newer version of lib (libUpdated) that is not published, the internal state is broken and deserialization expects 1.1 to exist
-        def between = mavenRepo.module('org', 'between', '1.0').dependsOn(libUpdated).publish()
-        mavenRepo.module('org', 'lib', '1.0').dependsOn(between).publish()
-
-        buildFile << """
-            apply plugin: 'java-library'
-
-            repositories {
-                maven {
-                    url '${mavenRepo.uri}'
-                }
-            }
-
-            dependencies {
-                implementation 'org:lib:1.0'
-            }
-        """
-        expect:
-        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
-    }
-
-    def 'local cycle between dependencies does not causes a ConcurrentModificationException during selector removal'() {
-        given:
-        def lib2 = mavenRepo.module('org', 'lib', '2.0').publish()
-        def lib3 = mavenRepo.module('org', 'lib', '3.0').publish()
-        def lib1 = mavenRepo.module('org', 'lib', '1.0')
-        // recursive dependencies between different versions of 'lib'
-            .dependencyConstraint(lib3).dependencyConstraint(lib2).withModuleMetadata().publish()
-
-        mavenRepo.module('org', 'direct', '1.0').dependsOn(lib1).publish()
-
-        def directUpdated = mavenRepo.module('org', 'direct', '1.1').publish()
-        def b = mavenRepo.module('org', 'b', '1.0').dependsOn(directUpdated).publish()
-        mavenRepo.module('org', 'a', '1.0').dependsOn(b).publish()
-
-        buildFile << """
-            apply plugin: 'java-library'
-
-            repositories {
-                maven {
-                    url '${mavenRepo.uri}'
-                }
-            }
-
-            dependencies {
-                implementation 'org:direct:1.0'  // dependeincy on 'lib'
-                implementation 'org:a:1.0'       // updates direct (to remove dependency on 'lib' again)
-            }
-        """
-
-        expect:
-        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
-    }
-
-    def 'local cycle between dependencies does not causes a ConcurrentModificationException during selector removal with strict version endorsement'() {
-        given:
-        def direct11 = mavenRepo.module('org', 'direct', '1.1')
-        def betweenLibAndDirect = mavenRepo.module('org', 'betweenLibAndDirect', '1.0').dependsOn(direct11)
-        def lib2 = mavenRepo.module('org', 'lib', '2.0').publish()
-        def lib1 = mavenRepo.module('org', 'lib', '1.0').dependsOn(betweenLibAndDirect)
-        // recursive dependencies between different versions of 'lib'
-            .dependencyConstraint(lib2).withModuleMetadata().publish()
-        def lib05 = mavenRepo.module('org', 'lib', '0.5').publish()
-
-        mavenRepo.module('org', 'direct', '1.0')
-        // endorsing dependency that will cause a reselection of the parent again causing a reselection of other children
-        // ("looping back" if a another child is the same as the one that was endorsed)
-            .dependsOn([endorseStrictVersions: true], lib1).dependsOn(lib05).dependsOn(lib1).withModuleMetadata().publish()
-
-        buildFile << """
-            apply plugin: 'java-library'
-
-            repositories {
-                maven {
-                    url '${mavenRepo.uri}'
-                }
-            }
-
-            dependencies {
-                implementation 'org:direct:1.0'  // dependency on 'lib' which will istself update 'direct'
-            }
-        """
-
-        expect:
-        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
-    }
-
-    def 'local cycle between dependencies does not causes a ConcurrentModificationException during selector removal with multiple strict version endorsements'() {
-        given:
-        def foo2 = mavenRepo.module('org', 'foo', '2.0').publish()
-        def foo1 = mavenRepo.module('org', 'foo', '1.0')
-            .dependencyConstraint(foo2).withModuleMetadata().publish()
-        def foo05 = mavenRepo.module('org', 'foo', '0.5').publish()
-
-        def direct11 = mavenRepo.module('org', 'direct', '1.1')
-        def betweenLibAndDirect = mavenRepo.module('org', 'betweenLibAndDirect', '1.0').dependsOn(direct11)
-        def lib2 = mavenRepo.module('org', 'lib', '2.0').publish()
-        def lib1 = mavenRepo.module('org', 'lib', '1.0').dependsOn(betweenLibAndDirect)
-        // recursive dependencies between different versions of 'lib'
-            .dependencyConstraint(lib2).withModuleMetadata().publish()
-        def lib05 = mavenRepo.module('org', 'lib', '0.5').publish()
-
-        mavenRepo.module('org', 'direct', '1.0')
-        // endorsing dependency that will cause a reselection of the parent again causing a reselection of other children
-        // ("looping back" if a another child is the same as the one that was endorsed)
-            .dependsOn([endorseStrictVersions: true], lib1)
-            .dependsOn([endorseStrictVersions: true], foo1).dependsOn(lib05).dependsOn(lib1).dependsOn([endorseStrictVersions: true], foo05).withModuleMetadata().publish()
-
-        buildFile << """
-            apply plugin: 'java-library'
-
-            repositories {
-                maven {
-                    url '${mavenRepo.uri}'
-                }
-            }
-
-            dependencies {
-                implementation 'org:direct:1.0'  // dependency on 'lib' which will istself update 'direct'
-            }
-        """
-
-        expect:
-        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
-    }
-
-    def "can resolve a graph with an obvious version cycle by breaking the cycle"() {
-        given:
-        def direct2 = mavenRepo.module('org', 'direct', '2.0').publish()
-        def trans = mavenRepo.module('org', 'transitive', '1.0').dependsOn(direct2).publish()
-        mavenRepo.module('org', 'direct', '1.0').dependsOn(trans).publish()
-
-        buildFile << """
-repositories {
-    maven {
-        name 'repo'
-        url '${mavenRepo.uri}'
-    }
-}
-
-configurations {
-    conf
-}
-
-dependencies {
-    conf "org:direct:1.0"
-}
-"""
-        expect:
-        succeeds 'dependencies', '--configuration', 'conf'
-    }
-
-    def "can resolve a graph with a local cycle caused by module replacement"() {
-        given:
-        def child1 = mavenRepo.module('org', 'child1', '1.0').publish()
-        def child2 = mavenRepo.module('org', 'child2', '1.0').publish()
-        mavenRepo.module('org', 'direct', '1.0').dependsOn(child1).dependsOn(child2).publish()
-
-        buildFile << """
-            repositories {
-                maven {
-                    name 'repo'
-                    url '${mavenRepo.uri}'
-                }
-            }
-
-            configurations {
-                conf
-            }
-
-            dependencies {
-                modules {
-                    module("org:child1") {
-                        replacedBy("org:direct")
-                    }
-                }
-                conf "org:direct:1.0"
-            }
-        """
-
-        expect:
-        succeeds 'dependencies', '--configuration', 'conf'
-    }
-
-    def 'does not cache node dependencies when node is deselected then reselected with different exclude filter'() {
-        given:
-        // Excluded module
-        def excluded = mavenRepo.module('org.test', 'excluded', '1.0').publish()
-
-        // Intermediates
-        def intermediate10 = mavenRepo.module('org.test', 'intermediate1', '1.0').dependsOn(excluded).publish()
-        def intermediate20 = mavenRepo.module('org.test', 'intermediate1', '2.0').dependsOn(excluded).publish()
-        def intermediate2 = mavenRepo.module('org.test', 'intermediate2', '1.0').dependsOn(intermediate10).publish()
-        def intermediate3 = mavenRepo.module('org.test', 'intermediate3', '1.0').dependsOn(intermediate2).publish()
-
-        // Aligned modules
-        def firstAligned = mavenRepo.module('org.aligned', 'aligned1', '1.0').dependsOn(intermediate20).publish()
-        mavenRepo.module('org.aligned', 'aligned1', '2.0').dependsOn(intermediate20).publish()
-        def otherAligned = mavenRepo.module('org.aligned', 'aligned2', '2.0').publish()
-
-        // Roots
-        mavenRepo.module('org.test', 'excludingRoot', '1.0').dependsOn(firstAligned, exclusions: [[group: 'org.test', module: 'excluded']]).publish()
-        mavenRepo.module('org.test', 'root2', '1.0').dependsOn(intermediate3).publish()
-        mavenRepo.module('org.test', 'root3', '1.0').dependsOn(otherAligned).publish()
-
-        buildFile << """
-            repositories {
-                maven {
-                    name 'repo'
-                    url '${mavenRepo.uri}'
-                }
-            }
-
-            configurations {
-                conf
-            }
-
-            dependencies {
-                conf 'org.test:excludingRoot:1.0'
-                conf 'org.test:root2:1.0'
-                conf 'org.test:root3:1.0'
-
-                components.all(AlignGroup.class)
-            }
-
-            class AlignGroup implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext ctx) {
-                    ctx.details.with { it ->
-                        if (it.getId().getGroup().startsWith("org.aligned")) {
-                            it.belongsTo("org.aligned:platform:\${it.getId().getVersion()}")
-                        }
-                    }
-                }
-            }
-"""
-        when:
-        succeeds 'dependencies', '--configuration', 'conf'
-
-        then:
-        outputContains('excluded')
-    }
-
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolvePairIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolvePairIntegrationTest.groovy
deleted file mode 100644
index 177f2cf..0000000
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolvePairIntegrationTest.groovy
+++ /dev/null
@@ -1,42 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
-import spock.lang.IgnoreIf
-
-@IgnoreIf({
-    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
-    // infrastructure to simulate everything done here, so we're only going to execute this test in
-    // embedded mode
-    !GradleContextualExecuter.embedded
-})
-class VersionRangeResolvePairIntegrationTest extends AbstractVersionRangeResolveIntegrationTest {
-    def "resolve pair #permutation"() {
-        given:
-        def candidates = permutation.candidates
-        def expectedSingle = permutation.expectedSingle
-        def expectedMulti = permutation.expectedMulti
-
-        expect:
-        checkScenarioResolution(expectedSingle, expectedMulti, candidates)
-
-        where:
-        permutation << VersionRangeResolveTestScenarios.SCENARIOS_TWO_DEPENDENCIES
-    }
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolvePreferPairIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolvePreferPairIntegrationTest.groovy
deleted file mode 100644
index 4667e2d..0000000
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolvePreferPairIntegrationTest.groovy
+++ /dev/null
@@ -1,42 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
-import spock.lang.IgnoreIf
-
-@IgnoreIf({
-    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
-    // infrastructure to simulate everything done here, so we're only going to execute this test in
-    // embedded mode
-    !GradleContextualExecuter.embedded
-})
-class VersionRangeResolvePreferPairIntegrationTest extends AbstractVersionRangeResolveIntegrationTest {
-    def "resolve prefer pair #permutation"() {
-        given:
-        def candidates = permutation.candidates
-        def expectedSingle = permutation.expectedSingle
-        def expectedMulti = permutation.expectedMulti
-
-        expect:
-        checkScenarioResolution(expectedSingle, expectedMulti, candidates)
-
-        where:
-        permutation << VersionRangeResolveTestScenarios.SCENARIOS_PREFER
-    }
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolveRejectPairIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolveRejectPairIntegrationTest.groovy
deleted file mode 100644
index 7358565..0000000
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/VersionRangeResolveRejectPairIntegrationTest.groovy
+++ /dev/null
@@ -1,42 +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.integtests.resolve
-
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
-import spock.lang.IgnoreIf
-
-@IgnoreIf({
-    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
-    // infrastructure to simulate everything done here, so we're only going to execute this test in
-    // embedded mode
-    !GradleContextualExecuter.embedded
-})
-class VersionRangeResolveRejectPairIntegrationTest extends AbstractVersionRangeResolveIntegrationTest {
-    def "resolve reject pair #permutation"() {
-        given:
-        def candidates = permutation.candidates
-        def expectedSingle = permutation.expectedSingle
-        def expectedMulti = permutation.expectedMulti
-
-        expect:
-        checkScenarioResolution(expectedSingle, expectedMulti, candidates)
-
-        where:
-        permutation << VersionRangeResolveTestScenarios.SCENARIOS_DEPENDENCY_WITH_REJECT
-    }
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/AlignmentIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/AlignmentIntegrationTest.groovy
index 2510265..b118c9d 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/AlignmentIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/AlignmentIntegrationTest.groovy
@@ -53,9 +53,14 @@
             root(":", ":test:") {
                 edge("org:xml:1.0", "org:xml:1.1") {
                     byConstraint("belongs to platform org:platform:1.1")
-                    module('org:core:1.1')
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
                     module('org:core:1.1')
                 }
             }
@@ -100,13 +105,19 @@
             root(":", ":test:") {
                 edge("org:xml:1.0", "org:xml:1.1") {
                     byConstraint("belongs to platform org:platform:1.1")
-                    module('org:core:1.1')
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
                     module('org:core:1.1')
                 }
                 module("outside:module:1.0") {
-                    edge('org:core:1.0', 'org:core:1.1').byConflictResolution("between versions 1.1 and 1.0")
+                    byConstraint("belongs to platform outside:platform:1.0")
+                    edge('org:core:1.0', 'org:core:1.1')
                 }
             }
         }
@@ -146,10 +157,15 @@
             root(":", ":test:") {
                 edge("org:xml:1.0", "org:xml:1.1") {
                     byConstraint("belongs to platform org:platform:1.1")
+                    byConflictResolution("between versions 1.1 and 1.0")
                 }
                 edge("org:json:1.0", "org:json:1.1") {
                     byConstraint("belongs to platform org:platform:1.1")
-                    module('org:core:1.1')
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module('org:core:1.1')
             }
@@ -185,11 +201,14 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module("org:xml:1.0") {
-                    edge('org:core:1.0', 'org:core:1.1')
-                        .byConflictResolution("between versions 1.1 and 1.0")
-                        .byConstraint("belongs to platform org:platform:1.1")
+                    byConstraint("belongs to platform org:platform:1.1")
+                    edge('org:core:1.0', 'org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
                     module('org:core:1.1')
                 }
             }
@@ -226,10 +245,14 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge("org:xml:1.0", "org:xml:1.1") {
-                    module('org:core:1.0')
                     byConstraint("belongs to platform org:platform:1.1")
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org:core:1.0') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                    }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
                     module('org:core:1.0')
                 }
             }
@@ -276,12 +299,22 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                module('org:core:2.9.4')
-                edge('org:databind:2.7.9', 'org:databind:2.9.4').byConstraint("belongs to platform org:platform:2.9.4.1")
+                module('org:core:2.9.4') {
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
+                }
+                edge('org:databind:2.7.9', 'org:databind:2.9.4') {
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
+                }
                 module('org:kt:2.9.4.1') {
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
                     module('org:databind:2.9.4') {
-                        module('org:core:2.9.4').byConstraint("belongs to platform org:platform:2.9.4.1")
-                        edge('org:annotations:2.9.0', 'org:annotations:2.9.4').byConstraint("belongs to platform org:platform:2.9.4.1")
+                        module('org:core:2.9.4')
+                        edge('org:annotations:2.9.0', 'org:annotations:2.9.4') {
+                            byConstraint("belongs to platform org:platform:2.9.4.1")
+                            byConflictResolution("between versions 2.9.4 and 2.9.0")
+                        }
                     }
                 }
             }
@@ -326,12 +359,22 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                module('org:core:2.9.4').byConstraint("belongs to platform org:platform:2.9.4.1")
-                edge('org:databind:2.7.9', 'org:databind:2.9.4').byConstraint("belongs to platform org:platform:2.9.4.1")
+                module('org:core:2.9.4') {
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
+                }
+                edge('org:databind:2.7.9', 'org:databind:2.9.4') {
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
+                }
                 module('org:kt:2.9.4.1') {
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
                     module('org:databind:2.9.4') {
                         module('org:core:2.9.4').byConstraint("belongs to platform org:platform:2.9.4.1")
-                        edge('org:annotations:2.9.0', 'org:annotations:2.9.4').byConstraint("belongs to platform org:platform:2.9.4.1")
+                        edge('org:annotations:2.9.0', 'org:annotations:2.9.4') {
+                            byConstraint("belongs to platform org:platform:2.9.4.1")
+                            byConflictResolution("between versions 2.9.4 and 2.9.0")
+                        }
                     }
                 }
             }
@@ -420,9 +463,15 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module('org:core:2.9.4') {
-                    edge("org:platform:2.9.4", "org:platform:2.9.4.1")
+                    byConstraint()
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
+                    edge("org:platform:2.9.4", "org:platform:2.9.4.1") {
+                        byConflictResolution("between versions 2.9.4.1 and 2.9.4")
+                    }
                 }
                 edge('org:databind:2.7.9', 'org:databind:2.9.4') {
+                    byConstraint()
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
                     edge("org:platform:2.9.4", "org:platform:2.9.4.1")
                 }
                 module('org:kt:2.9.4.1') {
@@ -436,6 +485,8 @@
                     module('org:databind:2.9.4') {
                         module('org:core:2.9.4')
                         edge('org:annotations:2.9.0', 'org:annotations:2.9.4') {
+                            byConstraint()
+                            byConflictResolution("between versions 2.9.4 and 2.9.0")
                             edge("org:platform:2.9.4", "org:platform:2.9.4.1")
                         }
                     }
@@ -486,17 +537,27 @@
             root(":", ":test:") {
                 edge("org:xml:1.0", "org:xml:1.1") {
                     byConstraint("belongs to platform org:platform:1.1")
-                    module('org:core:1.1')
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
                     module('org:core:1.1')
                 }
 
                 edge("org2:xml:1.0", "org2:xml:1.1") {
                     byConstraint("belongs to platform org2:platform:1.1")
-                    module('org2:core:1.1')
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org2:core:1.1') {
+                        byConstraint("belongs to platform org2:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module("org2:json:1.1") {
+                    byConstraint("belongs to platform org2:platform:1.1")
                     module('org2:core:1.1')
                 }
             }
@@ -542,7 +603,10 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module('org:xml:1.0') {
-                    module('org:core:1.0')
+                    byConstraint("belongs to platform org:platform:1.0")
+                    module('org:core:1.0') {
+                        byConstraint("belongs to platform org:platform:1.0")
+                    }
                 }
                 module('org2:foo:1.0') {
                     edge('org4:a:1.0', 'org4:a:1.1') {
@@ -611,12 +675,13 @@
             root(":", ":test:") {
                 edge("org:xml:1.0", "org:xml:1.0") {
                     byConstraint("belongs to platform org:platform:1.1")
-                    // byReason("version 1.1 is buggy") // TODO CC: uncomment when we collect rejection from component selection rule
                     edge('org:core:1.0', 'org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
                         byConflictResolution("between versions 1.1 and 1.0")
                     }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
                     module('org:core:1.1')
                 }
             }
@@ -657,9 +722,16 @@
                 edge("org:xml:1.0", "org:xml:1.1") {
                     byConstraint("belongs to platform org:platform:1.1")
                     byConstraint("belongs to platform org:platform2:1.1")
-                    module('org:core:1.1')
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                        byConstraint("belongs to platform org:platform2:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
+                    byConstraint("belongs to platform org:platform2:1.1")
                     module('org:core:1.1')
                 }
             }
@@ -741,8 +813,13 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge("org.apache.groovy:xml:2.4", "org.apache.groovy:xml:2.5") {
+                    byConstraint()
+                    byConflictResolution("between versions 2.5 and 2.4")
                     module("org.apache.groovy:core:2.5") {
+                        byConstraint()
+                        byConflictResolution("between versions 2.5 and 2.4")
                         module("org.apache.groovy:platform:2.5") {
+                            byConflictResolution("between versions 2.5 and 2.4")
                             noArtifacts()
                             module("org.apache.groovy:platform:2.5")          // The way the rule is defined, it is applied to the platform itself.
                             module("org.springframework:spring-platform:1.0") // This is not good practice, but we keep this to describe the current behavior.
@@ -760,11 +837,13 @@
                     module("org.springframework:spring-platform:1.0")
                 }
                 module("org.apache.groovy:json:2.5") {
+                    byConstraint()
                     module("org.apache.groovy:core:2.5")
                     module("org.apache.groovy:platform:2.5")
                     module("org.springframework:spring-platform:1.0")
                 }
                 edge("org.springframework:core:1.0", "org.springframework:core:1.1") {
+                    byConstraint()
                     byConflictResolution("between versions 1.1 and 1.0")
                 }
             }
@@ -838,7 +917,12 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge("org.apache.groovy:xml:2.4", "org.apache.groovy:xml:2.5") {
+                    byConstraint("belongs to platform org.apache.groovy:platform:2.5")
+                    byConflictResolution("between versions 2.5 and 2.4")
                     module("org.apache.groovy:core:2.5") {
+                        byConstraint()
+                        byConstraint("belongs to platform org.apache.groovy:platform:2.5")
+                        byConflictResolution("between versions 2.5 and 2.4")
                         module("org.springframework:spring-platform:1.0") {
                             noArtifacts()
                             constraint("org.apache.groovy:core:2.4", "org.apache.groovy:core:2.5")
@@ -848,10 +932,12 @@
                     module("org.springframework:spring-platform:1.0")
                 }
                 module("org.apache.groovy:json:2.5") {
+                    byConstraint("belongs to platform org.apache.groovy:platform:2.5")
                     module("org.apache.groovy:core:2.5")
                     module("org.springframework:spring-platform:1.0")
                 }
                 edge("org.springframework:core:1.0", "org.springframework:core:1.1") {
+                    byConstraint()
                     byConflictResolution("between versions 1.1 and 1.0")
                 }
             }
@@ -896,9 +982,14 @@
             root(":", ":test:") {
                 edge("org:xml:1.0", "org:xml:1.1") {
                     byConstraint("belongs to platform org:platform:1.1")
-                    module('org:core:1.1')
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
                     module('org:core:1.1')
                 }
             }
@@ -913,9 +1004,14 @@
             root(":", ":test:") {
                 edge("org:xml:1.0", "org:xml:1.1") {
                     byConstraint("belongs to platform org:platform:1.1")
-                    module('org:core:1.1')
+                    byConflictResolution("between versions 1.1 and 1.0")
+                    module('org:core:1.1') {
+                        byConstraint("belongs to platform org:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 module("org:json:1.1") {
+                    byConstraint("belongs to platform org:platform:1.1")
                     module('org:core:1.1')
                 }
             }
@@ -1019,13 +1115,18 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module('org:core:2.9.4') {
+                    byConstraint()
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
                     edge("org:platform:2.9.4", "org:platform:2.9.4.1")
                 }
                 edge('org:databind:2.7.9', 'org:databind:2.9.4') {
+                    byConstraint()
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
                     edge("org:platform:2.9.4", "org:platform:2.9.4.1")
                 }
                 module('org:kt:2.9.4.1') {
                     module("org:platform:2.9.4.1") {
+                        byConflictResolution("between versions 2.9.4.1 and 2.9.4")
                         noArtifacts()
                         constraint("org:core:2.9.4")
                         constraint("org:databind:2.9.4")
@@ -1035,6 +1136,8 @@
                     module('org:databind:2.9.4') {
                         module('org:core:2.9.4')
                         edge('org:annotations:2.9.0', 'org:annotations:2.9.4') {
+                            byConstraint()
+                            byConflictResolution("between versions 2.9.4 and 2.9.0")
                             edge("org:platform:2.9.4", "org:platform:2.9.4.1")
                         }
                     }
@@ -1051,13 +1154,18 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module('org:core:2.9.4') {
+                    byConstraint()
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
                     edge("org:platform:2.9.4", "org:platform:2.9.4.1")
                 }
                 edge('org:databind:2.7.9', 'org:databind:2.9.4') {
+                    byConstraint()
+                    byConflictResolution("between versions 2.9.4 and 2.7.9")
                     edge("org:platform:2.9.4", "org:platform:2.9.4.1")
                 }
                 module('org:kt:2.9.4.1') {
                     module("org:platform:2.9.4.1") {
+                        byConflictResolution("between versions 2.9.4.1 and 2.9.4")
                         noArtifacts()
                         constraint("org:core:2.9.4")
                         constraint("org:databind:2.9.4")
@@ -1067,6 +1175,8 @@
                     module('org:databind:2.9.4') {
                         module('org:core:2.9.4')
                         edge('org:annotations:2.9.0', 'org:annotations:2.9.4') {
+                            byConstraint()
+                            byConflictResolution("between versions 2.9.4 and 2.9.0")
                             edge("org:platform:2.9.4", "org:platform:2.9.4.1")
                         }
                     }
@@ -1108,7 +1218,10 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module("org:member2:1.1") {
-                    module("org:member1:1.1")
+                    byConstraint("belongs to platform org:platform:1.1")
+                    module("org:member1:1.1") {
+                        byConstraint("belongs to platform org:platform:1.1")
+                    }
                 }
             }
         }
@@ -1146,6 +1259,7 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge("org:foo:1.0", "org:foo:1.1") {
+                    byConflictResolution("between versions 1.1 and 1.0")
                     byConstraint("belongs to platform org:platform:1.1")
                 }
             }
@@ -1195,6 +1309,7 @@
                 module("start:start:1.0") {
                     edge("org:foo:1.0", "org:foo:1.1") {
                         byConstraint("belongs to platform org:platform:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
                         module("org:bar:1.1") {
                             byConstraint("belongs to platform org:platform:1.1")
                         }
@@ -1282,14 +1397,22 @@
                 module("start:start:1.0") {
                     edge("org:foo:1.2", "org:foo:1.5") {
                         byConstraint("belongs to platform org:platform:1.5")
+                        selectedByRule("substitution from 'org:foo:[1.2,)' to 'org:foo:1.0'")
+                        byConflictResolution("between versions 1.0 and 1.5")
                         module("org:bar:1.5") {
+                            selectedByRule("substitution from 'org:bar:[1.2,)' to 'org:bar:1.0'")
+                            byConflictResolution("between versions 1.5 and 1.0")
                             byConstraint("belongs to platform org:platform:1.5")
                         }
                         module("org:baz:1.5") {
                             module("org:fooBar:1.5") {
                                 byConstraint("belongs to platform org:platform:1.5")
+                                selectedByRule("substitution from 'org:fooBar:[1.2,)' to 'org:fooBar:1.0'")
+                                byConflictResolution("between versions 1.5 and 1.0")
                             }
                             byConstraint("belongs to platform org:platform:1.5")
+                            selectedByRule("substitution from 'org:baz:[1.2,)' to 'org:baz:1.0'")
+                            byConflictResolution("between versions 1.5 and 1.0")
                         }
                     }
                 }
@@ -1441,11 +1564,19 @@
                 module("org:a:1.0") {
                     edge('proto:java:1.0', "nebula:java:2.0")
                     module('proto:java-util:1.0') {
+                        byConstraint("belongs to platform aligned-group:proto:1.0")
                         edge('proto:java:1.0', "nebula:java:2.0")
                     }
                 }
-                module('align:first:2.0')
-                module('nebula:java:2.0')
+                module('align:first:2.0') {
+                    byConstraint("belongs to platform aligned-group:align:2.0")
+                    byConflictResolution("between versions 2.0 and 1.0")
+                }
+                module('nebula:java:2.0') {
+                    byConstraint("belongs to platform aligned-group:nebula:2.0")
+                    selectedByRule("proto:java replaced with nebula:java")
+                    byConflictResolution("between versions 2.0 and 1.1")
+                }
                 module('org:b:1.0') {
                     module('org:c:1.0') {
                         module('org:d:1.0') {
@@ -1453,7 +1584,11 @@
                                 edge('nebula:java:1.1', 'nebula:java:2.0')
                             }
                             edge('align:second:1.0', 'align:second:2.0') {
-                                module('align:third:2.0')
+                                byConstraint("belongs to platform aligned-group:align:2.0")
+                                byConflictResolution("between versions 2.0 and 1.0")
+                                module('align:third:2.0') {
+                                    byConstraint("belongs to platform aligned-group:align:2.0")
+                                }
                                 edge('align:first:1.0', 'align:first:2.0')
                                 module('org:f:1.0') {
                                     edge('proto:java:0.5', 'nebula:java:2.0')
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy
index 9f6944b..1e47f44 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingPlatformAlignmentTest.groovy
@@ -89,13 +89,20 @@
             root(":", ":test:") {
                 edge("org:core:2.9.4", "org:core:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                 }
                 module("org:databind:2.7.9") {
-                    module('org:annotations:2.7.9')
+                    forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
+                    module('org:annotations:2.7.9') {
+                        forced()
+                        byConstraint("belongs to platform org:platform:2.7.9")
+                    }
                     module('org:core:2.7.9')
                 }
                 edge("org:kotlin:2.9.4.1", "org:kotlin:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                     module('org:core:2.7.9')
                     module('org:annotations:2.7.9')
                 }
@@ -145,13 +152,21 @@
             root(":", ":test:") {
                 edge("org:core:2.9.4", "org:core:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                 }
                 module("org:databind:2.7.9") {
-                    module('org:annotations:2.7.9')
+                    selectedByRule()
+                    forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
+                    module('org:annotations:2.7.9') {
+                        forced()
+                        byConstraint("belongs to platform org:platform:2.7.9")
+                    }
                     module('org:core:2.7.9')
                 }
                 edge("org:kotlin:2.9.4.1", "org:kotlin:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                     module('org:core:2.7.9')
                     module('org:annotations:2.7.9')
                 }
@@ -398,21 +413,26 @@
             root(":", ":test:") {
                 module("bom:bom:1.0") {
                     configuration = 'platform-runtime'
-                    constraint("org:xml:2.0", "org:xml:1.0")
+                    constraint("org:xml:2.0", "org:xml:1.0") {
+                        forced()
+                        byConstraint()
+                        byConstraint("belongs to platform org:platform:1.0")
+                    }
                     noArtifacts()
                 }
                 module("root:root:1.0") {
                     module('org:webapp:1.0') {
+                        forced()
+                        byConstraint("belongs to platform org:platform:1.0")
                         module('org:xml:1.0')
                     }
                     module('org:other:1.0') {
                         forced()
+                        byConstraint("belongs to platform org:platform:1.0")
                     }
                 }
             }
         }
-
-
     }
 
     def "can force a virtual platform version by forcing the platform itself via a dependency"() {
@@ -444,13 +464,20 @@
             root(":", ":test:") {
                 edge("org:core:2.9.4", "org:core:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                 }
                 edge("org:databind:2.9.4", "org:databind:2.7.9") {
-                    module('org:annotations:2.7.9')
+                    forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
+                    module('org:annotations:2.7.9') {
+                        forced()
+                        byConstraint("belongs to platform org:platform:2.7.9")
+                    }
                     module('org:core:2.7.9')
                 }
                 edge("org:kotlin:2.9.4.1", "org:kotlin:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                     module('org:core:2.7.9')
                     module('org:annotations:2.7.9')
                 }
@@ -495,13 +522,20 @@
             root(":", ":test:") {
                 edge("org:core:2.9.4", "org:core:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                 }
                 module("org:databind:2.7.9") {
-                    module('org:annotations:2.7.9')
+                    forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
+                    module('org:annotations:2.7.9') {
+                        forced()
+                        byConstraint("belongs to platform org:platform:2.7.9")
+                    }
                     module('org:core:2.7.9')
                 }
                 edge("org:kotlin:2.9.4.1", "org:kotlin:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                     module('org:core:2.7.9')
                     module('org:annotations:2.7.9')
                 }
@@ -547,13 +581,20 @@
             root(":", ":test:") {
                 edge("org:core:2.9.4", "org:core:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                 }
                 module("org:databind:2.7.9") {
-                    module('org:annotations:2.7.9')
+                    forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
+                    module('org:annotations:2.7.9') {
+                        forced()
+                        byConstraint("belongs to platform org:platform:2.7.9")
+                    }
                     module('org:core:2.7.9')
                 }
                 edge("org:kotlin:2.9.4.1", "org:kotlin:2.7.9") {
                     forced()
+                    byConstraint("belongs to platform org:platform:2.7.9")
                     module('org:core:2.7.9')
                     module('org:annotations:2.7.9')
                 }
@@ -594,12 +635,22 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org:core:2.7.9", "org:core:2.9.4.1")
+                edge("org:core:2.7.9", "org:core:2.9.4.1") {
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
+                    maybeByConflictResolution()
+                }
                 edge("org:databind:2.7.9", "org:databind:2.9.4.1") {
-                    module('org:annotations:2.9.4.1')
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
+                    byConflictResolution("between versions 2.9.4.1 and 2.7.9")
+                    module('org:annotations:2.9.4.1') {
+                        byConstraint("belongs to platform org:platform:2.9.4.1")
+                        maybeByConflictResolution()
+                    }
                     module('org:core:2.9.4.1')
                 }
                 edge("org:kotlin:2.9.4", "org:kotlin:2.9.4.1") {
+                    byConstraint("belongs to platform org:platform:2.9.4.1")
+                    byConflictResolution("between versions 2.9.4.1 and 2.9.4")
                     module('org:core:2.9.4.1')
                     module('org:annotations:2.9.4.1')
                 }
@@ -651,10 +702,15 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge("org:core:2.9.4", "org:core:2.7.9") {
+                    byConstraint()
                     forced()
                 }
                 module("org:databind:2.7.9") {
+                    byConstraint()
+                    forced()
                     module('org:annotations:2.7.9') {
+                        byConstraint()
+                        forced()
                         module("org:platform:2.7.9")
                     }
                     module('org:core:2.7.9') {
@@ -663,6 +719,7 @@
                     module("org:platform:2.7.9")
                 }
                 edge("org:kotlin:2.9.4.1", "org:kotlin:2.7.9") {
+                    byConstraint()
                     forced()
                     module('org:core:2.7.9')
                     module('org:annotations:2.7.9')
@@ -670,6 +727,7 @@
                 }
                 String expectedVariant = GradleMetadataResolveRunner.isGradleMetadataPublished() ? 'enforcedRuntimeElements' : 'enforced-platform-runtime'
                 edge("org:platform:{strictly 2.7.9}", "org:platform:2.7.9") {
+                    byAncestor()
                     configuration(expectedVariant)
                     constraint('org:core:2.7.9')
                     constraint('org:databind:2.7.9')
@@ -765,10 +823,24 @@
             root(":", ":test:") {
                 module("com.amazonaws:aws-java-sdk-core:1.11.438") {
                     edge("org:cbor:2.6.7", "org:cbor:2.8.10") {
-                        module("org:core:2.8.10")
+                        byConstraint("belongs to platform org:platform:2.8.11.1")
+                        maybeByConflictResolution()
+                        forced()
+                        module("org:core:2.8.10") {
+                            byConstraint("belongs to platform org:platform:2.8.11.1")
+                            maybeByConflictResolution()
+                            forced()
+                        }
                     }
                     edge("org:databind:2.6.7.1", "org:databind:2.8.11.1") {
-                        edge("org:annotations:2.8.0", "org:annotations:2.8.10")
+                        byConstraint("belongs to platform org:platform:2.8.11.1")
+                        maybeByConflictResolution()
+                        forced()
+                        edge("org:annotations:2.8.0", "org:annotations:2.8.10") {
+                            byConstraint("belongs to platform org:platform:2.8.11.1")
+                            byConflictResolution("between versions 2.8.10 and 2.8.0")
+                            forced()
+                        }
                         module("org:core:2.8.10")
                     }
                 }
@@ -820,10 +892,21 @@
             root(":", ":test:") {
                 module("com.amazonaws:aws-java-sdk-core:1.11.438") {
                     module("org:cbor:2.6.7") {
-                        module("org:core:2.6.7")
+                        byConstraint("belongs to platform org:platform:2.6.7.1")
+                        forced()
+                        module("org:core:2.6.7") {
+                            byConstraint("belongs to platform org:platform:2.6.7.1")
+                            forced()
+                        }
                     }
                     edge("org:databind:2.8.0", "org:databind:2.6.7.1") {
-                        edge("org:annotations:2.6.0", "org:annotations:2.6.7")
+                        byConstraint("belongs to platform org:platform:2.6.7.1")
+                        forced()
+                        edge("org:annotations:2.6.0", "org:annotations:2.6.7") {
+                            byConstraint("belongs to platform org:platform:2.6.7.1")
+                            byConflictResolution("between versions 2.6.7 and 2.6.0")
+                            forced()
+                        }
                         module("org:core:2.6.7")
                     }
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingUsingStrictlyPlatformAlignmentTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingUsingStrictlyPlatformAlignmentTest.groovy
index 8a0e503..dc48c1a 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingUsingStrictlyPlatformAlignmentTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/alignment/ForcingUsingStrictlyPlatformAlignmentTest.groovy
@@ -67,12 +67,22 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org:core:2.9.4", "org:core:2.7.9")
+                edge("org:core:2.9.4", "org:core:2.7.9") {
+                    byConstraint('belongs to platform org:platform:2.7.9')
+                    forced()
+                }
                 edge("org:databind:{strictly 2.7.9}", "org:databind:2.7.9") {
-                    module('org:annotations:2.7.9')
+                    byConstraint('belongs to platform org:platform:2.7.9')
+                    forced()
+                    module('org:annotations:2.7.9') {
+                        byConstraint('belongs to platform org:platform:2.7.9')
+                        forced()
+                    }
                     module('org:core:2.7.9')
                 }
                 edge("org:kotlin:2.9.4.1", "org:kotlin:2.7.9") {
+                    byConstraint('belongs to platform org:platform:2.7.9')
+                    forced()
                     module('org:core:2.7.9')
                     module('org:annotations:2.7.9')
                 }
@@ -305,10 +315,24 @@
             root(":", ":test:") {
                 module("com.amazonaws:aws-java-sdk-core:1.11.438") {
                     edge("org:cbor:2.6.7", "org:cbor:2.8.10") {
-                        module("org:core:2.8.10")
+                        byConstraint("belongs to platform org:platform:2.8.11.1")
+                        byConflictResolution("between versions 2.8.10 and 2.6.7")
+                        forced()
+                        module("org:core:2.8.10") {
+                            byConstraint("belongs to platform org:platform:2.8.11.1")
+                            byConflictResolution("between versions 2.8.10 and 2.6.7")
+                            forced()
+                        }
                     }
                     edge("org:databind:2.6.7.1", "org:databind:2.8.11.1") {
-                        edge("org:annotations:2.8.0", "org:annotations:2.8.10")
+                        byConstraint("belongs to platform org:platform:2.8.11.1")
+                        byAncestor()
+                        forced()
+                        edge("org:annotations:2.8.0", "org:annotations:2.8.10") {
+                            byConstraint("belongs to platform org:platform:2.8.11.1")
+                            byConflictResolution("between versions 2.8.10 and 2.8.0")
+                            forced()
+                        }
                         module("org:core:2.8.10")
                     }
                 }
@@ -350,10 +374,22 @@
             root(":", ":test:") {
                 module("com.amazonaws:aws-java-sdk-core:1.11.438") {
                     module("org:cbor:2.6.7") {
-                        module("org:core:2.6.7")
+                        byConstraint("belongs to platform org:platform:2.6.7.1")
+                        forced()
+                        module("org:core:2.6.7") {
+                            byConstraint("belongs to platform org:platform:2.6.7.1")
+                            forced()
+                        }
                     }
                     edge("org:databind:2.8.0", "org:databind:2.6.7.1") {
-                        edge("org:annotations:2.6.0", "org:annotations:2.6.7")
+                        byConstraint("belongs to platform org:platform:2.6.7.1")
+                        byAncestor()
+                        forced()
+                        edge("org:annotations:2.6.0", "org:annotations:2.6.7") {
+                            byConstraint("belongs to platform org:platform:2.6.7.1")
+                            byConflictResolution("between versions 2.6.7 and 2.6.0")
+                            forced()
+                        }
                         module("org:core:2.6.7")
                     }
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/AddingConfigurationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/AddingConfigurationIntegrationTest.groovy
index 0fd1f54..d9cf63c 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/AddingConfigurationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/AddingConfigurationIntegrationTest.groovy
@@ -18,7 +18,6 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
-
 class AddingConfigurationIntegrationTest extends AbstractIntegrationSpec {
     def "can add configurations" () {
         buildFile << """
@@ -84,4 +83,29 @@
         expect:
         succeeds "addConfigs"
     }
+
+    def "can remove and add configurations between resolutions"() {
+        given:
+        mavenRepo.module("org", "foo", "1.0").publish()
+
+        buildFile << """
+            repositories {
+                maven { url '$mavenRepo.uri' }
+            }
+
+            task resolve {
+                def conf = configurations.create("conf")
+                conf.dependencies.add(project.dependencies.create("org:foo:1.0"))
+                conf.files
+                configurations.remove(conf)
+
+                def conf2 = configurations.create("conf2")
+                conf2.dependencies.add(project.dependencies.create("org:foo:1.0"))
+                conf2.files
+            }
+        """
+
+        expect:
+        succeeds("resolve")
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationDefaultsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationDefaultsIntegrationTest.groovy
index aba2289..9616f55 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationDefaultsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationDefaultsIntegrationTest.groovy
@@ -233,6 +233,7 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge("org.test:producer:1.0", ":producer", "org.test:producer:1.0") {
+                    compositeSubstitute()
                     module("org:default-dependency:1.0")
                 }
             }
@@ -280,9 +281,11 @@
     }
 }
 task broken {
+    def child = configurations.child
+    def conf = configurations.conf
     doLast {
-        configurations.child.resolve()
-        configurations.conf.resolve()
+        child.files
+        conf.files
     }
 }
 """
@@ -343,6 +346,7 @@
         succeeds ":check"
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "copied configuration have unique names"() {
         buildFile << """
             configurations {
@@ -362,4 +366,19 @@
         expect:
         succeeds ":check"
     }
+
+    def "configuration getAll is deprecated"() {
+        given:
+        buildFile << """
+            configurations {
+                conf {
+                    getAll()
+                }
+            }
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("Calling the Configuration.getAll() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use the configurations container to access the set of configurations instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_configuration_get_all")
+        succeeds "help"
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationMutationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationMutationIntegrationTest.groovy
index 5374b8f..58f1006 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationMutationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationMutationIntegrationTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.integtests.resolve.api
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 
 class ConfigurationMutationIntegrationTest extends AbstractDependencyResolutionTest {
@@ -178,6 +179,7 @@
         failure.assertHasCause("Bad user code")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "cannot add withDependencies rule after configuration has been used"() {
         when:
         buildFile << """
@@ -328,6 +330,7 @@
         resolve.expectGraph {
             root(":", "org.test:root:1.1") {
                 edge("org.test:producer:1.0", ":producer", "org.test:producer:1.0") {
+                    compositeSubstitute()
                     module("org:explicit-dependency:3.4")
                     module("org:added-dependency:3.4")
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationRoleUsageIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationRoleUsageIntegrationTest.groovy
index d6de3d4..2283f23 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationRoleUsageIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationRoleUsageIntegrationTest.groovy
@@ -17,11 +17,13 @@
 package org.gradle.integtests.resolve.api
 
 
-import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ConfigurationUsageChangingFixture
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
-class ConfigurationRoleUsageIntegrationTest extends AbstractIntegrationSpec {
+class ConfigurationRoleUsageIntegrationTest extends AbstractIntegrationSpec implements ConfigurationUsageChangingFixture {
     // region Roleless (Implicit LEGACY Role) Configurations
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "default usage for roleless configuration is to allow anything"() {
         given:
         buildFile << """
@@ -33,7 +35,7 @@
                 doLast {
                     assert configurations.custom.canBeConsumed
                     assert configurations.custom.canBeResolved
-                    assert configurations.custom.canBeDeclaredAgainst
+                    assert configurations.custom.canBeDeclared
                     assert !configurations.custom.deprecatedForConsumption
                     assert !configurations.custom.deprecatedForResolution
                     assert !configurations.custom.deprecatedForDeclarationAgainst
@@ -52,7 +54,7 @@
                 $configuration {
                     assert canBeConsumed
                     assert canBeResolved
-                    assert canBeDeclaredAgainst
+                    assert canBeDeclared
                     assert !deprecatedForConsumption
                     assert !deprecatedForResolution
                     assert !deprecatedForDeclarationAgainst
@@ -64,10 +66,7 @@
         succeeds 'help'
 
         where:
-        configuration << ConfigurationRoles.values().collect {
-            def name = it.name.replace(' ', '')
-            return name[0].toLowerCase() + name[1..-1]
-        }
+        configuration << ["legacy", "consumable", "resolvable", "resolvableBucket", "consumableBucket", "bucket"]
     }
 
     def "can prevent usage mutation of roleless configurations"() {
@@ -89,16 +88,14 @@
         assertUsageLockedFailure('custom')
     }
 
-    def "can prevent usage mutation of roleless configuration #configuration added by java plugin meant for consumption"() {
+    def "can prevent usage mutation of roleless configuration meant for consumption"() {
         given:
         buildFile << """
-            plugins {
-                id 'java'
-            }
-
             configurations {
-                $configuration {
-                    assert canBeConsumed == true
+                testConf {
+                    assert canBeConsumed
+                    canBeResolved = false
+                    canBeDeclared = false
                     preventUsageMutation()
                     canBeConsumed = false
                 }
@@ -109,10 +106,56 @@
         fails 'help'
 
         and:
-        assertUsageLockedFailure(configuration)
+        assertUsageLockedFailure('testConf')
+    }
 
-        where:
-        configuration << ['runtimeElements', 'apiElements']
+    def "can add declaration alternatives to configuration deprecated for declaration"() {
+        given:
+        buildFile << """
+            configurations {
+                createWithRole("testConf", org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE) {
+                    addDeclarationAlternatives("anotherConf")
+                }
+            }
+
+            dependencies {
+                testConf "org:foo:1.0"
+            }
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("The testConf configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use the anotherConf configuration instead. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+        succeeds 'help'
+    }
+
+    def "can add resolution alternatives to configuration deprecated for resolution"() {
+        given:
+        mavenRepo.module("org", "foo", "1.0").publish()
+        buildFile << """
+            configurations {
+                deps
+                createWithRole("testConf", org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration.LEGACY_TO_CONSUMABLE) {
+                    addResolutionAlternatives("anotherConf")
+                    extendsFrom(deps)
+                }
+            }
+
+            repositories { maven { url "${mavenRepo.uri}" } }
+
+            dependencies {
+                deps "org:foo:1.0"
+            }
+
+            task resolve {
+                configurations.testConf.files
+            }
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("The testConf configuration has been deprecated for resolution. This will fail with an error in Gradle 9.0. Please resolve the anotherConf configuration instead. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+        succeeds 'resolve'
     }
 
     def "can prevent usage mutation of roleless configuration #configuration added by java plugin meant for resolution"() {
@@ -135,7 +178,7 @@
         fails 'help'
 
         and:
-        assertUsageLockedFailure(configuration, 'Intended Resolvable')
+        assertUsageLockedFailure(configuration, 'Resolvable')
 
         where:
         configuration << ['runtimeClasspath', 'compileClasspath']
@@ -160,7 +203,7 @@
                 implementation {
                     canBeConsumed = !canBeConsumed
                     canBeResolved = !canBeResolved
-                    canBeDeclaredAgainst = !canBeDeclaredAgainst
+                    canBeDeclared = !canBeDeclared
                 }
             }
         """
@@ -170,6 +213,9 @@
         """
 
         expect:
+        expectConsumableChanging(':buildSrc:implementation', true)
+        expectResolvableChanging(':buildSrc:implementation', true)
+        expectDeclarableChanging(':buildSrc:implementation', false)
         succeeds 'myTask'
     }
 
@@ -201,7 +247,7 @@
                         implementation {
                             canBeConsumed = !canBeConsumed
                             canBeResolved = !canBeResolved
-                            canBeDeclaredAgainst = !canBeDeclaredAgainst
+                            canBeDeclared = !canBeDeclared
                         }
                     }
                 }
@@ -209,11 +255,18 @@
         """
 
         expect:
+        expectConsumableChanging(':projectA:implementation', true)
+        expectResolvableChanging(':projectA:implementation', true)
+        expectDeclarableChanging(':projectA:implementation', false)
+        expectConsumableChanging(':projectB:implementation', true)
+        expectResolvableChanging(':projectB:implementation', true)
+        expectDeclarableChanging(':projectB:implementation', false)
         succeeds 'help'
     }
     // endregion Roleless (Implicit LEGACY Role) Configurations
 
     // region Role-Based Configurations
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "intended usage is allowed for role-based configuration #role"() {
         given:
         buildFile << """
@@ -223,7 +276,7 @@
                 doLast {
                     assert configurations.custom.canBeConsumed == $consumable
                     assert configurations.custom.canBeResolved == $resolvable
-                    assert configurations.custom.canBeDeclaredAgainst == $declarableAgainst
+                    assert configurations.custom.canBeDeclared == $declarable
                     assert configurations.custom.deprecatedForConsumption == $consumptionDeprecated
                     assert configurations.custom.deprecatedForResolution == $resolutionDeprecated
                     assert configurations.custom.deprecatedForDeclarationAgainst == $declarationAgainstDeprecated
@@ -235,13 +288,36 @@
         succeeds('checkConfUsage')
 
         where:
-        role                    | customRoleBasedConf               || consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
+        role                    | customRoleBasedConf               || consumable  | resolvable    | declarable | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
         'consumable'            | "consumable('custom')"            || true        | false         | false             | false                 | false                 | false
         'resolvable'            | "resolvable('custom')"            || false       | true          | false             | false                 | false                 | false
         'resolvableBucket'      | "resolvableBucket('custom')"      || false       | true          | true              | false                 | false                 | false
         'bucket'                | "bucket('custom')"                || false       | false         | true              | false                 | false                 | false
-        'deprecated consumable' | "deprecatedConsumable('custom')"  || true        | true          | true              | false                 | true                  | true
-        'deprecated resolvable' | "deprecatedResolvable('custom')"  || true        | true          | true              | true                  | false                 | true
+    }
+
+    def "can prevent usage mutation of role-based configuration #configuration added by java plugin meant for consumption"() {
+        given:
+        buildFile << """
+            plugins {
+                id 'java'
+            }
+            configurations {
+                $configuration {
+                    assert canBeConsumed == true
+                    preventUsageMutation()
+                    canBeConsumed = false
+                }
+            }
+        """
+
+        expect:
+        fails 'help'
+
+        and:
+        assertUsageLockedFailure(configuration, 'Consumable')
+
+        where:
+        configuration << ['runtimeElements', 'apiElements']
     }
 
     def "can prevent usage mutation of role-based configuration #role"() {
@@ -264,11 +340,9 @@
 
         where:
         role                    | customRoleBasedConf               | displayName
-        'consumable'            | "consumable('custom')"            | 'Intended Consumable'
-        'resolvable'            | "resolvable('custom')"            | 'Intended Resolvable'
-        'bucket'                | "bucket('custom')"                | 'Intended Bucket'
-        'deprecated consumable' | "deprecatedConsumable('custom')"  | 'Deprecated Consumable'
-        'deprecated resolvable' | "deprecatedResolvable('custom')"  | 'Deprecated Resolvable'
+        'consumable'            | "consumable('custom')"            | 'Consumable'
+        'resolvable'            | "resolvable('custom')"            | 'Resolvable'
+        'bucket'                | "bucket('custom')"                | 'Bucket'
     }
 
     def "exhaustively try all new role-based creation syntax"() {
@@ -281,26 +355,22 @@
                 resolvable('resolvable1')
                 resolvableBucket('resolvableBucket1')
                 bucket('bucket1')
-                deprecatedConsumable('deprecatedConsumable1')
-                deprecatedResolvable('deprecatedResolvable1')
 
                 consumable('consumable2', true)
                 resolvable('resolvable2', true)
                 resolvableBucket('resolvableBucket2', true)
                 bucket('bucket2', true)
-                deprecatedConsumable('deprecatedConsumable2', true)
-                deprecatedResolvable('deprecatedResolvable2', true)
 
-                createWithRole('consumable3', ConfigurationRoles.INTENDED_CONSUMABLE)
-                createWithRole('consumable4', ConfigurationRoles.INTENDED_CONSUMABLE, true)
-                createWithRole('consumable5', ConfigurationRoles.INTENDED_CONSUMABLE, true) {
+                createWithRole('consumable3', ConfigurationRoles.CONSUMABLE)
+                createWithRole('consumable4', ConfigurationRoles.CONSUMABLE, true)
+                createWithRole('consumable5', ConfigurationRoles.CONSUMABLE, true) {
                     visible = false
                 }
-                createWithRole('consumable6', ConfigurationRoles.INTENDED_CONSUMABLE) {
+                createWithRole('consumable6', ConfigurationRoles.CONSUMABLE) {
                     visible = false
                 }
 
-                maybeCreateWithRole('resolvable7', ConfigurationRoles.INTENDED_RESOLVABLE, true, true)
+                maybeCreateWithRole('resolvable7', ConfigurationRoles.RESOLVABLE, true, true)
             }
         """
 
@@ -339,7 +409,7 @@
 
             configurations {
                 assert findByName('implementation')
-                assert maybeCreateWithRole('implementation', ConfigurationRoles.INTENDED_BUCKET, false, true)
+                assert maybeCreateWithRole('implementation', ConfigurationRoles.BUCKET, false, true)
             }
         """
 
@@ -358,17 +428,17 @@
 
             configurations {
                 assert findByName('implementation')
-                maybeCreateWithRole('implementation', ConfigurationRoles.INTENDED_RESOLVABLE, false, true)
+                maybeCreateWithRole('implementation', ConfigurationRoles.RESOLVABLE, false, true)
             }
         """
 
         expect:
         fails 'help'
-        result.assertHasErrorOutput("""Usage for configuration: implementation is not consistent with the role: Intended Resolvable.
+        result.assertHasErrorOutput("""Usage for configuration: implementation is not consistent with the role: Resolvable.
   Expected that it is:
   \tResolvable - this configuration can be resolved by this project to a set of files
   But is actually is:
-  \tDeclarable Against - this configuration can have dependencies added to it""")
+  \tDeclarable - this configuration can have dependencies added to it""")
     }
 
     def "maybeCreateWithRole verifies usage of existing custom configurations' roles when matching configuration is found, failing on mismatch"() {
@@ -379,13 +449,13 @@
             configurations {
                 consumable('custom')
                 assert findByName('custom')
-                maybeCreateWithRole('custom', ConfigurationRoles.INTENDED_RESOLVABLE, false, true)
+                maybeCreateWithRole('custom', ConfigurationRoles.RESOLVABLE, false, true)
             }
         """
 
         expect:
         fails 'help'
-        result.assertHasErrorOutput("""Usage for configuration: custom is not consistent with the role: Intended Resolvable.
+        result.assertHasErrorOutput("""Usage for configuration: custom is not consistent with the role: Resolvable.
   Expected that it is:
   \tResolvable - this configuration can be resolved by this project to a set of files
   But is actually is:
@@ -399,13 +469,16 @@
 
             configurations {
                 assert !findByName('custom')
-                def result = maybeCreateWithRole('custom', ConfigurationRoles.INTENDED_RESOLVABLE, true, false)
-                assert !result.isUsageMutable()
+                def result = maybeCreateWithRole('custom', ConfigurationRoles.RESOLVABLE, true, false)
+                result.canBeResolved = !result.canBeResolved
             }
         """
 
         expect:
-        succeeds 'help'
+        fails 'help'
+
+        and:
+        assertUsageLockedFailure('custom', 'Resolvable')
     }
 
     def "maybeCreateWithRole can lock existing roles"() {
@@ -419,14 +492,16 @@
 
             configurations {
                 def existing = findByName('implementation')
-                assert existing.isUsageMutable()
                 def result = maybeCreateWithRole('implementation', ConfigurationRoles.LEGACY, true, false)
-                assert !result.isUsageMutable()
+                result.canBeResolved = !result.canBeResolved
             }
         """
 
         expect:
-        succeeds 'help'
+        fails 'help'
+
+        and:
+        assertUsageLockedFailure('implementation', 'Bucket')
     }
 
     def "can update all roles for non-locked configurations"() {
@@ -436,22 +511,22 @@
 
             configurations {
                 def c1 = createWithRole('c1', ConfigurationRoles.LEGACY)
-                def c2 = createWithRole('c2', ConfigurationRoles.INTENDED_CONSUMABLE)
-                def c3 = createWithRole('c3', ConfigurationRoles.INTENDED_RESOLVABLE)
-                def c4 = createWithRole('c4', ConfigurationRoles.INTENDED_RESOLVABLE_BUCKET)
-                def c5 = createWithRole('c5', ConfigurationRoles.INTENDED_BUCKET)
-                def c6 = createWithRole('c6', ConfigurationRoles.DEPRECATED_CONSUMABLE)
-                def c7 = createWithRole('c7', ConfigurationRoles.DEPRECATED_RESOLVABLE)
+                def c2 = createWithRole('c2', ConfigurationRoles.CONSUMABLE)
+                def c3 = createWithRole('c3', ConfigurationRoles.RESOLVABLE)
+                def c4 = createWithRole('c4', ConfigurationRoles.RESOLVABLE_BUCKET)
+                def c5 = createWithRole('c5', ConfigurationRoles.CONSUMABLE_BUCKET)
+                def c6 = createWithRole('c6', ConfigurationRoles.BUCKET)
             }
 
             configurations.all {
                 canBeResolved = !canBeResolved
                 canBeConsumed = !canBeConsumed
-                canBeDeclaredAgainst = !canBeDeclaredAgainst
+                canBeDeclared = !canBeDeclared
             }
         """
 
         expect:
+        executer.noDeprecationChecks() // These are checked in the other tests, and there would be many of them here
         succeeds 'help'
     }
 
@@ -465,30 +540,106 @@
             configurations.all {
                 canBeResolved = !canBeResolved
                 canBeConsumed = !canBeConsumed
-                canBeDeclaredAgainst = !canBeDeclaredAgainst
+                canBeDeclared = !canBeDeclared
             }
         """
 
         expect:
         fails 'help'
-        assertUsageLockedFailure('custom', 'Intended Consumable')
+        assertUsageLockedFailure('custom', 'Consumable')
     }
     // endregion Role-Based Configurations
 
-    // region Logging
-    def "changing usage is logged as deprecation if requested for #usageName"() {
-        given:
+    // region Warnings
+    def "changing usage for configuration #configuration produces warnings"() {
+        given: "a buildscript which attempts to change a configuration's usage"
         buildFile << """
+            plugins {
+                id 'java-library'
+            }
+
             configurations {
-                def custom = consumable('custom')
-                assert !custom.canBeResolved
-                custom.setWarnOnChangingUsage(true)
-                custom.canBeResolved = true
+                $configuration {
+                    canBeResolved = !canBeResolved
+                }
+            }
+        """
+
+        expect: "the build succeeds and a deprecation warning is logged"
+        expectResolvableChanging(":$configuration", true)
+        succeeds 'help'
+
+        where: "a non-exhaustive list of configurations is tested"
+        configuration << ['api', 'implementation', 'compileOnly', 'runtimeOnly', 'archives']
+    }
+
+    /**
+     * This test ensures that the Kotlin plugin will not emit deprecation warnings when it prevents these configurations created by the
+     * Java plugin from being consumed.
+     *
+     * @see <a href="https://github.com/JetBrains/kotlin/blob/4be359ba02fba4c5539ba50392126b5367fa9169/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt#L101">KotlinJvmTarget.kt</a>
+     */
+    def "setting consumable = false is permitted without warning for special cases to support Kotlin plugin (can change #configuration usage without warning = #allowed)"() {
+        given: "a buildscript which attempts to change a configuration's usage"
+        buildFile << """
+            plugins {
+                id 'java-library'
+            }
+
+            configurations {
+                $configuration {
+                    assert canBeConsumed
+                    canBeConsumed = false
+                }
+            }
+        """
+
+        expect: "the build succeeds and a deprecation warning is logged if the configuration is not allowed to change"
+        if (!allowed) {
+            expectConsumableChanging(":$configuration", false)
+        }
+        succeeds 'help'
+
+        where: "a non-exhaustive list of configurations is tested"
+        configuration           || allowed
+        'apiElements'           || true
+        'runtimeElements'       || true
+        'mainSourceElements'    || false
+        'archives'              || false
+    }
+
+    def "changing consumable to true always warns for non-LEGACY configurations (can not change #configuration usage)"() {
+        given: "a buildscript which attempts to change a configuration's usage"
+        buildFile << """
+            plugins {
+                id 'java-library'
+            }
+
+            configurations {
+                $configuration {
+                    assert !canBeConsumed
+                    canBeConsumed = true
+                }
             }
         """
 
         expect:
-        executer.expectDocumentedDeprecationWarning("Allowed usage is changing for configuration ':custom', resolvable was false and is now true. Ideally, usage should be fixed upon creation. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Usage should be fixed upon creation. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#configurations_allowed_usage")
+        expectConsumableChanging(":$configuration", true)
+        succeeds 'help'
+
+        where:
+        configuration << ['api', 'implementation', 'runtimeOnly', 'compileOnly', 'compileOnlyApi', 'runtimeClasspath', 'compileClasspath']
+    }
+
+    def "changing usage for custom configuration in the legacy role is allowed"() {
+        given:
+        buildFile << """
+            configurations.createWithRole('custom', org.gradle.api.internal.artifacts.configurations.ConfigurationRoles.LEGACY)
+            assert configurations.custom.canBeResolved
+            configurations.custom.canBeResolved = false
+        """
+
+        expect:
         succeeds 'help'
     }
 
@@ -530,74 +681,95 @@
         executer.expectDocumentedDeprecationWarning("The configuration implementation was created explicitly. This configuration name is reserved for creation by Gradle. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Do not create a configuration with this name. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#configurations_allowed_usage")
         succeeds 'help'
     }
-    // endregion Logging
 
-    // region Custom Roles
-    def "can create configuration with custom role"() {
+    def "changing usage on detached configurations does not warn"() {
         given:
         buildFile << """
-            import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
-
-            ConfigurationRole customRole = ConfigurationRole.forUsage('custom', true, true, false, false, false, false)
-
-            configurations.createWithRole('custom', customRole) {
-                assert canBeConsumed
-                assert canBeResolved
-                assert !canBeDeclaredAgainst
-                assert !deprecatedForConsumption
-                assert !deprecatedForResolution
-                assert !deprecatedForDeclarationAgainst
-            }
+            def detached = project.configurations.detachedConfiguration()
+            assert detached.canBeResolved
+            detached.canBeResolved = false
         """
 
         expect:
-        succeeds 'help'
+        run "help"
     }
 
-    def "can prevent usage mutation for configuration with custom role"() {
+    def "incorrect usage combinations properly log at #logLevel when #desc"() {
         given:
         buildFile << """
             import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
+            import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles
 
-            ConfigurationRole customRole = ConfigurationRole.forUsage('custom', true, true, false, false, false, false)
-
-            configurations {
-                createWithRole('custom', customRole) {
-                    assert canBeConsumed
-                    preventUsageMutation()
-                    canBeConsumed = false
-                }
-            }
+            configurations.$elementsCreationCode
+            configurations.fooElements.preventFromFurtherMutation()
         """
 
-        expect:
-        fails 'help'
+        when:
+        executer.noDeprecationChecks() // These are checked in other tests, this one is just concerned with the log output
+        succeeds 'help', logLevel
 
-        and:
-        assertUsageLockedFailure('custom', 'custom')
-    }
-
-
-    def "custom role warns on creation if asked"() {
-        given:
-        buildFile << """
-            import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
-
-            ConfigurationRole customRole = ConfigurationRole.forUsage('custom', $consumable, $resolvable, $declarableAgainst, $consumptionDeprecated, $resolutionDeprecated, $declarationAgainstDeprecated, null, $warn)
-        """
-        if (warn) {
-            executer.expectDocumentedDeprecationWarning("Custom configuration roles are deprecated. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Use one of the standard roles defined in ConfigurationRoles instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#custom_configuration_roles")
+        then:
+        if (warningMessage) {
+            assert output.contains(warningMessage)
         }
 
+        where:
+        desc                                                        | elementsCreationCode                                                  | logLevel      || warningMessage
+        "using create to make an implicitly LEGACY configuration"   | "create('fooElements')"                                               | "--warn"      || null
+        "using consumable to make a configuration"                  | "consumable('fooElements')"                                           | "--warn"      || null
+        "using resolvable to make a configuration"                  | "resolvable('fooElements')"                                           | "--warn"      || null
+        "using resolvable_bucket to make a configuration"           | "createWithRole('fooElements', ConfigurationRoles.RESOLVABLE_BUCKET)" | "--warn"      || null
+        "using consumable_bucket to make a configuration"           | "createWithRole('fooElements', ConfigurationRoles.CONSUMABLE_BUCKET)" | "--warn"      || null
+        "using create to make an implicitly LEGACY configuration"   | "create('fooElements')"                                               | "--info"      || null
+        "using consumable to make a configuration"                  | "consumable('fooElements')"                                           | "--info"      || null
+        "using resolvable to make a configuration"                  | "resolvable('fooElements')"                                           | "--info"      || null
+        "using resolvable_bucket to make a configuration"           | "createWithRole('fooElements', ConfigurationRoles.RESOLVABLE_BUCKET)" | "--info"      || null
+        "using consumable_bucket to make a configuration"           | "createWithRole('fooElements', ConfigurationRoles.CONSUMABLE_BUCKET)" | "--info"      || 'The configuration :fooElements is both consumable and declarable. This combination is incorrect, only one of these flags should be set.'
+    }
+
+    def "redundantly calling #setMethod on a configuration that is already #isSetMethod warns when #desc"() {
+        given:
+        buildFile << """
+            import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
+            import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles
+
+            configurations.$confCreationCode
+
+            configurations.test {
+                assert $isSetMethod
+                $setMethod
+            }
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("The $usage usage is already allowed on configuration ':test'. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Remove the call to $setMethod, it has no effect. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#redundant_configuration_usage_activation")
+        succeeds 'help'
+
+        where:
+        desc                                                | confCreationCode                                                 | usage                | isSetMethod                   | setMethod
+        "using consumable to make a configuration"          | "consumable('test')"                                             | "consumable"         | "isCanBeConsumed()"           | "setCanBeConsumed(true)"
+        "using resolvable to make a configuration"          | "resolvable('test')"                                             | "resolvable"         | "isCanBeResolved()"           | "setCanBeResolved(true)"
+        "using resolvable_bucket to make a configuration"   | "createWithRole('test', ConfigurationRoles.RESOLVABLE_BUCKET)"   | "resolvable"         | "isCanBeResolved()"           | "setCanBeResolved(true)"
+        "using consumable_bucket to make a configuration"   | "createWithRole('test', ConfigurationRoles.CONSUMABLE_BUCKET)"   | "declarable"         | "isCanBeDeclared()"           | "setCanBeDeclared(true)"
+    }
+
+    def "redundantly calling #setMethod on a configuration that is already #isSetMethod does not warn when #desc"() {
+        given:
+        buildFile << """
+            def test = configurations.$confCreationCode
+            assert test.$isSetMethod
+            test.$setMethod
+        """
+
         expect:
         succeeds 'help'
 
         where:
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated | warn
-        true        | true         | false             | false                 | false                 | false                        | true
-        true        | true         | false             | false                 | false                 | false                        | false
+        desc                                                        | confCreationCode              | usage                | isSetMethod            | setMethod
+        "using create to make an implicitly LEGACY configuration"   | "create('test')"              | "consumable"         | "isCanBeConsumed()"    | "setCanBeConsumed(true)"
+        "creating a detachedConfiguration"                          | "detachedConfiguration()"     | "consumable"         | "isCanBeConsumed()"    | "setCanBeConsumed(true)"
     }
-    // endregion Custom Roles
+    // endregion Warnings
 
     private void assertUsageLockedFailure(String configurationName, String roleName = null) {
         String suffix = roleName ? "as it was locked upon creation to the role: '$roleName'." : "as it has been locked."
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationRolesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationRolesIntegrationTest.groovy
index dd2acfd..b7c182f 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationRolesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ConfigurationRolesIntegrationTest.groovy
@@ -17,14 +17,12 @@
 package org.gradle.integtests.resolve.api
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
-import spock.lang.Unroll
 
 @FluidDependenciesResolveTest
 class ConfigurationRolesIntegrationTest extends AbstractIntegrationSpec {
-
-    @Unroll("cannot resolve a configuration with role #role at execution time")
-    def "cannot resolve a configuration which is for publishing only at execution time"() {
+    def "cannot resolve a configuration with role #role at execution time"() {
         given:
         buildFile << """
 
@@ -38,8 +36,9 @@
         }
 
         task checkState {
+            def files = configurations.internal
             doLast {
-                configurations.internal.resolve()
+                files.files
             }
         }
 
@@ -58,8 +57,7 @@
 
     }
 
-    @Unroll("cannot resolve a configuration with role #role at configuration time")
-    def "cannot resolve a configuration which is for publishing only at configuration time"() {
+    def "cannot resolve a configuration with role #role at configuration time"() {
         given:
         buildFile << """
 
@@ -87,11 +85,10 @@
         role                      | code
         'consume or publish only' | 'canBeResolved = false'
         'bucket'                  | 'canBeResolved = false; canBeConsumed = false'
-
     }
 
-    @Unroll("cannot resolve a configuration with role #role using #method")
-    def "cannot resolve a configuration which is for publishing only"() {
+    @ToBeFixedForConfigurationCache(because = "Uses Configuration API")
+    def "cannot resolve a configuration with role #role using #method"() {
         given:
         buildFile << """
 
@@ -113,6 +110,18 @@
         """
 
         when:
+        if (method == 'getResolvedConfiguration()') {
+            if (role == 'canBeResolved = false') {
+                executer.expectDocumentedDeprecationWarning("""Calling configuration method 'getResolvedConfiguration()' is deprecated for configuration 'internal', which has permitted usage(s):
+\tConsumable - this configuration can be selected by another project as a dependency
+\tDeclarable - this configuration can have dependencies added to it
+This method is only meant to be called on configurations which allow the (non-deprecated) usage(s): 'Resolvable'. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_configuration_usage""")
+            } else {
+                executer.expectDocumentedDeprecationWarning("""Calling configuration method 'getResolvedConfiguration()' is deprecated for configuration 'internal', which has permitted usage(s):
+\tDeclarable - this configuration can have dependencies added to it
+This method is only meant to be called on configurations which allow the (non-deprecated) usage(s): 'Resolvable'. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_configuration_usage""")
+            }
+        }
         fails 'checkState'
 
         then:
@@ -125,8 +134,7 @@
         ].combinations()
     }
 
-    @Unroll("cannot add a dependency on a configuration role #role")
-    def "cannot add a dependency on a configuration not meant to be consumed or published"() {
+    def "cannot add a dependency on a configuration role #role"() {
         given:
         file('settings.gradle') << 'include "a", "b"'
         buildFile << """
@@ -139,7 +147,8 @@
             }
 
             task check {
-                doLast { configurations.compile.resolve() }
+                def files = configurations.compile
+                doLast { files.files }
             }
         }
         project(':b') {
@@ -164,8 +173,7 @@
         'bucket'                | 'canBeResolved = false; canBeConsumed = false'
     }
 
-    @Unroll("cannot depend on default configuration if it's not consumable (#role)")
-    def "cannot depend on default configuration if it's not consumable"() {
+    def "cannot depend on default configuration if it's not consumable (#role)"() {
         given:
         file('settings.gradle') << 'include "a", "b"'
         buildFile << """
@@ -178,7 +186,8 @@
             }
 
             task check {
-                doLast { configurations.compile.resolve() }
+                def files = configurations.compile
+                doLast { files.files }
             }
         }
         project(':b') {
@@ -202,5 +211,4 @@
         'query or resolve only' | 'canBeConsumed = false'
         'bucket'                | 'canBeResolved = false; canBeConsumed = false'
     }
-
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/DeprecatedConfigurationUsageIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/DeprecatedConfigurationUsageIntegrationTest.groovy
new file mode 100644
index 0000000..76dbdcf
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/DeprecatedConfigurationUsageIntegrationTest.groovy
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2023 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.integtests.resolve.api
+
+import org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.ProperMethodUsage
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ConfigurationUsageChangingFixture
+
+class DeprecatedConfigurationUsageIntegrationTest extends AbstractIntegrationSpec implements ConfigurationUsageChangingFixture {
+    def "calling an invalid public API method #methodName for role #role fails"() {
+        given:
+        buildFile << """
+            import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
+
+            configurations.$role('custom')
+            configurations.custom.$methodCall
+        """
+
+        when:
+        executer.noDeprecationChecks() // These will be checked elsewhere, this test is about ensuring failures
+        fails('help')
+
+        then:
+        failureCauseContains(message)
+
+        where:
+        methodName                   | role         | methodCall                                                                                                                           || message
+        'resolve()'                  | 'consumable' | 'resolve()'                                                                                                                          || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'resolve()'                  | 'bucket'     | 'resolve()'                                                                                                                          || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'files(Closure)'             | 'consumable' | 'files { }'                                                                                                                          || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'files(Closure)'             | 'bucket'     | 'files { }'                                                                                                                          || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'fileCollection(Closure)'    | 'consumable' | 'fileCollection { }'                                                                                                                 || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'fileCollection(Closure)'    | 'bucket'     | 'fileCollection { }'                                                                                                                 || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'fileCollection(Dependency)' | 'consumable' | 'fileCollection(new org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency("org.jsoup", "jsoup", "1.15.3"))' || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'fileCollection(Dependency)' | 'bucket'     | 'fileCollection(new org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency("org.jsoup", "jsoup", "1.15.3"))' || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'getResolvedConfiguration()' | 'consumable' | 'getResolvedConfiguration()'                                                                                                         || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'getResolvedConfiguration()' | 'bucket'     | 'getResolvedConfiguration()'                                                                                                         || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'getBuildDependencies()'     | 'consumable' | 'getBuildDependencies()'                                                                                                             || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+        'getBuildDependencies()'     | 'bucket'     | 'getBuildDependencies()'                                                                                                             || "Resolving dependency configuration 'custom' is not allowed as it is defined as 'canBeResolved=false'."
+    }
+
+    def "calling an invalid public API method #methodName for role #role produces a deprecation warning"() {
+        given:
+        buildFile << """
+            import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
+
+            configurations.$role('custom')
+            configurations.custom.$methodCall
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning(buildDeprecationMessage(methodName, role, allowed, false))
+        succeeds('help')
+
+        where:
+        methodName                                     | role         | methodCall                                                     || allowed
+        'attributes(Action)'                           | 'bucket'     | "attributes { attribute(Attribute.of('foo', String), 'bar') }" || [ProperMethodUsage.CONSUMABLE, ProperMethodUsage.RESOLVABLE]
+        'defaultDependencies(Action)'                  | 'consumable' | 'defaultDependencies { }'                                      || [ProperMethodUsage.DECLARABLE_AGAINST]
+        'defaultDependencies(Action)'                  | 'resolvable' | 'defaultDependencies { }'                                      || [ProperMethodUsage.DECLARABLE_AGAINST]
+        'shouldResolveConsistentlyWith(Configuration)' | 'consumable' | 'shouldResolveConsistentlyWith(null)'                          || [ProperMethodUsage.RESOLVABLE]
+        'shouldResolveConsistentlyWith(Configuration)' | 'bucket'     | 'shouldResolveConsistentlyWith(null)'                          || [ProperMethodUsage.RESOLVABLE]
+        'disableConsistentResolution()'                | 'consumable' | 'disableConsistentResolution()'                                || [ProperMethodUsage.RESOLVABLE]
+        'disableConsistentResolution()'                | 'bucket'     | 'disableConsistentResolution()'                                || [ProperMethodUsage.RESOLVABLE]
+        'copy()'                                       | 'consumable' | 'copy()'                                                       || [ProperMethodUsage.RESOLVABLE]
+        'copy()'                                       | 'bucket'     | 'copy()'                                                       || [ProperMethodUsage.RESOLVABLE]
+        'copyRecursive()'                              | 'consumable' | 'copyRecursive()'                                              || [ProperMethodUsage.RESOLVABLE]
+        'copyRecursive()'                              | 'bucket'     | 'copyRecursive()'                                              || [ProperMethodUsage.RESOLVABLE]
+        'copy(Spec)'                                   | 'consumable' | 'copy { } as Spec'                                             || [ProperMethodUsage.RESOLVABLE]
+        'copy(Spec)'                                   | 'bucket'     | 'copy { } as Spec'                                             || [ProperMethodUsage.RESOLVABLE]
+        'copyRecursive(Spec)'                          | 'consumable' | 'copyRecursive { } as Spec'                                    || [ProperMethodUsage.RESOLVABLE]
+        'copyRecursive(Spec)'                          | 'bucket'     | 'copyRecursive { } as Spec'                                    || [ProperMethodUsage.RESOLVABLE]
+
+    }
+
+    def "calling an invalid internal API method #methodName for role #role produces a deprecation warning"() {
+        given:
+        buildFile << """
+            import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
+
+            configurations.$role('custom')
+            configurations.custom.$methodCall
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning(buildDeprecationMessage(methodName, role, allowed, true))
+        succeeds('help')
+
+        where:
+        methodName                         | role         | methodCall                         || allowed
+        'setExcludeRules(Set)'             | 'consumable' | "setExcludeRules([] as Set)"       || [ProperMethodUsage.DECLARABLE_AGAINST, ProperMethodUsage.RESOLVABLE]
+        'getConsistentResolutionSource()'  | 'consumable' | "getConsistentResolutionSource()"  || [ProperMethodUsage.RESOLVABLE]
+        'getConsistentResolutionSource()'  | 'bucket'     | "getConsistentResolutionSource()"  || [ProperMethodUsage.RESOLVABLE]
+        'getDependenciesResolverFactory()' | 'consumable' | "getDependenciesResolverFactory()" || [ProperMethodUsage.RESOLVABLE]
+        'getDependenciesResolverFactory()' | 'bucket'     | "getDependenciesResolverFactory()" || [ProperMethodUsage.RESOLVABLE]
+        'getResolvedState()'               | 'consumable' | "getResolvedState()"               || [ProperMethodUsage.RESOLVABLE]
+        'getResolvedState()'               | 'bucket'     | "getResolvedState()"               || [ProperMethodUsage.RESOLVABLE]
+        'getSyntheticDependencies()'       | 'consumable' | "getSyntheticDependencies()"       || [ProperMethodUsage.RESOLVABLE]
+        'getSyntheticDependencies()'       | 'bucket'     | "getSyntheticDependencies()"       || [ProperMethodUsage.RESOLVABLE]
+        'resetResolutionState()'           | 'consumable' | "resetResolutionState()"           || [ProperMethodUsage.RESOLVABLE]
+        'resetResolutionState()'           | 'bucket'     | "resetResolutionState()"           || [ProperMethodUsage.RESOLVABLE]
+        'toRootComponentMetaData()'        | 'consumable' | "toRootComponentMetaData()"        || [ProperMethodUsage.RESOLVABLE]
+        'toRootComponentMetaData()'        | 'bucket'     | "toRootComponentMetaData()"        || [ProperMethodUsage.RESOLVABLE]
+    }
+
+    def "forcing resolve of a non-resolvable configuration via calling invalid internal API method #methodName for role #role warns and then throws an exception"() {
+        given:
+        buildFile << """
+            import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
+
+            configurations.$role('custom')
+            configurations.custom.$methodCall
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning(buildDeprecationMessage(methodName, role, allowed, true))
+        fails('help')
+
+        where:
+        methodName       | role         | methodCall                  || allowed
+        'contains(File)' | 'consumable' | "contains(new File('foo'))" || [ProperMethodUsage.RESOLVABLE]
+    }
+
+    def "calling deprecated usage produces a deprecation warning"() {
+        given:
+        buildFile << """
+            import org.gradle.api.internal.artifacts.configurations.ConfigurationRole
+
+            ConfigurationRole customRole = new org.gradle.api.internal.artifacts.configurations.DefaultConfigurationRole("custom", true, false, false, true, false, false)
+            configurations.createWithRole('custom', customRole)
+
+            configurations.custom.attributes {
+                attribute(Attribute.of('foo', String), 'bar')
+            }
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("""Calling configuration method 'attributes(Action)' is deprecated for configuration 'custom', which has permitted usage(s):
+\tConsumable - this configuration can be selected by another project as a dependency (but this behavior is marked deprecated)
+This method is only meant to be called on configurations which allow the (non-deprecated) usage(s): 'Consumable, Resolvable'. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: ${documentationRegistry.getDocumentationFor("upgrading_version_8", "deprecated_configuration_usage")}""")
+        succeeds('help')
+    }
+
+    def "calling deprecated usage does not produce a deprecation warning if other allowed usage permits it"() {
+        given:
+        buildFile << """
+            configurations {
+                createWithRole('foo', org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET) {
+                    attributes {
+                        attribute(Attribute.of('foo', String), 'bar')
+                    }
+                }
+            }
+        """
+
+        expect:
+        succeeds('help')
+    }
+
+    def "configuration explicitly deprecated for resolution will warn if resolved, but not fail"() {
+        buildFile << """
+            configurations {
+                createWithRole('foo', org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_BUCKET)
+            }
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                foo 'org.apache.commons:commons-lang3:3.9'
+            }
+
+            configurations.foo.files
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("The foo configuration has been deprecated for resolution. This will fail with an error in Gradle 9.0. Please resolve another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+        succeeds("help")
+    }
+
+    private String buildDeprecationMessage(String methodName, String role, List<ProperMethodUsage> allowed, boolean allowDeprecated) {
+        return """Calling configuration method '$methodName' is deprecated for configuration 'custom', which has permitted usage(s):
+${buildAllowedUsages(role)}
+This method is only meant to be called on configurations which allow the ${allowDeprecated ? "" : "(non-deprecated) "}usage(s): '${buildProperNames(allowed)}'. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_configuration_usage"""
+    }
+
+    private String buildProperNames(List<ProperMethodUsage> usages) {
+        usages.collect { ProperMethodUsage.buildProperName(it) }.join(", ")
+    }
+
+    private String buildAllowedUsages(String role) {
+        switch (role) {
+            case 'bucket':
+                return "\tDeclarable - this configuration can have dependencies added to it"
+            case 'consumable':
+                return "\tConsumable - this configuration can be selected by another project as a dependency"
+            case 'resolvable':
+                return "\tResolvable - this configuration can be resolved by this project to a set of files"
+            default:
+                throw new IllegalArgumentException("Unknown role: $role")
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ExtendingConfigurationsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ExtendingConfigurationsIntegrationTest.groovy
index 9734cae..ad15268 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ExtendingConfigurationsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ExtendingConfigurationsIntegrationTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.integtests.resolve.api
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 import spock.lang.Issue
 
@@ -23,6 +24,7 @@
 class ExtendingConfigurationsIntegrationTest extends AbstractDependencyResolutionTest {
 
     @Issue("GRADLE-2873")
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "may replace configuration extension targets"() {
         mavenRepo.module("org", "foo").publish()
         mavenRepo.module("org", "bar").publish()
@@ -86,16 +88,20 @@
 }
 
 task checkResolveChild {
+    def files = configurations.child
     doFirst {
-        assert configurations.child.files*.name == ['foo-1.0.jar', 'bar-1.0.jar', 'baz-1.0.jar']
+        assert files*.name == ['foo-1.0.jar', 'bar-1.0.jar', 'baz-1.0.jar']
     }
 }
 
 task checkResolveParentThenChild {
+    def two = configurations.two
+    def one = configurations.one
+    def child = configurations.child
     doFirst {
-        assert configurations.two.files*.name == ['bar-1.0.jar']
-        assert configurations.one.files*.name == ['foo-1.0.jar', 'baz-1.0.jar']
-        assert configurations.child.files*.name == ['foo-1.0.jar', 'bar-1.0.jar', 'baz-1.0.jar']
+        assert two*.name == ['bar-1.0.jar']
+        assert one*.name == ['foo-1.0.jar', 'baz-1.0.jar']
+        assert child*.name == ['foo-1.0.jar', 'bar-1.0.jar', 'baz-1.0.jar']
     }
 }
 """
@@ -104,4 +110,77 @@
         succeeds "checkResolveChild"
         succeeds "checkResolveParentThenChild"
     }
+
+    @Issue("https://github.com/gradle/gradle/issues/24109")
+    def "can resolve configuration after extending a resolved configuration"() {
+        given:
+        mavenRepo.module("org", "foo").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                superConfiguration
+                subConfiguration
+            }
+            dependencies {
+                superConfiguration 'org:foo:1.0'
+            }
+
+            task resolve {
+                println configurations.superConfiguration.files.collect { it.name }
+                configurations.subConfiguration.extendsFrom(configurations.superConfiguration)
+                println configurations.subConfiguration.files.collect { it.name }
+            }
+        """
+
+        when:
+        succeeds("resolve")
+
+        then:
+        output.contains("[foo-1.0.jar]\n[foo-1.0.jar]")
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/24234")
+    def "configuration extensions can be changed in withDependencies during resolution"() {
+        given:
+        mavenRepo.module("org", "foo").publish()
+        buildFile << """
+            def attr = Attribute.of('org.example.attr', String)
+
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+
+            configurations {
+                parentConf
+                depConf {
+                    attributes {
+                        attribute(attr, 'pick-me')
+                    }
+                    withDependencies {
+                        depConf.extendsFrom(parentConf)
+                    }
+                }
+                conf
+            }
+
+            dependencies {
+                conf(project(':')) {
+                    attributes {
+                        attribute(attr, 'pick-me')
+                    }
+                }
+                parentConf 'org:foo:1.0'
+            }
+
+            task resolve {
+                assert configurations.conf.files.collect { it.name } == ["foo-1.0.jar"]
+            }
+        """
+
+        expect:
+        succeeds("resolve")
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/InvalidConfigurationResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/InvalidConfigurationResolutionIntegrationTest.groovy
index e10164e..7d0a998 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/InvalidConfigurationResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/InvalidConfigurationResolutionIntegrationTest.groovy
@@ -23,7 +23,7 @@
 /**
  * This class tests that when configurations are used incorrectly - for instance, when a configuration where
  * {@link org.gradle.api.artifacts.Configuration#isCanBeResolved()} returns {@code false} is resolved - now (as of Gradle 8.0) throw exceptions
- * instead of merely warning.  We want to ensure that the {@code canBeResolved}, {@code canBeConsumed}, and {@code canBeDeclaredAgainst}
+ * instead of merely warning.  We want to ensure that the {@code canBeResolved}, {@code canBeConsumed}, and {@code canBeDeclared}
  * flags are set appropriately and consistently on all configurations prior to their usage towards one of these goals.
  */
 class InvalidConfigurationResolutionIntegrationTest extends AbstractIntegrationSpec {
@@ -38,12 +38,12 @@
             allprojects {
                 configurations {
                     implementation
-                    compile.canBeDeclaredAgainst = false
+                    compile.canBeDeclared = false
                     compile.canBeConsumed = false
                     compile.canBeResolved = false
                     compileOnly.canBeResolved = false
                     apiElements {
-                        canBeConsumed = true
+                        assert canBeConsumed
                         canBeResolved = false
                         extendsFrom compile
                         extendsFrom compileOnly
@@ -51,7 +51,7 @@
                     }
                     compileClasspath {
                         canBeConsumed = false
-                        canBeResolved = true
+                        assert canBeResolved
                         extendsFrom compile
                         extendsFrom compileOnly
                         extendsFrom implementation
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
index f3c78eb..09dce81 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
@@ -19,6 +19,7 @@
 package org.gradle.integtests.resolve.api
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 import spock.lang.Issue
@@ -31,6 +32,7 @@
     The ResolutionResult API is also covered by the dependency report integration tests.
      */
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "selection reasons are described"() {
         given:
         mavenRepo.module("org", "leaf", "1.0").publish()
@@ -83,6 +85,7 @@
 """
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "resolution result API gives access to dependency reasons in case of conflict"() {
         given:
         mavenRepo.with {
@@ -138,6 +141,7 @@
         run "checkDeps"
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "resolution result API gives access to dependency reasons in case of conflict and selection by rule"() {
         given:
         mavenRepo.with {
@@ -226,6 +230,7 @@
         }
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "constraint are not mis-showing up as a separate REQUESTED and do not overwrite selection by rule"() {
         given:
         mavenRepo.module("org", "foo", "1.0").publish()
@@ -289,6 +294,7 @@
         useReason << [true, false]
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "direct dependency reasons are not mis-showing up as a separate REQUESTED and do not overwrite selection by rule"() {
         given:
         mavenRepo.module("org", "foo", "1.0").publish()
@@ -344,6 +350,7 @@
         useReason << [true, false]
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     void "expired cache entry doesn't break reading from cache"() {
         given:
         mavenRepo.module("org", "foo", "1.0").publish()
@@ -409,9 +416,9 @@
 
         then:
         noExceptionThrown()
-
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "each dependency is associated to its resolved variant"() {
         mavenRepo.module("org", "dep", "1.0").publish()
         mavenRepo.module("com", "foo", "1.0").publish()
@@ -449,8 +456,6 @@
                 testImplementation(testFixtures(project(":tool")))
                 testImplementation(testFixtures(project(":tool"))) // intentional duplication
             }
-
-
         """
         withResolutionResultDumper("testCompileClasspath", "testRuntimeClasspath")
 
@@ -480,6 +485,7 @@
 """
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "requested dependency attributes are reported on dependency result as desugared attributes"() {
         settingsFile << "include 'platform'"
         buildFile << """
@@ -506,9 +512,9 @@
 
         expect:
         succeeds 'checkDependencyAttributes'
-
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "reports duplicated dependencies in all variants"() {
         mavenRepo.module('org', 'foo', '1.0').publish()
         mavenRepo.module('org', 'bar', '1.0').publish()
@@ -578,6 +584,7 @@
 """)
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "reports if we try to get dependencies from a different variant"() {
         mavenRepo.module('org', 'foo', '1.0').publish()
 
@@ -630,6 +637,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/12643")
+    @ToBeFixedForConfigurationCache(because = "task exercises the resolution result API")
     def "resolved variant of a selected node shouldn't be null"() {
         buildFile << """
         apply plugin: 'java-library'
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedArtifactsApiIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedArtifactsApiIntegrationTest.groovy
index db2a411..43d9fba 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedArtifactsApiIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedArtifactsApiIntegrationTest.groovy
@@ -88,8 +88,8 @@
 
 task show {
     inputs.files configurations.compile
+    def artifacts = configurations.compile.${expression}
     doLast {
-        def artifacts = configurations.compile.${expression}
         println "files: " + artifacts.collect { it.file.name }
         println "ids: " + artifacts.collect { it.id.displayName }
         println "unique ids: " + artifacts.collect { it.id }.unique()
@@ -164,8 +164,8 @@
 
 task show {
     inputs.files configurations.compile
+    def artifacts = configurations.compile.${expression}
     doLast {
-        def artifacts = configurations.compile.${expression}
         println "files: " + artifacts.collect { it.file.name }
         println "ids: " + artifacts.collect { it.id.displayName }
         println "components: " + artifacts.collect { it.id.componentIdentifier.displayName }
@@ -269,8 +269,8 @@
 
 task show {
     inputs.files configurations.compile.${expression}.artifactFiles
+    def artifacts = configurations.compile.${expression}
     doLast {
-        def artifacts = configurations.compile.${expression}
         println "files: " + artifacts.collect { it.file.name }
         println "ids: " + artifacts.collect { it.id.displayName }
         println "components: " + artifacts.collect { it.id.componentIdentifier.displayName }
@@ -371,8 +371,8 @@
 
 task show {
     inputs.files configurations.compile.${expression}.artifactFiles
+    def artifacts = configurations.compile.${expression}
     doLast {
-        def artifacts = configurations.compile.${expression}
         println "files: " + artifacts.collect { it.file.name }
         println "ids: " + artifacts.collect { it.id.displayName }
         println "components: " + artifacts.collect { it.id.componentIdentifier.displayName }
@@ -457,13 +457,10 @@
 }
 
 task show {
+    def artifacts = configurations.compile.${expression}
     doLast {
-        def artifacts = configurations.compile.${expression}
         println "files: " + artifacts.collect { it.file.name }
-        println "ids: " + artifacts.collect { it.id.displayName }
-        println "components: " + artifacts.collect { it.id.componentIdentifier.displayName }
-        println "variants: " + artifacts.collect { it.variant.attributes }
-        assert artifacts.failures.empty
+        throw new RuntimeException()
     }
 }
 """
@@ -548,10 +545,10 @@
 
 task show {
     inputs.files configurations.compile
+    def artifacts = configurations.compile.incoming.artifactView {
+        attributes({it.attribute(usage, 'transformed')})
+    }.artifacts
     doLast {
-        def artifacts = configurations.compile.incoming.artifactView {
-            attributes({it.attribute(usage, 'transformed')})
-        }.artifacts
         println "files: " + artifacts.collect { it.file.name }
         println "components: " + artifacts.collect { it.id.componentIdentifier.displayName }
         println "variants: " + artifacts.collect { it.variant.attributes }
@@ -602,9 +599,10 @@
 
 task show {
     inputs.files configurations.compile
+    def artifacts = configurations.compile.${expression}
+    def rootDir = rootProject.projectDir.toPath()
     doLast {
-        def artifacts = configurations.compile.${expression}
-        println "files: " + artifacts.collect { rootProject.relativePath(it.file).replace(File.separator, '/') }
+        println "files: " + artifacts.collect { rootDir.relativize(it.file.toPath()).toString().replace(File.separator, '/') }
         println "ids: " + artifacts.collect { it.id.displayName }
         println "unique ids: " + artifacts.collect { it.id }.unique()
         println "components: " + artifacts.collect { it.id.componentIdentifier.displayName }
@@ -755,6 +753,7 @@
         "incoming.artifactView({lenient(false)}).artifacts"           | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "error reporting is different when CC is enabled")
     def "reports failure to query file dependency when artifacts are queried"() {
         buildFile << """
 dependencies {
@@ -779,6 +778,7 @@
         "incoming.artifactView({lenient(false)}).artifacts"           | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "error reporting is different when CC is enabled")
     def "reports multiple failures to resolve artifacts when artifacts are queried"() {
         settingsFile << "include 'a'"
         buildFile << """
@@ -830,7 +830,7 @@
         "incoming.artifactView({lenient(false)}).artifacts"           | _
     }
 
-    @ToBeFixedForConfigurationCache(because = "broken file collection")
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "lenient artifact view reports failure to resolve graph and artifacts"() {
         settingsFile << "include 'a', 'b'"
 
@@ -909,7 +909,7 @@
           - Doesn't say anything about usage (required 'compile')""")
     }
 
-    @ToBeFixedForConfigurationCache(because = "broken file collection")
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "successfully resolved local artifacts are built when lenient file view used as task input"() {
         settingsFile << "include 'a', 'b', 'c'"
 
@@ -977,8 +977,8 @@
     def showFailuresTask(expression) {
         """
 task show {
+    def artifacts = configurations.compile.${expression}
     doLast {
-        def artifacts = configurations.compile.${expression}
         artifacts.collect { true }
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedConfigurationApiIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedConfigurationApiIntegrationTest.groovy
index 8dca922..f3f6d96 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedConfigurationApiIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedConfigurationApiIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve.api
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 
 @FluidDependenciesResolveTest
@@ -37,6 +38,7 @@
 """
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the ResolvedConfiguration API")
     def "artifacts may have no extension"() {
         def m1 = ivyHttpRepo.module('org', 'test', '1.0')
         m1.artifact(type: 'jar', ext: '')
@@ -80,6 +82,7 @@
         outputContains("classifiers: [null, null, classy]")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the ResolvedConfiguration API")
     def "reports multiple failures to resolve components"() {
         buildFile << """
             repositories { maven { url '${mavenHttpRepo.uri}' } }
@@ -113,6 +116,7 @@
         failure.assertHasCause("Could not resolve test:test2:1.2.")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task exercises the ResolvedConfiguration API")
     def "reports failure to resolve artifact"() {
         buildFile << """
             repositories { maven { url '${mavenHttpRepo.uri}' } }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedFilesApiIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedFilesApiIntegrationTest.groovy
index 66a1aea..d068c43 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedFilesApiIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedFilesApiIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve.api
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class ResolvedFilesApiIntegrationTest extends AbstractHttpDependencyResolutionTest {
     def setup() {
@@ -40,6 +41,7 @@
 """
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "result includes files from local and external components and file dependencies in a fixed order"() {
         mavenRepo.module("org", "test", "1.0").publish()
         mavenRepo.module("org", "test2", "1.0").publish()
@@ -116,6 +118,7 @@
         outputContains("files 12: [test-lib.jar, a.jar, a-lib.jar, b.jar, b-lib.jar, test2-1.0.jar, test-1.0.jar")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "applies compatibility rules to select variant"() {
         settingsFile << """
 include 'a', 'b'
@@ -193,6 +196,7 @@
         "configurations.compile.incoming.artifactView({componentFilter { true }}).artifacts.artifactFiles" | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "applies disambiguation rules to select variant"() {
         settingsFile << """
 include 'a', 'b'
@@ -255,6 +259,7 @@
         "configurations.compile.incoming.artifactView({componentFilter { true }}).artifacts.artifactFiles" | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "reports failure when there is more than one compatible variant"() {
         settingsFile << """
 include 'a', 'b'
@@ -312,6 +317,7 @@
         "configurations.compile.incoming.artifactView({componentFilter { true }}).artifacts.artifactFiles" | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "reports failure when there is no compatible variant"() {
         mavenRepo.module("test", "test", "1.2").publish()
 
@@ -392,6 +398,7 @@
         "configurations.compile.incoming.artifactView({componentFilter { true }}).artifacts.artifactFiles" | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "reports failure to resolve component when files are queried using #expression"() {
         buildFile << """
 allprojects {
@@ -436,6 +443,7 @@
         "configurations.compile.incoming.artifactView({componentFilter { true }}).artifacts.artifactFiles" | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "reports failure to download artifact when files are queried using #expression"() {
         buildFile << """
 allprojects {
@@ -482,6 +490,7 @@
         "configurations.compile.incoming.artifactView({componentFilter { true }}).artifacts.artifactFiles" | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "reports failure to query file dependency when files are queried using #expression"() {
         buildFile << """
 dependencies {
@@ -516,6 +525,7 @@
         "configurations.compile.incoming.artifactView({componentFilter { true }}).artifacts.artifactFiles" | _
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "reports multiple failures to resolve artifacts when files are queried using #expression"() {
         settingsFile << "include 'a'"
         buildFile << """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/UnsupportedConfigurationMutationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/UnsupportedConfigurationMutationTest.groovy
index c63db3e..01cf5f3 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/UnsupportedConfigurationMutationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/UnsupportedConfigurationMutationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve.api
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import spock.lang.Issue
@@ -124,6 +125,7 @@
         then: failure.assertHasCause("Cannot change resolution strategy of dependency configuration ':a' after it has been resolved")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses dependencies API")
     def "does not allow changing dependencies of a configuration that has been resolved for task dependencies"() {
         mavenRepo.module("org.utils", "extra", '1.5').publish()
 
@@ -209,6 +211,7 @@
         failure.assertHasCause("Cannot change dependencies of dependency configuration ':api:compile' after it has been included in dependency resolution.")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses dependencies API")
     def "does not allow changing artifacts of a configuration that has been resolved for task dependencies"() {
         mavenRepo.module("org.utils", "extra", '1.5').publish()
 
@@ -430,6 +433,7 @@
     }
 
     @Issue("GRADLE-3297")
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "using offline flag does not emit deprecation warning when child configuration is explicitly resolved"() {
         def repo = new MavenFileRepository(file("repo"))
         repo.module('org.test', 'moduleA', '1.0').publish()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ArtifactResolutionQueryIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ArtifactResolutionQueryIntegrationTest.groovy
index e1c63ec..fba9119 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ArtifactResolutionQueryIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ArtifactResolutionQueryIntegrationTest.groovy
@@ -17,6 +17,8 @@
 package org.gradle.integtests.resolve.artifactreuse
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
+import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
 import org.gradle.integtests.fixtures.timeout.IntegrationTestTimeout
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
 import org.junit.Rule
@@ -32,6 +34,7 @@
 
     @Issue('https://github.com/gradle/gradle/issues/3579')
     @IntegrationTestTimeout(60)
+    @UnsupportedWithConfigurationCache(because = "task uses artifact query API")
     def 'can use artifact resolution queries in parallel to file resolution'() {
         given:
         def module = mavenHttpRepo.module('group', "artifact", '1.0').publish()
@@ -76,9 +79,6 @@
 """
         executer.requireOwnGradleUserHomeDir().requireIsolatedDaemons()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         expect:
         def build = executer.withArguments('query:query', ':resolve:resolve', '--parallel').start()
 
@@ -91,6 +91,7 @@
     }
 
     @Issue('https://github.com/gradle/gradle/issues/11247')
+    @ToBeFixedForConfigurationCache(because = "task uses artifact query API")
     def 'respects repository content filter'() {
         given:
         def module = mavenHttpRepo.module('group', "artifact", '1.0').publish()
@@ -124,9 +125,6 @@
         """
 
         expect:
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         succeeds('query')
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy
index 321471a..3db28ae 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/artifactreuse/ResolutionOverrideIntegrationTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.integtests.resolve.artifactreuse
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.resolve.ResolveFailureTestFixture
 import spock.lang.Shared
 
 class ResolutionOverrideIntegrationTest extends AbstractHttpDependencyResolutionTest {
@@ -87,7 +88,12 @@
 dependencies {
     missing 'org.name:projectA:1.2'
 }
-task showMissing { doLast { println configurations.missing.files } }
+task showMissing {
+    def files = configurations.missing
+    doLast {
+        println files.files
+    }
+}
 """
 
         when:
@@ -199,6 +205,8 @@
     }
 
     void "does not attempt to contact server when run with offline flag"() {
+        def resolve = new ResolveFailureTestFixture(buildFile)
+
         given:
         buildFile << """
 repositories {
@@ -207,20 +215,22 @@
 configurations { compile }
 dependencies { compile 'org.name:projectA:1.2' }
 task listJars {
+    def files = configurations.compile
     doLast {
-        assert configurations.compile.collect { it.name } == ['projectA-1.2.jar']
+        assert files.collect { it.name } == ['projectA-1.2.jar']
     }
 }
 """
+        resolve.prepare()
 
         when:
         executer.withArguments("--offline")
 
         then:
-        fails 'listJars'
+        fails 'checkDeps'
 
         and:
-        failure.assertHasDescription('Execution failed for task \':listJars\'.')
+        resolve.assertFailurePresent(failure)
         failure.assertResolutionFailure(":compile")
             .assertHasCause('No cached version of org.name:projectA:1.2 available for offline mode')
     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/AbstractConfigurationAttributesResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/AbstractConfigurationAttributesResolveIntegrationTest.groovy
index 35735cf..6e4ea01 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/AbstractConfigurationAttributesResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/AbstractConfigurationAttributesResolveIntegrationTest.groovy
@@ -18,11 +18,12 @@
 package org.gradle.integtests.resolve.attributes
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ConfigurationUsageChangingFixture
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 
 @FluidDependenciesResolveTest
-abstract class AbstractConfigurationAttributesResolveIntegrationTest extends AbstractIntegrationSpec {
+abstract class AbstractConfigurationAttributesResolveIntegrationTest extends AbstractIntegrationSpec implements ConfigurationUsageChangingFixture {
 
     abstract String getTypeDefs()
 
@@ -64,13 +65,15 @@
                     _compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar']
+                       assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations._compileFreeRelease) {
+                    def files = configurations._compileFreeRelease
                     doLast {
-                       assert configurations._compileFreeRelease.collect { it.name } == ['b-bar.jar']
+                       assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -193,13 +196,15 @@
                     compile project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar']
+                        assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations._compileFreeRelease) {
+                    def files = configurations._compileFreeRelease
                     doLast {
-                        assert configurations._compileFreeRelease.collect { it.name } == ['b-bar.jar']
+                        assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -250,13 +255,15 @@
                     compile project(path:':b', configuration: 'bar')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-bar.jar']
+                        assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations._compileFreeRelease) {
+                    def files = configurations._compileFreeRelease
                     doLast {
-                        assert configurations._compileFreeRelease.collect { it.name } == ['b-bar.jar']
+                        assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -321,8 +328,9 @@
                     compile project(path:':b', configuration: 'bar')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-bar.jar']
+                        assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -365,13 +373,15 @@
                     compile project(path:':b', configuration: 'bar')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-bar.jar']
+                        assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations._compileFreeRelease) {
+                    def files = configurations._compileFreeRelease
                     doLast {
-                        assert configurations._compileFreeRelease.collect { it.name } == ['b-bar.jar']
+                        assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -395,8 +405,8 @@
         fails ':a:checkDebug'
 
         then:
-        failure.assertHasCause """Variant 'bar' in project :b does not match the consumer attributes
-Variant 'bar' capability test:b:unspecified declares attribute 'flavor' with value 'free':
+        failure.assertHasCause """Configuration 'bar' in project :b does not match the consumer attributes
+Configuration 'bar' declares attribute 'flavor' with value 'free':
   - Incompatible because this component declares attribute 'buildType' with value 'release' and the consumer needed attribute 'buildType' with value 'debug'"""
 
         when:
@@ -420,8 +430,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == []
+                        assert files.collect { it.name } == []
                     }
                 }
             }
@@ -461,8 +472,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-bar.jar']
+                        assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -621,6 +633,7 @@
         """
 
         when:
+        expectConsumableChanging(':b:default', false)
         fails ':a:checkDebug'
 
         then:
@@ -712,6 +725,7 @@
         """
 
         when:
+        expectConsumableChanging(':b:default', false)
         fails ':a:checkDebug'
 
         then:
@@ -740,8 +754,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar']
+                        assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
             }
@@ -863,8 +878,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar']
+                        assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
             }
@@ -1092,13 +1108,15 @@
                     _compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar', 'c-transitive.jar']
+                       assert files.collect { it.name } == ['b-foo.jar', 'c-transitive.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations._compileFreeRelease) {
+                    def files = configurations._compileFreeRelease
                     doLast {
-                       assert configurations._compileFreeRelease.collect { it.name } == ['b-bar.jar', 'd-transitive.jar']
+                       assert files.collect { it.name } == ['b-bar.jar', 'd-transitive.jar']
                     }
                 }
             }
@@ -1169,13 +1187,15 @@
                     _compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-transitive.jar', 'c-foo.jar']
+                       assert files.collect { it.name } == ['b-transitive.jar', 'c-foo.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations._compileFreeRelease) {
+                    def files = configurations._compileFreeRelease
                     doLast {
-                       assert configurations._compileFreeRelease.collect { it.name } == ['b-transitive.jar', 'c-bar.jar']
+                       assert files.collect { it.name } == ['b-transitive.jar', 'c-bar.jar']
                     }
                 }
             }
@@ -1248,13 +1268,15 @@
                     _compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-transitive.jar', 'c-foo.jar']
+                       assert files.collect { it.name } == ['b-transitive.jar', 'c-foo.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations._compileFreeRelease) {
+                    def files = configurations._compileFreeRelease
                     doLast {
-                       assert configurations._compileFreeRelease.collect { it.name } == ['b-transitive.jar', 'c-bar.jar']
+                       assert files.collect { it.name } == ['b-transitive.jar', 'c-bar.jar']
                     }
                 }
                 configurations.all {
@@ -1448,13 +1470,15 @@
                     _compileFreeDebug 'org.apache.commons:commons-lang3:3.5'
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name }.sort { it } == ['b-transitive.jar', 'c-foo.jar', 'commons-lang3-3.5.jar']
+                       assert files.collect { it.name }.sort { it } == ['b-transitive.jar', 'c-foo.jar', 'commons-lang3-3.5.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations._compileFreeRelease) {
+                    def files = configurations._compileFreeRelease
                     doLast {
-                       assert configurations._compileFreeRelease.collect { it.name }.sort { it } == ['b-transitive.jar', 'c-bar.jar', 'commons-lang3-3.4.jar']
+                       assert files.collect { it.name }.sort { it } == ['b-transitive.jar', 'c-bar.jar', 'commons-lang3-3.4.jar']
                     }
                 }
             }
@@ -1526,13 +1550,13 @@
                         extendsFrom(compileFreeDebug)
                         attributes { $freeDebug }
                         canBeConsumed = false
-                        canBeResolved = true
+                        assert canBeResolved
                     }
                     compileFreeReleasePath {
                         extendsFrom(compileFreeRelease)
                         attributes { $freeRelease }
                         canBeConsumed = false
-                        canBeResolved = true
+                        assert canBeResolved
                     }
                 }
                 dependencies {
@@ -1540,13 +1564,15 @@
                     compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations.compileFreeDebugPath) {
+                    def files = configurations.compileFreeDebugPath
                     doLast {
-                       assert configurations.compileFreeDebugPath.collect { it.name } == ['b-foo.jar']
+                       assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
                 task checkRelease(dependsOn: configurations.compileFreeReleasePath) {
+                    def files = configurations.compileFreeReleasePath
                     doLast {
-                       assert configurations.compileFreeReleasePath.collect { it.name } == ['b-bar.jar']
+                       assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -1610,8 +1636,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar']
+                        assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
             }
@@ -1648,8 +1675,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar']
+                        assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
             }
@@ -1701,8 +1729,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar', 'c-foo.jar']
+                        assert files.collect { it.name } == ['b-foo.jar', 'c-foo.jar']
                     }
                 }
             }
@@ -1770,8 +1799,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                        assert configurations._compileFreeDebug.collect { it.name } == ['b-foo.jar']
+                        assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
             }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ArtifactViewArtifactSelectionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ArtifactViewArtifactSelectionIntegrationTest.groovy
index ba9f8a9..e934580 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ArtifactViewArtifactSelectionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ArtifactViewArtifactSelectionIntegrationTest.groovy
@@ -28,7 +28,7 @@
             configurations {
                 resolveConf {
                     canBeConsumed = false
-                    canBeResolved = true
+                    assert canBeResolved
                 } 
             }
         """
@@ -40,11 +40,11 @@
         file("producer/build.gradle") << """
             configurations {
                 conf {
-                    canBeConsumed = true
+                    assert canBeConsumed
                     canBeResolved = false
                 }
                 confWithAttributes {
-                    canBeConsumed = true
+                    assert canBeConsumed
                     canBeResolved = false
                     attributes {
                         attribute(Attribute.of("usage", String), "alternative")
@@ -80,11 +80,11 @@
         file("producer/build.gradle") << """
             configurations {
                 conf {
-                    canBeConsumed = true
+                    assert canBeConsumed
                     canBeResolved = false
                 }
                 additionalConf {
-                    canBeConsumed = true
+                    assert canBeConsumed
                     canBeResolved = false
                 }
             }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ClasspathDependenciesAttributesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ClasspathDependenciesAttributesIntegrationTest.groovy
index f4b6a7e..cf45b99 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ClasspathDependenciesAttributesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ClasspathDependenciesAttributesIntegrationTest.groovy
@@ -172,13 +172,13 @@
 version = '1.0'
 configurations {
     conf {
-        canBeConsumed = true
+        assert canBeConsumed
         attributes {
             attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
         }
     }
     badConf {
-        canBeConsumed = true
+        assert canBeConsumed
         attributes {
             attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, 'not-good'))
         }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ComponentAttributesDynamicVersionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ComponentAttributesDynamicVersionIntegrationTest.groovy
index 63163d6..756e4de 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ComponentAttributesDynamicVersionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ComponentAttributesDynamicVersionIntegrationTest.groovy
@@ -128,7 +128,10 @@
         run ':checkDeps'
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:module:${requested}", 'org.test:module:1.1')
+                edge("org.test:module:${requested}", 'org.test:module:1.1') {
+                    byReason("rejection: version 1.3:   - Attribute 'quality' didn't match. Requested 'qa', was: 'rc'")
+                    byReason("rejection: version 1.2:   - Attribute 'quality' didn't match. Requested 'qa', was: 'rc'")
+                }
             }
         }
 
@@ -197,7 +200,10 @@
         run ':checkDeps'
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:module:${requested}", 'org.test:module:1.1')
+                edge("org.test:module:${requested}", 'org.test:module:1.1') {
+                    byReason("rejection: version 1.3:   - Attribute 'quality' didn't match. Requested 'qa', was: 'rc'")
+                    byReason("rejection: version 1.2:   - Attribute 'quality' didn't match. Requested 'qa', was: 'rc'")
+                }
             }
         }
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/CrossProjectMultipleVariantSelectionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/CrossProjectMultipleVariantSelectionIntegrationTest.groovy
index 7f7b782..b847985 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/CrossProjectMultipleVariantSelectionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/CrossProjectMultipleVariantSelectionIntegrationTest.groovy
@@ -47,7 +47,7 @@
             configurations {
                 testFixtures {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, 'java-api'))
                         attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, 'jar'))
@@ -104,7 +104,7 @@
             configurations {
                 testFixtures {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, 'java-api'))
                         attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, 'jar'))
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/DependenciesAttributesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/DependenciesAttributesIntegrationTest.groovy
index 8487751..0c9fa96 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/DependenciesAttributesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/DependenciesAttributesIntegrationTest.groovy
@@ -96,7 +96,9 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge('org:test', 'org:test:1.0')
+                edge('org:test', 'org:test:1.0') {
+                    byConstraint()
+                }
                 constraint('org:test:1.0', 'org:test:1.0')
             }
         }
@@ -486,6 +488,7 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge('org:test', 'org:test:1.0') {
+                    byConstraint()
                     configuration = expectedVariant
                     variant(expectedVariant, expectedAttributes)
                 }
@@ -546,6 +549,7 @@
             root(":", ":test:") {
                 module('org:test:1.0') {
                     configuration = expectedVariant
+                    byConstraint()
                     variant(expectedVariant, expectedAttributes)
                 }
                 constraint('org:test', 'org:test:1.0') {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ExclusiveVariantsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ExclusiveVariantsIntegrationTest.groovy
index 8bf8312..8e8cead 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ExclusiveVariantsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ExclusiveVariantsIntegrationTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.integtests.resolve.attributes
 
+
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
 class ExclusiveVariantsIntegrationTest extends AbstractIntegrationSpec {
@@ -29,7 +30,7 @@
             configurations {
                 sample1 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -42,7 +43,7 @@
 
                 sample2 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -54,7 +55,7 @@
                 }
 
                 resolver {
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -75,7 +76,9 @@
 
         expect:
         fails("resolveSample")
-        failure.assertHasDocumentedCause("Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes, but configuration ':sample2' and [configuration ':sample1'] contain identical attribute sets. Consider adding an additional attribute to one of the configurations to disambiguate them.  Run the 'outgoingVariants' task for more details. See https://docs.gradle.org/current/userguide/upgrading_version_7.html#unique_attribute_sets for more details.")
+        failure.assertHasDocumentedCause("Consumable configurations with identical capabilities within a project (other than the default configuration) must have unique attributes, but configuration ':sample2' and [configuration ':sample1'] contain identical attribute sets. " +
+            "Consider adding an additional attribute to one of the configurations to disambiguate them.  " +
+            "Run the 'outgoingVariants' task for more details. ${documentationRegistry.getDocumentationRecommendationFor("information", "upgrading_version_7", "unique_attribute_sets")}")
 
         where:
         capability << ['default', 'org.test:sample:1.0']
@@ -91,7 +94,7 @@
             configurations {
                 sample1 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -104,7 +107,7 @@
 
                 sample2 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -138,7 +141,7 @@
             configurations {
                 sample1 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -148,7 +151,7 @@
 
                 sample2 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -182,7 +185,7 @@
             configurations {
                 sample1 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -192,7 +195,7 @@
 
                 sample2 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -220,7 +223,7 @@
             configurations {
                 sample1 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -234,7 +237,7 @@
 
                 sample2 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -263,7 +266,7 @@
             configurations {
                 sampleA {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -286,7 +289,7 @@
             configurations {
                 sampleB {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -327,7 +330,7 @@
             configurations {
                 sampleA {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -353,7 +356,7 @@
             configurations {
                 sampleB {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -387,7 +390,7 @@
 
             configurations {
                 sample1 {
-                    canBeResolved = true
+                    assert canBeResolved
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -396,7 +399,7 @@
                 }
 
                 sample2 {
-                    canBeResolved = true
+                    assert canBeResolved
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -420,8 +423,8 @@
             configurations {
                 sample1 {
                     // Configurations that are both resolvable and consumable are legacy and should not be used
-                    canBeResolved = true
-                    canBeConsumed = true
+                    assert canBeResolved
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -431,8 +434,8 @@
 
                 sample2 {
                     // Configurations that are both resolvable and consumable are legacy and should not be used
-                    canBeResolved = true
-                    canBeConsumed = true
+                    assert canBeResolved
+                    assert canBeConsumed
 
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/LazyAttributesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/LazyAttributesIntegrationTest.groovy
index 178a128..586f127 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/LazyAttributesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/LazyAttributesIntegrationTest.groovy
@@ -34,7 +34,7 @@
                 sample {
                     visible = false
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
 
                     attributes {
                         attributeProvider(Usage.USAGE_ATTRIBUTE, sampleProperty.map(value -> objects.named(Usage, value)))
@@ -70,7 +70,7 @@
                 sample {
                     visible = false
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     extendsFrom(configurations.implementation)
 
                     attributes {
@@ -97,7 +97,7 @@
                 sample {
                     visible = false
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     extendsFrom(configurations.implementation)
 
                     attributes {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/MultipleVariantSelectionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/MultipleVariantSelectionIntegrationTest.groovy
index a08ea35..76f8163 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/MultipleVariantSelectionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/MultipleVariantSelectionIntegrationTest.groovy
@@ -260,10 +260,9 @@
         } else {
             resolve.expectGraph {
                 root(":", ":test:") {
-                    edge('org:test:1.0', 'org:test:1.0') {
-                        variant('runtime', ['org.gradle.status': MultipleVariantSelectionIntegrationTest.defaultStatus(), 'org.gradle.usage': 'java-runtime', 'org.gradle.libraryelements': 'jar', 'org.gradle.category': 'library', custom: 'c2'])
-                    }
+                    edge('org:test:1.0', 'org:test:1.0')
                     module('org:test:1.0') {
+                        maybeByConflictResolution()
                         variant('runtime', ['org.gradle.status': MultipleVariantSelectionIntegrationTest.defaultStatus(), 'org.gradle.usage': 'java-runtime', 'org.gradle.libraryelements': 'jar', 'org.gradle.category': 'library', custom: 'c2'])
                     }
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ProjectDependenciesAttributesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ProjectDependenciesAttributesIntegrationTest.groovy
index 0f7d1af..7f678118 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ProjectDependenciesAttributesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ProjectDependenciesAttributesIntegrationTest.groovy
@@ -132,14 +132,14 @@
             configurations {
                 blueVariant {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Attribute.of('color', String), 'blue')
                     }
                 }
                 redVariant {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Attribute.of('color', String), 'red')
                     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ProjectVariantResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ProjectVariantResolutionIntegrationTest.groovy
index b671785..20c12e6 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ProjectVariantResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/ProjectVariantResolutionIntegrationTest.groovy
@@ -44,7 +44,7 @@
 
                 configurations {
                     broken {
-                        canBeConsumed = true
+                        assert canBeConsumed
                         canBeResolved = false
                         attributes.attribute(color, 'orange')
                         outgoing {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/StringConfigurationAttributesResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/StringConfigurationAttributesResolveIntegrationTest.groovy
index fb037b2..e7d0484 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/StringConfigurationAttributesResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/StringConfigurationAttributesResolveIntegrationTest.groovy
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 package org.gradle.integtests.resolve.attributes
+
 /**
  * Variant of the configuration attributes resolution integration test which makes use of the string attributes notation.
  */
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/StronglyTypedConfigurationAttributesResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/StronglyTypedConfigurationAttributesResolveIntegrationTest.groovy
index 00dd804..4422eb2 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/StronglyTypedConfigurationAttributesResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/StronglyTypedConfigurationAttributesResolveIntegrationTest.groovy
@@ -188,8 +188,9 @@
                     _compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-foo2.jar']
+                       assert files.collect { it.name } == ['b-foo2.jar']
                     }
                 }
             }
@@ -263,8 +264,9 @@
                     _compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-foo2.jar']
+                       assert files.collect { it.name } == ['b-foo2.jar']
                     }
                 }
             }
@@ -458,8 +460,9 @@
                     _compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-foo2.jar']
+                       assert files.collect { it.name } == ['b-foo2.jar']
                     }
                 }
             }
@@ -548,13 +551,15 @@
                     _compileDebug project(':b')
                 }
                 task checkFreeDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-foo2.jar']
+                       assert files.collect { it.name } == ['b-foo2.jar']
                     }
                 }
                 task checkDebug(dependsOn: configurations._compileDebug) {
+                    def files = configurations._compileDebug
                     doLast {
-                       assert configurations._compileDebug.collect { it.name } == ['b-foo.jar']
+                       assert files.collect { it.name } == ['b-foo.jar']
                     }
                 }
             }
@@ -628,8 +633,9 @@
                     _compileFreeRelease project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-bar.jar']
+                       assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -701,8 +707,9 @@
                     _compileFreeDebug project(':b')
                 }
                 task checkDebug(dependsOn: configurations._compileFreeDebug) {
+                    def files = configurations._compileFreeDebug
                     doLast {
-                       assert configurations._compileFreeDebug.collect { it.name } == ['b-bar.jar']
+                       assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -762,8 +769,9 @@
                     compile project(':b')
                 }
                 task check(dependsOn: configurations.compile) {
+                    def files = configurations.compile
                     doLast {
-                       assert configurations.compile.collect { it.name } == ['b-bar.jar']
+                       assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -821,8 +829,9 @@
                     compile project(':b')
                 }
                 task check(dependsOn: configurations.compile) {
+                    def files = configurations.compile
                     doLast {
-                       assert configurations.compile.collect { it.name } == ['b-bar.jar']
+                       assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -878,8 +887,9 @@
                     compile project(':b')
                 }
                 task check(dependsOn: configurations.compile) {
+                    def files = configurations.compile
                     doLast {
-                       assert configurations.compile.collect { it.name } == ['b-bar.jar']
+                       assert files.collect { it.name } == ['b-bar.jar']
                     }
                 }
             }
@@ -956,8 +966,9 @@
                     compile project(':c')
                 }
                 task check(dependsOn: configurations.compile) {
+                    def files = configurations.compile
                     doLast {
-                       assert configurations.compile.collect { it.name } == ['b-bar.jar', 'c-bar.jar']
+                       assert files.collect { it.name } == ['b-bar.jar', 'c-bar.jar']
                     }
                 }
             }
@@ -1057,9 +1068,10 @@
                     compile project(':b')
                 }
                 task checkDebug(dependsOn: configurations.compile) {
+                    def files = configurations.compile
                     doLast {
                         // Compatibility rules select paid flavors, disambiguation rules select debug
-                        assert configurations.compile.collect { it.name } == ['b-foo2.jar']
+                        assert files.collect { it.name } == ['b-foo2.jar']
                     }
                 }
             }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/VariantAwareResolutionWithConfigurationAttributesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/VariantAwareResolutionWithConfigurationAttributesIntegrationTest.groovy
index 3aa9215..a6b8a69 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/VariantAwareResolutionWithConfigurationAttributesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/attributes/VariantAwareResolutionWithConfigurationAttributesIntegrationTest.groovy
@@ -58,7 +58,7 @@
                                 }
                                 def compileConfig = p.configurations.create("compile$baseName") {
                                     extendsFrom p.configurations.implementation
-                                    canBeConsumed = true
+                                    assert canBeConsumed
                                     canBeResolved = false
                                     attributes.attribute(buildType, bt)
                                     attributes.attribute(flavor, f)
@@ -67,7 +67,7 @@
                                 def _compileConfig = p.configurations.create("_compile$baseName") {
                                     extendsFrom implementationConfig
                                     canBeConsumed = false
-                                    canBeResolved = true
+                                    assert canBeResolved
                                     attributes.attribute(buildType, bt)
                                     attributes.attribute(flavor, f)
                                     attributes.attribute(usage, 'compile')
@@ -118,6 +118,7 @@
         '''
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses the Configuration API")
     def "configurations are wired properly"() {
         withVariants(buildFile)
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy
index 91308f8..225e48e 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachedMissingModulesIntegrationTest.groovy
@@ -23,7 +23,6 @@
 
 class CachedMissingModulesIntegrationTest extends AbstractHttpDependencyResolutionTest {
 
-    @ToBeFixedForConfigurationCache
     def "caches missing module when module found in another repository"() {
         given:
         def repo1 = ivyHttpRepo("repo1")
@@ -40,7 +39,10 @@
 dependencies {
     missing 'group:projectA:1.2'
 }
-task showMissing { doLast { println configurations.missing.files } }
+task showMissing {
+    def missing = configurations.missing
+    doLast { println missing.files }
+}
 """
 
         when:
@@ -58,7 +60,6 @@
         succeeds('showMissing')
     }
 
-    @ToBeFixedForConfigurationCache
     def "caches missing changing module when module found in another repository"() {
         given:
         def repo1 = ivyHttpRepo("repo1")
@@ -80,7 +81,10 @@
 dependencies {
     missing group: 'group', name: 'projectA', version: '1.2', changing: true
 }
-task showMissing { doLast { println configurations.missing.files } }
+task showMissing {
+    def missing = configurations.missing
+    doLast { println missing.files }
+}
 """
 
         when:
@@ -100,7 +104,6 @@
         succeeds('showMissing')
     }
 
-    @ToBeFixedForConfigurationCache
     def "checks for missing modules in each repository when run with --refresh-dependencies"() {
         given:
         def repo1 = ivyHttpRepo("repo1")
@@ -117,7 +120,10 @@
 dependencies {
     missing 'group:projectA:1.2'
 }
-task showMissing { doLast { println configurations.missing.files } }
+task showMissing {
+    def missing = configurations.missing
+    doLast { println missing.files }
+}
 """
 
         when:
@@ -442,7 +448,7 @@
         run 'retrieve'
     }
 
-    @ToBeFixedForConfigurationCache
+    @ToBeFixedForConfigurationCache(because = "HTTP server interactions are different when CC is enabled")
     def "cached missing module is ignored when no module for dynamic version is available in any repo"() {
         given:
         def repo1 = mavenHttpRepo("repo1")
@@ -467,7 +473,10 @@
         conf2 'group:projectA:1.+'
     }
 
-    task cache { doLast { configurations.conf1.files } }
+    task cache {
+        def conf1 = configurations.conf1
+        doLast { println conf1.files }
+    }
     task retrieve(type: Sync) {
         into 'libs'
         from configurations.conf2
@@ -535,14 +544,13 @@
     }
 
     @IgnoreIf({ GradleContextualExecuter.isParallel() })
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "hit each remote repo only once per build and missing module"() {
         given:
         def repo1 = mavenHttpRepo("repo1")
         def repo1Module = repo1.module("group", "projectA", "1.0")
-        def repo1Artifact = repo1Module.artifact
         def repo2 = mavenHttpRepo("repo2")
         def repo2Module = repo2.module("group", "projectA", "1.0")
-        def repo2Artifact = repo2Module.artifact
 
         settingsFile << "include 'subproject'"
         buildFile << """
@@ -568,7 +576,7 @@
             task resolveConfig1 {
                 doLast {
                    configurations.config1.incoming.resolutionResult.allDependencies{
-                        it instanceof UnresolvedDependencyResult
+                        assert it instanceof UnresolvedDependencyResult
                    }
                }
             }
@@ -583,7 +591,7 @@
                 task resolveConfig2 {
                     doLast {
                         configurations.config2.incoming.resolutionResult.allDependencies{
-                            it instanceof UnresolvedDependencyResult
+                            assert it instanceof UnresolvedDependencyResult
                         }
                     }
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy
index 1160468..0c64f16 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/CachingDependencyMetadataInMemoryIntegrationTest.groovy
@@ -41,8 +41,9 @@
             }
             //runs first and resolves
             task resolveOne {
+                def files = configurations.one
                 doLast {
-                    configurations.one.files
+                    files.files
                 }
             }
             //runs second, purges repo
@@ -51,8 +52,9 @@
             }
             //runs last, still works even though local repo is empty
             task resolveTwo(dependsOn: purgeRepo) {
+                def files = configurations.two
                 doLast {
-                    println "Resolved " + configurations.two.files*.name
+                    println "Resolved " + files*.name
                 }
             }
         """
@@ -75,7 +77,10 @@
                 configurations { conf }
                 repositories { ivy { url "${ivyHttpRepo.uri}" } }
                 dependencies { conf 'org:lib:1.0' }
-                task resolveConf { doLast { println path + " " + configurations.conf.files*.name } }
+                task resolveConf {
+                    def files = configurations.conf
+                    doLast { println path + " " + files*.name }
+                }
             }
             resolveConf.dependsOn(':impl:resolveConf')
         """
@@ -100,9 +105,18 @@
 
         file("build.gradle") << """
             allprojects {
-                configurations { conf }
+                configurations {
+                    conf {
+                        incoming.afterResolve { deps ->
+                            println "\${project.path} " + deps.files*.name
+                        }
+                    }
+                }
                 dependencies { conf 'org:lib:1.0' }
-                task resolveConf { doLast { println "\$path " + configurations.conf.files*.name } }
+                task resolveConf {
+                    def files = configurations.conf
+                    doLast { files.files }
+                }
             }
             repositories { ivy { url "${ivyRepo.uri}" } }
             project(":impl") {
@@ -115,7 +129,7 @@
         runAndFail ":impl:resolveConf"
 
         then:
-        output.contains ':resolveConf [lib-1.0.jar]'
+        output.contains ': [lib-1.0.jar]'
         //uses different repo that does not contain this dependency
         failure.assertResolutionFailure(":impl:conf").assertHasCause("Could not find org:lib:1.0")
     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ConcurrentBuildsCachingIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ConcurrentBuildsCachingIntegrationTest.groovy
index 0646e8d..c9db763 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ConcurrentBuildsCachingIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ConcurrentBuildsCachingIntegrationTest.groovy
@@ -45,13 +45,15 @@
     b "group1:module2:0.99"
 }
 task a {
+    def files = configurations.a
     doLast {
-        configurations.a.files
+        files.files
     }
 }
 task b {
+    def files = configurations.b
     doLast {
-        configurations.b.files
+        files.files
     }
 }
 task block1 {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ConcurrentDependencyResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ConcurrentDependencyResolutionIntegrationTest.groovy
index 9e3fe4a..21bbc0e 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ConcurrentDependencyResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ConcurrentDependencyResolutionIntegrationTest.groovy
@@ -37,7 +37,7 @@
         buildFile << '''
              def usage = Attribute.of('usage', String)
              allprojects {
-            
+
                 dependencies {
                     attributesSchema {
                         attribute(usage)
@@ -55,14 +55,15 @@
                     }
                     'default'
                 }
-                
+
                 task resolve {
+                    def files = configurations.compile
                     doLast {
-                        configurations.compile.files
+                        files.files
                     }
                 }
             }
-           
+
         '''
         int groups = 20
         int iterations = 100
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ParallelDependencyResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ParallelDependencyResolutionIntegrationTest.groovy
index d28fb58..25b0670 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ParallelDependencyResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/caching/ParallelDependencyResolutionIntegrationTest.groovy
@@ -58,7 +58,7 @@
                 task resolveDependencies {
                     def compileClasspath = configurations.compileClasspath
                     doLast {
-                        compileClasspath.resolve()
+                        compileClasspath.files
                     }
                 }
             """
@@ -96,7 +96,7 @@
                 task resolveDependencies {
                     def compileClasspath = configurations.compileClasspath
                     doLast {
-                        compileClasspath.resolve()
+                        compileClasspath.files
                     }
                 }
             """
@@ -122,7 +122,7 @@
                 task resolveDependencies {
                     def compileClasspath = configurations.compileClasspath
                     doLast {
-                        compileClasspath.resolve()
+                        compileClasspath.files
                     }
                 }
             """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/CapabilitiesRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/CapabilitiesRulesIntegrationTest.groovy
index a9efa8e..a9bf908 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/CapabilitiesRulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/CapabilitiesRulesIntegrationTest.groovy
@@ -182,13 +182,12 @@
 
 
             tasks.register("dumpCapabilitiesFromArtifactView") {
+                def artifactCollection = configurations.conf.incoming.artifactView {
+                    attributes {
+                        attribute(Attribute.of("artifactType", String), "jar")
+                    }
+                }.artifacts
                 doFirst {
-                    def artifactCollection = configurations.conf.incoming.artifactView {
-                        attributes {
-                            attribute(Attribute.of("artifactType", String), "jar")
-                        }
-                    }.artifacts
-
                     artifactCollection.artifacts.each {
                         println "Artifact: \${it.id.componentIdentifier.displayName}"
                         println "  - artifact: \${it.file}"
@@ -514,7 +513,11 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge('jakarta:xml', 'jakarta:xml:1.0') {
-                    module('jakarta:activation:1.0')
+                    byConflictResolution()
+                    selectedByRule()
+                    module('jakarta:activation:1.0') {
+                        byConflictResolution()
+                    }
                 }
                 module('hadoop:yarn:1.0') {
                     edge('javax:jaxb:1.0', 'jakarta:xml:1.0')
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/CapabilitiesUseCasesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/CapabilitiesUseCasesIntegrationTest.groovy
index 743df87..935d542 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/CapabilitiesUseCasesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/CapabilitiesUseCasesIntegrationTest.groovy
@@ -207,7 +207,9 @@
                 root(":", ":test:") {
                     module('org:a:1.0') {
                         edge('org.apache:groovy:1.0', 'org.apache:groovy-all:1.0')
-                        edge('org.apache:groovy-json:1.0', 'org.apache:groovy-all:1.0')
+                        edge('org.apache:groovy-json:1.0', 'org.apache:groovy-all:1.0') {
+                            selectedByRule()
+                        }
                     }
                     module('org:b:1.0') {
                         edge('org.apache:groovy-all:1.0', 'org.apache:groovy-all:1.0')
@@ -325,7 +327,9 @@
                 root(":", ":test:") {
                     module('org:a:1.0') {
                         edge('org.apache:groovy:1.0', 'org.apache:groovy:1.0')
-                        edge('org.apache:groovy-json:1.0', 'org.apache:groovy-json:1.0')
+                        edge('org.apache:groovy-json:1.0', 'org.apache:groovy-json:1.0') {
+                            selectedByRule()
+                        }
                     }
                     module('org:b:1.0') {
                         // this is not quite right, as we should replace with 2 edges
@@ -406,7 +410,7 @@
 
         then:
         if (failOnVersionConflict) {
-            failure.assertHasCause("Conflict(s) found for the following module(s):\n  - org.ow2.asm:asm latest version of capability asm:asm")
+            failure.assertHasCause("Conflict found for the following module:\n  - org.ow2.asm:asm latest version of capability asm:asm")
         } else {
             resolve.expectGraph {
                 root(":", ":test:") {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/PublishedCapabilitiesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/PublishedCapabilitiesIntegrationTest.groovy
index f4c7a1d..16d7dfdd 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/PublishedCapabilitiesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/capabilities/PublishedCapabilitiesIntegrationTest.groovy
@@ -236,13 +236,12 @@
             configurations.conf.resolutionStrategy.capabilitiesResolution.all { selectHighestVersion() }
 
             tasks.register("dumpCapabilitiesFromArtifactView") {
+                def artifactCollection = configurations.conf.incoming.artifactView {
+                    attributes {
+                        attribute(Attribute.of("artifactType", String), "jar")
+                    }
+                }.artifacts
                 doFirst {
-                    def artifactCollection = configurations.conf.incoming.artifactView {
-                        attributes {
-                            attribute(Attribute.of("artifactType", String), "jar")
-                        }
-                    }.artifacts
-
                     artifactCollection.artifacts.each {
                         println "Artifact: \${it.id.componentIdentifier.displayName}"
                         println "  - artifact: \${it.file}"
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/KotlinDslVersionCatalogExtensionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/KotlinDslVersionCatalogExtensionIntegrationTest.groovy
index 578299b..71c599a 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/KotlinDslVersionCatalogExtensionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/KotlinDslVersionCatalogExtensionIntegrationTest.groovy
@@ -494,4 +494,40 @@
         then:
         succeeds ':checkDeps'
     }
+
+    @Issue("https://github.com/gradle/gradle/issues/24426")
+    def "can use version catalogs in buildscript block of applied script"() {
+
+        given:
+        def lib = mavenHttpRepo.module('org.gradle.test', 'lib', '1.1').publish()
+        settingsKotlinFile << """
+            dependencyResolutionManagement {
+                versionCatalogs {
+                    create("libs") {
+                        library("my-lib", "org.gradle.test:lib:1.1")
+                    }
+                }
+            }
+        """
+        buildKotlinFile << """
+            apply(from = "applied.gradle.kts")
+        """
+        file("applied.gradle.kts") << """
+            buildscript {
+                dependencies {
+                    classpath(libs.my.lib)
+                }
+                repositories {
+                    maven(url = "${mavenHttpRepo.uri}")
+                }
+            }
+        """
+
+        when:
+        lib.pom.expectGet()
+        lib.artifact.expectGet()
+
+        then:
+        succeeds ':help'
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/TomlDependenciesExtensionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/TomlDependenciesExtensionIntegrationTest.groovy
index a3648ad..b7f44dc 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/TomlDependenciesExtensionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/TomlDependenciesExtensionIntegrationTest.groovy
@@ -758,7 +758,7 @@
         then:
         verifyContains(failure.error, parseError {
             inCatalog('libs')
-            addError('At line 3, column 1: Unexpected \'/\', expected a newline or end-of-input')
+            addError("In file '${tomlFile.absolutePath}' at line 3, column 1: Unexpected \'/\', expected a newline or end-of-input")
         })
     }
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/VersionCatalogExtensionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/VersionCatalogExtensionIntegrationTest.groovy
index e9fbfa6..b5c4f39 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/VersionCatalogExtensionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/catalog/VersionCatalogExtensionIntegrationTest.groovy
@@ -19,6 +19,7 @@
 import org.gradle.api.internal.catalog.problems.VersionCatalogErrorMessages
 import org.gradle.api.internal.catalog.problems.VersionCatalogProblemId
 import org.gradle.api.internal.catalog.problems.VersionCatalogProblemTestFor
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.resolve.PluginDslSupport
 import spock.lang.Issue
 
@@ -314,9 +315,12 @@
                 constraint("org.gradle.test:lib-core:{strictly [1.0,1.1)}", "org.gradle.test:lib-core:1.0")
                 constraint("org.gradle.test:lib-ext:{strictly [1.0,1.1)}", "org.gradle.test:lib-ext:1.0")
                 edge("org.gradle.test:lib-core:1.+", "org.gradle.test:lib-core:1.0") {
+                    notRequested()
                     byReasons(["rejected version 1.1", "constraint"])
                 }
-                edge("org.gradle.test:lib-ext", "org.gradle.test:lib-ext:1.0")
+                edge("org.gradle.test:lib-ext", "org.gradle.test:lib-ext:1.0") {
+                    byConstraint()
+                }
             }
         }
     }
@@ -377,9 +381,12 @@
                 constraint("org.gradle.test:lib-core:{strictly [1.0,1.1)}", "org.gradle.test:lib-core:1.0")
                 constraint("org.gradle.test:lib-ext:{strictly [1.0,1.1)}", "org.gradle.test:lib-ext:1.0")
                 edge("org.gradle.test:lib-core:1.+", "org.gradle.test:lib-core:1.0") {
+                    notRequested()
                     byReasons(["rejected version 1.1", "constraint"])
                 }
-                edge("org.gradle.test:lib-ext", "org.gradle.test:lib-ext:1.0")
+                edge("org.gradle.test:lib-ext", "org.gradle.test:lib-ext:1.0") {
+                    byConstraint()
+                }
             }
         }
     }
@@ -463,7 +470,9 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 constraint('org.gradle.test:lib:1.1')
-                edge('org.gradle.test:lib', 'org.gradle.test:lib:1.1')
+                edge('org.gradle.test:lib', 'org.gradle.test:lib:1.1') {
+                    byConstraint()
+                }
             }
         }
     }
@@ -2219,8 +2228,12 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.gradle.test:lib:3.0.6", "org.gradle.test:lib:3.0.5")
-                edge("org.gradle.test:lib2:3.0.6", "org.gradle.test:lib2:3.0.5")
+                edge("org.gradle.test:lib:3.0.6", "org.gradle.test:lib:3.0.5") {
+                    forced()
+                }
+                edge("org.gradle.test:lib2:3.0.6", "org.gradle.test:lib2:3.0.5") {
+                    forced()
+                }
             }
         }
     }
@@ -2300,6 +2313,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/23096")
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def 'all properties of version catalog dependencies are copied when the dependency is copied'() {
         given:
         buildFile << """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/consistency/ProjectLocalDependencyResolutionConsistencyIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/consistency/ProjectLocalDependencyResolutionConsistencyIntegrationTest.groovy
index f7cc3ca..ae67287 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/consistency/ProjectLocalDependencyResolutionConsistencyIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/consistency/ProjectLocalDependencyResolutionConsistencyIntegrationTest.groovy
@@ -57,6 +57,7 @@
             root(':', ':test:') {
                 edge('org:foo:1.0', 'org:foo:1.1') {
                     byConsistentResolution('other')
+                    byConflictResolution('between versions 1.1 and 1.0')
                 }
                 constraint("org:foo:{strictly 1.1}", "org:foo:1.1")
             }
@@ -203,8 +204,12 @@
                     byConsistentResolution('compileClasspath')
                     module('org:fooA:1.0') {
                         byConsistentResolution('compileClasspath')
+                        byAncestor()
+                        notRequested()
                         module("org:transitive:1.0") {
                             byConsistentResolution('compileClasspath')
+                            byAncestor()
+                            notRequested()
                         }
                     }
                 }
@@ -289,6 +294,7 @@
             root(':', ':test:') {
                 edge('org:foo:1.1', 'org:foo:1.2') {
                     selectedByRule()
+                    byConsistentResolution('other')
                 }
                 constraint("org:foo:{strictly 1.0}", "org:foo:1.2")
             }
@@ -327,6 +333,7 @@
             root(':', ':test:') {
                 edge('org:foo:1.0', 'org:foo:1.1') {
                     byConsistentResolution('other')
+                    byConflictResolution('between versions 1.1 and 1.0')
                 }
                 constraint("org:foo:{strictly 1.1}", "org:foo:1.1")
             }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsAndResolutionStrategiesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsAndResolutionStrategiesIntegrationTest.groovy
index 62b2919..2c931d7 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsAndResolutionStrategiesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsAndResolutionStrategiesIntegrationTest.groovy
@@ -66,7 +66,10 @@
             root(":", ":test:") {
                 constraint("org:foo:1.1","org:foo:1.0")
                 module("org:bar:1.0") {
-                    edge("org:foo:1.0","org:foo:1.0")
+                    edge("org:foo:1.0","org:foo:1.0") {
+                        forced()
+                        byConstraint()
+                    }
                 }
             }
         }
@@ -90,7 +93,7 @@
         fails 'checkDeps'
 
         then:
-        failure.assertHasCause """Conflict(s) found for the following module(s):
+        failure.assertHasCause """Conflict found for the following module:
   - org:foo between versions 1.1 and 1.0"""
     }
 
@@ -118,7 +121,10 @@
             root(":", ":test:") {
                 constraint("org:foo:1.1","org:foo:1.0")
                 module("org:bar:1.0") {
-                    edge("org:foo:1.0","org:foo:1.0")
+                    edge("org:foo:1.0","org:foo:1.0") {
+                        selectedByRule()
+                        byConstraint()
+                    }
                 }
             }
         }
@@ -150,7 +156,11 @@
             root(":", ":test:") {
                 constraint("org:foo:1.1","org:foo:1.0")
                 module("org:bar:1.0") {
-                    edge("org:foo:1.0","org:foo:1.0")
+                    selectedByRule()
+                    edge("org:foo:1.0","org:foo:1.0") {
+                        selectedByRule()
+                        byConstraint()
+                    }
                 }
             }
         }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsIntegrationTest.groovy
index 9a7062e..b21d86d 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/DependencyConstraintsIntegrationTest.groovy
@@ -89,7 +89,9 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org:foo", "org:foo:1.1")
+                edge("org:foo", "org:foo:1.1") {
+                    byConstraint()
+                }
                 constraint("org:foo:1.1", "org:foo:1.1")
             }
         }
@@ -148,7 +150,10 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module("org:bar:1.0") {
-                    edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0")
+                    edge("org:foo:1.0", "org:foo:1.1") {
+                        byConstraint()
+                        byConflictResolution("between versions 1.1 and 1.0")
+                    }
                 }
                 constraint("org:foo:1.1", "org:foo:1.1")
             }
@@ -332,7 +337,10 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 constraint("org:bar:1.1", "org:foo:1.1").selectedByRule()
-                edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0")
+                edge("org:foo:1.0", "org:foo:1.1") {
+                    byConstraint()
+                    byConflictResolution("between versions 1.1 and 1.0")
+                }
             }
         }
     }
@@ -366,7 +374,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0")
+                edge("org:foo:1.0", "org:foo:1.1") {
+                    byConstraint()
+                    byConflictResolution("between versions 1.1 and 1.0")
+                }
                 constraint("org:foo:1.1", "org:foo:1.1")
             }
         }
@@ -459,6 +470,7 @@
             root(":", ":test:") {
                 edge("org:foo:1.0", "org:foo:1.1") {
                     configuration("runtime")
+                    byConstraint()
                     byConflictResolution("between versions 1.1 and 1.0")
                 }
                 edge("org:included:1.0", ":includeBuild", "org:included:1.0") {
@@ -498,7 +510,9 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org:foo", "org:foo:1.0")
+                edge("org:foo", "org:foo:1.0") {
+                    byConstraint()
+                }
                 constraint("org:foo:1.0")
             }
         }
@@ -525,10 +539,12 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org:foo:1.0", "org:foo:1.1")
-                constraint("org:foo:1.1", "org:foo:1.1") {
+                edge("org:foo:1.0", "org:foo:1.1") {
+                    byConstraint()
+                    byConflictResolution("between versions 1.1 and 1.0")
                     artifact(classifier: 'shaded')
                 }
+                constraint("org:foo:1.1", "org:foo:1.1")
             }
         }
     }
@@ -556,11 +572,13 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                constraint("org:foo:1.1", "org:foo:1.1") {
-                    artifact(classifier: 'shaded')
-                }
+                constraint("org:foo:1.1", "org:foo:1.1")
                 module("org:bar:1.0") {
-                    edge("org:foo:1.0", "org:foo:1.1")
+                    edge("org:foo:1.0", "org:foo:1.1") {
+                        byConstraint()
+                        byConflictResolution("between versions 1.1 and 1.0")
+                        artifact(classifier: 'shaded')
+                    }
                 }
             }
         }
@@ -588,10 +606,12 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                constraint("org:bar:1.1", "org:bar:1.1") {
+                constraint("org:bar:1.1", "org:bar:1.1")
+                edge('org:bar:1.0', 'org:bar:1.1') {
+                    byConstraint()
+                    byConflictResolution("between versions 1.1 and 1.0")
                     edge('org:foo:1.0', 'org:foo:1.0')
                 }
-                edge('org:bar:1.0', 'org:bar:1.1')
             }
         }
     }
@@ -618,10 +638,11 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                constraint("org:bar:1.1", "org:bar:1.1") {
+                constraint("org:bar:1.1", "org:bar:1.1")
+                edge('org:bar', 'org:bar:1.1') {
+                    byConstraint()
                     edge('org:foo:1.0', 'org:foo:1.0')
                 }
-                edge('org:bar', 'org:bar:1.1')
             }
         }
     }
@@ -716,7 +737,11 @@
                 }
                 module("org:indirect:1.0") {
                     edge("org:user:1.0", "org:user:1.1") {
-                        edge("org:constrained:1.0", "org:constrained:1.1")
+                        byConflictResolution("between versions 1.1 and 1.0")
+                        edge("org:constrained:1.0", "org:constrained:1.1") {
+                            byConstraint()
+                            byConflictResolution("between versions 1.1 and 1.0")
+                        }
                     }
                     module("org:otherUser:1.0") {
                         module("org:user:1.1")
@@ -835,6 +860,8 @@
             root(':', ':test:') {
                 edge("org:foo:1.0", ":foo", "org:foo:1.1") {
                     configuration = 'default'
+                    byConstraint()
+                    byConflictResolution("between versions 1.1 and 1.0")
                     noArtifacts()
                     project(":bar", "org:bar:1.1") {
                         configuration = 'default'
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/PublishedDependencyConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/PublishedDependencyConstraintsIntegrationTest.groovy
index 33098d3..4f483f6 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/PublishedDependencyConstraintsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/constraints/PublishedDependencyConstraintsIntegrationTest.groovy
@@ -18,6 +18,7 @@
 import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
 import org.gradle.integtests.fixtures.RequiredFeature
 import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
+import spock.lang.Issue
 
 class PublishedDependencyConstraintsIntegrationTest extends AbstractModuleDependencyResolveTest {
 
@@ -219,7 +220,10 @@
                 }
                 module("org:first-level2:1.0") {
                     if (available) {
-                        edge("org:foo:1.0","org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0")
+                        edge("org:foo:1.0","org:foo:1.1") {
+                            byConstraint()
+                            byConflictResolution("between versions 1.1 and 1.0")
+                        }
                     } else {
                         module("org:foo:1.0")
                     }
@@ -284,7 +288,9 @@
             root(":", ":test:") {
                 module("org:bar:1.0") {
                     if (available) {
-                        edge("org:foo:[1.1,1.2]", "org:foo:1.1")
+                        edge("org:foo:[1.1,1.2]", "org:foo:1.1") {
+                            byConstraint("didn't match version 1.2")
+                        }
                     } else {
                         edge("org:foo:[1.1,1.2]", "org:foo:1.2")
                     }
@@ -392,8 +398,12 @@
             root(":", ":test:") {
                 module("org:first-level:1.0") {
                     if (available) {
-                        constraint("org:bar:1.1", "org:foo:1.1").selectedByRule()
-                        edge("org:foo:1.0", "org:foo:1.1").byConflictResolution("between versions 1.1 and 1.0")
+                        constraint("org:bar:1.1", "org:foo:1.1")
+                        edge("org:foo:1.0", "org:foo:1.1") {
+                            selectedByRule()
+                            byConstraint()
+                            byConflictResolution("between versions 1.1 and 1.0")
+                        }
                     } else {
                         module("org:foo:1.0")
                     }
@@ -452,7 +462,9 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                edge('org:weird:1.0', 'org:weird:1.1')
+                edge('org:weird:1.0', 'org:weird:1.1') {
+                    byConflictResolution("between versions 1.1 and 1.0")
+                }
                 module('org:other:1.0') {
                     module('org:bar:1.0')
                     module('org:weird:1.1')
@@ -531,7 +543,9 @@
                     noArtifacts()
                     configuration(platformConfiguration)
                 }
-                edge('org:first:1.0', 'org:first:2.0')
+                edge('org:first:1.0', 'org:first:2.0') {
+                    byConflictResolution("between versions 2.0 and 1.0")
+                }
                 module('org:second:1.0') {
                     module('org:intermediate:1.0') {
                         module('org:first:2.0')
@@ -541,4 +555,100 @@
         }
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/24037")
+    @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value="true")
+    void "duplicate constraint going back to pending not leave hanging edge"() {
+        when:
+        repository {
+            'org:foundation:1.0' {
+                dependsOn 'org:compose:1.0'
+            }
+            'org:compose:1.0' {
+                dependsOn 'org:activity-ktx:1.7'
+            }
+            'org:activity-ktx:1.5' {
+                constraint 'org:activity-compose:1.5'
+            }
+            'org:activity-ktx:1.7' {
+                constraint 'org:activity-compose:1.7'
+            }
+
+            'org:activity-compose:1.5' {
+                dependsOn 'org:activity-ktx:1.5'
+                dependsOn 'org:lifecycle-java8:2.5'
+            }
+            'org:activity-compose:1.7' {
+                dependsOn 'org:activity-ktx:1.7'
+                constraint 'org:activity-ktx:1.7'
+            }
+
+            'org:lifecycle-java8:2.5' {
+                dependsOn 'org:annotation:1.1'
+                dependsOn 'org:lifecycle-common:2.5'
+            }
+            'org:lifecycle-runtime:2.6' {
+                dependsOn 'org:annotation:1.1'
+                dependsOn 'org:lifecycle-common:2.6'
+                constraint 'org:lifecycle-common:2.6'
+            }
+            'org:annotation:1.1'()
+            'org:lifecycle-common:2.6' {
+                dependsOn 'org:annotation:1.1'
+                dependsOn 'org:coroutines:1.6'
+                constraint 'org:lifecycle-runtime:2.6'
+                // Constraint duplicated to match reproducer
+                constraint 'org:lifecycle-java8:2.5'
+                constraint 'org:lifecycle-java8:2.5'
+            }
+            'org:coroutines:1.6'()
+        }
+
+        buildFile << """
+dependencies {
+    conf 'org:foundation:1.0'
+    conf 'org:activity-compose:1.5'
+    conf 'org:lifecycle-runtime:2.6'
+}
+"""
+
+        repositoryInteractions {
+            'org:foundation:1.0' {
+                allowAll()
+            }
+            'org:compose:1.0' {
+                allowAll()
+            }
+            'org:activity-ktx:1.5' {
+                allowAll()
+            }
+            'org:activity-ktx:1.7' {
+                allowAll()
+            }
+            'org:activity-compose:1.5' {
+                allowAll()
+            }
+            'org:lifecycle-java8:2.5' {
+                allowAll()
+            }
+            'org:activity-compose:1.7' {
+                allowAll()
+            }
+            'org:lifecycle-runtime:2.6' {
+                allowAll()
+            }
+            'org:annotation:1.1' {
+                allowAll()
+            }
+            'org:coroutines:1.6' {
+                allowAll()
+            }
+            'org:lifecycle-common:2.6' {
+                allowAll()
+            }
+        }
+
+        then:
+        succeeds 'checkDeps'
+        // We do not check the graph state as the bug was failing resolution
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/derived/JvmDerivedVariantsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/derived/JvmDerivedVariantsIntegrationTest.groovy
index 3a47af6..a65e676 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/derived/JvmDerivedVariantsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/derived/JvmDerivedVariantsIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve.derived
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class JvmDerivedVariantsIntegrationTest extends AbstractIntegrationSpec {
     def setup() {
@@ -69,13 +70,14 @@
 * Foo class.
 */
 public class Foo {
-    
+
 }
 """
         using m2
         succeeds("publishToMavenLocal")
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "sources jar attributes match derived variants attributes"() {
         file("consumer/build.gradle") << """
 task resolve {
@@ -122,6 +124,7 @@
         noExceptionThrown()
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "javadoc jar attributes match derived variants attributes"() {
         file("consumer/build.gradle") << """
 task resolve {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/derived/MultiProjectVariantResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/derived/MultiProjectVariantResolutionIntegrationTest.groovy
index 7b3e6bb..08162b8 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/derived/MultiProjectVariantResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/derived/MultiProjectVariantResolutionIntegrationTest.groovy
@@ -33,7 +33,7 @@
                 configurations {
                     producerArtifacts {
                         canBeConsumed = false
-                        canBeResolved = true
+                        assert canBeResolved
 
                         attributes {
                             attribute(Attribute.of('shared', String), 'shared-value')
@@ -112,7 +112,7 @@
             configurations {
                 jarElements {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Attribute.of('shared', String), 'shared-value')
                         attribute(Attribute.of('unique', String), 'jar-value')
@@ -124,7 +124,7 @@
                 }
                 javadocElements {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Attribute.of('shared', String), 'shared-value')
                         attribute(Attribute.of('unique', String), 'javadoc-value')
@@ -136,7 +136,7 @@
                 }
                 otherElements {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Attribute.of('other', String), 'foobar')
                     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/gmm/GradleModuleMetadataAvailableAtIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/gmm/GradleModuleMetadataAvailableAtIntegrationTest.groovy
index 4136e1f..09da352 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/gmm/GradleModuleMetadataAvailableAtIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/gmm/GradleModuleMetadataAvailableAtIntegrationTest.groovy
@@ -18,6 +18,7 @@
 
 import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
 import org.gradle.integtests.fixtures.RequiredFeature
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
 import spock.lang.Issue
 
@@ -213,6 +214,7 @@
         }
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "resolution result can tell if a dependency is for an available-at variant"() {
         given:
         repository {
@@ -274,6 +276,7 @@
         }
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "resolution result ignores an ignored available-at variant"() {
         given:
         repository {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/DeprecatedTLSVersionDependencyResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/DeprecatedTLSVersionDependencyResolutionIntegrationTest.groovy
index dc8c209..8fbf3c2 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/DeprecatedTLSVersionDependencyResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/DeprecatedTLSVersionDependencyResolutionIntegrationTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.integtests.resolve.http
 
+
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.resolve.ResolveFailureTestFixture
@@ -63,7 +64,7 @@
                     startsWith("The server may not support the client's requested TLS protocol versions:") // Windows
                 ),
                 containsString("You may need to configure the client to allow other protocols to be used."),
-                containsString("See: https://docs.gradle.org/")
+                containsString(documentationRegistry.getDocumentationRecommendationFor("on this", "build_environment", "sec:gradle_system_properties"))
             )
         )
     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpsToHttpRedirectResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpsToHttpRedirectResolveIntegrationTest.groovy
index 948c158..d14c44d 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpsToHttpRedirectResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/HttpsToHttpRedirectResolveIntegrationTest.groovy
@@ -54,12 +54,9 @@
         server.forbidGet('/repo/group/projectA/1.0/projectA-1.0.jar')
 
         then:
-        def failure = fails('listJars')
-        failure.assertHasCause(
-            "Redirecting from secure protocol to insecure protocol, without explict opt-in, is unsupported. " +
-                "'$frontServerBaseUrl/repo' is redirecting to '${backingServer.uri}/redirected/group/projectA/1.0/ivy-1.0.xml'. " +
-                "Switch Ivy repository 'ivy($frontServerBaseUrl/repo)' to redirect to a secure protocol (like HTTPS) or allow insecure protocols. " +
-                Documentation.dslReference(UrlArtifactRepository, "allowInsecureProtocol").consultDocumentationMessage()
-        )
+        fails('listJars')
+            .assertHasCause("Redirecting from secure protocol to insecure protocol, without explicit opt-in, is unsupported. '$frontServerBaseUrl/repo' is redirecting to '${backingServer.uri}/redirected/group/projectA/1.0/ivy-1.0.xml'.")
+            .assertHasResolution("Switch Ivy repository 'ivy($frontServerBaseUrl/repo)' to redirect to a secure protocol (like HTTPS) or allow insecure protocols.")
+            .assertHasResolution(Documentation.dslReference(UrlArtifactRepository, "allowInsecureProtocol").consultDocumentationMessage())
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/MetadataSourcesResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/MetadataSourcesResolveIntegrationTest.groovy
index 882ed76..4dd43a1 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/MetadataSourcesResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/http/MetadataSourcesResolveIntegrationTest.groovy
@@ -21,6 +21,12 @@
 import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
 import org.gradle.test.fixtures.HttpRepository
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 class MetadataSourcesResolveIntegrationTest extends AbstractModuleDependencyResolveTest {
 
     @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true")
@@ -178,7 +184,12 @@
         failure.assertHasCause("""Could not find org.test:projectA:1.1.
 Searched in the following locations:
   - ${metadataUri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in '$format' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:""")
+        failure.assertHasResolutions(repositoryHint(format),
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
+
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesDependencyResolveIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesDependencyResolveIntegTest.groovy
index b601feb..5eeaacb 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesDependencyResolveIntegTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesDependencyResolveIntegTest.groovy
@@ -27,7 +27,6 @@
         (GradleMetadataResolveRunner.useIvy() || mavenCompatible) && (!GradleMetadataResolveRunner.gradleMetadataPublished || gradleCompatible)
     }
 
-    @ToBeFixedForConfigurationCache
     def "uses '#rule' rule to choose component for #selector"() {
         given:
         Assume.assumeTrue isWellBehaved(mavenCompatible, gradleCompatible)
@@ -57,7 +56,10 @@
         checkDependencies {
             resolve.expectGraph {
                 root(":", ":test:") {
-                    edge("org.utils:api:${selector}", "org.utils:${chosenModule}:${chosenVersion}").byReasons(reasons)
+                    edge("org.utils:api:${selector}", "org.utils:${chosenModule}:${chosenVersion}") {
+                        byReasons(reasons)
+                        maybeRequested()
+                    }
                 }
             }
         }
@@ -114,7 +116,7 @@
         chosenModule
     }
 
-    @ToBeFixedForConfigurationCache
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "uses '#rule' rule to reject all candidates for dynamic version #selector"() {
         given:
         Assume.assumeTrue isWellBehaved(mavenCompatible)
@@ -253,7 +255,7 @@
 """)
     }
 
-    @ToBeFixedForConfigurationCache
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "uses '#rule' rule to reject candidate for static version #selector"() {
         given:
         Assume.assumeTrue isWellBehaved(mavenCompatible, gradleCompatible)
@@ -370,6 +372,7 @@
         selector << ["1.1", "1.+"]
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "can control selection of components by module rule #rule for #selector"() {
         given:
         Assume.assumeTrue isWellBehaved(mavenCompatible, gradleCompatible)
@@ -427,7 +430,6 @@
     }
 
     @Issue("GRADLE-3236")
-    @ToBeFixedForConfigurationCache
     def "can select a different component for the same selector in different configurations"() {
         def descriptorArg = GradleMetadataResolveRunner.useIvy() ? 'selection.getDescriptor(IvyModuleDescriptor)' : 'selection.metadata'
         buildFile << """
@@ -466,9 +468,11 @@
             }
 
             task verify {
+                def filesA = configurations.modulesA
+                def filesB = configurations.modulesB
                 doLast {
-                    assert configurations.modulesA.files.collect { it.name } == [ 'api-1.1.jar']
-                    assert configurations.modulesB.files.collect { it.name } == [ 'api-1.0.jar']
+                    assert filesA.collect { it.name } == [ 'api-1.1.jar']
+                    assert filesB.collect { it.name } == [ 'api-1.0.jar']
                 }
             }
         """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesProcessingIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesProcessingIntegTest.groovy
index d3a85ea..b4d6988 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesProcessingIntegTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/ComponentSelectionRulesProcessingIntegTest.groovy
@@ -18,11 +18,13 @@
 
 import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
 import org.gradle.integtests.fixtures.RequiredFeature
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.test.fixtures.ivy.IvyModule
 import org.gradle.test.fixtures.maven.MavenModule
 
 class ComponentSelectionRulesProcessingIntegTest extends AbstractComponentSelectionRulesIntegrationTest {
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "rules are not fired when no candidate matches selector"() {
         buildFile << """
 
@@ -194,9 +196,11 @@
             }
 
             task resolveConf {
+                def files = configurations.conf
+                def modules = provider { fired }
                 doLast {
-                    configurations.conf.files
-                    assert fired.sort() == [ 'child', 'child_dep', 'parent_dep' ]
+                    files.files
+                    assert modules.get().sort() == [ 'child', 'child_dep', 'parent_dep' ]
                 }
             }
         """
@@ -398,10 +402,13 @@
             configurations.add(configurations.conf.copy())
 
             task('assertDeps') {
+                def conf = configurations.conf
+                def confCopy = configurations.confCopy
+                def notCopy = configurations.notCopy
                 doLast {
-                    assert configurations.conf.files*.name == ['api-1.1.jar']
-                    assert configurations.confCopy.files*.name == ['api-1.1.jar']
-                    assert configurations.notCopy.files*.name == ['api-1.2.jar']
+                    assert conf*.name == ['api-1.1.jar']
+                    assert confCopy*.name == ['api-1.1.jar']
+                    assert notCopy*.name == ['api-1.2.jar']
                 }
             }
         """
@@ -466,6 +473,7 @@
         checkDependencies()
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "can provide component selection rule as rule source"() {
         buildFile << """
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
index 1597e4a..766eef2 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyBrokenRemoteResolveIntegrationTest.groovy
@@ -18,7 +18,15 @@
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 class IvyBrokenRemoteResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
+    public final static String REPOSITORY_HINT = repositoryHint("ivy.xml")
+
     @ToBeFixedForConfigurationCache
     void "reports and recovers from missing module"() {
         given:
@@ -46,7 +54,6 @@
             .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${module.ivy.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
 
@@ -60,9 +67,14 @@
             .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${module.ivy.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
+        failure.assertHasResolutions(REPOSITORY_HINT,
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
+
 
         when:
         server.resetExpectations()
@@ -109,15 +121,18 @@
             .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${moduleA.ivy.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
             .assertHasCause("""Could not find group:projectB:1.0-milestone-9.
 Searched in the following locations:
   - ${moduleB.ivy.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
+        failure.assertHasResolutions(REPOSITORY_HINT,
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
 
         when:
         server.resetExpectations()
@@ -189,16 +204,19 @@
             .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${moduleA.ivy.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project : > group:projectC:0.99
     project : > project :child1 > group:projectD:1.0GA""")
             .assertHasCause("""Could not find group:projectB:1.0-milestone-9.
 Searched in the following locations:
   - ${moduleB.ivy.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project : > project :child1 > group:projectD:1.0GA""")
+        failure.assertHasResolutions(REPOSITORY_HINT,
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
 
         when:
         server.resetExpectations()
@@ -247,9 +265,13 @@
             .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${module.ivy.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
 """)
+        failure.assertHasResolutions(REPOSITORY_HINT,
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
 
         when:
         server.resetExpectations()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionMultiRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionMultiRepoResolveIntegrationTest.groovy
index 2a29568..80dacb4 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionMultiRepoResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionMultiRepoResolveIntegrationTest.groovy
@@ -58,7 +58,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.1").byReason("didn't match version 1.2")
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.1") {
+                    notRequested()
+                    byReason("didn't match version 1.2")
+                }
             }
         }
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
index f97b403..b864bb8 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionRemoteResolveIntegrationTest.groovy
@@ -570,7 +570,10 @@
         succeeds "checkDeps"
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:1.+", "org.test:projectA:1.2").byReason("didn't match version 2.1")
+                edge("org.test:projectA:1.+", "org.test:projectA:1.2") {
+                    notRequested()
+                    byReason("didn't match version 2.1")
+                }
             }
         }
 
@@ -590,8 +593,11 @@
         succeeds "checkDeps"
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:1.+", "org.test:projectA:2.1").byConflictResolution("between versions 1.2 and 2.1")
-                edge("org.test:projectA:2.+", "org.test:projectA:2.1").byConflictResolution("between versions 1.2 and 2.1")
+                edge("org.test:projectA:1.+", "org.test:projectA:2.1") {
+                    byConflictResolution("between versions 1.2 and 2.1")
+                    byReason("didn't match version 2.1")
+                }
+                edge("org.test:projectA:2.+", "org.test:projectA:2.1")
             }
         }
 
@@ -613,9 +619,15 @@
         succeeds "checkDeps"
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:1.+", "org.test:projectA:3.0").byConflictResolution("between versions 3.0, 1.2 and 2.1")
-                edge("org.test:projectA:2.+", "org.test:projectA:3.0").byConflictResolution("between versions 3.0, 1.2 and 2.1")
-                edge("org.test:projectA:3.+", "org.test:projectA:3.0").byConflictResolution("between versions 3.0, 1.2 and 2.1")
+                edge("org.test:projectA:1.+", "org.test:projectA:3.0") {
+                    notRequested()
+                    byConflictResolution("between versions 3.0, 1.2 and 2.1")
+                    byReason("didn't match versions 2.1, 1.2, 1.1")
+                    byReason("didn't match versions 3.0, 2.1")
+                    byReason("didn't match version 3.0")
+                }
+                edge("org.test:projectA:2.+", "org.test:projectA:3.0")
+                edge("org.test:projectA:3.+", "org.test:projectA:3.0")
             }
         }
     }
@@ -1280,7 +1292,7 @@
             root(":", ":test:") {
                 edges.each { from, to ->
                     if (to instanceof List) {
-                        edge(from, to[0]).byReason(to[1])
+                        edge(from, to[0]).byReason(to[1]).notRequested()
                     } else {
                         edge(from, to)
                     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
index a7a496a..8ed538a 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyDynamicRevisionResolveIntegrationTest.groovy
@@ -187,7 +187,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.1").byReason("didn't match version 1.3")
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.1") {
+                    notRequested()
+                    byReason("didn't match version 1.3")
+                }
             }
         }
 
@@ -217,7 +220,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.2").byReason("didn't match version 1.3")
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.2") {
+                    notRequested()
+                    byReason("didn't match version 1.3")
+                }
             }
         }
 
@@ -248,7 +254,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.2").byReason("didn't match version 1.3")
+                edge("org.test:projectA:latest.milestone", "org.test:projectA:1.2") {
+                    notRequested()
+                    byReason("didn't match version 1.3")
+                }
             }
         }
     }
@@ -341,7 +350,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:latest.release", "org.test:projectA:1.1").byReason("didn't match versions 1.3, 1.2")
+                edge("org.test:projectA:latest.release", "org.test:projectA:1.1") {
+                    notRequested()
+                    byReason("didn't match versions 1.3, 1.2")
+                }
             }
         }
 
@@ -382,7 +394,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:latest.release", "org.test:projectA:1.1").byReason("didn't match versions 1.3, 1.2, 1.1.1")
+                edge("org.test:projectA:latest.release", "org.test:projectA:1.1") {
+                    notRequested()
+                    byReason("didn't match versions 1.3, 1.2, 1.1.1")
+                }
             }
         }
     }
@@ -431,7 +446,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.1").byReason("didn't match version 2.0")
+                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.1") {
+                    notRequested()
+                    byReason("didn't match version 2.0")
+                }
             }
         }
 
@@ -454,7 +472,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.9").byReason("didn't match version 2.0")
+                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.9") {
+                    notRequested()
+                    byReason("didn't match version 2.0")
+                }
             }
         }
 
@@ -477,7 +498,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.10").byReason("didn't match version 2.0")
+                edge("org.test:projectA:1.2+", "org.test:projectA:1.2.10") {
+                    notRequested()
+                    byReason("didn't match version 2.0")
+                }
             }
         }
     }
@@ -527,7 +551,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.2.1").byReason("didn't match version 2.1")
+                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.2.1") {
+                    notRequested()
+                    byReason("didn't match version 2.1")
+                }
             }
         }
 
@@ -550,7 +577,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.3").byReason("didn't match version 2.1")
+                edge("org.test:projectA:[1.2,2.0]", "org.test:projectA:1.3") {
+                    notRequested()
+                    byReason("didn't match version 2.1")
+                }
             }
         }
     }
@@ -602,7 +632,10 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge("org.test:projectA:[1.2,2.0)", "org.test:projectA:1.2.1:default").byReason("didn't match version 2.0")
+                edge("org.test:projectA:[1.2,2.0)", "org.test:projectA:1.2.1:default") {
+                    notRequested()
+                    byReason("didn't match version 2.0")
+                }
             }
         }
     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
index c847e9f..86abd18 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ivy/IvyResolveIntegrationTest.groovy
@@ -58,6 +58,7 @@
         resolve.expectGraph {
             root(":", "org.gradle:test:1.0") {
                 module("org.gradle:test:1.45") {
+                    byConflictResolution("between versions 1.45 and 1.0")
                     artifact()
                     artifact(classifier: "classifier")
                     artifact(name: "test-extra")
@@ -378,6 +379,7 @@
                     configuration('alice')
                     byConstraint()
                     module('org:bar:1.0') {
+                        notRequested()
                         byAncestor()
                         byConstraint()
                         configuration('extra')
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/AbstractLockingIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/AbstractLockingIntegrationTest.groovy
index 7f278c4..4941ea6 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/AbstractLockingIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/AbstractLockingIntegrationTest.groovy
@@ -18,6 +18,7 @@
 
 import org.gradle.api.artifacts.dsl.LockMode
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 
 abstract class AbstractLockingIntegrationTest extends AbstractDependencyResolutionTest {
@@ -174,7 +175,8 @@
         lockfileFixture.verifyLockfile('lockedConf', ['org:foo:1.0', 'org:bar:1.0'])
     }
 
-    def 'does not write lock file when build fails'() {
+    @ToBeFixedForConfigurationCache(because = "Does actually write the lock file when CC is enabled (which is fine, because all dependency resolution has completed successfully by the time the task fails)")
+    def 'does not write lock file when task execution fails'() {
         mavenRepo.module('org', 'bar', '1.1').publish()
 
         buildFile << """
@@ -211,6 +213,44 @@
         lockfileFixture.expectLockStateMissing('conf')
     }
 
+    def 'does not write lock file when dependency resolution fails'() {
+        mavenRepo.module('org', 'bar', '1.1').publish()
+
+        buildFile << """
+dependencyLocking {
+    lockAllConfigurations()
+}
+
+repositories {
+    maven {
+        url '${mavenRepo.uri}'
+    }
+}
+configurations {
+    conf {
+        beforeResolve {
+            throw new RuntimeException("Build failed")
+        }
+    }
+}
+
+dependencies {
+    conf 'org:bar:1.+'
+}
+
+task copyDeps(type: Copy) {
+    from configurations.conf
+    into "\$buildDir/output"
+}
+"""
+
+        when:
+        fails 'copyDeps', '--write-locks'
+
+        then:
+        lockfileFixture.expectLockStateMissing('conf')
+    }
+
     def "writes dependency lock file for resolved version #version"() {
         mavenRepo.module('org', 'bar', '1.0').publish()
         mavenRepo.module('org', 'bar', '1.1').publish()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/DependencyLockingLenientModeIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/DependencyLockingLenientModeIntegrationTest.groovy
index f3dde226..664a89f 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/DependencyLockingLenientModeIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/DependencyLockingLenientModeIntegrationTest.groovy
@@ -53,7 +53,7 @@
 }
 """
 
-        lockfileFixture.createLockfile('lockedConf',['org:foo:1.0'], unique)
+        lockfileFixture.createLockfile('lockedConf', ['org:foo:1.0'], unique)
 
         when:
         succeeds 'checkDeps'
@@ -62,11 +62,12 @@
         resolve.expectDefaultConfiguration('runtime')
         resolve.expectGraph {
             root(":", ":depLock:") {
-                edge("org:foo:1.+", "org:foo:1.1")
-                edge("org:foo:{strictly 1.1}", "org:foo:1.1")
-                constraint("org:foo:1.0", "org:foo:1.1"){
+                edge("org:foo:1.+", "org:foo:1.1") {
+                    byConflictResolution("between versions 1.0 and 1.1")
                     byConstraint("dependency was locked to version '1.0' (update/lenient mode)")
                 }
+                edge("org:foo:{strictly 1.1}", "org:foo:1.1")
+                constraint("org:foo:1.0", "org:foo:1.1")
             }
         }
 
@@ -101,7 +102,7 @@
 }
 """
 
-        lockfileFixture.createLockfile('lockedConf',['org:foo:1.0'], unique)
+        lockfileFixture.createLockfile('lockedConf', ['org:foo:1.0'], unique)
 
         when:
         succeeds 'checkDeps'
@@ -110,11 +111,12 @@
         resolve.expectDefaultConfiguration('runtime')
         resolve.expectGraph {
             root(":", ":depLock:") {
-                edge("org:foo:1.+", "org:foo:1.1")
-                module("org:foo:1.1")
-                constraint("org:foo:1.0", "org:foo:1.1"){
+                edge("org:foo:1.+", "org:foo:1.1") {
                     byConstraint("dependency was locked to version '1.0' (update/lenient mode)")
+                    byConflictResolution("between versions 1.0 and 1.1")
                 }
+                module("org:foo:1.1")
+                constraint("org:foo:1.0", "org:foo:1.1")
             }
         }
 
@@ -149,7 +151,7 @@
 }
 """
 
-        lockfileFixture.createLockfile('lockedConf',['org:bar:1.0', 'org:foo:1.0', 'org:baz:1.0'], unique)
+        lockfileFixture.createLockfile('lockedConf', ['org:bar:1.0', 'org:foo:1.0', 'org:baz:1.0'], unique)
 
         when:
         succeeds 'checkDeps'
@@ -159,7 +161,7 @@
         resolve.expectGraph {
             root(":", ":depLock:") {
                 edge("org:foo:1.+", "org:foo:1.0")
-                constraint("org:foo:1.0", "org:foo:1.0"){
+                constraint("org:foo:1.0", "org:foo:1.0") {
                     byConstraint("dependency was locked to version '1.0' (update/lenient mode)")
                 }
             }
@@ -196,7 +198,7 @@
 }
 """
 
-        lockfileFixture.createLockfile('lockedConf',['org:foo:1.0'], unique)
+        lockfileFixture.createLockfile('lockedConf', ['org:foo:1.0'], unique)
 
         when:
         succeeds 'checkDeps'
@@ -207,7 +209,7 @@
             root(":", ":depLock:") {
                 edge("org:foo:1.+", "org:foo:1.0")
                 edge("org:bar:1.+", "org:bar:1.0")
-                constraint("org:foo:1.0", "org:foo:1.0"){
+                constraint("org:foo:1.0", "org:foo:1.0") {
                     byConstraint("dependency was locked to version '1.0' (update/lenient mode)")
                 }
             }
@@ -282,7 +284,7 @@
 }
 """
 
-        lockfileFixture.createLockfile('lockedConf',['org:foo:1.0'])
+        lockfileFixture.createLockfile('lockedConf', ['org:foo:1.0'])
 
         when:
         run 'dependencies'
@@ -323,7 +325,7 @@
 }
 """
 
-        lockfileFixture.createLockfile('lockedConf',['org:bar:1.0', 'org:foo:1.0'])
+        lockfileFixture.createLockfile('lockedConf', ['org:bar:1.0', 'org:foo:1.0'])
 
         when:
         run 'dependencies'
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/LockingInteractionsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/LockingInteractionsIntegrationTest.groovy
index aa56f0d..4f2d631 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/LockingInteractionsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/LockingInteractionsIntegrationTest.groovy
@@ -340,8 +340,9 @@
 }
 
 task resolve {
+    def files = configurations.lockedConf
     doLast {
-        println configurations.lockedConf.files
+        println files.files
     }
 }
 """
@@ -384,8 +385,9 @@
 }
 
 task resolve {
+    def files = configurations.lockedConf
     doLast {
-        println configurations.lockedConf.files
+        println files.files
     }
 }
 """
@@ -430,8 +432,9 @@
 }
 
 task resolve {
+    def files = configurations.lockedConf
     doLast {
-        println configurations.lockedConf.files
+        println files.files
     }
 }
 """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/UsingLockingOnNonProjectConfigurationsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/UsingLockingOnNonProjectConfigurationsIntegrationTest.groovy
index 91cd085..82cbeaa 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/UsingLockingOnNonProjectConfigurationsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/locking/UsingLockingOnNonProjectConfigurationsIntegrationTest.groovy
@@ -150,8 +150,9 @@
 }
 
 task resolve {
+    def files = configurations.foo
     doLast {
-        println configurations.foo.files
+        println files.files
     }
 }
 """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBomResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBomResolveIntegrationTest.groovy
index 7c62da2..fad0dac 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBomResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBomResolveIntegrationTest.groovy
@@ -64,7 +64,9 @@
                     constraint("group:moduleA:2.0", "group:moduleA:2.0")
                     noArtifacts()
                 }
-                edge("group:moduleA", "group:moduleA:2.0")
+                edge("group:moduleA", "group:moduleA:2.0") {
+                    byConstraint()
+                }
             }
         }
     }
@@ -92,7 +94,9 @@
                     constraint("group:moduleA:2.0", "group:moduleA:2.0")
                     noArtifacts()
                 }
-                edge("group:moduleA", "group:moduleA:2.0")
+                edge("group:moduleA", "group:moduleA:2.0") {
+                    byConstraint()
+                }
             }
         }
     }
@@ -132,7 +136,9 @@
                     constraint("group:moduleA:2.0", "group:moduleA:2.0")
                     noArtifacts()
                 }
-                edge("group:moduleA", "group:moduleA:2.0")
+                edge("group:moduleA", "group:moduleA:2.0") {
+                    byConstraint()
+                }
             }
         }
     }
@@ -178,7 +184,9 @@
                     module("group:moduleC:1.0")
                     noArtifacts()
                 }
-                edge("group:moduleA", "group:moduleA:2.0")
+                edge("group:moduleA", "group:moduleA:2.0") {
+                    byConstraint()
+                }
             }
         }
     }
@@ -298,7 +306,9 @@
                     constraint("group:moduleA:2.0", "group:moduleA:2.0")
                     noArtifacts()
                 }
-                edge("group:moduleA", "group:moduleA:2.0")
+                edge("group:moduleA", "group:moduleA:2.0") {
+                    byConstraint()
+                }
             }
         }
     }
@@ -327,7 +337,9 @@
                     constraint("group:moduleA:2.0", "group:moduleA:2.0")
                     noArtifacts()
                 }
-                edge("group:moduleA", "group:moduleA:2.0")
+                edge("group:moduleA", "group:moduleA:2.0") {
+                    byConstraint()
+                }
             }
         }
     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy
index f1e6ba9..e0eb45c 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenBrokenRemoteResolveIntegrationTest.groovy
@@ -17,11 +17,17 @@
 package org.gradle.integtests.resolve.maven
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import spock.lang.Unroll
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 class MavenBrokenRemoteResolveIntegrationTest extends AbstractHttpDependencyResolutionTest {
-    @ToBeFixedForConfigurationCache
+    public static final REPOSITORY_HINT = repositoryHint("Maven POM")
+
     void "reports and recovers from missing module"() {
         given:
         def repo = mavenHttpRepo("repo1")
@@ -35,7 +41,10 @@
 dependencies {
     missing 'group:projectA:1.2'
 }
-task showMissing { doLast { println configurations.missing.files } }
+task showMissing {
+    def files = configurations.missing
+    doLast { println files.files }
+}
 """
 
         when:
@@ -43,12 +52,10 @@
 
         then:
         fails("showMissing")
-        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
-            .assertResolutionFailure(':missing')
+        failure.assertResolutionFailure(':missing')
             .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${module.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
 
@@ -57,14 +64,17 @@
 
         then:
         fails("showMissing")
-        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
-            .assertResolutionFailure(':missing')
+        failure.assertResolutionFailure(':missing')
             .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${module.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
+        failure.assertHasResolutions(REPOSITORY_HINT,
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
 
         when:
         server.resetExpectations()
@@ -81,7 +91,6 @@
         succeeds('showMissing')
     }
 
-    @ToBeFixedForConfigurationCache
     void "reports and recovers from multiple missing modules"() {
         given:
         def repo = mavenHttpRepo("repo1")
@@ -97,7 +106,10 @@
     missing 'group:projectA:1.2'
     missing 'group:projectB:1.0-milestone-9'
 }
-task showMissing { doLast { println configurations.missing.files } }
+task showMissing {
+    def files = configurations.missing
+    doLast { println files.files }
+}
 """
 
         when:
@@ -106,20 +118,23 @@
 
         then:
         fails("showMissing")
-        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
-                .assertResolutionFailure(':missing')
-                .assertHasCause("""Could not find group:projectA:1.2.
+        failure.assertResolutionFailure(':missing')
+            .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${moduleA.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
-                .assertHasCause("""Could not find group:projectB:1.0-milestone-9.
+            .assertHasCause("""Could not find group:projectB:1.0-milestone-9.
 Searched in the following locations:
   - ${moduleB.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
+        failure.assertHasResolutions(REPOSITORY_HINT,
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
+
 
         when:
         server.resetExpectations()
@@ -138,7 +153,6 @@
         succeeds('showMissing')
     }
 
-    @ToBeFixedForConfigurationCache
     void "reports and recovers from multiple missing transitive modules"() {
         settingsFile << "include 'child1'"
 
@@ -175,7 +189,10 @@
         compile 'group:projectD:1.0GA'
     }
 }
-task showMissing { doLast { println configurations.compile.files } }
+task showMissing {
+    def files = configurations.compile
+    doLast { println files.files }
+}
 """
 
         when:
@@ -186,21 +203,23 @@
 
         then:
         fails("showMissing")
-        failure.assertHasDescription('Execution failed for task \':showMissing\'.')
-                .assertResolutionFailure(':compile')
-                .assertHasCause("""Could not find group:projectA:1.2.
+        failure.assertResolutionFailure(':compile')
+            .assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${moduleA.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project : > group:projectC:0.99
     project : > project :child1 > group:projectD:1.0GA""")
-                .assertHasCause("""Could not find group:projectB:1.0-milestone-9.
+            .assertHasCause("""Could not find group:projectB:1.0-milestone-9.
 Searched in the following locations:
   - ${moduleB.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project : > project :child1 > group:projectD:1.0GA""")
+        failure.assertHasResolutions(REPOSITORY_HINT,
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
 
         when:
         server.resetExpectations()
@@ -221,7 +240,6 @@
         succeeds('showMissing')
     }
 
-    @ToBeFixedForConfigurationCache
     void "reports and recovers from failed POM download"() {
         given:
         def module = mavenHttpRepo.module('group', 'projectA', '1.3').publish()
@@ -236,7 +254,10 @@
 dependencies {
     broken 'group:projectA:1.3'
 }
-task showBroken { doLast { println configurations.broken.files } }
+task showBroken {
+    def files = configurations.broken
+    doLast { println files.files }
+}
 """
 
         when:
@@ -244,9 +265,7 @@
         fails("showBroken")
 
         then:
-        failure
-            .assertHasDescription('Execution failed for task \':showBroken\'.')
-            .assertResolutionFailure(':broken')
+        failure.assertResolutionFailure(':broken')
             .assertHasCause('Could not resolve group:projectA:1.3.')
             .assertHasCause("Could not GET '${module.pom.uri}'. Received status code 500 from server: broken")
 
@@ -282,11 +301,14 @@
 dependencies {
     broken 'group:projectA:1.3'
 }
-task showBroken { doLast { println configurations.broken.files } }
+task showBroken {
+    def files = configurations.broken
+    doLast { println files.files }
+}
 """
 
         when:
-        (retries-1).times {
+        (retries - 1).times {
             module.pom.expectGetBroken()
         }
         module.pom.expectGet()
@@ -316,12 +338,15 @@
 dependencies {
     broken 'group:projectA:1.3'
 }
-task showBroken { doLast { println configurations.broken.files } }
+task showBroken {
+    def files = configurations.broken
+    doLast { println files.files }
+}
 """
 
         when:
         module.pom.expectGet()
-        (retries-1).times {
+        (retries - 1).times {
             module.artifact.expectGetBroken()
         }
         module.artifact.expectGet()
@@ -350,7 +375,10 @@
 dependencies {
     broken 'group:projectA:1.3'
 }
-task showBroken { doLast { println configurations.broken.files } }
+task showBroken {
+    def files = configurations.broken
+    doLast { println files.files }
+}
 """
 
         when:
@@ -360,9 +388,7 @@
         fails("showBroken")
 
         and:
-        failure
-            .assertHasDescription('Execution failed for task \':showBroken\'.')
-            .assertResolutionFailure(':broken')
+        failure.assertResolutionFailure(':broken')
             .assertHasCause('Could not find group:projectA:1.3.')
 
         where:
@@ -386,7 +412,10 @@
 dependencies {
     broken 'group:projectA:1.3'
 }
-task showBroken { doLast { println configurations.broken.files } }
+task showBroken {
+    def files = configurations.broken
+    doLast { println files.files }
+}
 """
 
         when:
@@ -397,9 +426,7 @@
         fails("showBroken")
 
         and:
-        failure
-            .assertHasDescription('Execution failed for task \':showBroken\'.')
-            .assertResolutionFailure(':broken')
+        failure.assertResolutionFailure(':broken')
             .assertHasCause("Could not resolve all files for configuration ':broken'.")
             .assertHasCause('Could not find projectA-1.3.jar (group:projectA:1.3).')
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy
index dd9bed4..40ecf85 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenComponentMetadataRulesStatusIntegrationTest.groovy
@@ -60,8 +60,9 @@
 }
 
 task resolve {
+    def files = configurations.compile
     doLast {
-        configurations.compile.resolve()
+        files.files
     }
 }
 """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyManagementImportOrderTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyManagementImportOrderTest.groovy
index dcc3f07..2d9404f 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyManagementImportOrderTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenDependencyManagementImportOrderTest.groovy
@@ -17,13 +17,13 @@
 package org.gradle.integtests.resolve.maven;
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 class MavenDependencyManagementImportOrderTest extends AbstractIntegrationSpec {
 	@Issue("https://github.com/gradle/gradle/issues/2212")
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
 	def "Verify that gradle resolves org.wildfly.swarm:undertow the same as maven"() {
 		when:
 		buildFile.text = """
@@ -37,8 +37,9 @@
 	}
 }
 task verifyUndertowVersion {
-	doLast {	
-		def fileNames = configurations.test.files.collect { it.name }
+    def files = configurations.test
+	doLast {
+		def fileNames = files.collect { it.name }
 		assert fileNames.contains('undertow-servlet-1.4.11.Final.jar')
 		assert !fileNames.contains('undertow-servlet-1.2.9.Final.jar')
 	}
@@ -166,8 +167,9 @@
 	test 'group:level1:1'
 }
 task verifyVersion {
+    def files = configurations.test
 	doLast {
-		def fileNames = configurations.test.files.collect { it.name }
+		def fileNames = files.collect { it.name }
 		assert fileNames.contains('level1-1.jar')
 		assert fileNames.contains('level2-1.jar')
 		assert !fileNames.contains('level2-2.jar')
@@ -178,6 +180,4 @@
 		then:
 		succeeds 'verifyVersion'
 	}
-
-
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy
index c8ff239..d9495f2 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenJvmLibraryArtifactResolutionIntegrationTest.groovy
@@ -249,7 +249,6 @@
         checkArtifactsResolvedAndCached()
     }
 
-    @ToBeFixedForConfigurationCache(because = "Exception cause goes missing")
     def "reports on failure to list artifacts and recovers on subsequent resolve"() {
         fixture.requestingSource()
                 .expectComponentResolutionFailure()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy
index 4e442f9..4f56ffb 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenLocalRepoResolveIntegrationTest.groovy
@@ -19,6 +19,12 @@
 import org.gradle.test.fixtures.maven.MavenModule
 import spock.lang.Issue
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 class MavenLocalRepoResolveIntegrationTest extends AbstractDependencyResolutionTest {
 
     def setup() {
@@ -175,9 +181,13 @@
         failure.assertHasCause("""Could not find group:projectA:1.2.
 Searched in the following locations:
   - ${module.pomFile.toURL()}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
 """)
+        failure.assertHasResolutions(repositoryHint("Maven POM"),
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
 
         when:
         module.publish()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy
index f96455b..d4df775 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenPomResolveIntegrationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve.maven
 
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 import spock.lang.Issue
 
@@ -188,6 +189,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/22279")
+    @ToBeFixedForConfigurationCache(because = "task uses Artifact Query API")
     def "can resolve POM as an artifact after it was resolved via ARQ"() {
         given:
         def original = mavenHttpRepo.module('groupA', 'artifactA', '1.0').publishPom()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRealProjectsDependencyResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRealProjectsDependencyResolveIntegrationTest.groovy
index 0974f752..8c5c5fa 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRealProjectsDependencyResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRealProjectsDependencyResolveIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class MavenRealProjectsDependencyResolveIntegrationTest extends AbstractDependencyResolutionTest {
     def resolve = new ResolveTestFixture(buildFile, "compile")
@@ -32,7 +32,7 @@
         """
     }
 
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     def "resolves dependencies on real projects"() {
         // Real but ancient projects
         // Hibernate core brings in conflicts, exclusions and root poms
@@ -52,28 +52,10 @@
     compile "ch.qos.logback:logback-classic:0.9.30"
     compile "org.hibernate:hibernate-core:3.6.7.Final"
 }
-
-task check {
-    doLast {
-        def compile = configurations.compile
-
-        def filteredDependencies = compile.resolvedConfiguration.getFirstLevelModuleDependencies({ it.name == 'logback-classic' } as Spec)
-        assert filteredDependencies.collect { it.name } == [
-            'ch.qos.logback:logback-classic:0.9.30'
-        ]
-
-        def filteredFiles = compile.resolvedConfiguration.getFiles({ it.name == 'logback-classic' } as Spec)
-        assert filteredFiles.collect { it.name } == [
-            'logback-classic-0.9.30.jar',
-            'slf4j-api-1.6.2.jar',
-            'logback-core-0.9.30.jar'
-        ]
-    }
-}
 """
 
         expect:
-        succeeds "check", "checkDep"
+        succeeds "checkDep"
         resolve.expectDefaultConfiguration('runtime')
         resolve.expectGraph {
             root(':', ':testproject:') {
@@ -102,6 +84,4 @@
             }
         }
     }
-
-
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRemoteDependencyWithGradleMetadataResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRemoteDependencyWithGradleMetadataResolutionIntegrationTest.groovy
index 0543e92..e1e44a0 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRemoteDependencyWithGradleMetadataResolutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenRemoteDependencyWithGradleMetadataResolutionIntegrationTest.groovy
@@ -21,6 +21,11 @@
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 
 import static org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.GradleModuleMetadataParser.FORMAT_VERSION
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
 
 class MavenRemoteDependencyWithGradleMetadataResolutionIntegrationTest extends AbstractHttpDependencyResolutionTest {
     def resolve = new ResolveTestFixture(buildFile, "compile").expectDefaultConfiguration("runtime")
@@ -544,10 +549,12 @@
     release 'test:a:1.2'
 }
 task checkDebug {
-    doLast { assert configurations.debug.files*.name == ['a-1.2-debug.jar', 'b-2.0.jar', 'c-preview-debug.jar'] }
+    def files = configurations.debug
+    doLast { assert files*.name == ['a-1.2-debug.jar', 'b-2.0.jar', 'c-preview-debug.jar'] }
 }
 task checkRelease {
-    doLast { assert configurations.release.files*.name == [] }
+    def files = configurations.release
+    doLast { assert files*.name == [] }
 }
 """
 
@@ -682,9 +689,13 @@
         failure.assertHasCause("""Could not find test:a:1.2.
 Searched in the following locations:
   - ${m.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
     project :""")
+        failure.assertHasResolutions(repositoryHint("Maven POM"),
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
 
         when:
         server.resetExpectations()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
index 1111cea..b9c0f5d 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenSnapshotResolveIntegrationTest.groovy
@@ -24,6 +24,12 @@
 import org.gradle.test.fixtures.server.http.MavenHttpModule
 import spock.lang.Issue
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
 class MavenSnapshotResolveIntegrationTest extends AbstractModuleDependencyResolveTest {
 
@@ -996,9 +1002,14 @@
         failure.assertHasCause("""Could not find org.gradle.integtests.resolve:projectA:${published.publishArtifactVersion}.
 Searched in the following locations:
   - ${projectA.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
 """)
+        failure.assertHasResolutions(repositoryHint("Maven POM"),
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
+
     }
 
     @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
@@ -1092,7 +1103,7 @@
 configurations {
     poms {
         extendsFrom(configurations.compile)
-        canBeResolved = true
+        assert canBeResolved
         canBeConsumed = false
         attributes {
             attribute(Category.CATEGORY_ATTRIBUTE, getObjects().named(Category, Category.DOCUMENTATION))
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenVersionRangeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenVersionRangeResolveIntegrationTest.groovy
index d1855c4..6346613 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenVersionRangeResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/maven/MavenVersionRangeResolveIntegrationTest.groovy
@@ -143,7 +143,9 @@
             root(":", ":test:") {
                 edge("org.test:child:1.0", "org.test:child:1.0") {
                     edge("org.test:imported:[2.0,3.0)", "org.test:imported:2.1") {
-                        artifact(type: 'pom').byReason("didn't match version 3.0")
+                        notRequested()
+                        byReason("didn't match version 3.0")
+                        artifact(type: 'pom')
                         edge("org.test:dep:2.1", "org.test:dep:2.1")
                     }
                 }
@@ -221,6 +223,7 @@
         resolve.expectGraph {
             root(":", ":testrange:") {
                 edge("org.test:dep:[1.0, 2.0[", "org.test:dep:1.5") {
+                    notRequested()
                     byReason("didn't match versions 3.0, 2.1, 2.0, 2.0-final, 2.0-dev1")
                 }
             }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/override/ComponentOverrideMetadataResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/override/ComponentOverrideMetadataResolveIntegrationTest.groovy
index 5b8a469..25e079d 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/override/ComponentOverrideMetadataResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/override/ComponentOverrideMetadataResolveIntegrationTest.groovy
@@ -65,6 +65,7 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge('org:foo', 'org:foo:1.0') {
+                    byConstraint()
                     artifact(type: 'distribution-tgz')
                 }
                 constraint('org:foo:1.0')
@@ -114,6 +115,7 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 edge('org:foo', 'org:foo:1.0') {
+                    byConstraint()
                     artifact(name: artifactName, type: 'distribution-tgz')
                     artifact(name: artifactName, type: 'zip')
                 }
@@ -164,7 +166,11 @@
         def notSelectedModule = "org:foo:$otherVersion"
         resolve.expectGraph {
             root(":", ":test:") {
-                module('org:foo:1.0')
+                module('org:foo:1.0') {
+                    if (notSelectedModule != 'org:foo:1.0') {
+                        byConflictResolution('between versions 1.0 and 0.9')
+                    }
+                }
                 if (notSelectedModule != 'org:foo:1.0') {
                     edge(notSelectedModule, 'org:foo:1.0')
                 }
@@ -214,6 +220,7 @@
         resolve.expectGraph {
             root(":", ":test:") {
                 module('org:foo:1.1') {
+                    byConflictResolution('between versions 1.1 and 1.0')
                     module('org:bar:1.0')
                 }
                 edge('org:foo:[1.0,1.1]', 'org:foo:1.1')
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/EnforcedPlatformIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/EnforcedPlatformIntegrationTest.groovy
index a2f377b..4ecb399 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/EnforcedPlatformIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/EnforcedPlatformIntegrationTest.groovy
@@ -63,7 +63,10 @@
                     noArtifacts()
                     constraint('com.fasterxml.jackson.core:jackson-core:2.12.3', ':jackson-core', 'com.fasterxml.jackson.core:jackson-core:2.12.3-local-patch')
                 }
-                edge('com.fasterxml.jackson.core:jackson-core', ':jackson-core', 'com.fasterxml.jackson.core:jackson-core:2.12.3-local-patch')
+                edge('com.fasterxml.jackson.core:jackson-core', ':jackson-core', 'com.fasterxml.jackson.core:jackson-core:2.12.3-local-patch') {
+                    compositeSubstitute()
+                    byConstraint()
+                }
             }
         }
     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/JavaPlatformEcosystemIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/JavaPlatformEcosystemIntegrationTest.groovy
index ddbb999..968f661 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/JavaPlatformEcosystemIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/JavaPlatformEcosystemIntegrationTest.groovy
@@ -45,7 +45,7 @@
             configurations {
                 runtimeClasspath {
                     canBeConsumed = false
-                    canBeResolved = true
+                    assert canBeResolved
                 }
             }
 
@@ -75,6 +75,36 @@
         }
     }
 
+    def "Configuration.copy() should when configuration contains project dependency constraints"() {
+        setup:
+        buildFile << """
+            configurations {
+                custom {
+                    canBeResolved = true
+                }
+            }
+
+            dependencies {
+                constraints {
+                    custom(project(":lib")) { because "platform alignment" }
+                }
+            }
+
+            configurations.custom.copy()
+        """
+        settingsFile << "include 'lib'"
+
+        expect:
+        succeeds ":help"
+    }
+
+    /**
+     * I think the test above: {@link JavaPlatformEcosystemIntegrationTest#"Configuration.copy() should when configuration contains project dependency constraints"}
+     * should be sufficient to cover this case, which seems to apply to any configurations that has a project dependency constraint
+     * and is independent of the involvement of the Java Platform plugin.  But I'll leave this test here just in case for now.
+     *
+     * Once the deprecation becomes an error, this test should be removed.
+     */
     @Issue("https://github.com/gradle/gradle/issues/17179")
     def "Configuration.copy() should work when platform declares project dependency constraints"() {
         setup:
@@ -94,6 +124,9 @@
         settingsFile << "include 'lib'"
 
         expect:
+        executer.expectDocumentedDeprecationWarning("Calling configuration method 'copy()' is deprecated for configuration 'api', which has permitted usage(s):\n" +
+                "\tDeclarable - this configuration can have dependencies added to it\n" +
+                "This method is only meant to be called on configurations which allow the (non-deprecated) usage(s): 'Resolvable'. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_configuration_usage")
         succeeds ":help"
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/JavaPlatformResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/JavaPlatformResolveIntegrationTest.groovy
index 9bbe95e..d8cd8ac 100755
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/JavaPlatformResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/JavaPlatformResolveIntegrationTest.groovy
@@ -196,6 +196,7 @@
                     noArtifacts()
                 }
                 edge('org:foo:1.2', 'org:foo:1.1') {
+                    forced()
                     byConstraint()
                 }
             }
@@ -246,6 +247,7 @@
                     noArtifacts()
                 }
                 edge('org:foo:1.1', 'org:foo:1.0') {
+                    forced()
                     configuration = 'api'
                 }
             }
@@ -563,6 +565,7 @@
                     configuration = 'runtime'
                     module('org:foo:1.0') {
                         configuration = 'runtime'
+                        byConstraint()
                         module('org:foobar:1.0') {
                             configuration = 'runtime'
                         }
@@ -620,7 +623,9 @@
         then:
         resolve.expectGraph {
             root(":", "org.test:test:1.9") {
-                edge("org.test:depA", "org.test:depA:1.0")
+                edge("org.test:depA", "org.test:depA:1.0") {
+                    byConstraint()
+                }
                 module("org.test:depB:1.0") {
                     module("org.test:depC:1.0") {
                         module('org.test:platform:1.0') {
@@ -837,6 +842,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/20684")
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "multiple platform deselection - reselection does not leave pending constraints in graph - different issue"() {
         given:
         def depJackDb20 = mavenHttpRepo.module('jack', 'db', '2.0').withModuleMetadata()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/NativeAlignmentWithJavaPlatformResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/NativeAlignmentWithJavaPlatformResolveIntegrationTest.groovy
index 0fde112..e8842dc 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/NativeAlignmentWithJavaPlatformResolveIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/platforms/NativeAlignmentWithJavaPlatformResolveIntegrationTest.groovy
@@ -196,6 +196,7 @@
             root(":", ":consumer:") {
                 edge('com.acme.foo:core:1.0', 'com.acme.foo:core:1.1') {
                     byConstraint("platform alignment")
+                    byConflictResolution("between versions 1.1 and 1.0")
                     variant "apiElements", [
                         'org.gradle.category':'library',
                         'org.gradle.dependency.bundling':'external',
@@ -211,6 +212,7 @@
                             'org.gradle.usage': 'java-api']
                         constraint('com.acme.foo:core:1.1')
                         constraint('com.acme.foo:lib:1.1')
+                        byConflictResolution("between versions 1.1 and 1.0")
                         noArtifacts()
                     }
                     module('com.acme.foo:lib:1.1') {
@@ -222,6 +224,7 @@
                             'org.gradle.usage': 'java-api',
                             'org.gradle.libraryelements': 'jar',
                         ]
+                        byConflictResolution("between versions 1.1 and 1.0")
                         byConstraint("platform alignment")
                     }
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/ReadOnlyCacheValidationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/ReadOnlyCacheValidationTest.groovy
index f9afe35..b3eb3bd 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/ReadOnlyCacheValidationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/ReadOnlyCacheValidationTest.groovy
@@ -49,6 +49,6 @@
         then:
         outputContains("The read-only dependency cache is disabled because of a configuration problem:")
         outputContains("Read-only cache is configured but the directory layout isn't expected. You must have a pre-populated modules-2 directory at ")
-        outputContains("Please follow the instructions at ${new DocumentationRegistry().getDocumentationFor("dependency_resolution", "sub:shared-readonly-cache")}")
+        outputContains("${new DocumentationRegistry().getDocumentationRecommendationFor("instructions on how to do this", "dependency_resolution", "sub:shared-readonly-cache")}")
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/ReadOnlyDependencyCacheWithinContainerTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/ReadOnlyDependencyCacheWithinContainerTest.groovy
index b70e7ff..4cd125d 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/ReadOnlyDependencyCacheWithinContainerTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/ReadOnlyDependencyCacheWithinContainerTest.groovy
@@ -17,18 +17,18 @@
 package org.gradle.integtests.resolve.rocache
 
 import org.gradle.containers.GradleInContainer
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
 import org.gradle.test.fixtures.server.http.MavenHttpModule
 import org.gradle.test.fixtures.server.http.MavenHttpRepository
-import org.gradle.util.Requires
-import spock.lang.IgnoreIf
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-import static org.gradle.util.TestPrecondition.HAS_DOCKER
-
-@Requires([HAS_DOCKER])
-@IgnoreIf({ GradleContextualExecuter.embedded }) // needs real Gradle distribution to run in container
+@Requires(
+    value = [UnitTestPreconditions.HasDocker, IntegTestPreconditions.NotEmbeddedExecutor],
+    reason = "needs real Gradle distribution to run in container"
+)
 class ReadOnlyDependencyCacheWithinContainerTest extends AbstractReadOnlyCacheDependencyResolutionTest {
 
     BlockingHttpServer synchronizer
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/StaticVersionsReadOnlyCacheDependencyResolutionTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/StaticVersionsReadOnlyCacheDependencyResolutionTest.groovy
index 1579fda..e7edebc 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/StaticVersionsReadOnlyCacheDependencyResolutionTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rocache/StaticVersionsReadOnlyCacheDependencyResolutionTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve.rocache
 
 import org.gradle.api.internal.artifacts.ivyservice.CacheLayout
+import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
 import org.gradle.test.fixtures.server.http.MavenHttpModule
 import org.gradle.test.fixtures.server.http.MavenHttpRepository
 
@@ -82,6 +83,7 @@
         assertNotInReadOnlyCache("other-1.0.jar")
     }
 
+    @UnsupportedWithConfigurationCache(because = "task uses artifact resolution API")
     def "can recover from corrupt read-only cache (#file)"() {
         given:
         def core = mavenHttpRepo.module('org.readonly', 'core', '1.0')
@@ -123,6 +125,7 @@
         ]
     }
 
+    @UnsupportedWithConfigurationCache(because = "task uses artifact resolution API")
     def "fetches javadocs and sources from read-only cache"() {
         given:
         buildFile << """
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/AbstractDependencyMetadataRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/AbstractDependencyMetadataRulesIntegrationTest.groovy
new file mode 100644
index 0000000..c3f0564
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/AbstractDependencyMetadataRulesIntegrationTest.groovy
@@ -0,0 +1,1094 @@
+/*
+ * 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.integtests.resolve.rules
+
+import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
+import org.gradle.integtests.fixtures.RequiredFeature
+import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
+import org.gradle.test.fixtures.maven.MavenFileRepository
+
+import static org.gradle.util.internal.GUtil.toCamelCase
+
+abstract class AbstractDependencyMetadataRulesIntegrationTest extends AbstractModuleDependencyResolveTest {
+    @Override
+    String getTestConfiguration() { variantToTest }
+
+    /**
+     * Does the published metadata provide variants with attributes? Eventually all metadata should do that.
+     * For Ivy and Maven POM metadata, the variants and attributes should be derived from configurations and scopes.
+     */
+    boolean getPublishedModulesHaveAttributes() { gradleMetadataPublished }
+
+    String getVariantToTest() {
+        if (gradleMetadataPublished || useIvy()) {
+            'customVariant'
+        } else {
+            'runtime'
+        }
+    }
+
+    def setup() {
+        repository {
+            'org.test:moduleA:1.0'() {
+                variant 'customVariant', [format: 'custom']
+            }
+            'org.test:moduleB:1.0'()
+        }
+
+        buildFile << """
+            configurations { $variantToTest { attributes { attribute(Attribute.of('format', String), 'custom') } } }
+
+            dependencies {
+                $variantToTest group: 'org.test', name: 'moduleA', version: '1.0' ${publishedModulesHaveAttributes ? "" : ", configuration: '$variantToTest'"}
+            }
+        """
+    }
+
+    def "#thing can be added using #notation notation"() {
+        when:
+        buildFile << """
+            class ModifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        with${toCamelCase(thing)} {
+                            add $declaration
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                $variantToTest 'org.test:moduleB'
+                components {
+                    withModule('org.test:moduleA', ModifyRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                edge('org.test:moduleB', 'org.test:moduleB:1.0') {
+                    maybeByConstraint()
+                }
+                module("org.test:moduleA:1.0:$expectedVariant") {
+                    if (thing == "dependencies") {
+                        edge('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
+                    } else {
+                        constraint('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
+                    }
+                }
+            }
+        }
+
+        where:
+        thing                    | notation | declaration
+        "dependency constraints" | "string" | "'org.test:moduleB:1.0'"
+        "dependency constraints" | "map"    | "group: 'org.test', name: 'moduleB', version: '1.0'"
+        "dependencies"           | "string" | "'org.test:moduleB:1.0'"
+        "dependencies"           | "map"    | "group: 'org.test', name: 'moduleB', version: '1.0'"
+    }
+
+    def "#thing can be added to a new variant"() {
+        when:
+        buildFile << """
+            class ModifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.addVariant("new") {
+                        with${toCamelCase(thing)} {
+                            add 'org.test:moduleB:1.0'
+                        }
+                        withCapabilities {
+                            removeCapability("org.test", "moduleA")
+                            addCapability("all", "new", "1.0")
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                $variantToTest 'org.test:moduleB'
+                $variantToTest('org.test:moduleA:1.0') {
+                    capabilities { requireCapability("all:new") }
+                }
+                components {
+                    withModule('org.test:moduleA', ModifyRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                edge('org.test:moduleB', 'org.test:moduleB:1.0') {
+                    maybeByConstraint()
+                }
+                module("org.test:moduleA:1.0:$expectedVariant")
+                module("org.test:moduleA:1.0:new") {
+                    if (thing == "dependencies") {
+                        edge('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
+                    } else {
+                        constraint('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
+                    }
+                }
+            }
+        }
+
+        where:
+        thing                    | _
+        "dependency constraints" | _
+        "dependencies"           | _
+    }
+
+    def "#thing can be added and configured using #notation notation"() {
+        when:
+        buildFile << """
+            class ModifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        with${toCamelCase(thing)} {
+                            add($declaration) {
+                                it.version { strictly '1.0' }
+                            }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                $variantToTest 'org.test:moduleB'
+                components {
+                    withModule('org.test:moduleA', ModifyRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                edge('org.test:moduleB', 'org.test:moduleB:1.0') {
+                    maybeByConstraint()
+                }
+                module("org.test:moduleA:1.0:$expectedVariant") {
+                    if (thing == "dependencies") {
+                        edge('org.test:moduleB:{strictly 1.0}', 'org.test:moduleB:1.0')
+                    } else {
+                        constraint('org.test:moduleB:{strictly 1.0}', 'org.test:moduleB:1.0')
+                    }
+                }
+            }
+        }
+
+        where:
+        thing                    | notation | declaration
+        "dependency constraints" | "string" | "'org.test:moduleB:1.0'"
+        "dependency constraints" | "map"    | "group: 'org.test', name: 'moduleB', version: '1.0'"
+        "dependencies"           | "string" | "'org.test:moduleB:1.0'"
+        "dependencies"           | "map"    | "group: 'org.test', name: 'moduleB', version: '1.0'"
+    }
+
+    def "dependencies can be removed"() {
+        given:
+        repository {
+            'org.test:moduleA:1.0' {
+                dependsOn 'org.test:moduleB:1.0'
+            }
+        }
+
+        when:
+        buildFile << """
+            class ModifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        withDependencies {
+                            removeAll { it.versionConstraint.requiredVersion == '1.0' }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleA', ModifyRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                module("org.test:moduleA:1.0:$expectedVariant")
+            }
+        }
+    }
+
+    def "dependency constraints can be removed"() {
+        given:
+        repository {
+            'org.test:moduleA:1.0' {
+                constraint 'org.test:moduleB:2.0'
+            }
+        }
+
+        when:
+        buildFile << """
+            class ModifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        withDependencyConstraints {
+                            removeAll { it.versionConstraint.requiredVersion == '2.0' }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                $variantToTest 'org.test:moduleB:1.0'
+                components {
+                    withModule('org.test:moduleA', ModifyRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+
+            }
+            'org.test:moduleB'() {
+                version('1.0') {
+                    expectGetMetadata()
+                    expectGetArtifact()
+                }
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                module("org.test:moduleB:1.0")
+                module("org.test:moduleA:1.0:$expectedVariant")
+            }
+        }
+    }
+
+    def "#thing modifications are visible in the next rule"() {
+        when:
+        buildFile << """
+            class AddRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        with${toCamelCase(thing)} { d ->
+                            assert d.size() == 0
+                            d.add 'org.test:moduleB:1.0'
+                        }
+                    }
+                }
+            }
+
+            class RemoveRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        with${toCamelCase(thing)} { d ->
+                            assert d.size() == 1
+                            d.removeAll { true }
+                        }
+                    }
+                }
+            }
+
+            class VerifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        with${toCamelCase(thing)} { d ->
+                            assert d.size() == 0
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleA', AddRule)
+                    withModule('org.test:moduleA', RemoveRule)
+                    all(VerifyRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                module("org.test:moduleA:1.0:$expectedVariant")
+            }
+        }
+
+        where:
+        thing                    | _
+        "dependencies"           | _
+        "dependency constraints" | _
+    }
+
+    def "can set version on dependency using #keyword"() {
+        given:
+        repository {
+            'org.test:moduleA:1.0'() {
+                dependsOn 'org.test:moduleB'
+            }
+        }
+
+        when:
+        buildFile << """
+            class VersionSettingRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        withDependencies {
+                            it.each {
+                                it.version {
+                                    require ''
+                                    ${keyword} '1.0'
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleA', VersionSettingRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        def versionConstraint = keyword == 'require' ? '1.0' : "{${keyword} 1.0}"
+        resolve.expectGraph {
+            root(':', ':test:') {
+                module("org.test:moduleA:1.0:$expectedVariant") {
+                    edge('org.test:moduleB:' + versionConstraint, 'org.test:moduleB:1.0')
+                }
+            }
+        }
+
+        where:
+        keyword << ["prefer", "require", "strictly"]
+    }
+
+    @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true")
+    def "can set version on dependency constraint"() {
+        given:
+        repository {
+            'org.test:moduleA:1.0'() {
+                constraint 'org.test:moduleB:0.1'
+            }
+        }
+
+        when:
+        buildFile << """
+            class VersionSettingRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        withDependencyConstraints {
+                            it.each {
+                                it.version { require '1.0' }
+                            }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                $variantToTest 'org.test:moduleB'
+                components {
+                    withModule('org.test:moduleA', VersionSettingRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                edge('org.test:moduleB', 'org.test:moduleB:1.0') {
+                    byConstraint(null)
+                }
+                module("org.test:moduleA:1.0:$expectedVariant") {
+                    constraint('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
+                }
+            }
+        }
+    }
+
+
+    def "changing dependencies in one variant leaves other variants untouched"() {
+        when:
+        buildFile << """
+            class ModifyDepRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("default") {
+                        withDependencies {
+                            add('org.test:moduleB:1.0')
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleA', ModifyDepRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                variant("default", ['some':'other'])
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                module("org.test:moduleA:1.0:$expectedVariant")
+            }
+        }
+    }
+
+    def "can update all variants at once"() {
+        when:
+        buildFile << """
+            class ModifyDepRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.allVariants {
+                        withDependencies {
+                            add('org.test:moduleB:1.0')
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleA', ModifyDepRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                variant("default", ['some':'other'])
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0' {
+                expectResolve()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                module("org.test:moduleA:1.0:$expectedVariant") {
+                    module('org.test:moduleB:1.0')
+                }
+            }
+        }
+    }
+
+    def "#thing of transitive dependencies can be changed"() {
+        given:
+        repository {
+            'org.test:moduleA:1.0' {
+                dependsOn 'org.test:moduleB:1.0'
+            }
+            'org.test:moduleB:1.0' {
+                variant 'customVariant', [format: 'custom']
+            }
+            'org.test:moduleC:1.0'()
+        }
+
+        when:
+        def transitiveSelectedVariant = !gradleMetadataPublished && useIvy()? 'default' : variantToTest
+        buildFile << """
+            class ModifyDepRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant('$transitiveSelectedVariant') {
+                        with${toCamelCase(thing)} { d ->
+                            add('org.test:moduleC:1.0')
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                $variantToTest 'org.test:moduleC'
+                components {
+                    withModule('org.test:moduleB', ModifyDepRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleC:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                edge('org.test:moduleC', 'org.test:moduleC:1.0') {
+                    maybeByConstraint()
+                }
+                module("org.test:moduleA:1.0:$expectedVariant") {
+                    module("org.test:moduleB:1.0") {
+                        if (thing == "dependencies") {
+                            edge('org.test:moduleC:1.0', 'org.test:moduleC:1.0')
+                        } else {
+                            constraint('org.test:moduleC:1.0', 'org.test:moduleC:1.0')
+                        }
+                    }
+                }
+            }
+        }
+
+        where:
+        thing                    | _
+        "dependencies"           | _
+        "dependency constraints" | _
+    }
+
+    def "attribute matching is used to select a variant of the dependency's target if the dependency was added by a rule"() {
+        given:
+        repository {
+            'org.test:moduleA:1.0' {
+                dependsOn 'org.test:moduleB:1.0'
+            }
+            'org.test:moduleB:1.0' {
+                variant 'customVariant', [format: 'custom']
+            }
+            'org.test:moduleD:1.0'()
+        }
+
+        def mavenGradleRepo = new MavenFileRepository(file("maven-gradle-repo"))
+        buildFile << """
+            repositories {
+                maven {
+                    url "$mavenGradleRepo.uri"
+                }
+            }
+        """
+        //All dependencies added by rules are of type GradleDependencyMetadata and thus attribute matching is used for selecting the variant/configuration of the dependency's target.
+        //Here we add a module with Gradle metadata which defines a variant that uses the same attributes declared in the build script (format: "custom").
+        //The dependency to this module is then added using the rule and thus is matched correctly.
+        mavenGradleRepo.module("org.test", "moduleC").withModuleMetadata().variant("anotherVariantWithFormatCustom", [format: "custom"]).publish()
+
+        when:
+        def transitiveSelectedVariant = !gradleMetadataPublished && useIvy()? 'default' : variantToTest
+        buildFile << """
+            class AddModuleCRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant('$transitiveSelectedVariant') {
+                        withDependencies {
+                            add('org.test:moduleC:1.0')
+                        }
+                    }
+                }
+            }
+
+            class AddModuleDRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant('anotherVariantWithFormatCustom') {
+                        withDependencies {
+                            add('org.test:moduleD:1.0')
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleB', AddModuleCRule)
+                    //this second rule is here to test that the correct variant was chosen, which is the one adding the dependency to moduleD
+                    withModule('org.test:moduleC', AddModuleDRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleC:1.0'() {
+                expectGetMetadataMissing()
+            }
+            'org.test:moduleD:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                module("org.test:moduleA:1.0:$expectedVariant") {
+                    module("org.test:moduleB:1.0") {
+                        module('org.test:moduleC:1.0') {
+                            module('org.test:moduleD:1.0')
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    def "resolving one configuration does not influence the result of resolving another configuration."() {
+        given:
+        repository {
+            'org.test:moduleA:1.0'() {
+                dependsOn 'org.test:moduleB:1.0'
+            }
+        }
+
+        when:
+        buildFile << """
+            class ModifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        withDependencies {
+                            //check that the dependency has not been removed already during resolution of the other configuration
+                            assert it.size() == 1
+                            removeAll { true }
+                        }
+                    }
+                }
+            }
+
+            configurations { anotherConfiguration { attributes { attribute(Attribute.of('format', String), 'custom') } } }
+
+            dependencies {
+                anotherConfiguration group: 'org.test', name: 'moduleA', version: '1.0' ${publishedModulesHaveAttributes ? "" : ", configuration: '$variantToTest'"}
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleA', ModifyRule)
+                }
+            }
+
+            configurations.all {
+                incoming.beforeResolve { println "Resolving \$name" }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+            }
+        }
+
+        then:
+        succeeds 'dependencies'
+    }
+
+    def "can make #thing strict"() {
+        given:
+        repository {
+            'org.test:moduleB:1.1' {
+                variant 'customVariant', [format: 'custom']
+            }
+            'org.test:moduleA:1.0'() {
+                if (defineAsConstraint) {
+                    constraint 'org.test:moduleB:1.1'
+                } else {
+                    dependsOn 'org.test:moduleB:1.1'
+                }
+            }
+        }
+
+        when:
+        buildFile << """
+            class ModifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        with${toCamelCase(thing)} { d ->
+                            d.findAll { it.name == 'moduleB' }.each {
+                                it.version { strictly '1.0' }
+                            }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                $variantToTest group: 'org.test', name: 'moduleB', version: '1.1' ${publishedModulesHaveAttributes ? "" : ", configuration: '$variantToTest'"}
+
+                components {
+                    withModule('org.test:moduleA', ModifyRule)
+                }
+            }
+        """
+        if (defineAsConstraint && !gradleMetadataPublished) {
+            //in plain ivy, we do not have the constraint published. But we can add still add it.
+            buildFile.text = buildFile.text.replace("d ->", "d -> d.add('org.test:moduleB:1.0')")
+        }
+
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+            }
+            'org.test:moduleB:1.1'() {
+                expectGetMetadata()
+            }
+        }
+
+        then:
+        fails 'checkDep'
+        failure.assertHasCause """Cannot find a version of 'org.test:moduleB' that satisfies the version constraints:
+   Dependency path ':test:unspecified' --> 'org.test:moduleB:1.1'
+   ${defineAsConstraint? 'Constraint' : 'Dependency'} path ':test:unspecified' --> 'org.test:moduleA:1.0' ($variantToTest) --> 'org.test:moduleB:{strictly 1.0}'"""
+
+        where:
+        thing                    | defineAsConstraint
+        "dependencies"           | false
+        "dependency constraints" | true
+    }
+
+    def "can add rejections to #thing"() {
+        given:
+        repository {
+            'org.test:moduleB:1.1' {
+                variant 'customVariant', [format: 'custom']
+            }
+            'org.test:moduleA:1.0'() {
+                if (defineAsConstraint) {
+                    constraint 'org.test:moduleB:1.+'
+                } else {
+                    dependsOn 'org.test:moduleB:1.+'
+                }
+            }
+        }
+
+        when:
+        buildFile << """
+            class ModifyRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant("$variantToTest") {
+                        with${toCamelCase(thing)} { d ->
+                            d.findAll { it.name == 'moduleB' }.each {
+                                it.version {
+                                    reject '1.1', '1.2'
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                $variantToTest group: 'org.test', name: 'moduleB', version: '1.1' ${publishedModulesHaveAttributes ? "" : ", configuration: '$variantToTest'"}
+
+                components {
+                    withModule('org.test:moduleA', ModifyRule)
+                }
+            }
+        """
+        if (defineAsConstraint && !gradleMetadataPublished) {
+            //in plain ivy, we do not have the constraint published. But we can add still add it.
+            buildFile.text = buildFile.text.replace("d ->", "d -> d.add('org.test:moduleB') { version { require '1.+'; reject '1.1', '1.2' }}")
+        }
+
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+            }
+            'org.test:moduleB' {
+                expectVersionListing()
+                '1.0' {
+                    expectGetMetadataMissing()
+                }
+                '1.1' {
+                    expectGetMetadata()
+                }
+            }
+        }
+
+        then:
+        fails 'checkDep'
+        failure.assertHasCause """Cannot find a version of 'org.test:moduleB' that satisfies the version constraints:
+   Dependency path ':test:unspecified' --> 'org.test:moduleB:1.1'
+   ${defineAsConstraint? 'Constraint' : 'Dependency'} path ':test:unspecified' --> 'org.test:moduleA:1.0' ($variantToTest) --> 'org.test:moduleB:{require 1.+; reject 1.1 & 1.2}'"""
+
+        where:
+        thing                    | defineAsConstraint
+        "dependencies"           | false
+        "dependency constraints" | true
+    }
+
+    def "a rule can provide a custom selection reason thanks to dependency reason"() {
+        given:
+        repository {
+            'org.test:moduleA:1.0' {
+                dependsOn group:'org.test', artifact:'moduleB', version:'1.0', reason: 'will be overwritten by rule'
+            }
+            'org.test:moduleB:1.0'()
+        }
+
+        when:
+        buildFile << """
+            class ReasonRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant('$variantToTest') {
+                        withDependencies {
+                            it.each {
+                                it.because 'can set a custom reason in a rule'
+                            }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleA', ReasonRule)
+                }
+            }
+        """
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        resolve.expectGraph {
+            root(':', ':test:') {
+                module("org.test:moduleA:1.0:$expectedVariant") {
+                    module("org.test:moduleB:1.0") {
+                        notRequested()
+                        byReason('can set a custom reason in a rule')
+                    }
+                }
+            }
+        }
+
+    }
+
+    def "a rule can provide a custom selection reason thanks to dependency constraint reason"() {
+        given:
+        repository {
+            'org.test:moduleA:1.0' {
+                dependsOn group:'org.test', artifact:'moduleB', version:'1.0', reason: 'will be overwritten by rule'
+                constraint group:'org.test', artifact:'moduleC', version:'1.0', reason: 'will be overwritten by rule'
+            }
+            'org.test:moduleB:1.0' {
+                dependsOn 'org.test:moduleC:1.0'
+            }
+            'org.test:moduleC:1.0'()
+            'org.test:moduleC:1.1'()
+        }
+
+        when:
+        buildFile << """
+            class ReasonRule implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext context) {
+                    context.details.withVariant('$variantToTest') {
+                        withDependencies {
+                            it.each {
+                                it.because 'can set a custom reason in a rule'
+                            }
+                        }
+                        withDependencyConstraints {
+                            it.each {
+                                it.version { strictly '1.1' }
+                                it.because '1.0 is buggy'
+                            }
+                        }
+                    }
+                }
+            }
+
+            dependencies {
+                components {
+                    withModule('org.test:moduleA', ReasonRule)
+                }
+            }
+        """
+        boolean constraintsUnsupported = !gradleMetadataPublished
+
+        repositoryInteractions {
+            'org.test:moduleA:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleB:1.0'() {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org.test:moduleC'() {
+                '1.0' {
+                    if (constraintsUnsupported) {
+                        expectGetMetadata()
+                        expectGetArtifact()
+                    }
+                }
+                if (!constraintsUnsupported) {
+                    '1.1' {
+                        expectResolve()
+                    }
+                }
+            }
+        }
+
+        then:
+        succeeds 'checkDep'
+        def expectedVariant = variantToTest
+        if (constraintsUnsupported) {
+            resolve.expectGraph {
+                root(':', ':test:') {
+                    module("org.test:moduleA:1.0:$expectedVariant") {
+                        module("org.test:moduleB:1.0") {
+                            notRequested()
+                            byReason('can set a custom reason in a rule')
+                            module("org.test:moduleC:1.0")
+                        }
+                    }
+                }
+            }
+        } else {
+            resolve.expectGraph {
+                root(':', ':test:') {
+                    module("org.test:moduleA:1.0:$expectedVariant") {
+                        module("org.test:moduleB:1.0") {
+                            notRequested()
+                            byReason('can set a custom reason in a rule')
+                            edge("org.test:moduleC:1.0", "org.test:moduleC:1.1") {
+                                notRequested()
+                                byAncestor()
+                                byConstraint("1.0 is buggy")
+                            }
+                        }
+                        constraint("org.test:moduleC:{strictly 1.1}", "org.test:moduleC:1.1")
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentAttributesRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentAttributesRulesIntegrationTest.groovy
index 02fcdf0..68fb87b 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentAttributesRulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentAttributesRulesIntegrationTest.groovy
@@ -220,7 +220,9 @@
         run ':checkDeps'
         resolve.expectGraph {
             root(":", ":test:") {
-                edge('org.test:module:[1.0,2.0)', 'org.test:module:1.1')
+                edge('org.test:module:[1.0,2.0)', 'org.test:module:1.1') {
+                    byReason("rejection: version 1.2:   - Attribute 'quality' didn't match. Requested 'qa', was: 'low'")
+                }
             }
         }
 
@@ -444,7 +446,15 @@
         then:
         resolve.expectGraph {
             root(":", ":test:") {
-                edge('org:test:[1,)', "org:test:$selected")
+                edge('org:test:[1,)', "org:test:$selected") {
+                    if (status == 'release') {
+                        byReason("rejection: version 5:   - Attribute 'org.gradle.status' didn't match. Requested 'release', was: 'integration'")
+                    } else if (status == 'milestone') {
+                        byReason("rejection: version 5:   - Attribute 'org.gradle.status' didn't match. Requested 'milestone', was: 'integration'")
+                        byReason("rejection: version 4:   - Attribute 'org.gradle.status' didn't match. Requested 'milestone', was: 'release'")
+                        byReason("rejection: version 3:   - Attribute 'org.gradle.status' didn't match. Requested 'milestone', was: 'integration'")
+                    }
+                }
             }
         }
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesCachingIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesCachingIntegrationTest.groovy
index 0faed96..61ed771 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesCachingIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesCachingIntegrationTest.groovy
@@ -18,7 +18,6 @@
 
 import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
 import org.gradle.integtests.fixtures.RequiredFeature
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
 
 class ComponentMetadataRulesCachingIntegrationTest extends AbstractModuleDependencyResolveTest {
@@ -32,21 +31,14 @@
     conf 'org.test:projectA:1.0'
 }
 
-// implement Sync manually to make sure that task is never up-to-date
-task resolve {
-    doLast {
-        delete 'libs'
-        copy {
-            from configurations.conf
-            into 'libs'
-        }
-    }
+task resolve(type: Sync) {
+    from configurations.conf
+    into 'libs'
 }
 """
         executer.withArgument("-Ddebug.modulesource=true")
     }
 
-    @ToBeFixedForConfigurationCache
     def "rule is cached across builds"() {
         repository {
             'org.test:projectA:1.0' {
@@ -100,7 +92,6 @@
         outputDoesNotContain('See dependency')
     }
 
-    @ToBeFixedForConfigurationCache
     @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
     def 'cached rule can access PomModuleDescriptor for Maven component'() {
         given:
@@ -249,7 +240,6 @@
         outputContains('Rule B executed - saw changing true')
     }
 
-    @ToBeFixedForConfigurationCache
     def 'can cache rules having a custom type attribute as parameter'() {
         repository {
             'org.test:projectA:1.0'()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesInSettingsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesInSettingsIntegrationTest.groovy
index 82fa151..c708807 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesInSettingsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesInSettingsIntegrationTest.groovy
@@ -23,8 +23,8 @@
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.plugin.PluginBuilder
 import org.gradle.test.fixtures.server.http.MavenHttpPluginRepository
-import org.gradle.util.TestPrecondition
-import spock.lang.IgnoreIf
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 // Restrict the number of combinations because that's not really what we want to test
 @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
@@ -219,7 +219,7 @@
     }
 
     // fails to delete directory under Windows otherwise
-    @IgnoreIf({ TestPrecondition.WINDOWS.fulfilled })
+    @Requires(UnitTestPreconditions.NotWindows)
     def "rules applied in settings don't apply to plugin resolution"() {
         def pluginPortal = MavenHttpPluginRepository.asGradlePluginPortal(executer, mavenRepo)
         pluginPortal.start()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesIntegrationTest.groovy
index 25376d1..d510618 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesIntegrationTest.groovy
@@ -17,7 +17,6 @@
 
 import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
 import org.gradle.integtests.fixtures.RequiredFeature
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
 import spock.lang.Issue
 
@@ -33,15 +32,10 @@
     conf 'org.test:projectA:1.0'
 }
 
-// implement Sync manually to make sure that task is never up-to-date
-task resolve {
-    doLast {
-        delete 'libs'
-        copy {
-            from configurations.conf
-            into 'libs'
-        }
-    }
+task resolve(type: Sync) {
+    def files = configurations.conf
+    from files
+    into 'libs'
 }
 """
     }
@@ -221,7 +215,6 @@
         succeeds 'resolve'
     }
 
-    @ToBeFixedForConfigurationCache
     def "changes made by a rule are not cached"() {
         repository {
             'org.test:projectA:1.0'()
@@ -309,8 +302,9 @@
                 }
             }
 
+            def rules1 = provider { rulesInvoked }
             resolve.doLast {
-                assert rulesInvoked == [ '1.0', '1.0', '1.0', '1.0', '1.0' ]
+                assert rules1.get() == [ '1.0', '1.0', '1.0', '1.0', '1.0' ]
                 assert VerifyingRule.ruleInvoked
             }
         """
@@ -388,9 +382,11 @@
                 }
             }
 
+            def rules1 = provider { rulesInvoked }
+            def rules2 = provider { rulesUninvoked }
             resolve.doLast {
-                assert rulesInvoked.sort() == [ 1, 2, 3 ]
-                assert rulesUninvoked.empty
+                assert rules1.get().sort() == [ 1, 2, 3 ]
+                assert rules2.get().empty
                 assert InvokedRule.ruleInvoked
                 assert !NotInvokedRule.ruleInvoked
             }
@@ -432,7 +428,6 @@
     }
 
     @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
-    @ToBeFixedForConfigurationCache
     def "rule that accepts IvyModuleDescriptor isn't invoked for Maven component"() {
         given:
         repository {
@@ -508,7 +503,6 @@
         succeeds 'resolve'
     }
 
-    @ToBeFixedForConfigurationCache
     @RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
     def 'rule can access PomModuleDescriptor for Maven component'() {
         given:
@@ -582,19 +576,22 @@
         }
     }
     task res {
+        def conf = configurations.conf
+        def other = configurations.other
         doLast {
             // If we resolve twice the modified component metadata for 'projectA' must not be cached in-memory
-            println configurations.conf.collect { it.name }
-            println configurations.other.collect { it.name }
+            println conf.collect { it.name }
+            println other.collect { it.name }
         }
     }
 }
 
 task res {
+    def conf = configurations.conf
     doLast {
         // Should get the unmodified component metadata for 'projectA'
-        println configurations.conf.collect { it.name }
-        assert configurations.conf.collect { it.name } == ['projectA-1.0.jar']
+        println conf.collect { it.name }
+        assert conf.collect { it.name } == ['projectA-1.0.jar']
     }
 }
 """
@@ -678,12 +675,15 @@
     conf 'org.test:projectA:1.0'
 }
 
-resolve {
+task downloadRules {
+    def files = configurations.ruleDownloader
     doFirst {
-        configurations.ruleDownloader.resolve() // trigger resolution before rules are added
+        files.files // trigger resolution before rules are added
     }
 }
 
+resolve.dependsOn(downloadRules)
+
 """
 
         when:
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesStatusIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesStatusIntegrationTest.groovy
index ecd6820..79380b3 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesStatusIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/ComponentMetadataRulesStatusIntegrationTest.groovy
@@ -40,4 +40,4 @@
 }
 """
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIntegrationTest.groovy
deleted file mode 100644
index afa228d..0000000
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIntegrationTest.groovy
+++ /dev/null
@@ -1,1076 +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.integtests.resolve.rules
-
-import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
-import org.gradle.integtests.fixtures.RequiredFeature
-import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
-import org.gradle.test.fixtures.maven.MavenFileRepository
-
-import static org.gradle.util.internal.GUtil.toCamelCase
-
-class DependencyMetadataRulesIntegrationTest extends AbstractModuleDependencyResolveTest {
-    @Override
-    String getTestConfiguration() { variantToTest }
-
-    /**
-     * Does the published metadata provide variants with attributes? Eventually all metadata should do that.
-     * For Ivy and Maven POM metadata, the variants and attributes should be derived from configurations and scopes.
-     */
-    boolean getPublishedModulesHaveAttributes() { gradleMetadataPublished }
-
-    String getVariantToTest() {
-        if (gradleMetadataPublished || useIvy()) {
-            'customVariant'
-        } else {
-            'runtime'
-        }
-    }
-
-    def setup() {
-        repository {
-            'org.test:moduleA:1.0'() {
-                variant 'customVariant', [format: 'custom']
-            }
-            'org.test:moduleB:1.0'()
-        }
-
-        buildFile << """
-            configurations { $variantToTest { attributes { attribute(Attribute.of('format', String), 'custom') } } }
-
-            dependencies {
-                $variantToTest group: 'org.test', name: 'moduleA', version: '1.0' ${publishedModulesHaveAttributes ? "" : ", configuration: '$variantToTest'"}
-            }
-        """
-    }
-
-    def "#thing can be added using #notation notation"() {
-        when:
-        buildFile << """
-            class ModifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        with${toCamelCase(thing)} {
-                            add $declaration
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                $variantToTest 'org.test:moduleB'
-                components {
-                    withModule('org.test:moduleA', ModifyRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                edge('org.test:moduleB', 'org.test:moduleB:1.0')
-                module("org.test:moduleA:1.0:$expectedVariant") {
-                    if (thing == "dependencies") {
-                        edge('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
-                    } else {
-                        constraint('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
-                    }
-                }
-            }
-        }
-
-        where:
-        thing                    | notation | declaration
-        "dependency constraints" | "string" | "'org.test:moduleB:1.0'"
-        "dependency constraints" | "map"    | "group: 'org.test', name: 'moduleB', version: '1.0'"
-        "dependencies"           | "string" | "'org.test:moduleB:1.0'"
-        "dependencies"           | "map"    | "group: 'org.test', name: 'moduleB', version: '1.0'"
-    }
-
-    def "#thing can be added to a new variant"() {
-        when:
-        buildFile << """
-            class ModifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.addVariant("new") {
-                        with${toCamelCase(thing)} {
-                            add 'org.test:moduleB:1.0'
-                        }
-                        withCapabilities {
-                            removeCapability("org.test", "moduleA")
-                            addCapability("all", "new", "1.0")
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                $variantToTest 'org.test:moduleB'
-                $variantToTest('org.test:moduleA:1.0') {
-                    capabilities { requireCapability("all:new") }
-                }
-                components {
-                    withModule('org.test:moduleA', ModifyRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                edge('org.test:moduleB', 'org.test:moduleB:1.0')
-                module("org.test:moduleA:1.0:$expectedVariant")
-                module("org.test:moduleA:1.0:new") {
-                    if (thing == "dependencies") {
-                        edge('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
-                    } else {
-                        constraint('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
-                    }
-                }
-            }
-        }
-
-        where:
-        thing                    | _
-        "dependency constraints" | _
-        "dependencies"           | _
-    }
-
-    def "#thing can be added and configured using #notation notation"() {
-        when:
-        buildFile << """
-            class ModifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        with${toCamelCase(thing)} {
-                            add($declaration) {
-                                it.version { strictly '1.0' }
-                            }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                $variantToTest 'org.test:moduleB'
-                components {
-                    withModule('org.test:moduleA', ModifyRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                edge('org.test:moduleB', 'org.test:moduleB:1.0')
-                module("org.test:moduleA:1.0:$expectedVariant") {
-                    if (thing == "dependencies") {
-                        edge('org.test:moduleB:{strictly 1.0}', 'org.test:moduleB:1.0')
-                    } else {
-                        constraint('org.test:moduleB:{strictly 1.0}', 'org.test:moduleB:1.0')
-                    }
-                }
-            }
-        }
-
-        where:
-        thing                    | notation | declaration
-        "dependency constraints" | "string" | "'org.test:moduleB:1.0'"
-        "dependency constraints" | "map"    | "group: 'org.test', name: 'moduleB', version: '1.0'"
-        "dependencies"           | "string" | "'org.test:moduleB:1.0'"
-        "dependencies"           | "map"    | "group: 'org.test', name: 'moduleB', version: '1.0'"
-    }
-
-    def "dependencies can be removed"() {
-        given:
-        repository {
-            'org.test:moduleA:1.0' {
-                dependsOn 'org.test:moduleB:1.0'
-            }
-        }
-
-        when:
-        buildFile << """
-            class ModifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        withDependencies {
-                            removeAll { it.versionConstraint.requiredVersion == '1.0' }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleA', ModifyRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                module("org.test:moduleA:1.0:$expectedVariant")
-            }
-        }
-    }
-
-    def "dependency constraints can be removed"() {
-        given:
-        repository {
-            'org.test:moduleA:1.0' {
-                constraint 'org.test:moduleB:2.0'
-            }
-        }
-
-        when:
-        buildFile << """
-            class ModifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        withDependencyConstraints {
-                            removeAll { it.versionConstraint.requiredVersion == '2.0' }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                $variantToTest 'org.test:moduleB:1.0'
-                components {
-                    withModule('org.test:moduleA', ModifyRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-
-            }
-            'org.test:moduleB'() {
-                version('1.0') {
-                    expectGetMetadata()
-                    expectGetArtifact()
-                }
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                module("org.test:moduleB:1.0")
-                module("org.test:moduleA:1.0:$expectedVariant")
-            }
-        }
-    }
-
-    def "#thing modifications are visible in the next rule"() {
-        when:
-        buildFile << """
-            class AddRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        with${toCamelCase(thing)} { d ->
-                            assert d.size() == 0
-                            d.add 'org.test:moduleB:1.0'
-                        }
-                    }
-                }
-            }
-
-            class RemoveRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        with${toCamelCase(thing)} { d ->
-                            assert d.size() == 1
-                            d.removeAll { true }
-                        }
-                    }
-                }
-            }
-
-            class VerifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        with${toCamelCase(thing)} { d ->
-                            assert d.size() == 0
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleA', AddRule)
-                    withModule('org.test:moduleA', RemoveRule)
-                    all(VerifyRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                module("org.test:moduleA:1.0:$expectedVariant")
-            }
-        }
-
-        where:
-        thing                    | _
-        "dependencies"           | _
-        "dependency constraints" | _
-    }
-
-    def "can set version on dependency using #keyword"() {
-        given:
-        repository {
-            'org.test:moduleA:1.0'() {
-                dependsOn 'org.test:moduleB'
-            }
-        }
-
-        when:
-        buildFile << """
-            class VersionSettingRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        withDependencies {
-                            it.each {
-                                it.version {
-                                    require ''
-                                    ${keyword} '1.0'
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleA', VersionSettingRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        def versionConstraint = keyword == 'require' ? '1.0' : "{${keyword} 1.0}"
-        resolve.expectGraph {
-            root(':', ':test:') {
-                module("org.test:moduleA:1.0:$expectedVariant") {
-                    edge('org.test:moduleB:' + versionConstraint, 'org.test:moduleB:1.0')
-                }
-            }
-        }
-
-        where:
-        keyword << ["prefer", "require", "strictly"]
-    }
-
-    @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true")
-    def "can set version on dependency constraint"() {
-        given:
-        repository {
-            'org.test:moduleA:1.0'() {
-                constraint 'org.test:moduleB:0.1'
-            }
-        }
-
-        when:
-        buildFile << """
-            class VersionSettingRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        withDependencyConstraints {
-                            it.each {
-                                it.version { require '1.0' }
-                            }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                $variantToTest 'org.test:moduleB'
-                components {
-                    withModule('org.test:moduleA', VersionSettingRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                edge('org.test:moduleB', 'org.test:moduleB:1.0')
-                module("org.test:moduleA:1.0:$expectedVariant") {
-                    constraint('org.test:moduleB:1.0', 'org.test:moduleB:1.0')
-                }
-            }
-        }
-    }
-
-
-    def "changing dependencies in one variant leaves other variants untouched"() {
-        when:
-        buildFile << """
-            class ModifyDepRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("default") {
-                        withDependencies {
-                            add('org.test:moduleB:1.0')
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleA', ModifyDepRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                variant("default", ['some':'other'])
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                module("org.test:moduleA:1.0:$expectedVariant")
-            }
-        }
-    }
-
-    def "can update all variants at once"() {
-        when:
-        buildFile << """
-            class ModifyDepRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.allVariants {
-                        withDependencies {
-                            add('org.test:moduleB:1.0')
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleA', ModifyDepRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                variant("default", ['some':'other'])
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0' {
-                expectResolve()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                module("org.test:moduleA:1.0:$expectedVariant") {
-                    module('org.test:moduleB:1.0')
-                }
-            }
-        }
-    }
-
-    def "#thing of transitive dependencies can be changed"() {
-        given:
-        repository {
-            'org.test:moduleA:1.0' {
-                dependsOn 'org.test:moduleB:1.0'
-            }
-            'org.test:moduleB:1.0' {
-                variant 'customVariant', [format: 'custom']
-            }
-            'org.test:moduleC:1.0'()
-        }
-
-        when:
-        def transitiveSelectedVariant = !gradleMetadataPublished && useIvy()? 'default' : variantToTest
-        buildFile << """
-            class ModifyDepRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant('$transitiveSelectedVariant') {
-                        with${toCamelCase(thing)} { d ->
-                            add('org.test:moduleC:1.0')
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                $variantToTest 'org.test:moduleC'
-                components {
-                    withModule('org.test:moduleB', ModifyDepRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleC:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                edge('org.test:moduleC', 'org.test:moduleC:1.0')
-                module("org.test:moduleA:1.0:$expectedVariant") {
-                    module("org.test:moduleB:1.0") {
-                        if (thing == "dependencies") {
-                            edge('org.test:moduleC:1.0', 'org.test:moduleC:1.0')
-                        } else {
-                            constraint('org.test:moduleC:1.0', 'org.test:moduleC:1.0')
-                        }
-                    }
-                }
-            }
-        }
-
-        where:
-        thing                    | _
-        "dependencies"           | _
-        "dependency constraints" | _
-    }
-
-    def "attribute matching is used to select a variant of the dependency's target if the dependency was added by a rule"() {
-        given:
-        repository {
-            'org.test:moduleA:1.0' {
-                dependsOn 'org.test:moduleB:1.0'
-            }
-            'org.test:moduleB:1.0' {
-                variant 'customVariant', [format: 'custom']
-            }
-            'org.test:moduleD:1.0'()
-        }
-
-        def mavenGradleRepo = new MavenFileRepository(file("maven-gradle-repo"))
-        buildFile << """
-            repositories {
-                maven {
-                    url "$mavenGradleRepo.uri"
-                }
-            }
-        """
-        //All dependencies added by rules are of type GradleDependencyMetadata and thus attribute matching is used for selecting the variant/configuration of the dependency's target.
-        //Here we add a module with Gradle metadata which defines a variant that uses the same attributes declared in the build script (format: "custom").
-        //The dependency to this module is then added using the rule and thus is matched correctly.
-        mavenGradleRepo.module("org.test", "moduleC").withModuleMetadata().variant("anotherVariantWithFormatCustom", [format: "custom"]).publish()
-
-        when:
-        def transitiveSelectedVariant = !gradleMetadataPublished && useIvy()? 'default' : variantToTest
-        buildFile << """
-            class AddModuleCRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant('$transitiveSelectedVariant') {
-                        withDependencies {
-                            add('org.test:moduleC:1.0')
-                        }
-                    }
-                }
-            }
-
-            class AddModuleDRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant('anotherVariantWithFormatCustom') {
-                        withDependencies {
-                            add('org.test:moduleD:1.0')
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleB', AddModuleCRule)
-                    //this second rule is here to test that the correct variant was chosen, which is the one adding the dependency to moduleD
-                    withModule('org.test:moduleC', AddModuleDRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleC:1.0'() {
-                expectGetMetadataMissing()
-            }
-            'org.test:moduleD:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                module("org.test:moduleA:1.0:$expectedVariant") {
-                    module("org.test:moduleB:1.0") {
-                        module('org.test:moduleC:1.0') {
-                            module('org.test:moduleD:1.0')
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    def "resolving one configuration does not influence the result of resolving another configuration."() {
-        given:
-        repository {
-            'org.test:moduleA:1.0'() {
-                dependsOn 'org.test:moduleB:1.0'
-            }
-        }
-
-        when:
-        buildFile << """
-            class ModifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        withDependencies {
-                            //check that the dependency has not been removed already during resolution of the other configuration
-                            assert it.size() == 1
-                            removeAll { true }
-                        }
-                    }
-                }
-            }
-
-            configurations { anotherConfiguration { attributes { attribute(Attribute.of('format', String), 'custom') } } }
-
-            dependencies {
-                anotherConfiguration group: 'org.test', name: 'moduleA', version: '1.0' ${publishedModulesHaveAttributes ? "" : ", configuration: '$variantToTest'"}
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleA', ModifyRule)
-                }
-            }
-
-            configurations.all {
-                incoming.beforeResolve { println "Resolving \$name" }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-            }
-        }
-
-        then:
-        succeeds 'dependencies'
-    }
-
-    def "can make #thing strict"() {
-        given:
-        repository {
-            'org.test:moduleB:1.1' {
-                variant 'customVariant', [format: 'custom']
-            }
-            'org.test:moduleA:1.0'() {
-                if (defineAsConstraint) {
-                    constraint 'org.test:moduleB:1.1'
-                } else {
-                    dependsOn 'org.test:moduleB:1.1'
-                }
-            }
-        }
-
-        when:
-        buildFile << """
-            class ModifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        with${toCamelCase(thing)} { d ->
-                            d.findAll { it.name == 'moduleB' }.each {
-                                it.version { strictly '1.0' }
-                            }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                $variantToTest group: 'org.test', name: 'moduleB', version: '1.1' ${publishedModulesHaveAttributes ? "" : ", configuration: '$variantToTest'"}
-
-                components {
-                    withModule('org.test:moduleA', ModifyRule)
-                }
-            }
-        """
-        if (defineAsConstraint && !gradleMetadataPublished) {
-            //in plain ivy, we do not have the constraint published. But we can add still add it.
-            buildFile.text = buildFile.text.replace("d ->", "d -> d.add('org.test:moduleB:1.0')")
-        }
-
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-            }
-            'org.test:moduleB:1.1'() {
-                expectGetMetadata()
-            }
-        }
-
-        then:
-        fails 'checkDep'
-        failure.assertHasCause """Cannot find a version of 'org.test:moduleB' that satisfies the version constraints:
-   Dependency path ':test:unspecified' --> 'org.test:moduleB:1.1'
-   ${defineAsConstraint? 'Constraint' : 'Dependency'} path ':test:unspecified' --> 'org.test:moduleA:1.0' ($variantToTest) --> 'org.test:moduleB:{strictly 1.0}'"""
-
-        where:
-        thing                    | defineAsConstraint
-        "dependencies"           | false
-        "dependency constraints" | true
-    }
-
-    def "can add rejections to #thing"() {
-        given:
-        repository {
-            'org.test:moduleB:1.1' {
-                variant 'customVariant', [format: 'custom']
-            }
-            'org.test:moduleA:1.0'() {
-                if (defineAsConstraint) {
-                    constraint 'org.test:moduleB:1.+'
-                } else {
-                    dependsOn 'org.test:moduleB:1.+'
-                }
-            }
-        }
-
-        when:
-        buildFile << """
-            class ModifyRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant("$variantToTest") {
-                        with${toCamelCase(thing)} { d ->
-                            d.findAll { it.name == 'moduleB' }.each {
-                                it.version {
-                                    reject '1.1', '1.2'
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                $variantToTest group: 'org.test', name: 'moduleB', version: '1.1' ${publishedModulesHaveAttributes ? "" : ", configuration: '$variantToTest'"}
-
-                components {
-                    withModule('org.test:moduleA', ModifyRule)
-                }
-            }
-        """
-        if (defineAsConstraint && !gradleMetadataPublished) {
-            //in plain ivy, we do not have the constraint published. But we can add still add it.
-            buildFile.text = buildFile.text.replace("d ->", "d -> d.add('org.test:moduleB') { version { require '1.+'; reject '1.1', '1.2' }}")
-        }
-
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-            }
-            'org.test:moduleB' {
-                expectVersionListing()
-                '1.0' {
-                    expectGetMetadataMissing()
-                }
-                '1.1' {
-                    expectGetMetadata()
-                }
-            }
-        }
-
-        then:
-        fails 'checkDep'
-        failure.assertHasCause """Cannot find a version of 'org.test:moduleB' that satisfies the version constraints:
-   Dependency path ':test:unspecified' --> 'org.test:moduleB:1.1'
-   ${defineAsConstraint? 'Constraint' : 'Dependency'} path ':test:unspecified' --> 'org.test:moduleA:1.0' ($variantToTest) --> 'org.test:moduleB:{require 1.+; reject 1.1 & 1.2}'"""
-
-        where:
-        thing                    | defineAsConstraint
-        "dependencies"           | false
-        "dependency constraints" | true
-    }
-
-    def "a rule can provide a custom selection reason thanks to dependency reason"() {
-        given:
-        repository {
-            'org.test:moduleA:1.0' {
-                dependsOn group:'org.test', artifact:'moduleB', version:'1.0', reason: 'will be overwritten by rule'
-            }
-            'org.test:moduleB:1.0'()
-        }
-
-        when:
-        buildFile << """
-            class ReasonRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant('$variantToTest') {
-                        withDependencies {
-                            it.each {
-                                it.because 'can set a custom reason in a rule'
-                            }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleA', ReasonRule)
-                }
-            }
-        """
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        resolve.expectGraph {
-            root(':', ':test:') {
-                module("org.test:moduleA:1.0:$expectedVariant") {
-                    module("org.test:moduleB:1.0").byReason('can set a custom reason in a rule')
-                }
-            }
-        }
-
-    }
-
-    def "a rule can provide a custom selection reason thanks to dependency constraint reason"() {
-        given:
-        repository {
-            'org.test:moduleA:1.0' {
-                dependsOn group:'org.test', artifact:'moduleB', version:'1.0', reason: 'will be overwritten by rule'
-                constraint group:'org.test', artifact:'moduleC', version:'1.0', reason: 'will be overwritten by rule'
-            }
-            'org.test:moduleB:1.0' {
-                dependsOn 'org.test:moduleC:1.0'
-            }
-            'org.test:moduleC:1.0'()
-            'org.test:moduleC:1.1'()
-        }
-
-        when:
-        buildFile << """
-            class ReasonRule implements ComponentMetadataRule {
-                void execute(ComponentMetadataContext context) {
-                    context.details.withVariant('$variantToTest') {
-                        withDependencies {
-                            it.each {
-                                it.because 'can set a custom reason in a rule'
-                            }
-                        }
-                        withDependencyConstraints {
-                            it.each {
-                                it.version { strictly '1.1' }
-                                it.because '1.0 is buggy'
-                            }
-                        }
-                    }
-                }
-            }
-
-            dependencies {
-                components {
-                    withModule('org.test:moduleA', ReasonRule)
-                }
-            }
-        """
-        boolean constraintsUnsupported = !gradleMetadataPublished
-
-        repositoryInteractions {
-            'org.test:moduleA:1.0' {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleB:1.0'() {
-                expectGetMetadata()
-                expectGetArtifact()
-            }
-            'org.test:moduleC'() {
-                '1.0' {
-                    if (constraintsUnsupported) {
-                        expectGetMetadata()
-                        expectGetArtifact()
-                    }
-                }
-                if (!constraintsUnsupported) {
-                    '1.1' {
-                        expectResolve()
-                    }
-                }
-            }
-        }
-
-        then:
-        succeeds 'checkDep'
-        def expectedVariant = variantToTest
-        if (constraintsUnsupported) {
-            resolve.expectGraph {
-                root(':', ':test:') {
-                    module("org.test:moduleA:1.0:$expectedVariant") {
-                        module("org.test:moduleB:1.0") {
-                            module("org.test:moduleC:1.0")
-                            byReason('can set a custom reason in a rule')
-                        }
-
-                    }
-                }
-            }
-        } else {
-            resolve.expectGraph {
-                root(':', ':test:') {
-                    module("org.test:moduleA:1.0:$expectedVariant") {
-                        module("org.test:moduleB:1.0") {
-                            edge("org.test:moduleC:1.0", "org.test:moduleC:1.1")
-                            byReason('can set a custom reason in a rule')
-                        }
-                        constraint("org.test:moduleC:{strictly 1.1}", "org.test:moduleC:1.1").byAncestor()
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIvyIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIvyIntegrationTest.groovy
new file mode 100644
index 0000000..e369dfd
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesIvyIntegrationTest.groovy
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.integtests.resolve.rules
+
+import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
+import org.gradle.integtests.fixtures.RequiredFeature
+
+@RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "ivy")
+class DependencyMetadataRulesIvyIntegrationTest extends AbstractDependencyMetadataRulesIntegrationTest {
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesMavenIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesMavenIntegrationTest.groovy
new file mode 100644
index 0000000..661dfb8
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyMetadataRulesMavenIntegrationTest.groovy
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.integtests.resolve.rules
+
+import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
+import org.gradle.integtests.fixtures.RequiredFeature
+
+@RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
+class DependencyMetadataRulesMavenIntegrationTest extends AbstractDependencyMetadataRulesIntegrationTest{
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesDisableGlobalDependencySubstitutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesDisableGlobalDependencySubstitutionIntegrationTest.groovy
index 1d1c29d..866e0d4 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesDisableGlobalDependencySubstitutionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesDisableGlobalDependencySubstitutionIntegrationTest.groovy
@@ -52,7 +52,7 @@
                 }
                 configurations.create('runtime') {
                     extendsFrom(conf)
-                    canBeConsumed = true
+                    assert canBeConsumed
                     canBeResolved = false
                     attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
                 }
@@ -61,13 +61,13 @@
                 configurations.create('localPath') {
                     extendsFrom(configurations.conf)
                     canBeConsumed = false
-                    canBeResolved = true
+                    assert canBeResolved
                     attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
                 }
                 configurations.create('publishedPath') {
                     extendsFrom(configurations.conf)
                     canBeConsumed = false
-                    canBeResolved = true
+                    assert canBeResolved
                     resolutionStrategy.useGlobalDependencySubstitutionRules.set(false)
                     attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
                 }
@@ -97,10 +97,12 @@
         resolve.expectGraph {
             root(":m1", "org.test:m1:0.9") {
                 edge("org.test:m2:1.0", ":m2", "org.test:m2:0.9") {
+                    compositeSubstitute()
+                    noArtifacts()
                     edge("org.test:m3:1.0", ":m3", "org.test:m3:0.9") {
+                        compositeSubstitute()
                         noArtifacts()
                     }
-                    noArtifacts()
                 }
             }
         }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesIntegrationTest.groovy
index 05c029e..49b5e51 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesIntegrationTest.groovy
@@ -19,9 +19,19 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.extensions.FluidDependenciesResolveTest
+import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
+
+import java.util.concurrent.CopyOnWriteArrayList
 
 @FluidDependenciesResolveTest
 class DependencyResolveRulesIntegrationTest extends AbstractIntegrationSpec {
+    ResolveTestFixture resolve = new ResolveTestFixture(buildFile)
+
+    def setup() {
+        settingsFile << """
+            rootProject.name = 'test'
+        """
+    }
 
     /**
      * Test demonstrating current (not necessarily desired) behaviour
@@ -51,22 +61,20 @@
                         assert it.target.toString() == 'org.gradle.test:a:1.3'
                     }
                 }
-                task check {
-                    doLast {
-                        def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
-                        assert deps.size() == 1
-                        assert deps[0].selected.id instanceof ModuleComponentIdentifier
-                        assert deps[0].selected.id.version == '1.3'
-                    }
-                }
             }
 """
-        expect:
-        // Force resolve to catch failures
-        succeeds("resolveConf")
-        succeeds("check")
-    }
+        resolve.prepare("conf")
 
+        expect:
+        succeeds(":b:checkDeps")
+        resolve.expectGraph {
+            root(":b", "test:b:") {
+                edge("project :a", "org.gradle.test:a:1.3") {
+                    selectedByRule()
+                }
+            }
+        }
+    }
 
     void "forces multiple modules by rule"()
     {
@@ -98,12 +106,24 @@
                 failOnVersionConflict()
             }
 """
+        resolve.prepare("conf")
 
-        when:
-        run("resolveConf")
-
-        then:
-        noExceptionThrown()
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                 module("org.stuff:foo:2.0") {
+                    module("org.utils:api:1.5") {
+                        selectedByRule()
+                    }
+                }
+                edge("org.utils:impl:1.3", "org.utils:impl:1.5") {
+                    selectedByRule()
+                    module("org.utils:api:1.5")
+                }
+                module("org.utils:optional-lib:5.0")
+            }
+        }
     }
 
     void "module forced by rule has correct selection reason"()
@@ -130,23 +150,23 @@
                     }
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                    assert deps*.selected.id.module == ['foo', 'impl', 'api']
-                    assert deps*.selected.id.version == ['2.0', '1.5', '1.5']
-                    assert deps*.selected.selectionReason.forced         == [false, false, false]
-                    assert deps*.selected.selectionReason.selectedByRule == [false, true, true]
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.stuff:foo:2.0") {
+                    edge("org.utils:impl:1.3", "org.utils:impl:1.5") {
+                        selectedByRule()
+                        module("org.utils:api:1.5") {
+                            selectedByRule()
+                        }
+                    }
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     void "all rules are executed orderly and last one wins"()
@@ -180,26 +200,21 @@
                     //don't change the version
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                    assert deps.size() == 2
-                    deps.each {
-                        assert it.selected.id.version == '1.5'
-                        assert it.selected.selectionReason.selectedByRule
-                        assert it.selected.selectionReason.toString() == 'selected by rule'
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.utils:impl:1.3", "org.utils:impl:1.5") {
+                    selectedByRule()
+                    module("org.utils:api:1.5") {
+                        selectedByRule()
                     }
                 }
             }
-"""
-
-        when:
-        succeeds("resolveConf")
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     void "can override forced version with rule"()
@@ -224,26 +239,23 @@
                     it.useVersion it.requested.version
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                    assert deps.size() == 2
-                    deps.each {
-                        assert it.selected.id.version == '1.3'
-                        def reason = it.selected.selectionReason
-                        assert reason.forced
-                        assert reason.selectedByRule
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.utils:impl:1.3") {
+                    selectedByRule()
+                    forced()
+                    module("org.utils:api:1.3") {
+                        selectedByRule()
+                        forced()
                     }
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     void "rule are applied after forced modules"()
@@ -269,26 +281,23 @@
                     it.useVersion '1.3'
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                    assert deps.size() == 2
-                    deps.each {
-                        assert it.selected.id.version == '1.3'
-                        def reason = it.selected.selectionReason
-                        assert reason.forced
-                        assert reason.selectedByRule
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.utils:impl:1.3") {
+                    selectedByRule()
+                    forced()
+                    module("org.utils:api:1.3") {
+                        selectedByRule()
+                        forced()
                     }
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     void "forced modules and rules coexist"()
@@ -316,32 +325,21 @@
                     }
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def deps = configurations.conf.incoming.resolutionResult.allDependencies
-                    assert deps.find {
-                        it.selected.id.module == 'impl' &&
-                        it.selected.id.version == '1.5' &&
-                        it.selected.selectionReason.forced &&
-                        !it.selected.selectionReason.selectedByRule
-                    }
-
-                    assert deps.find {
-                        it.selected.id.module == 'api' &&
-                        it.selected.id.version == '1.5' &&
-                        !it.selected.selectionReason.forced &&
-                        it.selected.selectionReason.selectedByRule
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.utils:impl:1.3", "org.utils:impl:1.5") {
+                    forced()
+                    module("org.utils:api:1.5") {
+                        selectedByRule()
                     }
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     void "rule selects a dynamic version"()
@@ -360,27 +358,20 @@
             configurations.conf.resolutionStrategy.eachDependency {
                 it.useVersion '1.+'
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
-                    assert deps.size() == 1
-                    assert deps[0].requested.version == '1.3'
-                    assert deps[0].selected.id.version == '1.5'
-                    assert !deps[0].selected.selectionReason.forced
-                    assert deps[0].selected.selectionReason.selectedByRule
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.utils:api:1.3", "org.utils:api:1.5") {
+                    selectedByRule()
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
-
     void "can deny a version"()
     {
         mavenRepo.module("org.utils", "a",  '1.4').publish()
@@ -401,25 +392,22 @@
                     it.useVersion '1.4'
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                    def a = modules.find { it.id.module == 'a' }
-                    assert a.id.version == '1.4'
-                    assert a.selectionReason.conflictResolution
-                    assert a.selectionReason.selectedByRule
-                    assert !a.selectionReason.forced
-                    assert a.selectionReason.descriptions*.description.containsAll(['selected by rule', 'between versions 1.4 and 1.3'])
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.utils:a:1.2", "org.utils:a:1.4") {
+                    selectedByRule()
+                    byConflictResolution("between versions 1.4 and 1.3")
+                }
+                module("org.utils:b:1.3") {
+                    edge("org.utils:a:1.3", "org.utils:a:1.4")
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     void "can deny a version that is not used"()
@@ -441,24 +429,22 @@
                     it.useVersion '1.2.1'
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                    def a = modules.find { it.id.module == 'a' }
-                    assert a.id.version == '1.3'
-                    assert a.selectionReason.conflictResolution
-                    assert a.selectionReason.selectedByRule
-                    assert !a.selectionReason.forced
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.utils:a:1.2", "org.utils:a:1.3") {
+                    selectedByRule()
+                    byConflictResolution("between versions 1.3 and 1.2.1")
+                }
+                module("org.utils:b:1.3") {
+                    module("org.utils:a:1.3")
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     def "can use custom versioning scheme"()
@@ -477,23 +463,18 @@
                     it.useVersion '1.3'
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
-                    assert deps.size() == 1
-                    deps[0].requested.version == 'default'
-                    deps[0].selected.id.version == '1.3'
-                    deps[0].selected.selectionReason.selectedByRule
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.utils:api:default", "org.utils:api:1.3") {
+                    selectedByRule()
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     def "can use custom versioning scheme for transitive dependencies"()
@@ -513,24 +494,20 @@
                     it.useVersion '1.3'
                 }
             }
+"""
+        resolve.prepare("conf")
 
-            task check {
-                doLast {
-                    def deps = configurations.conf.incoming.resolutionResult.allDependencies as List
-                    assert deps.size() == 2
-                    def api = deps.find { it.requested.module == 'api' }
-                    api.requested.version == 'default'
-                    api.selected.id.version == '1.3'
-                    api.selected.selectionReason.selectedByRule
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org.utils:impl:1.3") {
+                    edge("org.utils:api:default", "org.utils:api:1.3") {
+                        selectedByRule()
+                    }
                 }
             }
-"""
-
-        when:
-        run("check")
-
-        then:
-        noExceptionThrown()
+        }
     }
 
     void "rule selects unavailable version"()
@@ -598,7 +575,7 @@
                 conf 'org.utils:impl:1.3', 'org.stuff:foo:2.0', 'org.stuff:bar:2.0'
             }
 
-            List requested = [].asSynchronized()
+            List requested = new ${CopyOnWriteArrayList.name}()
 
             configurations.conf.resolutionStrategy {
                 eachDependency {
@@ -607,8 +584,9 @@
             }
 
             task check {
+                def files = configurations.conf
                 doLast {
-                    configurations.conf.resolve()
+                    files.files
                     requested = requested.sort()
                     assert requested == ['api:1.3', 'api:1.5', 'bar:2.0', 'foo:2.0', 'impl:1.3']
                 }
@@ -676,28 +654,20 @@
                     it.useTarget(it.requested.group + ':b:2.1')
                 }
             }
-
-            task check {
-                doLast {
-                    def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                    assert !modules.find { it.id.module == 'a' }
-                    def b = modules.find { it.id.module == 'b' }
-                    assert b.id.version == '2.1'
-                    assert b.selectionReason.conflictResolution
-                    assert b.selectionReason.selectedByRule
-                    assert !b.selectionReason.forced
-                    assert b.selectionReason.descriptions*.description.containsAll(['selected by rule', 'between versions 2.1 and 2.0'])
-                }
-            }
 """
+        resolve.prepare("conf")
 
-        when:
-        run("check", "dependencies")
-
-        then:
-        output.contains """conf
-+--- org.utils:a:1.2 -> org.utils:b:2.1
-\\--- org.utils:b:2.0 -> 2.1"""
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org.utils:a:1.2", "org.utils:b:2.1") {
+                    selectedByRule()
+                    byConflictResolution("between versions 2.1 and 2.0")
+                }
+                edge("org.utils:b:2.0", "org.utils:b:2.1")
+            }
+        }
     }
 
     def "can substitute module group"()
@@ -722,16 +692,22 @@
                 }
             }
 """
+        resolve.prepare("conf")
 
-        when:
-        run("dependencies")
-
-        then:
-        output.contains """
-+--- org:a:1.0 -> 2.0
-|    \\--- org:c:1.0
-\\--- foo:b:1.0 -> org:b:1.0
-     \\--- org:a:2.0 (*)"""
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1.0", "org:a:2.0") {
+                    byConflictResolution("between versions 2.0 and 1.0")
+                    module("org:c:1.0")
+                }
+                edge("foo:b:1.0", "org:b:1.0") {
+                    selectedByRule()
+                    module("org:a:2.0")
+                }
+            }
+        }
     }
 
     def "can substitute module group, name and version"()
@@ -756,16 +732,22 @@
                 }
             }
 """
+        resolve.prepare("conf")
 
-        when:
-        run("dependencies")
-
-        then:
-        output.contains """
-+--- org:a:1.0 -> 2.0
-|    \\--- org:c:1.0
-\\--- foo:bar:baz -> org:b:1.0
-     \\--- org:a:2.0 (*)"""
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1.0", "org:a:2.0") {
+                    byConflictResolution("between versions 2.0 and 1.0")
+                    module("org:c:1.0")
+                }
+                edge("foo:bar:baz", "org:b:1.0") {
+                    selectedByRule()
+                    module("org:a:2.0")
+                }
+            }
+        }
     }
 
     def "provides decent feedback when target module incorrectly specified"()
@@ -783,7 +765,7 @@
 """
 
         when:
-        runAndFail("dependencies")
+        runAndFail("dependencies", "resolveConf")
 
         then:
         failure.assertHasCause("Could not resolve all files for configuration ':conf'.")
@@ -810,18 +792,23 @@
                 }
             }
 """
+        resolve.prepare("conf")
 
-        when:
-        run("dependencies")
-
-        then:
-        output.contains """
-conf
-+--- org:a:1.0 -> org:c:2.0
-\\--- org:a:2.0
-     \\--- org:b:2.0
-          \\--- org:c:2.0
-"""
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1.0", "org:c:2.0") {
+                    selectedByRule()
+                    byConflictResolution("between versions 2.0 and 1.1")
+                }
+                module("org:a:2.0") {
+                    module("org:b:2.0") {
+                        module("org:c:2.0")
+                    }
+                }
+            }
+        }
     }
 
     def "module selected by conflict resolution can be selected again in a another pass of conflict resolution"()
@@ -849,17 +836,24 @@
             dependencies {
                 conf 'org:b:3.0', 'org:b:4.0', 'org:a:1.0', 'org:a:2.0'
             }
-
-            task check {
-                doLast {
-                    def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                    assert modules.find { it.id.module == 'b' && it.id.version == '4.0' && it.selectionReason.conflictResolution }
-                }
-            }
 """
+        resolve.prepare("conf")
 
         expect:
-        run("check")
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:b:3.0", "org:b:4.0")
+                module("org:b:4.0") {
+                    byConflictResolution("between versions 4.0, 3.0 and 2.5")
+                }
+                edge("org:a:1.0", "org:a:2.0")
+                module("org:a:2.0") {
+                    byConflictResolution("between versions 2.0 and 1.0")
+                    edge("org:b:2.5", "org:b:4.0")
+                }
+            }
+        }
     }
 
     def "custom selection reasons are available in resolution result"() {
@@ -895,15 +889,24 @@
                 conf 'org:bar:1.0'
                 conf 'org:baz:1.0'
             }
-            task check {
-                doLast {
-                    def modules = configurations.conf.incoming.resolutionResult.allComponents.findAll { it.id instanceof ModuleComponentIdentifier } as List
-                    assert modules.find { it.id.module == 'foo' }.selectionReason.toString() == 'because I am in control'
-                    assert modules.find { it.id.module == 'bar' }.selectionReason.toString() == 'why not?'
-                    assert modules.find { it.id.module == 'baz' }.selectionReason.toString() == 'selected by rule'
+        """
+        resolve.prepare("conf")
+
+        expect:
+        succeeds("checkDeps")
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:1.0", "org:foo:2.0") {
+                    selectedByRule("because I am in control")
+                }
+                edge("org:bar:1.0", "org.test:bar:2.0") {
+                    selectedByRule("why not?")
+                }
+                module("org:baz:1.0") {
+                    selectedByRule()
                 }
             }
-        """
+        }
 
         when:
         run "check"
@@ -917,10 +920,10 @@
         repositories {
             maven { url "${mavenRepo.uri}" }
         }
-        task resolveConf { doLast { configurations.conf.files } }
-
-        //resolving the configuration at the end:
-        gradle.startParameter.taskNames += 'resolveConf'
+        task resolveConf {
+            def files = configurations.conf
+            doLast { files.files }
+        }
         """
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesPreferProjectModulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesPreferProjectModulesIntegrationTest.groovy
index 0f7d926..98f8b64 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesPreferProjectModulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/DependencyResolveRulesPreferProjectModulesIntegrationTest.groovy
@@ -82,7 +82,9 @@
             root(":Subproject_with_preferProjectModules", "test:Subproject_with_preferProjectModules:") {
                 module("myorg:ModuleB:1.0") {
                     // Prefers project, regardless of version
-                    edge("myorg:ModuleC:2.0", ":ModuleC", "myorg:ModuleC:1.0")
+                    edge("myorg:ModuleC:2.0", ":ModuleC", "myorg:ModuleC:1.0") {
+                        byConflictResolution("between versions 1.0 and 2.0")
+                    }
                 }
                 project(":ModuleC", "myorg:ModuleC:1.0") {
                     noArtifacts()
@@ -99,7 +101,9 @@
                 project(":Subproject_with_preferProjectModules", "test:Subproject_with_preferProjectModules:") {
                     noArtifacts()
                     module("myorg:ModuleB:1.0") {
-                        module("myorg:ModuleC:2.0")
+                        module("myorg:ModuleC:2.0") {
+                            byConflictResolution("between versions 1.0 and 2.0")
+                        }
                     }
                     // 'Subproject_with_preferProjectModules' config DOES NOT influence this dependency
                     // and hence the higher version is picked from repo
@@ -156,7 +160,9 @@
         resolve.expectGraph {
             root(":ProjectA", "test:ProjectA:") {
                 module("myorg:ModuleB:1.0") {
-                    module("myorg:ModuleC:2.0")
+                    module("myorg:ModuleC:2.0") {
+                        byConflictResolution("between versions 1.0 and 2.0")
+                    }
                 }
                 // 'preferProjectModules()' is not inherited from 'baseConf'
                 // and hence the higher version is picked from repo
@@ -172,7 +178,9 @@
         resolve.expectGraph {
             root(":ProjectA", "test:ProjectA:") {
                 module("myorg:ModuleB:1.0") {
-                    edge("myorg:ModuleC:2.0", ":ModuleC", "myorg:ModuleC:1.0")
+                    edge("myorg:ModuleC:2.0", ":ModuleC", "myorg:ModuleC:1.0") {
+                        byConflictResolution("between versions 1.0 and 2.0")
+                    }
                 }
                 project("project :ModuleC", "myorg:ModuleC:1.0") {
                     noArtifacts()
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/IvySpecificComponentMetadataRulesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/IvySpecificComponentMetadataRulesIntegrationTest.groovy
index 92da50d..810298c 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/IvySpecificComponentMetadataRulesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/rules/IvySpecificComponentMetadataRulesIntegrationTest.groovy
@@ -19,7 +19,6 @@
 import org.gradle.api.internal.artifacts.ivyservice.NamespaceId
 import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
 import org.gradle.integtests.fixtures.RequiredFeature
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
 import org.gradle.test.fixtures.encoding.Identifier
@@ -35,21 +34,14 @@
     conf 'org.test:projectA:1.0'
 }
 
-// implement Sync manually to make sure that task is never up-to-date
-task resolve {
-    doLast {
-        delete 'libs'
-        copy {
-            from configurations.conf
-            into 'libs'
-        }
-    }
+task resolve(type: Sync) {
+    from configurations.conf
+    into 'libs'
 }
 """
         new ResolveTestFixture(buildFile).addDefaultVariantDerivationStrategy()
     }
 
-    @ToBeFixedForConfigurationCache
     def "can access Ivy metadata"() {
         given:
         repository {
@@ -139,7 +131,6 @@
         fails 'resolve'
 
         then:
-        failure.assertHasDescription("Execution failed for task ':resolve'.")
         failure.assertHasLineNumber(lines + 6)
         failure.assertHasCause("Could not resolve all files for configuration ':conf'.")
         failure.assertHasCause("Could not resolve org.test:projectA:1.0.")
@@ -258,7 +249,6 @@
         succeeds 'resolve'
     }
 
-    @ToBeFixedForConfigurationCache
     def "changed Ivy metadata becomes visible once module is refreshed"() {
         def baseScript = buildFile.text
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/EndorseStrictVersionsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/EndorseStrictVersionsIntegrationTest.groovy
index efa1a9a..b54d9bb 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/EndorseStrictVersionsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/EndorseStrictVersionsIntegrationTest.groovy
@@ -22,10 +22,6 @@
 @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true")
 class EndorseStrictVersionsIntegrationTest extends AbstractModuleDependencyResolveTest {
 
-    def setup() {
-        resolve.withStrictReasonsCheck()
-    }
-
     void "can downgrade version through platform"() {
         given:
         repository {
@@ -71,11 +67,15 @@
             root(':', ':test:') {
                 module('org:platform:1.0') {
                     constraint('org:bar:1.0').byConstraint()
-                    constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                    constraint('org:foo:{strictly 1.0}', 'org:foo:1.0')
                 }
                 edge('org:bar', 'org:bar:1.0') {
-                    edge('org:foo:2.0', 'org:foo:1.0').byAncestor()
-                }.byRequest()
+                    edge('org:foo:2.0', 'org:foo:1.0') {
+                        notRequested()
+                        byConstraint()
+                        byAncestor()
+                    }
+                }
             }
         }
     }
@@ -132,13 +132,19 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 edge('org:platform:1.0', 'org:platform:2.0') {
-                    constraint('org:bar:1.0').byConstraint()
-                    constraint('org:foo:1.0', 'org:foo:2.0').byConstraint().byConflictResolution("between versions 2.0 and 1.0")
-                }.byConflictResolution("between versions 2.0 and 1.0")
+                    byConflictResolution("between versions 2.0 and 1.0")
+                    constraint('org:bar:1.0')
+                    constraint('org:foo:1.0', 'org:foo:2.0') {
+                        notRequested()
+                        byConstraint()
+                        byConflictResolution("between versions 2.0 and 1.0")
+                    }
+                }
                 edge('org:bar', 'org:bar:1.0') {
+                    byConstraint()
                     module('org:foo:2.0')
-                    module('org:platform:2.0').byRequest()
-                }.byRequest()
+                    module('org:platform:2.0')
+                }
             }
         }
     }
@@ -244,11 +250,15 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 module('org:platform:1.0') {
-                    constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                    constraint('org:foo:{strictly 1.0}', 'org:foo:1.0')
                 }
                 module('org:baz:1.0') {
                     module('org:bar:1.0') {
-                        edge('org:foo:2.0', 'org:foo:1.0').byAncestor()
+                        edge('org:foo:2.0', 'org:foo:1.0') {
+                            notRequested()
+                            byConstraint()
+                            byAncestor()
+                        }
                     }
                 }
             }
@@ -306,11 +316,14 @@
                 module('org:baz:1.0') {
                     module('org:platform:1.0') {
                         constraint('org:bar:1.0').byConstraint()
-                        constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                        constraint('org:foo:{strictly 1.0}', 'org:foo:1.0')
                     }
                     edge('org:bar', 'org:bar:1.0') {
-                        byRequest()
-                        edge('org:foo:2.0', 'org:foo:1.0').byAncestor()
+                        edge('org:foo:2.0', 'org:foo:1.0') {
+                            notRequested()
+                            byConstraint()
+                            byAncestor()
+                        }
                     }
                 }
             }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionConstraintsFeatureInteractionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionConstraintsFeatureInteractionIntegrationTest.groovy
index ee0b7d0..5371078 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionConstraintsFeatureInteractionIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionConstraintsFeatureInteractionIntegrationTest.groovy
@@ -20,11 +20,6 @@
 import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
 
 class StrictVersionConstraintsFeatureInteractionIntegrationTest extends AbstractModuleDependencyResolveTest {
-
-    def setup() {
-        resolve.withStrictReasonsCheck()
-    }
-
     def "can turn constraint into strict constraint by using a component metadata rule"() {
         given:
         repository {
@@ -77,7 +72,7 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 module('org:bar:1.0') {
-                    edge('org:baz:{strictly 1.0}', 'org:baz:1.0').byRequest()
+                    edge('org:baz:{strictly 1.0}', 'org:baz:1.0')
                     edge('org:foo:{strictly 1.0}', 'org:foo:1.0') {
                         edge('org:baz:2.0', 'org:baz:1.0').byAncestor()
                     }
@@ -144,7 +139,7 @@
                 module('org:bar:1.0') {
                     edge('org:baz:1.0', 'org:baz:2.0').byConflictResolution('between versions 2.0 and 1.0')
                     module('org:foo:1.0') {
-                        module('org:baz:2.0').byRequest()
+                        module('org:baz:2.0')
                     }
                 }
             }
@@ -189,9 +184,13 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0')
                 module('org:bar:1.0') {
-                    edge('org:foo:2.0', 'org:foo:1.0').byAncestor()
+                    edge('org:foo:2.0', 'org:foo:1.0') {
+                        notRequested()
+                        byAncestor()
+                        byConstraint()
+                    }
                 }
             }
         }
@@ -279,7 +278,7 @@
                 constraint('org:bar:{strictly 1.0}', 'org:bar:2.0').byConstraint().forced()
                 project(':foo', 'test:foo:') {
                     configuration = 'conf'
-                    module('org:bar:2.0').byRequest()
+                    module('org:bar:2.0')
                 }
             }
         }
@@ -324,7 +323,7 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                edge('org:foo:{strictly 1.0}', 'org:foo:1.0').byRequest()
+                edge('org:foo:{strictly 1.0}', 'org:foo:1.0')
                 module('org:bar:1.0') {
                     edge('org:old:2.0', 'org:foo:1.0').selectedByRule("better foo than old").byAncestor()
                 }
@@ -389,9 +388,14 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0')
                 module('org:bar:1.0') {
-                    edge("org:foo:2.2", 'org:foo:1.0').selectedByRule("bad version").byAncestor()
+                    edge("org:foo:2.2", 'org:foo:1.0') {
+                        selectedByRule("bad version")
+                        byAncestor()
+                        byConstraint()
+                        notRequested()
+                    }
                 }
             }
         }
@@ -442,7 +446,7 @@
             root(':', ':test:') {
                 constraint('org:foo:{strictly 1.0}', 'org:foo:0.11').byConstraint()
                 module('org:bar:1.0') {
-                    edge('org:foo:2.0', 'org:foo:0.11').byRequest().selectedByRule('because I can')
+                    edge('org:foo:2.0', 'org:foo:0.11').selectedByRule('because I can')
                 }
             }
         }
@@ -493,7 +497,7 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                edge('org:foo:{strictly 1.0}', 'org:new:1.0').byRequest().selectedByRule()
+                edge('org:foo:{strictly 1.0}', 'org:new:1.0').selectedByRule()
                 module('org:bar:1.0') {
                     module('org:foo:2.0')
                 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionConstraintsIntegrationTest.groovy
index d037475..bc88787 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionConstraintsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionConstraintsIntegrationTest.groovy
@@ -20,11 +20,6 @@
 import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
 
 class StrictVersionConstraintsIntegrationTest extends AbstractModuleDependencyResolveTest {
-
-    def setup() {
-        resolve.withStrictReasonsCheck()
-    }
-
     def "can downgrade version"() {
         given:
         repository {
@@ -60,7 +55,7 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                edge('org:foo:{strictly 1.0}', 'org:foo:1.0').byRequest()
+                edge('org:foo:{strictly 1.0}', 'org:foo:1.0')
                 module('org:bar:1.0') {
                     edge('org:foo:2.0', 'org:foo:1.0').byAncestor()
                 }
@@ -105,9 +100,13 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0') {
+                    notRequested()
+                    byConstraint()
+                    byAncestor()
+                }
                 module('org:bar:1.0') {
-                    edge('org:foo:2.0', 'org:foo:1.0').byAncestor()
+                    edge('org:foo:2.0', 'org:foo:1.0')
                 }
             }
         }
@@ -161,13 +160,17 @@
             root(':', ':test:') {
                 module('org:a:1.0') {
                     module('org:b:1.0') {
-                        edge('org:c:3.0', 'org:c:1.0').byAncestor()
+                        edge('org:c:3.0', 'org:c:1.0') {
+                            notRequested()
+                            byAncestor()
+                            byConstraint()
+                        }
                     }
                     if (publishedConstraintsSupported) {
                         constraint('org:c:{strictly 2.0}', 'org:c:1.0')
                     }
                 }
-                constraint('org:c:{strictly 1.0}', 'org:c:1.0').byConstraint()
+                constraint('org:c:{strictly 1.0}', 'org:c:1.0')
             }
         }
     }
@@ -219,13 +222,17 @@
             root(':', ':test:') {
                 module('org:a:1.0') {
                     module('org:b:1.0') {
-                        edge('org:c:2.0', 'org:c:1.0').byAncestor()
+                        edge('org:c:2.0', 'org:c:1.0') {
+                            notRequested()
+                            byAncestor()
+                            byConstraint()
+                        }
                     }
                     if (publishedConstraintsSupported) {
                         constraint('org:c:{strictly 1.0}', 'org:c:1.0')
                     }
                 }
-                constraint('org:c:{strictly 1.0}', 'org:c:1.0').byConstraint()
+                constraint('org:c:{strictly 1.0}', 'org:c:1.0')
             }
         }
     }
@@ -343,13 +350,13 @@
             root(':', ':test:') {
                 edge('org:a:1.0', 'org:a:2.0') {
                     module('org:b:1.0') {
-                        module('org:c:2.0').byRequest()
+                        module('org:c:2.0')
                     }
                     edge('org:c:1.0', 'org:c:2.0').byConflictResolution("between versions 2.0 and 1.0")
                 }.byConflictResolution("between versions 2.0 and 1.0")
                 module('org:x:1.0') {
                     module('org:y:1.0') {
-                        module('org:a:2.0').byRequest()
+                        module('org:a:2.0')
                     }
                 }
             }
@@ -395,9 +402,13 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0') {
+                    notRequested()
+                    byConstraint()
+                    byAncestor()
+                }
                 module('org:bar:1.0') {
-                    edge("org:foo:$publishedFooDependencyVersion", 'org:foo:1.0').byAncestor()
+                    edge("org:foo:$publishedFooDependencyVersion", 'org:foo:1.0')
                 }
             }
         }
@@ -441,9 +452,13 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0') {
+                    notRequested()
+                    byConstraint()
+                    byAncestor()
+                }
                 module('org:bar:1.0') {
-                    edge("org:foo:[2.0,3.0)", 'org:foo:1.0').byAncestor()
+                    edge("org:foo:[2.0,3.0)", 'org:foo:1.0')
                 }
             }
         }
@@ -495,7 +510,7 @@
                 project(':foo', 'org:foo:1.0') {
                     configuration = 'default'
                     noArtifacts()
-                }.byRequest()
+                }
             }
         }
     }
@@ -631,14 +646,18 @@
             root(':', ':test:') {
                 module('org:x1:1.0') {
                     module('org:bar:1.0') {
-                        edge('org:foo:2.0', 'org:foo:1.0').byAncestor()
+                        edge('org:foo:2.0', 'org:foo:1.0') {
+                            notRequested()
+                            byConstraint()
+                            byAncestor()
+                        }
                     }
-                    constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                    constraint('org:foo:{strictly 1.0}', 'org:foo:1.0')
                 }
                 module('org:x2:1.0') {
                     module('org:bar:1.0')
                 }
-                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0').byConstraint()
+                constraint('org:foo:{strictly 1.0}', 'org:foo:1.0')
             }
         }
     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionsInPlatformCentricDevelopmentIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionsInPlatformCentricDevelopmentIntegrationTest.groovy
index 56d3a77..8bcde3f 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionsInPlatformCentricDevelopmentIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/strict/StrictVersionsInPlatformCentricDevelopmentIntegrationTest.groovy
@@ -39,10 +39,6 @@
         MODULE             // constraints in module are published with strict constraints, consumer uses normal dependencies with 'endorseStrictVersions()'
     }
 
-    def setup() {
-        resolve.withStrictReasonsCheck()
-    }
-
     String platformDependency(platformType, String dependency) {
         //noinspection GroovyFallthrough
         switch (platformType) {
@@ -178,7 +174,6 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 edge(platformType == ENFORCED_PLATFORM ? "org:platform:${expectStrictVersion(platformType, '1.+')}" : 'org:platform:1.+', 'org:platform:1.0') {
-                    byRequest()
                     if (platformType != MODULE) {
                         configuration(platformType == ENFORCED_PLATFORM ? 'enforcedApiElements' : 'apiElements')
                         noArtifacts()
@@ -191,15 +186,14 @@
                     constraint("org:foo:${expectStrictVersion(platformType, '3.0', '3.1 & 3.2')}", 'org:foo:3.0').byConstraint()
                 }
                 edge('org:bar', 'org:bar:2.0') {
-                    byRequest()
                     if (platformType == ENFORCED_PLATFORM) {
                         forced()
                     }
                     edge('org:foo:3.1', 'org:foo:3.0') {
                         if (platformType != ENFORCED_PLATFORM) {
+                            notRequested()
                             byAncestor()
                         } else {
-                            byRequest()
                             forced()
                         }
                     }
@@ -241,7 +235,6 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 edge(platformType == ENFORCED_PLATFORM ? "org:platform:${expectStrictVersion(platformType, '1.+')}" : 'org:platform:1.+', 'org:platform:1.1') {
-                    byRequest()
                     if (platformType != MODULE) {
                         configuration(platformType == ENFORCED_PLATFORM ? 'enforcedApiElements' : 'apiElements')
                         noArtifacts()
@@ -254,15 +247,14 @@
                     constraint("org:foo:${expectStrictVersion(platformType, '3.1.1', '3.1 & 3.2')}", 'org:foo:3.1.1').byConstraint()
                 }
                 edge('org:bar', 'org:bar:2.0') {
-                    byRequest()
                     if (platformType == ENFORCED_PLATFORM) {
                         forced()
                     }
                     edge('org:foo:3.1', 'org:foo:3.1.1') {
                         if (platformType != ENFORCED_PLATFORM) {
+                            notRequested()
                             byAncestor()
                         } else {
-                            byRequest()
                             forced()
                         }
                     }
@@ -388,7 +380,6 @@
                 root(':', ':test:') {
                     constraint('org:foo:{strictly 3.2}', "org:foo:$expectedFooVersion").byConstraint()
                     edge('org:platform:1.+', 'org:platform:1.1') {
-                        byRequest()
                         if (platformType != MODULE) {
                             configuration(platformType == ENFORCED_PLATFORM ? 'enforcedApiElements' : 'apiElements')
                             noArtifacts()
@@ -397,8 +388,13 @@
                         constraint("org:foo:${expectStrictVersion(platformType, '3.1.1', '3.1 & 3.2')}", "org:foo:$expectedFooVersion").byConstraint()
                     }
                     edge('org:bar', 'org:bar:2.0') {
-                        edge('org:foo:3.1', "org:foo:$expectedFooVersion").byAncestor()
-                    }.byRequest()
+                        edge('org:foo:3.1', "org:foo:$expectedFooVersion") {
+                            if (platformType != ENFORCED_PLATFORM) {
+                                notRequested()
+                            }
+                            byAncestor()
+                        }
+                    }
                 }
             }
         }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/suppliers/DynamicRevisionRemoteResolveWithMetadataSupplierIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/suppliers/DynamicRevisionRemoteResolveWithMetadataSupplierIntegrationTest.groovy
index 6c1692a..2243419 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/suppliers/DynamicRevisionRemoteResolveWithMetadataSupplierIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/suppliers/DynamicRevisionRemoteResolveWithMetadataSupplierIntegrationTest.groovy
@@ -1345,7 +1345,7 @@
             root(":", ":test:") {
                 edges.each { from, to ->
                     if (to instanceof List) {
-                        edge(from, to[0]).byReason(to[1])
+                        edge(from, to[0]).byReason(to[1]).maybeRequested()
                     } else {
                         edge(from, to)
                     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactFilterIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactFilterIntegrationTest.groovy
index 61fbbe8..32f5d3b 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactFilterIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactFilterIntegrationTest.groovy
@@ -32,7 +32,7 @@
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
-            
+
             project(':libInclude') {
                 configurations.create('default')
                 task jar {}
@@ -71,20 +71,27 @@
                 assert component instanceof ModuleComponentIdentifier
                 return component.group == 'org.include'
             }
-            
+
             task check {
+                def files1 = configurations.compile
+                def files2 = configurations.compile.incoming.artifactView({}).files
+                def files3 = configurations.compile.incoming.artifactView({componentFilter { true }}).files
+                def files4 = configurations.compile.incoming.artifactView({componentFilter { false }}).files
+                def filterView = configurations.compile.incoming.artifactView({componentFilter(artifactFilter)})
+                def files5 = filterView.files
+                def artifacts1 = filterView.artifacts
+                def files6 = filterView.artifacts.artifactFiles
                 doLast {
                     def all = ['included-1.3.jar', 'excluded-2.3.jar', 'libInclude.jar', 'libExclude.jar']
                     def filtered = ['included-1.3.jar', 'libInclude.jar']
-                    assert configurations.compile.collect { it.name } == all
-                    assert configurations.compile.incoming.artifactView({}).getFiles().collect { it.name } == all
-                    assert configurations.compile.incoming.artifactView({componentFilter { true }}).files.collect { it.name } == all
-                    assert configurations.compile.incoming.artifactView({componentFilter { false }}).files.collect { it.name } == []
+                    assert files1.collect { it.name } == all
+                    assert files2.files.collect { it.name } == all
+                    assert files3.collect { it.name } == all
+                    assert files4.collect { it.name } == []
 
-                    def filterView = configurations.compile.incoming.artifactView({componentFilter(artifactFilter)})
-                    assert filterView.files.collect { it.name } == filtered
-                    assert filterView.artifacts.collect { it.file.name } == filtered
-                    assert filterView.artifacts.artifactFiles.collect { it.name } == filtered
+                    assert files5.collect { it.name } == filtered
+                    assert artifacts1.collect { it.file.name } == filtered
+                    assert files6.collect { it.name } == filtered
                 }
             }
 """
@@ -106,7 +113,7 @@
             }
             def filteredView = configurations.compile.incoming.artifactView({componentFilter(artifactFilter)}).files
             def unfilteredView = configurations.compile.incoming.artifactView({componentFilter({ true })}).files
-            
+
             task checkFiltered {
                 inputs.files(filteredView)
                 doLast {
@@ -147,7 +154,7 @@
             }
             def artifactFilter = { component -> component instanceof ModuleComponentIdentifier}
             def filteredView = configurations.compile.incoming.artifactView{componentFilter(artifactFilter)}.files
-            
+
             task checkFiltered {
                 inputs.files(filteredView)
                 doLast {
@@ -175,7 +182,7 @@
             }
             def artifactFilter = { component -> component instanceof ProjectComponentIdentifier}
             def filteredView = configurations.compile.incoming.artifactView{componentFilter(artifactFilter)}.files
-            
+
             task checkFiltered {
                 inputs.files(filteredView)
                 doLast {
@@ -198,13 +205,13 @@
             dependencies {
                 compile files("internalLocalLibExclude.jar")
             }
-            
-            def artifactFilter = { component -> 
+
+            def artifactFilter = { component ->
                 println "filter applied to " + component
-                false 
+                false
             }
             def filteredView = configurations.compile.incoming.artifactView{componentFilter(artifactFilter)}.files
-            
+
             task checkFiltered {
                 inputs.files(filteredView)
                 doLast {
@@ -237,7 +244,7 @@
             dependencies {
                 compile project('libInclude')
                 compile project('libExclude')
-                
+
                 registerTransform(Jar2Class) {
                     from.attribute(Attribute.of('artifactType', String), "jar")
                     to.attribute(Attribute.of('artifactType', String), "class")
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformBuildOperationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformBuildOperationIntegrationTest.groovy
index f3c6a3c..2aad020 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformBuildOperationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformBuildOperationIntegrationTest.groovy
@@ -17,12 +17,15 @@
 package org.gradle.integtests.resolve.transform
 
 import groovy.transform.EqualsAndHashCode
-import org.gradle.api.internal.artifacts.transform.ExecuteScheduledTransformationStepBuildOperationType
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.BuildOperationsFixture
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.internal.operations.trace.BuildOperationRecord
 import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType
 import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.PlannedNode
 import org.gradle.internal.taskgraph.NodeIdentity
+import org.gradle.operations.dependencies.transforms.ExecutePlannedTransformStepBuildOperationType
+import org.gradle.test.fixtures.file.TestFile
 
 import java.util.function.Predicate
 
@@ -44,25 +47,68 @@
         def matchNode(plannedNode) {
             plannedNode.nodeIdentity.nodeType.toString() == nodeType && identityPredicate.test(plannedNode.nodeIdentity)
         }
+
+        @Override
+        String toString() {
+            "NodeMatcher(nodeId=$nodeId, nodeType=$nodeType)"
+        }
+    }
+
+    static class PlannedTransformStepIdentityWithoutId {
+        String consumerBuildPath
+        String consumerProjectPath
+        Map<String, String> componentId
+        Map<String, String> sourceAttributes
+        Map<String, String> targetAttributes
+        List<Map<String, String>> capabilities
+        String artifactName
+        Map<String, String> dependenciesConfigurationIdentity
     }
 
     static final Set<String> KNOWN_NODE_TYPES = NodeIdentity.NodeType.values()*.name() as Set<String>
     static final String TASK = NodeIdentity.NodeType.TASK.name()
-    static final String ARTIFACT_TRANSFORM = NodeIdentity.NodeType.ARTIFACT_TRANSFORM.name()
+    static final String TRANSFORM_STEP = NodeIdentity.NodeType.TRANSFORM_STEP.name()
 
     def buildOperations = new BuildOperationsFixture(executer, testDirectoryProvider)
 
     def setup() {
         requireOwnGradleUserHomeDir()
 
+        // group name is included in the capabilities of components, which are part of the transform identity
         buildFile << """
             allprojects {
                 group = "colored"
             }
         """
+
+        printTaskOnlyExecutionPlan()
     }
 
-    def setupExternalDependency() {
+    def printTaskOnlyExecutionPlan(TestFile buildFile = getBuildFile()) {
+        // Log a task-only execution plan, which can only be computed during the runtime of the build
+        buildFile << """
+            import org.gradle.api.services.BuildService
+            import org.gradle.api.services.BuildServiceParameters
+            import org.gradle.internal.operations.*
+            import org.gradle.internal.taskgraph.*
+
+            abstract class LoggingListener implements BuildOperationListener, BuildService<BuildServiceParameters.None> {
+                void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) { throw new RuntimeException() }
+                void progress(OperationIdentifier operationIdentifier, OperationProgressEvent progressEvent) { throw new RuntimeException() }
+                void finished(BuildOperationDescriptor buildOperation, OperationFinishEvent finishEvent) {
+                    if (finishEvent.result instanceof CalculateTaskGraphBuildOperationType.Result) {
+                        def plannedTasks = finishEvent.result.getExecutionPlan([NodeIdentity.NodeType.TASK] as Set)
+                        println("Task-only execution plan: " + plannedTasks.collect { "PlannedTask('\${it.nodeIdentity}', deps=\${it.nodeDependencies})" })
+                    }
+                }
+            }
+
+            def listener = gradle.sharedServices.registerIfAbsent("listener", LoggingListener) { }
+            services.get(BuildEventsListenerRegistry).onOperationCompletion(listener)
+        """
+    }
+
+    def setupExternalDependency(TestFile buildFile = getBuildFile()) {
         def m1 = mavenRepo.module("test", "test", "4.2").publish()
         m1.artifactFile.text = "test-test"
 
@@ -88,15 +134,6 @@
         setupExternalDependency()
 
         buildFile << """
-            allprojects {
-                dependencies {
-                    registerTransform(MakeGreen) {
-                        from.attribute(color, 'blue')
-                        to.attribute(color, 'green')
-                    }
-                }
-            }
-
             project(":consumer") {
                 dependencies {
                     implementation project(":producer")
@@ -110,51 +147,44 @@
         then:
         executedAndNotSkipped(":consumer:resolve")
 
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer])]")
+
         result.groupedOutput.transform("MakeGreen")
             .assertOutputContains("processing [producer.jar]")
 
         result.groupedOutput.task(":consumer:resolve")
             .assertOutputContains("result = [producer.jar.green, test-4.2.jar]")
 
-        def plannedNodes = buildOperations.only(CalculateTaskGraphBuildOperationType).result.executionPlan as List<PlannedNode>
-        plannedNodes.every { KNOWN_NODE_TYPES.contains(it.nodeIdentity.nodeType) }
-        plannedNodes.count { it.nodeIdentity.nodeType.toString() == ARTIFACT_TRANSFORM } == 1
+        List<PlannedNode> plannedNodes = getPlannedNodes(1)
+
+        def expectedTransformId = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
 
         checkExecutionPlanMatchingDependencies(
             plannedNodes,
             [
-                taskMatcher(
-                    "node1", ":producer:producer",
-                    []
-                ),
-                transformMatcher(
-                    "node2", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "green"],
-                    ["node1"]
-                ),
-                taskMatcher(
-                    "node3", ":consumer:resolve",
-                    ["node2"]
-                ),
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId, ["node1"]),
+                taskMatcher("node3", ":consumer:resolve", ["node2"]),
             ]
         )
 
-        with(buildOperations.only(ExecuteScheduledTransformationStepBuildOperationType).details) {
+        List<BuildOperationRecord> executeTransformationOps = getExecuteTransformOperations(1)
+
+        with(executeTransformationOps[0].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId)
+            transformActionClass == "MakeGreen"
+
             transformerName == "MakeGreen"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "green", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == null
-            }
-            transformType == "MakeGreen"
-            sourceAttributes == [color: "blue", artifactType: "jar"]
-            fromAttributes == [color: "blue"]
-            toAttributes == [color: "green"]
         }
     }
 
@@ -180,6 +210,8 @@
         then:
         executedAndNotSkipped(":consumer:resolve")
 
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer])]")
+
         result.groupedOutput.transform("MakeColor")
             .assertOutputContains("processing [producer.jar]")
             .assertOutputContains("processing [producer.jar.red]")
@@ -187,71 +219,55 @@
         result.groupedOutput.task(":consumer:resolve")
             .assertOutputContains("result = [producer.jar.red.green, test-4.2.jar]")
 
-        def plannedNodes = buildOperations.only(CalculateTaskGraphBuildOperationType).result.executionPlan as List<PlannedNode>
-        plannedNodes.every { KNOWN_NODE_TYPES.contains(it.nodeIdentity.nodeType) }
-        plannedNodes.count { it.nodeIdentity.nodeType.toString() == ARTIFACT_TRANSFORM } == 2
+        def plannedNodes = getPlannedNodes(2)
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "red", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        def expectedTransformId2 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "red", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
 
         checkExecutionPlanMatchingDependencies(
             plannedNodes,
             [
-                taskMatcher(
-                    "node1", ":producer:producer",
-                    []
-                ),
-                transformMatcher(
-                    "node2", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "red"],
-                    ["node1"]
-                ),
-                transformMatcher(
-                    "node3", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "green"],
-                    ["node2"]
-                ),
-                taskMatcher(
-                    "node4", ":consumer:resolve",
-                    ["node3"]
-                ),
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                transformStepMatcher("node3", expectedTransformId2, ["node2"]),
+                taskMatcher("node4", ":consumer:resolve", ["node3"]),
             ]
         )
 
-        def executeTransformationOps = buildOperations.all(ExecuteScheduledTransformationStepBuildOperationType)
-        def executeTransformationOp1 = executeTransformationOps[0]
-        with(executeTransformationOp1.details) {
+        def executeTransformationOps = getExecuteTransformOperations(2)
+        with(executeTransformationOps[0].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId1)
+            transformActionClass == "MakeColor"
+
             transformerName == "MakeColor"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "red", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == null
-            }
-            transformType == "MakeColor"
-            sourceAttributes == [color: "blue", artifactType: "jar"]
-            fromAttributes == [color: "blue"]
-            toAttributes == [color: "red"]
         }
 
-        def executeTransformationOp2 = executeTransformationOps[1]
-        with(executeTransformationOp2.details) {
+        with(executeTransformationOps[1].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId2)
+            transformActionClass == "MakeColor"
+
             transformerName == "MakeColor"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "green", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == null
-            }
-            transformType == "MakeColor"
-            sourceAttributes == [color: "red", artifactType: "jar"]
-            fromAttributes == [color: "red"]
-            toAttributes == [color: "green"]
         }
     }
 
@@ -278,77 +294,64 @@
         then:
         executedAndNotSkipped(":consumer:resolve")
 
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer])]")
+
         result.groupedOutput.transform("MakeGreen")
             .assertOutputContains("processing producer.jar using [producer.jar.red, test-4.2.jar]")
 
         result.groupedOutput.task(":consumer:resolve")
             .assertOutputContains("result = [producer.jar.green, test-4.2.jar]")
 
-        def plannedNodes = buildOperations.only(CalculateTaskGraphBuildOperationType).result.executionPlan as List<PlannedNode>
-        plannedNodes.every { KNOWN_NODE_TYPES.contains(it.nodeIdentity.nodeType) }
-        plannedNodes.count { it.nodeIdentity.nodeType.toString() == ARTIFACT_TRANSFORM } == 2
+        def plannedNodes = getPlannedNodes(2)
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "red", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        def expectedTransformId2 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
 
         checkExecutionPlanMatchingDependencies(
             plannedNodes,
             [
-                taskMatcher(
-                    "node1", ":producer:producer",
-                    []
-                ),
-                transformMatcher(
-                    "node2", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "red"],
-                    ["node1"]
-                ),
-                transformMatcher(
-                    "node3", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "green"],
-                    ["node1", "node2"]
-                ),
-                taskMatcher(
-                    "node4", ":consumer:resolve",
-                    ["node3"]
-                ),
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                transformStepMatcher("node3", expectedTransformId2, ["node1", "node2"]),
+                taskMatcher("node4", ":consumer:resolve", ["node3"]),
             ]
         )
 
-        def executeTransformationOps = buildOperations.all(ExecuteScheduledTransformationStepBuildOperationType)
-        def executeTransformationOp1 = executeTransformationOps[0]
-        with(executeTransformationOp1.details) {
+        def executeTransformationOps = getExecuteTransformOperations(2)
+
+        with(executeTransformationOps[0].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId1)
+            transformActionClass == "MakeRed"
+
             transformerName == "MakeRed"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "red", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == null
-            }
-            transformType == "MakeRed"
-            sourceAttributes == [color: "blue", artifactType: "jar"]
-            fromAttributes == [color: "blue"]
-            toAttributes == [color: "red"]
         }
 
-        def executeTransformationOp2 = executeTransformationOps[1]
-        with(executeTransformationOp2.details) {
+        with(executeTransformationOps[1].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId2)
+            transformActionClass == "MakeGreen"
+
             transformerName == "MakeGreen"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "green", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == null
-            }
-            transformType == "MakeGreen"
-            sourceAttributes == [color: "blue", artifactType: "jar"]
-            fromAttributes == [color: "blue"]
-            toAttributes == [color: "green"]
         }
     }
 
@@ -383,51 +386,42 @@
         then:
         executedAndNotSkipped(":consumer:resolve")
 
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer])]")
+
         result.groupedOutput.transform("MakeGreen")
             .assertOutputContains("processing producer.jar using [test-4.2.jar]")
 
         result.groupedOutput.task(":consumer:resolve")
             .assertOutputContains("result = [producer.jar.green, test-4.2.jar]")
 
-        def plannedNodes = buildOperations.only(CalculateTaskGraphBuildOperationType).result.executionPlan as List<PlannedNode>
-        plannedNodes.every { KNOWN_NODE_TYPES.contains(it.nodeIdentity.nodeType) }
-        plannedNodes.count { it.nodeIdentity.nodeType.toString() == ARTIFACT_TRANSFORM } == 1
+        def plannedNodes = getPlannedNodes(1)
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: [buildPath: ":", projectPath: ":consumer", name: "resolver"],
+        ])
 
         checkExecutionPlanMatchingDependencies(
             plannedNodes,
             [
-                taskMatcher(
-                    "node1", ":producer:producer",
-                    []
-                ),
-                transformMatcher(
-                    "node2", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "green"],
-                    ["node1"]
-                ),
-                taskMatcher(
-                    "node3", ":consumer:resolve",
-                    ["node2"]
-                ),
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                taskMatcher("node3", ":consumer:resolve", ["node2"]),
             ]
         )
 
-        with(buildOperations.only(ExecuteScheduledTransformationStepBuildOperationType).details) {
+        with(buildOperations.only(ExecutePlannedTransformStepBuildOperationType).details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId1)
+            transformActionClass == "MakeGreen"
+
             transformerName == "MakeGreen"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "green", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == [buildPath: ":", projectPath: ":consumer", name: "resolver"]
-            }
-            transformType == "MakeGreen"
-            sourceAttributes == [color: "blue", artifactType: "jar"]
-            fromAttributes == [color: "blue"]
-            toAttributes == [color: "green"]
         }
     }
 
@@ -453,6 +447,8 @@
         then:
         executedAndNotSkipped(":consumer:resolve")
 
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer])]")
+
         result.groupedOutput.transform("MakeRed")
             .assertOutputContains("processing [producer.jar]")
 
@@ -462,71 +458,55 @@
         result.groupedOutput.task(":consumer:resolve")
             .assertOutputContains("result = [producer.jar.red.green, test-4.2.jar]")
 
-        def plannedNodes = buildOperations.only(CalculateTaskGraphBuildOperationType).result.executionPlan as List<PlannedNode>
-        plannedNodes.every { KNOWN_NODE_TYPES.contains(it.nodeIdentity.nodeType) }
-        plannedNodes.count { it.nodeIdentity.nodeType.toString() == ARTIFACT_TRANSFORM } == 2
+        def plannedNodes = getPlannedNodes(2)
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "red", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        def expectedTransformId2 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "red", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: [buildPath: ":", projectPath: ":consumer", name: "resolver"],
+        ])
 
         checkExecutionPlanMatchingDependencies(
             plannedNodes,
             [
-                taskMatcher(
-                    "node1", ":producer:producer",
-                    []
-                ),
-                transformMatcher(
-                    "node2", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "red"],
-                    ["node1"]
-                ),
-                transformMatcher(
-                    "node3", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "green"],
-                    ["node2"]
-                ),
-                taskMatcher(
-                    "node4", ":consumer:resolve",
-                    ["node3"]
-                ),
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                transformStepMatcher("node3", expectedTransformId2, ["node2"]),
+                taskMatcher("node4", ":consumer:resolve", ["node3"]),
             ]
         )
 
-        def executeTransformationOps = buildOperations.all(ExecuteScheduledTransformationStepBuildOperationType)
-        def executeTransformationOp1 = executeTransformationOps[0]
-        with(executeTransformationOp1.details) {
+        def executeTransformationOps = getExecuteTransformOperations(2)
+        with(executeTransformationOps[0].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId1)
+            transformActionClass == "MakeRed"
+
             transformerName == "MakeRed"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "red", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == null
-            }
-            transformType == "MakeRed"
-            sourceAttributes == [color: "blue", artifactType: "jar"]
-            fromAttributes == [color: "blue"]
-            toAttributes == [color: "red"]
         }
 
-        def executeTransformationOp2 = executeTransformationOps[1]
-        with(executeTransformationOp2.details) {
+        with(executeTransformationOps[1].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId2)
+            transformActionClass == "MakeGreen"
+
             transformerName == "MakeGreen"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "green", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == [buildPath: ":", projectPath: ":consumer", name: "resolver"]
-            }
-            transformType == "MakeGreen"
-            sourceAttributes == [color: "red", artifactType: "jar"]
-            fromAttributes == [color: "red"]
-            toAttributes == [color: "green"]
         }
     }
 
@@ -592,6 +572,8 @@
         then:
         executedAndNotSkipped(":consumer:resolve")
 
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer])]")
+
         result.groupedOutput.transform("MakeColor")
             .assertOutputContains("processing [producer.jar]")
             .assertOutputContains("processing [producer.jar.red-1]")
@@ -600,71 +582,530 @@
         result.groupedOutput.task(":consumer:resolve")
             .assertOutputContains("result = [producer.jar.red-1.green-1, producer.jar.red-2.green-1, test-4.2.jar]")
 
-        def plannedNodes = buildOperations.only(CalculateTaskGraphBuildOperationType).result.executionPlan as List<PlannedNode>
-        plannedNodes.every { KNOWN_NODE_TYPES.contains(it.nodeIdentity.nodeType) }
-        plannedNodes.count { it.nodeIdentity.nodeType.toString() == ARTIFACT_TRANSFORM } == 2
+        def plannedNodes = getPlannedNodes(2)
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "red", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        def expectedTransformId2 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "red", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
 
         checkExecutionPlanMatchingDependencies(
             plannedNodes,
             [
-                taskMatcher(
-                    "node1", ":producer:producer",
-                    []
-                ),
-                transformMatcher(
-                    "node2", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "red"],
-                    ["node1"]
-                ),
-                transformMatcher(
-                    "node3", ":consumer", [buildPath: ":", projectPath: ":producer"], [artifactType: "jar", color: "green"],
-                    ["node2"]
-                ),
-                taskMatcher(
-                    "node4", ":consumer:resolve",
-                    ["node3"]
-                ),
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                transformStepMatcher("node3", expectedTransformId2, ["node2"]),
+                taskMatcher("node4", ":consumer:resolve", ["node3"]),
             ]
         )
 
-        def executeTransformationOps = buildOperations.all(ExecuteScheduledTransformationStepBuildOperationType)
-        def executeTransformationOp1 = executeTransformationOps[0]
-        with(executeTransformationOp1.details) {
+        def executeTransformationOps = getExecuteTransformOperations(2)
+        with(executeTransformationOps[0].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId1)
+            transformActionClass == "MakeColor"
+
             transformerName == "MakeColor"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "red", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == null
-            }
-            transformType == "MakeColor"
-            sourceAttributes == [color: "blue", artifactType: "jar"]
-            fromAttributes == [color: "blue"]
-            toAttributes == [color: "red"]
         }
 
-        def executeTransformationOp2 = executeTransformationOps[1]
-        with(executeTransformationOp2.details) {
+        with(executeTransformationOps[1].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId2)
+            transformActionClass == "MakeColor"
+
             transformerName == "MakeColor"
             subjectName == "producer.jar (project :producer)"
-            with(transformationIdentity) {
-                nodeType == "ARTIFACT_TRANSFORM"
-                buildPath == ":"
-                projectPath == ":consumer"
-                componentId == [buildPath: ":", projectPath: ":producer"]
-                targetAttributes == [color: "green", artifactType: "jar"]
-                capabilities == [[group: "colored", name: "producer", version: "unspecified"]]
-                artifactName == "producer.jar"
-                dependenciesConfigurationIdentity == null
+        }
+    }
+
+    def "single transform consuming multiple artifacts from task"() {
+        settingsFile << """
+            include 'producer', 'consumer'
+        """
+
+        taskTypeWithMultipleOutputFileProperties()
+        setupBuildWithColorVariants()
+
+        buildFile << """
+            allprojects {
+                task producer(type: OutputFilesTask) {
+                    out1.convention(layout.buildDirectory.file("\${project.name}.out1.jar"))
+                    out2.convention(layout.buildDirectory.file("\${project.name}.out2.jar"))
+                }
+
+                artifacts {
+                    implementation producer.out1
+                    implementation producer.out2
+                }
+
+                task resolve(type: ShowFileCollection) {
+                    def view = configurations.resolver.incoming.artifactView {
+                        attributes.attribute(color, 'green')
+                    }.files
+                    files.from(view)
+                }
             }
-            transformType == "MakeColor"
-            sourceAttributes == [color: "red", artifactType: "jar"]
-            fromAttributes == [color: "red"]
-            toAttributes == [color: "green"]
+        """
+
+        buildFile << """
+            allprojects {
+                dependencies {
+                    registerTransform(MakeGreen) {
+                        from.attribute(color, 'blue')
+                        to.attribute(color, 'green')
+                    }
+                }
+            }
+
+            abstract class MakeGreen implements TransformAction<org.gradle.api.artifacts.transform.TransformParameters.None> {
+                @InputArtifact
+                abstract Provider<FileSystemLocation> getInputArtifact()
+
+                void transform(TransformOutputs outputs) {
+                    def input = inputArtifact.get().asFile
+                    println "processing [\${input.name}]"
+                    def output = outputs.file(input.name + ".green")
+                    output.text = input.text + ".green"
+                }
+            }
+        """
+
+        buildFile << """
+            project(":consumer") {
+                dependencies {
+                    implementation project(":producer")
+                }
+            }
+        """
+
+        setupExternalDependency()
+
+        when:
+        run ":consumer:resolve"
+
+        then:
+        executedAndNotSkipped(":consumer:resolve")
+
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer])]")
+
+        result.groupedOutput.transform("MakeGreen", "producer.out1.jar (project :producer)")
+            .assertOutputContains("processing [producer.out1.jar]")
+        result.groupedOutput.transform("MakeGreen", "producer.out2.jar (project :producer)")
+            .assertOutputContains("processing [producer.out2.jar]")
+
+        result.groupedOutput.task(":consumer:resolve")
+            .assertOutputContains("result = [producer.out1.jar.green, producer.out2.jar.green, test-4.2.jar]")
+
+        def plannedNodes = getPlannedNodes(2)
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.out1.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        def expectedTransformId2 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.out2.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        checkExecutionPlanMatchingDependencies(
+            plannedNodes,
+            [
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                transformStepMatcher("node3", expectedTransformId2, ["node1"]),
+                taskMatcher("node4", ":consumer:resolve", ["node2", "node3"]),
+            ]
+        )
+
+        def executeTransformationOps = getExecuteTransformOperations(2)
+        // Order of scheduling/execution is not guaranteed between the transforms
+        checkExecuteTransformOperation(executeTransformationOps, expectedTransformId1, [
+            transformActionClass: "MakeGreen",
+            transformerName: "MakeGreen",
+            subjectName: "producer.out1.jar (project :producer)",
+        ])
+        checkExecuteTransformOperation(executeTransformationOps, expectedTransformId2, [
+            transformActionClass: "MakeGreen",
+            transformerName: "MakeGreen",
+            subjectName: "producer.out2.jar (project :producer)",
+        ])
+    }
+
+    def "single transform used by multiple consumers creates a node per consumer"() {
+        settingsFile << """
+            include 'producer', 'consumer1', 'consumer2'
+        """
+
+        setupBuildWithColorTransformImplementation()
+        setupExternalDependency()
+
+        buildFile << """
+            project(":consumer1") {
+                dependencies {
+                    implementation project(":producer")
+                }
+            }
+            project(":consumer2") {
+                dependencies {
+                    implementation project(":producer")
+                }
+            }
+        """
+
+        when:
+        run ":consumer1:resolve", ":consumer2:resolve"
+
+        then:
+        executedAndNotSkipped(":consumer1:resolve", ":consumer2:resolve")
+
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer1:resolve', deps=[Task :producer:producer]), PlannedTask('Task :consumer2:resolve', deps=[Task :producer:producer])]")
+
+        result.groupedOutput.transform("MakeGreen")
+            .assertOutputContains("processing [producer.jar]")
+        result.groupedOutput.task(":consumer1:resolve")
+            .assertOutputContains("result = [producer.jar.green, test-4.2.jar]")
+        result.groupedOutput.task(":consumer2:resolve")
+            .assertOutputContains("result = [producer.jar.green, test-4.2.jar]")
+
+        List<PlannedNode> plannedNodes = getPlannedNodes(2)
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer1",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        def expectedTransformId2 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer2",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        checkExecutionPlanMatchingDependencies(
+            plannedNodes,
+            [
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                transformStepMatcher("node3", expectedTransformId2, ["node1"]),
+                taskMatcher("node4", ":consumer1:resolve", ["node2"]),
+                taskMatcher("node5", ":consumer2:resolve", ["node3"]),
+            ]
+        )
+
+        List<BuildOperationRecord> executeTransformationOps = getExecuteTransformOperations(2)
+        // Order of scheduling/execution is not guaranteed between the consumer projects
+        checkExecuteTransformOperation(executeTransformationOps, expectedTransformId1, [
+            transformActionClass: "MakeGreen",
+            transformerName: "MakeGreen",
+            subjectName: "producer.jar (project :producer)",
+        ])
+        checkExecuteTransformOperation(executeTransformationOps, expectedTransformId2, [
+            transformActionClass: "MakeGreen",
+            transformerName: "MakeGreen",
+            subjectName: "producer.jar (project :producer)",
+        ])
+    }
+
+    def "failing transform"() {
+        settingsFile << """
+            include 'producer', 'consumer'
+        """
+
+        setupBuildWithColorAttributes()
+        setupExternalDependency()
+
+        buildFile << """
+            allprojects {
+                dependencies {
+                    registerTransform(MakeGreen) {
+                        from.attribute(color, 'blue')
+                        to.attribute(color, 'green')
+                    }
+                }
+            }
+
+            project(":consumer") {
+                dependencies {
+                    implementation project(":producer")
+                }
+            }
+
+            abstract class MakeGreen implements TransformAction<org.gradle.api.artifacts.transform.TransformParameters.None> {
+                @InputArtifact
+                abstract Provider<FileSystemLocation> getInputArtifact()
+
+                void transform(TransformOutputs outputs) {
+                    def input = inputArtifact.get().asFile
+                    println "processing [\${input.name}]"
+                    throw new IllegalStateException("failed making green: \${input.name}")
+                }
+            }
+        """
+
+        when:
+        runAndFail ":consumer:resolve"
+
+        then:
+        failureCauseContains("failed making green: producer.jar")
+
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer])]")
+
+        result.groupedOutput.transform("MakeGreen", "producer.jar (project :producer)")
+            .assertOutputContains("processing [producer.jar]")
+
+
+        def plannedNodes = getPlannedNodes(1)
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        checkExecutionPlanMatchingDependencies(
+            plannedNodes,
+            [
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                taskMatcher("node3", ":consumer:resolve", ["node2"]),
+            ]
+        )
+
+        def executeTransformationOp = getExecuteTransformOperations(1, true).first()
+        executeTransformationOp.failure.startsWith("org.gradle.api.internal.artifacts.transform.TransformException: Execution failed for MakeGreen:")
+
+        with(executeTransformationOp.details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId1)
+            transformActionClass == "MakeGreen"
+
+            transformerName == "MakeGreen"
+            subjectName == "producer.jar (project :producer)"
+        }
+    }
+
+    def "planned transform for external dependency substituted by included build"() {
+        file("included/settings.gradle") << """
+            include 'nested-producer'
+        """
+        setupBuildWithColorAttributes(file("included/build.gradle"))
+
+        settingsFile << """
+            includeBuild("included") {
+                dependencySubstitution {
+                    substitute(module("test:test")).using(project(":nested-producer"))
+                }
+            }
+        """
+
+        settingsFile << """
+            include 'producer', 'consumer'
+        """
+
+        setupBuildWithColorTransformImplementation()
+        setupExternalDependency()
+
+        buildFile << """
+            project(":consumer") {
+                dependencies {
+                    implementation project(":producer")
+                }
+            }
+        """
+
+        when:
+        run ":consumer:resolve"
+
+        then:
+        executedAndNotSkipped(":consumer:resolve")
+
+        outputContains("Task-only execution plan: [PlannedTask('Task :producer:producer', deps=[]), PlannedTask('Task :consumer:resolve', deps=[Task :producer:producer, Task :included:nested-producer:producer])]")
+        outputContains("Task-only execution plan: [PlannedTask('Task :included:nested-producer:producer', deps=[])]")
+
+        result.groupedOutput.transform("MakeGreen", "producer.jar (project :producer)")
+            .assertOutputContains("processing [producer.jar]")
+
+        result.groupedOutput.transform("MakeGreen", "nested-producer.jar (project :included:nested-producer)")
+            .assertOutputContains("processing [nested-producer.jar]")
+
+        result.groupedOutput.task(":consumer:resolve")
+            .assertOutputContains("result = [producer.jar.green, nested-producer.jar.green]")
+
+        // Included build runs a single task and no transforms
+        def includedPlannedNodes = getPlannedNodes(0, ":included")
+        includedPlannedNodes.size() == 1
+
+        def plannedNodes = getPlannedNodes(2, ":")
+
+        def expectedTransformId1 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        def expectedTransformId2 = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":included", projectPath: ":nested-producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "included", name: "nested-producer", version: "unspecified"]],
+            artifactName: "nested-producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        checkExecutionPlanMatchingDependencies(
+            [*includedPlannedNodes, *plannedNodes],
+            [
+                taskMatcher("node1", ":producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId1, ["node1"]),
+                taskMatcher("node3", ":included:nested-producer:producer", []),
+                transformStepMatcher("node4", expectedTransformId2, ["node3"]),
+                taskMatcher("node5", ":consumer:resolve", ["node2", "node4"]),
+            ]
+        )
+
+        List<BuildOperationRecord> executeTransformationOps = getExecuteTransformOperations(2)
+
+        // Order of scheduling/execution is not guaranteed between the transforms
+        checkExecuteTransformOperation(executeTransformationOps, expectedTransformId1, [
+            transformActionClass: "MakeGreen",
+            transformerName: "MakeGreen",
+            subjectName: "producer.jar (project :producer)",
+        ])
+        checkExecuteTransformOperation(executeTransformationOps, expectedTransformId2, [
+            transformActionClass: "MakeGreen",
+            transformerName: "MakeGreen",
+            subjectName: "nested-producer.jar (project :included:nested-producer)",
+        ])
+    }
+
+    def "included build transform operations are captured"() {
+        file("included/settings.gradle") << """
+            include 'producer', 'consumer'
+        """
+        def includedBuildFile = file("included/build.gradle")
+        includedBuildFile << """
+            allprojects {
+                group = "colored"
+            }
+        """
+        setupBuildWithColorTransformImplementation(includedBuildFile)
+        setupExternalDependency(includedBuildFile)
+        includedBuildFile << """
+            project(":consumer") {
+                dependencies {
+                    implementation project(":producer")
+                }
+            }
+        """
+
+        settingsFile << """
+            includeBuild("included")
+        """
+
+        buildFile << """
+            task rootConsumer() {
+                dependsOn(gradle.includedBuild("included").task(":consumer:resolve"))
+            }
+        """
+
+        when:
+        run ":rootConsumer"
+
+        then:
+        executedAndNotSkipped(":rootConsumer")
+
+        outputContains("Task-only execution plan: [PlannedTask('Task :rootConsumer', deps=[Task :included:consumer:resolve])]")
+        outputContains("Task-only execution plan: [PlannedTask('Task :included:producer:producer', deps=[]), PlannedTask('Task :included:consumer:resolve', deps=[Task :included:producer:producer])]")
+
+        result.groupedOutput.transform("MakeGreen", "producer.jar (project :included:producer)")
+            .assertOutputContains("processing [producer.jar]")
+
+        result.groupedOutput.task(":included:consumer:resolve")
+            .assertOutputContains("result = [producer.jar.green, test-4.2.jar]")
+
+        // Root build runs a single task and no transforms
+        getPlannedNodes(0, ":").size() == 1
+
+        def plannedNodes = getPlannedNodes(1, ":included")
+
+        def expectedTransformId = new PlannedTransformStepIdentityWithoutId([
+            consumerBuildPath: ":included",
+            consumerProjectPath: ":consumer",
+            componentId: [buildPath: ":included", projectPath: ":producer"],
+            sourceAttributes: [color: "blue", artifactType: "jar"],
+            targetAttributes: [color: "green", artifactType: "jar"],
+            capabilities: [[group: "colored", name: "producer", version: "unspecified"]],
+            artifactName: "producer.jar",
+            dependenciesConfigurationIdentity: null,
+        ])
+
+        checkExecutionPlanMatchingDependencies(
+            plannedNodes,
+            [
+                taskMatcher("node1", ":included:producer:producer", []),
+                transformStepMatcher("node2", expectedTransformId, ["node1"]),
+                taskMatcher("node3", ":included:consumer:resolve", ["node2"]),
+            ]
+        )
+
+        List<BuildOperationRecord> executeTransformationOps = getExecuteTransformOperations(1)
+
+        with(executeTransformationOps[0].details) {
+            verifyTransformationIdentity(plannedTransformStepIdentity, expectedTransformId)
+            transformActionClass == "MakeGreen"
+
+            transformerName == "MakeGreen"
+            subjectName == "producer.jar (project :included:producer)"
         }
     }
 
@@ -672,14 +1113,19 @@
         Map<TypedNodeId, List<String>> expectedDependencyNodeIdsByTypedNodeId = [:]
         Map<TypedNodeId, String> nodeIdByTypedNodeId = [:]
 
+        def usedMatchers = new HashSet<NodeMatcher>()
         for (def plannedNode : plannedNodes) {
             def matchers = nodeMatchers.findAll { it.matchNode(plannedNode) }
             assert matchers.size() == 1, "Expected exactly one matcher for node ${plannedNode.nodeIdentity}, but found ${matchers.size()}"
             def nodeMatcher = matchers[0]
             def nodeId = getTypedNodeId(plannedNode.nodeIdentity)
+            assert !usedMatchers.contains(nodeMatcher)
+            usedMatchers.add(nodeMatcher)
             nodeIdByTypedNodeId[nodeId] = nodeMatcher.nodeId
             expectedDependencyNodeIdsByTypedNodeId[nodeId] = nodeMatcher.dependencyNodeIds
         }
+        def unusedMatchers = nodeMatchers.toSet().tap { it.removeAll(usedMatchers) }
+        assert unusedMatchers.size() == 0
 
         for (def plannedNode : plannedNodes) {
             def typedNodeId = getTypedNodeId(plannedNode.nodeIdentity)
@@ -706,21 +1152,15 @@
         )
     }
 
-    def transformMatcher(
+    def transformStepMatcher(
         String nodeId,
-        String consumerIdentityPath,
-        Map<String, String> componentId,
-        Map<String, String> attributes,
+        PlannedTransformStepIdentityWithoutId plannedTransformStepIdentity,
         List<String> dependencyNodeIds
     ) {
         new NodeMatcher(
             nodeId: nodeId,
-            nodeType: ARTIFACT_TRANSFORM,
-            identityPredicate: {
-                joinPaths(it.buildPath, it.projectPath) == consumerIdentityPath &&
-                    it.componentId == componentId &&
-                    it.targetAttributes == attributes
-            },
+            nodeType: TRANSFORM_STEP,
+            identityPredicate: { matchTransformationIdentity(it, plannedTransformStepIdentity) },
             dependencyNodeIds: dependencyNodeIds
         )
     }
@@ -729,8 +1169,8 @@
         switch (nodeIdentity.nodeType) {
             case TASK:
                 return new TypedNodeId(nodeType: TASK, nodeIdInType: nodeIdentity.taskId.toString())
-            case ARTIFACT_TRANSFORM:
-                return new TypedNodeId(nodeType: ARTIFACT_TRANSFORM, nodeIdInType: nodeIdentity.transformationNodeId)
+            case TRANSFORM_STEP:
+                return new TypedNodeId(nodeType: TRANSFORM_STEP, nodeIdInType: nodeIdentity.transformStepNodeId)
             default:
                 throw new IllegalArgumentException("Unknown node type: ${nodeIdentity.nodeType}")
         }
@@ -741,4 +1181,69 @@
             acc + (acc.endsWith(":") && path.startsWith(":") ? path.substring(1) : path)
         }
     }
+
+    boolean matchTransformationIdentity(actual, PlannedTransformStepIdentityWithoutId expected) {
+        actual.nodeType.toString() == TRANSFORM_STEP &&
+            actual.consumerBuildPath == expected.consumerBuildPath &&
+            actual.consumerProjectPath == expected.consumerProjectPath &&
+            actual.componentId == expected.componentId &&
+            actual.sourceAttributes == expected.sourceAttributes &&
+            actual.targetAttributes == expected.targetAttributes &&
+            actual.capabilities == expected.capabilities &&
+            actual.artifactName == expected.artifactName &&
+            actual.dependenciesConfigurationIdentity == expected.dependenciesConfigurationIdentity
+    }
+
+    void verifyTransformationIdentity(actual, PlannedTransformStepIdentityWithoutId expected) {
+        verifyAll(actual) {
+            nodeType.toString() == TRANSFORM_STEP
+            consumerBuildPath == expected.consumerBuildPath
+            consumerProjectPath == expected.consumerProjectPath
+            componentId == expected.componentId
+            sourceAttributes == expected.sourceAttributes
+            targetAttributes == expected.targetAttributes
+            capabilities == expected.capabilities
+            artifactName == expected.artifactName
+            dependenciesConfigurationIdentity == expected.dependenciesConfigurationIdentity
+        }
+    }
+
+    List<PlannedNode> getPlannedNodes(int transformNodeCount, String buildPath = ":") {
+        def ops = buildOperations.all(CalculateTaskGraphBuildOperationType)
+            .findAll { it.details.buildPath == buildPath }
+        assert !ops.empty
+        if (GradleContextualExecuter.configCache) {
+            assert ops.size() == 2
+        } else {
+            assert ops.size() == 1
+        }
+        return ops.collect { op ->
+            def plannedNodes = op.result.executionPlan as List<PlannedNode>
+            assert plannedNodes.every { KNOWN_NODE_TYPES.contains(it.nodeIdentity.nodeType) }
+            assert plannedNodes.count { it.nodeIdentity.nodeType.toString() == TRANSFORM_STEP } == transformNodeCount
+            return plannedNodes
+        }.first()
+    }
+
+    def getExecuteTransformOperations(int expectOperationsCount, boolean expectFailure = false) {
+        def operations = buildOperations.all(ExecutePlannedTransformStepBuildOperationType)
+        assert operations.every { (it.failure != null) == expectFailure }
+        assert operations.size() == expectOperationsCount
+        return operations
+    }
+
+    void checkExecuteTransformOperation(List<BuildOperationRecord> executeOperations, PlannedTransformStepIdentityWithoutId expectedTransformId, Map<String, Object> expectedDetails) {
+        def matchedOperations = executeOperations.findAll {
+            matchTransformationIdentity(it.details.plannedTransformStepIdentity, expectedTransformId)
+        }
+        assert matchedOperations.size() == 1
+        def operation = matchedOperations[0]
+
+        verifyAll(operation.details) {
+            transformActionClass == expectedDetails.transformActionClass
+
+            transformerName == expectedDetails.transformerName
+            subjectName == expectedDetails.subjectName
+        }
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformCachingIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformCachingIntegrationTest.groovy
index c8253b4..8e2cdeb 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformCachingIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformCachingIntegrationTest.groovy
@@ -396,7 +396,7 @@
                 configurations {
                     green {
                         extendsFrom(compile)
-                        canBeResolved = true
+                        assert canBeResolved
                         canBeConsumed = false
                         attributes {
                             attribute(artifactType, 'green')
@@ -430,16 +430,41 @@
         when:
         run(":app:toBeFinalized", "withDependency")
 
+        def lib1Message = "Transforming lib1.jar with MakeGreen"
+        def lib2Message = "Transforming lib2.jar with MakeGreen"
+
         then:
-        output.count("Transforming lib1.jar with MakeGreen") == 1
-        output.count("Transforming lib2.jar with MakeGreen") == 1
+        if (!GradleContextualExecuter.configCache) {
+            // Only runs once, as the transform execution in-memory cache is not discarded prior to execution time
+            output.count(lib1Message) == 1
+            output.count(lib2Message) == 1
+        } else {
+            // Transform is also executed at execution time, as the artifact has changed and the execution cache is discarded on load from cache
+            output.count(lib1Message) == 2
+            output.count(lib2Message) == 2
+        }
 
         when:
         run(":app:toBeFinalized", "withDependency")
 
         then:
-        output.count("Transforming lib1.jar with MakeGreen") == 1
-        output.count("Transforming lib2.jar with MakeGreen") == 1
+        if (!GradleContextualExecuter.configCache) {
+            // Runs again, as the artifact has changed
+            output.count(lib1Message) == 1
+            output.count(lib2Message) == 1
+        } else {
+            // Not executed at configuration time, and the transform has already executed for the artifact
+            output.count(lib1Message) == 0
+            output.count(lib2Message) == 0
+        }
+
+        when:
+        run(":app:toBeFinalized", "withDependency")
+
+        then:
+        // Not executed as the transform has already executed for the artifact
+        output.count(lib1Message) == 0
+        output.count(lib2Message) == 0
     }
 
     def "each file is transformed once per set of configuration parameters"() {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformIntegrationTest.groovy
index ebf0af9..a0d1678 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformIntegrationTest.groovy
@@ -16,12 +16,12 @@
 
 package org.gradle.integtests.resolve.transform
 
-import org.gradle.api.internal.artifacts.transform.ExecuteScheduledTransformationStepBuildOperationType
 import org.gradle.integtests.fixtures.AbstractHttpDependencyResolutionTest
 import org.gradle.integtests.fixtures.BuildOperationsFixture
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 import org.gradle.internal.file.FileType
+import org.gradle.operations.dependencies.transforms.ExecutePlannedTransformStepBuildOperationType
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.hamcrest.Matcher
 import spock.lang.Issue
@@ -2598,17 +2598,17 @@
 
         given:
         buildFile << """
-            import org.gradle.api.internal.artifacts.transform.ArtifactTransformListener
+            import org.gradle.api.internal.artifacts.transform.TransformExecutionListener
             import org.gradle.internal.event.ListenerManager
 
-            project.services.get(ListenerManager).addListener(new ArtifactTransformListener() {
+            project.services.get(ListenerManager).addListener(new TransformExecutionListener() {
                 @Override
-                void beforeTransformerInvocation(Describable transformer, Describable subject) {
+                void beforeTransformExecution(Describable transformer, Describable subject) {
                     println "Before transformer \${transformer.displayName} on \${subject.displayName}"
                 }
 
                 @Override
-                void afterTransformerInvocation(Describable transformer, Describable subject) {
+                void afterTransformExecution(Describable transformer, Describable subject) {
                     println "After transformer \${transformer.displayName} on \${subject.displayName}"
                 }
             })
@@ -2639,11 +2639,24 @@
         outputContains("After transformer FileSizer on lib.jar (project :lib)")
 
         and:
-        with(buildOperations.only(ExecuteScheduledTransformationStepBuildOperationType)) {
-            it.failure == null
-            displayName == "Transform lib.jar (project :lib) with FileSizer"
-            details.transformerName == "FileSizer"
-            details.subjectName == "lib.jar (project :lib)"
+        def executeTransformationOp = buildOperations.only(ExecutePlannedTransformStepBuildOperationType)
+        executeTransformationOp.failure == null
+        executeTransformationOp.displayName == "Transform lib.jar (project :lib) with FileSizer"
+        with(executeTransformationOp.details) {
+            transformerName == "FileSizer"
+            subjectName == "lib.jar (project :lib)"
+            with(plannedTransformStepIdentity) {
+                nodeType == "TRANSFORM_STEP"
+                consumerBuildPath == ":"
+                consumerProjectPath == ":app"
+                componentId == [buildPath: ":", projectPath: ":lib"]
+                sourceAttributes == [artifactType: "jar", usage: "api"]
+                targetAttributes == [artifactType: "size", usage: "api"]
+                capabilities == [[group: "root", name: "lib", version: "unspecified"]]
+                artifactName == "lib.jar"
+                dependenciesConfigurationIdentity == null
+            }
+            transformActionClass == "FileSizer"
         }
     }
 
@@ -2652,17 +2665,17 @@
 
         given:
         buildFile << """
-            import org.gradle.api.internal.artifacts.transform.ArtifactTransformListener
+            import org.gradle.api.internal.artifacts.transform.TransformExecutionListener
             import org.gradle.internal.event.ListenerManager
 
-            project.services.get(ListenerManager).addListener(new ArtifactTransformListener() {
+            project.services.get(ListenerManager).addListener(new TransformExecutionListener() {
                 @Override
-                void beforeTransformerInvocation(Describable transformer, Describable subject) {
+                void beforeTransformExecution(Describable transformer, Describable subject) {
                     println "Before transformer \${transformer.displayName} on \${subject.displayName}"
                 }
 
                 @Override
-                void afterTransformerInvocation(Describable transformer, Describable subject) {
+                void afterTransformExecution(Describable transformer, Describable subject) {
                     println "After transformer \${transformer.displayName} on \${subject.displayName}"
                 }
             })
@@ -2699,10 +2712,24 @@
         outputContains("After transformer BrokenTransform on lib.jar (project :lib)")
 
         and:
-        with(buildOperations.only(ExecuteScheduledTransformationStepBuildOperationType)) {
-            displayName == "Transform lib.jar (project :lib) with BrokenTransform"
-            details.transformerName == "BrokenTransform"
-            details.subjectName == "lib.jar (project :lib)"
+        def executeTransformationOp = buildOperations.only(ExecutePlannedTransformStepBuildOperationType)
+        executeTransformationOp.failure != null
+        executeTransformationOp.displayName == "Transform lib.jar (project :lib) with BrokenTransform"
+        with(executeTransformationOp.details) {
+            transformerName == "BrokenTransform"
+            subjectName == "lib.jar (project :lib)"
+            with(plannedTransformStepIdentity) {
+                nodeType == "TRANSFORM_STEP"
+                consumerBuildPath == ":"
+                consumerProjectPath == ":app"
+                componentId == [buildPath: ":", projectPath: ":lib"]
+                sourceAttributes == [artifactType: "jar", usage: "api"]
+                targetAttributes == [artifactType: "size", usage: "api"]
+                capabilities == [[group: "root", name: "lib", version: "unspecified"]]
+                artifactName == "lib.jar"
+                dependenciesConfigurationIdentity == null
+            }
+            transformActionClass == "BrokenTransform"
         }
     }
 
@@ -2795,6 +2822,6 @@
                     println "capabilities: " + artifacts.collect { it.variant.capabilities }
                 }
             }
-"""
+        """
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformIsolationIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformIsolationIntegrationTest.groovy
index 7685b8f..217fe36 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformIsolationIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformIsolationIntegrationTest.groovy
@@ -133,7 +133,7 @@
                         counter = buildScriptCounter
                     }
                 }
-                buildScriptCounter.increment()
+                buildScriptCounter.increment() // should not be captured during registration
                 registerTransform(CountRecorder) {
                     from.attribute(artifactType, 'jar')
                     to.attribute(artifactType, 'secondCount')
@@ -186,18 +186,40 @@
         and:
         outputContains("variants: [{artifactType=secondCount, org.gradle.status=release}, {artifactType=secondCount, org.gradle.status=release}]")
         file("build/libs2").assertHasDescendants("test-1.3.jar.txt", "test2-2.3.jar.txt")
-        file("build/libs2/test-1.3.jar.txt").readLines() == ["2", "3", "4", "5", "6"]
-        file("build/libs2/test2-2.3.jar.txt").readLines() == ["2", "3", "4", "5", "6"]
+        if (GradleContextualExecuter.configCache) {
+            // Counter is serialized and isolated prior to execution, so transforms will not see the increment in each tasks' doLast { } (which is good)
+            file("build/libs1/test-1.3.jar.txt").readLines() == ["1", "2", "3", "4", "5"]
+            file("build/libs1/test2-2.3.jar.txt").readLines() == ["1", "2", "3", "4", "5"]
+        } else {
+            // Counter is isolated at execution time, so transforms will see the increment in each tasks' doLast { }
+            file("build/libs2/test-1.3.jar.txt").readLines() == ["2", "3", "4", "5", "6"]
+            file("build/libs2/test2-2.3.jar.txt").readLines() == ["2", "3", "4", "5", "6"]
+        }
 
         and:
         outputContains("variants: [{artifactType=thirdCount, org.gradle.status=release}, {artifactType=thirdCount, org.gradle.status=release}]")
         file("build/libs3").assertHasDescendants("test-1.3.jar.txt", "test2-2.3.jar.txt")
-        file("build/libs3/test-1.3.jar.txt").readLines() == ["3", "4", "5", "6", "7"]
-        file("build/libs3/test2-2.3.jar.txt").readLines() == ["3", "4", "5", "6", "7"]
+        if (GradleContextualExecuter.configCache) {
+            // Counter is serialized and isolated prior to execution, so transforms will not see the increment in each tasks' doLast { } (which is good)
+            file("build/libs1/test-1.3.jar.txt").readLines() == ["1", "2", "3", "4", "5"]
+            file("build/libs1/test2-2.3.jar.txt").readLines() == ["1", "2", "3", "4", "5"]
+        } else {
+            // Counter is isolated at execution time, so transforms will see the increment in each tasks' doLast { }
+            file("build/libs3/test-1.3.jar.txt").readLines() == ["3", "4", "5", "6", "7"]
+            file("build/libs3/test2-2.3.jar.txt").readLines() == ["3", "4", "5", "6", "7"]
+        }
 
         and:
-        output.count("Transforming") == 6
-        output.count("Transforming test-1.3.jar to test-1.3.jar.txt") == 3
-        output.count("Transforming test2-2.3.jar to test2-2.3.jar.txt") == 3
+        if (GradleContextualExecuter.configCache) {
+            // Counter is serialized and isolated prior to execution, so transforms will not see the increment in each tasks' doLast { } (which is good)
+            output.count("Transforming") == 2
+            output.count("Transforming test-1.3.jar to test-1.3.jar.txt") == 1
+            output.count("Transforming test2-2.3.jar to test2-2.3.jar.txt") == 1
+        } else {
+            // Counter is isolated at execution time, so transforms will see the increment in each tasks' doLast { }
+            output.count("Transforming") == 6
+            output.count("Transforming test-1.3.jar to test-1.3.jar.txt") == 3
+            output.count("Transforming test2-2.3.jar to test2-2.3.jar.txt") == 3
+        }
     }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformWithDependenciesIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformWithDependenciesIntegrationTest.groovy
index 69db0fa..c76b0ec 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformWithDependenciesIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformWithDependenciesIntegrationTest.groovy
@@ -180,7 +180,7 @@
                 configurations {
                     testImplementation {
                         extendsFrom implementation
-                        canBeResolved = true
+                        assert canBeResolved
                         canBeConsumed = false
                         attributes.attribute(color, 'blue')
                     }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformWithFileInputsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformWithFileInputsIntegrationTest.groovy
index b10b3a0..576ac47 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformWithFileInputsIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformWithFileInputsIntegrationTest.groovy
@@ -229,7 +229,7 @@
                 configurations.create("toolsPath") {
                     extendsFrom(tools)
                     canBeConsumed = false
-                    canBeResolved = true
+                    assert canBeResolved
                     attributes.attribute(attr, 'blue')
                 }
                 ext.inputFiles = configurations.toolsPath.incoming.artifactView {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/DisambiguateArtifactTransformIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/DisambiguateArtifactTransformIntegrationTest.groovy
index 14f6aae..c514287 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/DisambiguateArtifactTransformIntegrationTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/DisambiguateArtifactTransformIntegrationTest.groovy
@@ -305,7 +305,6 @@
     }
 }
 
-
 task resolveTestClasses {
     def artifacts = configurations.compile.incoming.artifactView {
         attributes {
@@ -437,7 +436,7 @@
         }
         runtimeElements {
             extendsFrom(runtimeOnly)
-            canBeConsumed = true
+            assert canBeConsumed
             canBeResolved = false
             attributes {
                 attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/TransformLoggingIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/TransformLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..4b70878
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/TransformLoggingIntegrationTest.groovy
@@ -0,0 +1,251 @@
+/*
+ * 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.integtests.resolve.transform
+
+import org.gradle.api.logging.configuration.ConsoleOutput
+import org.gradle.integtests.fixtures.RichConsoleStyling
+import org.gradle.integtests.fixtures.console.AbstractConsoleGroupedTaskFunctionalTest
+import org.gradle.test.fixtures.server.http.BlockingHttpServer
+
+import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
+
+class TransformLoggingIntegrationTest extends AbstractConsoleGroupedTaskFunctionalTest {
+    ConsoleOutput consoleType
+
+    private static final List<ConsoleOutput> TESTED_CONSOLE_TYPES = [ConsoleOutput.Plain, ConsoleOutput.Verbose, ConsoleOutput.Rich, ConsoleOutput.Auto]
+
+    def setup() {
+        settingsFile << """
+            rootProject.name = 'root'
+            include 'lib'
+            include 'util'
+            include 'app'
+        """
+
+        buildFile << """
+            import java.nio.file.Files
+
+            def usage = Attribute.of('usage', String)
+            def artifactType = Attribute.of('artifactType', String)
+
+            allprojects {
+                dependencies {
+                    attributesSchema {
+                        attribute(usage)
+                    }
+                    registerTransform(GreenMultiplier) {
+                        from.attribute(artifactType, "jar")
+                        to.attribute(artifactType, "green")
+                    }
+                    registerTransform(BlueMultiplier) {
+                        from.attribute(artifactType, "green")
+                        to.attribute(artifactType, "blue")
+                    }
+                }
+                configurations {
+                    compile {
+                        attributes.attribute usage, 'api'
+                    }
+                }
+                ["blue", "green"].each { type ->
+                    tasks.register("resolve\${type.capitalize()}") {
+                        def artifacts = configurations.compile.incoming.artifactView {
+                            attributes { it.attribute(artifactType, type) }
+                        }.artifacts
+
+                        inputs.files artifacts.artifactFiles
+
+                        doLast {
+                            println "files: " + artifacts.collect { it.file.name }
+                        }
+                    }
+                }
+            }
+
+            project(':lib') {
+                task jar1(type: Jar) {
+                    archiveFileName = 'lib1.jar'
+                }
+                task jar2(type: Jar) {
+                    archiveFileName = 'lib2.jar'
+                }
+                tasks.withType(Jar) {
+                    destinationDirectory = buildDir
+                }
+                artifacts {
+                    compile jar1
+                    compile jar2
+                }
+            }
+
+            project(':util') {
+                dependencies {
+                    compile project(':lib')
+                }
+            }
+
+            project(':app') {
+                dependencies {
+                    compile project(':util')
+                }
+            }
+
+            import org.gradle.api.artifacts.transform.TransformParameters
+
+            abstract class Multiplier implements TransformAction<TransformParameters.None> {
+                private final boolean showOutput = System.getProperty("showOutput") != null
+                private final String target
+
+                Multiplier(String target) {
+                    if (showOutput) {
+                        println("Creating multiplier")
+                    }
+                    this.target = target
+                }
+
+                @InputArtifact
+                abstract Provider<FileSystemLocation> getInputArtifact()
+
+                @Override
+                void transform(TransformOutputs outputs) {
+                    def input = inputArtifact.get().asFile
+                    def output1 = outputs.file(input.name + ".1." + target)
+                    def output2 = outputs.file(input.name + ".2." + target)
+                    if (showOutput) {
+                        println("Transforming \${input.name} to \${input.name}.\${target}")
+                    }
+                    Files.copy(input.toPath(), output1.toPath())
+                    Files.copy(input.toPath(), output2.toPath())
+                }
+            }
+
+            abstract class GreenMultiplier extends Multiplier {
+                GreenMultiplier() {
+                    super("green")
+                }
+            }
+            abstract class BlueMultiplier extends Multiplier {
+                BlueMultiplier() {
+                    super("blue")
+                }
+            }
+        """
+    }
+
+    def "does not show transformation headers when there is no output for #type console"() {
+        consoleType = type
+
+        when:
+        succeeds(":util:resolveGreen")
+        then:
+        result.groupedOutput.transformCount == 0
+
+        where:
+        type << TESTED_CONSOLE_TYPES
+    }
+
+    def "does show transformation headers when there is output for #type console"() {
+        consoleType = type
+
+        when:
+        succeeds(":util:resolveGreen", "-DshowOutput")
+        then:
+        result.groupedOutput.transformCount == 2
+
+        result.groupedOutput.transform("GreenMultiplier", "lib1.jar (project :lib)")
+            .assertOutputContains("Creating multiplier")
+            .assertOutputContains("Transforming lib1.jar to lib1.jar.green")
+
+        result.groupedOutput.transform("GreenMultiplier", "lib2.jar (project :lib)")
+            .assertOutputContains("Creating multiplier")
+            .assertOutputContains("Transforming lib2.jar to lib2.jar.green")
+
+        where:
+        type << TESTED_CONSOLE_TYPES
+    }
+
+    def "progress display name is 'Transforming' for top level transforms"() {
+        // Build scan plugin filters artifact transform logging by the name of the progress display name
+        // since that is the only way it currently can distinguish transforms.
+        // When it has a better way, then this test can be removed.
+        consoleType = ConsoleOutput.Rich
+        BlockingHttpServer server = new BlockingHttpServer()
+        server.start()
+        buildFile << """
+            allprojects {
+                dependencies {
+                    registerTransform(Red) {
+                        from.attribute(artifactType, "jar")
+                        to.attribute(artifactType, "red")
+                    }
+                }
+                tasks.register("resolveRed") {
+                    def artifacts = configurations.compile.incoming.artifactView {
+                        attributes { it.attribute(artifactType, 'red') }
+                    }.artifacts
+
+                    inputs.files artifacts.artifactFiles
+
+                    doLast {
+                        println "files: " + artifacts.collect { it.file.name }
+                    }
+                }
+            }
+
+            // NOTE: This is named "Red" so that the status message fits on one line
+            abstract class Red extends Multiplier {
+                Red() {
+                    super("red")
+                }
+
+                @Override
+                void transform(TransformOutputs outputs) {
+                    ${server.callFromBuildUsingExpression("inputArtifact.get().asFile.name")}
+                    super.transform(outputs)
+                }
+            }
+        """
+
+        when:
+        def block = server.expectConcurrentAndBlock("lib1.jar", "lib2.jar")
+        def build = executer.withTasks(":util:resolveRed").start()
+        then:
+        block.waitForAllPendingCalls()
+        poll {
+            RichConsoleStyling.assertHasWorkInProgress(build, "> Transforming lib1.jar (project :lib) with Red > Red lib1.jar")
+            RichConsoleStyling.assertHasWorkInProgress(build, "> Transforming lib2.jar (project :lib) with Red > Red lib2.jar")
+        }
+
+        block.releaseAll()
+        build.waitForFinish()
+
+        cleanup:
+        server.stop()
+    }
+
+    def "each step is logged separately"() {
+        consoleType = ConsoleOutput.Plain
+
+        when:
+        succeeds(":util:resolveBlue", "-DshowOutput")
+        then:
+        result.groupedOutput.transformCount == 4
+        def initialSubjects = ["lib1.jar (project :lib)", "lib2.jar (project :lib)"] as Set
+        result.groupedOutput.subjectsFor('GreenMultiplier') == initialSubjects
+        result.groupedOutput.subjectsFor('BlueMultiplier') == initialSubjects
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/TransformationLoggingIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/TransformationLoggingIntegrationTest.groovy
deleted file mode 100644
index 4c60018..0000000
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/transform/TransformationLoggingIntegrationTest.groovy
+++ /dev/null
@@ -1,251 +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.integtests.resolve.transform
-
-import org.gradle.api.logging.configuration.ConsoleOutput
-import org.gradle.integtests.fixtures.RichConsoleStyling
-import org.gradle.integtests.fixtures.console.AbstractConsoleGroupedTaskFunctionalTest
-import org.gradle.test.fixtures.server.http.BlockingHttpServer
-
-import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
-
-class TransformationLoggingIntegrationTest extends AbstractConsoleGroupedTaskFunctionalTest {
-    ConsoleOutput consoleType
-
-    private static final List<ConsoleOutput> TESTED_CONSOLE_TYPES = [ConsoleOutput.Plain, ConsoleOutput.Verbose, ConsoleOutput.Rich, ConsoleOutput.Auto]
-
-    def setup() {
-        settingsFile << """
-            rootProject.name = 'root'
-            include 'lib'
-            include 'util'
-            include 'app'
-        """
-
-        buildFile << """
-            import java.nio.file.Files
-
-            def usage = Attribute.of('usage', String)
-            def artifactType = Attribute.of('artifactType', String)
-
-            allprojects {
-                dependencies {
-                    attributesSchema {
-                        attribute(usage)
-                    }
-                    registerTransform(GreenMultiplier) {
-                        from.attribute(artifactType, "jar")
-                        to.attribute(artifactType, "green")
-                    }
-                    registerTransform(BlueMultiplier) {
-                        from.attribute(artifactType, "green")
-                        to.attribute(artifactType, "blue")
-                    }
-                }
-                configurations {
-                    compile {
-                        attributes.attribute usage, 'api'
-                    }
-                }
-                ["blue", "green"].each { type ->
-                    tasks.register("resolve\${type.capitalize()}") {
-                        def artifacts = configurations.compile.incoming.artifactView {
-                            attributes { it.attribute(artifactType, type) }
-                        }.artifacts
-
-                        inputs.files artifacts.artifactFiles
-
-                        doLast {
-                            println "files: " + artifacts.collect { it.file.name }
-                        }
-                    }
-                }
-            }
-
-            project(':lib') {
-                task jar1(type: Jar) {
-                    archiveFileName = 'lib1.jar'
-                }
-                task jar2(type: Jar) {
-                    archiveFileName = 'lib2.jar'
-                }
-                tasks.withType(Jar) {
-                    destinationDirectory = buildDir
-                }
-                artifacts {
-                    compile jar1
-                    compile jar2
-                }
-            }
-
-            project(':util') {
-                dependencies {
-                    compile project(':lib')
-                }
-            }
-
-            project(':app') {
-                dependencies {
-                    compile project(':util')
-                }
-            }
-
-            import org.gradle.api.artifacts.transform.TransformParameters
-
-            abstract class Multiplier implements TransformAction<TransformParameters.None> {
-                private final boolean showOutput = System.getProperty("showOutput") != null
-                private final String target
-
-                Multiplier(String target) {
-                    if (showOutput) {
-                        println("Creating multiplier")
-                    }
-                    this.target = target
-                }
-
-                @InputArtifact
-                abstract Provider<FileSystemLocation> getInputArtifact()
-
-                @Override
-                void transform(TransformOutputs outputs) {
-                    def input = inputArtifact.get().asFile
-                    def output1 = outputs.file(input.name + ".1." + target)
-                    def output2 = outputs.file(input.name + ".2." + target)
-                    if (showOutput) {
-                        println("Transforming \${input.name} to \${input.name}.\${target}")
-                    }
-                    Files.copy(input.toPath(), output1.toPath())
-                    Files.copy(input.toPath(), output2.toPath())
-                }
-            }
-
-            abstract class GreenMultiplier extends Multiplier {
-                GreenMultiplier() {
-                    super("green")
-                }
-            }
-            abstract class BlueMultiplier extends Multiplier {
-                BlueMultiplier() {
-                    super("blue")
-                }
-            }
-        """
-    }
-
-    def "does not show transformation headers when there is no output for #type console"() {
-        consoleType = type
-
-        when:
-        succeeds(":util:resolveGreen")
-        then:
-        result.groupedOutput.transformationCount == 0
-
-        where:
-        type << TESTED_CONSOLE_TYPES
-    }
-
-    def "does show transformation headers when there is output for #type console"() {
-        consoleType = type
-
-        when:
-        succeeds(":util:resolveGreen", "-DshowOutput")
-        then:
-        result.groupedOutput.transformationCount == 2
-
-        result.groupedOutput.transform("GreenMultiplier", "lib1.jar (project :lib)")
-            .assertOutputContains("Creating multiplier")
-            .assertOutputContains("Transforming lib1.jar to lib1.jar.green")
-
-        result.groupedOutput.transform("GreenMultiplier", "lib2.jar (project :lib)")
-            .assertOutputContains("Creating multiplier")
-            .assertOutputContains("Transforming lib2.jar to lib2.jar.green")
-
-        where:
-        type << TESTED_CONSOLE_TYPES
-    }
-
-    def "progress display name is 'Transforming' for top level transforms"() {
-        // Build scan plugin filters artifact transform logging by the name of the progress display name
-        // since that is the only way it currently can distinguish transforms.
-        // When it has a better way, then this test can be removed.
-        consoleType = ConsoleOutput.Rich
-        BlockingHttpServer server = new BlockingHttpServer()
-        server.start()
-        buildFile << """
-            allprojects {
-                dependencies {
-                    registerTransform(Red) {
-                        from.attribute(artifactType, "jar")
-                        to.attribute(artifactType, "red")
-                    }
-                }
-                tasks.register("resolveRed") {
-                    def artifacts = configurations.compile.incoming.artifactView {
-                        attributes { it.attribute(artifactType, 'red') }
-                    }.artifacts
-
-                    inputs.files artifacts.artifactFiles
-
-                    doLast {
-                        println "files: " + artifacts.collect { it.file.name }
-                    }
-                }
-            }
-
-            // NOTE: This is named "Red" so that the status message fits on one line
-            abstract class Red extends Multiplier {
-                Red() {
-                    super("red")
-                }
-
-                @Override
-                void transform(TransformOutputs outputs) {
-                    ${server.callFromBuildUsingExpression("inputArtifact.get().asFile.name")}
-                    super.transform(outputs)
-                }
-            }
-        """
-
-        when:
-        def block = server.expectConcurrentAndBlock("lib1.jar", "lib2.jar")
-        def build = executer.withTasks(":util:resolveRed").start()
-        then:
-        block.waitForAllPendingCalls()
-        poll {
-            RichConsoleStyling.assertHasWorkInProgress(build, "> Transforming lib1.jar (project :lib) with Red > Red lib1.jar")
-            RichConsoleStyling.assertHasWorkInProgress(build, "> Transforming lib2.jar (project :lib) with Red > Red lib2.jar")
-        }
-
-        block.releaseAll()
-        build.waitForFinish()
-
-        cleanup:
-        server.stop()
-    }
-
-    def "each step is logged separately"() {
-        consoleType = ConsoleOutput.Plain
-
-        when:
-        succeeds(":util:resolveBlue", "-DshowOutput")
-        then:
-        result.groupedOutput.transformationCount == 4
-        def initialSubjects = ["lib1.jar (project :lib)", "lib2.jar (project :lib)"] as Set
-        result.groupedOutput.subjectsFor('GreenMultiplier') == initialSubjects
-        result.groupedOutput.subjectsFor('BlueMultiplier') == initialSubjects
-    }
-}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/AbstractDependencyVerificationIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/AbstractDependencyVerificationIntegTest.groovy
index 7ca171d..c26d177 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/AbstractDependencyVerificationIntegTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/AbstractDependencyVerificationIntegTest.groovy
@@ -92,7 +92,7 @@
     }
 
     static String getDocsUrl() {
-        new DocumentationRegistry().getDocumentationFor("dependency_verification", "sec:troubleshooting-verification")
+        new DocumentationRegistry().getDocumentationRecommendationFor("on how to do this", "dependency_verification", "sec:troubleshooting-verification")
     }
 
     protected void terseConsoleOutput(boolean terse, String gradlePropertiesDir = ".") {
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationIntegrityCheckIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationIntegrityCheckIntegTest.groovy
index c2d049f..579e720 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationIntegrityCheckIntegTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationIntegrityCheckIntegTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.integtests.resolve.verification
 
 import org.gradle.api.internal.artifacts.ivyservice.CacheLayout
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.cache.CachingIntegrationFixture
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
@@ -127,15 +128,16 @@
         uncheckedModule("org", "foo")
         uncheckedModule("org", "bar")
         buildFile << """
+            apply plugin: 'java-test-fixtures'
             dependencies {
                 implementation "org:foo:1.0"
-                testImplementation "org:bar:1.0"
+                testFixturesApi "org:bar:1.0"
             }
         """
         file("src/test/java/HelloTest.java") << "public class HelloTest {}"
 
         when:
-        succeeds([":test", *param] as String[])
+        succeeds([":compileJava", *param] as String[])
 
         then:
         errorOutput.contains("""Dependency verification failed for configuration ':compileClasspath':
@@ -310,6 +312,7 @@
         terse << [true, false]
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Configuration API")
     def "fails on the first access to an artifact (not at the end of the build) using #firstResolution"() {
         createMetadataFile {
             addChecksum("org:foo:1.0", "sha1", "invalid")
@@ -381,7 +384,7 @@
             return """Dependency verification failed for org:foo:1.0:
   - On artifact foo-1.0-sources.jar (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
         }
 
         String message = """Dependency verification failed for configuration ':compileClasspath':
@@ -525,7 +528,7 @@
   - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata.
   - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': checksum is missing from verification metadata.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
         }
         assertConfigCacheDiscarded()
 
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureCheckIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureCheckIntegTest.groovy
index a474456..570db1e 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureCheckIntegTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureCheckIntegTest.groovy
@@ -19,6 +19,7 @@
 import org.gradle.api.attributes.Category
 import org.gradle.api.attributes.Usage
 import org.gradle.api.internal.artifacts.ivyservice.CacheLayout
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
 import org.gradle.security.fixtures.KeyServer
 import org.gradle.security.fixtures.SigningFixtures
@@ -232,7 +233,7 @@
   - On artifact foo-1.0-classy.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
   - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
 
         where:
         stopInBetween << [false, true]
@@ -467,7 +468,7 @@
   - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
   - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
         }
         assertConfigCacheDiscarded()
         where:
@@ -509,7 +510,7 @@
   - On artifact foo-1.0-classy.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
   - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
         assertConfigCacheDiscarded()
     }
 
@@ -632,7 +633,7 @@
   - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
   - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '$pkId' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
         }
         assertConfigCacheDiscarded()
         where:
@@ -934,12 +935,12 @@
         assertVerificationError(terse) {
             whenTerse """Dependency verification failed for configuration ':compileClasspath'
 One artifact failed verification: foo-1.0.jar (org:foo:1.0) from repository maven
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
 
             whenVerbose """Dependency verification failed for configuration ':compileClasspath':
   - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key 'd7bf96a169f77b28c934ab1614f53f0824875d73' (Gradle Test (This is used for testing the gradle-signing-plugin) <test@gradle.org>) and passed verification but the key isn't in your trusted keys list.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
         }
 
         where:
@@ -1299,7 +1300,7 @@
       - Artifact was signed with key '14f53f0824875d73' but it wasn't found in any key server so it couldn't be verified
       - Artifact was signed with key '${pkId}' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
         }
         assertConfigCacheDiscarded()
         where:
@@ -1377,7 +1378,7 @@
   - On artifact foo-1.0.jar (org:foo:1.0) in repository 'maven': Artifact was signed with key '${pkId}' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
   - On artifact foo-1.0.pom (org:foo:1.0) in repository 'maven': Artifact was signed with key '${pkId}' (test-user@gradle.com) and passed verification but the key isn't in your trusted keys list.
 
-If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at ${docsUrl}"""
+If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. ${docsUrl}"""
             assertConfigCacheDiscarded()
         }
 
@@ -1468,6 +1469,7 @@
         terse << [true, false]
     }
 
+    @ToBeFixedForConfigurationCache(because = "task uses Artifact Query API")
     def "dependency verification should work correctly with artifact queries"() {
         createMetadataFile {
             keyServer(keyServerFixture.uri)
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureWriteIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureWriteIntegTest.groovy
index 87c061f..ac3854d 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureWriteIntegTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureWriteIntegTest.groovy
@@ -19,9 +19,13 @@
 import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
 import org.gradle.security.fixtures.SigningFixtures
 import org.gradle.security.internal.Fingerprint
+import org.gradle.security.internal.PGPUtils
 import org.gradle.security.internal.SecuritySupport
 import spock.lang.Issue
 
+import java.util.stream.Collectors
+import java.util.stream.Stream
+
 import static org.gradle.security.fixtures.SigningFixtures.signAsciiArmored
 
 class DependencyVerificationSignatureWriteIntegTest extends AbstractSignatureVerificationIntegrationTest {
@@ -48,9 +52,6 @@
         serveValidKey()
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         succeeds ":help"
 
         then:
@@ -92,9 +93,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         succeeds ":help"
 
         then:
@@ -153,9 +151,6 @@
         serveValidKey()
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         succeeds ":help"
 
         then:
@@ -259,9 +254,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         succeeds ":help"
 
         then:
@@ -314,9 +306,6 @@
         keyServerFixture.registerPublicKey(keyring.publicKey)
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         succeeds ":help", "--export-keys"
 
         then:
@@ -425,9 +414,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         succeeds ":help", "--export-keys"
 
         then:
@@ -456,9 +442,6 @@
         """
 
         when:
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         serveValidKey()
         keyServerFixture.registerPublicKey(keyring.publicKey)
         succeeds "build", "--export-keys"
@@ -471,4 +454,106 @@
         keyringsAscii.size() == 1
         keyringsAscii.find { it.publicKey.keyID == SigningFixtures.validPublicKey.keyID }
     }
+
+    @Issue("https://github.com/gradle/gradle/issues/23607")
+    def "verification-keyring.keys contains only necessary data"() {
+        def keyring = newKeyRing()
+        createMetadataFile {
+            keyServer(keyServerFixture.uri)
+        }
+
+        given:
+        javaLibrary()
+        uncheckedModule("org", "foo", "1.0") {
+            withSignature {
+                keyring.sign(it, [(SigningFixtures.validSecretKey): SigningFixtures.validPassword])
+            }
+        }
+        buildFile << """
+            dependencies {
+                implementation "org:foo:1.0"
+            }
+        """
+
+        when:
+        serveValidKey()
+        keyServerFixture.registerPublicKey(keyring.publicKey)
+        writeVerificationMetadata()
+        succeeds ":help", "--export-keys"
+
+        then:
+        def exportedKeyRingAscii = file("gradle/verification-keyring.keys")
+        exportedKeyRingAscii.exists()
+        def keyringsAscii = SecuritySupport.loadKeyRingFile(exportedKeyRingAscii)
+        keyringsAscii.size() == 2
+        keyringsAscii.forEach { keyRing ->
+            keyRing.publicKeys.forEachRemaining { publicKey ->
+                assert publicKey.getUserAttributes().size() == 0
+                assert publicKey.signatures.size() == publicKey.keySignatures.size()
+            }
+            assert keyRing.publicKey.userIDs.size() == 1
+        }
+    }
+
+    def "verification-keyring.keys is sorted and deduplicated by keyId"() {
+        def count = 50
+        def duplicatesCount = 10
+        def keyrings = Stream.generate { newKeyRing() }.limit(count - duplicatesCount).collect(Collectors.toList())
+        keyrings.addAll(keyrings.subList(0, duplicatesCount))
+
+        createMetadataFile {
+            keyServer(keyServerFixture.uri)
+        }
+
+        given:
+        javaLibrary()
+        def dependencies = new StringBuilder()
+        keyrings.eachWithIndex { keyring, index ->
+            uncheckedModule("org", "foo-$index", "1.0") {
+                withSignature {
+                    keyring.sign(it, [(SigningFixtures.validSecretKey): SigningFixtures.validPassword])
+                }
+            }
+            dependencies.append("implementation \"org:foo-$index:1.0\"\n")
+        }
+        buildFile << """
+            dependencies {
+                $dependencies
+            }
+        """
+
+        when:
+        serveValidKey()
+        keyrings.each { keyServerFixture.registerPublicKey(it.publicKey) }
+        writeVerificationMetadata()
+        succeeds ":help", "--export-keys"
+
+        then:
+        def exportedKeyRingAscii = file("gradle/verification-keyring.keys")
+        exportedKeyRingAscii.exists()
+        def keyringsAscii = SecuritySupport.loadKeyRingFile(exportedKeyRingAscii)
+        keyringsAscii.size() == count + 1 - duplicatesCount
+        (1..<keyringsAscii.size()).every {
+            keyringsAscii[it - 1].publicKey.keyID < keyringsAscii[it].publicKey.keyID
+        }
+    }
+
+    def "deduplicated keys are chosen by subkeys amount"() {
+        given:
+        javaLibrary()
+        file("gradle/verification-keyring.keys").copyFrom(
+            this.class.getResource("/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureWriteIntegTest/duplicated.keys")
+        )
+
+        when:
+        writeVerificationMetadata()
+        succeeds ":help", "--export-keys"
+
+        then:
+        def exportedKeyRingAscii = file("gradle/verification-keyring.keys")
+        exportedKeyRingAscii.exists()
+        def keyringsAscii = SecuritySupport.loadKeyRingFile(exportedKeyRingAscii)
+        keyringsAscii.size() == 1
+        PGPUtils.getSize(keyringsAscii[0]) == 5
+    }
 }
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationWritingIntegTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationWritingIntegTest.groovy
index c0d7b92..1e3e4e7 100644
--- a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationWritingIntegTest.groovy
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/verification/DependencyVerificationWritingIntegTest.groovy
@@ -38,7 +38,6 @@
         then:
         assertMetadataExists()
         hasNoModules()
-
     }
 
     def "warns if trying to generate with an unknown checksum type (#checksums)"() {
@@ -160,12 +159,12 @@
         buildFile << """
             configurations {
                 conf1 {
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
                 }
                 conf2 {
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                 }
             }
 
@@ -179,9 +178,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
@@ -285,9 +281,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
@@ -339,9 +332,6 @@
         """
 
         when:
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         writeVerificationMetadata()
         run ":help"
 
@@ -389,9 +379,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
@@ -470,9 +457,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         succeeds ':help'
 
         then:
@@ -516,9 +500,6 @@
         uncheckedModule("org", "bar")
 
         when:
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         writeVerificationMetadata()
         succeeds ":help"
 
@@ -551,9 +532,6 @@
         uncheckedModule("org", "bar")
 
         when:
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         writeVerificationMetadata()
         succeeds ":help"
 
@@ -674,9 +652,6 @@
         """
 
         when:
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         writeVerificationMetadata()
         run ":help"
 
@@ -718,9 +693,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
@@ -755,9 +727,6 @@
             }
         """
         when:
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         writeVerificationMetadata()
         run ":help"
 
@@ -808,9 +777,6 @@
         }
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
@@ -1284,9 +1250,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
@@ -1304,9 +1267,6 @@
         """
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
@@ -1342,9 +1302,6 @@
 
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help", "--offline"
 
         then:
@@ -1391,9 +1348,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
@@ -1434,9 +1388,6 @@
         when:
         writeVerificationMetadata()
 
-        //TODO: remove this once dependency verification stops triggering dependency resolution at execution time
-        executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
-
         run ":help"
 
         then:
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/AbstractRichVersionConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/AbstractRichVersionConstraintsIntegrationTest.groovy
new file mode 100644
index 0000000..a39926c
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/AbstractRichVersionConstraintsIntegrationTest.groovy
@@ -0,0 +1,1026 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
+import org.gradle.integtests.fixtures.RequiredFeature
+import org.gradle.integtests.resolve.AbstractModuleDependencyResolveTest
+import org.gradle.test.fixtures.ivy.IvyModule
+import spock.lang.Issue
+
+abstract class AbstractRichVersionConstraintsIntegrationTest extends AbstractModuleDependencyResolveTest {
+
+    void "can declare a strict dependency onto an external component"() {
+        given:
+        repository {
+            'org:foo:1.0'()
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:1.0!!')
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:{strictly 1.0}", "org:foo:1.0")
+            }
+        }
+    }
+
+    void "can declare a strict dependency constraint onto an external component"() {
+        given:
+        repository {
+            'org:foo:1.0'()
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo')
+                constraints {
+                    conf('org:foo:1.0!!')
+                }
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo", "org:foo:1.0") {
+                    byConstraint()
+                }
+                constraint("org:foo:{strictly 1.0}", "org:foo:1.0")
+            }
+        }
+    }
+
+    @Issue("gradle/gradle#4186")
+    def "should choose highest when multiple prefer versions disagree"() {
+        repository {
+            'org:foo' {
+                '1.0.0'()
+                '1.1.0'()
+                '1.2.0'()
+                '2.0.0'()
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                constraints {
+                    conf('org:foo') {
+                        version { prefer '1.1.0' }
+                    }
+                    conf('org:foo') {
+                        version { prefer '1.0.0' }
+                    }
+                }
+                conf 'org:foo:[1.0.0,2.0.0)'
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                expectVersionListing()
+                '1.1.0' {
+                    expectGetMetadata()
+                    expectGetArtifact()
+                }
+                '1.2.0' {
+                    expectGetMetadata()
+                }
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                constraint("org:foo:{prefer 1.0.0}", "org:foo:1.1.0")
+                constraint("org:foo:{prefer 1.1.0}", "org:foo:1.1.0")
+                edge("org:foo:[1.0.0,2.0.0)", "org:foo:1.1.0") {
+                    notRequested()
+                    byConstraint()
+                    byReason("didn't match version 2.0.0")
+                }
+            }
+        }
+    }
+
+    def "can combine required and preferred version in single dependency definition"() {
+        repository {
+            'org:foo' {
+                '1.0.0'()
+                '1.1.0'()
+                '1.2.0'()
+                '2.0.0'()
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:0.9')
+                conf('org:foo:[1.0.0,2.0.0)') {
+                    version {
+                        prefer '1.1.0'
+                    }
+                }
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                expectVersionListing()
+                '1.1.0' {
+                    expectGetMetadata()
+                    expectGetArtifact()
+                }
+                '1.2.0' {
+                    expectGetMetadata()
+                }
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:{require [1.0.0,2.0.0); prefer 1.1.0}", "org:foo:1.1.0")
+                edge("org:foo:0.9", "org:foo:1.1.0").byConflictResolution("between versions 0.9 and 1.1.0")
+            }
+        }
+    }
+
+    def "can combine strict and preferred version in single dependency definition"() {
+        repository {
+            'org:foo' {
+                '1.0.0'()
+                '1.1.0'()
+                '1.2.0'()
+                '2.0.0'()
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:[1.0.0,2.0.0)!!1.1.0')
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                expectVersionListing()
+                '1.1.0' {
+                    expectGetMetadata()
+                    expectGetArtifact()
+                }
+                '1.2.0' {
+                    expectGetMetadata()
+                }
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:{strictly [1.0.0,2.0.0); prefer 1.1.0}", "org:foo:1.1.0")
+            }
+        }
+    }
+
+    void "a strict dependency version takes precedence over a higher transitive version"() {
+        given:
+        repository {
+            'org:foo' {
+                '1.0'()
+                '1.1'()
+            }
+            'org:bar:1.0' {
+                dependsOn('org:foo:1.1')
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo') {
+                    version {
+                       strictly '1.0'
+                    }
+                }
+                conf('org:bar:1.0')
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                '1.0' {
+                    expectResolve()
+                }
+            }
+            'org:bar:1.0' {
+                expectResolve()
+            }
+        }
+        succeeds ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge('org:foo:{strictly 1.0}', 'org:foo:1.0')
+                module('org:bar:1.0') {
+                    edge('org:foo:1.1', 'org:foo:1.0') {
+                        byAncestor()
+                    }
+                }
+            }
+        }
+
+    }
+
+    void "should pass if transitive dependency version matches exactly the strict dependency version"() {
+        given:
+        repository {
+            'org:foo:1.0'()
+            'org:bar:1.0' {
+                dependsOn 'org:foo:1.0'
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:1.0!!')
+                conf('org:bar:1.0')
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org:bar:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge ("org:foo:{strictly 1.0}", "org:foo:1.0") {
+                    byAncestor()
+                }
+                edge("org:bar:1.0", "org:bar:1.0") {
+                    edge "org:foo:1.0", "org:foo:1.0"
+                }
+            }
+        }
+    }
+
+    void "can upgrade a non-strict dependency"() {
+        given:
+        repository {
+            'org:foo' {
+                '1.0'()
+                '1.1'()
+            }
+            'org:bar:1.0' {
+                dependsOn 'org:foo:1.0'
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo') {
+                    version {
+                       strictly '1.1'
+                    }
+                }
+                conf('org:bar:1.0')
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo:1.1' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+            'org:bar:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge "org:foo:{strictly 1.1}", "org:foo:1.1"
+                edge("org:bar:1.0", "org:bar:1.0") {
+                    edge("org:foo:1.0", "org:foo:1.1").byAncestor()
+                }
+            }
+        }
+    }
+
+    void "should pass if transitive dependency version (#transitiveDependencyVersion) matches a strict dependency version (#directDependencyVersion)"() {
+        given:
+        repository {
+            'org:foo' {
+                '1.0'()
+                '1.1'()
+                '1.2'()
+                '1.3'()
+            }
+            'org:bar:1.0' {
+                dependsOn("org:foo:$transitiveDependencyVersion")
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo') {
+                    version {
+                       strictly '$directDependencyVersion'
+                    }
+                }
+                conf('org:bar:1.0')
+            }
+
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                if (listVersions) {
+                    expectVersionListing()
+                }
+                "$resolvedVersion" {
+                    expectResolve()
+                }
+            }
+            'org:bar:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        noExceptionThrown()
+
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:{strictly $directDependencyVersion}", "org:foo:$resolvedVersion")
+                edge("org:bar:1.0", "org:bar:1.0") {
+                    edge("org:foo:$transitiveDependencyVersion", "org:foo:$resolvedVersion") {
+                        if (transitiveDependencyVersion != resolvedVersion) {
+                            byAncestor()
+                        }
+                        maybeRequested()
+                        maybeByReason("didn't match version 1.3")
+                    }
+                }
+            }
+        }
+
+        where:
+        directDependencyVersion | transitiveDependencyVersion | listVersions | resolvedVersion
+        '[1.0,1.3]'             | '1.2'                       | true         | '1.3' // should probably choose 1.2 instead
+        '1.2'                   | '[1.0,1.3]'                 | false        | '1.2'
+        '[1.0,1.2]'             | '[1.0, 1.3]'                | true         | '1.2'
+        '[1.0,1.3]'             | '[1.0,1.2]'                 | true         | '1.3' // should probably choose 1.2 instead
+    }
+
+    def "should not downgrade dependency version when a transitive dependency has strict version"() {
+        given:
+        repository {
+            'org:foo:15'()
+            'org:foo:17'()
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:17')
+                conf project(path: 'other', configuration: 'conf')
+            }
+        """
+        file("other/build.gradle") << """
+            $repositoryDeclaration
+
+            configurations {
+                conf
+            }
+            dependencies {
+                conf('org:foo') {
+                    version {
+                        strictly '15'
+                    }
+                }
+            }
+        """
+        settingsFile << "\ninclude 'other'"
+
+        when:
+        repositoryInteractions {
+            'org:foo:15' {
+                maybeGetMetadata()
+            }
+            'org:foo:17' {
+                expectGetMetadata()
+            }
+        }
+        fails ':checkDeps'
+
+        then:
+        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
+   Dependency path ':test:unspecified' --> 'org:foo:17'
+   Dependency path ':test:unspecified' --> 'test:other:unspecified' (conf) --> 'org:foo:{strictly 15}'""")
+
+    }
+
+    def "should fail if 2 1st-level strict versions disagree"() {
+        given:
+        repository {
+            'org:foo:15'()
+            'org:foo:17'()
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo') {
+                    version {
+                        strictly '17'
+                    }
+                }
+                conf('org:foo') {
+                    version {
+                        strictly '15'
+                    }
+                }
+            }
+        """
+        settingsFile << "\ninclude 'other'"
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                '15' {
+                    maybeGetMetadata()
+                }
+                '17' {
+                    expectGetMetadata()
+                }
+            }
+        }
+        fails ':checkDeps'
+
+        then:
+        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
+   Dependency path ':test:unspecified' --> 'org:foo:{strictly 17}'
+   Dependency path ':test:unspecified' --> 'org:foo:{strictly 15}'""")
+
+    }
+
+
+    @RequiredFeature(feature = GradleMetadataResolveRunner.GRADLE_METADATA, value = "true")
+    def "should fail if 1st-level version disagrees with transitive strict version"() {
+        given:
+        repository {
+            'org:foo:15'()
+            'org:foo:17'()
+            'org:bar:1' {
+                dependsOn(group: 'org', artifact: 'foo', strictly: '15')
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:17')
+                conf('org:bar:1')
+            }
+        """
+        settingsFile << "\ninclude 'other'"
+
+        when:
+        repositoryInteractions {
+            'org:bar:1' {
+                expectGetMetadata()
+            }
+            'org:foo' {
+                '15' {
+                    maybeGetMetadata()
+                }
+                '17' {
+                    expectGetMetadata()
+                }
+            }
+        }
+        fails ':checkDeps'
+
+        then:
+        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
+   Dependency path ':test:unspecified' --> 'org:foo:17'
+   Dependency path ':test:unspecified' --> 'org:bar:1' (runtime) --> 'org:foo:{strictly 15}'""")
+
+    }
+
+    def "strict range defined as 1st level dependency wins over transitive one"() {
+        given:
+        repository {
+            'org:foo:15'()
+            'org:foo:16'()
+            'org:foo:17'()
+            'org:foo:18'()
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo') {
+                    version {
+                        strictly '[15,16]'
+                    }
+                }
+                conf project(path: 'other', configuration: 'conf')
+            }
+        """
+        file("other/build.gradle") << """
+            $repositoryDeclaration
+
+            configurations {
+                conf
+            }
+            dependencies {
+                conf('org:foo') {
+                    version {
+                        strictly '[17,18]'
+                    }
+                }
+            }
+        """
+        settingsFile << "\ninclude 'other'"
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                expectVersionListing()
+                '16' {
+                    expectResolve()
+                }
+            }
+        }
+        succeeds ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge('org:foo:{strictly [15,16]}', 'org:foo:16')
+                project(':other', 'test:other:') {
+                    configuration 'conf'
+                    edge('org:foo:{strictly [17,18]}', 'org:foo:16') {
+                        notRequested()
+                        byAncestor()
+                        byReason("didn't match versions 18, 17")
+                    }
+                    noArtifacts()
+                }
+            }
+        }
+
+    }
+
+    def "should fail and include path to unresolvable strict version range"() {
+        given:
+        repository {
+            'org:foo:15'()
+        }
+
+        buildFile << """
+            dependencies {
+                conf 'org:foo:15'
+                conf('org:foo') {
+                    version {
+                        strictly '[0,1]'
+                    }
+                }
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                expectVersionListing()
+                '15' {
+                    expectGetMetadata()
+                }
+            }
+        }
+        fails ':checkDeps'
+
+        then:
+        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
+   Dependency path ':test:unspecified' --> 'org:foo:15'
+   Dependency path ':test:unspecified' --> 'org:foo:{strictly [0,1]}'""")
+    }
+
+    void "can reject dependency versions of an external component"() {
+        given:
+        repository {
+            'org:foo:1.0'()
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:1.0') {
+                   version {
+                      reject '1.1'
+                   }
+                }
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:{require 1.0; reject 1.1}", "org:foo:1.0")
+            }
+        }
+    }
+
+    void "honors rejection using dynamic versions using dependency notation #notation"() {
+        given:
+        repository {
+            'org:foo:1.0' {
+                withModule(IvyModule) {
+                    withStatus('release')
+                }
+            }
+            'org:foo:1.1' {
+                withModule(IvyModule) {
+                    withStatus('release')
+                }
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:$notation') {
+                   version {
+                      reject '1.1'
+                   }
+                }
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                expectVersionListing()
+                if (requiresMetadata) {
+                    '1.1' {
+                        expectGetMetadata()
+                    }
+                }
+                '1.0' {
+                    expectResolve()
+                }
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:{require $notation; reject 1.1}", "org:foo:1.0") {
+                    notRequested()
+                    byReason("rejected version 1.1")
+                }
+            }
+        }
+
+        where:
+        notation         | requiresMetadata
+        '1+'             | false
+        '1.+'            | false
+        '[1.0,)'         | false
+        'latest.release' | true
+    }
+
+    def "should fail during conflict resolution when one module rejects version"() {
+        given:
+        repository {
+            'org:foo' {
+                '1.0'()
+                '1.1'()
+            }
+            'org:bar:1.0' {
+                dependsOn('org:foo:1.1') // transitive dependency on rejected version
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:1.0') {
+                   version {
+                      reject '1.1'
+                   }
+                }
+                conf 'org:bar:1.0'
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                '1.0' {
+                    expectGetMetadata()
+                }
+                '1.1' {
+                    expectGetMetadata()
+                }
+            }
+            'org:bar:1.0' {
+                expectGetMetadata()
+            }
+        }
+        fails ':checkDeps'
+
+        then:
+        def selected = GradleMetadataResolveRunner.gradleMetadataPublished || GradleMetadataResolveRunner.useMaven() ? 'runtime' : 'default'
+        failure.assertHasCause("""Cannot find a version of 'org:foo' that satisfies the version constraints:
+   Dependency path ':test:unspecified' --> 'org:foo:{require 1.0; reject 1.1}'
+   Dependency path ':test:unspecified' --> 'org:bar:1.0' ($selected) --> 'org:foo:1.1'""")
+    }
+
+    def "can reject a version range"() {
+        given:
+        repository {
+            (0..5).each {
+                "org:foo:1.$it"()
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:[1.0,)') {
+                   version {
+                      reject '[1.2, 1.5]'
+                   }
+                }
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                expectVersionListing()
+                '1.1' {
+                    expectResolve()
+                }
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:{require [1.0,); reject [1.2, 1.5]}", "org:foo:1.1") {
+                    notRequested()
+                    byReason("rejected versions 1.5, 1.4, 1.3, 1.2")
+                }
+            }
+        }
+    }
+
+    void "can reject multiple versions of an external component"() {
+        given:
+        repository {
+            'org:foo:1.0'()
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:1.0') {
+                   version {
+                      reject '1.1', '1.2'
+                   }
+                }
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo:1.0' {
+                expectGetMetadata()
+                expectGetArtifact()
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:foo:{require 1.0; reject 1.1 & 1.2}", "org:foo:1.0")
+            }
+        }
+    }
+
+    void "honors multiple rejections #rejects using dynamic versions using dependency notation #notation"() {
+        given:
+        repository {
+            (0..5).each {
+                "org:foo:1.$it" {
+                    withModule(IvyModule) {
+                        withStatus('release')
+                    }
+                }
+            }
+        }
+
+        def rejectString = rejects.collect({ "'${it}'" }).join(', ')
+        buildFile << """
+            dependencies {
+                conf('org:foo:$notation') {
+                   version {
+                      reject $rejectString
+                   }
+                }
+            }
+        """
+
+        when:
+        repositoryInteractions {
+            'org:foo' {
+                expectVersionListing()
+                if (requiresMetadata) {
+                    5.downto(selected + 1) {
+                        "1.$it" {
+                            expectGetMetadata()
+                        }
+                    }
+                }
+                "1.$selected" {
+                    expectResolve()
+                }
+            }
+        }
+        run ':checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                String rejectedVersions = (selected + 1..5).collect { "1.${it}" }.reverse().join(", ")
+                edge("org:foo:{require $notation; reject ${rejects.join(' & ')}}", "org:foo:1.$selected") {
+                    notRequested()
+                    byReason("rejected versions ${rejectedVersions}")
+                }
+            }
+        }
+
+        where:
+        notation         | rejects                      | selected | requiresMetadata
+        '1+'             | ['1.4', '1.5']               | 3        | false
+        '1.+'            | ['1.4', '1.5']               | 3        | false
+        '[1.0,)'         | ['1.4', '1.5']               | 3        | false
+        'latest.release' | ['1.4', '1.5']               | 3        | true
+
+        '1+'             | ['[1.2,)', '1.5']            | 1        | false
+        '1.+'            | ['[1.2,)', '1.5']            | 1        | false
+        '[1.0,)'         | ['[1.2,)', '1.5']            | 1        | false
+        'latest.release' | ['[1.2,)', '1.5']            | 1        | true
+
+        '1+'             | ['1.5', '[1.1, 1.3]', '1.4'] | 0        | false
+        '1.+'            | ['1.5', '[1.1, 1.3]', '1.4'] | 0        | false
+        '[1.0,)'         | ['1.5', '[1.1, 1.3]', '1.4'] | 0        | false
+        'latest.release' | ['1.5', '[1.1, 1.3]', '1.4'] | 0        | true
+    }
+
+    def "adding rejectAll on a dependency is pointless and make it fail"() {
+        given:
+        repository {
+            'org:foo' {
+                '1.0'()
+            }
+        }
+
+        buildFile << """
+            dependencies {
+                conf('org:foo:1.0') {
+                   version {
+                      rejectAll()
+                   }
+                }
+            }
+        """
+
+        when:
+        fails ':checkDeps'
+
+        then:
+        failure.assertHasCause("Module 'org:foo' has been rejected")
+    }
+
+    @Issue("gradle/gradle#4608")
+    def "conflict resolution should consider all constraints for each candidate"() {
+        repository {
+            'org:foo:2' {
+                dependsOn("org:bar:2")
+            }
+            'org:bar:1'()
+            'org:bar:2'()
+        }
+
+        buildFile << """
+            dependencies {
+                constraints {
+                    conf('org:bar') {
+                        version {
+                            strictly '1'
+                        }
+                    }
+                }
+                conf 'org:bar:1'
+                conf 'org:foo:2' // Brings in org:bar:2, which is chosen over org:bar:1 in conflict resolution
+            }
+"""
+        when:
+        repositoryInteractions {
+            'org:bar' {
+                '1' {
+                    expectResolve()
+                }
+            }
+            'org:foo:2' {
+                expectResolve()
+            }
+        }
+
+        succeeds ":checkDeps"
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                constraint('org:bar:{strictly 1}', 'org:bar:1')
+                module('org:bar:1') {
+                    byConstraint()
+                }
+                module('org:foo:2') {
+                    edge('org:bar:2', 'org:bar:1') {
+                        byAncestor()
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/AbstractVersionRangeResolveIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/AbstractVersionRangeResolveIntegrationTest.groovy
new file mode 100644
index 0000000..b1a82fa
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/AbstractVersionRangeResolveIntegrationTest.groovy
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
+import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
+import spock.lang.IgnoreIf
+
+/**
+ * A comprehensive test of dependency resolution of a single module version, given a set of input selectors.
+ * This integration test validates all scenarios in {@link VersionRangeResolveTestScenarios}, as well as some adhoc scenarios.
+ */
+@IgnoreIf({
+    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
+    // infrastructure to simulate everything done here, so we're only going to execute this test in
+    // embedded mode
+    !GradleContextualExecuter.embedded
+})
+abstract class AbstractVersionRangeResolveIntegrationTest extends AbstractDependencyResolutionTest {
+
+    def baseBuild
+    def baseSettings
+    def resolve = new ResolveTestFixture(buildFile, "conf").expectDefaultConfiguration("runtime")
+
+    def setup() {
+        (9..13).each {
+            mavenRepo.module("org", "foo", "${it}").publish()
+        }
+
+        settingsFile << "rootProject.name = 'test'"
+        buildFile << """
+            repositories {
+                maven { url '${mavenRepo.uri}' }
+            }
+            configurations {
+                conf
+            }
+"""
+        resolve.prepare()
+        baseBuild = buildFile.text
+        baseSettings = settingsFile.text
+    }
+
+    void checkScenarioResolution(String expectedSingle, String expectedMulti, VersionRangeResolveTestScenarios.RenderableVersion... versions) {
+        checkScenarioResolution(expectedSingle, expectedMulti, versions as List)
+    }
+
+    void checkScenarioResolution(String expectedSingle, String expectedMulti, List<VersionRangeResolveTestScenarios.RenderableVersion> versions) {
+        settingsFile.text = baseSettings
+
+        def singleProjectConfs = []
+        def singleProjectDeps = []
+        versions.eachWithIndex { VersionRangeResolveTestScenarios.RenderableVersion version, int i ->
+            singleProjectConfs << "single${i}"
+            singleProjectDeps << "single${i} " + version.render()
+        }
+
+        buildFile.text = baseBuild + """
+            allprojects {
+                configurations { conf }
+            }
+
+            configurations {
+                ${singleProjectConfs.join('\n')}
+                single {
+                    extendsFrom(${singleProjectConfs.join(',')})
+                }
+            }
+
+            dependencies {
+                conf 'org:foo'
+                conf project(path: ':p1', configuration: 'conf')
+                ${singleProjectDeps.join('\n')}
+            }
+
+            task resolveMultiProject(type: Sync) {
+                from configurations.conf
+                into 'libs-multi'
+            }
+
+            task resolveSingleProject(type: Sync) {
+                from configurations.single
+                into 'libs-single'
+            }
+"""
+        for (int i = 1; i <= versions.size(); i++) {
+            VersionRangeResolveTestScenarios.RenderableVersion version = versions.get(i - 1);
+            def nextProjectDependency = i < versions.size() ? "conf project(path: ':p${i + 1}', configuration: 'conf')" : ""
+            buildFile << """
+                project('p${i}') {
+                    dependencies {
+                        conf ${version.render()}
+                        ${nextProjectDependency}
+                    }
+                }
+"""
+            settingsFile << """
+                include ':p${i}'
+"""
+        }
+
+        boolean expectFailureSingle = expectedSingle == VersionRangeResolveTestScenarios.REJECTED || expectedSingle == VersionRangeResolveTestScenarios.FAILED
+        boolean expectFailureMulti = expectedMulti == VersionRangeResolveTestScenarios.REJECTED || expectedMulti == VersionRangeResolveTestScenarios.FAILED
+        if (expectFailureMulti) {
+            fails 'resolveMultiProject'
+        }
+        if (expectFailureSingle) {
+            fails 'resolveSingleProject'
+        }
+
+        if (!expectFailureMulti) {
+            run 'resolveMultiProject'
+            def multiProjectResolve = file('libs-multi').list() as List
+            assert parseResolvedVersion(multiProjectResolve) == expectedMulti
+        }
+
+        if (!expectFailureSingle) {
+            run 'resolveSingleProject'
+            def singleProjectResolve = file('libs-single').list() as List
+            assert parseResolvedVersion(singleProjectResolve) == expectedSingle
+        }
+    }
+
+    def parseResolvedVersion(resolvedFiles) {
+        assert resolvedFiles.size() == 1
+        def resolvedFile = resolvedFiles.get(0)
+        assert resolvedFile.startsWith('foo-')
+        assert resolvedFile.endsWith('.jar')
+        def resolvedVersion = (resolvedFile =~ /\d+/).getAt(0)
+        resolvedVersion
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/IvyRichVersionConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/IvyRichVersionConstraintsIntegrationTest.groovy
new file mode 100644
index 0000000..24cd6b7
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/IvyRichVersionConstraintsIntegrationTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
+import org.gradle.integtests.fixtures.RequiredFeature
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
+@IgnoreIf({
+    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
+    // infrastructure to simulate everything done here, so we're only going to execute this test in
+    // embedded mode
+    !GradleContextualExecuter.embedded
+})
+@RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "ivy")
+class IvyRichVersionConstraintsIntegrationTest extends AbstractRichVersionConstraintsIntegrationTest {
+    // Test split in two to reduce overall serial time execution
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/MavenRichVersionConstraintsIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/MavenRichVersionConstraintsIntegrationTest.groovy
new file mode 100644
index 0000000..9594313
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/MavenRichVersionConstraintsIntegrationTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.GradleMetadataResolveRunner
+import org.gradle.integtests.fixtures.RequiredFeature
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
+@IgnoreIf({
+    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
+    // infrastructure to simulate everything done here, so we're only going to execute this test in
+    // embedded mode
+    !GradleContextualExecuter.embedded
+})
+@RequiredFeature(feature = GradleMetadataResolveRunner.REPOSITORY_TYPE, value = "maven")
+class MavenRichVersionConstraintsIntegrationTest extends AbstractRichVersionConstraintsIntegrationTest {
+    // Test split in two to reduce overall serial time execution
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionConflictResolutionIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionConflictResolutionIntegrationTest.groovy
new file mode 100644
index 0000000..8c5c3f7
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionConflictResolutionIntegrationTest.groovy
@@ -0,0 +1,2432 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
+import spock.lang.Issue
+
+import static org.hamcrest.CoreMatchers.containsString
+
+class VersionConflictResolutionIntegrationTest extends AbstractIntegrationSpec {
+    public static final String CONFLICT_FOUND_HEADER_MESSAGE = 'Conflict found for the following module:'
+    private ResolveTestFixture resolve = new ResolveTestFixture(buildFile, "compile")
+
+    def setup() {
+        settingsFile << """
+            rootProject.name = 'test'
+        """
+        resolve.expectDefaultConfiguration("runtime")
+        resolve.addDefaultVariantDerivationStrategy()
+    }
+
+    void "strict conflict resolution should fail due to conflict"() {
+        mavenRepo.module("org", "foo", '1.3.3').publish()
+        mavenRepo.module("org", "foo", '1.4.4').publish()
+
+        settingsFile << "include 'api', 'impl', 'tool'"
+
+        buildFile << """
+allprojects {
+	apply plugin: 'java'
+	repositories {
+		maven { url "${mavenRepo.uri}" }
+	}
+}
+
+project(':api') {
+	dependencies {
+		implementation (group: 'org', name: 'foo', version:'1.3.3')
+	}
+}
+
+project(':impl') {
+	dependencies {
+		implementation (group: 'org', name: 'foo', version:'1.4.4')
+	}
+}
+
+project(':tool') {
+	dependencies {
+		implementation project(':api')
+		implementation project(':impl')
+	}
+
+	configurations.runtimeClasspath.resolutionStrategy.failOnVersionConflict()
+}
+"""
+
+        expect:
+        runAndFail("tool:dependencies")
+        failure.assertThatCause(containsString(CONFLICT_FOUND_HEADER_MESSAGE))
+    }
+
+    void "strict conflict resolution should pass when no conflicts"() {
+        mavenRepo.module("org", "foo", '1.3.3').publish()
+
+        settingsFile << "include 'api', 'impl', 'tool'"
+
+        buildFile << """
+allprojects {
+	apply plugin: 'java'
+	repositories {
+		maven { url "${mavenRepo.uri}" }
+	}
+}
+
+project(':api') {
+	dependencies {
+		implementation (group: 'org', name: 'foo', version:'1.3.3')
+	}
+}
+
+project(':impl') {
+	dependencies {
+		implementation (group: 'org', name: 'foo', version:'1.3.3')
+	}
+}
+
+project(':tool') {
+	dependencies {
+		implementation project(':api')
+		implementation project(':impl')
+	}
+
+	configurations.all { resolutionStrategy.failOnVersionConflict() }
+}
+"""
+
+        expect:
+        run("tool:dependencies")
+    }
+
+    void "resolves module version conflicts to the latest version by default"() {
+        mavenRepo.module("org", "foo", '1.3.3').publish()
+        mavenRepo.module("org", "foo", '1.4.4').publish()
+
+        settingsFile << """
+include 'api', 'impl', 'tool'
+"""
+
+        buildFile << """
+allprojects {
+	apply plugin: 'java'
+	repositories {
+		maven { url "${mavenRepo.uri}" }
+	}
+}
+
+project(':api') {
+	dependencies {
+		implementation (group: 'org', name: 'foo', version:'1.3.3')
+	}
+}
+
+project(':impl') {
+	dependencies {
+		implementation (group: 'org', name: 'foo', version:'1.4.4')
+	}
+}
+
+project(':tool') {
+	dependencies {
+		implementation project(':api')
+		implementation project(':impl')
+	}
+}
+"""
+
+        def resolve = new ResolveTestFixture(buildFile)
+        resolve.prepare()
+
+        when:
+        run("tool:checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":tool", "test:tool:") {
+                project(":api", "test:api:") {
+                    configuration = "runtimeElements"
+                    edge("org:foo:1.3.3", "org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3")
+                }
+                project(":impl", "test:impl:") {
+                    configuration = "runtimeElements"
+                    module("org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3")
+                }
+            }
+        }
+    }
+
+    void "resolves transitive module version conflicts to the latest version by default"() {
+        def foo133 = mavenRepo.module("org", "foo", '1.3.3').publish()
+        def foo144 = mavenRepo.module("org", "foo", '1.4.4').publish()
+        mavenRepo.module("org", "bar", "1.0").dependsOn(foo133).publish()
+        mavenRepo.module("org", "baz", "1.0").dependsOn(foo144).publish()
+
+        buildFile << """
+apply plugin: 'java'
+group = 'org'
+version = '1.0'
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+dependencies {
+    implementation (group: 'org', name: 'bar', version:'1.0')
+    implementation (group: 'org', name: 'baz', version:'1.0')
+}
+
+task resolve {
+    doLast {
+        println configurations.compile.files
+    }
+}
+"""
+
+        def resolve = new ResolveTestFixture(buildFile).expectDefaultConfiguration("runtime")
+        resolve.prepare()
+
+        when:
+        run(":checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", "org:test:1.0") {
+                module("org:bar:1.0") {
+                    edge("org:foo:1.3.3", "org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3")
+                }
+                module("org:baz:1.0") {
+                    module("org:foo:1.4.4").byConflictResolution("between versions 1.4.4 and 1.3.3")
+                }
+            }
+        }
+    }
+
+    void "re-selects target version for previously resolved then evicted selector"() {
+        def depOld = mavenRepo.module("org", "dep", "2.0").publish()
+        def depNew = mavenRepo.module("org", "dep", "2.5").publish()
+
+        def controlOld = mavenRepo.module("org", "control", "1.0").dependsOn(depNew).publish()
+        def controlNew = mavenRepo.module("org", "control", "1.2").dependsOn(depNew).publish()
+        def controlNewBringer = mavenRepo.module("org", "control-1.2-bringer", "1.0").dependsOn(controlNew).publish()
+
+        mavenRepo.module("org", "one", "1.0").dependsOn(controlOld).publish()
+
+        def depOldBringer = mavenRepo.module("org", "dep-2.0-bringer", "1.0").dependsOn(depOld).publish()
+        // Note: changing the order of the following dependencies makes the test pass
+        mavenRepo.module("org", "two", "1.0").dependsOn(controlNewBringer).dependsOn(depOldBringer).publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:one:1.0'
+    compile 'org:two:1.0'
+}
+"""
+
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:one:1.0") {
+                    edge("org:control:1.0", "org:control:1.2") {
+                        byConflictResolution("between versions 1.2 and 1.0")
+                        module("org:dep:2.5")
+                    }
+                }
+                module("org:two:1.0") {
+                    module("org:dep-2.0-bringer:1.0") {
+                        edge("org:dep:2.0", "org:dep:2.5") {
+                            byConflictResolution("between versions 2.5 and 2.0")
+                        }
+                    }
+                    module("org:control-1.2-bringer:1.0") {
+                        module("org:control:1.2")
+                    }
+                }
+            }
+        }
+    }
+
+    void "does not attempt to resolve an evicted dependency"() {
+        mavenRepo.module("org", "external", "1.2").publish()
+        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.0").publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:external:1.2'
+    compile 'org:dep:2.2'
+}
+"""
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:external:1.2").byConflictResolution("between versions 1.2 and 1.0")
+                module("org:dep:2.2") {
+                    edge("org:external:1.0", "org:external:1.2")
+                }
+            }
+        }
+    }
+
+    @Issue("GRADLE-2890")
+    void "selects latest from multiple conflicts"() {
+        mavenRepo.module("org", "child", '1').publish()
+        mavenRepo.module("org", "child", '2').publish()
+        mavenRepo.module("org", "parent", '1').dependsOn("org", "child", "1").publish()
+        mavenRepo.module("org", "parent", '2').dependsOn("org", "child", "2").publish()
+        mavenRepo.module("org", "dep", '2').dependsOn("org", "parent", "2").publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+configurations {
+    compile
+}
+dependencies {
+    compile 'org:parent:1'
+    compile 'org:child:2'
+    compile 'org:dep:2'
+}
+"""
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:child:2") {
+                    byConflictResolution("between versions 2 and 1")
+                }
+                edge("org:parent:1", "org:parent:2") {
+                    byConflictResolution("between versions 2 and 1")
+                    module("org:child:2")
+                }
+                module("org:dep:2") {
+                    module("org:parent:2")
+                }
+            }
+        }
+    }
+
+    void "resolves dynamic dependency before resolving conflict"() {
+        mavenRepo.module("org", "external", "1.2").publish()
+        mavenRepo.module("org", "external", "1.4").publish()
+        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "[1.3,)").publish()
+
+        def buildFile = file("build.gradle")
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:external:1.2'
+    compile 'org:dep:2.2'
+}
+"""
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:external:1.2", "org:external:1.4") {
+                    byConflictResolution("between versions 1.2 and 1.4")
+                }
+                module("org:dep:2.2") {
+                    edge("org:external:[1.3,)", "org:external:1.4")
+                }
+            }
+        }
+    }
+
+    void "fails when version selected by conflict resolution does not exist"() {
+        mavenRepo.module("org", "external", "1.2").publish()
+        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.4").publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:external:1.2'
+    compile 'org:dep:2.2'
+}
+
+task checkDeps {
+    def files = configurations.compile
+    doLast {
+        files.files
+    }
+}
+"""
+
+        expect:
+        runAndFail("checkDeps")
+        failure.assertHasCause("Could not find org:external:1.4.")
+    }
+
+    void "does not fail when evicted version does not exist"() {
+        mavenRepo.module("org", "external", "1.4").publish()
+        mavenRepo.module("org", "dep", "2.2").dependsOn("org", "external", "1.4").publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+configurations { compile }
+
+dependencies {
+    compile 'org:external:1.2'
+    compile 'org:dep:2.2'
+}
+"""
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:external:1.2", "org:external:1.4") {
+                    byConflictResolution("between versions 1.4 and 1.2")
+                }
+                module("org:dep:2.2") {
+                    module("org:external:1.4")
+                }
+            }
+        }
+    }
+
+    void "takes newest dynamic version when dynamic version forced"() {
+        mavenRepo.module("org", "foo", '1.3.0').publish()
+
+        mavenRepo.module("org", "foo", '1.4.1').publish()
+        mavenRepo.module("org", "foo", '1.4.4').publish()
+        mavenRepo.module("org", "foo", '1.4.9').publish()
+
+        mavenRepo.module("org", "foo", '1.6.0').publish()
+
+        settingsFile << "include 'api', 'impl', 'tool'"
+
+        buildFile << """
+allprojects {
+	apply plugin: 'java'
+	repositories {
+		maven { url "${mavenRepo.uri}" }
+	}
+}
+
+project(':api') {
+	dependencies {
+		implementation 'org:foo:1.4.4'
+	}
+}
+
+project(':impl') {
+	dependencies {
+		implementation 'org:foo:1.4.1'
+	}
+}
+
+project(':tool') {
+
+	dependencies {
+		implementation project(':api'), project(':impl'), 'org:foo:1.3.0'
+	}
+
+	configurations.all {
+	    resolutionStrategy {
+	        force 'org:foo:[1.4, 1.5)'
+	        failOnVersionConflict()
+	    }
+	}
+}
+
+"""
+        resolve.prepare("runtimeClasspath")
+
+        when:
+        run("tool:checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":tool", "test:tool:") {
+                project(":api", "test:api:") {
+                    edge("org:foo:1.4.4", "org:foo:1.4.9") {
+                        forced()
+                        byReason("didn't match version 1.6.0")
+                    }
+                }
+                project(":impl", "test:impl:") {
+                    edge("org:foo:1.4.1", "org:foo:1.4.9")
+                }
+                edge("org:foo:1.3.0", "org:foo:1.4.9")
+            }
+        }
+    }
+
+    void "parent pom does not participate in forcing mechanism"() {
+        mavenRepo.module("org", "foo", '1.3.0').publish()
+        mavenRepo.module("org", "foo", '2.4.0').publish()
+
+        def parent = mavenRepo.module("org", "someParent", "1.0")
+        parent.type = 'pom'
+        parent.dependsOn("org", "foo", "1.3.0")
+        parent.publish()
+
+        def otherParent = mavenRepo.module("org", "someParent", "2.0")
+        otherParent.type = 'pom'
+        otherParent.dependsOn("org", "foo", "2.4.0")
+        otherParent.publish()
+
+        def dep = mavenRepo.module("org", "someArtifact", '1.0')
+        dep.parent("org", "someParent", "1.0")
+        dep.publish()
+
+        buildFile << """
+apply plugin: 'java'
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+dependencies {
+    implementation 'org:someArtifact:1.0'
+}
+
+configurations.all {
+    resolutionStrategy {
+        force 'org:someParent:2.0'
+        failOnVersionConflict()
+    }
+}
+"""
+        resolve.prepare("runtimeClasspath")
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:someArtifact:1.0") {
+                    module("org:foo:1.3.0")
+                }
+            }
+        }
+    }
+
+    void "previously evicted nodes should contain correct target version"() {
+        /*
+        a1->b1
+        a2->b2->a1
+
+        resolution process:
+
+        1. stop resolution, resolve conflict a1 vs a2
+        2. select a2, restart resolution
+        3. stop, resolve b1 vs b2
+        4. select b2, restart
+        5. resolve b2 dependencies, a1 has been evicted previously but it should show correctly on the report
+           ('dependencies' report pre 1.2 would not show the a1 dependency leaf for this scenario)
+        */
+
+        ivyRepo.module("org", "b", '1.0').publish()
+        ivyRepo.module("org", "a", '1.0').dependsOn("org", "b", '1.0').publish()
+        ivyRepo.module("org", "b", '2.0').dependsOn("org", "a", "1.0").publish()
+        ivyRepo.module("org", "a", '2.0').dependsOn("org", "b", '2.0').publish()
+
+        buildFile << """
+            repositories {
+                ivy { url "${ivyRepo.uri}" }
+            }
+
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:a:2.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1.0", "org:a:2.0")
+                module("org:a:2.0") {
+                    configuration = "default"
+                    byConflictResolution("between versions 2.0 and 1.0")
+                    module("org:b:2.0") {
+                        edge("org:a:1.0", "org:a:2.0")
+                    }
+                }
+            }
+        }
+    }
+
+    @Issue("GRADLE-2555")
+    void "can deal with transitive with parent in conflict"() {
+        /*
+            Graph looks like…
+
+            \--- org:a:1.0
+                 \--- org:in-conflict:1.0 -> 2.0
+                      \--- org:target:1.0
+                           \--- org:target-child:1.0
+            \--- org:b:1.0
+                 \--- org:b-child:1.0
+                      \--- org:in-conflict:2.0 (*)
+
+            This is the simplest structure I could boil it down to that produces the error.
+            - target *must* have a child
+            - Having "b" depend directly on "in-conflict" does not produce the error, needs to go through "b-child"
+         */
+
+        mavenRepo.module("org", "target-child", "1.0").publish()
+        mavenRepo.module("org", "target", "1.0").dependsOn("org", "target-child", "1.0").publish()
+        mavenRepo.module("org", "in-conflict", "1.0").dependsOn("org", "target", "1.0").publish()
+        mavenRepo.module("org", "in-conflict", "2.0").dependsOn("org", "target", "1.0").publish()
+
+        mavenRepo.module("org", "a", '1.0').dependsOn("org", "in-conflict", "1.0").publish()
+
+        mavenRepo.module("org", "b-child", '1.0').dependsOn("org", "in-conflict", "2.0").publish()
+
+        mavenRepo.module("org", "b", '1.0').dependsOn("org", "b-child", "1.0").publish()
+
+        buildFile << """
+            repositories { maven { url "${mavenRepo.uri}" } }
+
+            configurations { compile }
+
+            dependencies {
+                compile "org:a:1.0", "org:b:1.0"
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:in-conflict:1.0", "org:in-conflict:2.0") {
+                        byConflictResolution("between versions 2.0 and 1.0")
+                    }
+                }
+                module("org:b:1.0") {
+                    module("org:b-child:1.0") {
+                        module("org:in-conflict:2.0") {
+                            module("org:target:1.0") {
+                                module("org:target-child:1.0")
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Issue("GRADLE-2555")
+    void "batched up conflicts with conflicted parent and child"() {
+        /*
+        Dependency tree:
+
+        a->c1
+        b->c2->x1
+        d->x1
+        f->x2
+
+        Everything is resolvable but not x2
+
+        Scenario:
+         - We have batched up conflicts
+         - root of one conflicted version is also conflicted
+         - conflicted root is positioned on the conflicts queue after the conflicted child (the order of declaring dependencies matters)
+         - winning root depends on a child that previously was evicted
+         - finally, the winning child is an unresolved dependency
+        */
+        mavenRepo.module("org", "c", '1.0').publish()
+        mavenRepo.module("org", "x", '1.0').publish()
+        mavenRepo.module("org", "c", '2.0').dependsOn("org", "x", '1.0').publish()
+        mavenRepo.module("org", "a").dependsOn("org", "c", "1.0").publish()
+        mavenRepo.module("org", "b").dependsOn("org", "c", "2.0").publish()
+        mavenRepo.module("org", "d").dependsOn("org", "x", "1.0").publish()
+        mavenRepo.module("org", "f").dependsOn("org", "x", "2.0").publish()
+
+        buildFile << """
+            repositories { maven { url "${mavenRepo.uri}" } }
+            configurations {
+                childFirst
+                parentFirst
+            }
+            dependencies {
+                //conflicted child is resolved first
+                childFirst 'org:d:1.0', 'org:f:1.0', 'org:a:1.0', 'org:b:1.0'
+                //conflicted parent is resolved first
+                parentFirst 'org:a:1.0', 'org:b:1.0', 'org:d:1.0', 'org:f:1.0'
+            }
+        """
+
+        when:
+        run("dependencies")
+
+        then:
+        output.contains """
+childFirst
++--- org:d:1.0
+|    \\--- org:x:1.0 -> 2.0 FAILED
++--- org:f:1.0
+|    \\--- org:x:2.0 FAILED
++--- org:a:1.0
+|    \\--- org:c:1.0 -> 2.0
+|         \\--- org:x:1.0 -> 2.0 FAILED
+\\--- org:b:1.0
+     \\--- org:c:2.0 (*)
+
+parentFirst
++--- org:a:1.0
+|    \\--- org:c:1.0 -> 2.0
+|         \\--- org:x:1.0 -> 2.0 FAILED
++--- org:b:1.0
+|    \\--- org:c:2.0 (*)
++--- org:d:1.0
+|    \\--- org:x:1.0 -> 2.0 FAILED
+\\--- org:f:1.0
+     \\--- org:x:2.0 FAILED"""
+    }
+
+    @Issue("GRADLE-2752")
+    void "selects root module when earlier version of module requested"() {
+        mavenRepo.module("org", "test", "1.2").publish()
+        mavenRepo.module("org", "other", "1.7").dependsOn("org", "test", "1.2").publish()
+
+        buildFile << """
+apply plugin: 'java'
+
+group "org"
+version "1.3"
+
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+dependencies {
+    implementation "org:other:1.7"
+}
+"""
+
+        def resolve = new ResolveTestFixture(buildFile).expectDefaultConfiguration("runtime")
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", "org:test:1.3") {
+                module("org:other:1.7") {
+                    edge("org:test:1.2", "org:test:1.3")
+                }
+            }
+        }
+    }
+
+    @Issue("GRADLE-2920")
+    void "selects later version of root module when requested"() {
+        mavenRepo.module("org", "test", "2.1").publish()
+        mavenRepo.module("org", "other", "1.7").dependsOn("org", "test", "2.1").publish()
+
+        buildFile << """
+apply plugin: 'java'
+
+group "org"
+version "1.3"
+
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+
+dependencies {
+    implementation "org:other:1.7"
+}
+"""
+
+        def resolve = new ResolveTestFixture(buildFile).expectDefaultConfiguration("runtime")
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", "org:test:1.3") {
+                module("org:other:1.7") {
+                    module("org:test:2.1").byConflictResolution("between versions 2.1 and 1.3")
+                }
+            }
+        }
+    }
+
+    void "module is required only by selected conflicting version and in turn requires evicted conflicting version"() {
+        /*
+            a2 -> b1 -> c1
+            a1
+            c2
+         */
+        mavenRepo.module("org", "a", "1").publish()
+        mavenRepo.module("org", "a", "2").dependsOn("org", "b", "1").publish()
+        mavenRepo.module("org", "b", "1").dependsOn("org", "c", "1").publish()
+        mavenRepo.module("org", "c", "1").publish()
+        mavenRepo.module("org", "c", "2").publish()
+
+        buildFile << """
+repositories {
+    maven { url "${mavenRepo.uri}" }
+}
+configurations {
+    compile
+}
+dependencies {
+    compile "org:a:2"
+    compile "org:a:1"
+    compile "org:c:2"
+}
+"""
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:2") {
+                    byConflictResolution("between versions 2 and 1")
+                }
+                edge("org:a:1", "org:a:2") {
+                    module("org:b:1") {
+                        edge("org:c:1", "org:c:2") {
+                            byConflictResolution("between versions 2 and 1")
+                        }
+                    }
+                }
+                module("org:c:2")
+            }
+        }
+    }
+
+    @Issue("GRADLE-2738")
+    def "resolution fails when any selector cannot be resolved"() {
+        given:
+        //only 1.5 published:
+        mavenRepo.module("org", "leaf", "1.5").publish()
+
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "(,1.0)").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "1.0").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[1.5,1.9]").publish()
+        mavenRepo.module("org", "d", "1.0").dependsOn("org", "leaf", "2.0+").publish()
+
+        buildFile << """
+            version = 12
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf
+            }
+            dependencies {
+                conf 'org:a:1.0', 'org:b:1.0', 'org:c:1.0', 'org:d:1.0'
+            }
+            task resolve {
+                def files = configurations.conf
+                doLast {
+                    files.files
+                }
+            }
+            task checkGraph {
+                def result = configurations.conf.incoming.resolutionResult.rootComponent
+                doLast {
+                    def root = result.get()
+                    def components = root.dependencies.collect { it.selected }
+                    def a = components.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'a' }
+                    def b = components.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'b' }
+                    def c = components.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'c' }
+                    def d = components.find { it.id instanceof ModuleComponentIdentifier && it.id.module == 'd' }
+
+                    a.dependencies.each {
+                        assert it instanceof UnresolvedDependencyResult
+                        assert it.requested.toString() == 'org:leaf:(,1.0)'
+                        assert it.failure.getMessage().startsWith('Could not find any version that matches org:leaf:(,1.0).')
+                    }
+                    b.dependencies.each {
+                        assert it instanceof ResolvedDependencyResult
+                        assert it.requested.toString() == 'org:leaf:1.0'
+                        assert it.selected.id.module == 'leaf'
+                    }
+                    c.dependencies.each {
+                        assert it instanceof ResolvedDependencyResult
+                        assert it.requested.toString() == 'org:leaf:[1.5,1.9]'
+                        assert it.selected.id.module == 'leaf'
+                    }
+                    d.dependencies.each {
+                        assert it instanceof UnresolvedDependencyResult
+                        assert it.requested.toString() == 'org:leaf:2.0+'
+                        assert it.failure.getMessage().startsWith('Could not find any version that matches org:leaf:2.0+.')
+                    }
+                }
+            }
+        """
+
+        when:
+        succeeds "checkGraph"
+
+        and:
+        runAndFail "resolve"
+
+        then:
+        failure.assertResolutionFailure(":conf").assertFailedDependencyRequiredBy("project : > org:d:1.0")
+    }
+
+    def "chooses highest version that is included in both ranges"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,6]", "org:leaf:6") {
+                        byReason("didn't match versions 10, 9, 8, 7")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[4,8]", "org:leaf:6")
+                }
+            }
+        }
+    }
+
+    def "chooses highest version that is included in both ranges, with the highest version in the intersection missing"() {
+        given:
+        (1..10).findAll {
+            // We skip v6, as we test what happens when the top version of the intersection is missing
+            it != 6
+        }.each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,6]", "org:leaf:5") {
+                        byReason("didn't match versions 10, 9, 8, 7")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[4,8]", "org:leaf:5")
+                }
+            }
+        }
+    }
+
+    def "chooses highest version that is included in both ranges when fail on conflict is set"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile {
+                    resolutionStrategy {
+                        failOnVersionConflict()
+                    }
+                }
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,6]", "org:leaf:6") {
+                        byReason("didn't match versions 10, 9, 8, 7")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[4,8]", "org:leaf:6")
+                }
+            }
+        }
+    }
+
+    def "chooses highest version that is included in all ranges"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[3,5]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,6]", "org:leaf:5") {
+                        byReason("didn't match versions 10, 9, 8, 7")
+                        byReason("didn't match versions 10, 9, 8, 7, 6")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[4,8]", "org:leaf:5")
+                }
+                module("org:c:1.0") {
+                    edge("org:leaf:[3,5]", "org:leaf:5")
+                }
+            }
+        }
+    }
+
+    def "chooses highest version that is included in all ranges, when dependencies are included at different transitivity levels"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        // top level
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+
+        // b will include 'leaf' through a transitive dependency
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "b2", "1.0").publish()
+        mavenRepo.module("org", "b2", "1.0").dependsOn("org", "leaf", "[3,5]").publish()
+
+        // c will include 'leaf' through a 2d level transitive dependency
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "c2", "1.0").publish()
+        mavenRepo.module("org", "c2", "1.0").dependsOn("org", "c3", "1.0").publish()
+        mavenRepo.module("org", "c3", "1.0").dependsOn("org", "leaf", "[3,5]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,6]", "org:leaf:5") {
+                        notRequested()
+                        byReason("didn't match versions 10, 9, 8, 7")
+                        byReason("didn't match versions 10, 9, 8, 7, 6")
+                    }
+                }
+                module("org:b:1.0") {
+                    module("org:b2:1.0") {
+                        edge("org:leaf:[3,5]", "org:leaf:5")
+                    }
+                }
+                module("org:c:1.0") {
+                    module("org:c2:1.0") {
+                        module("org:c3:1.0") {
+                            edge("org:leaf:[3,5]", "org:leaf:5")
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    def "upgrades version when ranges are disjoint"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,3]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[5,8]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,3]", "org:leaf:8") {
+                        notRequested()
+                        byReason("didn't match versions 10, 9")
+                        byReason("didn't match versions 10, 9, 8, 7, 6, 5, 4")
+                        byConflictResolution("between versions 3 and 8")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[5,8]", "org:leaf:8")
+                }
+            }
+        }
+    }
+
+    def "fail when ranges are disjoint and no top range artifact is present"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[1,5]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[11,15]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf
+            }
+            dependencies {
+                conf 'org:a:1.0', 'org:b:1.0'
+            }
+            task checkDeps {
+                def files = configurations.conf
+                doLast {
+                    files.files
+                }
+            }
+        """
+
+        when:
+        fails 'checkDeps'
+
+        then:
+        failure.assertThatCause(containsString("Could not find any version that matches org:leaf:[11,15]."))
+    }
+
+    def "upgrades version when ranges are disjoint unless failOnVersionConflict is set"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,3]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[5,8]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf {
+                    resolutionStrategy.failOnVersionConflict()
+                }
+            }
+            dependencies {
+                conf 'org:a:1.0', 'org:b:1.0'
+            }
+            task checkDeps {
+                def files = configurations.conf
+                doLast {
+                    files.files
+                }
+            }
+        """
+
+        when:
+        fails 'checkDeps'
+
+        then:
+        failure.assertThatCause(containsString(CONFLICT_FOUND_HEADER_MESSAGE))
+    }
+
+    def "upgrades version when one of the ranges is disjoint"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[3,4]").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[7,8]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,6]", "org:leaf:8") {
+                        notRequested()
+                        byReason("didn't match versions 10, 9")
+                        byReason("didn't match versions 10, 9, 8, 7")
+                        byReason("didn't match versions 10, 9, 8, 7, 6, 5")
+                        byConflictResolution("between versions 8 and 4")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[3,4]", "org:leaf:8")
+                }
+                module("org:c:1.0") {
+                    edge("org:leaf:[7,8]", "org:leaf:8")
+                }
+            }
+        }
+    }
+
+    def "fails when one of the ranges is disjoint and fail on conflict is set"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[3,4]").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[7,8]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf {
+                    resolutionStrategy {
+                        failOnVersionConflict()
+                    }
+                }
+            }
+            dependencies {
+                conf 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
+            }
+            task checkDeps {
+                def files = configurations.conf
+                doLast {
+                    files.files
+                }
+            }
+        """
+
+        when:
+        fails 'checkDeps'
+
+        then:
+        failure.assertThatCause(containsString(CONFLICT_FOUND_HEADER_MESSAGE))
+    }
+
+    def "chooses highest version of all versions fully included within range"() {
+        given:
+        (1..12).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[1,12]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[3,8]").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[2,10]").publish()
+        mavenRepo.module("org", "d", "1.0").dependsOn("org", "leaf", "[4,7]").publish()
+        mavenRepo.module("org", "e", "1.0").dependsOn("org", "leaf", "[4,11]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0', 'org:d:1.0', 'org:e:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[1,12]", "org:leaf:7") {
+                        byReason("didn't match versions 12, 11")
+                        byReason("didn't match versions 12, 11, 10, 9")
+                        byReason("didn't match versions 12, 11, 10, 9, 8")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[3,8]", "org:leaf:7")
+                }
+                module("org:c:1.0") {
+                    edge("org:leaf:[2,10]", "org:leaf:7")
+                }
+                module("org:d:1.0") {
+                    edge("org:leaf:[4,7]", "org:leaf:7")
+                }
+                module("org:e:1.0") {
+                    edge("org:leaf:[4,11]", "org:leaf:7")
+                }
+            }
+        }
+    }
+
+    def "selects the minimal version when in there's an open range"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[5,)").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,6]", "org:leaf:6") {
+                        byReason("didn't match versions 10, 9, 8, 7")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[5,)", "org:leaf:6")
+                }
+            }
+        }
+    }
+
+    def "range selector should not win over sub-version selector"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "1.$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[1.2,1.6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "1.+").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[1.2,1.6]", "org:leaf:1.10") {
+                        byConflictResolution("between versions 1.6 and 1.10")
+                        byReason("didn't match versions 1.10, 1.9, 1.8, 1.7")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:1.+", "org:leaf:1.10")
+                }
+            }
+        }
+    }
+
+    def "range conflict resolution not interfering between distinct configurations"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[3,5]").publish()
+        mavenRepo.module("org", "d", "1.0").dependsOn("org", "leaf", "8").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf
+                conf2
+                conf3
+                conf4
+            }
+            dependencies {
+                conf 'org:a:1.0', 'org:b:1.0'
+                conf2 'org:a:1.0', 'org:c:1.0'
+                conf3 'org:b:1.0', 'org:c:1.0'
+                conf4 'org:b:1.0', 'org:c:1.0', 'org:d:1.0'
+            }
+            task checkDeps {
+                def files1 = configurations.conf
+                def files2 = configurations.conf2
+                def files3 = configurations.conf3
+                def files4 = configurations.conf4
+                doLast {
+                    def files = files1*.name.sort()
+                    assert files == ['a-1.0.jar', 'b-1.0.jar', 'leaf-6.jar']
+                    files = files2*.name.sort()
+                    assert files == ['a-1.0.jar', 'c-1.0.jar', 'leaf-5.jar']
+                    files = files3*.name.sort()
+                    assert files == ['b-1.0.jar', 'c-1.0.jar', 'leaf-5.jar']
+                    files = files4*.name.sort()
+                    assert files == ['b-1.0.jar', 'c-1.0.jar', 'd-1.0.jar', 'leaf-8.jar']
+                }
+            }
+        """
+
+        when:
+        run 'checkDeps'
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "conflict resolution on different dependencies are handled separately"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "leaf", "$it").publish()
+            mavenRepo.module("org", "leaf2", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "leaf", "[2,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "leaf", "[4,8]").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf2", "[3,4]").publish()
+        mavenRepo.module("org", "d", "1.0").dependsOn("org", "leaf2", "[1,7]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0', 'org:d:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[2,6]", "org:leaf:6") {
+                        byReason("didn't match versions 10, 9, 8, 7")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:leaf:[4,8]", "org:leaf:6")
+                }
+                module("org:c:1.0") {
+                    edge("org:leaf2:[3,4]", "org:leaf2:4") {
+                        byReason("didn't match versions 10, 9, 8, 7, 6, 5")
+                    }
+                }
+                module("org:d:1.0") {
+                    edge("org:leaf2:[1,7]", "org:leaf2:4")
+                }
+            }
+        }
+    }
+
+    def "previously selected transitive dependency is not used when it becomes orphaned because of selection of a different version of its dependent module"() {
+        given:
+        (1..10).each {
+            def dep = mavenRepo.module('org', 'zdep', "$it").publish()
+            mavenRepo.module("org", "leaf", "$it").dependsOn(dep).publish()
+        }
+        mavenRepo.module("org", "a", "1.0")
+            .dependsOn("org", "c", "1.0")
+            .dependsOn('org', 'leaf', '[1,8]')
+            .publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "c", "1.1").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "leaf", "[3,4]").publish()
+        mavenRepo.module("org", "c", "1.1").dependsOn("org", "leaf", "[4,6]").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1.0") {
+                    edge("org:leaf:[1,8]", "org:leaf:6") {
+                        notRequested()
+                        byReason("didn't match versions 10, 9")
+                        byReason("didn't match versions 10, 9, 8, 7")
+                        module("org:zdep:6")
+                    }
+                    edge("org:c:1.0", "org:c:1.1") {
+                        byConflictResolution("between versions 1.1 and 1.0")
+                        edge("org:leaf:[4,6]", "org:leaf:6")
+                    }
+                }
+                module("org:b:1.0") {
+                    module("org:c:1.1")
+                }
+            }
+        }
+    }
+
+    def "evicted version removes range constraint from transitive dependency"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "e", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "e", "[3,6]").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "e", "[1,10]").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "a", "2.0").publish()
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", "e", "[4,8]").publish()
+
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1.0", "org:a:2.0") {
+                    byConflictResolution("between versions 2.0 and 1.0")
+                    edge("org:e:[4,8]", "org:e:8") {
+                        byReason("didn't match versions 10, 9")
+                    }
+                }
+                module("org:b:1.0") {
+                    edge("org:e:[1,10]", "org:e:8")
+                }
+                module("org:c:1.0") {
+                    module("org:a:2.0")
+                }
+            }
+        }
+    }
+
+    def "orphan node can be re-selected later with a non short-circuiting selector"() {
+        given:
+        (1..10).each {
+            mavenRepo.module("org", "e", "$it").publish()
+        }
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "e", "10").publish()
+        mavenRepo.module("org", "b", "1.0").dependsOn("org", "a", "2.0").publish()
+        mavenRepo.module("org", "a", "2.0").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn("org", "d", "1.0").publish()
+        mavenRepo.module("org", "d", "1.0").dependsOn("org", "e", "latest.release").publish()
+
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:b:1.0', 'org:c:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1.0", "org:a:2.0") {
+                    byConflictResolution("between versions 2.0 and 1.0")
+                }
+                module("org:b:1.0") {
+                    module("org:a:2.0")
+                }
+                module("org:c:1.0") {
+                    module("org:d:1.0") {
+                        edge("org:e:latest.release", "org:e:10")
+                    }
+                }
+            }
+        }
+    }
+
+    def "presence of evicted and orphan node for a module do not fail selection"() {
+        given:
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "b", "1.0").publish()
+        mavenRepo.module("org", "b", "1.0").publish()
+        mavenRepo.module("org", "b", "2.0").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn('org', 'b', '2.0').publish()
+        mavenRepo.module("org", "d", "1.0").dependsOn("org", 'e', '1.0').publish()
+        mavenRepo.module("org", "e", "1.0").dependsOn("org", 'a', '2.0').dependsOn('org', 'c', '1.0').publish()
+        mavenRepo.module("org", "a", "2.0").dependsOn("org", 'c', '2.0').publish()
+        mavenRepo.module('org', 'c', '2.0').publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:c:1.0', 'org:d:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1.0", "org:a:2.0") {
+                    byConflictResolution("between versions 2.0 and 1.0")
+                    module("org:c:2.0") {
+                        byConflictResolution("between versions 2.0 and 1.0")
+                    }
+                }
+                edge("org:c:1.0", "org:c:2.0")
+                module("org:d:1.0") {
+                    module("org:e:1.0") {
+                        module("org:a:2.0")
+                        edge("org:c:1.0", "org:c:2.0")
+                    }
+                }
+            }
+        }
+    }
+
+    def "can have a dependency on evicted node"() {
+        given:
+        mavenRepo.module("org", "a", "1.0").dependsOn("org", "b", "1.0").publish()
+        mavenRepo.module("org", "b", "1.0").publish()
+        mavenRepo.module("org", "c", "1.0").dependsOn('org', 'a', '1.0').publish()
+        mavenRepo.module('org', 'd', '1.0').dependsOn('org', 'a', '2.0').publish()
+        mavenRepo.module('org', 'a', '2.0').publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1.0', 'org:c:1.0', 'org:d:1.0'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1.0", "org:a:2.0") {
+                    byConflictResolution("between versions 2.0 and 1.0")
+                }
+                module("org:c:1.0") {
+                    edge("org:a:1.0", "org:a:2.0")
+                }
+                module("org:d:1.0") {
+                    module("org:a:2.0")
+                }
+            }
+        }
+    }
+
+    def "evicted hard dependency shouldn't add constraint on range"() {
+        given:
+        4.times { mavenRepo.module("org", "e", "${it + 1}").publish() }
+        4.times { mavenRepo.module("org", "a", "${it + 1}").dependsOn('org', 'e', "${it + 1}").publish() }
+        mavenRepo.module("org", "b", "1").dependsOn('org', 'a', '4').publish() // this will be evicted
+        mavenRepo.module('org', 'c', '1').dependsOn('org', 'd', '1').publish()
+        mavenRepo.module('org', 'd', '1').dependsOn('org', 'b', '2').publish()
+        mavenRepo.module('org', 'b', '2').publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:[1,3]', 'org:b:1', 'org:c:1'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:[1,3]", "org:a:3") {
+                    notRequested()
+                    byReason("didn't match version 4")
+                    module("org:e:3")
+                }
+                edge("org:b:1", "org:b:2") {
+                    byConflictResolution("between versions 2 and 1")
+                }
+                module("org:c:1") {
+                    module("org:d:1") {
+                        module("org:b:2")
+                    }
+                }
+            }
+        }
+    }
+
+    def "evicted hard dependency shouldn't add constraint on version"() {
+        given:
+        mavenRepo.module("org", "a", "1").publish()
+        mavenRepo.module("org", "a", "4").publish()
+        mavenRepo.module("org", "b", "1").dependsOn('org', 'a', '4').publish() // this will be evicted
+        mavenRepo.module('org', 'b', '2').publish()
+        mavenRepo.module('org', 'c', '1').dependsOn('org', 'd', '1').publish()
+        mavenRepo.module('org', 'd', '1').dependsOn('org', 'b', '2').publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1', 'org:b:1', 'org:c:1'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module("org:a:1")
+                edge("org:b:1", "org:b:2") {
+                    byConflictResolution("between versions 2 and 1")
+                }
+                module("org:c:1") {
+                    module("org:d:1") {
+                        module("org:b:2")
+                    }
+                }
+            }
+        }
+    }
+
+    def "doesn't include evicted version from branch which has been deselected"() {
+        given:
+        mavenRepo.module('org', 'a', '1').dependsOn('org', 'b', '2').publish()
+        mavenRepo.module('org', 'b', '1').publish()
+        mavenRepo.module('org', 'b', '2').publish()
+        mavenRepo.module('org', 'c', '1').dependsOn('org', 'a', '2').publish()
+        mavenRepo.module('org', 'a', '2').publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                compile
+            }
+            dependencies {
+                compile 'org:a:1', 'org:b:1', 'org:c:1'
+            }
+        """
+        resolve.prepare()
+
+        when:
+        run("checkDeps")
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                edge("org:a:1", "org:a:2") {
+                    byConflictResolution("between versions 2 and 1")
+                }
+                module("org:b:1")
+                module("org:c:1") {
+                    module("org:a:2")
+                }
+            }
+        }
+    }
+
+    def 'order of dependency declaration does not effect transitive dependency versions'() {
+        given:
+        def foo11 = mavenRepo.module('org', 'foo', '1.1').publish()
+        def foo12 = mavenRepo.module('org', 'foo', '1.2').publish()
+        def baz11 = mavenRepo.module('org', 'baz', '1.1').dependsOn(foo11).publish()
+        mavenRepo.module('org', 'baz', '1.2').dependsOn(foo12).publish()
+        mavenRepo.module('org', 'bar', '1.1').dependsOn(baz11).publish()
+
+        ResolveTestFixture resolve = new ResolveTestFixture(buildFile, "conf").expectDefaultConfiguration("runtime")
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf
+            }
+            dependencies {
+                if ($barFirst) {
+                    conf 'org:bar:1.1' // WORKS IF THIS DEPENDENCY IS FIRST
+                }
+                conf 'org:baz:[1.0,2.0)'
+                if (!$barFirst) {
+                    conf 'org:bar:1.1' // FAILED IF HERE
+                }
+                conf 'org:foo:[1.0,2.0)'
+            }
+"""
+        resolve.prepare()
+
+        when:
+        run 'dependencies', 'checkDeps'
+
+        then:
+        resolve.expectGraph {
+            root(":", ":test:") {
+                module('org:bar:1.1') {
+                    module('org:baz:1.1') {
+                        module('org:foo:1.1')
+                    }
+                }
+                edge("org:foo:[1.0,2.0)", 'org:foo:1.1')
+                edge('org:baz:[1.0,2.0)', 'org:baz:1.1')
+            }
+        }
+
+        where:
+        barFirst << [false, true]
+    }
+
+    @Issue("gradle/gradle-private#1268")
+    def "shouldn't fail if root component is also added through cycle, and that failOnVersionConflict() is used"() {
+        settingsFile << """
+            include "testlib", "common"
+        """
+
+        buildFile << """
+            subprojects {
+                apply plugin: 'java-library'
+                configurations.all {
+                   resolutionStrategy.failOnVersionConflict()
+                }
+            }
+        """
+
+        file("testlib/build.gradle") << """
+            dependencies {
+                api project(':common') // cycle causes resolution to fail, but shouldn't
+            }
+        """
+
+        file("common/build.gradle") << """
+            dependencies {
+                testImplementation project(':testlib')
+            }
+        """
+
+        when:
+        run 'common:dependencies', '--configuration', 'testCompileClasspath'
+
+        then:
+        noExceptionThrown()
+    }
+
+    @Issue("gradle/gradle#6403")
+    def "shouldn't fail when forcing a dynamic version in resolution strategy"() {
+
+        given:
+        mavenRepo.module("org", "moduleA", "1.1").publish()
+
+        buildFile << """
+            repositories {
+                maven { url "${mavenRepo.uri}" }
+            }
+            configurations {
+                conf {
+                   resolutionStrategy {
+                      force "org:moduleA:1.+"
+                      failOnVersionConflict()
+                   }
+                }
+            }
+
+            dependencies {
+               conf("org:moduleA:1.+")
+               conf("org:moduleA:1.1")
+            }
+        """
+
+        when:
+        run 'dependencies', '--configuration', 'conf'
+
+        then:
+        noExceptionThrown()
+
+
+    }
+
+    def 'optional dependency marked as no longer pending reverts to pending if hard edge disappears (remover has constraint: #dependsOptional, root has constraint: #constraintsOptional)'() {
+        given:
+        def optional = mavenRepo.module('org', 'optional', '1.0').publish()
+        def main = mavenRepo.module('org', 'main', '1.0').dependsOn(optional, optional: true).publish()
+        mavenRepo.module('org.a', 'root', '1.0').dependsOn(main).dependsOn(optional).publish()
+        def root11 = mavenRepo.module('org.a', 'root', '1.1').dependsOn(main).publish()
+        def bom = mavenRepo.module("org", "bom", "1.0")
+        bom.hasPackaging('pom')
+        bom.dependencyConstraint(root11)
+        if (dependsOptional) {
+            bom.dependencyConstraint(optional)
+        }
+        bom.publish()
+
+        buildFile << """
+apply plugin: 'java'
+
+repositories {
+    maven {
+        name 'repo'
+        url '${mavenRepo.uri}'
+    }
+}
+
+dependencies {
+    implementation 'org.a:root'
+    implementation platform('org:bom:1.0')
+    constraints {
+        implementation 'org.a:root:1.0'
+        if ($constraintsOptional) {
+            implementation 'org:optional:1.0'
+        }
+    }
+}
+"""
+        when:
+        succeeds 'dependencies', '--configuration', 'compileClasspath'
+
+        then:
+        outputDoesNotContain('org:optional')
+
+        where:
+        dependsOptional | constraintsOptional
+        true            | true
+        true            | false
+        false           | true
+        false           | false
+    }
+
+    @Issue("gradle/gradle#8944")
+    def 'verify that cleaning up constraints no longer causes a ConcurrentModificationException'() {
+        given:
+        // Direct dependency with transitive to be substituted by project
+        def project = mavenRepo.module('org', 'project', '1.0')
+        mavenRepo.module('org', 'direct', '1.0').dependsOn(project).publish()
+
+        // Updated version no longer depends on project
+        def updated = mavenRepo.module('org', 'direct', '1.1').publish()
+
+        // Chain of deps to make sure upgrade happens after substituting and finding deps
+        def b = mavenRepo.module('org', 'b', '1.0').dependsOn(updated).publish()
+        mavenRepo.module('org', 'a', '1.0').dependsOn(b).publish()
+
+        mavenRepo.module('org', 'lib', '1.0').publish()
+        mavenRepo.module('org', 'other', '1.0').publish()
+
+        settingsFile << """
+include 'sub'
+"""
+
+        buildFile << """
+apply plugin: 'java'
+
+repositories {
+    maven {
+        name 'repo'
+        url '${mavenRepo.uri}'
+    }
+}
+
+configurations.all {
+    resolutionStrategy.dependencySubstitution {
+        substitute module('org:project') using project(':sub')
+    }
+}
+
+dependencies {
+    implementation 'org:direct:1.0'
+    implementation 'org:a:1.0'
+}
+
+project(':sub') {
+    apply plugin: 'java'
+
+    group = 'org'
+    version = '1.0'
+
+    dependencies {
+        constraints {
+            implementation 'org:lib:1.0'
+        }
+
+        implementation 'org:lib'
+        implementation 'org:other:1.0'
+    }
+}
+"""
+        expect:
+        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
+    }
+
+    @Issue("gradle/gradle#11844")
+    def 'does not fail serialization in recursive error case'() {
+        // org:lib:1.0 -> org:between:1.0 -> org:lib:1.1
+        //
+        //  - org:lib:1.1 is selected
+        //  - removes org:between:1.0
+        //  - org:lib:1.1 stays selected (because of cycle), still internal state is updated partially and org:lib:1.1 selector is removed in some places
+
+        given:
+        def libUpdated = mavenRepo.module('org', 'lib', '1.1')
+        // depend on newer version of lib (libUpdated) that is not published, the internal state is broken and deserialization expects 1.1 to exist
+        def between = mavenRepo.module('org', 'between', '1.0').dependsOn(libUpdated).publish()
+        mavenRepo.module('org', 'lib', '1.0').dependsOn(between).publish()
+
+        buildFile << """
+            apply plugin: 'java-library'
+
+            repositories {
+                maven {
+                    url '${mavenRepo.uri}'
+                }
+            }
+
+            dependencies {
+                implementation 'org:lib:1.0'
+            }
+        """
+        expect:
+        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
+    }
+
+    def 'local cycle between dependencies does not causes a ConcurrentModificationException during selector removal'() {
+        given:
+        def lib2 = mavenRepo.module('org', 'lib', '2.0').publish()
+        def lib3 = mavenRepo.module('org', 'lib', '3.0').publish()
+        def lib1 = mavenRepo.module('org', 'lib', '1.0')
+        // recursive dependencies between different versions of 'lib'
+            .dependencyConstraint(lib3).dependencyConstraint(lib2).withModuleMetadata().publish()
+
+        mavenRepo.module('org', 'direct', '1.0').dependsOn(lib1).publish()
+
+        def directUpdated = mavenRepo.module('org', 'direct', '1.1').publish()
+        def b = mavenRepo.module('org', 'b', '1.0').dependsOn(directUpdated).publish()
+        mavenRepo.module('org', 'a', '1.0').dependsOn(b).publish()
+
+        buildFile << """
+            apply plugin: 'java-library'
+
+            repositories {
+                maven {
+                    url '${mavenRepo.uri}'
+                }
+            }
+
+            dependencies {
+                implementation 'org:direct:1.0'  // dependeincy on 'lib'
+                implementation 'org:a:1.0'       // updates direct (to remove dependency on 'lib' again)
+            }
+        """
+
+        expect:
+        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
+    }
+
+    def 'local cycle between dependencies does not causes a ConcurrentModificationException during selector removal with strict version endorsement'() {
+        given:
+        def direct11 = mavenRepo.module('org', 'direct', '1.1')
+        def betweenLibAndDirect = mavenRepo.module('org', 'betweenLibAndDirect', '1.0').dependsOn(direct11)
+        def lib2 = mavenRepo.module('org', 'lib', '2.0').publish()
+        def lib1 = mavenRepo.module('org', 'lib', '1.0').dependsOn(betweenLibAndDirect)
+        // recursive dependencies between different versions of 'lib'
+            .dependencyConstraint(lib2).withModuleMetadata().publish()
+        def lib05 = mavenRepo.module('org', 'lib', '0.5').publish()
+
+        mavenRepo.module('org', 'direct', '1.0')
+        // endorsing dependency that will cause a reselection of the parent again causing a reselection of other children
+        // ("looping back" if a another child is the same as the one that was endorsed)
+            .dependsOn([endorseStrictVersions: true], lib1).dependsOn(lib05).dependsOn(lib1).withModuleMetadata().publish()
+
+        buildFile << """
+            apply plugin: 'java-library'
+
+            repositories {
+                maven {
+                    url '${mavenRepo.uri}'
+                }
+            }
+
+            dependencies {
+                implementation 'org:direct:1.0'  // dependency on 'lib' which will istself update 'direct'
+            }
+        """
+
+        expect:
+        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
+    }
+
+    def 'local cycle between dependencies does not causes a ConcurrentModificationException during selector removal with multiple strict version endorsements'() {
+        given:
+        def foo2 = mavenRepo.module('org', 'foo', '2.0').publish()
+        def foo1 = mavenRepo.module('org', 'foo', '1.0')
+            .dependencyConstraint(foo2).withModuleMetadata().publish()
+        def foo05 = mavenRepo.module('org', 'foo', '0.5').publish()
+
+        def direct11 = mavenRepo.module('org', 'direct', '1.1')
+        def betweenLibAndDirect = mavenRepo.module('org', 'betweenLibAndDirect', '1.0').dependsOn(direct11)
+        def lib2 = mavenRepo.module('org', 'lib', '2.0').publish()
+        def lib1 = mavenRepo.module('org', 'lib', '1.0').dependsOn(betweenLibAndDirect)
+        // recursive dependencies between different versions of 'lib'
+            .dependencyConstraint(lib2).withModuleMetadata().publish()
+        def lib05 = mavenRepo.module('org', 'lib', '0.5').publish()
+
+        mavenRepo.module('org', 'direct', '1.0')
+        // endorsing dependency that will cause a reselection of the parent again causing a reselection of other children
+        // ("looping back" if a another child is the same as the one that was endorsed)
+            .dependsOn([endorseStrictVersions: true], lib1)
+            .dependsOn([endorseStrictVersions: true], foo1).dependsOn(lib05).dependsOn(lib1).dependsOn([endorseStrictVersions: true], foo05).withModuleMetadata().publish()
+
+        buildFile << """
+            apply plugin: 'java-library'
+
+            repositories {
+                maven {
+                    url '${mavenRepo.uri}'
+                }
+            }
+
+            dependencies {
+                implementation 'org:direct:1.0'  // dependency on 'lib' which will istself update 'direct'
+            }
+        """
+
+        expect:
+        succeeds 'dependencies', '--configuration', 'runtimeClasspath'
+    }
+
+    def "can resolve a graph with an obvious version cycle by breaking the cycle"() {
+        given:
+        def direct2 = mavenRepo.module('org', 'direct', '2.0').publish()
+        def trans = mavenRepo.module('org', 'transitive', '1.0').dependsOn(direct2).publish()
+        mavenRepo.module('org', 'direct', '1.0').dependsOn(trans).publish()
+
+        buildFile << """
+repositories {
+    maven {
+        name 'repo'
+        url '${mavenRepo.uri}'
+    }
+}
+
+configurations {
+    conf
+}
+
+dependencies {
+    conf "org:direct:1.0"
+}
+"""
+        expect:
+        succeeds 'dependencies', '--configuration', 'conf'
+    }
+
+    def "can resolve a graph with a local cycle caused by module replacement"() {
+        given:
+        def child1 = mavenRepo.module('org', 'child1', '1.0').publish()
+        def child2 = mavenRepo.module('org', 'child2', '1.0').publish()
+        mavenRepo.module('org', 'direct', '1.0').dependsOn(child1).dependsOn(child2).publish()
+
+        buildFile << """
+            repositories {
+                maven {
+                    name 'repo'
+                    url '${mavenRepo.uri}'
+                }
+            }
+
+            configurations {
+                conf
+            }
+
+            dependencies {
+                modules {
+                    module("org:child1") {
+                        replacedBy("org:direct")
+                    }
+                }
+                conf "org:direct:1.0"
+            }
+        """
+
+        expect:
+        succeeds 'dependencies', '--configuration', 'conf'
+    }
+
+    def 'does not cache node dependencies when node is deselected then reselected with different exclude filter'() {
+        given:
+        // Excluded module
+        def excluded = mavenRepo.module('org.test', 'excluded', '1.0').publish()
+
+        // Intermediates
+        def intermediate10 = mavenRepo.module('org.test', 'intermediate1', '1.0').dependsOn(excluded).publish()
+        def intermediate20 = mavenRepo.module('org.test', 'intermediate1', '2.0').dependsOn(excluded).publish()
+        def intermediate2 = mavenRepo.module('org.test', 'intermediate2', '1.0').dependsOn(intermediate10).publish()
+        def intermediate3 = mavenRepo.module('org.test', 'intermediate3', '1.0').dependsOn(intermediate2).publish()
+
+        // Aligned modules
+        def firstAligned = mavenRepo.module('org.aligned', 'aligned1', '1.0').dependsOn(intermediate20).publish()
+        mavenRepo.module('org.aligned', 'aligned1', '2.0').dependsOn(intermediate20).publish()
+        def otherAligned = mavenRepo.module('org.aligned', 'aligned2', '2.0').publish()
+
+        // Roots
+        mavenRepo.module('org.test', 'excludingRoot', '1.0').dependsOn(firstAligned, exclusions: [[group: 'org.test', module: 'excluded']]).publish()
+        mavenRepo.module('org.test', 'root2', '1.0').dependsOn(intermediate3).publish()
+        mavenRepo.module('org.test', 'root3', '1.0').dependsOn(otherAligned).publish()
+
+        buildFile << """
+            repositories {
+                maven {
+                    name 'repo'
+                    url '${mavenRepo.uri}'
+                }
+            }
+
+            configurations {
+                conf
+            }
+
+            dependencies {
+                conf 'org.test:excludingRoot:1.0'
+                conf 'org.test:root2:1.0'
+                conf 'org.test:root3:1.0'
+
+                components.all(AlignGroup.class)
+            }
+
+            class AlignGroup implements ComponentMetadataRule {
+                void execute(ComponentMetadataContext ctx) {
+                    ctx.details.with { it ->
+                        if (it.getId().getGroup().startsWith("org.aligned")) {
+                            it.belongsTo("org.aligned:platform:\${it.getId().getVersion()}")
+                        }
+                    }
+                }
+            }
+"""
+        when:
+        succeeds 'dependencies', '--configuration', 'conf'
+
+        then:
+        outputContains('excluded')
+    }
+
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePairBatch1IntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePairBatch1IntegrationTest.groovy
new file mode 100644
index 0000000..ea695f0
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePairBatch1IntegrationTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
+import spock.lang.IgnoreIf
+
+@IgnoreIf({
+    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
+    // infrastructure to simulate everything done here, so we're only going to execute this test in
+    // embedded mode
+    !GradleContextualExecuter.embedded
+})
+class VersionRangeResolvePairBatch1IntegrationTest extends AbstractVersionRangeResolveIntegrationTest {
+    def "resolve pair #permutation"() {
+        given:
+        def candidates = permutation.candidates
+        def expectedSingle = permutation.expectedSingle
+        def expectedMulti = permutation.expectedMulti
+
+        expect:
+        checkScenarioResolution(expectedSingle, expectedMulti, candidates)
+
+        where:
+        permutation << VersionRangeResolveTestScenarios.SCENARIOS_TWO_DEPENDENCIES_BATCH1
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePairBatch2IntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePairBatch2IntegrationTest.groovy
new file mode 100644
index 0000000..5921639
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePairBatch2IntegrationTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
+import spock.lang.IgnoreIf
+
+@IgnoreIf({
+    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
+    // infrastructure to simulate everything done here, so we're only going to execute this test in
+    // embedded mode
+    !GradleContextualExecuter.embedded
+})
+class VersionRangeResolvePairBatch2IntegrationTest extends AbstractVersionRangeResolveIntegrationTest {
+    def "resolve pair #permutation"() {
+        given:
+        def candidates = permutation.candidates
+        def expectedSingle = permutation.expectedSingle
+        def expectedMulti = permutation.expectedMulti
+
+        expect:
+        checkScenarioResolution(expectedSingle, expectedMulti, candidates)
+
+        where:
+        permutation << VersionRangeResolveTestScenarios.SCENARIOS_TWO_DEPENDENCIES_BATCH2
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePreferPairBatch1IntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePreferPairBatch1IntegrationTest.groovy
new file mode 100644
index 0000000..29aa9c0
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePreferPairBatch1IntegrationTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
+import spock.lang.IgnoreIf
+
+@IgnoreIf({
+    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
+    // infrastructure to simulate everything done here, so we're only going to execute this test in
+    // embedded mode
+    !GradleContextualExecuter.embedded
+})
+class VersionRangeResolvePreferPairBatch1IntegrationTest extends AbstractVersionRangeResolveIntegrationTest {
+    def "resolve prefer pair #permutation"() {
+        given:
+        def candidates = permutation.candidates
+        def expectedSingle = permutation.expectedSingle
+        def expectedMulti = permutation.expectedMulti
+
+        expect:
+        checkScenarioResolution(expectedSingle, expectedMulti, candidates)
+
+        where:
+        permutation << VersionRangeResolveTestScenarios.SCENARIOS_PREFER_BATCH1
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePreferPairBatch2IntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePreferPairBatch2IntegrationTest.groovy
new file mode 100644
index 0000000..3109dec
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolvePreferPairBatch2IntegrationTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
+import spock.lang.IgnoreIf
+
+@IgnoreIf({
+    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
+    // infrastructure to simulate everything done here, so we're only going to execute this test in
+    // embedded mode
+    !GradleContextualExecuter.embedded
+})
+class VersionRangeResolvePreferPairBatch2IntegrationTest extends AbstractVersionRangeResolveIntegrationTest {
+    def "resolve prefer pair #permutation"() {
+        given:
+        def candidates = permutation.candidates
+        def expectedSingle = permutation.expectedSingle
+        def expectedMulti = permutation.expectedMulti
+
+        expect:
+        checkScenarioResolution(expectedSingle, expectedMulti, candidates)
+
+        where:
+        permutation << VersionRangeResolveTestScenarios.SCENARIOS_PREFER_BATCH2
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolveRejectPairIntegrationTest.groovy b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolveRejectPairIntegrationTest.groovy
new file mode 100644
index 0000000..8ceed1e
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/versions/VersionRangeResolveRejectPairIntegrationTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 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.integtests.resolve.versions
+
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios
+import spock.lang.IgnoreIf
+
+@IgnoreIf({
+    // This test is very expensive. Ideally we shouldn't need an integration test here, but lack the
+    // infrastructure to simulate everything done here, so we're only going to execute this test in
+    // embedded mode
+    !GradleContextualExecuter.embedded
+})
+class VersionRangeResolveRejectPairIntegrationTest extends AbstractVersionRangeResolveIntegrationTest {
+    def "resolve reject pair #permutation"() {
+        given:
+        def candidates = permutation.candidates
+        def expectedSingle = permutation.expectedSingle
+        def expectedMulti = permutation.expectedMulti
+
+        expect:
+        checkScenarioResolution(expectedSingle, expectedMulti, candidates)
+
+        where:
+        permutation << VersionRangeResolveTestScenarios.SCENARIOS_DEPENDENCY_WITH_REJECT
+    }
+}
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/build.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/build.gradle
index b13250c..b3cf983 100644
--- a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/build.gradle
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/build.gradle
@@ -18,42 +18,3 @@
 file("projectB-1.5.jar").text = ''
 file("projectB-api-1.5.jar").text = ''
 file("projectB-extraRuntime-1.5.jar").text = ''
-
-defaultTasks 'listJars'
-
-task listJars {
-    doLast {
-        def compile = configurations.compile
-
-        Set jars = compile.collect { it.name } as Set
-        assert ['projectA-1.2.jar', 'projectB-api-1.5.jar'] as Set == jars
-
-        def projectA = compile.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'api' }
-        def root = (projectA.parents as List)[0]
-        def artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
-        assert ['projectA', 'projectB-api'] as Set == artifacts
-
-        def projectB = projectA.children.find { it.moduleName == 'projectB' && it.configuration == 'compileTime' }
-        artifacts = projectB.getAllArtifacts(projectA).collect { it.name } as Set
-        assert ['projectB-api'] as Set == artifacts
-
-        def runtime = configurations.runtime
-
-        jars = runtime.collect { it.name } as Set
-        assert ['projectA-1.2.jar', 'projectB-api-1.5.jar', 'projectB-1.5.jar', 'projectB-extraRuntime-1.5.jar'] as Set == jars
-
-        projectA = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'api' }
-        root = (projectA.parents as List)[0]
-        artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
-        assert ['projectA', 'projectB-api'] as Set == artifacts
-
-        projectA = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' && it.configuration == 'default' }
-        root = (projectA.parents as List)[0]
-        artifacts = projectA.getAllArtifacts(root).collect { it.name } as Set
-        assert ['projectA', 'projectB'] as Set == artifacts
-
-        projectB = runtime.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectB' && it.configuration == 'extraRuntime' }
-        artifacts = projectB.getAllArtifacts(root).collect { it.name } as Set
-        assert ['projectB', 'projectB-extraRuntime'] as Set == artifacts
-    }
-}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/settings.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/settings.gradle
new file mode 100644
index 0000000..56e0e87
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveConfigurationHierarchy/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'test'
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/build.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/build.gradle
index 989d0c8..fd1df1b3 100644
--- a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/build.gradle
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/build.gradle
@@ -13,25 +13,3 @@
 
 file("projectA-1.2.jar").text = ''
 file("projectB-1.5.jar").text = ''
-
-defaultTasks 'listJars'
-
-task listJars {
-    doLast {
-        def compile = configurations.compile
-
-        Set jars = compile.collect { it.name } as Set
-        assert ['projectA-1.2.jar', 'projectB-1.5.jar'] as Set == jars
-
-        Set artifacts = compile.resolvedConfiguration.resolvedArtifacts.collect {
-            "${it.name}-${it.type}-${it.extension}" as String
-        } as Set
-        assert ['projectA-jar-jar', 'projectB-jar-jar'] as Set == artifacts
-
-        Set modules = compile.resolvedConfiguration.resolvedArtifacts.collect {
-            def dep = it.moduleVersion
-            "${dep.id.group}-${dep.id.name}-${dep.id.version}" as String
-        } as Set
-        assert ['test-projectA-1.2', 'test-projectB-1.5'] as Set == modules
-    }
-}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/settings.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/settings.gradle
new file mode 100644
index 0000000..56e0e87
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canHaveCycleInDependencyGraph/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'test'
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/build.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/build.gradle
index 85c63ea..863bd54 100644
--- a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/build.gradle
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/build.gradle
@@ -13,23 +13,3 @@
 
 file("projectA-1.2.jar").text = ''
 file("projectB-1.5.jar").text = ''
-
-defaultTasks 'listJars'
-
-task listJars {
-    doLast {
-        def compile = configurations.compile
-
-        def jars = compile.collect { it.name }
-        assert ['projectA-1.2.jar', 'projectB-1.5.jar'] == jars
-
-        def artifacts = compile.resolvedConfiguration.resolvedArtifacts.collect { "$it.name-$it.moduleVersion.id.version.$it.extension" }
-        assert ['projectA-1.2.jar', 'projectB-1.5.jar'] == artifacts
-
-        def projectA = compile.resolvedConfiguration.firstLevelModuleDependencies.find { it.moduleName == 'projectA' }
-        assert '1.2' == projectA.moduleVersion
-
-        def projectB = projectA.children.find { it.moduleName == 'projectB' }
-        assert '1.5' == projectB.moduleVersion
-    }
-}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/settings.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/settings.gradle
new file mode 100644
index 0000000..56e0e87
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/canUseDynamicVersions/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'test'
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/build.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/build.gradle
new file mode 100644
index 0000000..ee68046
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/build.gradle
@@ -0,0 +1,41 @@
+allprojects {
+    configurations {
+        evictedTransitive
+        evictedDirect
+        multiProject
+        create('default')
+    }
+    dependencies {
+        repositories {
+            ivy {
+                artifactPattern projectDir.absolutePath + '/[module]-[revision].jar'
+                ivyPattern projectDir.absolutePath + '/[module]-[revision]-ivy.xml'
+            }
+        }
+    }
+}
+
+dependencies {
+    // projectA-1.2 depends on projectB-1.5, which should be evicted by projectB-2.1.5
+    evictedTransitive 'test:projectA:1.2'
+    evictedTransitive 'test:projectB:2.1.5'
+
+    // projectA-2.0 depends on projectB-2.1.5, which should evict projectB-1.5
+    evictedDirect 'test:projectA:2.0'
+    evictedDirect 'test:projectB:1.5'
+
+    // subproject depends on projectA-2.0, which should evict projectA-1.2
+    multiProject 'test:projectA:1.2'
+    multiProject project(':subproject')
+}
+
+project(':subproject') {
+    dependencies {
+        add('default', 'test:projectA:2.0')
+    }
+}
+
+file("projectA-1.2.jar").text = ''
+file("projectA-2.0.jar").text = ''
+file("projectB-1.5.jar").text = ''
+file("projectB-2.1.5.jar").text = ''
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
deleted file mode 100644
index 6924c66..0000000
--- a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/projectWithConflicts.gradle
+++ /dev/null
@@ -1,56 +0,0 @@
-allprojects {
-    configurations {
-        evictedTransitive
-        evictedDirect
-        multiProject
-        create('default')
-    }
-    dependencies {
-        repositories {
-            ivy {
-                artifactPattern projectDir.absolutePath + '/[module]-[revision].jar'
-                ivyPattern projectDir.absolutePath + '/[module]-[revision]-ivy.xml'
-            }
-        }
-    }
-}
-
-dependencies {
-    // projectA-1.2 depends on projectB-1.5, which should be evicted by projectB-2.1.5
-    evictedTransitive 'test:projectA:1.2'
-    evictedTransitive 'test:projectB:2.1.5'
-
-    // projectA-2.0 depends on projectB-2.1.5, which should evict projectB-1.5
-    evictedDirect 'test:projectA:2.0'
-    evictedDirect 'test:projectB:1.5'
-
-    // subproject depends on projectA-2.0, which should evict projectA-1.2
-    multiProject 'test:projectA:1.2'
-    multiProject project(':subproject')
-}
-
-project(':subproject') {
-    dependencies {
-        add('default', 'test:projectA:2.0')
-    }
-}
-
-file("projectA-1.2.jar").text = ''
-file("projectA-2.0.jar").text = ''
-file("projectB-1.5.jar").text = ''
-file("projectB-2.1.5.jar").text = ''
-
-defaultTasks 'check'
-
-task check {
-    doLast {
-        def jars = configurations.evictedTransitive.collect { it.name }
-        assert ['projectA-1.2.jar', 'projectB-2.1.5.jar'] as Set == jars as Set
-
-        jars = configurations.evictedDirect.collect { it.name }
-        assert ['projectA-2.0.jar', 'projectB-2.1.5.jar'] as Set == jars as Set
-
-        jars = configurations.multiProject.collect { it.name }
-        assert ['projectA-2.0.jar', 'projectB-2.1.5.jar'] as Set == jars as Set
-    }
-}
\ No newline at end of file
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
index e7bc057..be107a2 100644
--- a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/ArtifactDependenciesIntegrationTest/dependencyReportWithConflicts/settings.gradle
@@ -1,3 +1,3 @@
 include 'subproject'
 
-rootProject.buildFileName = 'projectWithConflicts.gradle'
\ No newline at end of file
+rootProject.name = 'test'
diff --git a/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureWriteIntegTest/duplicated.keys b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureWriteIntegTest/duplicated.keys
new file mode 100644
index 0000000..b99acc7
--- /dev/null
+++ b/subprojects/dependency-management/src/integTest/resources/org/gradle/integtests/resolve/verification/DependencyVerificationSignatureWriteIntegTest/duplicated.keys
@@ -0,0 +1,156 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQINBFrDOW0BEAC9byiOScTIrOeZ4+nUJHx5nApGe1jT0RBK6QGl0utlqWubJS3W
+oOt11vQPbugBTTX9QkIFRARxs/gqkToJR2UqfJJKE6FdtKStSsVGGesnYM+5Tp0v
+YAx/lNr/DfbJAwOhs0i5AkUxHD2Q/TxD9gpYDlYGxVtUwjCOEAby750wfjxHVHeW
+lBY22t6lqVdfzBiTpsrsQggoTUR0ZT3RL63UfV4YhO889x73f29X6hE0aWjWDmD2
+9+KciCN0QOE79tjstp/bVI1o2kDD2VdcZ6fHd6/xi5oiAb+SaKQADuMloSHgi9Hc
+WbyjWJznz8magFP/VZKWrmkbiHO2R9i5DGdtHFcTltTNADG3S/qqWpCAKbNX7kFe
+bBPQt0xOmakD6m9zAKkN4HcrHWhhdmnxgCV2SZWKy6K2JEtbS6gncDZJWbXeIqu7
+pCgHvOTzRyKmGpBWmATh0W5ypkIL7+8BYC8cS9DPcx0seIK4w+kHyTxTNguTxrL2
+96EfHU5up75lKhKErNbN+09tpiede3ETv423RxnlvIBR2mFONw8i6/osoE+9rPcU
+vE7qFORR6B02Q0l7mpUF7Qs5KJaclqqafr3xgv5eXrnSUPauprfmrXn5MbKZ7+cf
+i2xdrzFQx2srKVW8L2Tco83ROEZwdEl8MD9ABQ3fWhHdSryVOfxyqcuJTQARAQAB
+tBhHcmFkbGUgPG1hcmNAZ3JhZGxlLmNvbT65Ag0EVzI06gEQAMfgdIiOy73j97TM
+YElvKsUUITwhIZMjscA19RB4vQKmXsRulA2MgYVsS290+F55rPmEnmyDd23+iDd9
+D2gEBeSTHrleZGewvBi53m4jhtLbjRRX4dcMEEBVMT+W5B8inoJYiZJjd2l9JFlZ
+qteRTe8O1mCPd2tKtjwNssE9ToH17tCpOjLeqZlD39U3tARdH4DI0NHZqMRsLOGR
+bK9cP7tUmD6XOEOfN6kjGYOaluLCaxP0nWL4GgbwWs375lFVdo4SyUBE/T6u+kgr
+pFkb3B0G1vT1Ek4MGe5/Kmtg/T/8aZxnI5kJvIsF8mo4ju9Ri7vzHIFxvBCBu6XA
+yinew38iDEJMYVjhHjBoeaB8x1qAE2hsK/luM4N96AB4qYj9OaDiyml8ffX5hqGe
+1hn4xkLGBsJZGk4O63omVn8pbTXkj8ECOvFyP9aigMzEaCrztIBgXr4qX9mbh42n
+x6Z24h8tCC5nKYCvLNZCLFbBkV+SKz8NVgA6FlZi+VdqjVE8AwwcWGG37nvxq0qk
+ljMxxrpbMZflO4tKKna1dFHljyTu9YxURBpOVDIdACXePDrZJzhYju7u8Dd51tb7
+7XAfyRC+gdMiN1QekYSQaI0O5WLZ2WvQsfXIShXKhli76xJ5GEEp7Me0+w53TaJU
+F68khemdUD3P8WVMQ4F9zPigUrKJABEBAAGJAh8EGAEIAAkFAlcyNOoCGwwACgkQ
+FcccCk4Ljt3t8hAAmfRLEBwnmJIp6cgcLOJ6kM/1nreGOq6ECCYOhXFzWynhjgwx
+Steq6dK43mLZFc1gfY508IK/I6O3++OMjSk+sDGL4PqccTr68UBowLTN4oV0rIfJ
+tp+D3LN3R7rS/j+9c6Sy0GrzX5ebxrAPbQnDj2sEAW76myDENpKjyMp5nnfqeL16
+tNNnUVP55EbygguWFFtdfo8pIl9hu/EzrwtYl4/Ifx+N4vgN9l94CpsPkzK38rBT
+mIXMTGd8iUbQV7XYl078ZiDKqT2XYehu6BF3nhIFb6CzI0IbmDbZoGTdJ51pZ8u2
+swZt//bDRRd1pFPhBkCRC+EbnH/oBadgVTx43F7p/jixoWXqX+ZvTZCnoWA1MC1Q
+VLzfvf7D6Rw5vNtA8mtlEqMKzx5Kf3YeUN2FIvkDbCfX51QlJC4Oe9J5vdFjnooW
+VKgiBPAar689Y4C7tzpGM2KOcl0+io/g9ANkSm6cpRCTZKwgOXl0DVebeWjsdt6/
+bqHKOPLhLn0UNbUmMzzrPo71y7qiMDmv5D8K/aVgxiX7roDSv9PSqwsZ3mw+EV4L
+Qr12Aw2WG2uNijO99r02xqNU6vvHEglWH/f5gT4eYNEtGTqyp5PNTuYkI7GKybBg
+EPtLjZykvvWJNn/P6KdmcsxQthX3XnbCIRq2LDL7A4GNor2DcqTyOw3cjy0=
+=zmeZ
+-----END PGP PUBLIC KEY BLOCK-----
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+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
+uYlR
+=BiJx
+-----END PGP PUBLIC KEY BLOCK-----
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQINBFrDOW0BEAC9byiOScTIrOeZ4+nUJHx5nApGe1jT0RBK6QGl0utlqWubJS3W
+oOt11vQPbugBTTX9QkIFRARxs/gqkToJR2UqfJJKE6FdtKStSsVGGesnYM+5Tp0v
+YAx/lNr/DfbJAwOhs0i5AkUxHD2Q/TxD9gpYDlYGxVtUwjCOEAby750wfjxHVHeW
+lBY22t6lqVdfzBiTpsrsQggoTUR0ZT3RL63UfV4YhO889x73f29X6hE0aWjWDmD2
+9+KciCN0QOE79tjstp/bVI1o2kDD2VdcZ6fHd6/xi5oiAb+SaKQADuMloSHgi9Hc
+WbyjWJznz8magFP/VZKWrmkbiHO2R9i5DGdtHFcTltTNADG3S/qqWpCAKbNX7kFe
+bBPQt0xOmakD6m9zAKkN4HcrHWhhdmnxgCV2SZWKy6K2JEtbS6gncDZJWbXeIqu7
+pCgHvOTzRyKmGpBWmATh0W5ypkIL7+8BYC8cS9DPcx0seIK4w+kHyTxTNguTxrL2
+96EfHU5up75lKhKErNbN+09tpiede3ETv423RxnlvIBR2mFONw8i6/osoE+9rPcU
+vE7qFORR6B02Q0l7mpUF7Qs5KJaclqqafr3xgv5eXrnSUPauprfmrXn5MbKZ7+cf
+i2xdrzFQx2srKVW8L2Tco83ROEZwdEl8MD9ABQ3fWhHdSryVOfxyqcuJTQARAQAB
+tBhHcmFkbGUgPG1hcmNAZ3JhZGxlLmNvbT4=
+=M2BX
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactTransformRegistration.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactTransformRegistration.java
deleted file mode 100644
index 97beb67..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ArtifactTransformRegistration.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019 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.api.internal.artifacts;
-
-import org.gradle.api.internal.artifacts.transform.TransformationStep;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-
-/**
- * Registration of an artifact transform.
- */
-public interface ArtifactTransformRegistration {
-    /**
-     * Attributes that match the variant that is consumed.
-     */
-    ImmutableAttributes getFrom();
-
-    /**
-     * Attributes that match the variant that is produced.
-     */
-    ImmutableAttributes getTo();
-
-    /**
-     * Transformation for artifacts of the variant.
-     */
-    TransformationStep getTransformationStep();
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ConfigurationResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ConfigurationResolver.java
index dc72b13..50141ea9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ConfigurationResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ConfigurationResolver.java
@@ -16,30 +16,30 @@
 package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.artifacts.ResolveException;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
 import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
 
 import java.util.List;
 
 public interface ConfigurationResolver {
-    /**
-     * Traverses enough of the graph to calculate the build dependencies of the given configuration. All failures are packaged in the result.
-     */
-    void resolveBuildDependencies(ConfigurationInternal configuration, ResolverResults result);
 
     /**
-     * Traverses the full dependency graph of the given configuration. All failures are packaged in the result.
+     * Traverses enough of the graph to calculate the build dependencies of the given resolve context. All failures are packaged in the result.
      */
-    void resolveGraph(ConfigurationInternal configuration, ResolverResults results) throws ResolveException;
+    ResolverResults resolveBuildDependencies(ResolveContext configuration);
 
     /**
-     * Calculates the artifacts to include in the result for the given configuration. All failures are packaged in the result.
-     * Must be called using the same result instance as was passed to {@link #resolveGraph(ConfigurationInternal, ResolverResults)}.
+     * Traverses the full dependency graph of the given resolve context. All failures are packaged in the result.
      */
-    void resolveArtifacts(ConfigurationInternal configuration, ResolverResults results) throws ResolveException;
+    ResolverResults resolveGraph(ResolveContext resolveContext) throws ResolveException;
 
     /**
-     * Returns the list of repositories available to resolve a given configuration. This is used for reporting only.
+     * Calculates the artifacts to include in the result for the given resolve context. All failures are packaged in the result.
+     * Must be called using the same result instance that was returned by {@link #resolveGraph(ResolveContext)}.
+     */
+    ResolverResults resolveArtifacts(ResolveContext resolveContext, ResolverResults graphResults) throws ResolveException;
+
+    /**
+     * Returns the list of repositories available to resolve a given resolve context. This is used for reporting only.
      */
     List<ResolutionAwareRepository> getRepositories();
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultComponentSelectorConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultComponentSelectorConverter.java
index 9e11d4c..ebee791 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultComponentSelectorConverter.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultComponentSelectorConverter.java
@@ -22,19 +22,16 @@
 import org.gradle.api.artifacts.component.LibraryComponentSelector;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentSelector;
-import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.LocalComponentRegistry;
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
 import org.gradle.internal.component.local.model.LocalComponentGraphResolveState;
 import org.gradle.util.internal.GUtil;
 
 public class DefaultComponentSelectorConverter implements ComponentSelectorConverter {
     private static final ModuleVersionSelector UNKNOWN_MODULE_VERSION_SELECTOR = DefaultModuleVersionSelector.newSelector(DefaultModuleIdentifier.newId("", "unknown"), "");
-    private final ComponentIdentifierFactory componentIdentifierFactory;
     private final LocalComponentRegistry localComponentRegistry;
 
-    public DefaultComponentSelectorConverter(ComponentIdentifierFactory componentIdentifierFactory, LocalComponentRegistry localComponentRegistry) {
-        this.componentIdentifierFactory = componentIdentifierFactory;
+    public DefaultComponentSelectorConverter(LocalComponentRegistry localComponentRegistry) {
         this.localComponentRegistry = localComponentRegistry;
     }
 
@@ -53,9 +50,9 @@ public ModuleVersionSelector getSelector(ComponentSelector selector) {
         if (selector instanceof ModuleComponentSelector) {
             return DefaultModuleVersionSelector.newSelector((ModuleComponentSelector) selector);
         }
-        if (selector instanceof ProjectComponentSelector) {
-            ProjectComponentSelector projectSelector = (ProjectComponentSelector) selector;
-            ProjectComponentIdentifier projectId = componentIdentifierFactory.createProjectComponentIdentifier(projectSelector);
+        if (selector instanceof DefaultProjectComponentSelector) {
+            DefaultProjectComponentSelector projectSelector = (DefaultProjectComponentSelector) selector;
+            ProjectComponentIdentifier projectId = projectSelector.toIdentifier();
             LocalComponentGraphResolveState projectComponent = localComponentRegistry.getComponent(projectId);
             if (projectComponent != null) {
                 ModuleVersionIdentifier moduleVersionId = projectComponent.getModuleVersionId();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyConstraintSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyConstraintSet.java
index 1cbde34..cd1bd89 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyConstraintSet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyConstraintSet.java
@@ -22,11 +22,8 @@
 import org.gradle.api.artifacts.DependencyConstraintSet;
 import org.gradle.api.internal.DelegatingDomainObjectSet;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.internal.deprecation.DeprecatableConfiguration;
-import org.gradle.internal.deprecation.DeprecationLogger;
 
 import java.util.Collection;
-import java.util.List;
 
 public class DefaultDependencyConstraintSet extends DelegatingDomainObjectSet<DependencyConstraint> implements DependencyConstraintSet {
     private final Describable displayName;
@@ -45,8 +42,8 @@ public String toString() {
 
     @Override
     public boolean add(final DependencyConstraint dependencyConstraint) {
-        assertConfigurationIsDeclarableAgainst();
-        warnIfConfigurationIsDeprecated();
+        assertConfigurationIsDeclarable();
+        clientConfiguration.maybeEmitDeclarationDeprecation();
         return addInternalDependencyConstraint(dependencyConstraint);
     }
 
@@ -55,18 +52,8 @@ public boolean addInternalDependencyConstraint(DependencyConstraint dependencyCo
         return super.add(dependencyConstraint);
     }
 
-    private void warnIfConfigurationIsDeprecated() {
-        List<String> alternatives = ((DeprecatableConfiguration) clientConfiguration).getDeclarationAlternatives();
-        if (alternatives != null) {
-            DeprecationLogger.deprecateConfiguration(clientConfiguration.getName()).forDependencyDeclaration().replaceWith(alternatives)
-                .willBecomeAnErrorInGradle9()
-                .withUpgradeGuideSection(5, "dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
-                .nagUser();
-        }
-    }
-
-    private void assertConfigurationIsDeclarableAgainst() {
-        if (!clientConfiguration.isCanBeDeclaredAgainst()) {
+    private void assertConfigurationIsDeclarable() {
+        if (!clientConfiguration.isCanBeDeclared()) {
             throw new GradleException("Dependency constraints can not be declared against the `" + clientConfiguration.getName() + "` configuration.");
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
index 0bf680f..ec83ae3 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencyManagementServices.java
@@ -64,7 +64,9 @@
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyResolver;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.AttributeContainerSerializer;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentDetailsSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.SelectedVariantSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.ResolutionResultsStoreFactory;
 import org.gradle.api.internal.artifacts.mvnsettings.LocalMavenRepositoryLocator;
 import org.gradle.api.internal.artifacts.query.ArtifactResolutionQueryFactory;
@@ -75,20 +77,20 @@
 import org.gradle.api.internal.artifacts.repositories.metadata.IvyMutableModuleMetadataFactory;
 import org.gradle.api.internal.artifacts.repositories.metadata.MavenMutableModuleMetadataFactory;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformActionScheme;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformListener;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformParameterScheme;
 import org.gradle.api.internal.artifacts.transform.ConsumerProvidedVariantFinder;
-import org.gradle.api.internal.artifacts.transform.DefaultArtifactTransforms;
-import org.gradle.api.internal.artifacts.transform.DefaultTransformationRegistrationFactory;
+import org.gradle.api.internal.artifacts.transform.DefaultTransformInvocationFactory;
+import org.gradle.api.internal.artifacts.transform.DefaultTransformRegistrationFactory;
 import org.gradle.api.internal.artifacts.transform.DefaultTransformedVariantFactory;
-import org.gradle.api.internal.artifacts.transform.DefaultTransformerInvocationFactory;
+import org.gradle.api.internal.artifacts.transform.DefaultVariantSelectorFactory;
 import org.gradle.api.internal.artifacts.transform.DefaultVariantTransformRegistry;
-import org.gradle.api.internal.artifacts.transform.ImmutableTransformationWorkspaceServices;
-import org.gradle.api.internal.artifacts.transform.MutableTransformationWorkspaceServices;
-import org.gradle.api.internal.artifacts.transform.TransformationRegistrationFactory;
+import org.gradle.api.internal.artifacts.transform.ImmutableTransformWorkspaceServices;
+import org.gradle.api.internal.artifacts.transform.MutableTransformWorkspaceServices;
+import org.gradle.api.internal.artifacts.transform.TransformActionScheme;
+import org.gradle.api.internal.artifacts.transform.TransformExecutionListener;
+import org.gradle.api.internal.artifacts.transform.TransformInvocationFactory;
+import org.gradle.api.internal.artifacts.transform.TransformParameterScheme;
+import org.gradle.api.internal.artifacts.transform.TransformRegistrationFactory;
 import org.gradle.api.internal.artifacts.transform.TransformedVariantFactory;
-import org.gradle.api.internal.artifacts.transform.TransformerInvocationFactory;
 import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry;
 import org.gradle.api.internal.artifacts.type.DefaultArtifactTypeRegistry;
 import org.gradle.api.internal.attributes.AttributeDesugaring;
@@ -108,8 +110,8 @@
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.configuration.internal.UserCodeApplicationContext;
-import org.gradle.initialization.internal.InternalBuildFinishedListener;
 import org.gradle.internal.authentication.AuthenticationSchemeRegistry;
+import org.gradle.internal.build.BuildModelLifecycleListener;
 import org.gradle.internal.build.BuildState;
 import org.gradle.internal.component.external.model.JavaEcosystemVariantDerivationStrategy;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
@@ -163,7 +165,7 @@ public DependencyResolutionServices create(FileResolver resolver, FileCollection
         services.add(DependencyMetaDataProvider.class, dependencyMetaDataProvider);
         services.add(ProjectFinder.class, projectFinder);
         services.add(DomainObjectContext.class, domainObjectContext);
-        services.addProvider(new ArtifactTransformResolutionGradleUserHomeServices());
+        services.addProvider(new TransformGradleUserHomeServices());
         services.addProvider(new DependencyResolutionScopeServices(domainObjectContext));
         return services.get(DependencyResolutionServices.class);
     }
@@ -173,16 +175,16 @@ public void addDslServices(ServiceRegistration registration, DomainObjectContext
         registration.addProvider(new DependencyResolutionScopeServices(domainObjectContext));
     }
 
-    private static class ArtifactTransformResolutionGradleUserHomeServices {
+    private static class TransformGradleUserHomeServices {
 
-        ArtifactTransformListener createArtifactTransformListener() {
-            return new ArtifactTransformListener() {
+        TransformExecutionListener createTransformExecutionListener() {
+            return new TransformExecutionListener() {
                 @Override
-                public void beforeTransformerInvocation(Describable transformer, Describable subject) {
+                public void beforeTransformExecution(Describable transform, Describable subject) {
                 }
 
                 @Override
-                public void afterTransformerInvocation(Describable transformer, Describable subject) {
+                public void afterTransformExecution(Describable transform, Describable subject) {
                 }
             };
         }
@@ -199,7 +201,7 @@ public DependencyResolutionScopeServices(DomainObjectContext domainObjectContext
         void configure(ServiceRegistration registration) {
             registration.add(DefaultTransformedVariantFactory.class);
             registration.add(DefaultRootComponentMetadataBuilder.Factory.class);
-            registration.add(ProjectDependencyResolver.class);
+            registration.add(ResolveExceptionContextualizer.class);
         }
 
         AttributesSchemaInternal createConfigurationAttributesSchema(InstantiatorFactory instantiatorFactory, IsolatableFactory isolatableFactory, PlatformSupport platformSupport) {
@@ -209,38 +211,38 @@ AttributesSchemaInternal createConfigurationAttributesSchema(InstantiatorFactory
             return attributesSchema;
         }
 
-        MutableTransformationWorkspaceServices createTransformerWorkspaceServices(ProjectLayout projectLayout, ExecutionHistoryStore executionHistoryStore) {
-            return new MutableTransformationWorkspaceServices(projectLayout.getBuildDirectory().dir(".transforms"), executionHistoryStore);
+        MutableTransformWorkspaceServices createTransformWorkspaceServices(ProjectLayout projectLayout, ExecutionHistoryStore executionHistoryStore) {
+            return new MutableTransformWorkspaceServices(projectLayout.getBuildDirectory().dir(".transforms"), executionHistoryStore);
         }
 
-        TransformerInvocationFactory createTransformerInvocationFactory(
+        TransformInvocationFactory createTransformInvocationFactory(
                 ExecutionEngine executionEngine,
                 FileSystemAccess fileSystemAccess,
-                ImmutableTransformationWorkspaceServices transformationWorkspaceServices,
-                ArtifactTransformListener artifactTransformListener,
+                ImmutableTransformWorkspaceServices transformWorkspaceServices,
+                TransformExecutionListener transformExecutionListener,
                 FileCollectionFactory fileCollectionFactory,
                 ProjectStateRegistry projectStateRegistry,
                 BuildOperationExecutor buildOperationExecutor
         ) {
-            return new DefaultTransformerInvocationFactory(
+            return new DefaultTransformInvocationFactory(
                 executionEngine,
                 fileSystemAccess,
-                artifactTransformListener,
-                transformationWorkspaceServices,
+                transformExecutionListener,
+                transformWorkspaceServices,
                 fileCollectionFactory,
                 projectStateRegistry,
                 buildOperationExecutor
             );
         }
 
-        TransformationRegistrationFactory createTransformationRegistrationFactory(
+        TransformRegistrationFactory createTransformRegistrationFactory(
                 BuildOperationExecutor buildOperationExecutor,
                 IsolatableFactory isolatableFactory,
                 ClassLoaderHierarchyHasher classLoaderHierarchyHasher,
-                TransformerInvocationFactory transformerInvocationFactory,
+                TransformInvocationFactory transformInvocationFactory,
                 DomainObjectContext domainObjectContext,
-                ArtifactTransformParameterScheme parameterScheme,
-                ArtifactTransformActionScheme actionScheme,
+                TransformParameterScheme parameterScheme,
+                TransformActionScheme actionScheme,
                 InputFingerprinter inputFingerprinter,
                 CalculatedValueContainerFactory calculatedValueContainerFactory,
                 FileCollectionFactory fileCollectionFactory,
@@ -248,11 +250,11 @@ TransformationRegistrationFactory createTransformationRegistrationFactory(
                 ServiceRegistry internalServices,
                 DocumentationRegistry documentationRegistry
         ) {
-            return new DefaultTransformationRegistrationFactory(
+            return new DefaultTransformRegistrationFactory(
                 buildOperationExecutor,
                 isolatableFactory,
                 classLoaderHierarchyHasher,
-                transformerInvocationFactory,
+                transformInvocationFactory,
                 fileCollectionFactory,
                 fileLookup,
                 inputFingerprinter,
@@ -265,8 +267,8 @@ TransformationRegistrationFactory createTransformationRegistrationFactory(
             );
         }
 
-        VariantTransformRegistry createArtifactTransformRegistry(InstantiatorFactory instantiatorFactory, ImmutableAttributesFactory attributesFactory, ServiceRegistry services, TransformationRegistrationFactory transformationRegistrationFactory, ArtifactTransformParameterScheme parameterScheme) {
-            return new DefaultVariantTransformRegistry(instantiatorFactory, attributesFactory, services, transformationRegistrationFactory, parameterScheme.getInstantiationScheme());
+        VariantTransformRegistry createVariantTransformRegistry(InstantiatorFactory instantiatorFactory, ImmutableAttributesFactory attributesFactory, ServiceRegistry services, TransformRegistrationFactory transformRegistrationFactory, TransformParameterScheme parameterScheme) {
+            return new DefaultVariantTransformRegistry(instantiatorFactory, attributesFactory, services, transformRegistrationFactory, parameterScheme.getInstantiationScheme());
         }
 
         DefaultUrlArtifactRepository.Factory createDefaultUrlArtifactRepositoryFactory(FileResolver fileResolver) {
@@ -341,7 +343,7 @@ DefaultConfigurationFactory createDefaultConfigurationFactory(
             BuildOperationExecutor buildOperationExecutor,
             PublishArtifactNotationParserFactory artifactNotationParserFactory,
             ImmutableAttributesFactory attributesFactory,
-            DocumentationRegistry documentationRegistry,
+            ResolveExceptionContextualizer exceptionContextualizer,
             UserCodeApplicationContext userCodeApplicationContext,
             ProjectStateRegistry projectStateRegistry,
             WorkerThreadRegistry workerThreadRegistry,
@@ -350,7 +352,7 @@ DefaultConfigurationFactory createDefaultConfigurationFactory(
             TaskDependencyFactory taskDependencyFactory
         ) {
             return new DefaultConfigurationFactory(instantiator, resolver, listenerManager, metaDataProvider, componentIdentifierFactory, dependencyLockingProvider, domainObjectContext, fileCollectionFactory,
-                buildOperationExecutor, artifactNotationParserFactory, attributesFactory, documentationRegistry, userCodeApplicationContext, projectStateRegistry, workerThreadRegistry,
+                buildOperationExecutor, artifactNotationParserFactory, attributesFactory, exceptionContextualizer, userCodeApplicationContext, projectStateRegistry, workerThreadRegistry,
                 domainObjectCollectionFactory, calculatedValueContainerFactory, taskDependencyFactory
             );
         }
@@ -414,7 +416,7 @@ DependencyHandler createDependencyHandler(Instantiator instantiator,
                                                   ComponentModuleMetadataHandler componentModuleMetadataHandler,
                                                   ArtifactResolutionQueryFactory resolutionQueryFactory,
                                                   AttributesSchema attributesSchema,
-                                                  VariantTransformRegistry artifactTransformRegistrations,
+                                                  VariantTransformRegistry variantTransformRegistry,
                                                   ArtifactTypeRegistry artifactTypeRegistry,
                                                   ObjectFactory objects,
                                                   PlatformSupport platformSupport) {
@@ -427,7 +429,7 @@ DependencyHandler createDependencyHandler(Instantiator instantiator,
                 componentModuleMetadataHandler,
                 resolutionQueryFactory,
                 attributesSchema,
-                artifactTransformRegistrations,
+                variantTransformRegistry,
                 artifactTypeRegistry,
                 objects,
                 platformSupport);
@@ -448,10 +450,10 @@ DependencyLockingProvider createDependencyLockingProvider(FileResolver fileResol
 
             DefaultDependencyLockingProvider dependencyLockingProvider = new DefaultDependencyLockingProvider(fileResolver, startParameter, context, globalDependencyResolutionRules.getDependencySubstitutionRules(), propertyFactory, filePropertyFactory, listenerManager.getBroadcaster(FileResourceListener.class));
             if (startParameter.isWriteDependencyLocks()) {
-                listenerManager.addListener(new InternalBuildFinishedListener() {
+                listenerManager.addListener(new BuildModelLifecycleListener() {
                     @Override
-                    public void buildFinished(GradleInternal gradle, boolean failed) {
-                        if (!failed) {
+                    public void beforeModelDiscarded(GradleInternal model, boolean buildFailed) {
+                        if (!buildFailed) {
                             dependencyLockingProvider.buildFinished();
                         }
                     }
@@ -497,58 +499,74 @@ GlobalDependencyResolutionRules createModuleMetadataHandler(ComponentMetadataPro
             return new DefaultGlobalDependencyResolutionRules(componentMetadataProcessorFactory, moduleMetadataProcessor, rules);
         }
 
-        ConfigurationResolver createDependencyResolver(ArtifactDependencyResolver artifactDependencyResolver,
-                                                       RepositoriesSupplier repositoriesSupplier,
-                                                       GlobalDependencyResolutionRules metadataHandler,
-                                                       ComponentIdentifierFactory componentIdentifierFactory,
-                                                       ResolutionResultsStoreFactory resolutionResultsStoreFactory,
-                                                       StartParameter startParameter,
-                                                       AttributesSchemaInternal attributesSchema,
-                                                       VariantTransformRegistry variantTransforms,
-                                                       ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-                                                       ImmutableAttributesFactory attributesFactory,
-                                                       BuildOperationExecutor buildOperationExecutor,
-                                                       ArtifactTypeRegistry artifactTypeRegistry,
-                                                       ComponentSelectorConverter componentSelectorConverter,
-                                                       AttributeContainerSerializer attributeContainerSerializer,
-                                                       BuildState currentBuild,
-                                                       TransformedVariantFactory transformedVariantFactory,
-                                                       DependencyVerificationOverride dependencyVerificationOverride,
-                                                       ProjectDependencyResolver projectDependencyResolver,
-                                                       ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
-                                                       WorkerLeaseService workerLeaseService) {
+        ConfigurationResolver createDependencyResolver(
+            ArtifactDependencyResolver artifactDependencyResolver,
+            RepositoriesSupplier repositoriesSupplier,
+            GlobalDependencyResolutionRules metadataHandler,
+            ComponentIdentifierFactory componentIdentifierFactory,
+            ResolutionResultsStoreFactory resolutionResultsStoreFactory,
+            StartParameter startParameter,
+            AttributesSchemaInternal attributesSchema,
+            VariantTransformRegistry variantTransforms,
+            ImmutableModuleIdentifierFactory moduleIdentifierFactory,
+            ImmutableAttributesFactory attributesFactory,
+            BuildOperationExecutor buildOperationExecutor,
+            ArtifactTypeRegistry artifactTypeRegistry,
+            ComponentSelectorConverter componentSelectorConverter,
+            AttributeContainerSerializer attributeContainerSerializer,
+            BuildState currentBuild,
+            TransformedVariantFactory transformedVariantFactory,
+            DependencyVerificationOverride dependencyVerificationOverride,
+            ProjectDependencyResolver projectDependencyResolver,
+            ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
+            AttributeDesugaring attributeDesugaring,
+            WorkerLeaseService workerLeaseService,
+            ResolveExceptionContextualizer resolveExceptionContextualizer,
+            ComponentDetailsSerializer componentDetailsSerializer,
+            SelectedVariantSerializer selectedVariantSerializer
+        ) {
+            DefaultConfigurationResolver defaultResolver = new DefaultConfigurationResolver(
+                artifactDependencyResolver,
+                repositoriesSupplier,
+                metadataHandler,
+                resolutionResultsStoreFactory,
+                startParameter.isBuildProjectDependencies(),
+                attributesSchema,
+                new DefaultVariantSelectorFactory(
+                    new ConsumerProvidedVariantFinder(
+                        variantTransforms,
+                        attributesSchema,
+                        attributesFactory
+                    ),
+                    attributesSchema,
+                    attributesFactory,
+                    transformedVariantFactory
+                ),
+                moduleIdentifierFactory,
+                buildOperationExecutor,
+                artifactTypeRegistry,
+                componentSelectorConverter,
+                attributeContainerSerializer,
+                currentBuild.getBuildIdentifier(),
+                attributeDesugaring,
+                dependencyVerificationOverride,
+                projectDependencyResolver,
+                componentSelectionDescriptorFactory,
+                workerLeaseService,
+                resolveExceptionContextualizer,
+                componentDetailsSerializer,
+                selectedVariantSerializer
+            );
+
             return new ErrorHandlingConfigurationResolver(
-                    new ShortCircuitEmptyConfigurationResolver(
-                            new DefaultConfigurationResolver(
-                                    artifactDependencyResolver,
-                                    repositoriesSupplier,
-                                    metadataHandler,
-                                    resolutionResultsStoreFactory,
-                                    startParameter.isBuildProjectDependencies(),
-                                    attributesSchema,
-                                    new DefaultArtifactTransforms(
-                                            new ConsumerProvidedVariantFinder(
-                                                    variantTransforms,
-                                                    attributesSchema,
-                                                    attributesFactory),
-                                            attributesSchema,
-                                            attributesFactory,
-                                            transformedVariantFactory
-                                    ),
-                                    moduleIdentifierFactory,
-                                    buildOperationExecutor,
-                                    artifactTypeRegistry,
-                                    componentSelectorConverter,
-                                    attributeContainerSerializer,
-                                    currentBuild.getBuildIdentifier(),
-                                    new AttributeDesugaring(attributesFactory),
-                                    dependencyVerificationOverride,
-                                    projectDependencyResolver,
-                                    componentSelectionDescriptorFactory,
-                                    workerLeaseService),
-                            componentIdentifierFactory,
-                            moduleIdentifierFactory,
-                            currentBuild.getBuildIdentifier()));
+                new ShortCircuitEmptyConfigurationResolver(
+                    defaultResolver,
+                    componentIdentifierFactory,
+                    moduleIdentifierFactory,
+                    currentBuild.getBuildIdentifier()
+                ),
+                resolveExceptionContextualizer
+            );
         }
 
         ArtifactPublicationServices createArtifactPublicationServices(ServiceRegistry services) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencySet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencySet.java
index 22ea9db..3c067e8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencySet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultDependencySet.java
@@ -29,11 +29,8 @@
 import org.gradle.api.internal.artifacts.dependencies.AbstractModuleDependency;
 import org.gradle.api.tasks.TaskDependency;
 import org.gradle.internal.Actions;
-import org.gradle.internal.deprecation.DeprecatableConfiguration;
-import org.gradle.internal.deprecation.DeprecationLogger;
 
 import java.util.Collection;
-import java.util.List;
 
 public class DefaultDependencySet extends DelegatingDomainObjectSet<Dependency> implements DependencySet {
     private final Describable displayName;
@@ -63,27 +60,16 @@ public TaskDependency getBuildDependencies() {
 
     @Override
     public boolean add(final Dependency o) {
-        assertConfigurationIsDeclarableAgainst();
-        warnIfConfigurationIsDeprecated();
+        assertConfigurationIsDeclarable();
+        clientConfiguration.maybeEmitDeclarationDeprecation();
         if (o instanceof AbstractModuleDependency) {
             ((AbstractModuleDependency) o).addMutationValidator(mutationValidator);
         }
         return super.add(o);
     }
 
-    private void warnIfConfigurationIsDeprecated() {
-        List<String> alternatives = ((DeprecatableConfiguration) clientConfiguration).getDeclarationAlternatives();
-
-        if (alternatives != null) {
-            DeprecationLogger.deprecateConfiguration(clientConfiguration.getName()).forDependencyDeclaration().replaceWith(alternatives)
-                .willBecomeAnErrorInGradle9()
-                .withUpgradeGuideSection(5, "dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
-                .nagUser();
-        }
-    }
-
-    private void assertConfigurationIsDeclarableAgainst() {
-        if (!clientConfiguration.isCanBeDeclaredAgainst()) {
+    private void assertConfigurationIsDeclarable() {
+        if (!clientConfiguration.isCanBeDeclared()) {
             throw new GradleException("Dependencies can not be declared against the `" + clientConfiguration.getName() + "` configuration.");
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultImmutableModuleIdentifierFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultImmutableModuleIdentifierFactory.java
index 01e555c..3a4a5ba 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultImmutableModuleIdentifierFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultImmutableModuleIdentifierFactory.java
@@ -58,9 +58,4 @@ public ModuleVersionIdentifier moduleWithVersion(ModuleIdentifier mi, String ver
         }
         return identifier;
     }
-
-    @Override
-    public ModuleVersionIdentifier moduleWithVersion(Module module) {
-        return moduleWithVersion(module.getGroup(), module.getName(), module.getVersion());
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolverResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolverResults.java
index 417fdf7..0c3a714 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolverResults.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DefaultResolverResults.java
@@ -16,25 +16,60 @@
 
 package org.gradle.api.internal.artifacts;
 
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.LenientConfiguration;
 import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.ResolvedArtifact;
 import org.gradle.api.artifacts.ResolvedConfiguration;
+import org.gradle.api.artifacts.ResolvedDependency;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveState;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.SelectedArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedLocalComponentsResult;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.GraphValidationException;
+import org.gradle.api.internal.attributes.AttributeContainerInternal;
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
+import org.gradle.api.specs.Spec;
 
+import javax.annotation.Nullable;
+import java.io.File;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * Default implementation of {@link ResolverResults}.
+ */
 public class DefaultResolverResults implements ResolverResults {
-    private ResolvedConfiguration resolvedConfiguration;
-    private ResolutionResult resolutionResult;
-    private ResolveException fatalFailure;
-    private ResolveException nonFatalFailure;
-    private ResolvedLocalComponentsResult resolvedLocalComponentsResult;
-    private Object artifactResolveState;
-    private VisitedArtifactSet visitedArtifacts;
+
+    private final ResolvedLocalComponentsResult resolvedLocalComponentsResult;
+    private final ResolutionResult resolutionResult;
+    private final ResolvedConfiguration resolvedConfiguration;
+    private final VisitedArtifactSet visitedArtifacts;
+    private final ArtifactResolveState artifactResolveState;
+    private final ResolveException failure;
+
+    public DefaultResolverResults(
+        @Nullable ResolvedLocalComponentsResult resolvedLocalComponentsResult,
+        @Nullable ResolutionResult resolutionResult,
+        VisitedArtifactSet visitedArtifacts,
+        @Nullable ResolvedConfiguration resolvedConfiguration,
+        @Nullable ArtifactResolveState artifactResolveState,
+        @Nullable ResolveException failure
+    ) {
+        this.resolvedLocalComponentsResult = resolvedLocalComponentsResult;
+        this.resolutionResult = resolutionResult;
+        this.visitedArtifacts = visitedArtifacts;
+        this.resolvedConfiguration = resolvedConfiguration;
+        this.artifactResolveState = artifactResolveState;
+        this.failure = failure;
+    }
 
     @Override
     public boolean hasError() {
-        if (fatalFailure != null || nonFatalFailure != null) {
+        if (failure != null) {
             return true;
         }
         return resolvedConfiguration != null && resolvedConfiguration.hasError();
@@ -42,119 +77,226 @@ public boolean hasError() {
 
     @Override
     public ResolvedConfiguration getResolvedConfiguration() {
-        assertHasArtifactResult();
+        if (resolvedConfiguration == null) {
+            throw new IllegalStateException("Cannot get ResolvedConfiguration before graph resolution.");
+        }
         return resolvedConfiguration;
     }
 
     @Override
     public ResolutionResult getResolutionResult() {
-        assertHasGraphResult();
+        maybeRethrowFatalError();
         return resolutionResult;
     }
 
     @Override
     public ResolvedLocalComponentsResult getResolvedLocalComponents() {
-        assertHasGraphResult();
+        maybeRethrowFatalError();
         return resolvedLocalComponentsResult;
     }
 
     @Override
+    public ArtifactResolveState getArtifactResolveState() {
+        maybeRethrowAnyError();
+        return artifactResolveState;
+    }
+
+    @Override
     public VisitedArtifactSet getVisitedArtifacts() {
-        assertHasVisitResult();
+        maybeRethrowFatalError();
         return visitedArtifacts;
     }
 
-    private void assertHasVisitResult() {
-        maybeRethrowFatalError();
-        if (visitedArtifacts == null) {
-            throw new IllegalStateException("Resolution result has not been attached.");
+    public void maybeRethrowFatalError() {
+        if (failure != null && isFatalError(failure)) {
+            throw failure;
         }
     }
 
-    private void assertHasGraphResult() {
-        maybeRethrowFatalError();
-        if (resolvedLocalComponentsResult == null) {
-            throw new IllegalStateException("Resolution result has not been attached.");
-        }
-    }
-
-    private void maybeRethrowFatalError() {
-        if (fatalFailure != null) {
-            throw fatalFailure;
-        }
+    private static boolean isFatalError(ResolveException failure) {
+        boolean isNonFatal = failure.getCause() instanceof GraphValidationException;
+        return !isNonFatal;
     }
 
     private void maybeRethrowAnyError() {
-        maybeRethrowFatalError();
-        if (nonFatalFailure != null) {
-            throw nonFatalFailure;
-        }
-    }
-
-    private void assertHasArtifactResult() {
-        if (resolvedConfiguration == null) {
-            throw new IllegalStateException("Resolution artifacts have not been attached.");
+        if (failure != null) {
+            throw failure;
         }
     }
 
     @Override
-    public void graphResolved(ResolutionResult resolutionResult, ResolvedLocalComponentsResult resolvedLocalComponentsResult, VisitedArtifactSet visitedArtifacts) {
-        this.resolutionResult = resolutionResult;
-        this.resolvedLocalComponentsResult = resolvedLocalComponentsResult;
-        this.visitedArtifacts = visitedArtifacts;
-        this.fatalFailure = null;
+    public Throwable getNonFatalFailure() {
+        return failure != null && !isFatalError(failure) ? failure : null;
     }
 
     @Override
-    public void failed(ResolveException failure) {
-        if (isNonFatalError(failure)) {
-            nonFatalFailure = failure;
-        } else {
-            this.resolutionResult = null;
-            this.resolvedLocalComponentsResult = null;
-            this.fatalFailure = failure;
+    public ResolveException getFailure() {
+        return failure;
+    }
+
+    @Override
+    public ResolverResults withFailure(ResolveException resolveException) {
+        return new DefaultResolverResults(
+            resolvedLocalComponentsResult,
+            resolutionResult,
+            visitedArtifacts,
+            resolvedConfiguration,
+            artifactResolveState,
+            resolveException
+        );
+    }
+
+    @Override
+    public ResolverResults updateResolutionResult(Function<ResolutionResult, ResolutionResult> updater) {
+        return new DefaultResolverResults(
+            resolvedLocalComponentsResult,
+            updater.apply(resolutionResult),
+            visitedArtifacts,
+            resolvedConfiguration,
+            artifactResolveState,
+            failure
+        );
+    }
+
+    /**
+     * Create a new result representing the result of resolving build dependencies.
+     */
+    public static ResolverResults buildDependenciesResolved(
+        ResolutionResult resolutionResult,
+        ResolvedLocalComponentsResult resolvedLocalComponentsResult,
+        VisitedArtifactSet visitedArtifacts
+    ) {
+        return new DefaultResolverResults(
+            resolvedLocalComponentsResult,
+            resolutionResult,
+            visitedArtifacts,
+            null,
+            null,
+            null
+        );
+    }
+
+    /**
+     * Create a new result representing the result of resolving the dependency graph.
+     */
+    public static ResolverResults graphResolved(
+        ResolutionResult resolutionResult,
+        ResolvedLocalComponentsResult resolvedLocalComponentsResult,
+        VisitedArtifactSet visitedArtifacts,
+        @Nullable ArtifactResolveState artifactResolveState
+    ) {
+        return new DefaultResolverResults(
+            resolvedLocalComponentsResult,
+            resolutionResult,
+            visitedArtifacts,
+            null,
+            artifactResolveState,
+            null
+        );
+    }
+
+    /**
+     * Create a new result representing the result of resolving the artifacts.
+     */
+    public static ResolverResults artifactsResolved(ResolutionResult resolutionResult, ResolvedLocalComponentsResult localComponentsResult, ResolvedConfiguration resolvedConfiguration, VisitedArtifactSet visitedArtifacts) {
+        return new DefaultResolverResults(
+            localComponentsResult,
+            resolutionResult,
+            visitedArtifacts,
+            resolvedConfiguration,
+            null, // Do not need to keep the artifact resolve state around after artifact resolution
+            null
+        );
+    }
+
+    /**
+     * Create a new result representing a failure to resolve the dependency graph.
+     */
+    public static ResolverResults failed(Exception failure, ResolveException contextualizedFailure) {
+        BrokenResolvedConfiguration broken = new BrokenResolvedConfiguration(failure, contextualizedFailure);
+        return new DefaultResolverResults(
+            null,
+            null,
+            broken,
+            broken,
+            null,
+            contextualizedFailure
+        );
+    }
+
+    /**
+     * Create a new result representing a failure to resolve the artifacts of a resolved dependency graph.
+     */
+    public static ResolverResults failed(ResolutionResult resolutionResult, ResolvedLocalComponentsResult localComponentsResult, Exception failure, ResolveException contextualizedFailure) {
+        BrokenResolvedConfiguration broken = new BrokenResolvedConfiguration(failure, contextualizedFailure);
+        return artifactsResolved(resolutionResult, localComponentsResult, broken, broken).withFailure(contextualizedFailure);
+    }
+
+    /**
+     * Allows resolution failures to be visited.
+     */
+    private static class BrokenResolvedConfiguration implements ResolvedConfiguration, VisitedArtifactSet, SelectedArtifactSet {
+        private final Throwable originalException;
+        private final ResolveException contextualizedException;
+
+        public BrokenResolvedConfiguration(Throwable originalException, ResolveException contextualizedException) {
+            this.originalException = originalException;
+            this.contextualizedException = contextualizedException;
         }
-    }
 
-    private static boolean isNonFatalError(ResolveException failure) {
-        return failure.getCause() instanceof GraphValidationException;
-    }
-
-    @Override
-    public void artifactsResolved(ResolvedConfiguration resolvedConfiguration, VisitedArtifactSet visitedArtifacts) {
-        this.resolvedConfiguration = resolvedConfiguration;
-        this.visitedArtifacts = visitedArtifacts;
-        this.artifactResolveState = null;
-    }
-
-    @Override
-    public ResolveException consumeNonFatalFailure() {
-        try {
-            return nonFatalFailure;
-        } finally {
-            nonFatalFailure = null;
+        @Override
+        public boolean hasError() {
+            return true;
         }
-    }
 
-    @Override
-    public Throwable getFailure() {
-        if (fatalFailure != null) {
-            return fatalFailure;
+        @Override
+        public LenientConfiguration getLenientConfiguration() {
+            throw contextualizedException;
         }
-        if (nonFatalFailure != null) {
-            return nonFatalFailure;
+
+        @Override
+        public void rethrowFailure() throws ResolveException {
+            throw contextualizedException;
         }
-        return null;
-    }
 
-    @Override
-    public void retainState(Object artifactResolveState) {
-        this.artifactResolveState = artifactResolveState;
-    }
+        @Override
+        public Set<File> getFiles() throws ResolveException {
+            throw contextualizedException;
+        }
 
-    @Override
-    public Object getArtifactResolveState() {
-        maybeRethrowAnyError();
-        return artifactResolveState;
+        @Override
+        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) throws ResolveException {
+            throw contextualizedException;
+        }
+
+        @Override
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveException {
+            throw contextualizedException;
+        }
+
+        @Override
+        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
+            throw contextualizedException;
+        }
+
+        @Override
+        public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
+            throw contextualizedException;
+        }
+
+        @Override
+        public SelectedArtifactSet select(Spec<? super Dependency> dependencySpec, AttributeContainerInternal requestedAttributes, Spec<? super ComponentIdentifier> componentSpec, boolean allowNoMatchingVariant, boolean selectFromAllVariants) {
+            return this;
+        }
+
+        @Override
+        public void visitDependencies(TaskDependencyResolveContext context) {
+            context.visitFailure(originalException);
+        }
+
+        @Override
+        public void visitArtifacts(ArtifactVisitor visitor, boolean continueOnSelectionFailure) {
+            visitor.visitFailure(originalException);
+        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java
index 6fc06c3..21da2a6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildScopeServices.java
@@ -33,7 +33,6 @@
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheMetadata;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCachesProvider;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ChangingValueDependencyResolutionListener;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConnectionFailureRepositoryDisabler;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleDescriptorHashCodec;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleDescriptorHashModuleSource;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryDisabler;
@@ -74,10 +73,10 @@
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.InMemoryModuleVersionsCache;
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.ReadOnlyModuleVersionsCache;
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.TwoStageModuleVersionsCache;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyMetadataFactory;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectLocalComponentProvider;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.DefaultProjectPublicationRegistry;
-import org.gradle.api.internal.artifacts.ivyservice.projectmodule.LocalComponentRegistry;
+import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyResolver;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.DefaultArtifactDependencyResolver;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariantCache;
@@ -98,9 +97,10 @@
 import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceAccessor;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
-import org.gradle.api.internal.artifacts.transform.TransformationNodeDependencyResolver;
+import org.gradle.api.internal.artifacts.transform.TransformStepNodeDependencyResolver;
 import org.gradle.api.internal.artifacts.verification.signatures.DefaultSignatureVerificationServiceFactory;
 import org.gradle.api.internal.artifacts.verification.signatures.SignatureVerificationServiceFactory;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.internal.catalog.DefaultDependenciesAccessors;
 import org.gradle.api.internal.catalog.DependenciesAccessorsWorkspaceProvider;
@@ -127,14 +127,17 @@
 import org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory;
 import org.gradle.configuration.internal.UserCodeApplicationContext;
 import org.gradle.initialization.DependenciesAccessors;
-import org.gradle.initialization.internal.InternalBuildFinishedListener;
+import org.gradle.internal.build.BuildModelLifecycleListener;
 import org.gradle.internal.build.BuildState;
 import org.gradle.internal.build.BuildStateRegistry;
 import org.gradle.internal.buildoption.FeatureFlags;
 import org.gradle.internal.classpath.ClasspathBuilder;
 import org.gradle.internal.classpath.ClasspathWalker;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveStateFactory;
 import org.gradle.internal.component.external.model.PreferJavaRuntimeVariant;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory;
+import org.gradle.internal.component.model.ComponentIdGenerator;
 import org.gradle.internal.component.model.PersistentModuleSource;
 import org.gradle.internal.component.model.VariantResolveMetadata;
 import org.gradle.internal.event.ListenerManager;
@@ -218,10 +221,12 @@
  */
 class DependencyManagementBuildScopeServices {
     void configure(ServiceRegistration registration) {
-        registration.add(TransformationNodeDependencyResolver.class);
+        registration.add(TransformStepNodeDependencyResolver.class);
         registration.add(DefaultProjectLocalComponentProvider.class);
         registration.add(DefaultProjectPublicationRegistry.class);
         registration.add(FileResourceConnector.class);
+        registration.add(DefaultComponentSelectorConverter.class);
+        registration.add(ProjectDependencyResolver.class);
     }
 
     DependencyResolutionManagementInternal createSharedDependencyResolutionServices(Instantiator instantiator,
@@ -484,10 +489,6 @@ RepositoryTransportFactory createRepositoryTransportFactory(TemporaryFileProvide
         ));
     }
 
-    RepositoryDisabler createRepositoryDisabler() {
-        return new ConnectionFailureRepositoryDisabler();
-    }
-
     DependencyVerificationOverride createDependencyVerificationOverride(StartParameterResolutionOverride startParameterResolutionOverride,
                                                                         BuildOperationExecutor buildOperationExecutor,
                                                                         ChecksumService checksumService,
@@ -510,6 +511,7 @@ ResolveIvyFactory createResolveIvyFactory(StartParameterResolutionOverride start
                                               RepositoryDisabler repositoryBlacklister,
                                               VersionParser versionParser,
                                               ListenerManager listenerManager,
+                                              ModuleComponentGraphResolveStateFactory resolveStateFactory,
                                               CalculatedValueContainerFactory calculatedValueContainerFactory) {
         return new ResolveIvyFactory(
             moduleRepositoryCacheProvider,
@@ -521,6 +523,7 @@ ResolveIvyFactory createResolveIvyFactory(StartParameterResolutionOverride start
             repositoryBlacklister,
             versionParser,
             listenerManager.getBroadcaster(ChangingValueDependencyResolutionListener.class),
+            resolveStateFactory,
             calculatedValueContainerFactory);
     }
 
@@ -535,7 +538,7 @@ public ResolvedVariant computeIfAbsent(VariantResolveMetadata.Identifier key, Fu
     }
 
     ArtifactDependencyResolver createArtifactDependencyResolver(ResolveIvyFactory resolveIvyFactory,
-                                                                DependencyDescriptorFactory dependencyDescriptorFactory,
+                                                                DependencyMetadataFactory dependencyMetadataFactory,
                                                                 VersionComparator versionComparator,
                                                                 List<ResolverProviderFactory> resolverFactories,
                                                                 ModuleExclusions moduleExclusions,
@@ -548,12 +551,16 @@ ArtifactDependencyResolver createArtifactDependencyResolver(ResolveIvyFactory re
                                                                 InstantiatorFactory instantiatorFactory,
                                                                 ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
                                                                 CalculatedValueContainerFactory calculatedValueContainerFactory,
-                                                                ResolvedVariantCache resolvedVariantCache) {
+                                                                ResolvedVariantCache resolvedVariantCache,
+                                                                AttributeDesugaring attributeDesugaring,
+                                                                LocalComponentGraphResolveStateFactory localResolveStateFactory,
+                                                                ModuleComponentGraphResolveStateFactory moduleResolveStateFactory,
+                                                                ComponentIdGenerator idGenerator) {
         return new DefaultArtifactDependencyResolver(
             buildOperationExecutor,
             resolverFactories,
             resolveIvyFactory,
-            dependencyDescriptorFactory,
+            dependencyMetadataFactory,
             versionComparator,
             moduleExclusions,
             componentSelectorConverter,
@@ -563,11 +570,12 @@ ArtifactDependencyResolver createArtifactDependencyResolver(ResolveIvyFactory re
             componentMetadataSupplierRuleExecutor,
             instantiatorFactory,
             componentSelectionDescriptorFactory,
-            calculatedValueContainerFactory, resolvedVariantCache);
-    }
-
-    ComponentSelectorConverter createModuleVersionSelectorFactory(ComponentIdentifierFactory componentIdentifierFactory, LocalComponentRegistry localComponentRegistry) {
-        return new DefaultComponentSelectorConverter(componentIdentifierFactory, localComponentRegistry);
+            calculatedValueContainerFactory,
+            resolvedVariantCache,
+            attributeDesugaring,
+            localResolveStateFactory,
+            moduleResolveStateFactory,
+            idGenerator);
     }
 
     VersionSelectorScheme createVersionSelectorScheme(VersionComparator versionComparator, VersionParser versionParser) {
@@ -605,9 +613,9 @@ ComponentMetadataSupplierRuleExecutor createComponentMetadataSupplierRuleExecuto
                                                                                       SuppliedComponentMetadataSerializer suppliedComponentMetadataSerializer,
                                                                                       ListenerManager listenerManager) {
         if (cacheDecoratorFactory instanceof CleaningInMemoryCacheDecoratorFactory) {
-            listenerManager.addListener(new InternalBuildFinishedListener() {
+            listenerManager.addListener(new BuildModelLifecycleListener() {
                 @Override
-                public void buildFinished(GradleInternal build, boolean failed) {
+                public void beforeModelDiscarded(GradleInternal model, boolean buildFailed) {
                     ((CleaningInMemoryCacheDecoratorFactory) cacheDecoratorFactory).clearCaches(ComponentMetadataRuleExecutor::isMetadataRuleExecutorCache);
                 }
             });
@@ -629,10 +637,10 @@ SignatureVerificationServiceFactory createSignatureVerificationServiceFactory(
     }
 
     private void registerBuildFinishedHooks(ListenerManager listenerManager, DependencyVerificationOverride dependencyVerificationOverride) {
-        listenerManager.addListener(new InternalBuildFinishedListener() {
+        listenerManager.addListener(new BuildModelLifecycleListener() {
             @Override
-            public void buildFinished(GradleInternal build, boolean failed) {
-                dependencyVerificationOverride.buildFinished(build);
+            public void beforeModelDiscarded(GradleInternal model, boolean buildFailed) {
+                dependencyVerificationOverride.buildFinished(model);
             }
         });
     }
@@ -653,7 +661,7 @@ DependenciesAccessors createDependenciesAccessorGenerator(ClassPathRegistry regi
     /**
      * Execution engine for usage above Gradle scope
      *
-     * Currently used for running artifact transformations in buildscript blocks.
+     * Currently used for running artifact transforms in buildscript blocks.
      */
     ExecutionEngine createExecutionEngine(
         BuildOperationExecutor buildOperationExecutor,
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildSessionScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildSessionScopeServices.java
index 820dbec..9d778d5 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildSessionScopeServices.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildSessionScopeServices.java
@@ -17,6 +17,7 @@
 
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.CachingComponentSelectionDescriptorFactory;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorFactory;
+import org.gradle.api.internal.attributes.DefaultImmutableAttributesFactory;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.internal.catalog.DependenciesAccessorsWorkspaceProvider;
 import org.gradle.api.internal.model.NamedObjectInstantiator;
@@ -27,6 +28,7 @@ public class DependencyManagementBuildSessionScopeServices {
 
     void configure(ServiceRegistration registration) {
         registration.add(DependenciesAccessorsWorkspaceProvider.class);
+        registration.add(DefaultImmutableAttributesFactory.class);
     }
 
     ComponentSelectionDescriptorFactory createComponentSelectionDescriptorFactory() {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildTreeScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildTreeScopeServices.java
index b492b4f..3012eb6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildTreeScopeServices.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementBuildTreeScopeServices.java
@@ -20,15 +20,23 @@
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheLockingAccessCoordinator;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheMetadata;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCachesProvider;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConnectionFailureRepositoryDisabler;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.StartParameterResolutionOverride;
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.FileStoreAndIndexProvider;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectArtifactResolver;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ThisBuildOnlyComponentDetailsSerializer;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ThisBuildOnlySelectedVariantSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.ResolutionResultsStoreFactory;
+import org.gradle.api.internal.artifacts.transform.TransformStepNodeFactory;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.file.temp.TemporaryFileProvider;
 import org.gradle.api.internal.filestore.ArtifactIdentifierFileStore;
 import org.gradle.api.internal.filestore.DefaultArtifactIdentifierFileStore;
 import org.gradle.api.internal.filestore.TwoStageArtifactIdentifierFileStore;
 import org.gradle.initialization.layout.BuildLayout;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveStateFactory;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory;
+import org.gradle.internal.component.model.ComponentIdGenerator;
 import org.gradle.internal.resource.cached.ByUrlCachedExternalResourceIndex;
 import org.gradle.internal.resource.cached.CachedExternalResourceIndex;
 import org.gradle.internal.resource.cached.DefaultExternalResourceFileStore;
@@ -48,6 +56,14 @@ void configure(ServiceRegistration registration) {
         registration.add(ProjectArtifactResolver.class);
         registration.add(DefaultExternalResourceFileStore.Factory.class);
         registration.add(DefaultArtifactIdentifierFileStore.Factory.class);
+        registration.add(TransformStepNodeFactory.class);
+        registration.add(AttributeDesugaring.class);
+        registration.add(ComponentIdGenerator.class);
+        registration.add(LocalComponentGraphResolveStateFactory.class);
+        registration.add(ModuleComponentGraphResolveStateFactory.class);
+        registration.add(ThisBuildOnlyComponentDetailsSerializer.class);
+        registration.add(ThisBuildOnlySelectedVariantSerializer .class);
+        registration.add(ConnectionFailureRepositoryDisabler.class);
     }
 
     BuildCommencedTimeProvider createBuildTimeProvider(StartParameter startParameter) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java
index 6397cfb..6e33807 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGlobalScopeServices.java
@@ -26,21 +26,19 @@
 import org.gradle.api.internal.artifacts.ivyservice.IvyContextManager;
 import org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution.ModuleSelectorStringNotationConverter;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultLocalComponentMetadataBuilder;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.LocalComponentMetadataBuilder;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DefaultDependencyDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DefaultDependencyMetadataFactory;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DefaultExcludeRuleConverter;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DefaultLocalConfigurationMetadataBuilder;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyMetadataFactory;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ExcludeRuleConverter;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ExternalModuleIvyDependencyDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ExternalModuleDependencyMetadataConverter;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectIvyDependencyDescriptorFactory;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformActionScheme;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformParameterScheme;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.ProjectDependencyMetadataConverter;
 import org.gradle.api.internal.artifacts.transform.CacheableTransformTypeAnnotationHandler;
 import org.gradle.api.internal.artifacts.transform.InputArtifactAnnotationHandler;
 import org.gradle.api.internal.artifacts.transform.InputArtifactDependenciesAnnotationHandler;
+import org.gradle.api.internal.artifacts.transform.TransformActionScheme;
+import org.gradle.api.internal.artifacts.transform.TransformParameterScheme;
 import org.gradle.api.internal.model.NamedObjectInstantiator;
 import org.gradle.api.internal.tasks.properties.InspectionScheme;
 import org.gradle.api.internal.tasks.properties.InspectionSchemeFactory;
@@ -102,25 +100,20 @@ ExcludeRuleConverter createExcludeRuleConverter(ImmutableModuleIdentifierFactory
         return new DefaultExcludeRuleConverter(moduleIdentifierFactory);
     }
 
-    ExternalModuleIvyDependencyDescriptorFactory createExternalModuleDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
-        return new ExternalModuleIvyDependencyDescriptorFactory(excludeRuleConverter);
+    DependencyMetadataFactory createDependencyMetadataFactory(ExcludeRuleConverter excludeRuleConverter) {
+        return new DefaultDependencyMetadataFactory(
+            new ProjectDependencyMetadataConverter(excludeRuleConverter),
+            new ExternalModuleDependencyMetadataConverter(excludeRuleConverter)
+        );
     }
 
-    DependencyDescriptorFactory createDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter, ExternalModuleIvyDependencyDescriptorFactory descriptorFactory) {
-        return new DefaultDependencyDescriptorFactory(
-            new ProjectIvyDependencyDescriptorFactory(excludeRuleConverter),
-            descriptorFactory);
-    }
-
-    LocalConfigurationMetadataBuilder createLocalConfigurationMetadataBuilder(DependencyDescriptorFactory dependencyDescriptorFactory,
-                                                                              ExcludeRuleConverter excludeRuleConverter) {
+    LocalConfigurationMetadataBuilder createLocalConfigurationMetadataBuilder(
+        DependencyMetadataFactory dependencyDescriptorFactory,
+        ExcludeRuleConverter excludeRuleConverter
+    ) {
         return new DefaultLocalConfigurationMetadataBuilder(dependencyDescriptorFactory, excludeRuleConverter);
     }
 
-    LocalComponentMetadataBuilder createLocalComponentMetaDataBuilder(LocalConfigurationMetadataBuilder localConfigurationMetadataBuilder) {
-        return new DefaultLocalComponentMetadataBuilder(localConfigurationMetadataBuilder);
-    }
-
     ResourceConnectorFactory createFileConnectorFactory() {
         return new FileConnectorFactory();
     }
@@ -149,7 +142,7 @@ PlatformSupport createPlatformSupport(NamedObjectInstantiator instantiator) {
         return new PlatformSupport(instantiator);
     }
 
-    ArtifactTransformParameterScheme createArtifactTransformParameterScheme(InspectionSchemeFactory inspectionSchemeFactory, InstantiatorFactory instantiatorFactory) {
+    TransformParameterScheme createTransformParameterScheme(InspectionSchemeFactory inspectionSchemeFactory, InstantiatorFactory instantiatorFactory) {
         InstantiationScheme instantiationScheme = instantiatorFactory.decorateScheme();
         InspectionScheme inspectionScheme = inspectionSchemeFactory.inspectionScheme(
             ImmutableSet.of(
@@ -174,10 +167,10 @@ ArtifactTransformParameterScheme createArtifactTransformParameterScheme(Inspecti
             ),
             instantiationScheme
         );
-        return new ArtifactTransformParameterScheme(instantiationScheme, inspectionScheme);
+        return new TransformParameterScheme(instantiationScheme, inspectionScheme);
     }
 
-    ArtifactTransformActionScheme createArtifactTransformActionScheme(InspectionSchemeFactory inspectionSchemeFactory, InstantiatorFactory instantiatorFactory) {
+    TransformActionScheme createTransformActionScheme(InspectionSchemeFactory inspectionSchemeFactory, InstantiatorFactory instantiatorFactory) {
         InstantiationScheme instantiationScheme = instantiatorFactory.injectScheme(ImmutableSet.of(
             InputArtifact.class,
             InputArtifactDependencies.class
@@ -198,6 +191,6 @@ ArtifactTransformActionScheme createArtifactTransformActionScheme(InspectionSche
             ),
             instantiationScheme
         );
-        return new ArtifactTransformActionScheme(instantiationScheme, inspectionScheme);
+        return new TransformActionScheme(instantiationScheme, inspectionScheme);
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGradleUserHomeScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGradleUserHomeScopeServices.java
index 5ccb263..2918d0a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGradleUserHomeScopeServices.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementGradleUserHomeScopeServices.java
@@ -21,8 +21,8 @@
 import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCachesProvider;
 import org.gradle.api.internal.artifacts.ivyservice.DefaultArtifactCaches;
-import org.gradle.api.internal.artifacts.transform.ImmutableTransformationWorkspaceServices;
-import org.gradle.api.internal.artifacts.transform.ToPlannedTransformConverter;
+import org.gradle.api.internal.artifacts.transform.ImmutableTransformWorkspaceServices;
+import org.gradle.api.internal.artifacts.transform.ToPlannedTransformStepConverter;
 import org.gradle.api.internal.cache.CacheConfigurationsInternal;
 import org.gradle.api.internal.cache.StringInterner;
 import org.gradle.api.internal.changedetection.state.DefaultExecutionHistoryCacheAccess;
@@ -43,8 +43,8 @@
 
 public class DependencyManagementGradleUserHomeScopeServices {
 
-    ToPlannedNodeConverter createToPlannedTransformConverter() {
-        return new ToPlannedTransformConverter();
+    ToPlannedNodeConverter createToPlannedTransformStepConverter() {
+        return new ToPlannedTransformStepConverter();
     }
 
     DefaultArtifactCaches.WritableArtifactCacheLockingParameters createWritableArtifactCacheLockingParameters(FileAccessTimeJournal fileAccessTimeJournal, UsedGradleVersions usedGradleVersions) {
@@ -100,7 +100,7 @@ ExecutionHistoryStore createExecutionHistoryStore(
         );
     }
 
-    ImmutableTransformationWorkspaceServices createTransformerWorkspaceServices(
+    ImmutableTransformWorkspaceServices createTransformWorkspaceServices(
         ArtifactCachesProvider artifactCaches,
         UnscopedCacheBuilderFactory unscopedCacheBuilderFactory,
         CrossBuildInMemoryCacheFactory crossBuildInMemoryCacheFactory,
@@ -108,7 +108,7 @@ ImmutableTransformationWorkspaceServices createTransformerWorkspaceServices(
         ExecutionHistoryStore executionHistoryStore,
         CacheConfigurationsInternal cacheConfigurations
     ) {
-        return new ImmutableTransformationWorkspaceServices(
+        return new ImmutableTransformWorkspaceServices(
             unscopedCacheBuilderFactory
                 .cache(artifactCaches.getWritableCacheMetadata().getTransformsStoreDirectory())
                 .withCrossVersionCache(CacheBuilder.LockTarget.DefaultTarget)
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementProjectScopeServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementProjectScopeServices.java
index 4604b7e..88548ea 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementProjectScopeServices.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyManagementProjectScopeServices.java
@@ -24,7 +24,7 @@
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactoryInternal;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheLockingAccessCoordinator;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCacheMetadata;
-import org.gradle.api.internal.artifacts.transform.TransformationNodeDependencyResolver;
+import org.gradle.api.internal.artifacts.transform.TransformStepNodeDependencyResolver;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.internal.filestore.DefaultArtifactIdentifierFileStore;
@@ -51,7 +51,7 @@ class DependencyManagementProjectScopeServices {
     void configure(ServiceRegistration registration) {
         registration.add(DefaultExternalResourceFileStore.Factory.class);
         registration.add(DefaultArtifactIdentifierFileStore.Factory.class);
-        registration.add(TransformationNodeDependencyResolver.class);
+        registration.add(TransformStepNodeDependencyResolver.class);
     }
 
     /** This is needed in order to get the {@code taskDependencyFactory} from the current project. */
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyServices.java
index 42dc11a..c7cafc8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyServices.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/DependencyServices.java
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSetToFileCollectionFactory;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransformListener;
+import org.gradle.api.internal.artifacts.transform.TransformExecutionListener;
 import org.gradle.internal.event.ListenerManager;
 import org.gradle.internal.service.ServiceRegistration;
 import org.gradle.internal.service.scopes.AbstractPluginServiceRegistry;
@@ -61,8 +61,8 @@ public void registerGradleServices(ServiceRegistration registration) {
 
     @SuppressWarnings("unused")
     private static class DependencyManagementGradleServices {
-        ArtifactTransformListener createArtifactTransformListener(ListenerManager listenerManager) {
-            return listenerManager.getBroadcaster(ArtifactTransformListener.class);
+        TransformExecutionListener createTransformExecutionListener(ListenerManager listenerManager) {
+            return listenerManager.getBroadcaster(TransformExecutionListener.class);
         }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ImmutableModuleIdentifierFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ImmutableModuleIdentifierFactory.java
index 4ab2f48..c36b9a9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ImmutableModuleIdentifierFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ImmutableModuleIdentifierFactory.java
@@ -24,6 +24,5 @@
 public interface ImmutableModuleIdentifierFactory {
     ModuleIdentifier module(String group, String name);
     ModuleVersionIdentifier moduleWithVersion(String group, String name, String version);
-    ModuleVersionIdentifier moduleWithVersion(Module module);
     ModuleVersionIdentifier moduleWithVersion(ModuleIdentifier targetModuleId, String version);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolveContext.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolveContext.java
index 4d527ee..241c277 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolveContext.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolveContext.java
@@ -15,33 +15,65 @@
  */
 package org.gradle.api.internal.artifacts;
 
-import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
+import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependenciesResolverFactory;
+import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.internal.component.local.model.LocalComponentMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
+import org.gradle.util.Path;
 
 import java.util.List;
 
 /**
  * Represents something that can be resolved.
  */
-public interface ResolveContext {
+public interface ResolveContext extends DependencyMetaDataProvider {
 
     String getName();
 
     String getDisplayName();
 
+    Path getIdentityPath();
+
+    Path getProjectPath();
+
+    DomainObjectContext getDomainObjectContext();
+
     ResolutionStrategyInternal getResolutionStrategy();
 
+    boolean hasDependencies();
+
+    /**
+     * @implSpec Usage: This method should only be called on resolvable configurations and should throw an exception if
+     * called on a configuration that does not permit this usage.
+     */
     LocalComponentMetadata toRootComponentMetaData();
 
-    AttributeContainer getAttributes();
+    AttributeContainerInternal getAttributes();
+
+    /**
+     * @implSpec Usage: This method should only be called on resolvable configurations and should throw an exception if
+     * called on a configuration that does not permit this usage.
+     */
+    TransformUpstreamDependenciesResolverFactory getDependenciesResolverFactory();
 
     /**
      * Returns the synthetic dependencies for this context. These dependencies are generated
      * by Gradle and not provided by the user, and are used for dependency locking and consistent resolution.
      * These constraints are not always used during resolution, based on which phase of execution we are in
      * (task dependencies, execution, ...)
+     *
+     * @implSpec Usage: This method should only be called on resolvable configurations and should throw an exception if
+     * called on a configuration that does not permit this usage.
      */
     List<? extends DependencyMetadata> getSyntheticDependencies();
+
+    /**
+     * This method is a heuristic that gives an idea of the "size" of the graph. The larger
+     * the graph is, the higher the risk of internal resizes exists, so we try to estimate
+     * the size of the graph to avoid maps resizing.
+     */
+    int getEstimatedGraphSize();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolveExceptionContextualizer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolveExceptionContextualizer.java
new file mode 100644
index 0000000..dd9ad34
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolveExceptionContextualizer.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.exceptions.ResolutionProvider;
+import org.gradle.internal.resolve.ModuleVersionNotFoundException;
+
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Adds additional hints to a {@link ResolveException} given the context of a {@link ResolveContext}.
+ */
+public class ResolveExceptionContextualizer {
+
+    private final DomainObjectContext domainObjectContext;
+    private final DocumentationRegistry documentationRegistry;
+
+    public ResolveExceptionContextualizer(
+        DomainObjectContext domainObjectContext,
+        DocumentationRegistry documentationRegistry
+    ) {
+        this.domainObjectContext = domainObjectContext;
+        this.documentationRegistry = documentationRegistry;
+    }
+
+    public ResolveException contextualize(Throwable e, ResolveContext resolveContext) {
+        if (e instanceof ResolveException) {
+            return contextualize((ResolveException) e, resolveContext.getName());
+        }
+
+        ResolveException result = maybeContextualize(resolveContext.getDisplayName(), Collections.singletonList(e), resolveContext.getDomainObjectContext(), documentationRegistry);
+        if (result == null) {
+            return new ResolveException(resolveContext.getDisplayName(), e);
+        }
+        return result;
+    }
+
+    public ResolveException contextualize(ResolveException e, String resolveContextName) {
+        ResolveException result = maybeContextualize(resolveContextName, e.getCauses(), domainObjectContext, documentationRegistry);
+        if (result == null) {
+            return e;
+        }
+        return result;
+    }
+
+    @Nullable
+    public ResolveException maybeContextualize(Collection<? extends Throwable> causes, ResolveContext resolveContext) {
+        return maybeContextualize(resolveContext.getDisplayName(), causes, resolveContext.getDomainObjectContext(), documentationRegistry);
+    }
+
+    @Nullable
+    private static ResolveException maybeContextualize(String contextName, Collection<? extends Throwable> causes, DomainObjectContext domainObjectContext, DocumentationRegistry documentationRegistry) {
+        try {
+            boolean ignoresSettingsRepositories = false;
+            if (domainObjectContext instanceof ProjectInternal) {
+                ProjectInternal project = (ProjectInternal) domainObjectContext;
+                ignoresSettingsRepositories = !project.getRepositories().isEmpty() &&
+                    !project.getGradle().getSettings().getDependencyResolutionManagement().getRepositories().isEmpty();
+            }
+
+            boolean hasModuleNotFound = causes.stream().anyMatch(ModuleVersionNotFoundException.class::isInstance);
+
+            if (ignoresSettingsRepositories && hasModuleNotFound) {
+                return new ResolveExceptionWithHints(contextName, causes,
+                    "The project declares repositories, effectively ignoring the repositories you have declared in the settings.\n" +
+                        "You can figure out how project repositories are declared by configuring your build to fail on project repositories.\n" +
+                        documentationRegistry.getDocumentationRecommendationFor("information", "declaring_repositories", "sub:fail_build_on_project_repositories")
+                );
+            }
+
+            return null;
+        } catch (Throwable e) {
+            return new ResolveException(contextName, ImmutableList.<Throwable>builder().addAll(causes).add(e).build());
+        }
+    }
+
+    public static class ResolveExceptionWithHints extends ResolveException implements ResolutionProvider {
+
+        private final List<String> resolutions;
+
+        public ResolveExceptionWithHints(String resolveContext, Iterable<? extends Throwable> causes, String resolution) {
+            super(resolveContext, causes);
+            this.resolutions = ImmutableList.of(resolution);
+        }
+
+        @Override
+        public List<String> getResolutions() {
+            return resolutions;
+        }
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolverResults.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolverResults.java
index 3d99673..60e335a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolverResults.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ResolverResults.java
@@ -13,17 +13,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.gradle.api.internal.artifacts;
 
 import org.gradle.api.artifacts.ResolveException;
 import org.gradle.api.artifacts.ResolvedConfiguration;
 import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveState;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedLocalComponentsResult;
 
 import javax.annotation.Nullable;
+import java.util.function.Function;
 
+/**
+ * Immutable representation of the state of dependency resolution. Can represent intermediate resolution states after
+ * build dependency resolution, graph resolution, and artifact resolution. Results can have attached failures
+ * in cases of partial resolution successes.
+ *
+ * <p>This should eventually be merged with {@link org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.ResolveState}</p>
+ */
+@SuppressWarnings("JavadocReference")
 public interface ResolverResults {
+
+    /**
+     * Returns true if there was a failure attached to this result.
+     */
     boolean hasError();
 
     /**
@@ -32,56 +47,50 @@ public interface ResolverResults {
     ResolvedConfiguration getResolvedConfiguration();
 
     /**
-     * Returns details of the artifacts visited during dependency graph resolution. This set is later refined during artifact resolution and replaced with a new instance.
+     * Returns details of the artifacts visited during dependency graph resolution. This set is later refined during artifact resolution.
      */
     VisitedArtifactSet getVisitedArtifacts();
 
     /**
      * Returns the dependency graph resolve result.
      */
+    @Nullable
     ResolutionResult getResolutionResult();
 
     /**
      * Returns details of the local components in the resolved dependency graph.
      */
+    @Nullable
     ResolvedLocalComponentsResult getResolvedLocalComponents();
 
     /**
-     * Marks the dependency graph resolution as successful, with the given result.
-     */
-    void graphResolved(ResolutionResult resolutionResult, ResolvedLocalComponentsResult resolvedLocalComponentsResult, VisitedArtifactSet visitedArtifacts);
-
-    void failed(ResolveException failure);
-
-    /**
-     * Attaches some opaque state calculated during dependency graph resolution that will later be required to resolve the artifacts.
-     */
-    void retainState(Object artifactResolveState);
-
-    /**
-     * Returns the opaque state required to resolve the artifacts.
-     */
-    Object getArtifactResolveState();
-
-    /**
-     * Marks artifact resolution as successful, clearing state provided by {@link #retainState(Object)}.
-     */
-    void artifactsResolved(ResolvedConfiguration resolvedConfiguration, VisitedArtifactSet visitedArtifacts);
-
-    /**
-     * Consumes the failure, allowing to either throw or do something else with it. Consuming effectively
-     * removes the exception from the underlying resolver results, meaning that subsequent calls to consume
-     * will return null.
+     * Returns intermediate state saved between dependency graph resolution and artifact resolution.
      */
     @Nullable
-    ResolveException consumeNonFatalFailure();
+    ArtifactResolveState getArtifactResolveState();
 
     /**
-     * Returns the failure, fatal or non fatal, or null if there's no failure. Used internally to
-     * set the failure on the resolution build operation result. In opposite to {@link #consumeNonFatalFailure()},
-     * this doesn't consume the error, so subsequent calls will return the same instance, unless the error was
-     * consumed in between.
+     * Returns the non-fatal failure, if present.
      */
     @Nullable
-    Throwable getFailure();
+    Throwable getNonFatalFailure();
+
+    /**
+     * Returns the failure, fatal or non-fatal, or null if there's no failure. Used internally to
+     * set the failure on the resolution build operation result.
+     */
+    @Nullable
+    ResolveException getFailure();
+
+    /**
+     * Return a new result with the provided {@code resolveException} attached.
+     */
+    ResolverResults withFailure(ResolveException failure);
+
+    /**
+     * Returns a new result with a resolution result equal to the value returned by the provided updater.
+     *
+     * @param updater a function that takes the current resolution result and returns a new resolution result
+     */
+    ResolverResults updateResolutionResult(Function<ResolutionResult, ResolutionResult> updater);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/TransformRegistration.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/TransformRegistration.java
new file mode 100644
index 0000000..1a6b8d1
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/TransformRegistration.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 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.api.internal.artifacts;
+
+import org.gradle.api.internal.artifacts.transform.TransformStep;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+
+/**
+ * Registration of an artifact transform.
+ */
+public interface TransformRegistration {
+    /**
+     * Attributes that match the variant that is consumed.
+     */
+    ImmutableAttributes getFrom();
+
+    /**
+     * Attributes that match the variant that is produced.
+     */
+    ImmutableAttributes getTo();
+
+    /**
+     * Transform step for artifacts of the variant.
+     */
+    TransformStep getTransformStep();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/VariantTransformRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/VariantTransformRegistry.java
index 69646ee..d345dc0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/VariantTransformRegistry.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/VariantTransformRegistry.java
@@ -26,11 +26,11 @@
 public interface VariantTransformRegistry {
 
     /**
-     * Register an artifact transformation.
+     * Register an artifact transform.
      *
      * @see TransformAction
      */
     <T extends TransformParameters> void registerTransform(Class<? extends TransformAction<T>> actionType, Action<? super TransformSpec<T>> registrationAction);
 
-    List<ArtifactTransformRegistration> getTransforms();
+    List<TransformRegistration> getRegistrations();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifierFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifierFactory.java
index 1d1e474..250b4b0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifierFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifierFactory.java
@@ -17,14 +17,14 @@
 package org.gradle.api.internal.artifacts.component;
 
 import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
 import org.gradle.api.artifacts.component.ProjectComponentSelector;
 import org.gradle.api.internal.artifacts.Module;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
 
+@ServiceScope(Scopes.Build.class)
 public interface ComponentIdentifierFactory {
     ComponentIdentifier createComponentIdentifier(Module module);
 
     ProjectComponentSelector createProjectComponentSelector(String projectPath);
-
-    ProjectComponentIdentifier createProjectComponentIdentifier(ProjectComponentSelector selector);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java
index b44b394..6646bc9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/component/DefaultComponentIdentifierFactory.java
@@ -48,9 +48,4 @@ public ComponentIdentifier createComponentIdentifier(Module module) {
     public ProjectComponentSelector createProjectComponentSelector(String projectPath) {
         return DefaultProjectComponentSelector.newSelector(currentBuild.getProjects().getProject(Path.path(projectPath)).getComponentIdentifier());
     }
-
-    @Override
-    public ProjectComponentIdentifier createProjectComponentIdentifier(ProjectComponentSelector selector) {
-        return ((DefaultProjectComponentSelector) selector).toIdentifier();
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationContainerInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationContainerInternal.java
index a13b429..c8a09b4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationContainerInternal.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationContainerInternal.java
@@ -15,13 +15,14 @@
  */
 package org.gradle.api.internal.artifacts.configurations;
 
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.UnknownConfigurationException;
 
-public interface ConfigurationContainerInternal extends ConfigurationContainer {
+public interface ConfigurationContainerInternal extends RoleBasedConfigurationContainerInternal {
     @Override
     ConfigurationInternal getByName(String name) throws UnknownConfigurationException;
     @Override
     ConfigurationInternal detachedConfiguration(Dependency... dependencies);
+
+
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java
index 18d6e92..b269b9d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationInternal.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 the original author or authors.
+ * Copyright 2023 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.
@@ -20,23 +20,20 @@
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.ExcludeRule;
 import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.ResolveException;
 import org.gradle.api.capabilities.Capability;
 import org.gradle.api.internal.artifacts.ResolveContext;
-import org.gradle.api.internal.artifacts.transform.ExtraExecutionGraphDependenciesResolverFactory;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.DisplayName;
 import org.gradle.internal.FinalizableValue;
 import org.gradle.internal.deprecation.DeprecatableConfiguration;
-import org.gradle.util.Path;
 
 import javax.annotation.Nullable;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
-public interface ConfigurationInternal extends ResolveContext, Configuration, DeprecatableConfiguration, DependencyMetaDataProvider, FinalizableValue {
+public interface ConfigurationInternal extends ResolveContext, DeprecatableConfiguration, FinalizableValue, Configuration {
     enum InternalState {
         UNRESOLVED,
         BUILD_DEPENDENCIES_RESOLVED,
@@ -45,19 +42,8 @@ enum InternalState {
     }
 
     @Override
-    ResolutionStrategyInternal getResolutionStrategy();
-
-    @Override
     AttributeContainerInternal getAttributes();
 
-    String getPath();
-
-    Path getIdentityPath();
-
-    void setReturnAllVariants(boolean returnAllVariants);
-
-    boolean getReturnAllVariants();
-
     /**
      * Runs any registered dependency actions for this Configuration, and any parent Configuration.
      * Actions may mutate the dependency set for this configuration.
@@ -72,11 +58,6 @@ enum InternalState {
     void removeMutationValidator(MutationValidator validator);
 
     /**
-     * Converts this configuration to an {@link OutgoingVariant} view. The view may not necessarily be immutable.
-     */
-    OutgoingVariant convertToOutgoingVariant();
-
-    /**
      * Visits the variants of this configuration.
      */
     void collectVariants(VariantVisitor visitor);
@@ -99,61 +80,29 @@ enum InternalState {
     List<? extends GradleException> preventFromFurtherMutationLenient();
 
     /**
-     * Reports whether this configuration uses {@link org.gradle.api.Incubating Incubating} attributes types, such as {@link org.gradle.api.attributes.Category#VERIFICATION}.
-     * @return
-     */
-    boolean isIncubating();
-
-    /**
      * Gets the complete set of exclude rules including those contributed by
      * superconfigurations.
      */
     Set<ExcludeRule> getAllExcludeRules();
 
-    ExtraExecutionGraphDependenciesResolverFactory getDependenciesResolver();
-
+    /**
+     * @implSpec Usage: This method should only be called on resolvable configurations and should throw an exception if
+     * called on a configuration that does not permit this usage.
+     */
     @Nullable
     ConfigurationInternal getConsistentResolutionSource();
 
     /**
-     * Decorates a resolve exception with more context. This can be used
-     * to give hints to the user when a resolution error happens.
-     * @param e a resolve exception
-     * @return a decorated resolve exception, or the same exception
-     */
-    ResolveException maybeAddContext(ResolveException e);
-
-    /**
      * Test if this configuration can either be declared against or extends another
      * configuration which can be declared against.
      *
      * @return {@code true} if so; {@code false} otherwise
      */
-    default boolean isDeclarableAgainstByExtension() {
-        return isDeclarableAgainstByExtension(this);
+    default boolean isDeclarableByExtension() {
+        return isDeclarableByExtension(this);
     }
 
     /**
-     * Configures if a configuration can have dependencies declared upon it.
-     *
-     */
-    void setCanBeDeclaredAgainst(boolean allowed);
-
-    /**
-     * Returns true if it is allowed to declare dependencies upon this configuration.
-     * Defaults to true.
-     * @return true if this configuration can have dependencies declared
-     */
-    boolean isCanBeDeclaredAgainst();
-
-    /**
-     * Prevents any calls to methods that change this configuration's allowed usage (e.g. {@link #setCanBeConsumed(boolean)},
-     * {@link #setCanBeResolved(boolean)}, {@link #deprecateForResolution(String...)}) from succeeding; and causes them
-     * to throw an exception.
-     */
-    void preventUsageMutation();
-
-    /**
      * Returns the role used to create this configuration and set its initial allowed usage.
      */
     ConfigurationRole getRoleAtCreation();
@@ -166,13 +115,13 @@ default boolean isDeclarableAgainstByExtension() {
      * @param configuration the configuration to test
      * @return {@code true} if so; {@code false} otherwise
      */
-    static boolean isDeclarableAgainstByExtension(ConfigurationInternal configuration) {
-        if (configuration.isCanBeDeclaredAgainst()) {
+    static boolean isDeclarableByExtension(ConfigurationInternal configuration) {
+        if (configuration.isCanBeDeclared()) {
             return true;
         } else {
             return configuration.getExtendsFrom().stream()
                     .map(ConfigurationInternal.class::cast)
-                    .anyMatch(ci -> ci.isDeclarableAgainstByExtension());
+                    .anyMatch(ci -> ci.isDeclarableByExtension());
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRole.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRole.java
deleted file mode 100644
index 2c2f03f..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRole.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright 2022 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.api.internal.artifacts.configurations;
-
-import org.gradle.internal.deprecation.DeprecationLogger;
-
-import javax.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Defines how a {@link org.gradle.api.artifacts.Configuration} is intended to be used.
- *
- * Standard roles are defined in {@link ConfigurationRoles}.
- *
- * @since 8.1
- */
-public interface ConfigurationRole {
-    /**
-     * Returns a human-readable name for this role.
-     */
-    String getName();
-
-    boolean isConsumable();
-    boolean isResolvable();
-    boolean isDeclarableAgainst();
-    boolean isConsumptionDeprecated();
-    boolean isResolutionDeprecated();
-    boolean isDeclarationAgainstDeprecated();
-
-    /**
-     * Obtains a human-readable summary of the usage allowed by the given role.
-     */
-    default String describeUsage() {
-        return RoleDescriber.describeRole(this);
-    }
-
-    /**
-     * Attempts to locate a pre-defined role allowing the given usage in {@link ConfigurationRoles} and return it;
-     * if such a roles does not exist, creates a custom anonymous implementation and returns it instead.
-     *
-     * @param name the name to use when creating a role that wasn't found
-     * @param consumable whether this role is consumable
-     * @param resolvable whether this role is resolvable
-     * @param declarableAgainst whether this role is declarable against
-     * @param consumptionDeprecated whether this role is deprecated for consumption
-     * @param resolutionDeprecated whether this role is deprecated for resolution
-     * @param declarationAgainstDeprecated whether this role is deprecated for declaration against
-     * @param description a custom description to use when creating a role that wasn't found; or {@code null} to use the default description generated by {@link RoleDescriber}
-     * @param warnOnCustomRole {@code true} to warn if this call results in the creation of a custom role; {@code false} otherwise
-     * @return a role with matching usage characteristics
-     */
-    static ConfigurationRole forUsage(String name, boolean consumable, boolean resolvable, boolean declarableAgainst, boolean consumptionDeprecated, boolean resolutionDeprecated, boolean declarationAgainstDeprecated, @Nullable String description, boolean warnOnCustomRole) {
-        if ((!consumable && consumptionDeprecated) || (!resolvable && resolutionDeprecated) || (!declarableAgainst && declarationAgainstDeprecated)) {
-            throw new IllegalArgumentException("Cannot create a role that deprecates a usage that is not allowed");
-        }
-
-        ConfigurationRole result = ConfigurationRoles.byUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated)
-                .map(ConfigurationRole.class::cast)
-                .orElse(new CustomConfigurationRole(name, consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated, description));
-
-        //noinspection SuspiciousMethodCalls
-        if (warnOnCustomRole && !Arrays.asList(ConfigurationRoles.values()).contains(result)) {
-            DeprecationLogger.deprecateBehaviour("Custom configuration roles are deprecated.")
-                    .withAdvice("Use one of the standard roles defined in ConfigurationRoles instead.")
-                    .willBeRemovedInGradle9()
-                    .withUpgradeGuideSection(8, "custom_configuration_roles")
-                    .nagUser();
-        }
-
-        return result;
-    }
-
-    static ConfigurationRole forUsage(boolean consumable, boolean resolvable, boolean declarableAgainst, boolean consumptionDeprecated, boolean resolutionDeprecated, boolean declarationAgainstDeprecated) {
-        return forUsage(RoleDescriber.DEFAULT_CUSTOM_ROLE_NAME, consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated);
-    }
-
-    static ConfigurationRole forUsage(boolean consumable, boolean resolvable, boolean declarableAgainst) {
-        return forUsage(consumable, resolvable, declarableAgainst, false, false, false);
-    }
-
-    static ConfigurationRole forUsage(String name, boolean consumable, boolean resolvable, boolean declarableAgainst, boolean consumptionDeprecated, boolean resolutionDeprecated, boolean declarationAgainstDeprecated) {
-        return forUsage(name, consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated, null, false);
-    }
-
-    /**
-     * This static util class hides methods internal to the {@code default} methods of {@link ConfigurationRole} which
-     * can be used to build a human-readable description of the usage a role allows.
-     */
-    abstract class RoleDescriber {
-        private static final String DEFAULT_CUSTOM_ROLE_NAME = "Custom Role";
-
-        private static final String CONSUMABLE = "Consumable - this configuration can be selected by another project as a dependency";
-        private static final String RESOLVABLE = "Resolvable - this configuration can be resolved by this project to a set of files";
-        private static final String DECLARABLE_AGAINST = "Declarable Against - this configuration can have dependencies added to it";
-        private static final String UNUSABLE = "This configuration does not allow any usage";
-
-        private static final String IS_DEPRECATED = "(but this behavior is marked deprecated)";
-
-        private RoleDescriber() { /* not instantiable */ }
-
-        /**
-         * Builds a human-readable description of the usage allowed by the given role.
-         *
-         * @param role the role to describe
-         * @return a human-readable description of the role's allowed usage
-         */
-        public static String describeRole(ConfigurationRole role) {
-            List<String> descriptions = new ArrayList<>();
-            if (role.isConsumable()) {
-                descriptions.add("\t" + CONSUMABLE + describeDeprecation(role.isConsumptionDeprecated()));
-            }
-            if (role.isResolvable()) {
-                descriptions.add("\t" + RESOLVABLE + describeDeprecation(role.isResolutionDeprecated()));
-            }
-            if (role.isDeclarableAgainst()) {
-                descriptions.add("\t" + DECLARABLE_AGAINST + describeDeprecation(role.isDeclarationAgainstDeprecated()));
-            }
-            if (descriptions.isEmpty()) {
-                descriptions.add("\t" + UNUSABLE);
-            }
-            return String.join("\n", descriptions);
-        }
-
-        private static String describeDeprecation(boolean deprecated) {
-            return deprecated ? " " + IS_DEPRECATED : "";
-        }
-    }
-
-    /**
-     * A custom implementation of {@link ConfigurationRole} that allows for non-standard combinations of usage characteristics and deprecations.
-     */
-    final class CustomConfigurationRole implements ConfigurationRole {
-        private final String name;
-        private final boolean consumable;
-        private final boolean resolvable;
-        private final boolean declarableAgainst;
-        private final boolean consumptionDeprecated;
-        private final boolean resolutionDeprecated;
-        private final boolean declarationAgainstDeprecated;
-        @Nullable
-        private final String description;
-
-        private CustomConfigurationRole(String name, boolean consumable, boolean resolvable, boolean declarableAgainst, boolean consumptionDeprecated, boolean resolutionDeprecated, boolean declarationAgainstDeprecated, @Nullable String description) {
-            this.name = name;
-            this.consumable = consumable;
-            this.resolvable = resolvable;
-            this.declarableAgainst = declarableAgainst;
-            this.consumptionDeprecated = consumptionDeprecated;
-            this.resolutionDeprecated = resolutionDeprecated;
-            this.declarationAgainstDeprecated = declarationAgainstDeprecated;
-            this.description = description;
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-
-        @Override
-        public boolean isConsumable() {
-            return consumable;
-        }
-
-        @Override
-        public boolean isResolvable() {
-            return resolvable;
-        }
-
-        @Override
-        public boolean isDeclarableAgainst() {
-            return declarableAgainst;
-        }
-
-        @Override
-        public boolean isConsumptionDeprecated() {
-            return consumptionDeprecated;
-        }
-
-        @Override
-        public boolean isResolutionDeprecated() {
-            return resolutionDeprecated;
-        }
-
-        @Override
-        public boolean isDeclarationAgainstDeprecated() {
-            return declarationAgainstDeprecated;
-        }
-
-        @Override
-        public String describeUsage() {
-            if (description != null) {
-                return description;
-            } else {
-                return RoleDescriber.describeRole(this);
-            }
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRoles.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRoles.java
deleted file mode 100644
index 0d637a1..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationRoles.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2022 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.api.internal.artifacts.configurations;
-
-import org.apache.commons.lang.WordUtils;
-
-import java.util.Optional;
-
-/**
- * Defines {@link ConfigurationRole}s representing common allowed usage patterns.
- *
- * These should be preferred over defining custom roles; whenever possible.  Use {@link #byUsage(boolean, boolean, boolean, boolean, boolean, boolean)}
- * to attempt to locate a matching role by its usage characteristics.
- *
- * @since 8.1
- */
-public enum ConfigurationRoles implements ConfigurationRole {
-    /**
-     * An unrestricted configuration, which can be used for any purpose.
-     *
-     * This is available for backwards compatibility, but should not be used for new configurations.  It is
-     * the default role for configurations created when another more specific role is <strong>not</strong> specified.
-     */
-    @Deprecated
-    LEGACY(true, true, true, false, false, false),
-
-    /**
-     * Meant to be used only for consumption by other projects.
-     */
-    INTENDED_CONSUMABLE(true, false, false, false, false, false),
-
-    /**
-     * Meant to be used only for resolving dependencies.
-     */
-    INTENDED_RESOLVABLE(false, true, false, false, false, false),
-
-    /**
-     * Meant as a temporary solution for situations where we need to declare dependencies against a resolvable configuration.
-     *
-     * These situations should be updated to use a separate bucket configuration for declaring dependencies and extend it with a separate resolvable configuration.
-     */
-    @Deprecated
-    INTENDED_RESOLVABLE_BUCKET(false, true, true, false, false, false),
-
-    /**
-     * Meant to be used only for declaring dependencies.
-     *
-     * AKA {@code INTENDED_DECLARABLE}.
-     */
-    INTENDED_BUCKET(false, false, true, false, false, false),
-
-    /**
-     * Meant to be used only for consumption, permits other usage but emits warnings if used otherwise.
-     */
-    @Deprecated
-    DEPRECATED_CONSUMABLE(true, true, true, false, true, true),
-
-    /**
-     * Meant to be used only for resolution, permits other usage but emits warnings if used otherwise.
-     */
-    @Deprecated
-    DEPRECATED_RESOLVABLE(true, true, true, true, false, true);
-
-    private final boolean consumable;
-    private final boolean resolvable;
-    private final boolean declarableAgainst;
-    private final boolean consumptionDeprecated;
-    private final boolean resolutionDeprecated;
-    private final boolean declarationAgainstDeprecated;
-
-    /**
-     * Locates a pre-defined role allowing the given usage.
-     *
-     * @param consumable whether this role is consumable
-     * @param resolvable whether this role is resolvable
-     * @param declarableAgainst whether this role is declarable against
-     * @param consumptionDeprecated whether this role is deprecated for consumption
-     * @param resolutionDeprecated whether this role is deprecated for resolution
-     * @param declarationAgainstDeprecated whether this role is deprecated for declaration against
-     *
-     * @return the role enum token with matching usage characteristics, if one exists; otherwise {@link Optional#empty()}
-     */
-    public static Optional<ConfigurationRoles> byUsage(boolean consumable, boolean resolvable, boolean declarableAgainst, boolean consumptionDeprecated, boolean resolutionDeprecated, boolean declarationAgainstDeprecated) {
-        for (ConfigurationRoles role : values()) {
-            if (role.consumable == consumable && role.resolvable == resolvable && role.declarableAgainst == declarableAgainst && role.consumptionDeprecated == consumptionDeprecated && role.resolutionDeprecated == resolutionDeprecated && role.declarationAgainstDeprecated == declarationAgainstDeprecated) {
-                return Optional.of(role);
-            }
-        }
-        return Optional.empty();
-    }
-
-    ConfigurationRoles(boolean consumable, boolean resolvable, boolean declarableAgainst, boolean consumptionDeprecated, boolean resolutionDeprecated, boolean declarationAgainstDeprecated) {
-        this.consumable = consumable;
-        this.resolvable = resolvable;
-        this.declarableAgainst = declarableAgainst;
-        this.consumptionDeprecated = consumptionDeprecated;
-        this.resolutionDeprecated = resolutionDeprecated;
-        this.declarationAgainstDeprecated = declarationAgainstDeprecated;
-    }
-
-    @Override
-    public String getName() {
-        return upperSnakeToProperCase(name());
-    }
-
-    @Override
-    public boolean isConsumable() {
-        return consumable;
-    }
-
-    @Override
-    public boolean isResolvable() {
-        return resolvable;
-    }
-
-    @Override
-    public boolean isDeclarableAgainst() {
-        return declarableAgainst;
-    }
-
-    @Override
-    public boolean isConsumptionDeprecated() {
-        return consumptionDeprecated;
-    }
-
-    @Override
-    public boolean isResolutionDeprecated() {
-        return resolutionDeprecated;
-    }
-
-    @Override
-    public boolean isDeclarationAgainstDeprecated() {
-        return declarationAgainstDeprecated;
-    }
-
-    private String upperSnakeToProperCase(String name) {
-        return WordUtils.capitalizeFully(name.replaceAll("_", " "));
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
index 914168e..3fb07db 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationsProvider.java
@@ -15,18 +15,26 @@
  */
 package org.gradle.api.internal.artifacts.configurations;
 
+import javax.annotation.Nullable;
 import java.util.Set;
+import java.util.function.Consumer;
 
 public interface ConfigurationsProvider {
     /**
      * Returns the number of configurations in this provider.
      * <p>
      * This method is provided for performance reasons. It should be more efficient to call this method
-     * than to call {@link #getAll()} and then call {@link Set#size()} on the result.
+     * than to call {@link #visitAll(Consumer)} and then call {@link Set#size()} on the result.
      *
-     * @return the number of configurations in this provider, the same count as would be returned by calling {@link #getAll()}
+     * @return the number of configurations in this provider, the same count as would be visited by calling {@link #visitAll(Consumer)}
      */
     int size();
 
     Set<? extends ConfigurationInternal> getAll();
+
+    void visitAll(Consumer<ConfigurationInternal> visitor);
+
+    @Nullable
+    ConfigurationInternal findByName(String name);
+
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
index 28626f3..be52e72 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import groovy.lang.Closure;
+import org.apache.commons.lang.WordUtils;
 import org.gradle.api.Action;
 import org.gradle.api.Describable;
 import org.gradle.api.DomainObjectSet;
@@ -59,7 +60,6 @@
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.CompositeDomainObjectSet;
 import org.gradle.api.internal.DefaultDomainObjectSet;
-import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.api.internal.DomainObjectContext;
 import org.gradle.api.internal.artifacts.ConfigurationResolver;
 import org.gradle.api.internal.artifacts.DefaultDependencyConstraintSet;
@@ -67,10 +67,10 @@
 import org.gradle.api.internal.artifacts.DefaultExcludeRule;
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
 import org.gradle.api.internal.artifacts.DefaultPublishArtifactSet;
-import org.gradle.api.internal.artifacts.DefaultResolverResults;
 import org.gradle.api.internal.artifacts.ExcludeRuleNotationConverter;
 import org.gradle.api.internal.artifacts.Module;
 import org.gradle.api.internal.artifacts.ResolveContext;
+import org.gradle.api.internal.artifacts.ResolveExceptionContextualizer;
 import org.gradle.api.internal.artifacts.ResolverResults;
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
 import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyConstraint;
@@ -83,13 +83,13 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.SelectedArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedProjectConfiguration;
-import org.gradle.api.internal.artifacts.transform.DefaultExtraExecutionGraphDependenciesResolverFactory;
-import org.gradle.api.internal.artifacts.transform.ExtraExecutionGraphDependenciesResolverFactory;
+import org.gradle.api.internal.artifacts.result.ResolutionResultInternal;
+import org.gradle.api.internal.artifacts.transform.DefaultTransformUpstreamDependenciesResolverFactory;
+import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependenciesResolverFactory;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributeContainerWithErrorMessage;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
-import org.gradle.api.internal.attributes.IncubatingAttributesChecker;
 import org.gradle.api.internal.collections.DomainObjectCollectionFactory;
 import org.gradle.api.internal.file.AbstractFileCollection;
 import org.gradle.api.internal.file.FileCollectionFactory;
@@ -119,9 +119,7 @@
 import org.gradle.internal.component.local.model.LocalComponentMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
 import org.gradle.internal.component.model.LocalComponentDependencyMetadata;
-import org.gradle.internal.deprecation.DeprecatableConfiguration;
 import org.gradle.internal.deprecation.DeprecationLogger;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
 import org.gradle.internal.deprecation.DocumentedFailure;
 import org.gradle.internal.event.ListenerBroadcast;
 import org.gradle.internal.lazy.Lazy;
@@ -133,9 +131,9 @@
 import org.gradle.internal.operations.BuildOperationExecutor;
 import org.gradle.internal.operations.CallableBuildOperation;
 import org.gradle.internal.reflect.Instantiator;
-import org.gradle.internal.resolve.ModuleVersionNotFoundException;
 import org.gradle.internal.typeconversion.NotationParser;
 import org.gradle.internal.work.WorkerThreadRegistry;
+import org.gradle.operations.dependencies.configurations.ConfigurationIdentity;
 import org.gradle.util.Path;
 import org.gradle.util.internal.CollectionUtils;
 import org.gradle.util.internal.ConfigureUtil;
@@ -145,6 +143,7 @@
 
 import javax.annotation.Nullable;
 import java.io.File;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
@@ -155,7 +154,6 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -166,14 +164,16 @@
 import static org.gradle.api.internal.artifacts.configurations.ConfigurationInternal.InternalState.UNRESOLVED;
 import static org.gradle.util.internal.ConfigureUtil.configure;
 
+/**
+ * The default {@link Configuration} implementation.
+ * <p>
+ * After initialization, when the allowed usage is changed then role-related deprecation warnings will be emitted, except for the special cases
+ * noted in {@link #isSpecialCaseOfChangingUsage(String, boolean)}}.  Initialization is complete when the {@link #roleAtCreation} field is set.
+ */
 @SuppressWarnings("rawtypes")
 public class DefaultConfiguration extends AbstractFileCollection implements ConfigurationInternal, MutationValidator, ResettableConfiguration {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultConfiguration.class);
 
-    private static final Action<Throwable> DEFAULT_ERROR_HANDLER = throwable -> {
-        throw UncheckedException.throwAsUncheckedException(throwable);
-    };
-
     private final ConfigurationResolver resolver;
     private final DependencyMetaDataProvider metaDataProvider;
     private final ComponentIdentifierFactory componentIdentifierFactory;
@@ -202,7 +202,7 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf
     private Factory<ResolutionStrategyInternal> resolutionStrategyFactory;
     private ResolutionStrategyInternal resolutionStrategy;
     private final FileCollectionFactory fileCollectionFactory;
-    private final DocumentationRegistry documentationRegistry;
+    private final ResolveExceptionContextualizer exceptionContextualizer;
 
     private final Set<MutationValidator> childMutationValidators = Sets.newHashSet();
     private final MutationValidator parentMutationValidator = DefaultConfiguration.this::validateParentMutation;
@@ -210,7 +210,7 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf
     private final ConfigurationsProvider configurationsProvider;
 
     private final Path identityPath;
-    private final Path path;
+    private final Path projectPath;
 
     // These fields are not covered by mutation lock
     private final String name;
@@ -222,7 +222,6 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf
     private String description;
     private final Set<Object> excludeRules = new LinkedHashSet<>();
     private Set<ExcludeRule> parsedExcludeRules;
-    private boolean returnAllVariants = false;
 
     private final Object observationLock = new Object();
     private volatile InternalState observedState = UNRESOLVED;
@@ -232,12 +231,11 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf
     private boolean canBeConsumed;
     private boolean canBeResolved;
     private boolean canBeDeclaredAgainst;
-    private boolean consumptionDeprecated;
-    private boolean resolutionDeprecated;
-    private boolean declarationDeprecated;
+    private final boolean consumptionDeprecated;
+    private final boolean resolutionDeprecated;
+    private final boolean declarationDeprecated;
     private boolean usageCanBeMutated = true;
     private final ConfigurationRole roleAtCreation;
-    private boolean warnOnChangingUsage = false; // TODO: This should always be true in Gradle 8.1, and can be removed
 
     private boolean canBeMutated = true;
     private AttributeContainerInternal configurationAttributes;
@@ -255,15 +253,14 @@ public class DefaultConfiguration extends AbstractFileCollection implements Conf
 
     private Action<? super ConfigurationInternal> beforeLocking;
 
-    private List<String> declarationAlternatives;
-    private DeprecationMessageBuilder.WithDocumentation consumptionDeprecation;
-    private List<String> resolutionAlternatives;
+    private List<String> declarationAlternatives = ImmutableList.of();
+    private List<String> resolutionAlternatives = ImmutableList.of();
 
     private final CalculatedModelValue<ResolveState> currentResolveState;
 
     private ConfigurationInternal consistentResolutionSource;
     private String consistentResolutionReason;
-    private ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory;
+    private TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory;
     private final DefaultConfigurationFactory defaultConfigurationFactory;
 
     /**
@@ -287,7 +284,7 @@ public DefaultConfiguration(
             NotationParser<Object, Capability> capabilityNotationParser,
             ImmutableAttributesFactory attributesFactory,
             RootComponentMetadataBuilder rootComponentMetadataBuilder,
-            DocumentationRegistry documentationRegistry,
+            ResolveExceptionContextualizer exceptionContextualizer,
             UserCodeApplicationContext userCodeApplicationContext,
             ProjectStateRegistry projectStateRegistry,
             WorkerThreadRegistry workerThreadRegistry,
@@ -305,6 +302,7 @@ public DefaultConfiguration(
         this.domainObjectCollectionFactory = domainObjectCollectionFactory;
         this.calculatedValueContainerFactory = calculatedValueContainerFactory;
         this.identityPath = domainObjectContext.identityPath(name);
+        this.projectPath = domainObjectContext.projectPath(name);
         this.name = name;
         this.configurationsProvider = configurationsProvider;
         this.resolver = resolver;
@@ -321,7 +319,7 @@ public DefaultConfiguration(
         this.configurationAttributes = attributesFactory.mutable();
         this.domainObjectContext = domainObjectContext;
         this.intrinsicFiles = fileCollectionFromSpec(Specs.satisfyAll());
-        this.documentationRegistry = documentationRegistry;
+        this.exceptionContextualizer = exceptionContextualizer;
         this.resolvableDependencies = instantiator.newInstance(ConfigurationResolvableDependencies.class, this);
 
         displayName = Describables.memoize(new ConfigurationDescription(identityPath));
@@ -342,30 +340,21 @@ public DefaultConfiguration(
         this.outgoing = instantiator.newInstance(DefaultConfigurationPublications.class, displayName, artifacts, new AllArtifactsProvider(), configurationAttributes, instantiator, artifactNotationParser, capabilityNotationParser, fileCollectionFactory, attributesFactory, domainObjectCollectionFactory, taskDependencyFactory);
         this.rootComponentMetadataBuilder = rootComponentMetadataBuilder;
         this.currentResolveState = domainObjectContext.getModel().newCalculatedValue(ResolveState.NOT_RESOLVED);
-        this.path = domainObjectContext.projectPath(name);
         this.defaultConfigurationFactory = defaultConfigurationFactory;
 
-        this.roleAtCreation = roleAtCreation;
-
         this.canBeConsumed = roleAtCreation.isConsumable();
         this.canBeResolved = roleAtCreation.isResolvable();
-        this.canBeDeclaredAgainst = roleAtCreation.isDeclarableAgainst();
+        this.canBeDeclaredAgainst = roleAtCreation.isDeclarable();
+        this.consumptionDeprecated = roleAtCreation.isConsumptionDeprecated();
+        this.resolutionDeprecated = roleAtCreation.isResolutionDeprecated();
+        this.declarationDeprecated = roleAtCreation.isDeclarationAgainstDeprecated();
 
-        // Calling these during construction is not ideal, but we'd have to call the deprecateForConsumption(), etc.
-        // methods anyway even if replicated the code inside these methods here, so at least this keeps a single
-        // code path for the deprecation.
-        if (roleAtCreation.isConsumptionDeprecated()) {
-            deprecateForConsumption();
-        }
-        if (roleAtCreation.isResolutionDeprecated()) {
-            deprecateForResolution();
-        }
-        if (roleAtCreation.isDeclarationAgainstDeprecated()) {
-            deprecateForDeclarationAgainst();
-        }
         if (lockUsage) {
             preventUsageMutation();
         }
+
+        // Until the role at creation is set, changing usage won't trigger warnings
+        this.roleAtCreation = roleAtCreation;
     }
 
     private static Action<Void> validateMutationType(final MutationValidator mutationValidator, final MutationType type) {
@@ -392,8 +381,17 @@ public State getState() {
         }
     }
 
+    /**
+     * Get the current resolved state of this configuration.
+     * <p>
+     * Usage: This method should only be called on resolvable configurations and should throw an exception if
+     * called on a configuration that does not permit this usage.
+     *
+     * @return the current resolved state of this configuration
+     */
     @VisibleForTesting
     public InternalState getResolvedState() {
+        warnOnInvalidInternalAPIUsage("getResolvedState()", ProperMethodUsage.RESOLVABLE);
         return currentResolveState.get().state;
     }
 
@@ -421,7 +419,7 @@ public Set<Configuration> getExtendsFrom() {
 
     @Override
     public Configuration setExtendsFrom(Iterable<Configuration> extendsFrom) {
-        validateMutation(MutationType.DEPENDENCIES);
+        validateMutation(MutationType.HIERARCHY);
         for (Configuration configuration : this.extendsFrom) {
             if (inheritedArtifacts != null) {
                 inheritedArtifacts.removeCollection(configuration.getAllArtifacts());
@@ -443,7 +441,7 @@ public Configuration setExtendsFrom(Iterable<Configuration> extendsFrom) {
 
     @Override
     public Configuration extendsFrom(Configuration... extendsFrom) {
-        validateMutation(MutationType.DEPENDENCIES);
+        validateMutation(MutationType.HIERARCHY);
         for (Configuration configuration : extendsFrom) {
             if (configuration.getHierarchy().contains(this)) {
                 throw new InvalidUserDataException(String.format(
@@ -511,6 +509,7 @@ private void collectSuperConfigs(Configuration configuration, Set<Configuration>
 
     @Override
     public Configuration defaultDependencies(final Action<? super DependencySet> action) {
+        warnOnDeprecatedUsage("defaultDependencies(Action)", ProperMethodUsage.DECLARABLE_AGAINST);
         validateMutation(MutationType.DEPENDENCIES);
         defaultDependencyActions = defaultDependencyActions.add(dependencies -> {
             if (dependencies.isEmpty()) {
@@ -541,13 +540,21 @@ public void runDependencyActions() {
         }
     }
 
+    @Deprecated
     @Override
     public Set<Configuration> getAll() {
+        DeprecationLogger.deprecateAction("Calling the Configuration.getAll() method")
+                .withAdvice("Use the configurations container to access the set of configurations instead.")
+                .willBeRemovedInGradle9()
+                .withUpgradeGuideSection(8, "deprecated_configuration_get_all")
+                .nagUser();
+
         return ImmutableSet.copyOf(configurationsProvider.getAll());
     }
 
     @Override
     public Set<File> resolve() {
+        warnOnDeprecatedUsage("resolve()", ProperMethodUsage.RESOLVABLE);
         return getFiles();
     }
 
@@ -563,11 +570,18 @@ protected void visitContents(FileCollectionStructureVisitor visitor) {
 
     @Override
     protected void appendContents(TreeFormatter formatter) {
-        formatter.node("configuration: " + getIdentityPath());
+        formatter.node("configuration: " + identityPath);
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * @implNote Usage: This method should only be called on resolvable configurations and should throw an exception if
+     * called on a configuration that does not permit this usage.
+     */
     @Override
     public boolean contains(File file) {
+        warnOnInvalidInternalAPIUsage("contains(File)", ProperMethodUsage.RESOLVABLE);
         return intrinsicFiles.contains(file);
     }
 
@@ -594,6 +608,8 @@ public Set<File> files(Spec<? super Dependency> dependencySpec) {
     @Override
     public FileCollection fileCollection(Spec<? super Dependency> dependencySpec) {
         assertIsResolvable();
+        // After asserting, we are definitely allowed, but might be deprecated, so check to warn now
+        warnOnDeprecatedUsage("fileCollection(Spec)", ProperMethodUsage.RESOLVABLE);
         return fileCollectionFromSpec(dependencySpec);
     }
 
@@ -608,11 +624,13 @@ private ResolutionBackedFileCollection fileCollectionFromSpec(Spec<? super Depen
 
     @Override
     public FileCollection fileCollection(Closure dependencySpecClosure) {
+        warnOnDeprecatedUsage("fileCollection(Closure)", ProperMethodUsage.RESOLVABLE);
         return fileCollection(Specs.convertClosureToSpec(dependencySpecClosure));
     }
 
     @Override
     public FileCollection fileCollection(Dependency... dependencies) {
+        warnOnDeprecatedUsage("fileCollection(Dependency...)", ProperMethodUsage.RESOLVABLE);
         Set<Dependency> deps = WrapUtil.toLinkedSet(dependencies);
         return fileCollection(deps::contains);
     }
@@ -639,12 +657,13 @@ private void markParentsObserved(InternalState requestedState) {
 
     @Override
     public ResolvedConfiguration getResolvedConfiguration() {
+        warnOnDeprecatedUsage("getResolvedConfiguration()", ProperMethodUsage.RESOLVABLE);
         return resolveToStateOrLater(ARTIFACTS_RESOLVED).getResolvedConfiguration();
     }
 
     private ResolveState resolveToStateOrLater(final InternalState requestedState) {
         assertIsResolvable();
-        warnIfConfigurationIsDeprecatedForResolving();
+        maybeEmitResolutionDeprecation();
         logIfImproperConfiguration();
 
         ResolveState currentState = currentResolveState.get();
@@ -667,15 +686,6 @@ private ResolveState resolveToStateOrLater(final InternalState requestedState) {
         return resolveExclusively(requestedState);
     }
 
-    private void warnIfConfigurationIsDeprecatedForResolving() {
-        if (resolutionAlternatives != null) {
-            DeprecationLogger.deprecateConfiguration(this.name).forResolution().replaceWith(resolutionAlternatives)
-                    .willBecomeAnErrorInGradle9()
-                    .withUpgradeGuideSection(5, "dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
-                    .nagUser();
-        }
-    }
-
     private ResolveState resolveExclusively(InternalState requestedState) {
         return currentResolveState.update(initial -> {
             ResolveState current = initial;
@@ -708,8 +718,7 @@ public ResolveState call(BuildOperationContext context) {
 
                 ResolvableDependenciesInternal incoming = (ResolvableDependenciesInternal) getIncoming();
                 performPreResolveActions(incoming);
-                DefaultResolverResults results = new DefaultResolverResults();
-                resolver.resolveGraph(DefaultConfiguration.this, results);
+                ResolverResults results = resolver.resolveGraph(DefaultConfiguration.this);
                 dependenciesModified = false;
 
                 ResolveState newState = new GraphResolved(results);
@@ -723,12 +732,17 @@ public ResolveState call(BuildOperationContext context) {
 
                 if (!newState.hasError()) {
                     dependencyResolutionListeners.getSource().afterResolve(incoming);
-                    // Discard listeners
-                    dependencyResolutionListeners.removeAll();
 
                     // Use the current state, which may have changed if the listener queried the result
                     newState = currentResolveState.get();
                 }
+
+                // Discard State
+                dependencyResolutionListeners.removeAll();
+                if (resolutionStrategy != null) {
+                    resolutionStrategy.discardStateRequiredForGraphResolution();
+                }
+
                 captureBuildOperationResult(context, results);
                 return newState;
             }
@@ -774,6 +788,7 @@ public BuildOperationDescriptor.Builder description() {
 
     @Override
     public ConfigurationInternal getConsistentResolutionSource() {
+        warnOnInvalidInternalAPIUsage("getConsistentResolutionSource()", ProperMethodUsage.RESOLVABLE);
         return consistentResolutionSource;
     }
 
@@ -797,37 +812,6 @@ private void assertThatConsistentResolutionIsPropertyConfigured() {
         assertNoDependencyResolutionConsistencyCycle();
     }
 
-    @Override
-    public ResolveException maybeAddContext(ResolveException e) {
-        return failuresWithHint(e.getCauses()).orElse(e);
-    }
-
-    private Optional<ResolveException> failuresWithHint(Collection<? extends Throwable> causes) {
-        try {
-            if (ignoresSettingsRepositories()) {
-                boolean hasModuleNotFound = causes.stream().anyMatch(ModuleVersionNotFoundException.class::isInstance);
-                if (hasModuleNotFound) {
-                    return Optional.of(new ResolveExceptionWithHints(getDisplayName(), causes,
-                        "The project declares repositories, effectively ignoring the repositories you have declared in the settings.",
-                        "You can figure out how project repositories are declared by configuring your build to fail on project repositories.",
-                        "See " + documentationRegistry.getDocumentationFor("declaring_repositories", "sub:fail_build_on_project_repositories") + " for details."));
-                }
-            }
-        } catch (Throwable e) {
-            return Optional.of(new ResolveException(getDisplayName(), ImmutableList.<Throwable>builder().addAll(causes).add(e).build()));
-        }
-        return Optional.empty();
-    }
-
-    private boolean ignoresSettingsRepositories() {
-        if (domainObjectContext instanceof ProjectInternal) {
-            ProjectInternal project = (ProjectInternal) this.domainObjectContext;
-            return !project.getRepositories().isEmpty() &&
-                !project.getGradle().getSettings().getDependencyResolutionManagement().getRepositories().isEmpty();
-        }
-        return false;
-    }
-
     private void assertNoDependencyResolutionConsistencyCycle() {
         Set<ConfigurationInternal> sources = Sets.newLinkedHashSet();
         ConfigurationInternal src = this;
@@ -883,15 +867,16 @@ private ResolveState resolveArtifactsIfRequired(ResolveState currentState) {
         if (currentState.state != GRAPH_RESOLVED) {
             throw new IllegalStateException("Cannot resolve artifacts before graph has been resolved.");
         }
-        ResolverResults results = currentState.getCachedResolverResults();
-        resolver.resolveArtifacts(DefaultConfiguration.this, results);
-        return new ArtifactsResolved(results);
+        ResolverResults graphResults = currentState.getCachedResolverResults();
+        ResolverResults artifactResults = resolver.resolveArtifacts(DefaultConfiguration.this, graphResults);
+        return new ArtifactsResolved(artifactResults);
     }
 
     @Override
-    public ExtraExecutionGraphDependenciesResolverFactory getDependenciesResolver() {
+    public TransformUpstreamDependenciesResolverFactory getDependenciesResolverFactory() {
+        warnOnInvalidInternalAPIUsage("getDependenciesResolverFactory()", ProperMethodUsage.RESOLVABLE);
         if (dependenciesResolverFactory == null) {
-            dependenciesResolverFactory = new DefaultExtraExecutionGraphDependenciesResolverFactory(getIdentity(), new DefaultResolutionResultProvider(), domainObjectContext, calculatedValueContainerFactory,
+            dependenciesResolverFactory = new DefaultTransformUpstreamDependenciesResolverFactory(getIdentity(), new DefaultResolutionResultProvider(), domainObjectContext, calculatedValueContainerFactory,
                 (attributes, filter) -> {
                     ImmutableAttributes fullAttributes = attributesFactory.concat(configurationAttributes.asImmutable(), attributes);
                     return new ResolutionBackedFileCollection(
@@ -906,6 +891,7 @@ public ExtraExecutionGraphDependenciesResolverFactory getDependenciesResolver()
 
     @Override
     public void resetResolutionState() {
+        warnOnInvalidInternalAPIUsage("resetResolutionState()", ProperMethodUsage.RESOLVABLE);
         currentResolveState.set(ResolveState.NOT_RESOLVED);
     }
 
@@ -926,8 +912,7 @@ private ResolverResults resolveGraphForBuildDependenciesIfRequired() {
         ResolveState currentState = currentResolveState.update(initial -> {
             if (initial.state == UNRESOLVED) {
                 // Traverse graph
-                ResolverResults results = new DefaultResolverResults();
-                resolver.resolveBuildDependencies(DefaultConfiguration.this, results);
+                ResolverResults results = resolver.resolveBuildDependencies(DefaultConfiguration.this);
                 markReferencedProjectConfigurationsObserved(BUILD_DEPENDENCIES_RESOLVED, results);
                 return new BuildDependenciesResolved(results);
             } // Otherwise, already have a result, so reuse it
@@ -979,6 +964,19 @@ public DependencySet getAllDependencies() {
         return allDependencies;
     }
 
+    @Override
+    public boolean hasDependencies() {
+        return !getAllDependencies().isEmpty();
+    }
+
+    @Override
+    public int getEstimatedGraphSize() {
+        // TODO #24641: Why are the numbers and operations here the way they are?
+        //  Are they up-to-date? We should be able to test if these values are still optimal.
+        int estimate = (int) (512 * Math.log(getAllDependencies().size()));
+        return Math.max(10, estimate);
+    }
+
     private synchronized void initAllDependencies() {
         if (allDependencies != null) {
             return;
@@ -1087,7 +1085,16 @@ private synchronized void initExcludeRules() {
         }
     }
 
+    /**
+     * Adds exclude rules to this configuration.
+     * <p>
+     * Usage: This method should only be called on resolvable or declarable configurations and should throw an exception if
+     * called on a configuration that does not permit this usage.
+     *
+     * @param excludeRules the exclude rules to add.
+     */
     public void setExcludeRules(Set<ExcludeRule> excludeRules) {
+        warnOnInvalidInternalAPIUsage("setExcludeRules(Set)", ProperMethodUsage.DECLARABLE_AGAINST, ProperMethodUsage.RESOLVABLE);
         validateMutation(MutationType.DEPENDENCIES);
         parsedExcludeRules = null;
         this.excludeRules.clear();
@@ -1124,11 +1131,6 @@ public ConfigurationPublications getOutgoing() {
     }
 
     @Override
-    public OutgoingVariant convertToOutgoingVariant() {
-        return outgoing.convertToOutgoingVariant();
-    }
-
-    @Override
     public void collectVariants(VariantVisitor visitor) {
         outgoing.collectVariants(visitor);
     }
@@ -1250,32 +1252,31 @@ private boolean hasSameAttributesAs(ConfigurationInternal other) {
     }
 
     @Override
-    public boolean isIncubating() {
-        return IncubatingAttributesChecker.isAnyIncubating(getAttributes());
-    }
-
-    @Override
     public void outgoing(Action<? super ConfigurationPublications> action) {
         action.execute(outgoing);
     }
 
     @Override
     public ConfigurationInternal copy() {
+        warnOnDeprecatedUsage("copy()", ProperMethodUsage.RESOLVABLE);
         return createCopy(getDependencies(), getDependencyConstraints());
     }
 
     @Override
     public Configuration copyRecursive() {
+        warnOnDeprecatedUsage("copyRecursive()", ProperMethodUsage.RESOLVABLE);
         return createCopy(getAllDependencies(), getAllDependencyConstraints());
     }
 
     @Override
     public Configuration copy(Spec<? super Dependency> dependencySpec) {
+        warnOnDeprecatedUsage("copy(Spec)", ProperMethodUsage.RESOLVABLE);
         return createCopy(CollectionUtils.filter(getDependencies(), dependencySpec), getDependencyConstraints());
     }
 
     @Override
     public Configuration copyRecursive(Spec<? super Dependency> dependencySpec) {
+        warnOnDeprecatedUsage("copyRecursive(Spec)", ProperMethodUsage.RESOLVABLE);
         return createCopy(CollectionUtils.filter(getAllDependencies(), dependencySpec), getAllDependencyConstraints());
     }
 
@@ -1285,17 +1286,23 @@ public Configuration copyRecursive(Spec<? super Dependency> dependencySpec) {
      * deprecated in the copied configuration. In 9.0, we will update this to copy
      * roles and deprecations without modification. Or, better yet, we will remove support
      * for copying configurations altogether.
+     *
+     * This means the copy created is <strong>NOT</strong> a strictly identical copy of the original, as the role
+     * will be not only a different instance, but also may return different deprecation values.
      */
     private DefaultConfiguration createCopy(Set<Dependency> dependencies, Set<DependencyConstraint> dependencyConstraints) {
-        // Begin by allowing everything, and setting deprecations for disallowed roles
-        ConfigurationRole adjustedCurrentUsage = ConfigurationRole.forUsage(
-                true, true, true,
-                !canBeConsumed || consumptionDeprecation != null,
-                !canBeResolved || resolutionAlternatives != null,
-                !canBeDeclaredAgainst || declarationAlternatives != null);
+        // Begin by allowing everything, and setting deprecations for disallowed roles in a new role implementation
+        boolean deprecateConsumption = !canBeConsumed || consumptionDeprecated;
+        boolean deprecateResolution = !canBeResolved || resolutionDeprecated;
+        boolean deprecateDeclarationAgainst = !canBeDeclaredAgainst || declarationDeprecated;
+        ConfigurationRole adjustedCurrentUsage = new DefaultConfigurationRole(
+            "adjusted current usage with deprecations",
+            true, true, true,
+            deprecateConsumption, deprecateResolution, deprecateDeclarationAgainst
+        );
 
 
-        DefaultConfiguration copiedConfiguration = newConfiguration(adjustedCurrentUsage, this.usageCanBeMutated);
+        DefaultConfiguration copiedConfiguration = newConfiguration(adjustedCurrentUsage);
         // state, cachedResolvedConfiguration, and extendsFrom intentionally not copied - must re-resolve copy
         // copying extendsFrom could mess up dependencies when copy was re-resolved
 
@@ -1307,14 +1314,8 @@ private DefaultConfiguration createCopy(Set<Dependency> dependencies, Set<Depend
         copiedConfiguration.withDependencyActions = withDependencyActions;
         copiedConfiguration.dependencyResolutionListeners = dependencyResolutionListeners.copy();
 
-        copiedConfiguration.declarationAlternatives =
-            canBeDeclaredAgainst || declarationAlternatives != null ? declarationAlternatives : Collections.emptyList();
-        copiedConfiguration.resolutionAlternatives =
-            canBeResolved || resolutionAlternatives != null ? resolutionAlternatives : Collections.emptyList();
-        copiedConfiguration.consumptionDeprecation =
-            canBeConsumed || consumptionDeprecation != null ? consumptionDeprecation
-                : DeprecationLogger.deprecateConfiguration(name).forConsumption()
-                    .willBecomeAnErrorInGradle9().undocumented();
+        copiedConfiguration.declarationAlternatives = declarationAlternatives;
+        copiedConfiguration.resolutionAlternatives = resolutionAlternatives;
 
         copiedConfiguration.getArtifacts().addAll(getAllArtifacts());
 
@@ -1343,7 +1344,7 @@ private DefaultConfiguration createCopy(Set<Dependency> dependencies, Set<Depend
         return copiedConfiguration;
     }
 
-    private DefaultConfiguration newConfiguration(ConfigurationRole role, boolean usageCanBeMutated) {
+    private DefaultConfiguration newConfiguration(ConfigurationRole role) {
         DetachedConfigurationsProvider configurationsProvider = new DetachedConfigurationsProvider();
         RootComponentMetadataBuilder rootComponentMetadataBuilder = this.rootComponentMetadataBuilder.withConfigurationsProvider(configurationsProvider);
 
@@ -1356,7 +1357,7 @@ private DefaultConfiguration newConfiguration(ConfigurationRole role, boolean us
             childResolutionStrategy,
             rootComponentMetadataBuilder,
             role,
-            usageCanBeMutated
+            false
         );
         configurationsProvider.setTheOnlyConfiguration(copiedConfiguration);
         return copiedConfiguration;
@@ -1392,11 +1393,13 @@ public ResolutionStrategyInternal getResolutionStrategy() {
 
     @Override
     public LocalComponentMetadata toRootComponentMetaData() {
+        warnOnInvalidInternalAPIUsage("toRootComponentMetaData()", ProperMethodUsage.RESOLVABLE);
         return rootComponentMetadataBuilder.toRootComponentMetaData();
     }
 
     @Override
     public List<? extends DependencyMetadata> getSyntheticDependencies() {
+        warnOnInvalidInternalAPIUsage("getSyntheticDependencies()", ProperMethodUsage.RESOLVABLE);
         return syntheticDependencies.get();
     }
 
@@ -1442,8 +1445,8 @@ private String getLockReason(boolean strict, String lockedVersion) {
     }
 
     @Override
-    public String getPath() {
-        return path.getPath();
+    public Path getProjectPath() {
+        return projectPath;
     }
 
     @Override
@@ -1452,16 +1455,8 @@ public Path getIdentityPath() {
     }
 
     @Override
-    public void setReturnAllVariants(boolean returnAllVariants) {
-        if (!canBeMutated) {
-            throw new IllegalStateException("Configuration is unmodifiable");
-        }
-        this.returnAllVariants = returnAllVariants;
-    }
-
-    @Override
-    public boolean getReturnAllVariants() {
-        return this.returnAllVariants;
+    public DomainObjectContext getDomainObjectContext() {
+        return domainObjectContext;
     }
 
     @Override
@@ -1523,7 +1518,7 @@ private void preventIllegalParentMutation(MutationType type) {
     private void preventIllegalMutation(MutationType type) {
         // TODO: Deprecate and eventually prevent these mutations when already resolved
         if (type == MutationType.DEPENDENCY_ATTRIBUTES) {
-            assertIsDeclarableAgainst();
+            assertIsDeclarable();
             return;
         }
 
@@ -1575,6 +1570,38 @@ private ConfigurationIdentity getIdentity() {
         return new DefaultConfigurationIdentity(buildPath, projectPath, name);
     }
 
+    private boolean isProperUsage(boolean allowDeprecated, ProperMethodUsage... properUsages) {
+        for (ProperMethodUsage properUsage : properUsages) {
+            if (properUsage.isProperUsage(this, allowDeprecated)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void warnOnInvalidInternalAPIUsage(String methodName, ProperMethodUsage... properUsages) {
+        warnOnDeprecatedUsage(methodName, true, properUsages);
+    }
+
+    private void warnOnDeprecatedUsage(String methodName, ProperMethodUsage... properUsages) {
+        warnOnDeprecatedUsage(methodName, false, properUsages);
+    }
+
+    private void warnOnDeprecatedUsage(String methodName, boolean allowDeprecated, ProperMethodUsage... properUsages) {
+        if (!isProperUsage(allowDeprecated, properUsages)) {
+            String msgTemplate = "Calling configuration method '%s' is deprecated for configuration '%s', which has permitted usage(s):\n" +
+                    "%s\n" +
+                    "This method is only meant to be called on configurations which allow the %susage(s): '%s'.";
+            String currentUsageDesc = UsageDescriber.describeCurrentUsage(this);
+            String properUsageDesc = ProperMethodUsage.summarizeProperUsage(properUsages);
+
+            DeprecationLogger.deprecateBehaviour(String.format(msgTemplate, methodName, getName(), currentUsageDesc, allowDeprecated ? "" : "(non-deprecated) ", properUsageDesc))
+                    .willBeRemovedInGradle9()
+                    .withUpgradeGuideSection(8, "deprecated_configuration_usage")
+                    .nagUser();
+        }
+    }
+
     private static class ConfigurationDescription implements Describable {
         private final Path identityPath;
 
@@ -1695,31 +1722,26 @@ private Optional<? extends RuntimeException> mapFailure(String type, Collection<
             return Optional.empty();
         }
         if (failures.size() == 1) {
-            Optional<ResolveException> resolveException = failuresWithHint(failures);
-            if (resolveException.isPresent()) {
-                return resolveException;
+            ResolveException resolveException = exceptionContextualizer.maybeContextualize(failures, this);
+            if (resolveException != null) {
+                return Optional.of(resolveException);
             }
-            resolveException.ifPresent(UncheckedException::throwAsUncheckedException);
             Throwable failure = failures.iterator().next();
             if (failure instanceof ResolveException) {
-                return Optional.of((ResolveException) failure);
+                resolveException = (ResolveException) failure;
+                return Optional.of(exceptionContextualizer.contextualize(resolveException, getDisplayName()));
             }
         }
         return Optional.of(new DefaultLenientConfiguration.ArtifactResolveException(type, getIdentityPath().toString(), getDisplayName(), failures));
     }
 
-    @VisibleForTesting
-    public void setWarnOnChangingUsage(boolean warnOnChangingUsage) {
-        this.warnOnChangingUsage = warnOnChangingUsage;
-    }
-
     private void assertIsResolvable() {
         if (!canBeResolved) {
             throw new IllegalStateException("Resolving dependency configuration '" + name + "' is not allowed as it is defined as 'canBeResolved=false'.\nInstead, a resolvable ('canBeResolved=true') dependency configuration that extends '" + name + "' should be resolved.");
         }
     }
 
-    private void assertIsDeclarableAgainst() {
+    private void assertIsDeclarable() {
         if (!canBeDeclaredAgainst) {
             throw new IllegalStateException("Declaring dependencies for configuration '" + name + "' is not allowed as it is defined as 'canBeDeclared=false'.");
         }
@@ -1727,20 +1749,26 @@ private void assertIsDeclarableAgainst() {
 
     /**
      * Check that the combination of consumable, resolvable and declarable flags is sensible.
-     * This method should be called only after all mutations are known to be complete.
-     * This shouldn't do anything stronger than log to info, otherwise it will interrupt dependency reports.
-     * Many improper configurations are still in use, we can't just fail if one is detected here.
+     * <p>
+     * This should only check configurations <strong>not</strong> created in the {@link ConfigurationRoles#LEGACY} role.
+     * <p>
+     * This method should be called only after all mutations are known to be complete.  This shouldn't do
+     * anything stronger than log to info, otherwise it will interrupt dependency reports. Many improper
+     * configurations are still in use, we can't just fail if one is detected here.
      */
+    @SuppressWarnings("deprecation")
     private void logIfImproperConfiguration() {
-        if (canBeConsumed && canBeResolved) {
-            LOGGER.info("The configuration " + identityPath.toString() + " is both resolvable and consumable. This is considered a legacy configuration and it will eventually only be possible to be one of these.");
-        }
+        if (roleAtCreation != ConfigurationRoles.LEGACY) {
+            if (canBeConsumed && canBeResolved) {
+                LOGGER.info("The configuration " + identityPath.toString() + " is both resolvable and consumable. This is considered a legacy configuration and it will eventually only be possible to be one of these.");
+            }
 
-        if (canBeConsumed && canBeDeclaredAgainst) {
-            LOGGER.info("The configuration " + identityPath.toString() + " is both consumable and declarable. This combination is incorrect, only one of these flags should be set.");
-        }
+            if (canBeConsumed && canBeDeclaredAgainst) {
+                LOGGER.info("The configuration " + identityPath.toString() + " is both consumable and declarable. This combination is incorrect, only one of these flags should be set.");
+            }
 
-        // canBeDeclared && canBeResolved is a valid and expected combination
+            // canBeDeclared && canBeResolved is a valid and expected combination
+        }
     }
 
     @Override
@@ -1753,8 +1781,15 @@ public AttributeContainerInternal getAttributes() {
         return configurationAttributes;
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * @implNote Usage: This method should only be called on consumable or resolvable configurations and will emit a deprecation warning if
+     * called on a configuration that does not permit this usage, or has had allowed this usage but marked it as deprecated.
+     */
     @Override
     public Configuration attributes(Action<? super AttributeContainer> action) {
+        warnOnDeprecatedUsage("attributes(Action)", ProperMethodUsage.CONSUMABLE, ProperMethodUsage.RESOLVABLE);
         action.execute(configurationAttributes);
         return this;
     }
@@ -1764,11 +1799,6 @@ public void preventUsageMutation() {
         usageCanBeMutated = false;
     }
 
-    @VisibleForTesting
-    public boolean isUsageMutable() {
-        return usageCanBeMutated;
-    }
-
     @SuppressWarnings("deprecation")
     private void assertUsageIsMutable() {
         if (!usageCanBeMutated) {
@@ -1786,21 +1816,114 @@ private void assertUsageIsMutable() {
         }
     }
 
-    private void logChangingUsage(String usage, boolean allowed) {
-        String msgTemplate = "Allowed usage is changing for %s, %s. Ideally, usage should be fixed upon creation.";
-        if (warnOnChangingUsage) {
-            DeprecationLogger.deprecateBehaviour(String.format(msgTemplate, getDisplayName(), describeChangingUsage(usage, allowed)))
+    private void maybeWarnOnChangingUsage(String usage, boolean current) {
+        if (!isSpecialCaseOfChangingUsage(usage, current)) {
+            String msgTemplate = "Allowed usage is changing for %s, %s. Ideally, usage should be fixed upon creation.";
+            String changingUsage = usage + " was " + !current + " and is now " + current;
+
+            DeprecationLogger.deprecateBehaviour(String.format(msgTemplate, getDisplayName(), changingUsage))
                     .withAdvice("Usage should be fixed upon creation.")
                     .willBeRemovedInGradle9()
                     .withUpgradeGuideSection(8, "configurations_allowed_usage")
                     .nagUser();
-        } else if (LOGGER.isInfoEnabled()) {
-            LOGGER.info(String.format(msgTemplate, getDisplayName(), describeChangingUsage(usage, allowed)));
         }
     }
 
-    private String describeChangingUsage(String usage, boolean allowed) {
-        return usage + " was " + !allowed + " and is now " + allowed;
+    private void maybeWarnOnRedundantUsageActivation(String usage, String method) {
+        if (!isSpecialCaseOfRedundantUsageActivation()) {
+            String msgTemplate = "The %s usage is already allowed on %s.";
+            DeprecationLogger.deprecateBehaviour(String.format(msgTemplate, usage, getDisplayName()))
+                    .withAdvice(String.format("Remove the call to %s, it has no effect.", method))
+                    .willBeRemovedInGradle9()
+                    .withUpgradeGuideSection(8, "redundant_configuration_usage_activation")
+                    .nagUser();
+        }
+    }
+
+    /**
+     * This is a temporary method that decides if a usage change is a known/supported special case, where a deprecation warning message
+     * should not be emitted.
+     * <p>
+     * Many of these exceptions are needed to avoid spamming deprecations warnings whenever the Kotlin plugin is used.  This method is
+     * temporary as the eventual goal is that all configuration usage be specified upon creation and immutable thereafter.
+     * <p>
+     * <ol>
+     *     <li>While {#roleAtCreation} is {@code null}, we are still initializing, so we should NOT warn.</li>
+     *     <li>Changes to the usage of the detached configurations should NOT warn (this done by the Kotlin plugin).</li>
+     *     <li>Configurations with a legacy role should NOT warn when changing usage,
+since users cannot create non-legacy configurations and there is no current public API for setting roles upon creation</li>
+     *     <li>Setting consumable usage to false on the {@code apiElements} and {@code runtimeElements} configurations should NOT warn (this is done by the Kotlin plugin).</li>
+     *     <li>All other usage changes should warn.</li>
+     * </ol>
+     *
+     * @param usage the name usage that is being changed
+     * @param current the current value of the usage after the change
+     *
+     * @return {@code true} if the usage change is a known special case; {@code false} otherwise
+     */
+    private boolean isSpecialCaseOfChangingUsage(String usage, boolean current) {
+        return isInitializing() || isDetachedConfiguration() || isInLegacyRole() || isPermittedConfigurationForUsageChange(usage, current);
+    }
+
+    /**
+     * This is a temporary method that decides if a redundant usage activation is a known/supported special case,
+     * where a deprecation warning message should not be emitted.
+     * <p>
+     * These exceptions are needed to avoid spamming deprecations warnings whenever some important 3rd party plugins like
+     * Kotlin or Android are used.
+     * <p>
+     * <ol>
+     *     <li>Redundant activation of a usage of a detached configurations should NOT warn (this done by the Kotlin plugin).</li>
+     *     <li>Configurations with a legacy role should NOT warn during redundant usage activation,
+     since users cannot create non-legacy configurations and there is no current public API for setting roles upon creation</li>
+     *     <li>All other usage changes should warn.</li>
+     * </ol>
+     *
+     * @return {@code true} if the usage change is a known special case; {@code false} otherwise
+     */
+    private boolean isSpecialCaseOfRedundantUsageActivation() {
+        return isInLegacyRole() || isDetachedConfiguration() || isPermittedConfigurationForRedundantActivation();
+    }
+
+    private boolean isInitializing() {
+        return roleAtCreation == null;
+    }
+
+    private boolean isDetachedConfiguration() {
+        return this.configurationsProvider instanceof DetachedConfigurationsProvider;
+    }
+
+    @SuppressWarnings("deprecation")
+    private boolean isInLegacyRole() {
+        return roleAtCreation == ConfigurationRoles.LEGACY;
+    }
+
+    /**
+     * Determine if this is a configuration that is permitted to change its usage, to support important 3rd party
+     * plugins such as Kotlin that do this.
+     * <p>
+     * This method is temporary, so the duplication of the configuration names defined in
+     * {@link JvmConstants}, which are not available to be referenced directly from here, is unfortunate, but not a showstopper.
+     *
+     * @return {@code true} if this is a configuration that is permitted to change its usage; {@code false} otherwise
+     */
+    @SuppressWarnings("JavadocReference")
+    private boolean isPermittedConfigurationForUsageChange(String usage, boolean current) {
+        return name.equals("apiElements") || name.equals("runtimeElements") && usage.equals("consumable") && !current;
+    }
+
+    /**
+     * Determine if this is a configuration that is permitted to redundantly activate usage, to support important 3rd party
+     * plugins such as Kotlin that do this.
+     * <p>
+     * This method is temporary, so the duplication of the configuration names defined in
+     * {@link JvmConstants}, which are not available to be referenced directly from here, is unfortunate, but not a showstopper.
+     *
+     * @return {@code true} if this is a configuration that is permitted to redundantly activate usage; {@code false} otherwise
+     */
+    @SuppressWarnings("JavadocReference")
+    private boolean isPermittedConfigurationForRedundantActivation() {
+        return name.equals("runtimeClasspath") || name.endsWith("testRuntimeClasspath") || name.endsWith("TestRuntimeClasspath");
     }
 
     @Override
@@ -1828,7 +1951,9 @@ public void setCanBeConsumed(boolean allowed) {
         if (canBeConsumed != allowed) {
             validateMutation(MutationType.USAGE);
             canBeConsumed = allowed;
-            logChangingUsage("consumable", allowed);
+            maybeWarnOnChangingUsage("consumable", allowed);
+        } else if (canBeConsumed && allowed) {
+            maybeWarnOnRedundantUsageActivation("consumable", "setCanBeConsumed(true)");
         }
     }
 
@@ -1842,21 +1967,25 @@ public void setCanBeResolved(boolean allowed) {
         if (canBeResolved != allowed) {
             validateMutation(MutationType.USAGE);
             canBeResolved = allowed;
-            logChangingUsage("resolvable", allowed);
+            maybeWarnOnChangingUsage("resolvable", allowed);
+        } else if (canBeResolved && allowed) {
+            maybeWarnOnRedundantUsageActivation("resolvable", "setCanBeResolved(true)");
         }
     }
 
     @Override
-    public boolean isCanBeDeclaredAgainst() {
+    public boolean isCanBeDeclared() {
         return canBeDeclaredAgainst;
     }
 
     @Override
-    public void setCanBeDeclaredAgainst(boolean allowed) {
+    public void setCanBeDeclared(boolean allowed) {
         if (canBeDeclaredAgainst != allowed) {
             validateMutation(MutationType.USAGE);
             canBeDeclaredAgainst = allowed;
-            logChangingUsage("declarable against", allowed);
+            maybeWarnOnChangingUsage("declarable", allowed);
+        } else if (canBeDeclaredAgainst && allowed) {
+            maybeWarnOnRedundantUsageActivation("declarable", "setCanBeDeclared(true)");
         }
     }
 
@@ -1866,58 +1995,34 @@ ListenerBroadcast<DependencyResolutionListener> getDependencyResolutionListeners
     }
 
     @Override
-    @Nullable
     public List<String> getDeclarationAlternatives() {
         return declarationAlternatives;
     }
 
-    @Nullable
-    @Override
-    public DeprecationMessageBuilder.WithDocumentation getConsumptionDeprecation() {
-        return consumptionDeprecation;
-    }
-
-    @Nullable
     @Override
     public List<String> getResolutionAlternatives() {
         return resolutionAlternatives;
     }
 
     @Override
-    public DeprecatableConfiguration deprecateForDeclarationAgainst(String... alternativesForDeclaring) {
-        validateMutation(MutationType.USAGE);
-        this.declarationAlternatives = ImmutableList.copyOf(alternativesForDeclaring);
-        if (!declarationDeprecated) {
-            logChangingUsage("deprecated for declaration against", true);
-        }
-        declarationDeprecated = true;
-        return this;
+    public void addDeclarationAlternatives(String... alternativesForDeclaring) {
+        this.declarationAlternatives = ImmutableList.<String>builder()
+            .addAll(declarationAlternatives)
+            .addAll(Arrays.asList(alternativesForDeclaring))
+            .build();
     }
 
     @Override
-    public DeprecatableConfiguration deprecateForResolution(String... alternativesForResolving) {
-        validateMutation(MutationType.USAGE);
-        this.resolutionAlternatives = ImmutableList.copyOf(alternativesForResolving);
-        if (!consumptionDeprecated) {
-            logChangingUsage("deprecated for resolution", true);
-        }
-        resolutionDeprecated = true;
-        return this;
-    }
-
-    @Override
-    public DeprecatableConfiguration deprecateForConsumption(Function<DeprecationMessageBuilder.DeprecateConfiguration, DeprecationMessageBuilder.WithDocumentation> deprecation) {
-        validateMutation(MutationType.USAGE);
-        this.consumptionDeprecation = deprecation.apply(DeprecationLogger.deprecateConfiguration(name).forConsumption());
-        if (!consumptionDeprecated) {
-            logChangingUsage("deprecated for consumption", true);
-        }
-        consumptionDeprecated = true;
-        return this;
+    public void addResolutionAlternatives(String... alternativesForResolving) {
+        this.resolutionAlternatives = ImmutableList.<String>builder()
+            .addAll(resolutionAlternatives)
+            .addAll(Arrays.asList(alternativesForResolving))
+            .build();
     }
 
     @Override
     public Configuration shouldResolveConsistentlyWith(Configuration versionsSource) {
+        warnOnDeprecatedUsage("shouldResolveConsistentlyWith(Configuration)", ProperMethodUsage.RESOLVABLE);
         this.consistentResolutionSource = (ConfigurationInternal) versionsSource;
         this.consistentResolutionReason = "version resolved in " + versionsSource + " by consistent resolution";
         return this;
@@ -1925,62 +2030,12 @@ public Configuration shouldResolveConsistentlyWith(Configuration versionsSource)
 
     @Override
     public Configuration disableConsistentResolution() {
+        warnOnDeprecatedUsage("disableConsistentResolution()", ProperMethodUsage.RESOLVABLE);
         this.consistentResolutionSource = null;
         this.consistentResolutionReason = null;
         return this;
     }
 
-    /**
-     * Print a formatted representation of a Configuration
-     */
-    public String dump() {
-        StringBuilder reply = new StringBuilder();
-
-        reply.append("\nConfiguration:");
-        reply.append("  class='").append(this.getClass()).append("'");
-        reply.append("  name='").append(this.getName()).append("'");
-        reply.append("  hashcode='").append(this.hashCode()).append("'");
-
-        reply.append("\nLocal Dependencies:");
-        if (getDependencies().size() > 0) {
-            for (Dependency d : getDependencies()) {
-                reply.append("\n   ").append(d);
-            }
-        } else {
-            reply.append("\n   none");
-        }
-
-        reply.append("\nLocal Artifacts:");
-        if (getArtifacts().size() > 0) {
-            for (PublishArtifact a : getArtifacts()) {
-                reply.append("\n   ").append(a);
-            }
-        } else {
-            reply.append("\n   none");
-        }
-
-        reply.append("\nAll Dependencies:");
-        if (getAllDependencies().size() > 0) {
-            for (Dependency d : getAllDependencies()) {
-                reply.append("\n   ").append(d);
-            }
-        } else {
-            reply.append("\n   none");
-        }
-
-
-        reply.append("\nAll Artifacts:");
-        if (getAllArtifacts().size() > 0) {
-            for (PublishArtifact a : getAllArtifacts()) {
-                reply.append("\n   ").append(a);
-            }
-        } else {
-            reply.append("\n   none");
-        }
-
-        return reply.toString();
-    }
-
     private abstract static class ResolveState {
         static final ResolveState NOT_RESOLVED = new ResolveState(UNRESOLVED) {
             @Override
@@ -2077,12 +2132,13 @@ public String getName() {
 
         @Override
         public String getPath() {
-            return path.getPath();
+            // TODO: Can we update this to identityPath?
+            return projectPath.getPath();
         }
 
         @Override
         public String toString() {
-            return "dependencies '" + getIdentityPath() + "'";
+            return "dependencies '" + identityPath + "'";
         }
 
         @Override
@@ -2126,7 +2182,7 @@ public void afterResolve(Closure action) {
         @Override
         public ResolutionResult getResolutionResult() {
             assertIsResolvable();
-            return new LenientResolutionResult(DEFAULT_ERROR_HANDLER);
+            return new LenientResolutionResult(false);
         }
 
         @Override
@@ -2160,8 +2216,8 @@ public AttributeContainer getAttributes() {
         }
 
         @Override
-        public ResolutionResult getResolutionResult(Action<? super Throwable> errorHandler) {
-            return new LenientResolutionResult(errorHandler);
+        public ResolutionResult getLenientResolutionResult() {
+            return new LenientResolutionResult(true);
         }
 
         private class ConfigurationArtifactView implements ArtifactView {
@@ -2201,12 +2257,19 @@ public FileCollection getFiles() {
             }
         }
 
-        private class LenientResolutionResult implements ResolutionResult {
-            private final Action<? super Throwable> errorHandler;
+        /**
+         * A resolution result that lazily resolves the configuration. The laziness is needed to properly support {@link #getRootComponent}.
+         */
+        private class LenientResolutionResult implements ResolutionResultInternal {
+            private final boolean lenient;
+            private volatile Throwable nonFatalFailure;
             private volatile ResolutionResult delegate;
 
-            private LenientResolutionResult(Action<? super Throwable> errorHandler) {
-                this.errorHandler = errorHandler;
+            /**
+             * @param lenient If true, non-fatal failures will not be thrown during resolution.
+             */
+            private LenientResolutionResult(boolean lenient) {
+                this.lenient = lenient;
             }
 
             private void resolve() {
@@ -2215,9 +2278,9 @@ private void resolve() {
                         if (delegate == null) {
                             ResolveState currentState = resolveToStateOrLater(ARTIFACTS_RESOLVED);
                             delegate = currentState.getCachedResolverResults().getResolutionResult();
-                            Throwable failure = currentState.getCachedResolverResults().consumeNonFatalFailure();
-                            if (failure != null) {
-                                errorHandler.execute(failure);
+                            this.nonFatalFailure = currentState.getCachedResolverResults().getNonFatalFailure();
+                            if (nonFatalFailure != null && !lenient) {
+                                throw UncheckedException.throwAsUncheckedException(nonFatalFailure);
                             }
                         }
                     }
@@ -2235,6 +2298,13 @@ public Provider<ResolvedComponentResult> getRootComponent() {
                 return new DefaultProvider<>(this::getRoot);
             }
 
+            public Provider<Throwable> getNonFatalFailure() {
+                return new DefaultProvider<>(() -> {
+                    resolve();
+                    return nonFatalFailure;
+                });
+            }
+
             @Override
             public Set<? extends DependencyResult> getAllDependencies() {
                 resolve();
@@ -2406,4 +2476,58 @@ public Optional<? extends RuntimeException> mapFailure(String type, Collection<T
             return DefaultConfiguration.this.mapFailure(type, failures);
         }
     }
+
+    private enum ProperMethodUsage {
+        CONSUMABLE {
+            @Override
+            boolean isAllowed(ConfigurationInternal configuration) {
+                return configuration.isCanBeConsumed();
+            }
+
+            @Override
+            boolean isDeprecated(ConfigurationInternal configuration) {
+                return configuration.isDeprecatedForConsumption();
+            }
+        },
+        RESOLVABLE {
+            @Override
+            boolean isAllowed(ConfigurationInternal configuration) {
+                return configuration.isCanBeResolved();
+            }
+
+            @Override
+            boolean isDeprecated(ConfigurationInternal configuration) {
+                return configuration.isDeprecatedForResolution();
+            }
+        },
+        DECLARABLE_AGAINST {
+            @Override
+            boolean isAllowed(ConfigurationInternal configuration) {
+                return configuration.isCanBeDeclared();
+            }
+
+            @Override
+            boolean isDeprecated(ConfigurationInternal configuration) {
+                return configuration.isDeprecatedForDeclarationAgainst();
+            }
+        };
+
+        abstract boolean isAllowed(ConfigurationInternal configuration);
+
+        abstract boolean isDeprecated(ConfigurationInternal configuration);
+
+        boolean isProperUsage(ConfigurationInternal configuration, boolean allowDeprecated) {
+            return isAllowed(configuration) && (allowDeprecated || !isDeprecated(configuration));
+        }
+
+        public static String buildProperName(ProperMethodUsage usage) {
+            return WordUtils.capitalizeFully(usage.name().replace('_', ' '));
+        }
+
+        public static String summarizeProperUsage(ProperMethodUsage... properUsages) {
+            return Arrays.stream(properUsages)
+                    .map(ProperMethodUsage::buildProperName)
+                    .collect(Collectors.joining(", "));
+        }
+    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
index 352f0f2..ecda31e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainer.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.api.internal.artifacts.configurations;
 
+import com.google.common.collect.ImmutableSet;
 import org.gradle.api.Action;
 import org.gradle.api.DomainObjectSet;
 import org.gradle.api.UnknownDomainObjectException;
@@ -39,20 +40,21 @@
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.internal.notations.ComponentIdentifierParserFactory;
 import org.gradle.api.model.ObjectFactory;
+import org.gradle.internal.Cast;
 import org.gradle.internal.Factory;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.typeconversion.NotationParser;
 import org.gradle.vcs.internal.VcsMappingsStore;
 
-import java.util.Collection;
-import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
+import java.util.function.Consumer;
+import java.util.regex.Pattern;
 
-public class DefaultConfigurationContainer extends AbstractValidatingNamedDomainObjectContainer<Configuration>
-    implements RoleBasedConfigurationContainerInternal, ConfigurationsProvider {
+public class DefaultConfigurationContainer extends AbstractValidatingNamedDomainObjectContainer<Configuration> implements ConfigurationContainerInternal, ConfigurationsProvider {
     public static final String DETACHED_CONFIGURATION_DEFAULT_NAME = "detachedConfiguration";
+    private static final Pattern RESERVED_NAMES_FOR_DETACHED_CONFS = Pattern.compile(DETACHED_CONFIGURATION_DEFAULT_NAME + "\\d*");
 
     @SuppressWarnings("deprecation")
     private static final ConfigurationRole DEFAULT_ROLE_TO_CREATE = ConfigurationRoles.LEGACY;
@@ -86,6 +88,8 @@ public DefaultConfigurationContainer(
         };
         this.rootComponentMetadataBuilder = rootComponentMetadataBuilderFactory.create(this);
         this.defaultConfigurationFactory = defaultConfigurationFactory;
+        this.getEventRegister().registerLazyAddAction(x -> rootComponentMetadataBuilder.getValidator().validateMutation(MutationValidator.MutationType.HIERARCHY));
+        this.whenObjectRemoved(x -> rootComponentMetadataBuilder.getValidator().validateMutation(MutationValidator.MutationType.HIERARCHY));
     }
 
     @Override
@@ -95,7 +99,20 @@ protected Configuration doCreate(String name) {
 
     @Override
     public Set<? extends ConfigurationInternal> getAll() {
-        return stream().map(ConfigurationInternal.class::cast).collect(Collectors.toCollection(LinkedHashSet::new));
+        Set<? extends ConfigurationInternal> set = Cast.uncheckedCast(this);
+        return ImmutableSet.copyOf(set);
+    }
+
+    @Override
+    public void visitAll(Consumer<ConfigurationInternal> visitor) {
+        for (Configuration configuration : this) {
+            visitor.accept((ConfigurationInternal) configuration);
+        }
+    }
+
+    @Override
+    public ConfigurationInternal findByName(String name) {
+        return (ConfigurationInternal) super.findByName(name);
     }
 
     @Override
@@ -118,7 +135,7 @@ public ConfigurationInternal detachedConfiguration(Dependency... dependencies) {
         String name = nextDetachedConfigurationName();
         DetachedConfigurationsProvider detachedConfigurationsProvider = new DetachedConfigurationsProvider();
         @SuppressWarnings("deprecation")
-        DefaultConfiguration detachedConfiguration = newConfiguration(name, detachedConfigurationsProvider, rootComponentMetadataBuilder.withConfigurationsProvider(detachedConfigurationsProvider), ConfigurationRoles.LEGACY, false);
+        DefaultConfiguration detachedConfiguration = newConfiguration(name, detachedConfigurationsProvider, rootComponentMetadataBuilder.withConfigurationsProvider(detachedConfigurationsProvider), ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET, false);
         copyAllTo(detachedConfiguration, dependencies);
         detachedConfigurationsProvider.setTheOnlyConfiguration(detachedConfiguration);
         return detachedConfiguration;
@@ -143,23 +160,8 @@ private DefaultConfiguration newConfiguration(String name,
         return defaultConfigurationFactory.create(name, detachedConfigurationsProvider, resolutionStrategyFactory, componentMetadataBuilder, role, lockUsage);
     }
 
-    /**
-     * Build a formatted representation of all Configurations in this ConfigurationContainer. Configuration(s) being toStringed are likely derivations of DefaultConfiguration.
-     */
-    public String dump() {
-        StringBuilder reply = new StringBuilder();
-
-        reply.append("Configuration of type: ").append(getTypeDisplayName());
-        Collection<? extends Configuration> configs = getAll();
-        for (Configuration c : configs) {
-            reply.append("\n  ").append(c.toString());
-        }
-
-        return reply.toString();
-    }
-
     @Override
-    public ConfigurationInternal createWithRole(String name, ConfigurationRole role, boolean lockUsage, Action<? super ConfigurationInternal> configureAction) {
+    public Configuration createWithRole(String name, ConfigurationRole role, boolean lockUsage, Action<? super Configuration> configureAction) {
         assertMutable("createWithRole(String, ConfigurationRole, boolean, Action)");
         assertCanAdd(name);
         ConfigurationInternal object = doCreate(name, role, lockUsage);
@@ -169,8 +171,19 @@ public ConfigurationInternal createWithRole(String name, ConfigurationRole role,
     }
 
     private ConfigurationInternal doCreate(String name, ConfigurationRole role, boolean lockUsage) {
+        validateNameIsAllowed(name);
         DefaultConfiguration configuration = newConfiguration(name, this, rootComponentMetadataBuilder, role, lockUsage);
         configuration.addMutationValidator(rootComponentMetadataBuilder.getValidator());
         return configuration;
     }
+
+    private void validateNameIsAllowed(String name) {
+        if (RESERVED_NAMES_FOR_DETACHED_CONFS.matcher(name).matches()) {
+            DeprecationLogger.deprecateAction("Creating a configuration with a name that starts with 'detachedConfiguration'")
+                    .withAdvice(String.format("Use a different name for the configuration '%s'.", name))
+                    .willBeRemovedInGradle9()
+                    .withUpgradeGuideSection(8, "reserved_configuration_names")
+                    .nagUser();
+        }
+    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationFactory.java
index 8191563..ad2b845 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationFactory.java
@@ -19,9 +19,9 @@
 import org.gradle.api.artifacts.ConfigurablePublishArtifact;
 import org.gradle.api.artifacts.DependencyResolutionListener;
 import org.gradle.api.capabilities.Capability;
-import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.api.internal.DomainObjectContext;
 import org.gradle.api.internal.artifacts.ConfigurationResolver;
+import org.gradle.api.internal.artifacts.ResolveExceptionContextualizer;
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
 import org.gradle.api.internal.artifacts.dsl.CapabilityNotationParserFactory;
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactNotationParserFactory;
@@ -63,7 +63,7 @@ public class DefaultConfigurationFactory {
     private final NotationParser<Object, ConfigurablePublishArtifact> artifactNotationParser;
     private final NotationParser<Object, Capability> capabilityNotationParser;
     private final ImmutableAttributesFactory attributesFactory;
-    private final DocumentationRegistry documentationRegistry;
+    private final ResolveExceptionContextualizer exceptionContextualizer;
     private final UserCodeApplicationContext userCodeApplicationContext;
     private final ProjectStateRegistry projectStateRegistry;
     private final WorkerThreadRegistry workerThreadRegistry;
@@ -84,7 +84,7 @@ public DefaultConfigurationFactory(
         BuildOperationExecutor buildOperationExecutor,
         PublishArtifactNotationParserFactory artifactNotationParserFactory,
         ImmutableAttributesFactory attributesFactory,
-        DocumentationRegistry documentationRegistry,
+        ResolveExceptionContextualizer exceptionContextualizer,
         UserCodeApplicationContext userCodeApplicationContext,
         ProjectStateRegistry projectStateRegistry,
         WorkerThreadRegistry workerThreadRegistry,
@@ -104,7 +104,7 @@ public DefaultConfigurationFactory(
         this.artifactNotationParser = artifactNotationParserFactory.create();
         this.capabilityNotationParser = new CapabilityNotationParserFactory(true).create();
         this.attributesFactory = attributesFactory;
-        this.documentationRegistry = documentationRegistry;
+        this.exceptionContextualizer = exceptionContextualizer;
         this.userCodeApplicationContext = userCodeApplicationContext;
         this.projectStateRegistry = projectStateRegistry;
         this.workerThreadRegistry = workerThreadRegistry;
@@ -145,7 +145,7 @@ DefaultConfiguration create(
                 capabilityNotationParser,
                 attributesFactory,
                 rootComponentMetadataBuilder,
-                documentationRegistry,
+                exceptionContextualizer,
                 userCodeApplicationContext,
                 projectStateRegistry,
                 workerThreadRegistry,
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
index a4973c8..01b6f30 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DetachedConfigurationsProvider.java
@@ -18,8 +18,9 @@
 import com.google.common.collect.ImmutableSet;
 
 import java.util.Set;
+import java.util.function.Consumer;
 
-class DetachedConfigurationsProvider implements ConfigurationsProvider {
+public class DetachedConfigurationsProvider implements ConfigurationsProvider {
     private ConfigurationInternal theOnlyConfiguration;
 
     @Override
@@ -32,6 +33,19 @@ public Set<ConfigurationInternal> getAll() {
         return ImmutableSet.of(theOnlyConfiguration);
     }
 
+    @Override
+    public void visitAll(Consumer<ConfigurationInternal> visitor) {
+        visitor.accept(theOnlyConfiguration);
+    }
+
+    @Override
+    public ConfigurationInternal findByName(String name) {
+        if (name.equals(theOnlyConfiguration.getName())) {
+            return theOnlyConfiguration;
+        }
+        return null;
+    }
+
     public void setTheOnlyConfiguration(ConfigurationInternal theOnlyConfiguration) {
         this.theOnlyConfiguration = theOnlyConfiguration;
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/MutationValidator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/MutationValidator.java
index fc550f4..1e07cfe 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/MutationValidator.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/MutationValidator.java
@@ -46,7 +46,17 @@ enum MutationType {
         /**
          * The mutation of the allowed usage of the configuration (can be consumed, resolved, deprecated for declaration against...)
          */
-        USAGE("usage");
+        USAGE("usage"),
+
+        /**
+         * The mutation of the role of the configuration (can be queries, resolved, ...)
+         */
+        ROLE("role"),
+
+        /**
+         * The mutation of the hierarchy of the configuration, i.e. which configurations this configuration extends from.
+         */
+        HIERARCHY("hierarchy");
 
         private final String displayName;
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java
index 0a68cb3..a8ab27e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolutionStrategyInternal.java
@@ -15,16 +15,20 @@
  */
 package org.gradle.api.internal.artifacts.configurations;
 
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.DependencySubstitution;
 import org.gradle.api.artifacts.ResolutionStrategy;
 import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal;
+import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
 import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyLockingProvider;
 import org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution.DependencySubstitutionsInternal;
 import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.CapabilitiesResolutionInternal;
+import org.gradle.internal.ImmutableActionSet;
 
 public interface ResolutionStrategyInternal extends ResolutionStrategy {
+    /**
+     * Discard any configuration state that is not required after graph resolution has been attempted.
+     */
+    void discardStateRequiredForGraphResolution();
 
     /**
      * Gets the current expiry policy for dynamic revisions.
@@ -43,7 +47,7 @@ public interface ResolutionStrategyInternal extends ResolutionStrategy {
     /**
      * @return the dependency substitution rule (may aggregate multiple rules)
      */
-    Action<DependencySubstitution> getDependencySubstitutionRule();
+    ImmutableActionSet<DependencySubstitutionInternal> getDependencySubstitutionRule();
 
     /**
      * Used by tests to validate behaviour of the 'task graph modified' state
@@ -106,4 +110,8 @@ public interface ResolutionStrategyInternal extends ResolutionStrategy {
     boolean isFailingOnChangingVersions();
 
     boolean isDependencyVerificationEnabled();
+
+    void setReturnAllVariants(boolean returnAllVariants);
+
+    boolean getReturnAllVariants();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolvableDependenciesInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolvableDependenciesInternal.java
index 3ef7182..388c97b 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolvableDependenciesInternal.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolvableDependenciesInternal.java
@@ -15,10 +15,9 @@
  */
 package org.gradle.api.internal.artifacts.configurations;
 
-import org.gradle.api.Action;
 import org.gradle.api.artifacts.ResolvableDependencies;
 import org.gradle.api.artifacts.result.ResolutionResult;
 
 public interface ResolvableDependenciesInternal extends ResolvableDependencies  {
-    ResolutionResult getResolutionResult(Action<? super Throwable> errorHandler);
+    ResolutionResult getLenientResolutionResult();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveConfigurationResolutionBuildOperationDetails.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveConfigurationResolutionBuildOperationDetails.java
index a8fd11a..ded3dfc 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveConfigurationResolutionBuildOperationDetails.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveConfigurationResolutionBuildOperationDetails.java
@@ -170,7 +170,7 @@ private RepositoryImpl(RepositoryDescriptor descriptor) {
 
         @Override
         public String getId() {
-            return descriptor.name;
+            return descriptor.getId();
         }
 
         @Override
@@ -180,7 +180,7 @@ public String getType() {
 
         @Override
         public String getName() {
-            return descriptor.name;
+            return descriptor.getName();
         }
 
         @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveConfigurationResolutionBuildOperationResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveConfigurationResolutionBuildOperationResult.java
index c3b6209..e5da686 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveConfigurationResolutionBuildOperationResult.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveConfigurationResolutionBuildOperationResult.java
@@ -62,7 +62,7 @@ public ResolvedComponentResult getRootComponent() {
 
     @Override
     public String getRepositoryId(ResolvedComponentResult resolvedComponentResult) {
-        return ((ResolvedComponentResultInternal) resolvedComponentResult).getRepositoryName();
+        return ((ResolvedComponentResultInternal) resolvedComponentResult).getRepositoryId();
     }
 
     @Override
@@ -72,7 +72,7 @@ public Object getCustomOperationTraceSerializableModel() {
         final Map<String, Map<String, String>> components = Maps.newHashMap();
         resolutionResult.allComponents(component -> components.put(
             component.getId().getDisplayName(),
-            Collections.singletonMap("repoName", ((ResolvedComponentResultInternal) component).getRepositoryName())
+            Collections.singletonMap("repoId", getRepositoryId(component))
         ));
         model.put("components", components);
         ImmutableList.Builder<Object> requestedAttributesBuilder = new ImmutableList.Builder<>();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveExceptionWithHints.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveExceptionWithHints.java
deleted file mode 100644
index dae730f..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/ResolveExceptionWithHints.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2020 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.api.internal.artifacts.configurations;
-
-
-import org.gradle.api.artifacts.ResolveException;
-
-class ResolveExceptionWithHints extends ResolveException {
-
-    private final String[] hints;
-
-    public ResolveExceptionWithHints(String resolveContext, Iterable<? extends Throwable> causes, String... hints) {
-        super(resolveContext, causes);
-        this.hints = hints;
-    }
-
-    @Override
-    public String getMessage() {
-        StringBuilder sb = new StringBuilder(super.getMessage());
-        for (String hint : hints) {
-            sb.append("\n");
-            sb.append(hint);
-        }
-        return sb.toString();
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/RoleBasedConfigurationContainerInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/RoleBasedConfigurationContainerInternal.java
deleted file mode 100644
index 3ee39d6..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/RoleBasedConfigurationContainerInternal.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright 2022 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.api.internal.artifacts.configurations;
-
-import org.gradle.api.Action;
-import org.gradle.api.GradleException;
-import org.gradle.internal.Actions;
-
-/**
- * Extends {@link ConfigurationContainerInternal} with methods that can use {@link ConfigurationRole}s to
- * define the allowed usage of a configuration at creation time.
- */
-public interface RoleBasedConfigurationContainerInternal extends ConfigurationContainerInternal {
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#INTENDED_CONSUMABLE}.
-     */
-    default ConfigurationInternal consumable(String name, boolean lockRole) {
-        return createWithRole(name, ConfigurationRoles.INTENDED_CONSUMABLE, lockRole);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#INTENDED_RESOLVABLE}.
-     */
-    default ConfigurationInternal resolvable(String name, boolean lockRole) {
-        return createWithRole(name, ConfigurationRoles.INTENDED_RESOLVABLE, lockRole);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#INTENDED_RESOLVABLE_BUCKET}.
-     */
-    @SuppressWarnings("deprecation")
-    default ConfigurationInternal resolvableBucket(String name, boolean lockRole) {
-        return createWithRole(name, ConfigurationRoles.INTENDED_RESOLVABLE_BUCKET, lockRole);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#INTENDED_BUCKET}.
-     */
-    default ConfigurationInternal bucket(String name, boolean lockRole) {
-        return createWithRole(name, ConfigurationRoles.INTENDED_BUCKET, lockRole);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#INTENDED_CONSUMABLE} that is <strong>NOT</strong> locked
-     * against further usage mutations.
-     */
-    default ConfigurationInternal consumable(String name) {
-        return consumable(name, false);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#INTENDED_RESOLVABLE} that is <strong>NOT</strong> locked
-     * against further usage mutations.
-     */
-    default ConfigurationInternal resolvable(String name) {
-        return resolvable(name, false);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#INTENDED_RESOLVABLE_BUCKET} that is <strong>NOT</strong> locked
-     * against further usage mutations.
-     */
-    default ConfigurationInternal resolvableBucket(String name) {
-        return resolvableBucket(name, false);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#INTENDED_BUCKET} that is <strong>NOT</strong> locked
-     * against further usage mutations.
-     */
-    default ConfigurationInternal bucket(String name) {
-        return bucket(name, false);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#DEPRECATED_CONSUMABLE}.
-     */
-    @SuppressWarnings("deprecation")
-    default ConfigurationInternal deprecatedConsumable(String name, boolean lockRole) {
-        return createWithRole(name, ConfigurationRoles.DEPRECATED_CONSUMABLE, lockRole);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#DEPRECATED_RESOLVABLE}.
-     */
-    @SuppressWarnings("deprecation")
-    default ConfigurationInternal deprecatedResolvable(String name, boolean lockRole) {
-        return createWithRole(name, ConfigurationRoles.DEPRECATED_RESOLVABLE, lockRole);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#DEPRECATED_CONSUMABLE} that is <strong>NOT</strong> locked
-     * against further usage mutations.
-     */
-    default ConfigurationInternal deprecatedConsumable(String name) {
-        return deprecatedConsumable(name, false);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * using the role of {@link ConfigurationRoles#DEPRECATED_RESOLVABLE} that is <strong>NOT</strong> locked
-     * against further usage mutations.
-     */
-    default ConfigurationInternal deprecatedResolvable(String name) {
-        return deprecatedResolvable(name, false);
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #create(String)} using the given role
-     * at creation.
-     *
-     * @param name the name of the configuration
-     * @param role the role defining the configuration's allowed usage
-     * @param lockUsage {@code true} if the configuration's allowed usage should be locked to prevent any changes; {@code false} otherwise
-     * @param configureAction an action to run upon the configuration's creation to configure it
-     * @return the new configuration
-     */
-    ConfigurationInternal createWithRole(String name, ConfigurationRole role, boolean lockUsage, Action<? super ConfigurationInternal> configureAction);
-
-    /**
-     * Creates a new configuration in the same manner as {@link #create(String)} using the given role
-     * at creation.
-     *
-     * @param name the name of the configuration
-     * @param role the role defining the configuration's allowed usage
-     * @param lockUsage {@code true} if the configuration's allowed usage should be locked to prevent any changes; {@code false} otherwise
-     * @return the new configuration
-     */
-    default ConfigurationInternal createWithRole(String name, ConfigurationRole role, boolean lockUsage) {
-        return createWithRole(name, role, lockUsage, Actions.doNothing());
-    }
-
-    /**
-     * Creates a new configuration in the same manner as {@link #create(String)} using the given role
-     * at creation and configuring it with the given action, without automatically locking the configuration's allowed usage.
-     *
-     * @param name the name of the configuration
-     * @param role the role defining the configuration's allowed usage
-     * @param configureAction an action to run upon the configuration's creation to configure it
-     * @return the new configuration
-     */
-    default ConfigurationInternal createWithRole(String name, ConfigurationRole role, Action<? super ConfigurationInternal> configureAction) {
-        return createWithRole(name, role, false, configureAction);
-    }
-
-
-    /**
-     * Creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)}
-     * without locking the configuration's allowed usage.
-     */
-    default ConfigurationInternal createWithRole(String name, ConfigurationRole role) {
-        return createWithRole(name, role, false);
-    }
-
-    /**
-     * If it does not already exist, creates a new configuration in the same manner as {@link #createWithRole(String, ConfigurationRole, boolean)};
-     * if the configuration does already exist, this method will <strong>NOT</strong>> change anything about its allowed,
-     * including its role, but <strong>CAN</strong> optionally confirm that the current usage of the configuration
-     * matches the given role and/or prevent any further changes to the configuration's allowed usage.
-     *
-     * @param name the name of the configuration
-     * @param role the role defining the configuration's allowed usage
-     * @param lockUsage {@code true} if the configuration's allowed usage should be locked to prevent any changes; {@code false} otherwise
-     * @param assertInRole {@code true} if the configuration's current usage should be confirmed to match that specified by the given role
-     * @return the new configuration
-     */
-    default ConfigurationInternal maybeCreateWithRole(String name, ConfigurationRole role, boolean lockUsage, boolean assertInRole) {
-        ConfigurationInternal configuration = (ConfigurationInternal) findByName(name);
-        if (configuration == null) {
-            return createWithRole(name, role, lockUsage);
-        } else {
-            if (assertInRole) {
-                RoleChecker.assertIsInRole(configuration, role);
-            }
-            if (lockUsage) {
-                configuration.preventUsageMutation();
-            }
-            return configuration;
-        }
-    }
-
-    /**
-     * This static util class hides methods internal to the {@code default} methods in the {@link ConfigurationContainerInternal} interface.
-     */
-    abstract class RoleChecker {
-        private RoleChecker() { /* not instantiable */ }
-
-        /**
-         * Checks that the current allowed usage of a configuration is the same as that specified by a given role.
-         *
-         * @param configuration the configuration to check
-         * @param role the role to check against
-         * @return {@code true} if so; {@code false} otherwise
-         */
-        public static boolean isUsageConsistentWithRole(ConfigurationInternal configuration, ConfigurationRole role) {
-            return (role.isConsumable() == configuration.isCanBeConsumed())
-                    && (role.isResolvable() == configuration.isCanBeResolved())
-                    && (role.isDeclarableAgainst() == configuration.isCanBeDeclaredAgainst())
-                    && (role.isConsumptionDeprecated() == configuration.isDeprecatedForConsumption())
-                    && (role.isResolutionDeprecated() == configuration.isDeprecatedForResolution())
-                    && (role.isDeclarationAgainstDeprecated() == configuration.isDeprecatedForDeclarationAgainst());
-        }
-
-        /**
-         * Checks that the current allowed usage of a configuration is the same as that specified by a given role,
-         * and throws an exception with a message describing the differences if not.
-         *
-         * @param configuration the configuration to check
-         * @param role the role to check against
-         */
-        public static void assertIsInRole(ConfigurationInternal configuration, ConfigurationRole role) {
-            if (!isUsageConsistentWithRole(configuration, role)) {
-                throw new GradleException(describeDifferenceFromRole(configuration, role));
-            }
-        }
-
-        private static String describeDifferenceFromRole(ConfigurationInternal configuration, ConfigurationRole role) {
-            if (!isUsageConsistentWithRole(configuration, role)) {
-                ConfigurationRole currentUsage = ConfigurationRole.forUsage(
-                        configuration.isCanBeConsumed(), configuration.isCanBeResolved(), configuration.isCanBeDeclaredAgainst(),
-                        configuration.isDeprecatedForConsumption(), configuration.isDeprecatedForResolution(), configuration.isDeprecatedForDeclarationAgainst());
-                return "Usage for configuration: " + configuration.getName() + " is not consistent with the role: " + role.getName() + ".\n" +
-                        "Expected that it is:\n" +
-                        role.describeUsage() + "\n" +
-                        "But is actually is:\n" +
-                        currentUsage.describeUsage();
-            } else {
-                return "Usage for configuration: " + configuration.getName() + " is consistent with the role: " + role.getName() + ".";
-            }
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ArtifactFile.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ArtifactFile.java
index 2029318..b992196 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ArtifactFile.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/ArtifactFile.java
@@ -29,7 +29,7 @@ public class ArtifactFile {
     private String classifier;
     private String extension;
 
-    public ArtifactFile(File file, String version) {
+    public ArtifactFile(File file, @Nullable String version) {
         this(file.getName(), version);
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.java
index bdbf638..2b79483 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandler.java
@@ -89,9 +89,9 @@ private void warnIfConfigurationIsDeprecated(DeprecatableConfiguration configura
      * Exists only to maintain the existing fully deprecated logic in Gradle 8.0 - DO NOT USE.
      */
     private boolean isFullyDeprecated(DeprecatableConfiguration configuration) {
-        return configuration.getDeclarationAlternatives() != null &&
-                (!configuration.isCanBeConsumed() || configuration.getConsumptionDeprecation() != null) &&
-                (!configuration.isCanBeResolved() || configuration.getResolutionAlternatives() != null);
+        return configuration.isDeprecatedForDeclarationAgainst() &&
+            (!configuration.isCanBeConsumed() || configuration.isDeprecatedForConsumption()) &&
+            (!configuration.isCanBeResolved() || configuration.isDeprecatedForResolution());
     }
 
     private void assertConfigurationIsValidForArtifacts(DeprecatableConfiguration configuration) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/FileSystemPublishArtifact.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/FileSystemPublishArtifact.java
index 776697f..9cb287e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/FileSystemPublishArtifact.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/dsl/FileSystemPublishArtifact.java
@@ -21,6 +21,7 @@
 import org.gradle.api.internal.tasks.TaskDependencyInternal;
 import org.gradle.api.tasks.TaskDependency;
 
+import javax.annotation.Nullable;
 import java.io.File;
 import java.util.Date;
 
@@ -30,7 +31,7 @@ public class FileSystemPublishArtifact implements PublishArtifactInternal {
     private final String version;
     private ArtifactFile artifactFile;
 
-    public FileSystemPublishArtifact(final FileSystemLocation fileSystemLocation, final String version) {
+    public FileSystemPublishArtifact(FileSystemLocation fileSystemLocation, @Nullable String version) {
         this.fileSystemLocation = fileSystemLocation;
         this.version = version;
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveState.java
new file mode 100644
index 0000000..81c1e3b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactResolveState.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.ivyservice;
+
+import org.gradle.api.artifacts.UnresolvedDependency;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactsResults;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedFileDependencyResults;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.ResolvedGraphResults;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResultsBuilder;
+
+import java.util.Set;
+
+/**
+ * Intermediate state saved between graph resolution and artifact resolution.
+ */
+public class ArtifactResolveState {
+    final ResolvedGraphResults graphResults;
+    final VisitedArtifactsResults artifactsResults;
+    final VisitedFileDependencyResults fileDependencyResults;
+    final Set<UnresolvedDependency> failures;
+    final TransientConfigurationResultsBuilder transientConfigurationResultsBuilder;
+
+    ArtifactResolveState(ResolvedGraphResults graphResults, VisitedArtifactsResults artifactsResults, VisitedFileDependencyResults fileDependencyResults, Set<UnresolvedDependency> failures, TransientConfigurationResultsBuilder transientConfigurationResultsBuilder) {
+        this.graphResults = graphResults;
+        this.artifactsResults = artifactsResults;
+        this.fileDependencyResults = fileDependencyResults;
+        this.failures = failures;
+        this.transientConfigurationResultsBuilder = transientConfigurationResultsBuilder;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java
index e620f0a..256964d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/CacheLayout.java
@@ -73,6 +73,8 @@ public enum CacheLayout {
         .changedTo(97, "6.8-rc-1")
         .changedTo(99, "7.5-rc-1")
         .changedTo(100, "8.0-milestone-5")
+        .changedTo(105, "8.1-rc-2")
+        .changedTo(106, "8.2-milestone-1")
     ),
 
     RESOURCES(ROOT, "resources", introducedIn("1.9-rc-1")),
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultArtifactCaches.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultArtifactCaches.java
index 5c6a3f6..ef9d81d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultArtifactCaches.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultArtifactCaches.java
@@ -21,8 +21,8 @@
 import org.gradle.api.internal.cache.CacheConfigurationsInternal;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.cache.UnscopedCacheBuilderFactory;
 import org.gradle.cache.IndexedCache;
+import org.gradle.cache.UnscopedCacheBuilderFactory;
 import org.gradle.cache.internal.UsedGradleVersions;
 import org.gradle.cache.scopes.GlobalScopedCacheBuilderFactory;
 import org.gradle.internal.Factory;
@@ -82,10 +82,10 @@ private static File validateReadOnlyCache(DocumentationRegistry documentationReg
         }
         File root = CacheLayout.ROOT.getPath(cacheDir);
         if (!root.exists()) {
-            String docLink = documentationRegistry.getDocumentationFor("dependency_resolution", "sub:shared-readonly-cache");
+            String docLink = documentationRegistry.getDocumentationRecommendationFor("instructions on how to do this", "dependency_resolution", "sub:shared-readonly-cache");
             LOGGER.warn("The read-only dependency cache is disabled because of a configuration problem:");
             LOGGER.warn("Read-only cache is configured but the directory layout isn't expected. You must have a pre-populated " +
-                CacheLayout.ROOT.getKey() + " directory at " + root + " . Please follow the instructions at " + docLink);
+                CacheLayout.ROOT.getKey() + " directory at " + root + " . " + docLink);
             return null;
         }
         return cacheDir;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.java
index 0fef181d..9a8e8ee 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultConfigurationResolver.java
@@ -17,19 +17,20 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 import com.google.common.collect.ImmutableList;
-import org.gradle.api.artifacts.ProjectDependency;
 import org.gradle.api.artifacts.UnresolvedDependency;
 import org.gradle.api.artifacts.component.BuildIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentSelector;
 import org.gradle.api.artifacts.result.ResolvedComponentResult;
 import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
 import org.gradle.api.internal.artifacts.ComponentSelectorConverter;
 import org.gradle.api.internal.artifacts.ConfigurationResolver;
+import org.gradle.api.internal.artifacts.DefaultResolverResults;
 import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
 import org.gradle.api.internal.artifacts.RepositoriesSupplier;
+import org.gradle.api.internal.artifacts.ResolveContext;
+import org.gradle.api.internal.artifacts.ResolveExceptionContextualizer;
 import org.gradle.api.internal.artifacts.ResolverResults;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
 import org.gradle.api.internal.artifacts.configurations.ConflictResolution;
 import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.DependencyVerificationOverride;
@@ -37,6 +38,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.BuildDependenciesOnlyVisitedArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.DefaultResolvedArtifactsBuilder;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.DependencyArtifactsVisitor;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactsResults;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedFileDependencyResults;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.CompositeDependencyArtifactsVisitor;
@@ -52,13 +54,15 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResultsLoader;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedLocalComponentsResultGraphVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.AttributeContainerSerializer;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentDetailsSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorFactory;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.FileDependencyCollectingGraphVisitor;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.SelectedVariantSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.StreamingResolutionResultBuilder;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.ResolutionResultsStoreFactory;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.StoreSet;
 import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransforms;
+import org.gradle.api.internal.artifacts.transform.VariantSelectorFactory;
 import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry;
 import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
@@ -67,25 +71,25 @@
 import org.gradle.cache.internal.BinaryStore;
 import org.gradle.cache.internal.Store;
 import org.gradle.internal.Cast;
-import org.gradle.internal.component.local.model.DslOriginDependencyMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
 import org.gradle.internal.locking.DependencyLockingArtifactVisitor;
 import org.gradle.internal.operations.BuildOperationExecutor;
 import org.gradle.internal.work.WorkerLeaseService;
+import org.gradle.util.Path;
 
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
 public class DefaultConfigurationResolver implements ConfigurationResolver {
-    private static final Spec<DependencyMetadata> IS_LOCAL_EDGE = element -> element instanceof DslOriginDependencyMetadata && ((DslOriginDependencyMetadata) element).getSource() instanceof ProjectDependency;
+    private static final Spec<DependencyMetadata> IS_LOCAL_EDGE = element -> element.getSelector() instanceof ProjectComponentSelector;
     private final ArtifactDependencyResolver resolver;
     private final RepositoriesSupplier repositoriesSupplier;
     private final GlobalDependencyResolutionRules metadataHandler;
     private final ResolutionResultsStoreFactory storeFactory;
     private final boolean buildProjectDependencies;
     private final AttributesSchemaInternal attributesSchema;
-    private final ArtifactTransforms artifactTransforms;
+    private final VariantSelectorFactory variantSelectorFactory;
     private final ImmutableModuleIdentifierFactory moduleIdentifierFactory;
     private final BuildOperationExecutor buildOperationExecutor;
     private final ArtifactTypeRegistry artifactTypeRegistry;
@@ -97,31 +101,39 @@ public class DefaultConfigurationResolver implements ConfigurationResolver {
     private final ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory;
     private final WorkerLeaseService workerLeaseService;
     private final ProjectDependencyResolver projectDependencyResolver;
+    private final ResolveExceptionContextualizer exceptionContextualizer;
+    private final ComponentDetailsSerializer componentDetailsSerializer;
+    private final SelectedVariantSerializer selectedVariantSerializer;
 
-    public DefaultConfigurationResolver(ArtifactDependencyResolver resolver,
-                                        RepositoriesSupplier repositoriesSupplier,
-                                        GlobalDependencyResolutionRules metadataHandler,
-                                        ResolutionResultsStoreFactory storeFactory,
-                                        boolean buildProjectDependencies,
-                                        AttributesSchemaInternal attributesSchema,
-                                        ArtifactTransforms artifactTransforms,
-                                        ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-                                        BuildOperationExecutor buildOperationExecutor,
-                                        ArtifactTypeRegistry artifactTypeRegistry,
-                                        ComponentSelectorConverter componentSelectorConverter,
-                                        AttributeContainerSerializer attributeContainerSerializer,
-                                        BuildIdentifier currentBuild, AttributeDesugaring attributeDesugaring,
-                                        DependencyVerificationOverride dependencyVerificationOverride,
-                                        ProjectDependencyResolver projectDependencyResolver,
-                                        ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
-                                        WorkerLeaseService workerLeaseService) {
+    public DefaultConfigurationResolver(
+        ArtifactDependencyResolver resolver,
+        RepositoriesSupplier repositoriesSupplier,
+        GlobalDependencyResolutionRules metadataHandler,
+        ResolutionResultsStoreFactory storeFactory,
+        boolean buildProjectDependencies,
+        AttributesSchemaInternal attributesSchema,
+        VariantSelectorFactory variantSelectorFactory,
+        ImmutableModuleIdentifierFactory moduleIdentifierFactory,
+        BuildOperationExecutor buildOperationExecutor,
+        ArtifactTypeRegistry artifactTypeRegistry,
+        ComponentSelectorConverter componentSelectorConverter,
+        AttributeContainerSerializer attributeContainerSerializer,
+        BuildIdentifier currentBuild, AttributeDesugaring attributeDesugaring,
+        DependencyVerificationOverride dependencyVerificationOverride,
+        ProjectDependencyResolver projectDependencyResolver,
+        ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
+        WorkerLeaseService workerLeaseService,
+        ResolveExceptionContextualizer exceptionContextualizer,
+        ComponentDetailsSerializer componentDetailsSerializer,
+        SelectedVariantSerializer selectedVariantSerializer
+    ) {
         this.resolver = resolver;
         this.repositoriesSupplier = repositoriesSupplier;
         this.metadataHandler = metadataHandler;
         this.storeFactory = storeFactory;
         this.buildProjectDependencies = buildProjectDependencies;
         this.attributesSchema = attributesSchema;
-        this.artifactTransforms = artifactTransforms;
+        this.variantSelectorFactory = variantSelectorFactory;
         this.moduleIdentifierFactory = moduleIdentifierFactory;
         this.buildOperationExecutor = buildOperationExecutor;
         this.artifactTypeRegistry = artifactTypeRegistry;
@@ -133,22 +145,27 @@ public DefaultConfigurationResolver(ArtifactDependencyResolver resolver,
         this.projectDependencyResolver = projectDependencyResolver;
         this.componentSelectionDescriptorFactory = componentSelectionDescriptorFactory;
         this.workerLeaseService = workerLeaseService;
+        this.exceptionContextualizer = exceptionContextualizer;
+        this.componentDetailsSerializer = componentDetailsSerializer;
+        this.selectedVariantSerializer = selectedVariantSerializer;
     }
 
     @Override
-    public void resolveBuildDependencies(ConfigurationInternal configuration, ResolverResults result) {
-        ResolutionStrategyInternal resolutionStrategy = configuration.getResolutionStrategy();
+    public ResolverResults resolveBuildDependencies(ResolveContext resolveContext) {
+        ResolutionStrategyInternal resolutionStrategy = resolveContext.getResolutionStrategy();
         ResolutionFailureCollector failureCollector = new ResolutionFailureCollector(componentSelectorConverter);
         InMemoryResolutionResultBuilder resolutionResultBuilder = new InMemoryResolutionResultBuilder();
         ResolvedLocalComponentsResultGraphVisitor localComponentsVisitor = new ResolvedLocalComponentsResultGraphVisitor(currentBuild);
         CompositeDependencyGraphVisitor graphVisitor = new CompositeDependencyGraphVisitor(failureCollector, resolutionResultBuilder, localComponentsVisitor);
         DefaultResolvedArtifactsBuilder artifactsVisitor = new DefaultResolvedArtifactsBuilder(buildProjectDependencies, resolutionStrategy.getSortOrder());
-        resolver.resolve(configuration, ImmutableList.of(), metadataHandler, IS_LOCAL_EDGE, graphVisitor, artifactsVisitor, attributesSchema, artifactTypeRegistry, projectDependencyResolver, false);
-        result.graphResolved(resolutionResultBuilder.getResolutionResult(), localComponentsVisitor, new BuildDependenciesOnlyVisitedArtifactSet(failureCollector.complete(Collections.emptySet()), artifactsVisitor.complete(), artifactTransforms, configuration.getDependenciesResolver()));
+        resolver.resolve(resolveContext, ImmutableList.of(), metadataHandler, IS_LOCAL_EDGE, graphVisitor, artifactsVisitor, attributesSchema, artifactTypeRegistry, projectDependencyResolver, false);
+
+        VisitedArtifactSet artifacts = new BuildDependenciesOnlyVisitedArtifactSet(failureCollector.complete(Collections.emptySet()), artifactsVisitor.complete(), variantSelectorFactory, resolveContext.getDependenciesResolverFactory());
+        return DefaultResolverResults.buildDependenciesResolved(resolutionResultBuilder.getResolutionResult(), localComponentsVisitor, artifacts);
     }
 
     @Override
-    public void resolveGraph(ConfigurationInternal configuration, ResolverResults results) {
+    public ResolverResults resolveGraph(ResolveContext resolveContext) {
         List<ResolutionAwareRepository> resolutionAwareRepositories = getRepositories();
         StoreSet stores = storeFactory.createStoreSet();
 
@@ -160,11 +177,11 @@ public void resolveGraph(ConfigurationInternal configuration, ResolverResults re
 
         BinaryStore newModelStore = stores.nextBinaryStore();
         Store<ResolvedComponentResult> newModelCache = stores.newModelCache();
-        StreamingResolutionResultBuilder newModelBuilder = new StreamingResolutionResultBuilder(newModelStore, newModelCache, moduleIdentifierFactory, attributeContainerSerializer, attributeDesugaring, componentSelectionDescriptorFactory, configuration.getReturnAllVariants());
+        ResolutionStrategyInternal resolutionStrategy = resolveContext.getResolutionStrategy();
+        StreamingResolutionResultBuilder newModelBuilder = new StreamingResolutionResultBuilder(newModelStore, newModelCache, attributeContainerSerializer, componentDetailsSerializer, selectedVariantSerializer, attributeDesugaring, componentSelectionDescriptorFactory, resolutionStrategy.getReturnAllVariants());
 
         ResolvedLocalComponentsResultGraphVisitor localComponentsVisitor = new ResolvedLocalComponentsResultGraphVisitor(currentBuild);
 
-        ResolutionStrategyInternal resolutionStrategy = configuration.getResolutionStrategy();
         DefaultResolvedArtifactsBuilder artifactsBuilder = new DefaultResolvedArtifactsBuilder(buildProjectDependencies, resolutionStrategy.getSortOrder());
         FileDependencyCollectingGraphVisitor fileDependencyVisitor = new FileDependencyCollectingGraphVisitor();
         ResolutionFailureCollector failureCollector = new ResolutionFailureCollector(componentSelectorConverter);
@@ -175,24 +192,22 @@ public void resolveGraph(ConfigurationInternal configuration, ResolverResults re
         visitors.add(fileDependencyVisitor);
         visitors.add(artifactsBuilder);
         if (resolutionStrategy.getConflictResolution() == ConflictResolution.strict) {
-            ProjectComponentIdentifier projectId = configuration.getModule().getProjectId();
-            // projectId is null for DefaultModule used in settings
-            String projectPath = projectId != null
-                ? projectId.getProjectPath()
-                : "";
-            visitors.add(new FailOnVersionConflictArtifactsVisitor(projectPath, configuration.getName()));
+            Path projectPath = resolveContext.getDomainObjectContext().getProjectPath();
+            // projectPath is null for settings execution
+            String path = projectPath != null ? projectPath.getPath() : "";
+            visitors.add(new FailOnVersionConflictArtifactsVisitor(path, resolveContext.getName()));
         }
         DependencyLockingArtifactVisitor lockingVisitor = null;
         if (resolutionStrategy.isDependencyLockingEnabled()) {
-            lockingVisitor = new DependencyLockingArtifactVisitor(configuration.getName(), resolutionStrategy.getDependencyLockingProvider());
+            lockingVisitor = new DependencyLockingArtifactVisitor(resolveContext.getName(), resolutionStrategy.getDependencyLockingProvider());
             visitors.add(lockingVisitor);
         } else {
-            resolutionStrategy.confirmUnlockedConfigurationResolved(configuration.getName());
+            resolutionStrategy.confirmUnlockedConfigurationResolved(resolveContext.getName());
         }
         ImmutableList<DependencyArtifactsVisitor> allVisitors = visitors.build();
         CompositeDependencyArtifactsVisitor artifactsVisitor = new CompositeDependencyArtifactsVisitor(allVisitors);
 
-        resolver.resolve(configuration, resolutionAwareRepositories, metadataHandler, Specs.satisfyAll(), graphVisitor, artifactsVisitor, attributesSchema, artifactTypeRegistry, projectDependencyResolver, true);
+        resolver.resolve(resolveContext, resolutionAwareRepositories, metadataHandler, Specs.satisfyAll(), graphVisitor, artifactsVisitor, attributesSchema, artifactTypeRegistry, projectDependencyResolver, true);
 
         VisitedArtifactsResults artifactsResults = artifactsBuilder.complete();
         VisitedFileDependencyResults fileDependencyResults = fileDependencyVisitor.complete();
@@ -202,13 +217,23 @@ public void resolveGraph(ConfigurationInternal configuration, ResolverResults re
         Set<UnresolvedDependency> extraFailures = lockingVisitor == null
             ? Collections.emptySet()
             : lockingVisitor.collectLockingFailures();
-        Set<UnresolvedDependency> failures = failureCollector.complete(extraFailures);
-        results.graphResolved(newModelBuilder.complete(extraFailures), localComponentsVisitor, new BuildDependenciesOnlyVisitedArtifactSet(failures, artifactsResults, artifactTransforms, configuration.getDependenciesResolver()));
 
-        results.retainState(new ArtifactResolveState(graphResults, artifactsResults, fileDependencyResults, failures, oldTransientModelBuilder));
-        if (!results.hasError() && failures.isEmpty()) {
-            artifactsVisitor.complete();
+        Set<UnresolvedDependency> failures = failureCollector.complete(extraFailures);
+
+        ArtifactResolveState artifactResolveState = new ArtifactResolveState(graphResults, artifactsResults, fileDependencyResults, failures, oldTransientModelBuilder);
+        VisitedArtifactSet visitedArtifactSet = new BuildDependenciesOnlyVisitedArtifactSet(failures, artifactsResults, variantSelectorFactory, resolveContext.getDependenciesResolverFactory());
+        ResolverResults results = DefaultResolverResults.graphResolved(newModelBuilder.complete(extraFailures), localComponentsVisitor, visitedArtifactSet, artifactResolveState);
+
+        // TODO: Attach these failures to the resolution result instead of the ResolverResults.
+        if (failures.isEmpty()) {
+            try {
+                artifactsVisitor.complete();
+            } catch (Exception e) {
+                results = results.withFailure(exceptionContextualizer.contextualize(e, resolveContext));
+            }
         }
+
+        return results;
     }
 
     @Override
@@ -217,33 +242,17 @@ public List<ResolutionAwareRepository> getRepositories() {
     }
 
     @Override
-    public void resolveArtifacts(ConfigurationInternal configuration, ResolverResults results) {
-        ArtifactResolveState resolveState = (ArtifactResolveState) results.getArtifactResolveState();
-        ResolvedGraphResults graphResults = resolveState.graphResults;
+    public ResolverResults resolveArtifacts(ResolveContext resolveContext, ResolverResults graphResults) {
+        ArtifactResolveState resolveState = graphResults.getArtifactResolveState();
         VisitedArtifactsResults artifactResults = resolveState.artifactsResults;
         TransientConfigurationResultsBuilder transientConfigurationResultsBuilder = resolveState.transientConfigurationResultsBuilder;
 
-        TransientConfigurationResultsLoader transientConfigurationResultsFactory = new TransientConfigurationResultsLoader(transientConfigurationResultsBuilder, graphResults);
+        TransientConfigurationResultsLoader transientConfigurationResultsFactory = new TransientConfigurationResultsLoader(transientConfigurationResultsBuilder, resolveState.graphResults);
 
         boolean selectFromAllVariants = false;
-        DefaultLenientConfiguration result = new DefaultLenientConfiguration(configuration, selectFromAllVariants, resolveState.failures, artifactResults, resolveState.fileDependencyResults, transientConfigurationResultsFactory, artifactTransforms, buildOperationExecutor, dependencyVerificationOverride, workerLeaseService);
-        results.artifactsResolved(new DefaultResolvedConfiguration(result), result);
-    }
+        DefaultLenientConfiguration result = new DefaultLenientConfiguration(resolveContext, selectFromAllVariants, resolveState.failures, artifactResults, resolveState.fileDependencyResults, transientConfigurationResultsFactory, variantSelectorFactory, buildOperationExecutor, dependencyVerificationOverride, workerLeaseService);
 
-    private static class ArtifactResolveState {
-        final ResolvedGraphResults graphResults;
-        final VisitedArtifactsResults artifactsResults;
-        final VisitedFileDependencyResults fileDependencyResults;
-        final Set<UnresolvedDependency> failures;
-        final TransientConfigurationResultsBuilder transientConfigurationResultsBuilder;
-
-        ArtifactResolveState(ResolvedGraphResults graphResults, VisitedArtifactsResults artifactsResults, VisitedFileDependencyResults fileDependencyResults, Set<UnresolvedDependency> failures, TransientConfigurationResultsBuilder transientConfigurationResultsBuilder) {
-            this.graphResults = graphResults;
-            this.artifactsResults = artifactsResults;
-            this.fileDependencyResults = fileDependencyResults;
-            this.failures = failures;
-            this.transientConfigurationResultsBuilder = transientConfigurationResultsBuilder;
-        }
+        return DefaultResolverResults.artifactsResolved(graphResults.getResolutionResult(), graphResults.getResolvedLocalComponents(), new DefaultResolvedConfiguration(result), result);
     }
 
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
index fc599eb..be3cdd0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
@@ -28,7 +28,7 @@
 import org.gradle.api.capabilities.Capability;
 import org.gradle.api.internal.artifacts.DependencyGraphNodeResult;
 import org.gradle.api.internal.artifacts.ResolveArtifactsBuildOperationType;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.ResolveContext;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.DependencyVerificationOverride;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.CompositeResolvedArtifactSet;
@@ -43,8 +43,8 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedFileDependencyResults;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResults;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResultsLoader;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransforms;
 import org.gradle.api.internal.artifacts.transform.VariantSelector;
+import org.gradle.api.internal.artifacts.transform.VariantSelectorFactory;
 import org.gradle.api.internal.artifacts.verification.exceptions.DependencyVerificationException;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.file.FileCollectionInternal;
@@ -77,12 +77,12 @@ public class DefaultLenientConfiguration implements LenientConfiguration, Visite
     private final static ResolveArtifactsBuildOperationType.Result RESULT = new ResolveArtifactsBuildOperationType.Result() {
     };
 
-    private final ConfigurationInternal configuration;
+    private final ResolveContext resolveContext;
     private final Set<UnresolvedDependency> unresolvedDependencies;
     private final VisitedArtifactsResults artifactResults;
     private final VisitedFileDependencyResults fileDependencyResults;
     private final TransientConfigurationResultsLoader transientConfigurationResultsFactory;
-    private final ArtifactTransforms artifactTransforms;
+    private final VariantSelectorFactory variantSelectorFactory;
     private final AttributeContainerInternal implicitAttributes;
     private final BuildOperationExecutor buildOperationExecutor;
     private final DependencyVerificationOverride dependencyVerificationOverride;
@@ -94,15 +94,15 @@ public class DefaultLenientConfiguration implements LenientConfiguration, Visite
     private SelectedArtifactResults artifactsForThisConfiguration;
     private DependencyVerificationException dependencyVerificationException;
 
-    public DefaultLenientConfiguration(ConfigurationInternal configuration, boolean selectFromAllVariants, Set<UnresolvedDependency> unresolvedDependencies, VisitedArtifactsResults artifactResults, VisitedFileDependencyResults fileDependencyResults, TransientConfigurationResultsLoader transientConfigurationResultsLoader, ArtifactTransforms artifactTransforms, BuildOperationExecutor buildOperationExecutor, DependencyVerificationOverride dependencyVerificationOverride, WorkerLeaseService workerLeaseService) {
-        this.configuration = configuration;
-        this.implicitAttributes = configuration.getAttributes().asImmutable();
+    public DefaultLenientConfiguration(ResolveContext resolveContext, boolean selectFromAllVariants, Set<UnresolvedDependency> unresolvedDependencies, VisitedArtifactsResults artifactResults, VisitedFileDependencyResults fileDependencyResults, TransientConfigurationResultsLoader transientConfigurationResultsLoader, VariantSelectorFactory variantSelectorFactory, BuildOperationExecutor buildOperationExecutor, DependencyVerificationOverride dependencyVerificationOverride, WorkerLeaseService workerLeaseService) {
+        this.resolveContext = resolveContext;
+        this.implicitAttributes = resolveContext.getAttributes().asImmutable();
         this.selectFromAllVariants = selectFromAllVariants;
         this.unresolvedDependencies = unresolvedDependencies;
         this.artifactResults = artifactResults;
         this.fileDependencyResults = fileDependencyResults;
         this.transientConfigurationResultsFactory = transientConfigurationResultsLoader;
-        this.artifactTransforms = artifactTransforms;
+        this.variantSelectorFactory = variantSelectorFactory;
         this.buildOperationExecutor = buildOperationExecutor;
         this.dependencyVerificationOverride = dependencyVerificationOverride;
         this.workerLeaseService = workerLeaseService;
@@ -110,7 +110,7 @@ public DefaultLenientConfiguration(ConfigurationInternal configuration, boolean
 
     private SelectedArtifactResults getSelectedArtifacts() {
         if (artifactsForThisConfiguration == null) {
-            artifactsForThisConfiguration = artifactResults.selectLenient(Specs.satisfyAll(), artifactTransforms.variantSelector(implicitAttributes, false, selectFromAllVariants, configuration.getDependenciesResolver()));
+            artifactsForThisConfiguration = artifactResults.selectLenient(Specs.satisfyAll(), variantSelectorFactory.create(implicitAttributes, false, selectFromAllVariants, resolveContext.getDependenciesResolverFactory()));
         }
         return artifactsForThisConfiguration;
     }
@@ -125,7 +125,7 @@ public SelectedArtifactSet select(final Spec<? super Dependency> dependencySpec)
 
     @Override
     public SelectedArtifactSet select(final Spec<? super Dependency> dependencySpec, final AttributeContainerInternal requestedAttributes, final Spec<? super ComponentIdentifier> componentSpec, boolean allowNoMatchingVariants, boolean selectFromAllVariants) {
-        VariantSelector selector = artifactTransforms.variantSelector(requestedAttributes, allowNoMatchingVariants, selectFromAllVariants, configuration.getDependenciesResolver());
+        VariantSelector selector = variantSelectorFactory.create(requestedAttributes, allowNoMatchingVariants, selectFromAllVariants, resolveContext.getDependenciesResolverFactory());
         SelectedArtifactResults artifactResults = this.artifactResults.selectLenient(componentSpec, selector);
 
         return new SelectedArtifactSet() {
@@ -159,7 +159,7 @@ private ResolveException getFailure() {
         for (UnresolvedDependency unresolvedDependency : unresolvedDependencies) {
             failures.add(unresolvedDependency.getProblem());
         }
-        return new ResolveException(configuration.getDisplayName(), failures);
+        return new ResolveException(resolveContext.getDisplayName(), failures);
     }
 
     public boolean hasError() {
@@ -256,7 +256,7 @@ public void run(BuildOperationContext context) {
                     throw dependencyVerificationException;
                 } else {
                     try {
-                        dependencyVerificationOverride.artifactsAccessed(configuration.getDisplayName());
+                        dependencyVerificationOverride.artifactsAccessed(resolveContext.getDisplayName());
                     } catch (DependencyVerificationException e) {
                         dependencyVerificationException = e;
                         throw e;
@@ -267,11 +267,12 @@ public void run(BuildOperationContext context) {
 
             @Override
             public BuildOperationDescriptor.Builder description() {
-                String displayName = "Resolve files of " + configuration.getIdentityPath();
+                String displayName = "Resolve files of " + resolveContext.getIdentityPath();
                 return BuildOperationDescriptor
                     .displayName(displayName)
                     .progressDisplayName(displayName)
-                    .details(new ResolveArtifactsDetails(configuration.getPath()));
+                    // TODO: Can we update this to use the identity path?
+                    .details(new ResolveArtifactsDetails(resolveContext.getProjectPath().toString()));
             }
         });
     }
@@ -321,8 +322,8 @@ private void visitArtifacts(Spec<? super Dependency> dependencySpec, SelectedArt
         ParallelResolveArtifactSet.wrap(CompositeResolvedArtifactSet.of(artifactSets), buildOperationExecutor).visit(visitor);
     }
 
-    public ConfigurationInternal getConfiguration() {
-        return configuration;
+    public ResolveContext getResolveContext() {
+        return resolveContext;
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
index d3b4124..c2a5466 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultResolvedConfiguration.java
@@ -61,7 +61,11 @@ public Set<File> getFiles(final Spec<? super Dependency> dependencySpec) throws
         configuration.select(dependencySpec).visitArtifacts(visitor, false);
         Collection<Throwable> failures = visitor.getFailures();
         if (!failures.isEmpty()) {
-            throw new DefaultLenientConfiguration.ArtifactResolveException("files", configuration.getConfiguration().getIdentityPath().toString(), configuration.getConfiguration().getDisplayName(), failures);
+            throw new DefaultLenientConfiguration.ArtifactResolveException(
+                "files",
+                configuration.getResolveContext().getIdentityPath().toString(),
+                configuration.getResolveContext().getDisplayName(), failures
+            );
         }
         return visitor.getFiles();
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingConfigurationResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingConfigurationResolver.java
index c721045..4c578e0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingConfigurationResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingConfigurationResolver.java
@@ -24,22 +24,18 @@
 import org.gradle.api.artifacts.ResolvedConfiguration;
 import org.gradle.api.artifacts.ResolvedDependency;
 import org.gradle.api.artifacts.UnresolvedDependency;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.result.DependencyResult;
 import org.gradle.api.artifacts.result.ResolutionResult;
 import org.gradle.api.artifacts.result.ResolvedComponentResult;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.ConfigurationResolver;
+import org.gradle.api.internal.artifacts.DefaultResolverResults;
 import org.gradle.api.internal.artifacts.ResolveContext;
+import org.gradle.api.internal.artifacts.ResolveExceptionContextualizer;
 import org.gradle.api.internal.artifacts.ResolverResults;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.SelectedArtifactSet;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactSet;
 import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
-import org.gradle.api.internal.attributes.AttributeContainerInternal;
+import org.gradle.api.internal.artifacts.result.ResolutionResultInternal;
 import org.gradle.api.internal.provider.DefaultProvider;
-import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.specs.Spec;
 
@@ -49,9 +45,11 @@
 
 public class ErrorHandlingConfigurationResolver implements ConfigurationResolver {
     private final ConfigurationResolver delegate;
+    private final ResolveExceptionContextualizer contextualizer;
 
-    public ErrorHandlingConfigurationResolver(ConfigurationResolver delegate) {
+    public ErrorHandlingConfigurationResolver(ConfigurationResolver delegate, ResolveExceptionContextualizer contextualizer) {
         this.delegate = delegate;
+        this.contextualizer = contextualizer;
     }
 
     @Override
@@ -60,68 +58,52 @@ public List<ResolutionAwareRepository> getRepositories() {
     }
 
     @Override
-    public void resolveBuildDependencies(ConfigurationInternal configuration, ResolverResults results) {
+    public ResolverResults resolveBuildDependencies(ResolveContext resolveContext) {
         try {
-            delegate.resolveBuildDependencies(configuration, results);
+            return delegate.resolveBuildDependencies(resolveContext);
         } catch (Exception e) {
-            results.failed(wrapException(e, configuration));
-            BrokenResolvedConfiguration broken = new BrokenResolvedConfiguration(e, configuration);
-            results.artifactsResolved(broken, broken);
+            return DefaultResolverResults.failed(e, contextualizer.contextualize(e, resolveContext));
         }
     }
 
     @Override
-    public void resolveGraph(ConfigurationInternal configuration, ResolverResults results) throws ResolveException {
+    public ResolverResults resolveGraph(ResolveContext resolveContext) throws ResolveException {
+        ResolverResults results;
         try {
-            delegate.resolveGraph(configuration, results);
+            results = delegate.resolveGraph(resolveContext);
         } catch (Exception e) {
-            results.failed(wrapException(e, configuration));
-            BrokenResolvedConfiguration broken = new BrokenResolvedConfiguration(e, configuration);
-            results.artifactsResolved(broken, broken);
-            return;
+            return DefaultResolverResults.failed(e, contextualizer.contextualize(e, resolveContext));
         }
 
-        ResolutionResult wrappedResult = new ErrorHandlingResolutionResult(results.getResolutionResult(), configuration);
-        results.graphResolved(wrappedResult, results.getResolvedLocalComponents(), results.getVisitedArtifacts());
+        return results.updateResolutionResult(result -> new ErrorHandlingResolutionResult(result, resolveContext, contextualizer));
     }
 
     @Override
-    public void resolveArtifacts(ConfigurationInternal configuration, ResolverResults results) throws ResolveException {
+    public ResolverResults resolveArtifacts(ResolveContext resolveContext, ResolverResults graphResults) throws ResolveException {
+        ResolverResults artifactResults;
         try {
-            delegate.resolveArtifacts(configuration, results);
+            artifactResults = delegate.resolveArtifacts(resolveContext, graphResults);
         } catch (Exception e) {
-            BrokenResolvedConfiguration broken = new BrokenResolvedConfiguration(e, configuration);
-            results.artifactsResolved(broken, broken);
-            return;
+            return DefaultResolverResults.failed(graphResults.getResolutionResult(), graphResults.getResolvedLocalComponents(), e, contextualizer.contextualize(e, resolveContext));
         }
 
-        ResolvedConfiguration wrappedConfiguration = new ErrorHandlingResolvedConfiguration(results.getResolvedConfiguration(), configuration);
-        results.artifactsResolved(wrappedConfiguration, results.getVisitedArtifacts());
-    }
-
-    static ResolveException wrapException(Throwable e, ResolveContext resolveContext) {
-        if (e instanceof ResolveException) {
-            ResolveException resolveException = (ResolveException) e;
-            return maybeAddHintToResolveException(resolveContext, resolveException);
-        }
-        return maybeAddHintToResolveException(resolveContext, new ResolveException(resolveContext.getDisplayName(), e));
-    }
-
-    private static ResolveException maybeAddHintToResolveException(ResolveContext resolveContext, ResolveException resolveException) {
-        if (resolveContext instanceof ConfigurationInternal) {
-            ConfigurationInternal config = (ConfigurationInternal) resolveContext;
-            return config.maybeAddContext(resolveException);
-        }
-        return resolveException;
+        ResolvedConfiguration wrappedConfiguration = new ErrorHandlingResolvedConfiguration(artifactResults.getResolvedConfiguration(), resolveContext, contextualizer);
+        return DefaultResolverResults.artifactsResolved(graphResults.getResolutionResult(), graphResults.getResolvedLocalComponents(), wrappedConfiguration, artifactResults.getVisitedArtifacts());
     }
 
     private static class ErrorHandlingLenientConfiguration implements LenientConfiguration {
         private final LenientConfiguration lenientConfiguration;
         private final ResolveContext resolveContext;
+        private final ResolveExceptionContextualizer contextualizer;
 
-        private ErrorHandlingLenientConfiguration(LenientConfiguration lenientConfiguration, ResolveContext resolveContext) {
+        private ErrorHandlingLenientConfiguration(
+            LenientConfiguration lenientConfiguration,
+            ResolveContext resolveContext,
+            ResolveExceptionContextualizer contextualizer
+        ) {
             this.lenientConfiguration = lenientConfiguration;
             this.resolveContext = resolveContext;
+            this.contextualizer = contextualizer;
         }
 
         @Override
@@ -129,7 +111,7 @@ public Set<ResolvedArtifact> getArtifacts() {
             try {
                 return lenientConfiguration.getArtifacts();
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -138,7 +120,7 @@ public Set<ResolvedArtifact> getArtifacts(Spec<? super Dependency> dependencySpe
             try {
                 return lenientConfiguration.getArtifacts(dependencySpec);
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -147,7 +129,7 @@ public Set<ResolvedDependency> getFirstLevelModuleDependencies() {
             try {
                 return lenientConfiguration.getFirstLevelModuleDependencies();
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -156,7 +138,7 @@ public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Depe
             try {
                 return lenientConfiguration.getFirstLevelModuleDependencies(dependencySpec);
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -165,7 +147,7 @@ public Set<ResolvedDependency> getAllModuleDependencies() {
             try {
                 return lenientConfiguration.getAllModuleDependencies();
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -174,7 +156,7 @@ public Set<UnresolvedDependency> getUnresolvedModuleDependencies() {
             try {
                 return lenientConfiguration.getUnresolvedModuleDependencies();
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -183,7 +165,7 @@ public Set<File> getFiles() {
             try {
                 return lenientConfiguration.getFiles();
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -192,18 +174,24 @@ public Set<File> getFiles(Spec<? super Dependency> dependencySpec) {
             try {
                 return lenientConfiguration.getFiles(dependencySpec);
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
     }
 
-    private static class ErrorHandlingResolutionResult implements ResolutionResult {
-        private final ResolutionResult resolutionResult;
+    private static class ErrorHandlingResolutionResult implements ResolutionResultInternal {
+        private final ResolutionResultInternal resolutionResult;
         private final ResolveContext resolveContext;
+        private final ResolveExceptionContextualizer contextualizer;
 
-        public ErrorHandlingResolutionResult(ResolutionResult resolutionResult, ResolveContext configuration) {
-            this.resolutionResult = resolutionResult;
+        public ErrorHandlingResolutionResult(
+            ResolutionResult resolutionResult,
+            ResolveContext configuration,
+            ResolveExceptionContextualizer contextualizer
+        ) {
+            this.resolutionResult = (ResolutionResultInternal) resolutionResult;
             this.resolveContext = configuration;
+            this.contextualizer = contextualizer;
         }
 
         @Override
@@ -211,7 +199,7 @@ public ResolvedComponentResult getRoot() {
             try {
                 return resolutionResult.getRoot();
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -221,6 +209,11 @@ public Provider<ResolvedComponentResult> getRootComponent() {
         }
 
         @Override
+        public Provider<Throwable> getNonFatalFailure() {
+            return resolutionResult.getNonFatalFailure().map(e -> contextualizer.contextualize(e, resolveContext));
+        }
+
+        @Override
         public void allDependencies(Action<? super DependencyResult> action) {
             resolutionResult.allDependencies(action);
         }
@@ -230,7 +223,7 @@ public Set<? extends DependencyResult> getAllDependencies() {
             try {
                 return resolutionResult.getAllDependencies();
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -245,7 +238,7 @@ public Set<ResolvedComponentResult> getAllComponents() {
             try {
                 return resolutionResult.getAllComponents();
             } catch (Exception e) {
-                throw wrapException(e, resolveContext);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -268,12 +261,17 @@ public AttributeContainer getRequestedAttributes() {
 
     private static class ErrorHandlingResolvedConfiguration implements ResolvedConfiguration {
         private final ResolvedConfiguration resolvedConfiguration;
-        private final ConfigurationInternal configuration;
+        private final ResolveContext resolveContext;
+        private final ResolveExceptionContextualizer contextualizer;
 
-        public ErrorHandlingResolvedConfiguration(ResolvedConfiguration resolvedConfiguration,
-                                                  ConfigurationInternal configuration) {
+        public ErrorHandlingResolvedConfiguration(
+            ResolvedConfiguration resolvedConfiguration,
+            ResolveContext resolveContext,
+            ResolveExceptionContextualizer contextualizer
+        ) {
             this.resolvedConfiguration = resolvedConfiguration;
-            this.configuration = configuration;
+            this.resolveContext = resolveContext;
+            this.contextualizer = contextualizer;
         }
 
         @Override
@@ -284,9 +282,9 @@ public boolean hasError() {
         @Override
         public LenientConfiguration getLenientConfiguration() {
             try {
-                return new ErrorHandlingLenientConfiguration(resolvedConfiguration.getLenientConfiguration(), configuration);
+                return new ErrorHandlingLenientConfiguration(resolvedConfiguration.getLenientConfiguration(), resolveContext, contextualizer);
             } catch (Exception e) {
-                throw wrapException(e, configuration);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -295,7 +293,7 @@ public void rethrowFailure() throws ResolveException {
             try {
                 resolvedConfiguration.rethrowFailure();
             } catch (Exception e) {
-                throw wrapException(e, configuration);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -304,7 +302,7 @@ public Set<File> getFiles() throws ResolveException {
             try {
                 return resolvedConfiguration.getFiles();
             } catch (ResolveException e) {
-                throw wrapException(e, configuration);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -313,7 +311,7 @@ public Set<File> getFiles(Spec<? super Dependency> dependencySpec) throws Resolv
             try {
                 return resolvedConfiguration.getFiles(dependencySpec);
             } catch (Exception e) {
-                throw wrapException(e, configuration);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -322,7 +320,7 @@ public Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveE
             try {
                 return resolvedConfiguration.getFirstLevelModuleDependencies();
             } catch (Exception e) {
-                throw wrapException(e, configuration);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -331,7 +329,7 @@ public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Depe
             try {
                 return resolvedConfiguration.getFirstLevelModuleDependencies(dependencySpec);
             } catch (Exception e) {
-                throw wrapException(e, configuration);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
 
@@ -340,74 +338,9 @@ public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
             try {
                 return resolvedConfiguration.getResolvedArtifacts();
             } catch (Exception e) {
-                throw wrapException(e, configuration);
+                throw contextualizer.contextualize(e, resolveContext);
             }
         }
     }
 
-    private static class BrokenResolvedConfiguration implements ResolvedConfiguration, VisitedArtifactSet, SelectedArtifactSet {
-        private final Throwable ex;
-        private final ConfigurationInternal configuration;
-
-        public BrokenResolvedConfiguration(Throwable ex, ConfigurationInternal configuration) {
-            this.ex = ex;
-            this.configuration = configuration;
-        }
-
-        @Override
-        public boolean hasError() {
-            return true;
-        }
-
-        @Override
-        public LenientConfiguration getLenientConfiguration() {
-            throw wrapException(ex, configuration);
-        }
-
-        @Override
-        public void rethrowFailure() throws ResolveException {
-            throw wrapException(ex, configuration);
-        }
-
-        @Override
-        public Set<File> getFiles() throws ResolveException {
-            throw wrapException(ex, configuration);
-        }
-
-        @Override
-        public Set<File> getFiles(Spec<? super Dependency> dependencySpec) throws ResolveException {
-            throw wrapException(ex, configuration);
-        }
-
-        @Override
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies() throws ResolveException {
-            throw wrapException(ex, configuration);
-        }
-
-        @Override
-        public Set<ResolvedDependency> getFirstLevelModuleDependencies(Spec<? super Dependency> dependencySpec) throws ResolveException {
-            throw wrapException(ex, configuration);
-        }
-
-        @Override
-        public Set<ResolvedArtifact> getResolvedArtifacts() throws ResolveException {
-            throw wrapException(ex, configuration);
-        }
-
-        @Override
-        public SelectedArtifactSet select(Spec<? super Dependency> dependencySpec, AttributeContainerInternal requestedAttributes, Spec<? super ComponentIdentifier> componentSpec, boolean allowNoMatchingVariant, boolean selectFromAllVariants) {
-            return this;
-        }
-
-        @Override
-        public void visitDependencies(TaskDependencyResolveContext context) {
-            context.visitFailure(ex);
-        }
-
-        @Override
-        public void visitArtifacts(ArtifactVisitor visitor, boolean continueOnSelectionFailure) {
-            visitor.visitFailure(ex);
-        }
-
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/InMemoryResolutionResultBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/InMemoryResolutionResultBuilder.java
index 34eca8f..09543dc 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/InMemoryResolutionResultBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/InMemoryResolutionResultBuilder.java
@@ -17,10 +17,12 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 import org.gradle.api.artifacts.result.ResolutionResult;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.ResolveContext;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphComponent;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphSelector;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphVisitor;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphVariant;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.RootGraphNode;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.DefaultResolutionResultBuilder;
 
@@ -28,7 +30,7 @@
  * Dependency graph visitor that will build a {@link ResolutionResult} eagerly.
  * It is designed to be used during resolution for build dependencies.
  *
- * @see DefaultConfigurationResolver#resolveBuildDependencies(ConfigurationInternal, org.gradle.api.internal.artifacts.ResolverResults)
+ * @see DefaultConfigurationResolver#resolveBuildDependencies(ResolveContext)
  */
 public class InMemoryResolutionResultBuilder implements DependencyGraphVisitor {
 
@@ -37,12 +39,19 @@ public class InMemoryResolutionResultBuilder implements DependencyGraphVisitor {
 
     @Override
     public void start(RootGraphNode root) {
-        resolutionResultBuilder.setRequestedAttributes(root.getMetadata().getAttributes());
+        resolutionResultBuilder.setRequestedAttributes(root.getResolveState().getAttributes());
     }
 
     @Override
     public void visitNode(DependencyGraphNode node) {
-        resolutionResultBuilder.visitComponent(node.getOwner());
+        DependencyGraphComponent component = node.getOwner();
+        resolutionResultBuilder.startVisitComponent(component.getResultId(), component.getSelectionReason());
+        resolutionResultBuilder.visitComponentDetails(component.getComponentId(), component.getModuleVersion(), component.getResolveState().getRepositoryId());
+        for (ResolvedGraphVariant variant : component.getSelectedVariants()) {
+            resolutionResultBuilder.visitSelectedVariant(variant.getNodeId(), variant.getResolveState().getVariantResult(null));
+        }
+        resolutionResultBuilder.visitComponentVariants(component.getResolveState().getAllSelectableVariantResults());
+        resolutionResultBuilder.endVisitComponent();
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ShortCircuitEmptyConfigurationResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ShortCircuitEmptyConfigurationResolver.java
index fa81c06..b23185e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ShortCircuitEmptyConfigurationResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ShortCircuitEmptyConfigurationResolver.java
@@ -27,11 +27,12 @@
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.result.ResolutionResult;
 import org.gradle.api.internal.artifacts.ConfigurationResolver;
+import org.gradle.api.internal.artifacts.DefaultResolverResults;
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
 import org.gradle.api.internal.artifacts.Module;
+import org.gradle.api.internal.artifacts.ResolveContext;
 import org.gradle.api.internal.artifacts.ResolverResults;
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyLockingProvider;
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyLockingState;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
@@ -69,48 +70,49 @@ public List<ResolutionAwareRepository> getRepositories() {
     }
 
     @Override
-    public void resolveBuildDependencies(ConfigurationInternal configuration, ResolverResults result) {
-        if (configuration.getAllDependencies().isEmpty()) {
-            emptyGraph(configuration, result, false);
+    public ResolverResults resolveBuildDependencies(ResolveContext resolveContext) {
+        if (!resolveContext.hasDependencies()) {
+            return emptyGraph(resolveContext, false);
         } else {
-            delegate.resolveBuildDependencies(configuration, result);
+            return delegate.resolveBuildDependencies(resolveContext);
         }
     }
 
     @Override
-    public void resolveGraph(ConfigurationInternal configuration, ResolverResults results) throws ResolveException {
-        if (configuration.getAllDependencies().isEmpty()) {
-            emptyGraph(configuration, results, true);
+    public ResolverResults resolveGraph(ResolveContext resolveContext) throws ResolveException {
+        if (!resolveContext.hasDependencies()) {
+            return emptyGraph(resolveContext, true);
         } else {
-            delegate.resolveGraph(configuration, results);
+            return delegate.resolveGraph(resolveContext);
         }
     }
 
-    private void emptyGraph(ConfigurationInternal configuration, ResolverResults results, boolean verifyLocking) {
-        if (verifyLocking && configuration.getResolutionStrategy().isDependencyLockingEnabled()) {
-            DependencyLockingProvider dependencyLockingProvider = configuration.getResolutionStrategy().getDependencyLockingProvider();
-            DependencyLockingState lockingState = dependencyLockingProvider.loadLockState(configuration.getName());
+    private ResolverResults emptyGraph(ResolveContext resolveContext, boolean verifyLocking) {
+        if (verifyLocking && resolveContext.getResolutionStrategy().isDependencyLockingEnabled()) {
+            DependencyLockingProvider dependencyLockingProvider = resolveContext.getResolutionStrategy().getDependencyLockingProvider();
+            DependencyLockingState lockingState = dependencyLockingProvider.loadLockState(resolveContext.getName());
             if (lockingState.mustValidateLockState() && !lockingState.getLockedDependencies().isEmpty()) {
                 // Invalid lock state, need to do a real resolution to gather locking failures
-                delegate.resolveGraph(configuration, results);
-                return;
+                return delegate.resolveGraph(resolveContext);
             }
-            dependencyLockingProvider.persistResolvedDependencies(configuration.getName(), Collections.emptySet(), Collections.emptySet());
+            dependencyLockingProvider.persistResolvedDependencies(resolveContext.getName(), Collections.emptySet(), Collections.emptySet());
         }
-        Module module = configuration.getModule();
-        ModuleVersionIdentifier id = moduleIdentifierFactory.moduleWithVersion(module);
+
+        Module module = resolveContext.getModule();
+        ModuleVersionIdentifier id = moduleIdentifierFactory.moduleWithVersion(module.getGroup(), module.getName(), module.getVersion());
         ComponentIdentifier componentIdentifier = componentIdentifierFactory.createComponentIdentifier(module);
-        ResolutionResult emptyResult = DefaultResolutionResultBuilder.empty(id, componentIdentifier, configuration.getAttributes());
+        ResolutionResult emptyResult = DefaultResolutionResultBuilder.empty(id, componentIdentifier, resolveContext.getAttributes());
         ResolvedLocalComponentsResult emptyProjectResult = new ResolvedLocalComponentsResultGraphVisitor(thisBuild);
-        results.graphResolved(emptyResult, emptyProjectResult, EmptyResults.INSTANCE);
+        return DefaultResolverResults.graphResolved(emptyResult, emptyProjectResult, EmptyResults.INSTANCE, null);
     }
 
     @Override
-    public void resolveArtifacts(ConfigurationInternal configuration, ResolverResults results) throws ResolveException {
-        if (configuration.getAllDependencies().isEmpty() && results.getVisitedArtifacts() == EmptyResults.INSTANCE) {
-            results.artifactsResolved(new EmptyResolvedConfiguration(), EmptyResults.INSTANCE);
+    public ResolverResults resolveArtifacts(ResolveContext resolveContext, ResolverResults graphResults) throws ResolveException {
+        if (!resolveContext.hasDependencies() && graphResults.getVisitedArtifacts() == EmptyResults.INSTANCE) {
+            return DefaultResolverResults.artifactsResolved(graphResults.getResolutionResult(), graphResults.getResolvedLocalComponents(), new EmptyResolvedConfiguration(), EmptyResults.INSTANCE);
         } else {
-            delegate.resolveArtifacts(configuration, results);
+            assert graphResults.getArtifactResolveState() != null;
+            return delegate.resolveArtifacts(resolveContext, graphResults);
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java
index 0555411..84c689d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/clientmodule/ClientModuleResolver.java
@@ -16,7 +16,6 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.clientmodule;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -27,11 +26,13 @@
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyMetadataFactory;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.component.external.model.DefaultConfigurationMetadata;
+import org.gradle.internal.component.external.model.ExternalComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveStateFactory;
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadataWrapper;
@@ -40,8 +41,6 @@
 import org.gradle.internal.component.local.model.DslOriginDependencyMetadata;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
-import org.gradle.internal.component.model.DefaultComponentGraphResolveState;
 import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
 import org.gradle.internal.component.model.ModuleConfigurationMetadata;
 import org.gradle.internal.component.model.ModuleSources;
@@ -51,16 +50,19 @@
 
 import javax.annotation.Nullable;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 @NonNullApi
 public class ClientModuleResolver implements ComponentMetaDataResolver {
     private final ComponentMetaDataResolver resolver;
-    private final DependencyDescriptorFactory dependencyDescriptorFactory;
+    private final DependencyMetadataFactory dependencyMetadataFactory;
+    private final ModuleComponentGraphResolveStateFactory resolveStateFactory;
 
-    public ClientModuleResolver(ComponentMetaDataResolver resolver, DependencyDescriptorFactory dependencyDescriptorFactory) {
+    public ClientModuleResolver(ComponentMetaDataResolver resolver, DependencyMetadataFactory dependencyMetadataFactory, ModuleComponentGraphResolveStateFactory resolveStateFactory) {
         this.resolver = resolver;
-        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
+        this.dependencyMetadataFactory = dependencyMetadataFactory;
+        this.resolveStateFactory = resolveStateFactory;
     }
 
     @Override
@@ -77,7 +79,7 @@ public void resolve(ComponentIdentifier identifier, ComponentOverrideMetadata co
             ModuleComponentArtifactMetadata clientModuleArtifact = createClientModuleArtifact(originalMetadata);
             ClientModuleComponentResolveMetadata clientModuleMetaData = new ClientModuleComponentResolveMetadata(originalMetadata, clientModuleArtifact, clientModuleDependencies);
 
-            result.setResult(new DefaultComponentGraphResolveState<>(clientModuleMetaData, clientModuleMetaData));
+            result.setResult(resolveStateFactory.stateFor(clientModuleMetaData, clientModuleMetaData));
         }
     }
 
@@ -100,14 +102,14 @@ private ModuleComponentArtifactMetadata createClientModuleArtifact(ModuleCompone
     }
 
     private ModuleDependencyMetadata createDependencyMetadata(ComponentIdentifier identifier, ModuleDependency moduleDependency) {
-        LocalOriginDependencyMetadata dependencyMetadata = dependencyDescriptorFactory.createDependencyDescriptor(identifier, moduleDependency.getTargetConfiguration(), null, moduleDependency);
+        LocalOriginDependencyMetadata dependencyMetadata = dependencyMetadataFactory.createDependencyMetadata(identifier, moduleDependency.getTargetConfiguration(), null, moduleDependency);
         if (dependencyMetadata instanceof DslOriginDependencyMetadata) {
             return new ClientModuleDependencyMetadataWrapper((DslOriginDependencyMetadata) dependencyMetadata);
         }
         return new ModuleDependencyMetadataWrapper(dependencyMetadata);
     }
 
-    private static class ClientModuleComponentResolveMetadata implements ComponentResolveMetadata, ComponentGraphResolveMetadata {
+    private static class ClientModuleComponentResolveMetadata implements ExternalComponentResolveMetadata, ComponentGraphResolveMetadata {
         private final ModuleComponentResolveMetadata delegate;
         private final ModuleComponentArtifactMetadata clientModuleArtifact;
         private final List<ModuleDependencyMetadata> clientModuleDependencies;
@@ -134,11 +136,6 @@ public ModuleSources getSources() {
         }
 
         @Override
-        public ComponentResolveMetadata withSources(ModuleSources sources) {
-            return delegate.withSources(sources);
-        }
-
-        @Override
         public AttributesSchemaInternal getAttributesSchema() {
             return delegate.getAttributesSchema();
         }
@@ -156,7 +153,7 @@ public ModuleConfigurationMetadata getConfiguration(String name) {
 
         @Override
         public Optional<List<? extends VariantGraphResolveMetadata>> getVariantsForGraphTraversal() {
-            return Optional.absent();
+            return Optional.empty();
         }
 
         @Override
@@ -209,10 +206,5 @@ private ClientModuleDependencyMetadataWrapper(DslOriginDependencyMetadata delega
         public Dependency getSource() {
             return delegate.getSource();
         }
-
-        @Override
-        public String getReason() {
-            return delegate.getReason();
-        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/CachingDependencySubstitutionApplicator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/CachingDependencySubstitutionApplicator.java
index aa27e40..52b73c2 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/CachingDependencySubstitutionApplicator.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/CachingDependencySubstitutionApplicator.java
@@ -24,7 +24,7 @@
 import java.util.Map;
 
 /**
- * This caching substitutor is not intended to be called from places where it can be executed concurrently.
+ * This caching applicator is not intended to be called from places where it can be executed concurrently.
  */
 @NotThreadSafe
 public class CachingDependencySubstitutionApplicator implements DependencySubstitutionApplicator {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DefaultDependencySubstitutionApplicator.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DefaultDependencySubstitutionApplicator.java
index 59a0c44..b6ef1e8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DefaultDependencySubstitutionApplicator.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DefaultDependencySubstitutionApplicator.java
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution;
 
 import org.gradle.api.Action;
-import org.gradle.api.artifacts.DependencySubstitution;
 import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorFactory;
 import org.gradle.internal.component.model.DependencyMetadata;
@@ -24,10 +23,10 @@
 
 public class DefaultDependencySubstitutionApplicator implements DependencySubstitutionApplicator {
     private final ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory;
-    private final Action<DependencySubstitution> rule;
+    private final Action<? super DependencySubstitutionInternal> rule;
     private final Instantiator instantiator;
 
-    public DefaultDependencySubstitutionApplicator(ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory, Action<DependencySubstitution> rule, Instantiator instantiator) {
+    public DefaultDependencySubstitutionApplicator(ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory, Action<? super DependencySubstitutionInternal> rule, Instantiator instantiator) {
         this.componentSelectionDescriptorFactory = componentSelectionDescriptorFactory;
         this.rule = rule;
         this.instantiator = instantiator;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DefaultDependencySubstitutions.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DefaultDependencySubstitutions.java
index dc1b88f..89cc2ca 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DefaultDependencySubstitutions.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DefaultDependencySubstitutions.java
@@ -46,6 +46,7 @@
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.internal.Actions;
 import org.gradle.internal.Describables;
+import org.gradle.internal.ImmutableActionSet;
 import org.gradle.internal.build.IncludedBuildState;
 import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
 import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
@@ -59,13 +60,10 @@
 import org.gradle.util.Path;
 
 import javax.inject.Inject;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.function.Supplier;
 
 public class DefaultDependencySubstitutions implements DependencySubstitutionsInternal {
-    private final Set<Action<? super DependencySubstitution>> substitutionRules;
     private final NotationParser<Object, ComponentSelector> moduleSelectorNotationParser;
     private final NotationParser<Object, ComponentSelector> projectSelectorNotationParser;
     private final ComponentSelectionDescriptor reason;
@@ -75,6 +73,7 @@ public class DefaultDependencySubstitutions implements DependencySubstitutionsIn
     private final NotationParser<Object, Capability> capabilityNotationParser;
 
     private MutationValidator mutationValidator = MutationValidator.IGNORE;
+    private ImmutableActionSet<DependencySubstitution> substitutionRules;
     private boolean rulesMayAddProjectDependency;
 
     public static DefaultDependencySubstitutions forResolutionStrategy(ComponentIdentifierFactory componentIdentifierFactory,
@@ -118,11 +117,11 @@ public DefaultDependencySubstitutions(ComponentSelectionDescriptor reason,
                                           ObjectFactory objectFactory,
                                           ImmutableAttributesFactory attributesFactory,
                                           NotationParser<Object, Capability> capabilityNotationParser) {
-        this(reason, new LinkedHashSet<>(), moduleSelectorNotationParser, projectSelectorNotationParser, instantiator, objectFactory, attributesFactory, capabilityNotationParser);
+        this(reason, ImmutableActionSet.empty(), moduleSelectorNotationParser, projectSelectorNotationParser, instantiator, objectFactory, attributesFactory, capabilityNotationParser);
     }
 
     private DefaultDependencySubstitutions(ComponentSelectionDescriptor reason,
-                                           Set<Action<? super DependencySubstitution>> substitutionRules,
+                                           ImmutableActionSet<DependencySubstitution> substitutionRules,
                                            NotationParser<Object, ComponentSelector> moduleSelectorNotationParser,
                                            NotationParser<Object, ComponentSelector> projectSelectorNotationParser,
                                            Instantiator instantiator,
@@ -140,13 +139,19 @@ private DefaultDependencySubstitutions(ComponentSelectionDescriptor reason,
     }
 
     @Override
+    public void discard() {
+        substitutionRules = ImmutableActionSet.empty();
+        rulesMayAddProjectDependency = false;
+    }
+
+    @Override
     public boolean rulesMayAddProjectDependency() {
         return rulesMayAddProjectDependency;
     }
 
     @Override
     public Action<DependencySubstitution> getRuleAction() {
-        return Actions.composite(substitutionRules);
+        return substitutionRules;
     }
 
     protected void addSubstitution(Action<? super DependencySubstitution> rule, boolean projectInvolved) {
@@ -158,7 +163,7 @@ protected void addSubstitution(Action<? super DependencySubstitution> rule, bool
 
     private void addRule(Action<? super DependencySubstitution> rule) {
         mutationValidator.validateMutation(MutationValidator.MutationType.STRATEGY);
-        substitutionRules.add(rule);
+        substitutionRules = substitutionRules.add(rule);
     }
 
     @Override
@@ -236,11 +241,8 @@ public Substitution withoutArtifactSelectors() {
             public Substitution using(ComponentSelector notation) {
                 DefaultDependencySubstitution.validateTarget(notation);
 
-                boolean projectInvolved = false;
-                if (substituted instanceof ProjectComponentSelector || notation instanceof ProjectComponentSelector) {
-                    // A project is involved, need to be aware of it
-                    projectInvolved = true;
-                }
+                // A project is involved, need to be aware of it
+                boolean projectInvolved = substituted instanceof ProjectComponentSelector || notation instanceof ProjectComponentSelector;
 
                 if (substituted instanceof UnversionedModuleComponentSelector) {
                     final ModuleIdentifier moduleId = ((UnversionedModuleComponentSelector) substituted).getModuleIdentifier();
@@ -268,7 +270,7 @@ public void setMutationValidator(MutationValidator validator) {
     public DependencySubstitutionsInternal copy() {
         return new DefaultDependencySubstitutions(
             reason,
-            new LinkedHashSet<>(substitutionRules),
+            substitutionRules,
             moduleSelectorNotationParser,
             projectSelectorNotationParser,
             instantiator,
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DependencySubstitutionsInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DependencySubstitutionsInternal.java
index 864404f..3864aae 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DependencySubstitutionsInternal.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/dependencysubstitution/DependencySubstitutionsInternal.java
@@ -28,4 +28,9 @@ public interface DependencySubstitutionsInternal extends DependencySubstitutions
     void setMutationValidator(MutationValidator validator);
 
     DependencySubstitutionsInternal copy();
+
+    /**
+     * Discard all state held by this container.
+     */
+    void discard();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepository.java
index 7e43e36..3bf3965 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepository.java
@@ -22,18 +22,18 @@
 
 import java.util.Map;
 
-public class BaseModuleComponentRepository implements ModuleComponentRepository {
-    protected final ModuleComponentRepository delegate;
-    private final ModuleComponentRepositoryAccess localAccess;
-    private final ModuleComponentRepositoryAccess remoteAccess;
+public class BaseModuleComponentRepository<T> implements ModuleComponentRepository<T> {
+    protected final ModuleComponentRepository<T> delegate;
+    private final ModuleComponentRepositoryAccess<T> localAccess;
+    private final ModuleComponentRepositoryAccess<T> remoteAccess;
 
-    public BaseModuleComponentRepository(ModuleComponentRepository delegate, ModuleComponentRepositoryAccess localAccess, ModuleComponentRepositoryAccess remoteAccess) {
+    public BaseModuleComponentRepository(ModuleComponentRepository<T> delegate, ModuleComponentRepositoryAccess<T> localAccess, ModuleComponentRepositoryAccess<T> remoteAccess) {
         this.delegate = delegate;
         this.localAccess = localAccess;
         this.remoteAccess = remoteAccess;
     }
 
-    public BaseModuleComponentRepository(ModuleComponentRepository delegate) {
+    public BaseModuleComponentRepository(ModuleComponentRepository<T> delegate) {
         this.delegate = delegate;
         this.localAccess = delegate.getLocalAccess();
         this.remoteAccess = delegate.getRemoteAccess();
@@ -55,12 +55,12 @@ public String getName() {
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<T> getLocalAccess() {
         return localAccess;
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<T> getRemoteAccess() {
         return remoteAccess;
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryAccess.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryAccess.java
index 98c74d8..e641342 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryAccess.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/BaseModuleComponentRepositoryAccess.java
@@ -28,14 +28,14 @@
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
 import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
 
-public class BaseModuleComponentRepositoryAccess implements ModuleComponentRepositoryAccess {
-    private final ModuleComponentRepositoryAccess delegate;
+public class BaseModuleComponentRepositoryAccess<T> implements ModuleComponentRepositoryAccess<T> {
+    private final ModuleComponentRepositoryAccess<T> delegate;
 
-    public BaseModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess delegate) {
+    public BaseModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess<T> delegate) {
         this.delegate = delegate;
     }
 
-    public ModuleComponentRepositoryAccess getDelegate() {
+    public ModuleComponentRepositoryAccess<T> getDelegate() {
         return delegate;
     }
 
@@ -45,7 +45,7 @@ public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableMod
     }
 
     @Override
-    public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+    public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<T> result) {
         delegate.resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result);
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachedMetadataProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachedMetadataProvider.java
index 857e856..d2065e9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachedMetadataProvider.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachedMetadataProvider.java
@@ -20,20 +20,21 @@
 import org.gradle.api.artifacts.ivy.IvyModuleDescriptor;
 import org.gradle.api.internal.artifacts.ivyservice.DefaultIvyModuleDescriptor;
 import org.gradle.api.internal.artifacts.repositories.resolver.ComponentMetadataAdapter;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
 
 class CachedMetadataProvider implements MetadataProvider {
-    private final BuildableModuleComponentMetaDataResolveResult cachedResult;
+    private final BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> cachedResult;
     private final ComponentMetadata cachedComponentMetadata;
     private final boolean usable;
 
-    CachedMetadataProvider(BuildableModuleComponentMetaDataResolveResult result) {
+    CachedMetadataProvider(BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
         cachedResult = result;
         usable = cachedResult.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved;
         if (usable) {
-            cachedComponentMetadata = new ComponentMetadataAdapter(cachedResult.getMetaData());
+            cachedComponentMetadata = new ComponentMetadataAdapter(cachedResult.getMetaData().getModuleResolveMetadata());
         } else {
             cachedComponentMetadata = null;
         }
@@ -46,7 +47,7 @@ public ComponentMetadata getComponentMetadata() {
 
     @Override
     public IvyModuleDescriptor getIvyModuleDescriptor() {
-        ModuleComponentResolveMetadata metaData = cachedResult.getMetaData();
+        ModuleComponentResolveMetadata metaData = cachedResult.getMetaData().getModuleResolveMetadata();
         if (metaData instanceof IvyModuleResolveMetadata) {
             IvyModuleResolveMetadata ivyMetadata = (IvyModuleResolveMetadata) metaData;
             return new DefaultIvyModuleDescriptor(ivyMetadata.getExtraAttributes(), ivyMetadata.getBranch(), ivyMetadata.getStatus());
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java
index 9298f2d..b8f6b84 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepository.java
@@ -37,13 +37,17 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
 import org.gradle.api.internal.artifacts.repositories.resolver.MetadataFetchingCost;
 import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.cache.internal.ProducerGuard;
 import org.gradle.internal.action.InstantiatingAction;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveStateFactory;
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
 import org.gradle.internal.component.model.ComponentResolveMetadata;
+import org.gradle.internal.component.model.ImmutableModuleSources;
 import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.component.model.MutableModuleSources;
 import org.gradle.internal.hash.HashCode;
@@ -53,6 +57,7 @@
 import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
 import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
+import org.gradle.internal.resolve.result.DefaultBuildableModuleComponentMetaDataResolveResult;
 import org.gradle.util.internal.BuildCommencedTimeProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -72,7 +77,7 @@
  * The `ResolveAndCacheRepositoryAccess` provided by {@link #getRemoteAccess()} will first delegate any resolution request,
  * and then store the result in the dependency resolution cache.
  */
-public class CachingModuleComponentRepository implements ModuleComponentRepository {
+public class CachingModuleComponentRepository implements ModuleComponentRepository<ModuleComponentGraphResolveState> {
     private static final Logger LOGGER = LoggerFactory.getLogger(CachingModuleComponentRepository.class);
 
     private final ModuleVersionsCache moduleVersionsCache;
@@ -80,17 +85,20 @@ public class CachingModuleComponentRepository implements ModuleComponentReposito
     private final ModuleArtifactsCache moduleArtifactsCache;
     private final ModuleArtifactCache moduleArtifactCache;
 
-    private final ModuleComponentRepository delegate;
+    private final ModuleComponentRepository<ModuleComponentResolveMetadata> delegate;
+    private final ModuleComponentGraphResolveStateFactory resolveStateFactory;
     private final CachePolicy cachePolicy;
     private final BuildCommencedTimeProvider timeProvider;
     private final ComponentMetadataProcessor metadataProcessor;
     private final ChangingValueDependencyResolutionListener listener;
     private final LocateInCacheRepositoryAccess locateInCacheRepositoryAccess = new LocateInCacheRepositoryAccess();
     private final ResolveAndCacheRepositoryAccess resolveAndCacheRepositoryAccess = new ResolveAndCacheRepositoryAccess();
+    private final ProducerGuard<ModuleComponentIdentifier> metadataGuard = ProducerGuard.adaptive();
 
     public CachingModuleComponentRepository(
-        ModuleComponentRepository delegate,
+        ModuleComponentRepository<ModuleComponentResolveMetadata> delegate,
         ModuleRepositoryCaches caches,
+        ModuleComponentGraphResolveStateFactory resolveStateFactory,
         CachePolicy cachePolicy,
         BuildCommencedTimeProvider timeProvider,
         ComponentMetadataProcessor metadataProcessor,
@@ -101,6 +109,7 @@ public CachingModuleComponentRepository(
         this.moduleVersionsCache = caches.moduleVersionsCache;
         this.moduleArtifactsCache = caches.moduleArtifactsCache;
         this.moduleArtifactCache = caches.moduleArtifactCache;
+        this.resolveStateFactory = resolveStateFactory;
         this.cachePolicy = cachePolicy;
         this.timeProvider = timeProvider;
         this.metadataProcessor = metadataProcessor;
@@ -123,12 +132,12 @@ public String toString() {
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> getLocalAccess() {
         return locateInCacheRepositoryAccess;
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> getRemoteAccess() {
         return resolveAndCacheRepositoryAccess;
     }
 
@@ -142,15 +151,15 @@ public InstantiatingAction<ComponentMetadataSupplierDetails> getComponentMetadat
         return delegate.getComponentMetadataSupplier();
     }
 
-    private class LocateInCacheRepositoryAccess implements ModuleComponentRepositoryAccess {
+    private class LocateInCacheRepositoryAccess implements ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> {
         @Override
         public String toString() {
-            return "cache lookup for " + delegate.toString();
+            return "cache lookup for " + delegate;
         }
 
         @Override
         public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableModuleVersionListingResolveResult result) {
-            // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
+            // First try to determine the versions in-memory: don't use the cache in this case
             delegate.getLocalAccess().listModuleVersions(dependency, result);
             if (result.hasResult()) {
                 return;
@@ -183,17 +192,22 @@ private void listModuleVersionsFromCache(ModuleDependencyMetadata dependency, Bu
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
-            // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
-            delegate.getLocalAccess().resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result);
-            if (result.hasResult()) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
+            // First try to determine the metadata in-memory: don't use the cache in this case
+            DefaultBuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> localResult = new DefaultBuildableModuleComponentMetaDataResolveResult<>();
+            delegate.getLocalAccess().resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, localResult);
+            if (localResult.hasResult()) {
+                localResult.applyTo(result, resolveStateFactory::stateFor);
                 return;
             }
 
-            resolveComponentMetaDataFromCache(moduleComponentIdentifier, requestMetaData, result);
+            metadataGuard.guardByKey(moduleComponentIdentifier, () -> {
+                resolveComponentMetaDataFromCache(moduleComponentIdentifier, requestMetaData, result);
+                return null;
+            });
         }
 
-        private void resolveComponentMetaDataFromCache(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        private void resolveComponentMetaDataFromCache(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
             ModuleMetadataCache.CachedMetadata cachedMetadata = moduleMetadataCache.getCachedModuleDescriptor(delegate, moduleComponentIdentifier);
             if (cachedMetadata == null) {
                 return;
@@ -209,8 +223,8 @@ private void resolveComponentMetaDataFromCache(ModuleComponentIdentifier moduleC
                 result.setAuthoritative(cachedMetadata.getAge().toMillis() == 0);
                 return;
             }
-            ModuleComponentResolveMetadata metadata = getProcessedMetadata(metadataProcessor.getRulesHash(), cachedMetadata);
-            if (requestMetaData.isChanging() || metadata.isChanging()) {
+            ModuleComponentGraphResolveState state = getProcessedMetadata(metadataProcessor.getRulesHash(), cachedMetadata);
+            if (requestMetaData.isChanging() || state.getMetadata().isChanging()) {
                 Expiry expiry = cachePolicy.changingModuleExpiry(moduleComponentIdentifier, cachedMetadata.getModuleVersion(), cachedMetadata.getAge());
                 if (expiry.isMustCheck()) {
                     LOGGER.debug("Cached meta-data for changing module is expired: will perform fresh resolve of '{}' in '{}'", moduleComponentIdentifier, delegate.getName());
@@ -226,24 +240,25 @@ private void resolveComponentMetaDataFromCache(ModuleComponentIdentifier moduleC
             }
 
             LOGGER.debug("Using cached module metadata for module '{}' in '{}'", moduleComponentIdentifier, delegate.getName());
-            result.resolved(metadata);
+            result.resolved(state);
             // When age == 0, verified since the start of this build, assume the meta-data hasn't changed
             result.setAuthoritative(cachedMetadata.getAge().toMillis() == 0);
         }
 
-        private ModuleComponentResolveMetadata getProcessedMetadata(int key, ModuleMetadataCache.CachedMetadata cachedMetadata) {
-            ModuleComponentResolveMetadata metadata = cachedMetadata.getProcessedMetadata(key);
-            if (metadata == null) {
-                metadata = metadataProcessor.processMetadata(cachedMetadata.getMetadata());
+        private ModuleComponentGraphResolveState getProcessedMetadata(int key, ModuleMetadataCache.CachedMetadata cachedMetadata) {
+            ModuleComponentGraphResolveState state = cachedMetadata.getProcessedMetadata(key);
+            if (state == null) {
+                ModuleComponentResolveMetadata metadata = metadataProcessor.processMetadata(cachedMetadata.getMetadata());
+                metadata = attachRepositorySource(metadata);
+                state = resolveStateFactory.stateFor(metadata);
                 // Save the processed metadata for next time.
-                cachedMetadata.putProcessedMetadata(key, metadata);
+                cachedMetadata.putProcessedMetadata(key, state);
             }
-            return metadata;
+            return state;
         }
 
         @Override
         public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
-
             // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
             delegate.getLocalAccess().resolveArtifactsWithType(component, artifactType, result);
             if (result.hasResult()) {
@@ -272,7 +287,7 @@ private void resolveModuleArtifactsFromCache(String contextId, ComponentResolveM
 
         @Override
         public void resolveArtifact(ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactFileResolveResult result) {
-            // First try to determine the artifacts in-memory (e.g using the metadata): don't use the cache in this case
+            // First try to resolve the artifact in-memory (e.g using the metadata): don't use the cache in this case
             delegate.getLocalAccess().resolveArtifact(artifact, moduleSources, result);
             if (result.hasResult()) {
                 return;
@@ -293,8 +308,8 @@ public MetadataFetchingCost estimateMetadataFetchingCost(ModuleComponentIdentifi
                 }
                 return MetadataFetchingCost.CHEAP;
             }
-            ModuleComponentResolveMetadata metaData = getProcessedMetadata(metadataProcessor.getRulesHash(), cachedMetadata);
-            if (metaData.isChanging()) {
+            ModuleComponentGraphResolveState state = getProcessedMetadata(metadataProcessor.getRulesHash(), cachedMetadata);
+            if (state.getMetadata().isChanging()) {
                 if (cachePolicy.changingModuleExpiry(moduleComponentIdentifier, cachedMetadata.getModuleVersion(), cachedMetadata.getAge()).isMustCheck()) {
                     return estimateCostViaRemoteAccess(moduleComponentIdentifier);
                 }
@@ -345,10 +360,10 @@ private ModuleDescriptorHashModuleSource findCachingModuleSource(ModuleSources s
             .orElseThrow(() -> new RuntimeException("Cannot find expected module source " + ModuleDescriptorHashModuleSource.class.getSimpleName() + " in " + sources));
     }
 
-    private class ResolveAndCacheRepositoryAccess implements ModuleComponentRepositoryAccess {
+    private class ResolveAndCacheRepositoryAccess implements ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> {
         @Override
         public String toString() {
-            return "cache > " + delegate.getRemoteAccess().toString();
+            return "cache > " + delegate.getRemoteAccess();
         }
 
         @Override
@@ -377,38 +392,57 @@ public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableMod
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
+            metadataGuard.guardByKey(moduleComponentIdentifier, () -> {
+                resolveComponentMetaDataAndCache(moduleComponentIdentifier, requestMetaData, result);
+                return null;
+            });
+        }
+
+        private void resolveComponentMetaDataAndCache(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
             ComponentOverrideMetadata forced = requestMetaData.withChanging();
-            delegate.getRemoteAccess().resolveComponentMetaData(moduleComponentIdentifier, forced, result);
-            switch (result.getState()) {
+            DefaultBuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> localResult = new DefaultBuildableModuleComponentMetaDataResolveResult<>();
+            delegate.getRemoteAccess().resolveComponentMetaData(moduleComponentIdentifier, forced, localResult);
+            switch (localResult.getState()) {
                 case Missing:
                     moduleMetadataCache.cacheMissing(delegate, moduleComponentIdentifier);
+                    localResult.applyTo(result, metadata -> {
+                        // Should not be called
+                        throw new IllegalStateException();
+                    });
                     break;
                 case Resolved:
-                    ModuleComponentResolveMetadata resolvedMetadata = result.getMetaData();
+                    ModuleComponentResolveMetadata resolvedMetadata = localResult.getMetaData();
                     ModuleMetadataCache.CachedMetadata cachedMetadata = moduleMetadataCache.cacheMetaData(delegate, moduleComponentIdentifier, resolvedMetadata);
                     // Starting here we're going to process the component metadata rules
                     // Therefore metadata can be mutated, and will _not_ be stored in the module metadata cache
                     // but will be in the _in memory_ cache
                     ModuleComponentResolveMetadata processedMetadata = metadataProcessor.processMetadata(resolvedMetadata);
+                    processedMetadata = attachRepositorySource(processedMetadata);
                     if (processedMetadata.isChanging() || requestMetaData.isChanging()) {
-                        processedMetadata = makeChanging(resolvedMetadata, processedMetadata);
+                        processedMetadata = makeChanging(processedMetadata);
                         Expiry expiry = cachePolicy.changingModuleExpiry(moduleComponentIdentifier, cachedMetadata.getModuleVersion(), Duration.ZERO);
                         listener.onChangingModuleResolve(moduleComponentIdentifier, expiry);
                     }
-                    cachedMetadata.putProcessedMetadata(metadataProcessor.getRulesHash(), processedMetadata);
-                    result.resolved(processedMetadata);
+                    ModuleComponentGraphResolveState state = resolveStateFactory.stateFor(processedMetadata);
+                    cachedMetadata.putProcessedMetadata(metadataProcessor.getRulesHash(), state);
+                    localResult.applyTo(result);
+                    result.resolved(state);
                     break;
                 case Failed:
+                    localResult.applyTo(result, metadata -> {
+                        // should not be called
+                        throw new IllegalStateException();
+                    });
                     break;
                 default:
                     throw new IllegalStateException("Unexpected resolve state: " + result.getState());
             }
         }
 
-        private ModuleComponentResolveMetadata makeChanging(ModuleComponentResolveMetadata resolvedMetadata, ModuleComponentResolveMetadata processedMetadata) {
+        private ModuleComponentResolveMetadata makeChanging(ModuleComponentResolveMetadata processedMetadata) {
             MutableModuleSources sources = new MutableModuleSources();
-            resolvedMetadata.getSources().withSources(src -> {
+            processedMetadata.getSources().withSources(src -> {
                 if (src instanceof ModuleDescriptorHashModuleSource) {
                     ModuleDescriptorHashModuleSource changingSource = new ModuleDescriptorHashModuleSource(
                         ((ModuleDescriptorHashModuleSource) src).getDescriptorHash(),
@@ -419,8 +453,7 @@ private ModuleComponentResolveMetadata makeChanging(ModuleComponentResolveMetada
                     sources.add(src);
                 }
             });
-            processedMetadata = processedMetadata.withSources(sources);
-            return processedMetadata;
+            return processedMetadata.withSources(sources);
         }
 
         @Override
@@ -454,8 +487,12 @@ public MetadataFetchingCost estimateMetadataFetchingCost(ModuleComponentIdentifi
         }
     }
 
-    private ModuleDescriptorHashModuleSource findCachingModuleSource(ComponentResolveMetadata component) {
-        return findCachingModuleSource(component.getSources());
+    private ModuleComponentResolveMetadata attachRepositorySource(ModuleComponentResolveMetadata processedMetadata) {
+        RepositoryChainModuleSource moduleSource = new RepositoryChainModuleSource(delegate);
+        ModuleSources originSources = processedMetadata.getSources();
+        ImmutableModuleSources mergedSources = ImmutableModuleSources.of(originSources, moduleSource);
+        processedMetadata = processedMetadata.withSources(mergedSources);
+        return processedMetadata;
     }
 
     private String cacheKey(ArtifactType artifactType) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentMetaDataResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentMetaDataResolveState.java
index 307754d..30a0df2 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentMetaDataResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ComponentMetaDataResolveState.java
@@ -17,6 +17,7 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
 import org.gradle.internal.resolve.RejectedByRuleVersion;
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
@@ -24,25 +25,25 @@
 import org.gradle.internal.resolve.result.ResourceAwareResolveResult;
 
 class ComponentMetaDataResolveState {
-    private final DefaultBuildableModuleComponentMetaDataResolveResult resolveResult;
+    private final DefaultBuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> resolveResult;
     private final VersionedComponentChooser versionedComponentChooser;
     private final ComponentOverrideMetadata componentOverrideMetadata;
     private final ModuleComponentIdentifier componentIdentifier;
 
-    final ModuleComponentRepository repository;
+    final ModuleComponentRepository<ModuleComponentGraphResolveState> repository;
 
     private boolean searchedLocally;
     private boolean searchedRemotely;
 
-    public ComponentMetaDataResolveState(ModuleComponentIdentifier componentIdentifier, ComponentOverrideMetadata componentOverrideMetadata, ModuleComponentRepository repository, VersionedComponentChooser versionedComponentChooser) {
+    public ComponentMetaDataResolveState(ModuleComponentIdentifier componentIdentifier, ComponentOverrideMetadata componentOverrideMetadata, ModuleComponentRepository<ModuleComponentGraphResolveState> repository, VersionedComponentChooser versionedComponentChooser) {
         this.componentOverrideMetadata = componentOverrideMetadata;
         this.componentIdentifier = componentIdentifier;
         this.repository = repository;
         this.versionedComponentChooser = versionedComponentChooser;
-        this.resolveResult = new DefaultBuildableModuleComponentMetaDataResolveResult();
+        this.resolveResult = new DefaultBuildableModuleComponentMetaDataResolveResult<>();
     }
 
-    BuildableModuleComponentMetaDataResolveResult resolve() {
+    BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> resolve() {
         if (!searchedLocally) {
             searchedLocally = true;
             process(repository.getLocalAccess());
@@ -65,7 +66,7 @@ BuildableModuleComponentMetaDataResolveResult resolve() {
         throw new IllegalStateException();
     }
 
-    protected void process(ModuleComponentRepositoryAccess moduleAccess) {
+    protected void process(ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> moduleAccess) {
         moduleAccess.resolveComponentMetaData(componentIdentifier, componentOverrideMetadata, resolveResult);
         if (resolveResult.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved) {
             RejectedByRuleVersion rejectedComponent = versionedComponentChooser.isRejectedComponent(componentIdentifier, new CachedMetadataProvider(resolveResult));
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleComponentRepository.java
index b42f6b5..99964cc 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConfiguredModuleComponentRepository.java
@@ -16,12 +16,13 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.reflect.Instantiator;
 
 /**
  * A ModuleComponentRepository providing some user-configuration values.
  */
-public interface ConfiguredModuleComponentRepository extends ModuleComponentRepository {
+public interface ConfiguredModuleComponentRepository extends ModuleComponentRepository<ModuleComponentResolveMetadata> {
 
     /**
      * Should the 'dynamic resolve mode' be used for this (Ivy) repository, where the 'revConstraint'
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConnectionFailureRepositoryDisabler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConnectionFailureRepositoryDisabler.java
index ab5cff1..96bcb63 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConnectionFailureRepositoryDisabler.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ConnectionFailureRepositoryDisabler.java
@@ -16,14 +16,18 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Sets;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
 
 import java.util.Set;
 
 import static org.gradle.internal.resolve.ResolveExceptionAnalyzer.isCriticalFailure;
 
+@ServiceScope(Scopes.BuildTree.class)
 public class ConnectionFailureRepositoryDisabler implements RepositoryDisabler {
     private static final Logger LOGGER = Logging.getLogger(ConnectionFailureRepositoryDisabler.class);
 
@@ -51,7 +55,7 @@ public boolean disableRepository(String repositoryId, Throwable throwable) {
         return false;
     }
 
-    @Override
+    @VisibleForTesting
     public Set<String> getDisabledRepositories() {
         return disabledRepositories;
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProvider.java
index 14c1e29..de828e0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProvider.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProvider.java
@@ -39,6 +39,7 @@
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.internal.action.InstantiatingAction;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata;
 import org.gradle.internal.component.model.ComponentResolveMetadata;
@@ -52,7 +53,7 @@
 class DefaultMetadataProvider implements MetadataProvider {
     private final static Transformer<ComponentMetadata, BuildableComponentMetadataSupplierDetails> TO_COMPONENT_METADATA = BuildableComponentMetadataSupplierDetails::getExecutionResult;
     private final ModuleComponentResolveState resolveState;
-    private BuildableModuleComponentMetaDataResolveResult cachedResult;
+    private BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> cachedResult;
     private ComponentMetadata cachedComponentMetadata;
     private boolean computedMetadata;
 
@@ -81,7 +82,7 @@ private ComponentMetadata computeMetadata() {
         if (metadata != null) {
             metadata = transformThroughComponentMetadataRules(componentMetadataSupplier, metadata);
         } else if (resolve()) {
-            metadata = new ComponentMetadataAdapter(cachedResult.getMetaData());
+            metadata = new ComponentMetadataAdapter(cachedResult.getMetaData().getModuleResolveMetadata());
         }
         return metadata;
     }
@@ -105,7 +106,7 @@ private ComponentMetadata getComponentMetadataFromSupplier(InstantiatingAction<C
     @Override
     public IvyModuleDescriptor getIvyModuleDescriptor() {
         if (resolve()) {
-            ModuleComponentResolveMetadata metaData = cachedResult.getMetaData();
+            ModuleComponentResolveMetadata metaData = cachedResult.getMetaData().getModuleResolveMetadata();
             if (metaData instanceof IvyModuleResolveMetadata) {
                 IvyModuleResolveMetadata ivyMetadata = (IvyModuleResolveMetadata) metaData;
                 return new DefaultIvyModuleDescriptor(ivyMetadata.getExtraAttributes(), ivyMetadata.getBranch(), ivyMetadata.getStatus());
@@ -126,7 +127,7 @@ public boolean isUsable() {
         return cachedResult == null || cachedResult.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved;
     }
 
-    public BuildableModuleComponentMetaDataResolveResult getResult() {
+    public BuildableModuleComponentMetaDataResolveResult<?> getResult() {
         return cachedResult;
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooser.java
index bf5d601..c748a81 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooser.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooser.java
@@ -32,7 +32,8 @@
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveMetadata;
+import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.resolve.RejectedByAttributesVersion;
 import org.gradle.internal.resolve.RejectedByRuleVersion;
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
@@ -60,7 +61,7 @@ class DefaultVersionedComponentChooser implements VersionedComponentChooser {
     }
 
     @Override
-    public ComponentResolveMetadata selectNewestComponent(@Nullable ComponentResolveMetadata one, @Nullable ComponentResolveMetadata two) {
+    public ComponentGraphResolveMetadata selectNewestComponent(@Nullable ModuleComponentGraphResolveMetadata one, @Nullable ModuleComponentGraphResolveMetadata two) {
         if (one == null || two == null) {
             return two == null ? one : two;
         }
@@ -77,8 +78,8 @@ public ComponentResolveMetadata selectNewestComponent(@Nullable ComponentResolve
         return comparison < 0 ? two : one;
     }
 
-    private boolean isMissingModuleDescriptor(ComponentResolveMetadata componentResolveMetadata) {
-        return componentResolveMetadata.isMissing();
+    private boolean isMissingModuleDescriptor(ModuleComponentGraphResolveMetadata metadata) {
+        return metadata.isMissing();
     }
 
     @Override
@@ -178,7 +179,7 @@ private static DefaultMetadataProvider createMetadataProvider(ModuleComponentRes
     }
 
     private static void applyTo(DefaultMetadataProvider provider, ComponentSelectionContext result) {
-        BuildableModuleComponentMetaDataResolveResult metaDataResult = provider.getResult();
+        BuildableModuleComponentMetaDataResolveResult<?> metaDataResult = provider.getResult();
         switch (metaDataResult.getState()) {
             case Unknown:
             case Missing:
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyVerifyingModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyVerifyingModuleComponentRepository.java
index 724b0cb..d2f76a4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyVerifyingModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyVerifyingModuleComponentRepository.java
@@ -35,6 +35,7 @@
 import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
 import org.gradle.internal.component.external.model.ModuleComponentFileArtifactIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
@@ -55,13 +56,13 @@
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public class DependencyVerifyingModuleComponentRepository implements ModuleComponentRepository {
-    private final ModuleComponentRepository delegate;
-    private final ModuleComponentRepositoryAccess localAccess;
-    private final ModuleComponentRepositoryAccess remoteAccess;
+public class DependencyVerifyingModuleComponentRepository implements ModuleComponentRepository<ModuleComponentGraphResolveState> {
+    private final ModuleComponentRepository<ModuleComponentGraphResolveState> delegate;
+    private final ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> localAccess;
+    private final ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> remoteAccess;
     private final ArtifactVerificationOperation operation;
 
-    public DependencyVerifyingModuleComponentRepository(ModuleComponentRepository delegate, ArtifactVerificationOperation operation, boolean verifySignatures) {
+    public DependencyVerifyingModuleComponentRepository(ModuleComponentRepository<ModuleComponentGraphResolveState> delegate, ArtifactVerificationOperation operation, boolean verifySignatures) {
         this.delegate = delegate;
         this.localAccess = new VerifyingModuleComponentRepositoryAccess(delegate.getLocalAccess(), verifySignatures);
         this.remoteAccess = new VerifyingModuleComponentRepositoryAccess(delegate.getRemoteAccess(), verifySignatures);
@@ -79,12 +80,12 @@ public String getName() {
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> getLocalAccess() {
         return localAccess;
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> getRemoteAccess() {
         return remoteAccess;
     }
 
@@ -99,11 +100,11 @@ public InstantiatingAction<ComponentMetadataSupplierDetails> getComponentMetadat
         return delegate.getComponentMetadataSupplier();
     }
 
-    private class VerifyingModuleComponentRepositoryAccess implements ModuleComponentRepositoryAccess {
-        private final ModuleComponentRepositoryAccess delegate;
+    private class VerifyingModuleComponentRepositoryAccess implements ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> {
+        private final ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> delegate;
         private final boolean verifySignatures;
 
-        private VerifyingModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess delegate, boolean verifySignatures) {
+        private VerifyingModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> delegate, boolean verifySignatures) {
             this.delegate = delegate;
             this.verifySignatures = verifySignatures;
         }
@@ -113,17 +114,17 @@ public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableMod
             delegate.listModuleVersions(dependency, result);
         }
 
-        private boolean hasUsableResult(BuildableModuleComponentMetaDataResolveResult result) {
+        private boolean hasUsableResult(BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
             return result.hasResult() && result.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved;
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
             // For metadata, because the local file can be deleted we have to proceed in two steps
             // First resolve with a tmp result, and if it's found and that the file is still present
             // we can perform verification. If it's missing, then we do nothing so that it's downloaded
             // and verified later.
-            BuildableModuleComponentMetaDataResolveResult tmp = new DefaultBuildableModuleComponentMetaDataResolveResult();
+            BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> tmp = new DefaultBuildableModuleComponentMetaDataResolveResult<>();
             delegate.resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, tmp);
             AtomicBoolean ignore = new AtomicBoolean();
             if (hasUsableResult(tmp)) {
@@ -155,6 +156,7 @@ public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentId
             }
         }
 
+        @Nullable
         private File maybeFetchComponentMetadataSignatureFile(ModuleSources moduleSources, ModuleComponentArtifactIdentifier artifact) {
             ModuleComponentArtifactIdentifier signatureArtifactId;
             if (artifact instanceof DefaultModuleComponentArtifactIdentifier) {
@@ -166,6 +168,7 @@ private File maybeFetchComponentMetadataSignatureFile(ModuleSources moduleSource
             return maybeFetchSignatureFile(moduleSources, signatureArtifactMetadata);
         }
 
+        @Nullable
         private File maybeFetchArtifactSignatureFile(ModuleSources moduleSources, ModuleComponentArtifactIdentifier artifact, IvyArtifactName ivyArtifactName) {
             ModuleComponentArtifactIdentifier signatureArtifactId = createSignatureArtifactIdFromIvyArtifactName(artifact.getComponentIdentifier(), ivyArtifactName);
             SignatureArtifactMetadata signatureArtifactMetadata = new SignatureArtifactMetadata(signatureArtifactId);
@@ -177,6 +180,7 @@ private ModuleComponentArtifactIdentifier createSignatureArtifactIdFromIvyArtifa
             return new DefaultModuleComponentArtifactIdentifier(moduleComponentIdentifier, ivyArtifactName.getName(), "asc", extension + ".asc", ivyArtifactName.getClassifier());
         }
 
+        @Nullable
         private File maybeFetchSignatureFile(ModuleSources moduleSources, SignatureArtifactMetadata signatureArtifactMetadata) {
             if (!verifySignatures) {
                 return null;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java
index e0a68ef..05c8b22 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DynamicVersionResolver.java
@@ -19,7 +19,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import org.gradle.api.Action;
-import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.ComponentMetadataSupplierDetails;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
@@ -37,9 +36,8 @@
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.internal.action.InstantiatingAction;
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
-import org.gradle.internal.component.model.DefaultComponentGraphResolveState;
 import org.gradle.internal.component.model.DefaultComponentOverrideMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
@@ -76,31 +74,29 @@
 public class DynamicVersionResolver {
     private static final Logger LOGGER = LoggerFactory.getLogger(DynamicVersionResolver.class);
 
-    private final List<ModuleComponentRepository> repositories = new ArrayList<>();
+    private final List<ModuleComponentRepository<ModuleComponentGraphResolveState>> repositories = new ArrayList<>();
     private final List<String> repositoryNames = new ArrayList<>();
     private final VersionedComponentChooser versionedComponentChooser;
     private final VersionParser versionParser;
-    private final Transformer<ModuleComponentResolveMetadata, RepositoryChainModuleResolution> metaDataFactory;
     private final ImmutableAttributesFactory attributesFactory;
     private final ComponentMetadataProcessorFactory componentMetadataProcessor;
     private final ComponentMetadataSupplierRuleExecutor componentMetadataSupplierRuleExecutor;
     private final CachePolicy cachePolicy;
 
     public DynamicVersionResolver(
-        VersionedComponentChooser versionedComponentChooser, VersionParser versionParser, Transformer<ModuleComponentResolveMetadata, RepositoryChainModuleResolution> metaDataFactory,
+        VersionedComponentChooser versionedComponentChooser, VersionParser versionParser,
         ImmutableAttributesFactory attributesFactory, ComponentMetadataProcessorFactory componentMetadataProcessor,
         ComponentMetadataSupplierRuleExecutor componentMetadataSupplierRuleExecutor, CachePolicy cachePolicy
     ) {
         this.versionedComponentChooser = versionedComponentChooser;
         this.versionParser = versionParser;
-        this.metaDataFactory = metaDataFactory;
         this.attributesFactory = attributesFactory;
         this.componentMetadataProcessor = componentMetadataProcessor;
         this.componentMetadataSupplierRuleExecutor = componentMetadataSupplierRuleExecutor;
         this.cachePolicy = cachePolicy;
     }
 
-    public void add(ModuleComponentRepository repository) {
+    public void add(ModuleComponentRepository<ModuleComponentGraphResolveState> repository) {
         repositories.add(repository);
         repositoryNames.add(repository.getName());
     }
@@ -111,13 +107,13 @@ public void resolve(ModuleDependencyMetadata dependency, VersionSelector version
         List<Throwable> errors = new ArrayList<>();
 
         List<RepositoryResolveState> resolveStates = Lists.newArrayListWithCapacity(repositories.size());
-        for (ModuleComponentRepository repository : repositories) {
+        for (ModuleComponentRepository<ModuleComponentGraphResolveState> repository : repositories) {
             resolveStates.add(new RepositoryResolveState(versionedComponentChooser, dependency, repository, versionSelector, rejectedVersionSelector, versionParser, consumerAttributes, attributesFactory, componentMetadataProcessor, componentMetadataSupplierRuleExecutor, cachePolicy));
         }
 
         final RepositoryChainModuleResolution latestResolved = findLatestModule(resolveStates, errors);
         if (latestResolved != null) {
-            LOGGER.debug("Using {} from {}", latestResolved.module.getModuleVersionId(), latestResolved.repository);
+            LOGGER.debug("Using {} from {}", latestResolved.component.getId(), latestResolved.repository);
             for (Throwable error : errors) {
                 LOGGER.debug("Discarding resolve failure.", error);
             }
@@ -136,7 +132,7 @@ private void found(BuildableComponentIdResolveResult result, List<RepositoryReso
         for (RepositoryResolveState resolveState : resolveStates) {
             resolveState.registerAttempts(result);
         }
-        result.resolved(DefaultComponentGraphResolveState.of(metaDataFactory.transform(latestResolved)));
+        result.resolved(latestResolved.component);
     }
 
     private void notFound(BuildableComponentIdResolveResult result, ModuleComponentSelector requested, List<RepositoryResolveState> resolveStates) {
@@ -219,7 +215,7 @@ private RepositoryChainModuleResolution chooseBest(@Nullable RepositoryChainModu
         if (one == null || two == null) {
             return two == null ? one : two;
         }
-        return versionedComponentChooser.selectNewestComponent(one.module, two.module) == one.module ? one : two;
+        return versionedComponentChooser.selectNewestComponent(one.component.getMetadata(), two.component.getMetadata()) == one.component.getMetadata() ? one : two;
     }
 
     private static class AttemptCollector implements Action<ResourceAwareResolveResult> {
@@ -246,12 +242,12 @@ public void applyTo(ResourceAwareResolveResult result) {
      */
     private static class RepositoryResolveState implements ComponentSelectionContext {
         private final VersionedComponentChooser versionedComponentChooser;
-        private final BuildableModuleComponentMetaDataResolveResult resolvedVersionMetadata = new DefaultBuildableModuleComponentMetaDataResolveResult();
+        private final BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> resolvedVersionMetadata = new DefaultBuildableModuleComponentMetaDataResolveResult<>();
         private final Map<String, CandidateResult> candidateComponents = new LinkedHashMap<>();
         private final Set<String> unmatchedVersions = Sets.newLinkedHashSet();
         private final Set<RejectedVersion> rejectedVersions = Sets.newLinkedHashSet();
         private final VersionListResult versionListingResult;
-        private final ModuleComponentRepository repository;
+        private final ModuleComponentRepository<ModuleComponentGraphResolveState> repository;
         private final AttemptCollector attemptCollector;
         private final ModuleDependencyMetadata dependency;
         private final VersionSelector versionSelector;
@@ -265,7 +261,7 @@ private static class RepositoryResolveState implements ComponentSelectionContext
         private ModuleComponentIdentifier firstRejected = null;
 
 
-        public RepositoryResolveState(VersionedComponentChooser versionedComponentChooser, ModuleDependencyMetadata dependency, ModuleComponentRepository repository, VersionSelector versionSelector, VersionSelector rejectedVersionSelector, VersionParser versionParser, AttributeContainer consumerAttributes, ImmutableAttributesFactory attributesFactory, ComponentMetadataProcessorFactory componentMetadataProcessorFactory, ComponentMetadataSupplierRuleExecutor metadataSupplierRuleExecutor, CachePolicy cachePolicy) {
+        public RepositoryResolveState(VersionedComponentChooser versionedComponentChooser, ModuleDependencyMetadata dependency, ModuleComponentRepository<ModuleComponentGraphResolveState> repository, VersionSelector versionSelector, VersionSelector rejectedVersionSelector, VersionParser versionParser, AttributeContainer consumerAttributes, ImmutableAttributesFactory attributesFactory, ComponentMetadataProcessorFactory componentMetadataProcessorFactory, ComponentMetadataSupplierRuleExecutor metadataSupplierRuleExecutor, CachePolicy cachePolicy) {
             this.versionedComponentChooser = versionedComponentChooser;
             this.dependency = dependency;
             this.versionSelector = versionSelector;
@@ -404,7 +400,7 @@ private void registerAttempts(BuildableComponentIdResolveResult target) {
 
     private static class CandidateResult implements ModuleComponentResolveState {
         private final ModuleComponentIdentifier identifier;
-        private final ModuleComponentRepository repository;
+        private final ModuleComponentRepository<ModuleComponentGraphResolveState> repository;
         private final AttemptCollector attemptCollector;
         private final ModuleDependencyMetadata dependencyMetadata;
         private final Version version;
@@ -413,10 +409,10 @@ private static class CandidateResult implements ModuleComponentResolveState {
         private final ComponentMetadataSupplierRuleExecutor supplierRuleExecutor;
         private boolean searchedLocally;
         private boolean searchedRemotely;
-        private final DefaultBuildableModuleComponentMetaDataResolveResult result = new DefaultBuildableModuleComponentMetaDataResolveResult();
+        private final DefaultBuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result = new DefaultBuildableModuleComponentMetaDataResolveResult<>();
         private final CachePolicy cachePolicy;
 
-        public CandidateResult(ModuleDependencyMetadata dependencyMetadata, String version, ModuleComponentRepository repository, AttemptCollector attemptCollector, VersionParser versionParser, ComponentMetadataProcessorFactory componentMetadataProcessorFactory, ImmutableAttributesFactory attributesFactory, ComponentMetadataSupplierRuleExecutor supplierRuleExecutor, CachePolicy cachePolicy) {
+        public CandidateResult(ModuleDependencyMetadata dependencyMetadata, String version, ModuleComponentRepository<ModuleComponentGraphResolveState> repository, AttemptCollector attemptCollector, VersionParser versionParser, ComponentMetadataProcessorFactory componentMetadataProcessorFactory, ImmutableAttributesFactory attributesFactory, ComponentMetadataSupplierRuleExecutor supplierRuleExecutor, CachePolicy cachePolicy) {
             this.dependencyMetadata = dependencyMetadata;
             this.componentMetadataProcessorFactory = componentMetadataProcessorFactory;
             this.attributesFactory = attributesFactory;
@@ -440,7 +436,7 @@ public Version getVersion() {
         }
 
         @Override
-        public BuildableModuleComponentMetaDataResolveResult resolve() {
+        public BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> resolve() {
             if (!searchedLocally) {
                 searchedLocally = true;
                 process(repository.getLocalAccess(), result);
@@ -484,7 +480,7 @@ public CachePolicy getCachePolicy() {
             return cachePolicy;
         }
 
-        private void process(ModuleComponentRepositoryAccess access, DefaultBuildableModuleComponentMetaDataResolveResult result) {
+        private void process(ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> access, DefaultBuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
             DependencyMetadata dependency = dependencyMetadata.withRequestedVersion(new DefaultImmutableVersionConstraint(version.getSource()));
             IvyArtifactName firstArtifact = dependency.getArtifacts().isEmpty() ? null : dependency.getArtifacts().get(0);
             access.resolveComponentMetaData(identifier, DefaultComponentOverrideMetadata.forDependency(dependency.isChanging(), firstArtifact, DefaultComponentOverrideMetadata.extractClientModule(dependency)), result);
@@ -497,8 +493,8 @@ private void process(ModuleComponentRepositoryAccess access, DefaultBuildableMod
          *
          * @param target where to put metadata
          */
-        private void tryResolveMetadata(BuildableModuleComponentMetaDataResolveResult target) {
-            BuildableModuleComponentMetaDataResolveResult result = resolve();
+        private void tryResolveMetadata(BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> target) {
+            BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result = resolve();
             switch (result.getState()) {
                 case Resolved:
                     target.resolved(result.getMetaData());
@@ -520,13 +516,13 @@ private void tryResolveMetadata(BuildableModuleComponentMetaDataResolveResult ta
 
     private static class VersionListResult {
         private final DefaultBuildableModuleVersionListingResolveResult result = new DefaultBuildableModuleVersionListingResolveResult();
-        private final ModuleComponentRepository repository;
+        private final ModuleComponentRepository<?> repository;
         private final ModuleDependencyMetadata dependency;
 
         private boolean searchedLocally;
         private boolean searchedRemotely;
 
-        public VersionListResult(ModuleDependencyMetadata dependency, ModuleComponentRepository repository) {
+        public VersionListResult(ModuleDependencyMetadata dependency, ModuleComponentRepository<?> repository) {
             this.dependency = dependency;
             this.repository = repository;
         }
@@ -560,7 +556,7 @@ public void applyTo(ResourceAwareResolveResult target) {
             result.applyTo(target);
         }
 
-        private void process(ModuleDependencyMetadata dynamicVersionDependency, ModuleComponentRepositoryAccess moduleAccess) {
+        private void process(ModuleDependencyMetadata dynamicVersionDependency, ModuleComponentRepositoryAccess<?> moduleAccess) {
             moduleAccess.listModuleVersions(dynamicVersionDependency, result);
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java
index 94c50ed..3d31590 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolver.java
@@ -15,11 +15,9 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.internal.component.ArtifactType;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
-import org.gradle.internal.component.model.ModuleSources;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.resolve.ArtifactResolveException;
 import org.gradle.internal.resolve.resolver.ArtifactResolver;
 import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
@@ -33,7 +31,7 @@ public ErrorHandlingArtifactResolver(ArtifactResolver resolver) {
     }
 
     @Override
-    public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+    public void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
         try {
             resolver.resolveArtifactsWithType(component, artifactType, result);
         } catch (Exception t) {
@@ -42,9 +40,9 @@ public void resolveArtifactsWithType(ComponentResolveMetadata component, Artifac
     }
 
     @Override
-    public void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactResolveResult result) {
+    public void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result) {
         try {
-            resolver.resolveArtifact(ownerId, artifact, moduleSources, result);
+            resolver.resolveArtifact(component, artifact, result);
         } catch (Exception t) {
             result.failed(new ArtifactResolveException(artifact.getId(), t));
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingModuleComponentRepository.java
index 3ef24d5..afb3f14 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingModuleComponentRepository.java
@@ -30,6 +30,7 @@
 import org.gradle.internal.Factory;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.action.InstantiatingAction;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
@@ -54,13 +55,13 @@
  * This implementation will also disable any repository that throws a critical failure, failing-fast with that
  * repository for any subsequent requests.
  */
-public class ErrorHandlingModuleComponentRepository implements ModuleComponentRepository {
+public class ErrorHandlingModuleComponentRepository implements ModuleComponentRepository<ModuleComponentGraphResolveState> {
 
-    private final ModuleComponentRepository delegate;
+    private final ModuleComponentRepository<ModuleComponentGraphResolveState> delegate;
     private final ErrorHandlingModuleComponentRepositoryAccess local;
     private final ErrorHandlingModuleComponentRepositoryAccess remote;
 
-    public ErrorHandlingModuleComponentRepository(ModuleComponentRepository delegate, RepositoryDisabler remoteRepositoryBlacklister) {
+    public ErrorHandlingModuleComponentRepository(ModuleComponentRepository<ModuleComponentGraphResolveState> delegate, RepositoryDisabler remoteRepositoryBlacklister) {
         this.delegate = delegate;
         local = new ErrorHandlingModuleComponentRepositoryAccess(delegate.getLocalAccess(), getId(), RepositoryDisabler.NoOpBlacklister.INSTANCE, getName());
         remote = new ErrorHandlingModuleComponentRepositoryAccess(delegate.getRemoteAccess(), getId(), remoteRepositoryBlacklister, getName());
@@ -82,12 +83,12 @@ public String getName() {
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> getLocalAccess() {
         return local;
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> getRemoteAccess() {
         return remote;
     }
 
@@ -101,25 +102,25 @@ public InstantiatingAction<ComponentMetadataSupplierDetails> getComponentMetadat
         return delegate.getComponentMetadataSupplier();
     }
 
-    private static final class ErrorHandlingModuleComponentRepositoryAccess implements ModuleComponentRepositoryAccess {
+    private static final class ErrorHandlingModuleComponentRepositoryAccess implements ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> {
         private static final Logger LOGGER = Logging.getLogger(ErrorHandlingModuleComponentRepositoryAccess.class);
         private final static String MAX_TENTATIVES_BEFORE_BLACKLISTING = "org.gradle.internal.repository.max.tentatives";
         private final static String INITIAL_BACKOFF_MS = "org.gradle.internal.repository.initial.backoff";
 
         private final static String BLACKLISTED_REPOSITORY_ERROR_MESSAGE = "Skipped due to earlier error";
 
-        private final ModuleComponentRepositoryAccess delegate;
+        private final ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> delegate;
         private final String repositoryId;
         private final RepositoryDisabler repositoryBlacklister;
         private final int maxTentativesCount;
         private final int initialBackOff;
         private final String repositoryName;
 
-        private ErrorHandlingModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess delegate, String repositoryId, RepositoryDisabler repositoryBlacklister, String repositoryName) {
+        private ErrorHandlingModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> delegate, String repositoryId, RepositoryDisabler repositoryBlacklister, String repositoryName) {
             this(delegate, repositoryId, repositoryBlacklister, Integer.getInteger(MAX_TENTATIVES_BEFORE_BLACKLISTING, 3), Integer.getInteger(INITIAL_BACKOFF_MS, 1000), repositoryName);
         }
 
-        private ErrorHandlingModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess delegate, String repositoryId, RepositoryDisabler repositoryBlacklister, int maxTentativesCount, int initialBackoff, String repositoryName) {
+        private ErrorHandlingModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> delegate, String repositoryId, RepositoryDisabler repositoryBlacklister, int maxTentativesCount, int initialBackoff, String repositoryName) {
             this.repositoryName = repositoryName;
             assert maxTentativesCount > 0 : "Max tentatives must be > 0";
             assert initialBackoff >= 0 : "Initial backoff must be >= 0";
@@ -147,7 +148,7 @@ public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableMod
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
             performOperationWithRetries(result,
                     () -> delegate.resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result),
                     () -> new ModuleVersionResolveException(moduleComponentIdentifier, () -> BLACKLISTED_REPOSITORY_ERROR_MESSAGE),
@@ -202,12 +203,11 @@ private <E extends Throwable, R extends ErroringResolveResult<E>> void performOp
                 return;
             }
 
-            tryResolveAndMaybeBlacklist(result, operation, onBlacklisted, onError);
+            tryResolveAndMaybeBlacklist(result, operation, onError);
         }
 
         private <E extends Throwable, R extends ErroringResolveResult<E>> void tryResolveAndMaybeBlacklist(R result,
                                                                                                            Runnable operation,
-                                                                                                           Factory<E> onBlacklisted,
                                                                                                            Transformer<E, Throwable> onError) {
             tryResolveAndMaybeBlacklist(result, () -> {
                 operation.run();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/FilteredModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/FilteredModuleComponentRepository.java
index 6c62e63..2ff1f8d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/FilteredModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/FilteredModuleComponentRepository.java
@@ -30,6 +30,7 @@
 import org.gradle.internal.Actions;
 import org.gradle.internal.Factory;
 import org.gradle.internal.action.InstantiatingAction;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
@@ -44,20 +45,20 @@
 import java.util.Collections;
 import java.util.Map;
 
-public class FilteredModuleComponentRepository implements ModuleComponentRepository {
-    private final ModuleComponentRepository delegate;
+public class FilteredModuleComponentRepository implements ModuleComponentRepository<ModuleComponentGraphResolveState> {
+    private final ModuleComponentRepository<ModuleComponentGraphResolveState> delegate;
     private final Action<? super ArtifactResolutionDetails> filterAction;
     private final String consumerName;
     private final ImmutableAttributes consumerAttributes;
 
-    public static ModuleComponentRepository of(ModuleComponentRepository delegate, Action<? super ArtifactResolutionDetails> action, String consumerName, AttributeContainer attributes) {
+    public static ModuleComponentRepository<ModuleComponentGraphResolveState> of(ModuleComponentRepository<ModuleComponentGraphResolveState> delegate, Action<? super ArtifactResolutionDetails> action, String consumerName, AttributeContainer attributes) {
         if (action == Actions.doNothing()) {
             return delegate;
         }
         return new FilteredModuleComponentRepository(delegate, action, consumerName, attributes);
     }
 
-    private FilteredModuleComponentRepository(ModuleComponentRepository delegate, Action<? super ArtifactResolutionDetails> filterAction, String consumerName, AttributeContainer attributes) {
+    private FilteredModuleComponentRepository(ModuleComponentRepository<ModuleComponentGraphResolveState> delegate, Action<? super ArtifactResolutionDetails> filterAction, String consumerName, AttributeContainer attributes) {
         this.delegate = delegate;
         this.filterAction = filterAction;
         this.consumerName = consumerName;
@@ -83,12 +84,12 @@ public String getName() {
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> getLocalAccess() {
         return new FilteringAccess(delegate.getLocalAccess());
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> getRemoteAccess() {
         return new FilteringAccess(delegate.getRemoteAccess());
     }
 
@@ -103,10 +104,10 @@ public InstantiatingAction<ComponentMetadataSupplierDetails> getComponentMetadat
         return delegate.getComponentMetadataSupplier();
     }
 
-    private class FilteringAccess implements ModuleComponentRepositoryAccess {
-        private final ModuleComponentRepositoryAccess delegate;
+    private class FilteringAccess implements ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> {
+        private final ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> delegate;
 
-        private FilteringAccess(ModuleComponentRepositoryAccess delegate) {
+        private FilteringAccess(ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> delegate) {
             this.delegate = delegate;
         }
 
@@ -119,7 +120,7 @@ public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableMod
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
             whenModulePresent(moduleComponentIdentifier.getModuleIdentifier(), moduleComponentIdentifier,
                     () -> delegate.resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result),
                 result::missing);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepository.java
index e41a185..6f0c5c8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/IvyDynamicResolveModuleComponentRepository.java
@@ -16,8 +16,10 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveStateFactory;
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
+import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
 
@@ -25,38 +27,39 @@
  * A ModuleComponentRepository that provides the 'dynamic resolve mode' for an Ivy repository, where the 'revConstraint'
  * attribute is used for versions instead of the 'rev' attribute.
  */
-class IvyDynamicResolveModuleComponentRepository extends BaseModuleComponentRepository {
-
-    IvyDynamicResolveModuleComponentRepository(ModuleComponentRepository delegate) {
+class IvyDynamicResolveModuleComponentRepository extends BaseModuleComponentRepository<ModuleComponentGraphResolveState> {
+    IvyDynamicResolveModuleComponentRepository(ModuleComponentRepository<ModuleComponentGraphResolveState> delegate, ModuleComponentGraphResolveStateFactory resolveStateFactory) {
         super(delegate,
-            new IvyDynamicResolveModuleComponentRepositoryAccess(delegate.getLocalAccess()),
-            new IvyDynamicResolveModuleComponentRepositoryAccess(delegate.getRemoteAccess()));
+            new IvyDynamicResolveModuleComponentRepositoryAccess(delegate.getLocalAccess(), resolveStateFactory),
+            new IvyDynamicResolveModuleComponentRepositoryAccess(delegate.getRemoteAccess(), resolveStateFactory));
     }
 
-    private static class IvyDynamicResolveModuleComponentRepositoryAccess extends BaseModuleComponentRepositoryAccess {
+    private static class IvyDynamicResolveModuleComponentRepositoryAccess extends BaseModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> {
+        private final ModuleComponentGraphResolveStateFactory resolveStateFactory;
 
-        IvyDynamicResolveModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess delegate) {
+        IvyDynamicResolveModuleComponentRepositoryAccess(ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> delegate, ModuleComponentGraphResolveStateFactory resolveStateFactory) {
             super(delegate);
+            this.resolveStateFactory = resolveStateFactory;
         }
 
         @Override
         public String toString() {
-            return "Ivy dynamic resolve > " + getDelegate().toString();
+            return "Ivy dynamic resolve > " + getDelegate();
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
             super.resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result);
             if (result.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved) {
                 transformDependencies(result);
             }
         }
 
-        private void transformDependencies(BuildableModuleComponentMetaDataResolveResult result) {
-            ModuleComponentResolveMetadata metadata = result.getMetaData();
+        private void transformDependencies(BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> result) {
+            ModuleComponentResolveMetadata metadata = result.getMetaData().getModuleResolveMetadata();
             if (metadata instanceof IvyModuleResolveMetadata) {
                 IvyModuleResolveMetadata transformedMetadata = ((IvyModuleResolveMetadata) metadata).withDynamicConstraintVersions();
-                result.setMetadata(transformedMetadata);
+                result.setMetadata(resolveStateFactory.stateFor(transformedMetadata));
             }
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleComponentRepository.java
index 69b1b78..0eae873 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/LocalModuleComponentRepository.java
@@ -36,28 +36,28 @@
  * This is used to wrap a file-backed ExternalResourceRepository instance, so that both 'local' and 'remote' operations will
  * be considered local.
  */
-public class LocalModuleComponentRepository extends BaseModuleComponentRepository {
+public class LocalModuleComponentRepository<T> extends BaseModuleComponentRepository<T> {
     private final LocalAccess localAccess = new LocalAccess();
-    private final RemoteAccess remoteAccess = new RemoteAccess();
+    private final RemoteAccess<T> remoteAccess = new RemoteAccess<>();
 
-    public LocalModuleComponentRepository(ModuleComponentRepository delegate) {
+    public LocalModuleComponentRepository(ModuleComponentRepository<T> delegate) {
         super(delegate);
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<T> getLocalAccess() {
         return localAccess;
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<T> getRemoteAccess() {
         return remoteAccess;
     }
 
-    private class LocalAccess implements ModuleComponentRepositoryAccess {
+    private class LocalAccess implements ModuleComponentRepositoryAccess<T> {
         @Override
         public String toString() {
-            return "local adapter > " + delegate.toString();
+            return "local adapter > " + delegate;
         }
 
         @Override
@@ -69,7 +69,7 @@ public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableMod
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<T> result) {
             delegate.getLocalAccess().resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result);
             if (!result.hasResult()) {
                 delegate.getRemoteAccess().resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result);
@@ -98,7 +98,7 @@ public MetadataFetchingCost estimateMetadataFetchingCost(ModuleComponentIdentifi
         }
     }
 
-    private static class RemoteAccess implements ModuleComponentRepositoryAccess {
+    private static class RemoteAccess<T> implements ModuleComponentRepositoryAccess<T> {
         @Override
         public String toString() {
             return "empty";
@@ -109,7 +109,7 @@ public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableMod
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<T> result) {
         }
 
         @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepository.java
index 20b04a0..c81c126 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepository.java
@@ -26,11 +26,16 @@
 
 /**
  * A repository of module components.
+ *
+ * @param <T> the component resolution result type
  */
-public interface ModuleComponentRepository {
+public interface ModuleComponentRepository<T> {
     /**
-     * A unique identifier for this repository, based on it's type and attributes.
-     * Two repositories with the same configuration in different projects will share the same id.
+     * A unique identifier for this repository, based on its type and attributes.
+     * Two repositories with the same configuration will share the same id.
+     * This id is stable across builds on the same machine.
+     *
+     * <p>The name is not encoded in the id, as it is not relevant for resolution. The name is only used for diagnotics.</p>
      */
     String getId();
 
@@ -42,13 +47,13 @@ public interface ModuleComponentRepository {
     /**
      * Accessor that attempts to locate module components without expensive network operations.
      */
-    ModuleComponentRepositoryAccess getLocalAccess();
+    ModuleComponentRepositoryAccess<T> getLocalAccess();
 
     /**
      * Accessor that attempts to locate module components remotely, allowing expensive network operations.
      * This access will be disabled when Gradle is executed with `--offline`.
      */
-    ModuleComponentRepositoryAccess getRemoteAccess();
+    ModuleComponentRepositoryAccess<T> getRemoteAccess();
 
     // TODO - put this somewhere else
     Map<ComponentArtifactIdentifier, ResolvableArtifact> getArtifactCache();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepositoryAccess.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepositoryAccess.java
index cfd362a..e5c5b12 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepositoryAccess.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentRepositoryAccess.java
@@ -32,12 +32,9 @@
 /**
  * Provides access to a repository of components that are identified by a ModuleComponentIdentifier.
  *
- * The plan is to eventually sync this with
- * {@link org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver},
- * {@link org.gradle.internal.resolve.resolver.ComponentMetaDataResolver} and
- * {@link org.gradle.internal.resolve.resolver.ArtifactResolver}.
+ * @param <T> the component resolution result type
  */
-public interface ModuleComponentRepositoryAccess {
+public interface ModuleComponentRepositoryAccess<T> {
     /**
      * Resolves the given dependency to a list of module versions.
      */
@@ -46,7 +43,7 @@ public interface ModuleComponentRepositoryAccess {
     /**
      * Resolves the metadata for a module component.
      */
-    void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result);
+    void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<T> result);
 
     /**
      * Resolves a set of artifacts belonging to the given component, with the type specified. Any failures are packaged up in the result.
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentResolveState.java
index 9f9f6e2..f542e75 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ModuleComponentResolveState.java
@@ -22,6 +22,7 @@
 import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.internal.action.InstantiatingAction;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.resolve.caching.ComponentMetadataSupplierRuleExecutor;
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
 
@@ -30,7 +31,7 @@
 public interface ModuleComponentResolveState extends Versioned {
     ModuleComponentIdentifier getId();
 
-    BuildableModuleComponentMetaDataResolveResult resolve();
+    BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> resolve();
 
     ComponentMetadataProcessorFactory getComponentMetadataProcessorFactory();
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java
index 90bdbf0..7e8c13b 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/NoRepositoriesResolver.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import com.google.common.collect.ImmutableList;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet;
@@ -25,11 +25,10 @@
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.component.ArtifactType;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentArtifactResolveVariantState;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.resolve.ModuleVersionNotFoundException;
 import org.gradle.internal.resolve.resolver.ArtifactResolver;
 import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
@@ -69,7 +68,7 @@ public OriginArtifactSelector getArtifactSelector() {
 
     @Override
     public void resolve(DependencyMetadata dependency, VersionSelector acceptor, @Nullable VersionSelector rejector, BuildableComponentIdResolveResult result) {
-        result.failed(new ModuleVersionNotFoundException(dependency.getSelector(), () -> String.format("Cannot resolve external dependency %s because no repositories are defined.", dependency.getSelector())));
+        result.failed(new ModuleVersionNotFoundException(dependency.getSelector(), () -> String.format("Cannot resolve external dependency %s because no repositories are defined.", dependency.getSelector()), ImmutableList.of()));
     }
 
     @Override
@@ -84,17 +83,17 @@ public boolean isFetchingMetadataCheap(ComponentIdentifier identifier) {
 
     @Nullable
     @Override
-    public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ComponentArtifactResolveVariantState allVariants, Set<ResolvedVariant> legacyVariants, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) {
+    public ArtifactSet resolveArtifacts(ComponentArtifactResolveMetadata component, ComponentArtifactResolveVariantState allVariants, Set<ResolvedVariant> legacyVariants, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+    public void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactResolveResult result) {
+    public void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java
index a34f92e..3798733 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolver.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.DefaultResolvableArtifact;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSetFactory;
@@ -26,8 +25,8 @@
 import org.gradle.api.internal.component.ArtifactType;
 import org.gradle.internal.Describables;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentArtifactResolveVariantState;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.model.CalculatedValue;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
@@ -48,30 +47,30 @@
 import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet.NO_ARTIFACTS;
 
 class RepositoryChainArtifactResolver implements ArtifactResolver, OriginArtifactSelector {
-    private final Map<String, ModuleComponentRepository> repositories = new LinkedHashMap<>();
+    private final Map<String, ModuleComponentRepository<?>> repositories = new LinkedHashMap<>();
     private final CalculatedValueContainerFactory calculatedValueContainerFactory;
 
     RepositoryChainArtifactResolver(CalculatedValueContainerFactory calculatedValueContainerFactory) {
         this.calculatedValueContainerFactory = calculatedValueContainerFactory;
     }
 
-    void add(ModuleComponentRepository repository) {
+    void add(ModuleComponentRepository<?> repository) {
         repositories.put(repository.getId(), repository);
     }
 
     @Override
-    public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
-        ModuleComponentRepository sourceRepository = findSourceRepository(component.getSources());
+    public void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+        ModuleComponentRepository<?> sourceRepository = findSourceRepository(component.getSources());
         // First try to determine the artifacts locally before going remote
-        sourceRepository.getLocalAccess().resolveArtifactsWithType(component, artifactType, result);
+        sourceRepository.getLocalAccess().resolveArtifactsWithType(component.getMetadata(), artifactType, result);
         if (!result.hasResult()) {
-            sourceRepository.getRemoteAccess().resolveArtifactsWithType(component, artifactType, result);
+            sourceRepository.getRemoteAccess().resolveArtifactsWithType(component.getMetadata(), artifactType, result);
         }
     }
 
     @Nullable
     @Override
-    public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ComponentArtifactResolveVariantState allVariants, Set<ResolvedVariant> legacyVariants, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) {
+    public ArtifactSet resolveArtifacts(ComponentArtifactResolveMetadata component, ComponentArtifactResolveVariantState allVariants, Set<ResolvedVariant> legacyVariants, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) {
         if (component.getSources() == null) {
             // virtual components have no source
             return NO_ARTIFACTS;
@@ -80,17 +79,17 @@ public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, Componen
     }
 
     @Override
-    public void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMetadata artifact, ModuleSources sources, BuildableArtifactResolveResult result) {
-        ModuleComponentRepository sourceRepository = findSourceRepository(sources);
+    public void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result) {
+        ModuleComponentRepository<?> sourceRepository = findSourceRepository(component.getSources());
         ResolvableArtifact resolvableArtifact = sourceRepository.getArtifactCache().computeIfAbsent(artifact.getId(), id -> {
-            CalculatedValue<File> artifactSource = calculatedValueContainerFactory.create(Describables.of(artifact.getId()), (Supplier<File>)() -> resolveArtifactLater(artifact, sources, sourceRepository));
-            return new DefaultResolvableArtifact(ownerId, artifact.getName(), artifact.getId(), context -> context.add(artifact.getBuildDependencies()), artifactSource, calculatedValueContainerFactory);
+            CalculatedValue<File> artifactSource = calculatedValueContainerFactory.create(Describables.of(artifact.getId()), (Supplier<File>)() -> resolveArtifactLater(artifact, component.getSources(), sourceRepository));
+            return new DefaultResolvableArtifact(component.getModuleVersionId(), artifact.getName(), artifact.getId(), context -> context.add(artifact.getBuildDependencies()), artifactSource, calculatedValueContainerFactory);
         });
 
         result.resolved(resolvableArtifact);
     }
 
-    private File resolveArtifactLater(ComponentArtifactMetadata artifact, ModuleSources sources, ModuleComponentRepository sourceRepository) {
+    private File resolveArtifactLater(ComponentArtifactMetadata artifact, ModuleSources sources, ModuleComponentRepository<?> sourceRepository) {
         // First try to resolve the artifacts locally before going remote
         BuildableArtifactFileResolveResult artifactFile = new DefaultBuildableArtifactFileResolveResult();
         sourceRepository.getLocalAccess().resolveArtifact(artifact, sources, artifactFile);
@@ -100,9 +99,9 @@ private File resolveArtifactLater(ComponentArtifactMetadata artifact, ModuleSour
         return artifactFile.getResult();
     }
 
-    private ModuleComponentRepository findSourceRepository(ModuleSources sources) {
+    private ModuleComponentRepository<?> findSourceRepository(ModuleSources sources) {
         RepositoryChainModuleSource repositoryChainModuleSource = sources.getSource(RepositoryChainModuleSource.class).get();
-        ModuleComponentRepository moduleVersionRepository = repositories.get(repositoryChainModuleSource.getRepositoryId());
+        ModuleComponentRepository<?> moduleVersionRepository = repositories.get(repositoryChainModuleSource.getRepositoryId());
         if (moduleVersionRepository == null) {
             throw new IllegalStateException("Attempting to resolve artifacts from invalid repository");
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainComponentMetaDataResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainComponentMetaDataResolver.java
index a6a8c01..d696253 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainComponentMetaDataResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainComponentMetaDataResolver.java
@@ -16,13 +16,11 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.internal.artifacts.repositories.resolver.MetadataFetchingCost;
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
-import org.gradle.internal.component.model.DefaultComponentGraphResolveState;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
 import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
@@ -30,6 +28,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nullable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedList;
@@ -41,17 +40,15 @@
 public class RepositoryChainComponentMetaDataResolver implements ComponentMetaDataResolver {
     private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryChainComponentMetaDataResolver.class);
 
-    private final List<ModuleComponentRepository> repositories = new ArrayList<>();
+    private final List<ModuleComponentRepository<ModuleComponentGraphResolveState>> repositories = new ArrayList<>();
     private final List<String> repositoryNames = new ArrayList<>();
     private final VersionedComponentChooser versionedComponentChooser;
-    private final Transformer<ModuleComponentResolveMetadata, RepositoryChainModuleResolution> metaDataFactory;
 
-    public RepositoryChainComponentMetaDataResolver(VersionedComponentChooser componentChooser, Transformer<ModuleComponentResolveMetadata, RepositoryChainModuleResolution> metaDataFactory) {
+    public RepositoryChainComponentMetaDataResolver(VersionedComponentChooser componentChooser) {
         this.versionedComponentChooser = componentChooser;
-        this.metaDataFactory = metaDataFactory;
     }
 
-    public void add(ModuleComponentRepository repository) {
+    public void add(ModuleComponentRepository<ModuleComponentGraphResolveState> repository) {
         repositories.add(repository);
         repositoryNames.add(repository.getName());
     }
@@ -68,8 +65,8 @@ public void resolve(ComponentIdentifier identifier, ComponentOverrideMetadata co
     @Override
     public boolean isFetchingMetadataCheap(ComponentIdentifier identifier) {
         if (identifier instanceof ModuleComponentIdentifier) {
-            for (ModuleComponentRepository repository : repositories) {
-                ModuleComponentRepositoryAccess localAccess = repository.getLocalAccess();
+            for (ModuleComponentRepository<ModuleComponentGraphResolveState> repository : repositories) {
+                ModuleComponentRepositoryAccess<ModuleComponentGraphResolveState> localAccess = repository.getLocalAccess();
                 MetadataFetchingCost fetchingCost = localAccess.estimateMetadataFetchingCost((ModuleComponentIdentifier) identifier);
                 if (fetchingCost.isFast()) {
                     return true;
@@ -87,18 +84,18 @@ private void resolveModule(ModuleComponentIdentifier identifier, ComponentOverri
         List<Throwable> errors = new ArrayList<>();
 
         List<ComponentMetaDataResolveState> resolveStates = new ArrayList<>();
-        for (ModuleComponentRepository repository : repositories) {
+        for (ModuleComponentRepository<ModuleComponentGraphResolveState> repository : repositories) {
             resolveStates.add(new ComponentMetaDataResolveState(identifier, componentOverrideMetadata, repository, versionedComponentChooser));
         }
 
         final RepositoryChainModuleResolution latestResolved = findBestMatch(resolveStates, errors);
         if (latestResolved != null) {
-            LOGGER.debug("Using {} from {}", latestResolved.module.getModuleVersionId(), latestResolved.repository);
+            LOGGER.debug("Using {} from {}", latestResolved.component.getId(), latestResolved.repository);
             for (Throwable error : errors) {
                 LOGGER.debug("Discarding resolve failure.", error);
             }
 
-            result.resolved(DefaultComponentGraphResolveState.of(metaDataFactory.transform(latestResolved)));
+            result.resolved(latestResolved.component);
             return;
         }
         if (!errors.isEmpty()) {
@@ -111,6 +108,7 @@ private void resolveModule(ModuleComponentIdentifier identifier, ComponentOverri
         }
     }
 
+    @Nullable
     private RepositoryChainModuleResolution findBestMatch(List<ComponentMetaDataResolveState> resolveStates, Collection<Throwable> failures) {
         LinkedList<ComponentMetaDataResolveState> queue = new LinkedList<>(resolveStates);
 
@@ -131,11 +129,12 @@ private RepositoryChainModuleResolution findBestMatch(List<ComponentMetaDataReso
         return findBestMatch(queue, failures, missing);
     }
 
+    @Nullable
     private RepositoryChainModuleResolution findBestMatch(LinkedList<ComponentMetaDataResolveState> queue, Collection<Throwable> failures, Collection<ComponentMetaDataResolveState> missing) {
         RepositoryChainModuleResolution best = null;
         while (!queue.isEmpty()) {
             ComponentMetaDataResolveState request = queue.removeFirst();
-            BuildableModuleComponentMetaDataResolveResult metaDataResolveResult;
+            BuildableModuleComponentMetaDataResolveResult<ModuleComponentGraphResolveState> metaDataResolveResult;
             metaDataResolveResult = request.resolve();
             switch (metaDataResolveResult.getState()) {
                 case Failed:
@@ -152,7 +151,7 @@ private RepositoryChainModuleResolution findBestMatch(LinkedList<ComponentMetaDa
                     break;
                 case Resolved:
                     RepositoryChainModuleResolution moduleResolution = new RepositoryChainModuleResolution(request.repository, metaDataResolveResult.getMetaData());
-                    if (!metaDataResolveResult.getMetaData().isMissing()) {
+                    if (!metaDataResolveResult.getMetaData().getMetadata().isMissing()) {
                         return moduleResolution;
                     }
                     best = best != null ? best : moduleResolution;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyToComponentIdResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyToComponentIdResolver.java
index 71499ad..e3fd988 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyToComponentIdResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainDependencyToComponentIdResolver.java
@@ -16,7 +16,6 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.ModuleIdentifier;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentSelector;
@@ -30,7 +29,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadataWrapper;
 import org.gradle.internal.component.model.DependencyMetadata;
@@ -44,12 +43,12 @@ public class RepositoryChainDependencyToComponentIdResolver implements Dependenc
     private final DynamicVersionResolver dynamicRevisionResolver;
     private final AttributeContainer consumerAttributes;
 
-    public RepositoryChainDependencyToComponentIdResolver(VersionedComponentChooser componentChooser, Transformer<ModuleComponentResolveMetadata, RepositoryChainModuleResolution> metaDataFactory, VersionParser versionParser, AttributeContainer consumerAttributes, ImmutableAttributesFactory attributesFactory, ComponentMetadataProcessorFactory componentMetadataProcessorFactory, ComponentMetadataSupplierRuleExecutor componentMetadataSupplierRuleExecutor, CachePolicy cachePolicy) {
-        this.dynamicRevisionResolver = new DynamicVersionResolver(componentChooser, versionParser, metaDataFactory, attributesFactory, componentMetadataProcessorFactory, componentMetadataSupplierRuleExecutor, cachePolicy);
+    public RepositoryChainDependencyToComponentIdResolver(VersionedComponentChooser componentChooser, VersionParser versionParser, AttributeContainer consumerAttributes, ImmutableAttributesFactory attributesFactory, ComponentMetadataProcessorFactory componentMetadataProcessorFactory, ComponentMetadataSupplierRuleExecutor componentMetadataSupplierRuleExecutor, CachePolicy cachePolicy) {
+        this.dynamicRevisionResolver = new DynamicVersionResolver(componentChooser, versionParser, attributesFactory, componentMetadataProcessorFactory, componentMetadataSupplierRuleExecutor, cachePolicy);
         this.consumerAttributes = consumerAttributes;
     }
 
-    public void add(ModuleComponentRepository repository) {
+    public void add(ModuleComponentRepository<ModuleComponentGraphResolveState> repository) {
         dynamicRevisionResolver.add(repository);
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java
index 11b6d62..9c05cc8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleResolution.java
@@ -15,19 +15,19 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 
 class RepositoryChainModuleResolution implements StringVersioned {
-    public final ModuleComponentRepository repository;
-    public final ModuleComponentResolveMetadata module;
+    public final ModuleComponentRepository<?> repository;
+    public final ModuleComponentGraphResolveState component;
 
-    public RepositoryChainModuleResolution(ModuleComponentRepository repository, ModuleComponentResolveMetadata module) {
+    public RepositoryChainModuleResolution(ModuleComponentRepository<?> repository, ModuleComponentGraphResolveState component) {
         this.repository = repository;
-        this.module = module;
+        this.component = component;
     }
 
     @Override
     public String getVersion() {
-        return module.getModuleVersionId().getVersion();
+        return component.getId().getVersion();
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java
index 6e7f6c6..5872a65 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainModuleSource.java
@@ -19,11 +19,9 @@
 
 public class RepositoryChainModuleSource implements ModuleSource {
     private final String repositoryId;
-    private final String repositoryName;
 
-    public RepositoryChainModuleSource(ModuleComponentRepository repository) {
+    public RepositoryChainModuleSource(ModuleComponentRepository<?> repository) {
         this.repositoryId = repository.getId();
-        this.repositoryName = repository.getName();
     }
 
     @Override
@@ -34,8 +32,4 @@ public String toString() {
     public String getRepositoryId() {
         return repositoryId;
     }
-
-    public String getRepositoryName() {
-        return repositoryName;
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryDisabler.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryDisabler.java
index e9bac44..4beed36 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryDisabler.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryDisabler.java
@@ -16,17 +16,12 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import java.util.Collections;
-import java.util.Set;
-
 public interface RepositoryDisabler {
 
     boolean isDisabled(String repositoryId);
 
     boolean disableRepository(String repositoryId, Throwable throwable);
 
-    Set<String> getDisabledRepositories();
-
     enum NoOpBlacklister implements RepositoryDisabler {
         INSTANCE;
 
@@ -39,10 +34,5 @@ public boolean isDisabled(String repositoryId) {
         public boolean disableRepository(String repositoryId, Throwable throwable) {
             return false;
         }
-
-        @Override
-        public Set<String> getDisabledRepositories() {
-            return Collections.emptySet();
-        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
index a1911d8..4a6f793 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactory.java
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
 import org.gradle.api.Action;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.result.ArtifactResult;
 import org.gradle.api.attributes.AttributeContainer;
@@ -40,11 +39,13 @@
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.internal.component.ArtifactType;
 import org.gradle.internal.Actions;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveStateFactory;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.resolve.caching.ComponentMetadataSupplierRuleExecutor;
@@ -69,21 +70,25 @@ public class ResolveIvyFactory {
     private final ImmutableModuleIdentifierFactory moduleIdentifierFactory;
     private final RepositoryDisabler repositoryBlacklister;
     private final VersionParser versionParser;
+    private final ModuleComponentGraphResolveStateFactory moduleResolveStateFactory;
     private final CalculatedValueContainerFactory calculatedValueContainerFactory;
 
     private final DependencyVerificationOverride dependencyVerificationOverride;
     private final ChangingValueDependencyResolutionListener listener;
 
-    public ResolveIvyFactory(ModuleRepositoryCacheProvider cacheProvider,
-                             StartParameterResolutionOverride startParameterResolutionOverride,
-                             DependencyVerificationOverride dependencyVerificationOverride,
-                             BuildCommencedTimeProvider timeProvider,
-                             VersionComparator versionComparator,
-                             ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-                             RepositoryDisabler repositoryBlacklister,
-                             VersionParser versionParser,
-                             ChangingValueDependencyResolutionListener listener,
-                             CalculatedValueContainerFactory calculatedValueContainerFactory) {
+    public ResolveIvyFactory(
+        ModuleRepositoryCacheProvider cacheProvider,
+        StartParameterResolutionOverride startParameterResolutionOverride,
+        DependencyVerificationOverride dependencyVerificationOverride,
+        BuildCommencedTimeProvider timeProvider,
+        VersionComparator versionComparator,
+        ImmutableModuleIdentifierFactory moduleIdentifierFactory,
+        RepositoryDisabler repositoryBlacklister,
+        VersionParser versionParser,
+        ChangingValueDependencyResolutionListener listener,
+        ModuleComponentGraphResolveStateFactory moduleResolveStateFactory,
+        CalculatedValueContainerFactory calculatedValueContainerFactory
+    ) {
         this.cacheProvider = cacheProvider;
         this.startParameterResolutionOverride = startParameterResolutionOverride;
         this.timeProvider = timeProvider;
@@ -93,17 +98,20 @@ public ResolveIvyFactory(ModuleRepositoryCacheProvider cacheProvider,
         this.versionParser = versionParser;
         this.dependencyVerificationOverride = dependencyVerificationOverride;
         this.listener = listener;
+        this.moduleResolveStateFactory = moduleResolveStateFactory;
         this.calculatedValueContainerFactory = calculatedValueContainerFactory;
     }
 
-    public ComponentResolvers create(String resolveContextName,
-                                     ResolutionStrategyInternal resolutionStrategy,
-                                     Collection<? extends ResolutionAwareRepository> repositories,
-                                     ComponentMetadataProcessorFactory metadataProcessor,
-                                     AttributeContainer consumerAttributes,
-                                     AttributesSchema attributesSchema,
-                                     ImmutableAttributesFactory attributesFactory,
-                                     ComponentMetadataSupplierRuleExecutor componentMetadataSupplierRuleExecutor) {
+    public ComponentResolvers create(
+        String resolveContextName,
+        ResolutionStrategyInternal resolutionStrategy,
+        Collection<? extends ResolutionAwareRepository> repositories,
+        ComponentMetadataProcessorFactory metadataProcessor,
+        AttributeContainer consumerAttributes,
+        AttributesSchema attributesSchema,
+        ImmutableAttributesFactory attributesFactory,
+        ComponentMetadataSupplierRuleExecutor componentMetadataSupplierRuleExecutor
+    ) {
         if (repositories.isEmpty()) {
             return new NoRepositoriesResolver();
         }
@@ -122,18 +130,18 @@ public ComponentResolvers create(String resolveContextName,
             MetadataResolutionContext metadataResolutionContext = new DefaultMetadataResolutionContext(cachePolicy, instantiator);
             ComponentMetadataProcessor componentMetadataProcessor = metadataProcessor.createComponentMetadataProcessor(metadataResolutionContext);
 
-            ModuleComponentRepository moduleComponentRepository = baseRepository;
+            ModuleComponentRepository<ModuleComponentGraphResolveState> moduleComponentRepository;
             if (baseRepository.isLocal()) {
-                moduleComponentRepository = new CachingModuleComponentRepository(moduleComponentRepository, cacheProvider.getInMemoryOnlyCaches(), cachePolicy, timeProvider, componentMetadataProcessor, ChangingValueDependencyResolutionListener.NO_OP);
-                moduleComponentRepository = new LocalModuleComponentRepository(moduleComponentRepository);
+                moduleComponentRepository = new CachingModuleComponentRepository(baseRepository, cacheProvider.getInMemoryOnlyCaches(), moduleResolveStateFactory, cachePolicy, timeProvider, componentMetadataProcessor, ChangingValueDependencyResolutionListener.NO_OP);
+                moduleComponentRepository = new LocalModuleComponentRepository<>(moduleComponentRepository);
             } else {
-                moduleComponentRepository = startParameterResolutionOverride.overrideModuleVersionRepository(moduleComponentRepository);
-                moduleComponentRepository = new CachingModuleComponentRepository(moduleComponentRepository, cacheProvider.getPersistentCaches(), cachePolicy, timeProvider, componentMetadataProcessor, listener);
+                ModuleComponentRepository<ModuleComponentResolveMetadata> overrideRepository = startParameterResolutionOverride.overrideModuleVersionRepository(baseRepository);
+                moduleComponentRepository = new CachingModuleComponentRepository(overrideRepository, cacheProvider.getPersistentCaches(), moduleResolveStateFactory, cachePolicy, timeProvider, componentMetadataProcessor, listener);
             }
             moduleComponentRepository = cacheProvider.getResolvedArtifactCaches().provideResolvedArtifactCache(moduleComponentRepository, resolutionStrategy.isDependencyVerificationEnabled());
 
             if (baseRepository.isDynamicResolveMode()) {
-                moduleComponentRepository = new IvyDynamicResolveModuleComponentRepository(moduleComponentRepository);
+                moduleComponentRepository = new IvyDynamicResolveModuleComponentRepository(moduleComponentRepository, moduleResolveStateFactory);
             }
             moduleComponentRepository = new ErrorHandlingModuleComponentRepository(moduleComponentRepository, repositoryBlacklister);
             moduleComponentRepository = filterRepository(repository, moduleComponentRepository, resolveContextName, consumerAttributes);
@@ -145,13 +153,12 @@ public ComponentResolvers create(String resolveContextName,
         return moduleResolver;
     }
 
-    private ModuleComponentRepository filterRepository(ResolutionAwareRepository repository, ModuleComponentRepository moduleComponentRepository, String consumerName, AttributeContainer consumerAttributes) {
+    private ModuleComponentRepository<ModuleComponentGraphResolveState> filterRepository(ResolutionAwareRepository repository, ModuleComponentRepository<ModuleComponentGraphResolveState> moduleComponentRepository, String consumerName, AttributeContainer consumerAttributes) {
         Action<? super ArtifactResolutionDetails> filter = Actions.doNothing();
         if (repository instanceof ContentFilteringRepository) {
             filter = ((ContentFilteringRepository) repository).getContentFilter();
         }
-        moduleComponentRepository = FilteredModuleComponentRepository.of(moduleComponentRepository, filter, consumerName, consumerAttributes);
-        return moduleComponentRepository;
+        return FilteredModuleComponentRepository.of(moduleComponentRepository, filter, consumerName, consumerAttributes);
     }
 
     public ArtifactResult verifiedArtifact(DefaultResolvedArtifactResult defaultResolvedArtifactResult) {
@@ -164,21 +171,22 @@ public ArtifactResult verifiedArtifact(DefaultResolvedArtifactResult defaultReso
     private static class ParentModuleLookupResolver implements ComponentResolvers, DependencyToComponentIdResolver, ComponentMetaDataResolver, ArtifactResolver {
         private final UserResolverChain delegate;
 
-        public ParentModuleLookupResolver(VersionComparator versionComparator,
-                                          ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-                                          VersionParser versionParser,
-                                          AttributeContainer consumerAttributes,
-                                          AttributesSchema attributesSchema,
-                                          ImmutableAttributesFactory attributesFactory,
-                                          ComponentMetadataProcessorFactory componentMetadataProcessorFactory,
-                                          ComponentMetadataSupplierRuleExecutor componentMetadataSupplierRuleExecutor,
-                                          CalculatedValueContainerFactory calculatedValueContainerFactory,
-                                          CachePolicy cachePolicy
+        public ParentModuleLookupResolver(
+            VersionComparator versionComparator,
+            ImmutableModuleIdentifierFactory moduleIdentifierFactory,
+            VersionParser versionParser,
+            AttributeContainer consumerAttributes,
+            AttributesSchema attributesSchema,
+            ImmutableAttributesFactory attributesFactory,
+            ComponentMetadataProcessorFactory componentMetadataProcessorFactory,
+            ComponentMetadataSupplierRuleExecutor componentMetadataSupplierRuleExecutor,
+            CalculatedValueContainerFactory calculatedValueContainerFactory,
+            CachePolicy cachePolicy
         ) {
             this.delegate = new UserResolverChain(versionComparator, new DefaultComponentSelectionRules(moduleIdentifierFactory), versionParser, consumerAttributes, attributesSchema, attributesFactory, componentMetadataProcessorFactory, componentMetadataSupplierRuleExecutor, calculatedValueContainerFactory, cachePolicy);
         }
 
-        public void add(ModuleComponentRepository moduleComponentRepository) {
+        public void add(ModuleComponentRepository<ModuleComponentGraphResolveState> moduleComponentRepository) {
             delegate.add(moduleComponentRepository);
         }
 
@@ -218,13 +226,13 @@ public boolean isFetchingMetadataCheap(ComponentIdentifier identifier) {
         }
 
         @Override
-        public void resolveArtifactsWithType(final ComponentResolveMetadata component, final ArtifactType artifactType, final BuildableArtifactSetResolveResult result) {
+        public void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
             delegate.getArtifactResolver().resolveArtifactsWithType(component, artifactType, result);
         }
 
         @Override
-        public void resolveArtifact(ModuleVersionIdentifier ownerId, final ComponentArtifactMetadata artifact, final ModuleSources moduleSources, final BuildableArtifactResolveResult result) {
-            delegate.getArtifactResolver().resolveArtifact(ownerId, artifact, moduleSources, result);
+        public void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result) {
+            delegate.getArtifactResolver().resolveArtifact(component, artifact, result);
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
index 3b626635..8d5395e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/StartParameterResolutionOverride.java
@@ -20,6 +20,7 @@
 import org.gradle.api.artifacts.result.ResolvedArtifactResult;
 import org.gradle.api.artifacts.verification.DependencyVerificationMode;
 import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
 import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.ChecksumAndSignatureVerificationOverride;
@@ -32,11 +33,12 @@
 import org.gradle.api.internal.artifacts.verification.signatures.SignatureVerificationServiceFactory;
 import org.gradle.api.internal.component.ArtifactType;
 import org.gradle.api.internal.properties.GradleProperties;
-import org.gradle.api.invocation.Gradle;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.resources.ResourceException;
 import org.gradle.internal.Factory;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
@@ -65,7 +67,6 @@
 
 import javax.annotation.Nullable;
 import java.io.File;
-import java.io.IOException;
 import java.util.List;
 
 @ServiceScope(Scopes.BuildTree.class)
@@ -91,7 +92,7 @@ public void applyToCachePolicy(CachePolicy cachePolicy) {
         }
     }
 
-    public ModuleComponentRepository overrideModuleVersionRepository(ModuleComponentRepository original) {
+    public ModuleComponentRepository<ModuleComponentResolveMetadata> overrideModuleVersionRepository(ModuleComponentRepository<ModuleComponentResolveMetadata> original) {
         if (startParameter.isOffline()) {
             return new OfflineModuleComponentRepository(original);
         }
@@ -138,25 +139,25 @@ private File computeReportDirectory(BuildCommencedTimeProvider timeProvider) {
         // There's currently no good way to figure that out.
         File buildDir = new File(gradleDir.getParentFile(), "build");
         File reportsDirectory = new File(buildDir, "reports");
-        File verifReportsDirectory = new File(reportsDirectory, "dependency-verification");
-        return new File(verifReportsDirectory, "at-" + timeProvider.getCurrentTime());
+        File verifyReportsDirectory = new File(reportsDirectory, "dependency-verification");
+        return new File(verifyReportsDirectory, "at-" + timeProvider.getCurrentTime());
     }
 
-    private static class OfflineModuleComponentRepository extends BaseModuleComponentRepository {
+    private static class OfflineModuleComponentRepository extends BaseModuleComponentRepository<ModuleComponentResolveMetadata> {
 
         private final FailedRemoteAccess failedRemoteAccess = new FailedRemoteAccess();
 
-        public OfflineModuleComponentRepository(ModuleComponentRepository original) {
+        public OfflineModuleComponentRepository(ModuleComponentRepository<ModuleComponentResolveMetadata> original) {
             super(original);
         }
 
         @Override
-        public ModuleComponentRepositoryAccess getRemoteAccess() {
+        public ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> getRemoteAccess() {
             return failedRemoteAccess;
         }
     }
 
-    private static class FailedRemoteAccess implements ModuleComponentRepositoryAccess {
+    private static class FailedRemoteAccess implements ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> {
         @Override
         public String toString() {
             return "offline remote";
@@ -168,7 +169,7 @@ public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableMod
         }
 
         @Override
-        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
             result.failed(new ModuleVersionResolveException(moduleComponentIdentifier, () -> String.format("No cached version of %s available for offline mode.", moduleComponentIdentifier.getDisplayName())));
         }
 
@@ -222,7 +223,7 @@ public List<String> list(ExternalResourceName parent) throws ResourceException {
         }
 
         @Override
-        public void upload(ReadableContent resource, ExternalResourceName destination) throws IOException {
+        public void upload(ReadableContent resource, ExternalResourceName destination) {
             throw new ResourceException(destination.getUri(), String.format("Cannot upload to '%s' in offline mode.", destination.getUri()));
         }
 
@@ -239,7 +240,7 @@ private FailureVerificationOverride(Exception error) {
         }
 
         @Override
-        public ModuleComponentRepository overrideDependencyVerification(ModuleComponentRepository original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy) {
+        public ModuleComponentRepository<ModuleComponentGraphResolveState> overrideDependencyVerification(ModuleComponentRepository<ModuleComponentGraphResolveState> original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy) {
             throw new DependencyVerificationException("Dependency verification cannot be performed", error);
         }
     }
@@ -258,7 +259,7 @@ private DisablingVerificationOverride(DependencyVerificationOverride delegate) {
         }
 
         @Override
-        public ModuleComponentRepository overrideDependencyVerification(ModuleComponentRepository original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy) {
+        public ModuleComponentRepository<ModuleComponentGraphResolveState> overrideDependencyVerification(ModuleComponentRepository<ModuleComponentGraphResolveState> original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy) {
             if (resolutionStrategy.isDependencyVerificationEnabled()) {
                 return delegate.overrideDependencyVerification(original, resolveContextName, resolutionStrategy);
             } else {
@@ -268,8 +269,8 @@ public ModuleComponentRepository overrideDependencyVerification(ModuleComponentR
         }
 
         @Override
-        public void buildFinished(Gradle gradle) {
-            delegate.buildFinished(gradle);
+        public void buildFinished(GradleInternal model) {
+            delegate.buildFinished(model);
         }
 
         @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
index 63e3885..29652dc 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/UserResolverChain.java
@@ -16,7 +16,6 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve;
 
-import org.gradle.api.Transformer;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.attributes.AttributesSchema;
 import org.gradle.api.internal.artifacts.ComponentMetadataProcessorFactory;
@@ -25,9 +24,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
-import org.gradle.internal.component.model.ImmutableModuleSources;
-import org.gradle.internal.component.model.ModuleSources;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.resolve.caching.ComponentMetadataSupplierRuleExecutor;
 import org.gradle.internal.resolve.resolver.ArtifactResolver;
@@ -54,9 +51,8 @@ public UserResolverChain(VersionComparator versionComparator,
     ) {
         this.componentSelectionRules = componentSelectionRules;
         VersionedComponentChooser componentChooser = new DefaultVersionedComponentChooser(versionComparator, versionParser, componentSelectionRules, attributesSchema);
-        ModuleTransformer metaDataFactory = new ModuleTransformer();
-        componentIdResolver = new RepositoryChainDependencyToComponentIdResolver(componentChooser, metaDataFactory, versionParser, consumerAttributes, attributesFactory, componentMetadataProcessor, componentMetadataSupplierRuleExecutor, cachePolicy);
-        componentResolver = new RepositoryChainComponentMetaDataResolver(componentChooser, metaDataFactory);
+        componentIdResolver = new RepositoryChainDependencyToComponentIdResolver(componentChooser, versionParser, consumerAttributes, attributesFactory, componentMetadataProcessor, componentMetadataSupplierRuleExecutor, cachePolicy);
+        componentResolver = new RepositoryChainComponentMetaDataResolver(componentChooser);
         artifactResolver = new RepositoryChainArtifactResolver(calculatedValueContainerFactory);
     }
 
@@ -84,19 +80,9 @@ public ComponentSelectionRulesInternal getComponentSelectionRules() {
         return componentSelectionRules;
     }
 
-    public void add(ModuleComponentRepository repository) {
+    public void add(ModuleComponentRepository<ModuleComponentGraphResolveState> repository) {
         componentIdResolver.add(repository);
         componentResolver.add(repository);
         artifactResolver.add(repository);
     }
-
-    private static class ModuleTransformer implements Transformer<ModuleComponentResolveMetadata, RepositoryChainModuleResolution> {
-        @Override
-        public ModuleComponentResolveMetadata transform(RepositoryChainModuleResolution original) {
-            RepositoryChainModuleSource moduleSource = new RepositoryChainModuleSource(original.repository);
-            ModuleSources originSources = original.module.getSources();
-            ImmutableModuleSources mutated = ImmutableModuleSources.of(originSources, moduleSource);
-            return original.module.withSources(mutated);
-        }
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionedComponentChooser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionedComponentChooser.java
index ff32657..4fd4d41 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionedComponentChooser.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/VersionedComponentChooser.java
@@ -18,7 +18,8 @@
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveMetadata;
+import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.resolve.RejectedByRuleVersion;
 import org.gradle.internal.resolve.result.ComponentSelectionContext;
 
@@ -27,7 +28,7 @@
 
 public interface VersionedComponentChooser {
     @Nullable
-    ComponentResolveMetadata selectNewestComponent(ComponentResolveMetadata one, ComponentResolveMetadata two);
+    ComponentGraphResolveMetadata selectNewestComponent(ModuleComponentGraphResolveMetadata one, ModuleComponentGraphResolveMetadata two);
 
     void selectNewestMatchingComponent(Collection<? extends ModuleComponentResolveState> versions, ComponentSelectionContext result, VersionSelector versionSelector, VersionSelector rejectedVersionSelector, ImmutableAttributes consumerAttributes);
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradleModuleMetadataParser.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradleModuleMetadataParser.java
index 762bbf7..262c081 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradleModuleMetadataParser.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradleModuleMetadataParser.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser;
 
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.gson.stream.JsonReader;
@@ -53,7 +54,9 @@
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import static com.google.gson.stream.JsonToken.BOOLEAN;
 import static com.google.gson.stream.JsonToken.END_ARRAY;
@@ -291,8 +294,17 @@ private List<ModuleDependency> consumeVariantLocation(JsonReader reader) throws
         return ImmutableList.of(new ModuleDependency(group, module, new DefaultImmutableVersionConstraint(version), ImmutableList.of(), null, ImmutableAttributes.EMPTY, Collections.emptyList(), false, null));
     }
 
+    /**
+     * Consume the dependencies of a given variant.
+     * <p>
+     * This method needs to remove any duplicates from said dependencies.
+     *
+     * @param reader The Json to read from
+     * @return a list of dependencies
+     * @throws IOException when the reader fails
+     */
     private List<ModuleDependency> consumeDependencies(JsonReader reader) throws IOException {
-        List<ModuleDependency> dependencies = new ArrayList<>();
+        Set<ModuleDependency> dependencies = new LinkedHashSet<>();
         reader.beginArray();
         while (reader.peek() != END_ARRAY) {
             String group = null;
@@ -357,7 +369,7 @@ private List<ModuleDependency> consumeDependencies(JsonReader reader) throws IOE
             dependencies.add(new ModuleDependency(group, module, version, excludes, reason, attributes, requestedCapabilities, endorseStrictVersions, artifactSelector));
         }
         reader.endArray();
-        return dependencies;
+        return new ArrayList<>(dependencies);
     }
 
     private IvyArtifactName consumeArtifactSelector(JsonReader reader) throws IOException {
@@ -432,8 +444,17 @@ private List<VariantCapability> consumeCapabilities(JsonReader reader, boolean v
         return capabilities.build();
     }
 
+    /**
+     * Consume the dependency constraints of a given variant.
+     * <p>
+     * This method needs to remove any duplicates from said constraints.
+     *
+     * @param reader The Json to read from
+     * @return a list of constraints
+     * @throws IOException when the reader fails
+     */
     private List<ModuleDependencyConstraint> consumeDependencyConstraints(JsonReader reader) throws IOException {
-        List<ModuleDependencyConstraint> dependencies = new ArrayList<>();
+        Set<ModuleDependencyConstraint> dependencies = new LinkedHashSet<>();
         reader.beginArray();
         while (reader.peek() != END_ARRAY) {
             String group = null;
@@ -473,7 +494,7 @@ private List<ModuleDependencyConstraint> consumeDependencyConstraints(JsonReader
             dependencies.add(new ModuleDependencyConstraint(group, module, version, reason, attributes));
         }
         reader.endArray();
-        return dependencies;
+        return new ArrayList<>(dependencies);
     }
 
     private ImmutableVersionConstraint consumeVersion(JsonReader reader) throws IOException {
@@ -641,6 +662,33 @@ private static class ModuleDependency {
             this.endorsing = endorsing;
             this.artifact = artifact;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            ModuleDependency that = (ModuleDependency) o;
+            return Objects.equal(group, that.group)
+                && Objects.equal(module, that.module)
+                && Objects.equal(versionConstraint, that.versionConstraint)
+                && Objects.equal(excludes, that.excludes)
+                && Objects.equal(reason, that.reason)
+                && Objects.equal(attributes, that.attributes)
+                && Objects.equal(requestedCapabilities, that.requestedCapabilities)
+                && Objects.equal(endorsing, that.endorsing)
+                && Objects.equal(artifact, that.artifact);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(group, module, versionConstraint, excludes, reason, attributes, endorsing, artifact);
+        }
+
     }
 
     private static class ModuleDependencyConstraint {
@@ -657,6 +705,27 @@ private static class ModuleDependencyConstraint {
             this.reason = reason;
             this.attributes = attributes;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            ModuleDependencyConstraint that = (ModuleDependencyConstraint) o;
+            return Objects.equal(group, that.group)
+                && Objects.equal(module, that.module)
+                && Objects.equal(versionConstraint, that.versionConstraint)
+                && Objects.equal(reason, that.reason)
+                && Objects.equal(attributes, that.attributes);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(group, module, versionConstraint, reason, attributes);
+        }
     }
 
     private static class VariantCapability implements Capability {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/ChecksumAndSignatureVerificationOverride.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/ChecksumAndSignatureVerificationOverride.java
index 2affc6b..b254c87 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/ChecksumAndSignatureVerificationOverride.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/ChecksumAndSignatureVerificationOverride.java
@@ -42,6 +42,7 @@
 import org.gradle.internal.Factory;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.concurrent.Stoppable;
 import org.gradle.internal.hash.ChecksumService;
 import org.gradle.internal.logging.ConsoleRenderer;
@@ -163,7 +164,7 @@ public BuildOperationDescriptor.Builder description() {
     }
 
     @Override
-    public ModuleComponentRepository overrideDependencyVerification(ModuleComponentRepository original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy) {
+    public ModuleComponentRepository<ModuleComponentGraphResolveState> overrideDependencyVerification(ModuleComponentRepository<ModuleComponentGraphResolveState> original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy) {
         return new DependencyVerifyingModuleComponentRepository(original, this, verifier.getConfiguration().isVerifySignatures());
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/DependencyVerificationOverride.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/DependencyVerificationOverride.java
index d7f13f4..c47e940 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/DependencyVerificationOverride.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/DependencyVerificationOverride.java
@@ -16,9 +16,10 @@
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification;
 
 import org.gradle.api.artifacts.result.ResolvedArtifactResult;
+import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
-import org.gradle.api.invocation.Gradle;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 
 import java.io.File;
 
@@ -36,10 +37,9 @@ static File keyringsFile(File gradleDir) {
         return new File(gradleDir, VERIFICATION_KEYRING_GPG);
     }
 
-    ModuleComponentRepository overrideDependencyVerification(ModuleComponentRepository original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy);
+    ModuleComponentRepository<ModuleComponentGraphResolveState> overrideDependencyVerification(ModuleComponentRepository<ModuleComponentGraphResolveState> original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy);
 
-    default void buildFinished(Gradle gradle) {
-
+    default void buildFinished(GradleInternal model) {
     }
 
     /**
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/report/AbstractTextDependencyVerificationReportRenderer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/report/AbstractTextDependencyVerificationReportRenderer.java
index 44a3f62..c8e9d2f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/report/AbstractTextDependencyVerificationReportRenderer.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/report/AbstractTextDependencyVerificationReportRenderer.java
@@ -50,7 +50,7 @@ public void finish(VerificationHighLevelErrors highLevelErrors) {
         }
         if (highLevelErrors.canSuggestWriteMetadata()) {
             // the else is just to avoid telling people to use `--write-verification-metadata` if we suspect compromised dependencies
-            legend("If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file by following the instructions at " + documentationRegistry.getDocumentationFor("dependency_verification", "sec:troubleshooting-verification"));
+            legend("If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. " +  documentationRegistry.getDocumentationRecommendationFor("on how to do this", "dependency_verification", "sec:troubleshooting-verification"));
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/report/HtmlDependencyVerificationReportRenderer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/report/HtmlDependencyVerificationReportRenderer.java
index 0ee9ddd..3966732 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/report/HtmlDependencyVerificationReportRenderer.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/report/HtmlDependencyVerificationReportRenderer.java
@@ -113,8 +113,8 @@ public void renderNavBar() {
     }
 
     public void renderSections() {
-        contents.append("<div class=\"uk-container uk-container-expand\">\n");
-        contents.append("        <ul uk-accordion>\n");
+        contents.append("<div class=\"uk-container uk-container-expand\">\n")
+            .append("        <ul uk-accordion>\n");
         boolean first = true;
         for (Section section : sections.values()) {
             if (first) {
@@ -127,8 +127,8 @@ public void renderSections() {
             contents.append("            </li>\n");
             first = false;
         }
-        contents.append("         </ul>\n");
-        contents.append("        </div>\n");
+        contents.append("         </ul>\n")
+            .append("        </div>\n");
     }
 
     File writeReport() {
@@ -182,19 +182,19 @@ private void generateContent() {
     }
 
     private void registerStickyTip() {
-        contents.append("    <div class=\"uk-container uk-padding\">\n");
-        contents.append("        <div class=\"uk-card uk-card-default uk-card-body\" style=\"z-index: 980;\" uk-sticky=\"bottom: true\">\n");
-        contents.append("            <h2 class=\"uk-modal-title\">Troubleshooting</h2>\n");
-        contents.append("            <p>Please review the errors reported above carefully.");
-        contents.append("            Click on the icons near to the error descriptions for information about how to fix a particular problem.");
-        contents.append("            It is recommended that you edit the ").append(verificationFileLink()).append(" manually. ");
-        contents.append("            However, if you are confident that those are false positives, Gradle can help you by generating the missing verification metadata.");
-        contents.append("            In this case, you can run with the following command-line:</p>");
-        contents.append("            <pre>gradle --write-verification-metadata ").append(verificationOptions()).append(" help</pre>");
-        contents.append("            <p>In any case you <b>must review the result</b> of this operation.");
-        contents.append("            <p>Please refer to the <a href=\"").append(documentationRegistry.getDocumentationFor("dependency_verification")).append("\" target=\"_blank\">documentation</a> for more information.</p>\n");
-        contents.append("        </div>\n");
-        contents.append("    </div>\n");
+        contents.append("    <div class=\"uk-container uk-padding\">\n")
+            .append("        <div class=\"uk-card uk-card-default uk-card-body\" style=\"z-index: 980;\" uk-sticky=\"bottom: true\">\n")
+            .append("            <h2 class=\"uk-modal-title\">Troubleshooting</h2>\n")
+            .append("            <p>Please review the errors reported above carefully.")
+            .append("            Click on the icons near to the error descriptions for information about how to fix a particular problem.")
+            .append("            It is recommended that you edit the ").append(verificationFileLink()).append(" manually. ")
+            .append("            However, if you are confident that those are false positives, Gradle can help you by generating the missing verification metadata.")
+            .append("            In this case, you can run with the following command-line:</p>")
+            .append("            <pre>gradle --write-verification-metadata ").append(verificationOptions()).append(" help</pre>")
+            .append("            <p>In any case you <b>must review the result</b> of this operation.")
+            .append("            <p>Please refer to the <a href=\"").append(documentationRegistry.getDocumentationFor("dependency_verification")).append("\" target=\"_blank\">documentation</a> for more information.</p>\n")
+            .append("        </div>\n")
+            .append("    </div>\n");
     }
 
     private String verificationOptions() {
@@ -214,7 +214,7 @@ private void registerModal(String id, String title, String... lines) {
         for (String line : lines) {
             contents.append(line).append("\n");
         }
-        contents.append("        <p>See the <a href=\"").append(documentationRegistry.getDocumentationFor("dependency_verification", "sec:signature-verification")).append("\" target=\"_blank\">documentation</a> to get more information.</p>\n")
+        contents.append("        <p>See the <a href=\"").append(documentationRegistry.getDocumentationFor("dependency_verification", "sec:^")).append("\" target=\"_blank\">documentation</a> to get more information.</p>\n")
             .append("        <button class=\"uk-button uk-button-primary uk-modal-close\" type=\"button\">Ok</button>\n")
             .append("    </div>\n")
             .append("</div>\n");
@@ -286,7 +286,7 @@ private void renderSection(Section section) {
             .append("    <tbody>\n");
         section.errors.forEach(this::formatErrors);
         contents.append("    </tbody>\n" +
-            "</table>")
+                "</table>")
             .append("</div>");
     }
 
@@ -297,8 +297,8 @@ private void formatErrors(ArtifactErrors currentArtifact) {
         reportItem(createFileLink(currentArtifact.key, firstFailure.getFailure(), firstFailure.getRepositoryName()));
         contents.append("            <td>\n");
         currentArtifact.failures.forEach(this::formatError);
-        contents.append("            </td>\n");
-        contents.append("        </tr>\n");
+        contents.append("            </td>\n")
+            .append("        </tr>\n");
     }
 
     private void formatError(RepositoryAwareVerificationFailure failure) {
@@ -401,8 +401,8 @@ private void reportSignatureProblems(VerificationFailure vf) {
     }
 
     private void reportItem(String item) {
-        contents.append("            <td class=\"uk-text-nowrap\"");
-        contents.append(">").append(item).append("</td>\n");
+        contents.append("            <td class=\"uk-text-nowrap\"")
+            .append(">").append(item).append("</td>\n");
     }
 
     private void reportItem(String item, String tipTarget, String tipIcon) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/utils/PGPUtils.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/utils/PGPUtils.java
deleted file mode 100644
index 3bcfb14..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/utils/PGPUtils.java
+++ /dev/null
@@ -1,44 +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.api.internal.artifacts.ivyservice.ivyresolve.verification.utils;
-
-import org.bouncycastle.openpgp.PGPPublicKey;
-
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-
-public final class PGPUtils {
-
-    private PGPUtils() {
-    }
-
-    /**
-     * A custom method to get user ids since original method `PGPPublicKey.getUserIDs()` can fail fast in case user id is not correctly encoded in UTF-8.
-     * Original method fails because it is very strict at conversion from bytes to UTF-8 string.
-     * <p>
-     * Example of dependencies with "broken" public key is `com.google.auto.value:auto-value-annotations`.
-     */
-    public static List<String> getUserIDs(PGPPublicKey pk) {
-        List<String> userIds = new ArrayList<>();
-        pk.getRawUserIDs().forEachRemaining(id -> {
-            userIds.add(new String(id, StandardCharsets.UTF_8));
-        });
-        return userIds;
-    }
-
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/writer/WriteDependencyVerificationFile.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/writer/WriteDependencyVerificationFile.java
index bf698d4..1f96ed4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/writer/WriteDependencyVerificationFile.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/verification/writer/WriteDependencyVerificationFile.java
@@ -27,13 +27,13 @@
 import org.gradle.api.artifacts.ArtifactView;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.GradleInternal;
 import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DependencyVerifyingModuleComponentRepository;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.ArtifactVerificationOperation;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.DefaultKeyServers;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.DependencyVerificationOverride;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.utils.PGPUtils;
 import org.gradle.api.internal.artifacts.verification.exceptions.DependencyVerificationException;
 import org.gradle.api.internal.artifacts.verification.model.ChecksumKind;
 import org.gradle.api.internal.artifacts.verification.model.IgnoredKey;
@@ -53,6 +53,7 @@
 import org.gradle.internal.Factory;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.deprecation.DeprecatableConfiguration;
 import org.gradle.internal.hash.ChecksumService;
 import org.gradle.internal.operations.BuildOperationContext;
@@ -61,6 +62,7 @@
 import org.gradle.internal.operations.BuildOperationQueue;
 import org.gradle.internal.operations.RunnableBuildOperation;
 import org.gradle.security.internal.Fingerprint;
+import org.gradle.security.internal.PGPUtils;
 import org.gradle.security.internal.PublicKeyResultBuilder;
 import org.gradle.security.internal.PublicKeyService;
 import org.gradle.security.internal.SecuritySupport;
@@ -73,12 +75,14 @@
 import java.io.OutputStream;
 import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import static com.google.common.io.Files.getNameWithoutExtension;
@@ -171,12 +175,12 @@ private void warnAboutInsecureChecksums() {
     }
 
     @Override
-    public ModuleComponentRepository overrideDependencyVerification(ModuleComponentRepository original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy) {
+    public ModuleComponentRepository<ModuleComponentGraphResolveState> overrideDependencyVerification(ModuleComponentRepository<ModuleComponentGraphResolveState> original, String resolveContextName, ResolutionStrategyInternal resolutionStrategy) {
         return new DependencyVerifyingModuleComponentRepository(original, this, generatePgpInfo);
     }
 
     @Override
-    public void buildFinished(Gradle gradle) {
+    public void buildFinished(GradleInternal gradle) {
         ensureOutputDirCreated();
         maybeReadExistingFile();
         // when we generate the verification file, we intentionally ignore if the "use key servers" flag is false
@@ -503,15 +507,12 @@ private void exportKeyRingCollection(PublicKeyService publicKeyService, BuildTre
             }
         }
 
-        List<PGPPublicKeyRing> keysSeenInVerifier = builder.build()
+        Stream<PGPPublicKeyRing> keysSeenInVerifier = builder.build()
             .stream()
-            .filter(WriteDependencyVerificationFile::hasAtLeastOnePublicKey)
-            .filter(e -> existingRings.stream().noneMatch(ring -> keyIds(ring).equals(keyIds(e))))
-            .collect(Collectors.toList());
-        ImmutableList<PGPPublicKeyRing> allKeyRings = ImmutableList.<PGPPublicKeyRing>builder()
-            .addAll(existingRings)
-            .addAll(keysSeenInVerifier)
-            .build();
+            .filter(keyring -> PGPUtils.getSize(keyring) != 0);
+
+        Collection<PGPPublicKeyRing> allKeyRings = uniqueKeyRings(Stream.concat(keysSeenInVerifier, existingRings.stream()));
+
         File keyringFile = keyrings.getBinaryKeyringsFile();
         writeBinaryKeyringFile(keyringFile, allKeyRings);
         File asciiArmoredFile = keyrings.getAsciiKeyringsFile();
@@ -519,7 +520,19 @@ private void exportKeyRingCollection(PublicKeyService publicKeyService, BuildTre
         LOGGER.lifecycle("Exported {} keys to {} and {}", allKeyRings.size(), keyringFile, asciiArmoredFile);
     }
 
-    private void writeAsciiArmoredKeyRingFile(File ascii, ImmutableList<PGPPublicKeyRing> allKeyRings) throws IOException {
+    private static Collection<PGPPublicKeyRing> uniqueKeyRings(Stream<PGPPublicKeyRing> keyRings) {
+        SortedMap<Long, PGPPublicKeyRing> seenKeyIds = new TreeMap<>();
+        keyRings.forEach(keyRing -> {
+            Long keyId = keyRing.getPublicKey().getKeyID();
+            PGPPublicKeyRing current = seenKeyIds.get(keyId);
+            if (current == null || PGPUtils.getSize(current) < PGPUtils.getSize(keyRing)) {
+                seenKeyIds.put(keyId, keyRing);
+            }
+        });
+        return seenKeyIds.values();
+    }
+
+    private void writeAsciiArmoredKeyRingFile(File ascii, Collection<PGPPublicKeyRing> allKeyRings) throws IOException {
         if (ascii.exists()) {
             ascii.delete();
         }
@@ -555,7 +568,7 @@ private void writeAsciiArmoredKeyRingFile(File ascii, ImmutableList<PGPPublicKey
         }
     }
 
-    private void writeBinaryKeyringFile(File keyringFile, ImmutableList<PGPPublicKeyRing> allKeyRings) throws IOException {
+    private void writeBinaryKeyringFile(File keyringFile, Collection<PGPPublicKeyRing> allKeyRings) throws IOException {
         try (OutputStream out = new FileOutputStream(keyringFile)) {
             for (PGPPublicKeyRing keyRing : allKeyRings) {
                 keyRing.encode(out, true);
@@ -580,10 +593,6 @@ public List<PGPPublicKeyRing> build() {
         }
     }
 
-    private static boolean hasAtLeastOnePublicKey(PGPPublicKeyRing ring) {
-        return ring.getPublicKeys().hasNext();
-    }
-
     private List<PGPPublicKeyRing> loadExistingKeyRing(BuildTreeDefinedKeys keyrings) throws IOException {
         List<PGPPublicKeyRing> existingRings;
         if (!isDryRun) {
@@ -594,8 +603,4 @@ private List<PGPPublicKeyRing> loadExistingKeyRing(BuildTreeDefinedKeys keyrings
         }
         return existingRings;
     }
-
-    private static Set<Long> keyIds(PGPPublicKeyRing ring) {
-        return ImmutableList.copyOf(ring.getPublicKeys()).stream().map(PGPPublicKey::getKeyID).collect(Collectors.toSet());
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/AbstractModuleMetadataCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/AbstractModuleMetadataCache.java
index f37862d..57adbbd 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/AbstractModuleMetadataCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/AbstractModuleMetadataCache.java
@@ -31,13 +31,13 @@ public abstract class AbstractModuleMetadataCache implements ModuleMetadataCache
     }
 
     @Override
-    public CachedMetadata getCachedModuleDescriptor(ModuleComponentRepository repository, ModuleComponentIdentifier id) {
+    public CachedMetadata getCachedModuleDescriptor(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id) {
         final ModuleComponentAtRepositoryKey key = createKey(repository, id);
         return get(key);
     }
 
     @Override
-    public CachedMetadata cacheMissing(ModuleComponentRepository repository, ModuleComponentIdentifier id) {
+    public CachedMetadata cacheMissing(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id) {
         LOGGER.debug("Recording absence of module descriptor in cache: {} [changing = {}]", id, false);
         ModuleComponentAtRepositoryKey key = createKey(repository, id);
         ModuleMetadataCacheEntry entry = ModuleMetadataCacheEntry.forMissingModule(timeProvider.getCurrentTime());
@@ -47,7 +47,7 @@ public CachedMetadata cacheMissing(ModuleComponentRepository repository, ModuleC
     }
 
     @Override
-    public CachedMetadata cacheMetaData(ModuleComponentRepository repository, ModuleComponentIdentifier id, final ModuleComponentResolveMetadata metadata) {
+    public CachedMetadata cacheMetaData(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id, ModuleComponentResolveMetadata metadata) {
         LOGGER.debug("Recording module descriptor in cache: {} [changing = {}]", metadata.getId(), metadata.isChanging());
         final ModuleComponentAtRepositoryKey key = createKey(repository, id);
         ModuleMetadataCacheEntry entry = createEntry(metadata);
@@ -55,7 +55,7 @@ public CachedMetadata cacheMetaData(ModuleComponentRepository repository, Module
         return store(key, entry, cachedMetaData);
     }
 
-    protected ModuleComponentAtRepositoryKey createKey(ModuleComponentRepository repository, ModuleComponentIdentifier id) {
+    protected ModuleComponentAtRepositoryKey createKey(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id) {
         return new ModuleComponentAtRepositoryKey(repository.getId(), id);
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetadata.java
index 531a896..95c93f8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/DefaultCachedMetadata.java
@@ -17,6 +17,7 @@
 
 import org.gradle.api.artifacts.ResolvedModuleVersion;
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.DefaultResolvedModuleVersion;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
 import org.gradle.internal.component.model.ModuleSources;
@@ -32,7 +33,7 @@ class DefaultCachedMetadata implements ModuleMetadataCache.CachedMetadata {
     private final long ageMillis;
     private final ModuleComponentResolveMetadata metadata;
 
-    private volatile Map<Integer, ModuleComponentResolveMetadata> processedMetadataByRules;
+    private volatile Map<Integer, ModuleComponentGraphResolveState> processedMetadataByRules;
 
     DefaultCachedMetadata(ModuleMetadataCacheEntry entry, ModuleComponentResolveMetadata metadata, BuildCommencedTimeProvider timeProvider) {
         this(timeProvider.getCurrentTime() - entry.createTimestamp, metadata);
@@ -70,7 +71,7 @@ public Duration getAge() {
 
     @Nullable
     @Override
-    public ModuleComponentResolveMetadata getProcessedMetadata(int key) {
+    public ModuleComponentGraphResolveState getProcessedMetadata(int key) {
         if (processedMetadataByRules != null) {
             return processedMetadataByRules.get(key);
         }
@@ -78,7 +79,7 @@ public ModuleComponentResolveMetadata getProcessedMetadata(int key) {
     }
 
     @Override
-    public synchronized void putProcessedMetadata(int hash, ModuleComponentResolveMetadata processed) {
+    public synchronized void putProcessedMetadata(int hash, ModuleComponentGraphResolveState processed) {
         if (processedMetadataByRules == null) {
             processedMetadataByRules = Collections.singletonMap(hash, processed);
             return;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetadataCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetadataCache.java
index a4fa16f..5c9b541 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetadataCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ModuleMetadataCache.java
@@ -18,6 +18,7 @@
 import org.gradle.api.artifacts.ResolvedModuleVersion;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepository;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.model.ModuleSources;
 
@@ -25,11 +26,11 @@
 import java.time.Duration;
 
 public interface ModuleMetadataCache {
-    CachedMetadata cacheMissing(ModuleComponentRepository repository, ModuleComponentIdentifier id);
+    CachedMetadata cacheMissing(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id);
 
-    CachedMetadata cacheMetaData(ModuleComponentRepository repository, ModuleComponentIdentifier id, ModuleComponentResolveMetadata metaData);
+    CachedMetadata cacheMetaData(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id, ModuleComponentResolveMetadata metaData);
 
-    CachedMetadata getCachedModuleDescriptor(ModuleComponentRepository repository, ModuleComponentIdentifier id);
+    CachedMetadata getCachedModuleDescriptor(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id);
 
     interface CachedMetadata {
         ResolvedModuleVersion getModuleVersion();
@@ -49,12 +50,12 @@ interface CachedMetadata {
          * @param key the hash of the rules
          */
         @Nullable
-        ModuleComponentResolveMetadata getProcessedMetadata(int key);
+        ModuleComponentGraphResolveState getProcessedMetadata(int key);
 
         /**
          * Set the processed metadata to be cached in-memory only.
          */
-        void putProcessedMetadata(int key, ModuleComponentResolveMetadata processedMetadata);
+        void putProcessedMetadata(int key, ModuleComponentGraphResolveState state);
 
         /**
          * Returns a copy of this cached metadata where the module metadata is safe to store
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ReadOnlyModuleMetadataCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ReadOnlyModuleMetadataCache.java
index 9bcdd7f..e2b333e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ReadOnlyModuleMetadataCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ReadOnlyModuleMetadataCache.java
@@ -40,12 +40,12 @@ protected CachedMetadata store(ModuleComponentAtRepositoryKey key, ModuleMetadat
     }
 
     @Override
-    public CachedMetadata cacheMissing(ModuleComponentRepository repository, ModuleComponentIdentifier id) {
+    public CachedMetadata cacheMissing(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id) {
         return operationShouldNotHaveBeenCalled();
     }
 
     @Override
-    public CachedMetadata cacheMetaData(ModuleComponentRepository repository, ModuleComponentIdentifier id, ModuleComponentResolveMetadata metadata) {
+    public CachedMetadata cacheMetaData(ModuleComponentRepository<?> repository, ModuleComponentIdentifier id, ModuleComponentResolveMetadata metadata) {
         return operationShouldNotHaveBeenCalled();
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ResolvedArtifactCaches.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ResolvedArtifactCaches.java
index 66e7141..2f5a4c0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ResolvedArtifactCaches.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/ResolvedArtifactCaches.java
@@ -24,6 +24,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState;
 import org.gradle.internal.concurrent.Stoppable;
 
 import java.util.Map;
@@ -42,12 +43,12 @@ public class ResolvedArtifactCaches implements Stoppable {
      * For a remote repository, the only thing required is a resolved artifact cache.
      * The rest of the in-memory caching is handled by the CachingModuleComponentRepository.
      */
-    public ModuleComponentRepository provideResolvedArtifactCache(ModuleComponentRepository input, boolean withVerification) {
+    public ModuleComponentRepository<ModuleComponentGraphResolveState> provideResolvedArtifactCache(ModuleComponentRepository<ModuleComponentGraphResolveState> input, boolean withVerification) {
         Map<ComponentArtifactIdentifier, ResolvableArtifact> caches = getResolvedArtifactCache(withVerification ? cachePerRepoWithVerification : cachePerRepo, input);
         return new ResolvedArtifactCacheProvidingModuleComponentRepository(caches, input);
     }
 
-    private Map<ComponentArtifactIdentifier, ResolvableArtifact> getResolvedArtifactCache(Map<String, Map<ComponentArtifactIdentifier, ResolvableArtifact>> cache, ModuleComponentRepository input) {
+    private Map<ComponentArtifactIdentifier, ResolvableArtifact> getResolvedArtifactCache(Map<String, Map<ComponentArtifactIdentifier, ResolvableArtifact>> cache, ModuleComponentRepository<ModuleComponentGraphResolveState> input) {
         Map<ComponentArtifactIdentifier, ResolvableArtifact> resolvedArtifactCache = cache.get(input.getId());
         if (resolvedArtifactCache == null) {
             LOG.debug("Creating new in-memory cache for repo '{}' [{}].", input.getName(), input.getId());
@@ -65,11 +66,11 @@ public void stop() {
         cachePerRepoWithVerification.clear();
     }
 
-    private static class ResolvedArtifactCacheProvidingModuleComponentRepository extends BaseModuleComponentRepository {
+    private static class ResolvedArtifactCacheProvidingModuleComponentRepository extends BaseModuleComponentRepository<ModuleComponentGraphResolveState> {
 
         private final Map<ComponentArtifactIdentifier, ResolvableArtifact> resolvedArtifactCache;
 
-        public ResolvedArtifactCacheProvidingModuleComponentRepository(Map<ComponentArtifactIdentifier, ResolvableArtifact> resolvedArtifactsCache, ModuleComponentRepository delegate) {
+        public ResolvedArtifactCacheProvidingModuleComponentRepository(Map<ComponentArtifactIdentifier, ResolvableArtifact> resolvedArtifactsCache, ModuleComponentRepository<ModuleComponentGraphResolveState> delegate) {
             super(delegate);
             this.resolvedArtifactCache = resolvedArtifactsCache;
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/AbstractArtifactsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/AbstractArtifactsCache.java
index 082eafc..3d91e74 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/AbstractArtifactsCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/AbstractArtifactsCache.java
@@ -34,7 +34,7 @@ public AbstractArtifactsCache(BuildCommencedTimeProvider timeProvider) {
     }
 
     @Override
-    public CachedArtifacts cacheArtifacts(ModuleComponentRepository repository, ComponentIdentifier componentId, String context, HashCode descriptorHash, Collection<? extends ComponentArtifactMetadata> artifacts) {
+    public CachedArtifacts cacheArtifacts(ModuleComponentRepository<?> repository, ComponentIdentifier componentId, String context, HashCode descriptorHash, Collection<? extends ComponentArtifactMetadata> artifacts) {
         ArtifactsAtRepositoryKey key = new ArtifactsAtRepositoryKey(repository.getId(), componentId, context);
         ModuleArtifactsCacheEntry entry = new ModuleArtifactsCacheEntry(ImmutableSet.copyOf(artifacts), timeProvider.getCurrentTime(), descriptorHash);
         store(key, entry);
@@ -44,7 +44,7 @@ public CachedArtifacts cacheArtifacts(ModuleComponentRepository repository, Comp
     protected abstract void store(ArtifactsAtRepositoryKey key, ModuleArtifactsCacheEntry entry);
 
     @Override
-    public CachedArtifacts getCachedArtifacts(ModuleComponentRepository repository, ComponentIdentifier componentId, String context) {
+    public CachedArtifacts getCachedArtifacts(ModuleComponentRepository<?> repository, ComponentIdentifier componentId, String context) {
         ArtifactsAtRepositoryKey key = new ArtifactsAtRepositoryKey(repository.getId(), componentId, context);
         ModuleArtifactsCacheEntry entry = get(key);
         return entry == null ? null : createCacheArtifacts(entry);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/ModuleArtifactsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/ModuleArtifactsCache.java
index f961c54..99c9fdb 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/ModuleArtifactsCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/ModuleArtifactsCache.java
@@ -23,8 +23,8 @@
 import java.util.Collection;
 
 public interface ModuleArtifactsCache {
-    CachedArtifacts cacheArtifacts(ModuleComponentRepository repository, ComponentIdentifier componentId, String context, HashCode descriptorHash, Collection<? extends ComponentArtifactMetadata> artifacts);
+    CachedArtifacts cacheArtifacts(ModuleComponentRepository<?> repository, ComponentIdentifier componentId, String context, HashCode descriptorHash, Collection<? extends ComponentArtifactMetadata> artifacts);
 
-    CachedArtifacts getCachedArtifacts(ModuleComponentRepository delegate, ComponentIdentifier componentId, String context);
+    CachedArtifacts getCachedArtifacts(ModuleComponentRepository<?> delegate, ComponentIdentifier componentId, String context);
 
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/ReadOnlyModuleArtifactsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/ReadOnlyModuleArtifactsCache.java
index c49666e..db010f1 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/ReadOnlyModuleArtifactsCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/artifacts/ReadOnlyModuleArtifactsCache.java
@@ -35,7 +35,7 @@ protected void store(ArtifactsAtRepositoryKey key, ModuleArtifactsCacheEntry ent
     }
 
     @Override
-    public CachedArtifacts cacheArtifacts(ModuleComponentRepository repository, ComponentIdentifier componentId, String context, HashCode descriptorHash, Collection<? extends ComponentArtifactMetadata> artifacts) {
+    public CachedArtifacts cacheArtifacts(ModuleComponentRepository<?> repository, ComponentIdentifier componentId, String context, HashCode descriptorHash, Collection<? extends ComponentArtifactMetadata> artifacts) {
         return operationShouldNotHaveBeenCalled();
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/AbstractModuleVersionsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/AbstractModuleVersionsCache.java
index 2f78c6e..67df239 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/AbstractModuleVersionsCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/AbstractModuleVersionsCache.java
@@ -32,7 +32,7 @@ public AbstractModuleVersionsCache(BuildCommencedTimeProvider timeProvider) {
     }
 
     @Override
-    public void cacheModuleVersionList(ModuleComponentRepository repository, ModuleIdentifier moduleId, Set<String> listedVersions) {
+    public void cacheModuleVersionList(ModuleComponentRepository<?> repository, ModuleIdentifier moduleId, Set<String> listedVersions) {
         LOGGER.debug("Caching version list in module versions cache: Using '{}' for '{}'", listedVersions, moduleId);
         ModuleAtRepositoryKey key = createKey(repository, moduleId);
         ModuleVersionsCacheEntry entry = createEntry(listedVersions);
@@ -40,7 +40,7 @@ public void cacheModuleVersionList(ModuleComponentRepository repository, ModuleI
     }
 
     @Override
-    public CachedModuleVersionList getCachedModuleResolution(ModuleComponentRepository repository, ModuleIdentifier moduleId) {
+    public CachedModuleVersionList getCachedModuleResolution(ModuleComponentRepository<?> repository, ModuleIdentifier moduleId) {
         ModuleAtRepositoryKey key = createKey(repository, moduleId);
         ModuleVersionsCacheEntry entry = get(key);
         return entry == null ? null : versionList(entry);
@@ -53,7 +53,7 @@ private CachedModuleVersionList versionList(ModuleVersionsCacheEntry entry) {
         );
     }
 
-    private ModuleAtRepositoryKey createKey(ModuleComponentRepository repository, ModuleIdentifier moduleId) {
+    private ModuleAtRepositoryKey createKey(ModuleComponentRepository<?> repository, ModuleIdentifier moduleId) {
         return new ModuleAtRepositoryKey(repository.getId(), moduleId);
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/ModuleVersionsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/ModuleVersionsCache.java
index 1cbb23d..09e7693 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/ModuleVersionsCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/ModuleVersionsCache.java
@@ -23,9 +23,9 @@
 
 public interface ModuleVersionsCache {
 
-    void cacheModuleVersionList(ModuleComponentRepository repository, ModuleIdentifier moduleId, Set<String> listedVersions);
+    void cacheModuleVersionList(ModuleComponentRepository<?> repository, ModuleIdentifier moduleId, Set<String> listedVersions);
 
-    CachedModuleVersionList getCachedModuleResolution(ModuleComponentRepository repository, ModuleIdentifier moduleId);
+    CachedModuleVersionList getCachedModuleResolution(ModuleComponentRepository<?> repository, ModuleIdentifier moduleId);
 
     interface CachedModuleVersionList {
         Set<String> getModuleVersions();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/ReadOnlyModuleVersionsCache.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/ReadOnlyModuleVersionsCache.java
index fc8ec88..bfd265b 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/ReadOnlyModuleVersionsCache.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/modulecache/dynamicversions/ReadOnlyModuleVersionsCache.java
@@ -35,7 +35,7 @@ protected void store(ModuleAtRepositoryKey key, ModuleVersionsCacheEntry entry)
     }
 
     @Override
-    public void cacheModuleVersionList(ModuleComponentRepository repository, ModuleIdentifier moduleId, Set<String> listedVersions) {
+    public void cacheModuleVersionList(ModuleComponentRepository<?> repository, ModuleIdentifier moduleId, Set<String> listedVersions) {
         throw operationShouldNotHaveBeenCalled();
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultLocalComponentMetadataBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultLocalComponentMetadataBuilder.java
deleted file mode 100644
index 672f539..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultLocalComponentMetadataBuilder.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2007-2009 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.api.internal.artifacts.ivyservice.moduleconverter;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.capabilities.Capability;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.configurations.Configurations;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.DisplayName;
-import org.gradle.internal.component.external.model.ImmutableCapabilities;
-import org.gradle.internal.component.local.model.BuildableLocalComponentMetadata;
-import org.gradle.internal.component.local.model.BuildableLocalConfigurationMetadata;
-import org.gradle.internal.component.model.ComponentConfigurationIdentifier;
-import org.gradle.internal.component.model.VariantResolveMetadata;
-
-import java.util.Collection;
-
-public class DefaultLocalComponentMetadataBuilder implements LocalComponentMetadataBuilder {
-    private final LocalConfigurationMetadataBuilder configurationMetadataBuilder;
-
-    public DefaultLocalComponentMetadataBuilder(LocalConfigurationMetadataBuilder configurationMetadataBuilder) {
-        this.configurationMetadataBuilder = configurationMetadataBuilder;
-    }
-
-    @Override
-    public BuildableLocalConfigurationMetadata addConfiguration(BuildableLocalComponentMetadata metaData, ConfigurationInternal configuration) {
-        BuildableLocalConfigurationMetadata configurationMetadata = createConfiguration(metaData, configuration);
-
-        metaData.addDependenciesAndExcludesForConfiguration(configuration, configurationMetadataBuilder);
-        ComponentConfigurationIdentifier configurationIdentifier = new ComponentConfigurationIdentifier(metaData.getId(), configuration.getName());
-
-        configuration.collectVariants(new ConfigurationInternal.VariantVisitor() {
-            @Override
-            public void visitArtifacts(Collection<? extends PublishArtifact> artifacts) {
-                configurationMetadata.addArtifacts(artifacts);
-            }
-
-            @Override
-            public void visitOwnVariant(DisplayName displayName, ImmutableAttributes attributes, Collection<? extends Capability> capabilities, Collection<? extends PublishArtifact> artifacts) {
-                configurationMetadata.addVariant(configuration.getName(), configurationIdentifier, displayName, attributes, ImmutableCapabilities.of(capabilities), artifacts);
-            }
-
-            @Override
-            public void visitChildVariant(String name, DisplayName displayName, ImmutableAttributes attributes, Collection<? extends Capability> capabilities, Collection<? extends PublishArtifact> artifacts) {
-                configurationMetadata.addVariant(configuration.getName() + "-" + name, new NestedVariantIdentifier(configurationIdentifier, name), displayName, attributes, ImmutableCapabilities.of(capabilities), artifacts);
-            }
-        });
-        return configurationMetadata;
-    }
-
-    private BuildableLocalConfigurationMetadata createConfiguration(BuildableLocalComponentMetadata metaData,
-                                                                    ConfigurationInternal configuration) {
-        configuration.preventFromFurtherMutation();
-
-        ImmutableSet<String> hierarchy = Configurations.getNames(configuration.getHierarchy());
-        ImmutableSet<String> extendsFrom = Configurations.getNames(configuration.getExtendsFrom());
-        // Presence of capabilities is bound to the definition of a capabilities extension to the project
-        ImmutableCapabilities capabilities = ImmutableCapabilities.of(Configurations.collectCapabilities(configuration, Sets.newHashSet(), Sets.newHashSet()));
-        return metaData.addConfiguration(configuration.getName(),
-            configuration.getDescription(),
-            extendsFrom,
-            hierarchy,
-            configuration.isVisible(),
-            configuration.isTransitive(),
-            configuration.getAttributes().asImmutable(),
-            configuration.isCanBeConsumed(),
-            configuration.getConsumptionDeprecation(),
-            configuration.isCanBeResolved(),
-            capabilities);
-    }
-
-    private static class NestedVariantIdentifier implements VariantResolveMetadata.Identifier {
-        private final VariantResolveMetadata.Identifier parent;
-        private final String name;
-
-        public NestedVariantIdentifier(VariantResolveMetadata.Identifier parent, String name) {
-            this.parent = parent;
-            this.name = name;
-        }
-
-        @Override
-        public int hashCode() {
-            return parent.hashCode() ^ name.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == this) {
-                return true;
-            }
-            if (obj == null || getClass() != obj.getClass()) {
-                return false;
-            }
-            NestedVariantIdentifier other = (NestedVariantIdentifier) obj;
-            return parent.equals(other.parent) && name.equals(other.name);
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultRootComponentMetadataBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultRootComponentMetadataBuilder.java
index d49a48f..95edd66 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultRootComponentMetadataBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultRootComponentMetadataBuilder.java
@@ -25,6 +25,7 @@
 import org.gradle.api.internal.artifacts.configurations.ConfigurationsProvider;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.configurations.MutationValidator;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.EmptySchema;
 import org.gradle.api.internal.initialization.RootScriptDomainObjectContext;
@@ -42,7 +43,7 @@ public class DefaultRootComponentMetadataBuilder implements RootComponentMetadat
     private final DependencyMetaDataProvider metadataProvider;
     private final ComponentIdentifierFactory componentIdentifierFactory;
     private final ImmutableModuleIdentifierFactory moduleIdentifierFactory;
-    private final LocalComponentMetadataBuilder localComponentMetadataBuilder;
+    private final LocalConfigurationMetadataBuilder configurationMetadataBuilder;
     private final ConfigurationsProvider configurationsProvider;
     private final MetadataHolder holder;
     private final ProjectStateRegistry projectStateRegistry;
@@ -56,7 +57,7 @@ private DefaultRootComponentMetadataBuilder(
         DependencyMetaDataProvider metadataProvider,
         ComponentIdentifierFactory componentIdentifierFactory,
         ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-        LocalComponentMetadataBuilder localComponentMetadataBuilder,
+        LocalConfigurationMetadataBuilder configurationMetadataBuilder,
         ConfigurationsProvider configurationsProvider,
         ProjectStateRegistry projectStateRegistry,
         CalculatedValueContainerFactory calculatedValueContainerFactory,
@@ -65,12 +66,12 @@ private DefaultRootComponentMetadataBuilder(
         this.metadataProvider = metadataProvider;
         this.componentIdentifierFactory = componentIdentifierFactory;
         this.moduleIdentifierFactory = moduleIdentifierFactory;
-        this.localComponentMetadataBuilder = localComponentMetadataBuilder;
+        this.configurationMetadataBuilder = configurationMetadataBuilder;
         this.configurationsProvider = configurationsProvider;
         this.projectStateRegistry = projectStateRegistry;
         this.calculatedValueContainerFactory = calculatedValueContainerFactory;
         this.factory = factory;
-        this.holder = new MetadataHolder(configurationsProvider);
+        this.holder = new MetadataHolder();
     }
 
     @Override
@@ -103,15 +104,13 @@ private LocalComponentMetadata buildRootComponentMetadata(final Module module, f
     }
 
     private LocalComponentMetadata getRootComponentMetadata(Module module, ComponentIdentifier componentIdentifier, ModuleVersionIdentifier moduleVersionIdentifier, AttributesSchemaInternal schema, ModelContainer<?> model) {
-        DefaultLocalComponentMetadata metadata = new DefaultLocalComponentMetadata(moduleVersionIdentifier, componentIdentifier, module.getStatus(), schema, model, calculatedValueContainerFactory);
-        for (ConfigurationInternal configuration : configurationsProvider.getAll()) {
-            addConfiguration(metadata, configuration);
-        }
-        return metadata;
-    }
+        DefaultLocalComponentMetadata.ConfigurationsProviderMetadataFactory configurationMetadataFactory =
+            new DefaultLocalComponentMetadata.ConfigurationsProviderMetadataFactory(
+                configurationsProvider, configurationMetadataBuilder, model, calculatedValueContainerFactory);
 
-    private void addConfiguration(DefaultLocalComponentMetadata metadata, ConfigurationInternal configuration) {
-        localComponentMetadataBuilder.addConfiguration(metadata, configuration);
+        configurationsProvider.visitAll(ConfigurationInternal::preventFromFurtherMutation);
+
+        return new DefaultLocalComponentMetadata(moduleVersionIdentifier, componentIdentifier, module.getStatus(), schema, configurationMetadataFactory, null);
     }
 
     @Override
@@ -130,22 +129,15 @@ public void discardAll() {
 
     private static class MetadataHolder implements MutationValidator {
         private LocalComponentMetadata cachedValue;
-        private final ConfigurationsProvider configurationsProvider;
-
-        private MetadataHolder(ConfigurationsProvider configurationsProvider) {
-            this.configurationsProvider = configurationsProvider;
-        }
 
         @Override
         public void validateMutation(MutationType type) {
-            if (type == MutationType.DEPENDENCIES || type == MutationType.ARTIFACTS || type == MutationType.DEPENDENCY_ATTRIBUTES) {
+            if (type == MutationType.DEPENDENCIES || type == MutationType.ARTIFACTS ||
+                type == MutationType.DEPENDENCY_ATTRIBUTES || type == MutationType.USAGE ||
+                type == MutationType.HIERARCHY
+            ) {
                 if (cachedValue != null) {
-                    if (cachedValue.getConfigurationNames().size() != configurationsProvider.size()) {
-                        // The number of configurations in the project has changed, so we need to regenerate the root component metadata
-                        cachedValue = null;
-                    } else {
-                        cachedValue.reevaluate();
-                    }
+                    cachedValue.reevaluate();
                 }
             }
         }
@@ -170,7 +162,7 @@ public static class Factory {
         private final DependencyMetaDataProvider metaDataProvider;
         private final ComponentIdentifierFactory componentIdentifierFactory;
         private final ImmutableModuleIdentifierFactory moduleIdentifierFactory;
-        private final LocalComponentMetadataBuilder localComponentMetadataBuilder;
+        private final LocalConfigurationMetadataBuilder configurationMetadataBuilder;
         private final ProjectStateRegistry projectStateRegistry;
         private final CalculatedValueContainerFactory calculatedValueContainerFactory;
 
@@ -179,14 +171,14 @@ public Factory(
             DependencyMetaDataProvider metaDataProvider,
             ComponentIdentifierFactory componentIdentifierFactory,
             ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-            LocalComponentMetadataBuilder localComponentMetadataBuilder,
+            LocalConfigurationMetadataBuilder configurationMetadataBuilder,
             ProjectStateRegistry projectStateRegistry,
             CalculatedValueContainerFactory calculatedValueContainerFactory
         ) {
             this.metaDataProvider = metaDataProvider;
             this.componentIdentifierFactory = componentIdentifierFactory;
             this.moduleIdentifierFactory = moduleIdentifierFactory;
-            this.localComponentMetadataBuilder = localComponentMetadataBuilder;
+            this.configurationMetadataBuilder = configurationMetadataBuilder;
             this.projectStateRegistry = projectStateRegistry;
             this.calculatedValueContainerFactory = calculatedValueContainerFactory;
         }
@@ -196,7 +188,7 @@ public DefaultRootComponentMetadataBuilder create(ConfigurationsProvider configu
                 metaDataProvider,
                 componentIdentifierFactory,
                 moduleIdentifierFactory,
-                localComponentMetadataBuilder,
+                configurationMetadataBuilder,
                 configurationsProvider,
                 projectStateRegistry,
                 calculatedValueContainerFactory,
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/LocalComponentMetadataBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/LocalComponentMetadataBuilder.java
deleted file mode 100644
index c1af8a3..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/LocalComponentMetadataBuilder.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2007-2009 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.api.internal.artifacts.ivyservice.moduleconverter;
-
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.internal.component.local.model.BuildableLocalComponentMetadata;
-import org.gradle.internal.component.local.model.BuildableLocalConfigurationMetadata;
-
-public interface LocalComponentMetadataBuilder {
-    BuildableLocalConfigurationMetadata addConfiguration(BuildableLocalComponentMetadata metaData, ConfigurationInternal configuration);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyMetadataConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyMetadataConverter.java
new file mode 100644
index 0000000..90bca7e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractDependencyMetadataConverter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2007-2008 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.artifacts.DependencyArtifact;
+import org.gradle.api.artifacts.ExcludeRule;
+import org.gradle.internal.component.model.DefaultIvyArtifactName;
+import org.gradle.internal.component.model.ExcludeMetadata;
+import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.util.internal.CollectionUtils;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public abstract class AbstractDependencyMetadataConverter implements DependencyMetadataConverter {
+    private final ExcludeRuleConverter excludeRuleConverter;
+
+    public AbstractDependencyMetadataConverter(ExcludeRuleConverter excludeRuleConverter) {
+        this.excludeRuleConverter = excludeRuleConverter;
+    }
+
+    private String getExtension(DependencyArtifact artifact) {
+        return artifact.getExtension() != null ? artifact.getExtension() : artifact.getType();
+    }
+
+    protected List<ExcludeMetadata> convertExcludeRules(Set<ExcludeRule> excludeRules) {
+        return CollectionUtils.collect((Iterable<ExcludeRule>) excludeRules, excludeRuleConverter::convertExcludeRule);
+    }
+
+    protected List<IvyArtifactName> convertArtifacts(Set<DependencyArtifact> dependencyArtifacts) {
+        if (dependencyArtifacts.isEmpty()) {
+            return Collections.emptyList();
+        }
+        ImmutableList.Builder<IvyArtifactName> names = ImmutableList.builder();
+        for (DependencyArtifact dependencyArtifact : dependencyArtifacts) {
+            DefaultIvyArtifactName name = new DefaultIvyArtifactName(dependencyArtifact.getName(), dependencyArtifact.getType(), getExtension(dependencyArtifact), dependencyArtifact.getClassifier());
+            names.add(name);
+        }
+        return names.build();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java
deleted file mode 100644
index 7f4268d..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/AbstractIvyDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2007-2008 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.artifacts.DependencyArtifact;
-import org.gradle.api.artifacts.ExcludeRule;
-import org.gradle.internal.component.model.DefaultIvyArtifactName;
-import org.gradle.internal.component.model.ExcludeMetadata;
-import org.gradle.internal.component.model.IvyArtifactName;
-import org.gradle.util.internal.CollectionUtils;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-public abstract class AbstractIvyDependencyDescriptorFactory implements IvyDependencyDescriptorFactory {
-    private final ExcludeRuleConverter excludeRuleConverter;
-
-    public AbstractIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
-        this.excludeRuleConverter = excludeRuleConverter;
-    }
-
-    private String getExtension(DependencyArtifact artifact) {
-        return artifact.getExtension() != null ? artifact.getExtension() : artifact.getType();
-    }
-
-    protected List<ExcludeMetadata> convertExcludeRules(Set<ExcludeRule> excludeRules) {
-        return CollectionUtils.collect((Iterable<ExcludeRule>) excludeRules, excludeRuleConverter::convertExcludeRule);
-    }
-
-    protected List<IvyArtifactName> convertArtifacts(Set<DependencyArtifact> dependencyArtifacts) {
-        if (dependencyArtifacts.isEmpty()) {
-            return Collections.emptyList();
-        }
-        ImmutableList.Builder<IvyArtifactName> names = ImmutableList.builder();
-        for (DependencyArtifact dependencyArtifact : dependencyArtifacts) {
-            DefaultIvyArtifactName name = new DefaultIvyArtifactName(dependencyArtifact.getName(), dependencyArtifact.getType(), getExtension(dependencyArtifact), dependencyArtifact.getClassifier());
-            names.add(name);
-        }
-        return names.build();
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java
deleted file mode 100644
index 8ba5056..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2007-2008 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.DependencyConstraint;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
-import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependencyConstraint;
-import org.gradle.api.internal.artifacts.dependencies.DependencyConstraintInternal;
-import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
-import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
-import org.gradle.internal.component.model.LocalComponentDependencyMetadata;
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
-import org.gradle.util.internal.WrapUtil;
-
-import javax.annotation.Nullable;
-import java.util.Collections;
-import java.util.List;
-
-public class DefaultDependencyDescriptorFactory implements DependencyDescriptorFactory {
-    private final List<IvyDependencyDescriptorFactory> dependencyDescriptorFactories;
-
-    public DefaultDependencyDescriptorFactory(IvyDependencyDescriptorFactory... dependencyDescriptorFactories) {
-        this.dependencyDescriptorFactories = WrapUtil.toList(dependencyDescriptorFactories);
-    }
-
-    @Override
-    public LocalOriginDependencyMetadata createDependencyDescriptor(ComponentIdentifier componentId, @Nullable String clientConfiguration, @Nullable AttributeContainer attributes, ModuleDependency dependency) {
-        IvyDependencyDescriptorFactory factoryInternal = findFactoryForDependency(dependency);
-        return factoryInternal.createDependencyDescriptor(componentId, clientConfiguration, attributes, dependency);
-    }
-
-    @Override
-    public LocalOriginDependencyMetadata createDependencyConstraintDescriptor(ComponentIdentifier componentId, String clientConfiguration, AttributeContainer attributes, DependencyConstraint dependencyConstraint) {
-        ComponentSelector selector = createSelector(dependencyConstraint);
-        return new LocalComponentDependencyMetadata(componentId, selector, clientConfiguration, attributes, dependencyConstraint.getAttributes(), null,
-                Collections.emptyList(), Collections.emptyList(), ((DependencyConstraintInternal)dependencyConstraint).isForce(), false, false, true, false, dependencyConstraint.getReason());
-    }
-
-    private ComponentSelector createSelector(DependencyConstraint dependencyConstraint) {
-        if (dependencyConstraint instanceof DefaultProjectDependencyConstraint) {
-            return DefaultProjectComponentSelector.newSelector(((DefaultProjectDependencyConstraint) dependencyConstraint).getProjectDependency().getDependencyProject());
-        }
-        return DefaultModuleComponentSelector.newSelector(
-            DefaultModuleIdentifier.newId(nullToEmpty(dependencyConstraint.getGroup()), nullToEmpty(dependencyConstraint.getName())), dependencyConstraint.getVersionConstraint(), dependencyConstraint.getAttributes(), ImmutableList.of());
-    }
-
-    private IvyDependencyDescriptorFactory findFactoryForDependency(ModuleDependency dependency) {
-        for (IvyDependencyDescriptorFactory ivyDependencyDescriptorFactory : dependencyDescriptorFactories) {
-            if (ivyDependencyDescriptorFactory.canConvert(dependency)) {
-                return ivyDependencyDescriptorFactory;
-            }
-        }
-        throw new InvalidUserDataException("Can't map dependency of type: " + dependency.getClass());
-    }
-
-    private String nullToEmpty(@Nullable String input) {
-        return input == null ? "" : input;
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyMetadataFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyMetadataFactory.java
new file mode 100644
index 0000000..3034b2b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyMetadataFactory.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2007-2008 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.DependencyConstraint;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
+import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependencyConstraint;
+import org.gradle.api.internal.artifacts.dependencies.DependencyConstraintInternal;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
+import org.gradle.internal.component.model.LocalComponentDependencyMetadata;
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
+import org.gradle.util.internal.WrapUtil;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultDependencyMetadataFactory implements DependencyMetadataFactory {
+    private final List<DependencyMetadataConverter> dependencyDescriptorFactories;
+
+    public DefaultDependencyMetadataFactory(DependencyMetadataConverter... dependencyDescriptorFactories) {
+        this.dependencyDescriptorFactories = WrapUtil.toList(dependencyDescriptorFactories);
+    }
+
+    @Override
+    public LocalOriginDependencyMetadata createDependencyMetadata(ComponentIdentifier componentId, @Nullable String clientConfiguration, @Nullable AttributeContainer attributes, ModuleDependency dependency) {
+        DependencyMetadataConverter factoryInternal = findFactoryForDependency(dependency);
+        return factoryInternal.createDependencyMetadata(componentId, clientConfiguration, attributes, dependency);
+    }
+
+    @Override
+    public LocalOriginDependencyMetadata createDependencyConstraintMetadata(ComponentIdentifier componentId, String clientConfiguration, AttributeContainer attributes, DependencyConstraint dependencyConstraint) {
+        ComponentSelector selector = createSelector(dependencyConstraint);
+        return new LocalComponentDependencyMetadata(componentId, selector, clientConfiguration, attributes, dependencyConstraint.getAttributes(), null,
+                Collections.emptyList(), Collections.emptyList(), ((DependencyConstraintInternal)dependencyConstraint).isForce(), false, false, true, false, dependencyConstraint.getReason());
+    }
+
+    private ComponentSelector createSelector(DependencyConstraint dependencyConstraint) {
+        if (dependencyConstraint instanceof DefaultProjectDependencyConstraint) {
+            return DefaultProjectComponentSelector.newSelector(((DefaultProjectDependencyConstraint) dependencyConstraint).getProjectDependency().getDependencyProject());
+        }
+        return DefaultModuleComponentSelector.newSelector(
+            DefaultModuleIdentifier.newId(nullToEmpty(dependencyConstraint.getGroup()), nullToEmpty(dependencyConstraint.getName())), dependencyConstraint.getVersionConstraint(), dependencyConstraint.getAttributes(), ImmutableList.of());
+    }
+
+    private DependencyMetadataConverter findFactoryForDependency(ModuleDependency dependency) {
+        for (DependencyMetadataConverter dependencyMetadataConverter : dependencyDescriptorFactories) {
+            if (dependencyMetadataConverter.canConvert(dependency)) {
+                return dependencyMetadataConverter;
+            }
+        }
+        throw new InvalidUserDataException("Can't map dependency of type: " + dependency.getClass());
+    }
+
+    private String nullToEmpty(@Nullable String input) {
+        return input == null ? "" : input;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultLocalConfigurationMetadataBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultLocalConfigurationMetadataBuilder.java
index 19ccfaf..a7e6560 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultLocalConfigurationMetadataBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultLocalConfigurationMetadataBuilder.java
@@ -15,69 +15,285 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.DependencyConstraint;
 import org.gradle.api.artifacts.ExcludeRule;
 import org.gradle.api.artifacts.FileCollectionDependency;
 import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.attributes.Category;
+import org.gradle.api.capabilities.Capability;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.configurations.Configurations;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationsProvider;
 import org.gradle.api.internal.artifacts.dependencies.SelfResolvingDependencyInternal;
-import org.gradle.api.internal.attributes.AttributeContainerInternal;
+import org.gradle.api.internal.attributes.AttributeValue;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
-import org.gradle.internal.component.local.model.BuildableLocalConfigurationMetadata;
+import org.gradle.internal.DisplayName;
+import org.gradle.internal.component.external.model.ImmutableCapabilities;
+import org.gradle.internal.component.local.model.DefaultLocalConfigurationMetadata;
+import org.gradle.internal.component.local.model.LocalComponentMetadata;
+import org.gradle.internal.component.local.model.LocalConfigurationMetadata;
 import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
+import org.gradle.internal.component.local.model.LocalVariantMetadata;
+import org.gradle.internal.component.model.ComponentConfigurationIdentifier;
+import org.gradle.internal.component.model.ExcludeMetadata;
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
+import org.gradle.internal.component.model.VariantResolveMetadata;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
+import org.gradle.internal.model.ModelContainer;
 
 import javax.annotation.Nullable;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Queue;
+import java.util.Set;
 
+/**
+ * Encapsulates all logic required to build a {@link LocalConfigurationMetadata} from a
+ * {@link ConfigurationInternal}. Utilizes caching to prevent unnecessary duplicate conversions
+ * between DSL and internal metadata types.
+ */
 public class DefaultLocalConfigurationMetadataBuilder implements LocalConfigurationMetadataBuilder {
-    private final DependencyDescriptorFactory dependencyDescriptorFactory;
+    private final DependencyMetadataFactory dependencyMetadataFactory;
     private final ExcludeRuleConverter excludeRuleConverter;
 
-    public DefaultLocalConfigurationMetadataBuilder(DependencyDescriptorFactory dependencyDescriptorFactory,
-                                                    ExcludeRuleConverter excludeRuleConverter) {
-        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
+    public DefaultLocalConfigurationMetadataBuilder(
+        DependencyMetadataFactory dependencyMetadataFactory,
+        ExcludeRuleConverter excludeRuleConverter
+    ) {
+        this.dependencyMetadataFactory = dependencyMetadataFactory;
         this.excludeRuleConverter = excludeRuleConverter;
     }
 
     @Override
-    public void addDependenciesAndExcludes(BuildableLocalConfigurationMetadata metaData, ConfigurationInternal configuration) {
-        // Run any actions to add/modify dependencies
-        configuration.runDependencyActions();
+    public LocalConfigurationMetadata create(
+        ConfigurationInternal configuration,
+        ConfigurationsProvider configurationsProvider,
+        LocalComponentMetadata parent,
+        DependencyCache dependencyCache,
+        ModelContainer<?> model,
+        CalculatedValueContainerFactory calculatedValueContainerFactory
+    ) {
+        ComponentIdentifier componentId = parent.getId();
+        ComponentConfigurationIdentifier configurationIdentifier = new ComponentConfigurationIdentifier(componentId, configuration.getName());
 
-        addDependencies(metaData, configuration);
-        addDependencyConstraints(metaData, configuration);
-        addExcludeRules(metaData, configuration);
+        // Collect all artifacts and sub-variants.
+        ImmutableList.Builder<PublishArtifact> artifactBuilder = ImmutableList.builder();
+        ImmutableSet.Builder<LocalVariantMetadata> variantsBuilder = ImmutableSet.builder();
+        configuration.collectVariants(new ConfigurationInternal.VariantVisitor() {
+            @Override
+            public void visitArtifacts(Collection<? extends PublishArtifact> artifacts) {
+                artifactBuilder.addAll(artifacts);
+            }
+
+            @Override
+            public void visitOwnVariant(DisplayName displayName, ImmutableAttributes attributes, Collection<? extends Capability> capabilities, Collection<? extends PublishArtifact> artifacts) {
+                variantsBuilder.add(new LocalVariantMetadata(configuration.getName(), configurationIdentifier, componentId, displayName, attributes, artifacts, ImmutableCapabilities.of(capabilities), model, calculatedValueContainerFactory));
+            }
+
+            @Override
+            public void visitChildVariant(String name, DisplayName displayName, ImmutableAttributes attributes, Collection<? extends Capability> capabilities, Collection<? extends PublishArtifact> artifacts) {
+                variantsBuilder.add(new LocalVariantMetadata(configuration.getName() + "-" + name, new NestedVariantIdentifier(configurationIdentifier, name), componentId, displayName, attributes, artifacts, ImmutableCapabilities.of(capabilities), model, calculatedValueContainerFactory));
+            }
+        });
+
+        // We must call this before collecting dependency state, since dependency actions may modify the hierarchy.
+        runDependencyActionsInHierarchy(configuration);
+
+        // Collect all dependencies and excludes in hierarchy.
+        ImmutableAttributes attributes = configuration.getAttributes().asImmutable();
+        ImmutableSet<String> hierarchy = Configurations.getNames(configuration.getHierarchy());
+        DependencyState dependencies = getState(configurationsProvider, hierarchy, componentId, dependencyCache);
+
+        return new DefaultLocalConfigurationMetadata(
+            configuration.getName(),
+            configuration.getDescription(),
+            componentId,
+            configuration.isVisible(),
+            configuration.isTransitive(),
+            hierarchy,
+            attributes,
+            ImmutableCapabilities.of(Configurations.collectCapabilities(configuration, Sets.newHashSet(), Sets.newHashSet())),
+            configuration.isCanBeConsumed(),
+            configuration.isDeprecatedForConsumption(),
+            configuration.isCanBeResolved(),
+            maybeForceDependencies(dependencies.dependencies, attributes),
+            dependencies.files,
+            dependencies.excludes,
+            variantsBuilder.build(),
+            artifactBuilder.build(),
+            model,
+            calculatedValueContainerFactory,
+            parent
+        );
     }
 
-    private void addDependencies(BuildableLocalConfigurationMetadata configurationMetadata, ConfigurationInternal configuration) {
-        AttributeContainerInternal attributes = configuration.getAttributes();
-        for (Dependency dependency : configuration.getDependencies()) {
-            if (dependency instanceof ModuleDependency) {
-                ModuleDependency moduleDependency = (ModuleDependency) dependency;
-                configurationMetadata.addDependency(dependencyDescriptorFactory.createDependencyDescriptor(configurationMetadata.getComponentId(), configuration.getName(), attributes, moduleDependency));
-            } else if (dependency instanceof FileCollectionDependency) {
-                final FileCollectionDependency fileDependency = (FileCollectionDependency) dependency;
-                configurationMetadata.addFiles(new DefaultLocalFileDependencyMetadata(fileDependency));
-            } else {
-                throw new IllegalArgumentException("Cannot convert dependency " + dependency + " to local component dependency metadata.");
+    /**
+     * Runs the dependency actions for all configurations in {@code conf}'s hierarchy.
+     *
+     * <p>Specifically handles the case where {@link Configuration#extendsFrom} is called during the
+     * dependency action execution.</p>
+     */
+    private static void runDependencyActionsInHierarchy(ConfigurationInternal conf) {
+        Set<Configuration> seen = new HashSet<>();
+        Queue<Configuration> remaining = new ArrayDeque<>();
+        remaining.add(conf);
+        seen.add(conf);
+
+        while (!remaining.isEmpty()) {
+            Configuration current = remaining.remove();
+            ((ConfigurationInternal) current).runDependencyActions();
+
+            for (Configuration parent : current.getExtendsFrom()) {
+                if (seen.add(parent)) {
+                    remaining.add(parent);
+                }
             }
         }
     }
 
-    private void addDependencyConstraints(BuildableLocalConfigurationMetadata configurationMetadata, ConfigurationInternal configuration) {
-        AttributeContainerInternal attributes = configuration.getAttributes();
+    /**
+     * Collect all dependencies and excludes of all configurations in the provided {@code hierarchy}.
+     */
+    private DependencyState getState(
+        ConfigurationsProvider configurations,
+        ImmutableSet<String> hierarchy,
+        ComponentIdentifier componentId,
+        DependencyCache cache
+    ) {
+        ImmutableList.Builder<LocalOriginDependencyMetadata> dependencies = ImmutableList.builder();
+        ImmutableSet.Builder<LocalFileDependencyMetadata> files = ImmutableSet.builder();
+        ImmutableList.Builder<ExcludeMetadata> excludes = ImmutableList.builder();
+
+        configurations.visitAll(config -> {
+            if (hierarchy.contains(config.getName())) {
+                DependencyState defined = getDefinedState(config, componentId, cache);
+                dependencies.addAll(defined.dependencies);
+                files.addAll(defined.files);
+                excludes.addAll(defined.excludes);
+            }
+        });
+
+        return new DependencyState(dependencies.build(), files.build(), excludes.build());
+    }
+
+    /**
+     * Get the defined dependencies and excludes for {@code configuration}, while also caching the result.
+     */
+    private DependencyState getDefinedState(ConfigurationInternal configuration, ComponentIdentifier componentId, DependencyCache cache) {
+        return cache.computeIfAbsent(configuration, componentId, this::doGetDefinedState);
+    }
+
+    /**
+     * Calculate the defined dependencies and excludes for {@code configuration}, while converting the
+     * DSL representation to the internal representation.
+     */
+    @SuppressWarnings("deprecation")
+    private DependencyState doGetDefinedState(ConfigurationInternal configuration, ComponentIdentifier componentId) {
+
+        AttributeContainer attributes = configuration.getAttributes();
+
+        ImmutableList.Builder<LocalOriginDependencyMetadata> dependencyBuilder = ImmutableList.builder();
+        ImmutableSet.Builder<LocalFileDependencyMetadata> fileBuilder = ImmutableSet.builder();
+        ImmutableList.Builder<ExcludeMetadata> excludeBuilder = ImmutableList.builder();
+
+        // Configurations that are not declarable should not have dependencies or constraints present,
+        // but we need to allow dependencies to be checked to avoid emitting many warnings when the
+        // Kotlin plugin is applied.  This is because applying the Kotlin plugin adds dependencies
+        // to the testRuntimeClasspath configuration, which is not declarable.
+        // To demonstrate this, add a check for configuration.isCanBeDeclared() && configuration.assertHasNoDeclarations() if not
+        // and run tests such as KotlinDslPluginTest, or the building-kotlin-applications samples and you'll configurations which
+        // aren't declarable but have declared dependencies present.
+        for (Dependency dependency : configuration.getDependencies()) {
+            if (dependency instanceof ModuleDependency) {
+                ModuleDependency moduleDependency = (ModuleDependency) dependency;
+                dependencyBuilder.add(dependencyMetadataFactory.createDependencyMetadata(
+                        componentId, configuration.getName(), attributes, moduleDependency
+                ));
+            } else if (dependency instanceof FileCollectionDependency) {
+                final FileCollectionDependency fileDependency = (FileCollectionDependency) dependency;
+                fileBuilder.add(new DefaultLocalFileDependencyMetadata(fileDependency));
+            } else {
+                throw new IllegalArgumentException("Cannot convert dependency " + dependency + " to local component dependency metadata.");
+            }
+        }
+
+        // Configurations that are not declarable should not have dependencies or constraints present,
+        // no smoke-tested plugins add constraints, so we should be able to safely throw an exception here
+        // if we find any - but we'll avoid doing so for now to avoid breaking any existing builds and to
+        // remain consistent with the behavior for dependencies.
         for (DependencyConstraint dependencyConstraint : configuration.getDependencyConstraints()) {
-            configurationMetadata.addDependency(dependencyDescriptorFactory.createDependencyConstraintDescriptor(configurationMetadata.getComponentId(), configuration.getName(), attributes, dependencyConstraint));
+            dependencyBuilder.add(dependencyMetadataFactory.createDependencyConstraintMetadata(
+                    componentId, configuration.getName(), attributes, dependencyConstraint)
+            );
         }
-    }
 
-    private void addExcludeRules(BuildableLocalConfigurationMetadata configurationMetadata, ConfigurationInternal configuration) {
         for (ExcludeRule excludeRule : configuration.getExcludeRules()) {
-            configurationMetadata.addExclude(excludeRuleConverter.convertExcludeRule(excludeRule));
+            excludeBuilder.add(excludeRuleConverter.convertExcludeRule(excludeRule));
+        }
+
+        return new DependencyState(dependencyBuilder.build(), fileBuilder.build(), excludeBuilder.build());
+    }
+
+    private static ImmutableList<LocalOriginDependencyMetadata> maybeForceDependencies(
+        ImmutableList<LocalOriginDependencyMetadata> dependencies,
+        ImmutableAttributes attributes
+    ) {
+        AttributeValue<Category> attributeValue = attributes.findEntry(Category.CATEGORY_ATTRIBUTE);
+        if (!attributeValue.isPresent() || !attributeValue.get().getName().equals(Category.ENFORCED_PLATFORM)) {
+            return dependencies;
+        }
+
+        // Need to wrap all dependencies to force them.
+        ImmutableList.Builder<LocalOriginDependencyMetadata> forcedDependencies = ImmutableList.builder();
+        for (LocalOriginDependencyMetadata rawDependency : dependencies) {
+            forcedDependencies.add(rawDependency.forced());
+        }
+        return forcedDependencies.build();
+    }
+
+    /**
+     * {@link VariantResolveMetadata.Identifier} implementation for non-implicit sub-variants of a configuration.
+     */
+    private static class NestedVariantIdentifier implements VariantResolveMetadata.Identifier {
+        private final VariantResolveMetadata.Identifier parent;
+        private final String name;
+
+        public NestedVariantIdentifier(VariantResolveMetadata.Identifier parent, String name) {
+            this.parent = parent;
+            this.name = name;
+        }
+
+        @Override
+        public int hashCode() {
+            return parent.hashCode() ^ name.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            NestedVariantIdentifier other = (NestedVariantIdentifier) obj;
+            return parent.equals(other.parent) && name.equals(other.name);
         }
     }
 
+    /**
+     * Default implementation of {@link LocalFileDependencyMetadata}.
+     */
     private static class DefaultLocalFileDependencyMetadata implements LocalFileDependencyMetadata {
         private final FileCollectionDependency fileDependency;
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java
deleted file mode 100644
index 25deca2..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyDescriptorFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2013 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.gradle.api.artifacts.DependencyConstraint;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
-
-import javax.annotation.Nullable;
-
-public interface DependencyDescriptorFactory {
-    LocalOriginDependencyMetadata createDependencyDescriptor(ComponentIdentifier componentId, @Nullable String clientConfiguration, @Nullable AttributeContainer attributes, ModuleDependency dependency);
-    LocalOriginDependencyMetadata createDependencyConstraintDescriptor(ComponentIdentifier componentId, String clientConfiguration, AttributeContainer attributes, DependencyConstraint dependencyConstraint);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyMetadataConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyMetadataConverter.java
new file mode 100644
index 0000000..93a61f0
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyMetadataConverter.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2007-2008 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
+
+import javax.annotation.Nullable;
+
+public interface DependencyMetadataConverter {
+    LocalOriginDependencyMetadata createDependencyMetadata(ComponentIdentifier componentId, @Nullable String clientConfiguration, @Nullable AttributeContainer attributes, ModuleDependency dependency);
+
+    boolean canConvert(ModuleDependency dependency);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyMetadataFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyMetadataFactory.java
new file mode 100644
index 0000000..ce1e945
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DependencyMetadataFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.gradle.api.artifacts.DependencyConstraint;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
+
+import javax.annotation.Nullable;
+
+public interface DependencyMetadataFactory {
+    LocalOriginDependencyMetadata createDependencyMetadata(ComponentIdentifier componentId, @Nullable String clientConfiguration, @Nullable AttributeContainer attributes, ModuleDependency dependency);
+    LocalOriginDependencyMetadata createDependencyConstraintMetadata(ComponentIdentifier componentId, String clientConfiguration, AttributeContainer attributes, DependencyConstraint dependencyConstraint);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyMetadataConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyMetadataConverter.java
new file mode 100644
index 0000000..8df14b3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyMetadataConverter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2009 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.gradle.api.artifacts.ExternalModuleDependency;
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
+import org.gradle.api.internal.artifacts.VersionConstraintInternal;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetadataWrapper;
+import org.gradle.internal.component.model.ExcludeMetadata;
+import org.gradle.internal.component.model.LocalComponentDependencyMetadata;
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public class ExternalModuleDependencyMetadataConverter extends AbstractDependencyMetadataConverter {
+    public ExternalModuleDependencyMetadataConverter(ExcludeRuleConverter excludeRuleConverter) {
+        super(excludeRuleConverter);
+    }
+
+    @Override
+    public LocalOriginDependencyMetadata createDependencyMetadata(ComponentIdentifier componentId, @Nullable String clientConfiguration, @Nullable AttributeContainer clientAttributes, ModuleDependency dependency) {
+        ExternalModuleDependency externalModuleDependency = (ExternalModuleDependency) dependency;
+        boolean force = externalModuleDependency.isForce();
+        boolean changing = externalModuleDependency.isChanging();
+        boolean transitive = externalModuleDependency.isTransitive();
+
+        ModuleComponentSelector selector = DefaultModuleComponentSelector.newSelector(
+                DefaultModuleIdentifier.newId(nullToEmpty(dependency.getGroup()), nullToEmpty(dependency.getName())),
+                ((VersionConstraintInternal) externalModuleDependency.getVersionConstraint()).asImmutable(),
+                dependency.getAttributes(),
+                dependency.getRequestedCapabilities());
+
+        List<ExcludeMetadata> excludes = convertExcludeRules(dependency.getExcludeRules());
+        LocalComponentDependencyMetadata dependencyMetaData = new LocalComponentDependencyMetadata(
+                componentId, selector, clientConfiguration, clientAttributes,
+                dependency.getAttributes(),
+                dependency.getTargetConfiguration(),
+                convertArtifacts(dependency.getArtifacts()),
+                excludes, force, changing, transitive, false, dependency.isEndorsingStrictVersions(), dependency.getReason());
+        return new DslOriginDependencyMetadataWrapper(dependencyMetaData, dependency);
+    }
+
+    private String nullToEmpty(@Nullable String input) {
+        return input == null ? "" : input;
+    }
+
+    @Override
+    public boolean canConvert(ModuleDependency dependency) {
+        return dependency instanceof ExternalModuleDependency;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java
deleted file mode 100644
index 4832ee6..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleIvyDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2009 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.gradle.api.artifacts.ExternalModuleDependency;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ModuleComponentSelector;
-import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
-import org.gradle.api.internal.artifacts.VersionConstraintInternal;
-import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
-import org.gradle.internal.component.local.model.DslOriginDependencyMetadataWrapper;
-import org.gradle.internal.component.model.ExcludeMetadata;
-import org.gradle.internal.component.model.LocalComponentDependencyMetadata;
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
-
-import javax.annotation.Nullable;
-import java.util.List;
-
-public class ExternalModuleIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
-    public ExternalModuleIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
-        super(excludeRuleConverter);
-    }
-
-    @Override
-    public LocalOriginDependencyMetadata createDependencyDescriptor(ComponentIdentifier componentId, @Nullable String clientConfiguration, @Nullable AttributeContainer clientAttributes, ModuleDependency dependency) {
-        ExternalModuleDependency externalModuleDependency = (ExternalModuleDependency) dependency;
-        boolean force = externalModuleDependency.isForce();
-        boolean changing = externalModuleDependency.isChanging();
-        boolean transitive = externalModuleDependency.isTransitive();
-
-        ModuleComponentSelector selector = DefaultModuleComponentSelector.newSelector(
-                DefaultModuleIdentifier.newId(nullToEmpty(dependency.getGroup()), nullToEmpty(dependency.getName())),
-                ((VersionConstraintInternal) externalModuleDependency.getVersionConstraint()).asImmutable(),
-                dependency.getAttributes(),
-                dependency.getRequestedCapabilities());
-
-        List<ExcludeMetadata> excludes = convertExcludeRules(dependency.getExcludeRules());
-        LocalComponentDependencyMetadata dependencyMetaData = new LocalComponentDependencyMetadata(
-                componentId, selector, clientConfiguration, clientAttributes,
-                dependency.getAttributes(),
-                dependency.getTargetConfiguration(),
-                convertArtifacts(dependency.getArtifacts()),
-                excludes, force, changing, transitive, false, dependency.isEndorsingStrictVersions(), dependency.getReason());
-        return new DslOriginDependencyMetadataWrapper(dependencyMetaData, dependency);
-    }
-
-    private String nullToEmpty(@Nullable String input) {
-        return input == null ? "" : input;
-    }
-
-    @Override
-    public boolean canConvert(ModuleDependency dependency) {
-        return dependency instanceof ExternalModuleDependency;
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.java
deleted file mode 100644
index e5a3f8d..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/IvyDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2007-2008 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
-
-import javax.annotation.Nullable;
-
-public interface IvyDependencyDescriptorFactory {
-    LocalOriginDependencyMetadata createDependencyDescriptor(ComponentIdentifier componentId, @Nullable String clientConfiguration, @Nullable AttributeContainer attributes, ModuleDependency dependency);
-
-    boolean canConvert(ModuleDependency dependency);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/LocalConfigurationMetadataBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/LocalConfigurationMetadataBuilder.java
index 83cad4a..a4fd945 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/LocalConfigurationMetadataBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/LocalConfigurationMetadataBuilder.java
@@ -15,9 +15,86 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.internal.component.local.model.BuildableLocalConfigurationMetadata;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationsProvider;
+import org.gradle.internal.component.local.model.LocalComponentMetadata;
+import org.gradle.internal.component.local.model.LocalConfigurationMetadata;
+import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
+import org.gradle.internal.component.model.ExcludeMetadata;
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
+import org.gradle.internal.model.ModelContainer;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+/**
+ * Builds {@link LocalConfigurationMetadata} instances from {@link ConfigurationInternal}s, while
+ * caching intermediary dependency and exclude state.
+ */
 public interface LocalConfigurationMetadataBuilder {
-    void addDependenciesAndExcludes(BuildableLocalConfigurationMetadata metaData, ConfigurationInternal configuration);
+
+    /**
+     * TODO: Ideally, building a configuration's metadata should not require the parent component
+     * reference. We currently need it in order to traverse the configuration hierarchy for building
+     * artifact metadata.
+     */
+    LocalConfigurationMetadata create(
+        ConfigurationInternal configuration,
+        ConfigurationsProvider configurationsProvider,
+        LocalComponentMetadata parent,
+        DependencyCache dependencyCache,
+        ModelContainer<?> model,
+        CalculatedValueContainerFactory calculatedValueContainerFactory
+    );
+
+    /**
+     * A cache of the defined dependencies for dependency configurations. This tracks the cached internal
+     * dependency representations for these types so when constructing leaf configuration metadata
+     * (resolvable and consumable), these conversions do not need to be executed multiple times.
+     */
+    class DependencyCache {
+        private final Map<String, DefaultLocalConfigurationMetadataBuilder.DependencyState> cache = new HashMap<>();
+
+        public DefaultLocalConfigurationMetadataBuilder.DependencyState computeIfAbsent(
+            ConfigurationInternal configuration,
+            ComponentIdentifier componentId,
+            BiFunction<ConfigurationInternal, ComponentIdentifier, DefaultLocalConfigurationMetadataBuilder.DependencyState> factory
+        ) {
+            DependencyState state = cache.get(configuration.getName());
+            if (state == null) {
+                state = factory.apply(configuration, componentId);
+                cache.put(configuration.getName(), state);
+            }
+            return state;
+        }
+
+        public void invalidate() {
+            cache.clear();
+        }
+    }
+
+    /**
+     * The immutable state of a configuration's dependencies and excludes. This type tracks
+     * the internal representations, after they have been converted from their DSL representations.
+     */
+    class DependencyState {
+        public final ImmutableList<LocalOriginDependencyMetadata> dependencies;
+        public final ImmutableSet<LocalFileDependencyMetadata> files;
+        public final ImmutableList<ExcludeMetadata> excludes;
+
+        public DependencyState(
+            ImmutableList<LocalOriginDependencyMetadata> dependencies,
+            ImmutableSet<LocalFileDependencyMetadata> files,
+            ImmutableList<ExcludeMetadata> excludes
+        ) {
+            this.dependencies = dependencies;
+            this.files = files;
+            this.excludes = excludes;
+        }
+    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyMetadataConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyMetadataConverter.java
new file mode 100644
index 0000000..d9dd35c
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyMetadataConverter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
+
+import org.gradle.api.artifacts.ModuleDependency;
+import org.gradle.api.artifacts.ProjectDependency;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.artifacts.dependencies.ProjectDependencyInternal;
+import org.gradle.api.internal.attributes.AttributeContainerInternal;
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
+import org.gradle.internal.component.local.model.DslOriginDependencyMetadataWrapper;
+import org.gradle.internal.component.model.ExcludeMetadata;
+import org.gradle.internal.component.model.LocalComponentDependencyMetadata;
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public class ProjectDependencyMetadataConverter extends AbstractDependencyMetadataConverter {
+
+    public ProjectDependencyMetadataConverter(ExcludeRuleConverter excludeRuleConverter) {
+        super(excludeRuleConverter);
+    }
+
+    @Override
+    public LocalOriginDependencyMetadata createDependencyMetadata(ComponentIdentifier componentId, @Nullable String clientConfiguration, AttributeContainer clientAttributes, ModuleDependency dependency) {
+        ProjectDependencyInternal projectDependency = (ProjectDependencyInternal) dependency;
+        ComponentSelector selector = DefaultProjectComponentSelector.newSelector(projectDependency.getDependencyProject(),
+                ((AttributeContainerInternal)projectDependency.getAttributes()).asImmutable(),
+                projectDependency.getRequestedCapabilities());
+
+        List<ExcludeMetadata> excludes = convertExcludeRules(dependency.getExcludeRules());
+        LocalComponentDependencyMetadata dependencyMetaData = new LocalComponentDependencyMetadata(
+            componentId,
+            selector,
+            clientConfiguration,
+            clientAttributes,
+            dependency.getAttributes(),
+            projectDependency.getTargetConfiguration(),
+            convertArtifacts(dependency.getArtifacts()),
+            excludes,
+            false, false, dependency.isTransitive(), false, dependency.isEndorsingStrictVersions(), dependency.getReason());
+        return new DslOriginDependencyMetadataWrapper(dependencyMetaData, dependency);
+    }
+
+    @Override
+    public boolean canConvert(ModuleDependency dependency) {
+        return dependency instanceof ProjectDependency;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java
deleted file mode 100644
index c52182e..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectIvyDependencyDescriptorFactory.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2013 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies;
-
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.ProjectDependency;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.dependencies.ProjectDependencyInternal;
-import org.gradle.api.internal.attributes.AttributeContainerInternal;
-import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
-import org.gradle.internal.component.local.model.DslOriginDependencyMetadataWrapper;
-import org.gradle.internal.component.model.ExcludeMetadata;
-import org.gradle.internal.component.model.LocalComponentDependencyMetadata;
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
-
-import javax.annotation.Nullable;
-import java.util.List;
-
-public class ProjectIvyDependencyDescriptorFactory extends AbstractIvyDependencyDescriptorFactory {
-
-    public ProjectIvyDependencyDescriptorFactory(ExcludeRuleConverter excludeRuleConverter) {
-        super(excludeRuleConverter);
-    }
-
-    @Override
-    public LocalOriginDependencyMetadata createDependencyDescriptor(ComponentIdentifier componentId, @Nullable String clientConfiguration, AttributeContainer clientAttributes, ModuleDependency dependency) {
-        ProjectDependencyInternal projectDependency = (ProjectDependencyInternal) dependency;
-        ComponentSelector selector = DefaultProjectComponentSelector.newSelector(projectDependency.getDependencyProject(),
-                ((AttributeContainerInternal)projectDependency.getAttributes()).asImmutable(),
-                projectDependency.getRequestedCapabilities());
-
-        List<ExcludeMetadata> excludes = convertExcludeRules(dependency.getExcludeRules());
-        LocalComponentDependencyMetadata dependencyMetaData = new LocalComponentDependencyMetadata(
-            componentId,
-            selector,
-            clientConfiguration,
-            clientAttributes,
-            dependency.getAttributes(),
-            projectDependency.getTargetConfiguration(),
-            convertArtifacts(dependency.getArtifacts()),
-            excludes,
-            false, false, dependency.isTransitive(), false, dependency.isEndorsingStrictVersions(), dependency.getReason());
-        return new DslOriginDependencyMetadataWrapper(dependencyMetaData, dependency);
-    }
-
-    @Override
-    public boolean canConvert(ModuleDependency dependency) {
-        return dependency instanceof ProjectDependency;
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultLocalComponentRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultLocalComponentRegistry.java
index 04b24ac..4b4b08d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultLocalComponentRegistry.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultLocalComponentRegistry.java
@@ -55,13 +55,20 @@ public DefaultLocalComponentRegistry(
 
     @Override
     public LocalComponentGraphResolveState getComponent(ProjectComponentIdentifier projectIdentifier) {
-        CalculatedValueContainer<LocalComponentGraphResolveState, ?> valueContainer = projects.computeIfAbsent(projectIdentifier, projectComponentIdentifier -> {
-            ProjectState projectState = projectStateRegistry.stateFor(projectIdentifier);
-            return calculatedValueContainerFactory.create(Describables.of("metadata of", projectIdentifier), new MetadataSupplier(projectState));
-        });
-        // Calculate the value after adding the entry to the map, so that the value container can take care of thread synchronization
-        valueContainer.finalizeIfNotAlready();
-        return valueContainer.get();
+        ProjectState projectState = projectStateRegistry.stateFor(projectIdentifier);
+        if (isLocalProject(projectIdentifier)) {
+            CalculatedValueContainer<LocalComponentGraphResolveState, ?> valueContainer = projects.computeIfAbsent(projectIdentifier, projectComponentIdentifier ->
+                calculatedValueContainerFactory.create(Describables.of("metadata of", projectIdentifier), new MetadataSupplier(projectState)));
+            // Calculate the value after adding the entry to the map, so that the value container can take care of thread synchronization
+            valueContainer.finalizeIfNotAlready();
+            return valueContainer.get();
+        } else {
+            return otherBuildProvider.getComponent(projectState);
+        }
+    }
+
+    private boolean isLocalProject(ProjectComponentIdentifier projectIdentifier) {
+        return projectIdentifier.getBuild().equals(thisBuild);
     }
 
     @Override
@@ -78,15 +85,7 @@ public MetadataSupplier(ProjectState projectState) {
 
         @Override
         public LocalComponentGraphResolveState calculateValue(NodeExecutionContext context) {
-            if (isLocalProject(projectState.getComponentIdentifier())) {
-                return provider.getComponent(projectState);
-            } else {
-                return otherBuildProvider.getComponent(projectState);
-            }
-        }
-
-        private boolean isLocalProject(ProjectComponentIdentifier projectIdentifier) {
-            return projectIdentifier.getBuild().equals(thisBuild);
+            return provider.getComponent(projectState);
         }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectLocalComponentProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectLocalComponentProvider.java
index 9fe3c5b..b0dcb66 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectLocalComponentProvider.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/DefaultProjectLocalComponentProvider.java
@@ -15,19 +15,19 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.projectmodule;
 
-import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
 import org.gradle.api.internal.artifacts.Module;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.LocalComponentMetadataBuilder;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.project.ProjectState;
-import org.gradle.internal.Cast;
-import org.gradle.internal.component.local.model.DefaultLocalComponentGraphResolveState;
 import org.gradle.internal.component.local.model.DefaultLocalComponentMetadata;
 import org.gradle.internal.component.local.model.LocalComponentGraphResolveState;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory;
 import org.gradle.internal.component.local.model.LocalComponentMetadata;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 
@@ -39,16 +39,19 @@
  * <p>Currently, the metadata for a component is different based on whether it is consumed from the producing build or from another build. This difference should go away.
  */
 public class DefaultProjectLocalComponentProvider implements LocalComponentProvider {
-    private final LocalComponentMetadataBuilder metadataBuilder;
+    private final LocalConfigurationMetadataBuilder metadataBuilder;
+    private final LocalComponentGraphResolveStateFactory resolveStateFactory;
     private final ImmutableModuleIdentifierFactory moduleIdentifierFactory;
     private final CalculatedValueContainerFactory calculatedValueContainerFactory;
 
     public DefaultProjectLocalComponentProvider(
-        LocalComponentMetadataBuilder metadataBuilder,
+        LocalConfigurationMetadataBuilder metadataBuilder,
+        LocalComponentGraphResolveStateFactory resolveStateFactory,
         ImmutableModuleIdentifierFactory moduleIdentifierFactory,
         CalculatedValueContainerFactory calculatedValueContainerFactory
     ) {
         this.metadataBuilder = metadataBuilder;
+        this.resolveStateFactory = resolveStateFactory;
         this.moduleIdentifierFactory = moduleIdentifierFactory;
         this.calculatedValueContainerFactory = calculatedValueContainerFactory;
     }
@@ -58,17 +61,21 @@ public DefaultProjectLocalComponentProvider(
     public LocalComponentGraphResolveState getComponent(ProjectState projectState) {
         projectState.ensureConfigured();
         LocalComponentMetadata metadata = projectState.fromMutableState(p -> getLocalComponentMetadata(projectState, p));
-        return new DefaultLocalComponentGraphResolveState(metadata);
+        return resolveStateFactory.stateFor(metadata);
     }
 
     private LocalComponentMetadata getLocalComponentMetadata(ProjectState projectState, ProjectInternal project) {
         Module module = project.getDependencyMetaDataProvider().getModule();
         ModuleVersionIdentifier moduleVersionIdentifier = moduleIdentifierFactory.moduleWithVersion(module.getGroup(), module.getName(), module.getVersion());
         ProjectComponentIdentifier componentIdentifier = projectState.getComponentIdentifier();
-        DefaultLocalComponentMetadata metaData = new DefaultLocalComponentMetadata(moduleVersionIdentifier, componentIdentifier, module.getStatus(), (AttributesSchemaInternal) project.getDependencies().getAttributesSchema(), projectState, calculatedValueContainerFactory);
-        for (Configuration configuration : project.getConfigurations()) {
-            metadataBuilder.addConfiguration(metaData, Cast.uncheckedCast(configuration));
-        }
-        return metaData;
+        AttributesSchemaInternal schema = (AttributesSchemaInternal) project.getDependencies().getAttributesSchema();
+
+        DefaultLocalComponentMetadata.ConfigurationsProviderMetadataFactory configurationMetadataFactory =
+            new DefaultLocalComponentMetadata.ConfigurationsProviderMetadataFactory(
+                (DefaultConfigurationContainer) project.getConfigurations(), metadataBuilder, projectState, calculatedValueContainerFactory);
+
+        project.getConfigurations().forEach(conf -> ((ConfigurationInternal) conf).preventFromFurtherMutation());
+
+        return new DefaultLocalComponentMetadata(moduleVersionIdentifier, componentIdentifier, module.getStatus(), schema, configurationMetadataFactory, null);
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java
index 384692c..5dea5c9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectArtifactResolver.java
@@ -17,7 +17,6 @@
 package org.gradle.api.internal.artifacts.ivyservice.projectmodule;
 
 import com.google.common.collect.Maps;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentArtifactIdentifier;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
 import org.gradle.api.internal.artifacts.DefaultResolvableArtifact;
@@ -31,8 +30,7 @@
 import org.gradle.internal.Describables;
 import org.gradle.internal.component.local.model.LocalComponentArtifactMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
-import org.gradle.internal.component.model.ModuleSources;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.model.CalculatedValue;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.model.ValueCalculator;
@@ -57,12 +55,12 @@ public ProjectArtifactResolver(ProjectStateRegistry projectStateRegistry, Calcul
     }
 
     @Override
-    public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+    public void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactResolveResult result) {
+    public void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result) {
         // NOTE: This isn't thread-safe because we're not locking around allResolvedArtifacts to ensure we're not inserting multiple resolvableArtifacts for
         // the same artifact id.
         //
@@ -70,13 +68,13 @@ public void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMe
         // This is not thread-safe because of lock juggling that happens for project state. When calculating the dependencies for an IDEA model, we can easily
         // deadlock when there are multiple projects that need to be locked at the same time.
         ResolvableArtifact resolvableArtifact = allResolvedArtifacts.get(artifact.getId());
-        if (resolvableArtifact==null) {
+        if (resolvableArtifact == null) {
             LocalComponentArtifactMetadata projectArtifact = (LocalComponentArtifactMetadata) artifact;
             ProjectComponentIdentifier projectId = (ProjectComponentIdentifier) artifact.getComponentId();
             File localArtifactFile = projectStateRegistry.stateFor(projectId).fromMutableState(p -> projectArtifact.getFile());
             if (localArtifactFile != null) {
                 CalculatedValue<File> artifactSource = calculatedValueContainerFactory.create(Describables.of(artifact.getId()), resolveArtifactLater(artifact));
-                resolvableArtifact = new DefaultResolvableArtifact(ownerId, artifact.getName(), artifact.getId(), context -> context.add(artifact.getBuildDependencies()), artifactSource, calculatedValueContainerFactory);
+                resolvableArtifact = new DefaultResolvableArtifact(component.getModuleVersionId(), artifact.getName(), artifact.getId(), context -> context.add(artifact.getBuildDependencies()), artifactSource, calculatedValueContainerFactory);
                 allResolvedArtifacts.put(artifact.getId(), resolvableArtifact);
             }
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java
index cd51d11..6b763c9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolver.java
@@ -15,11 +15,8 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.projectmodule;
 
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentSelector;
-import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ComponentResolvers;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet;
@@ -31,11 +28,10 @@
 import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
 import org.gradle.internal.component.local.model.LocalComponentGraphResolveState;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentArtifactResolveVariantState;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 import org.gradle.internal.resolve.resolver.ArtifactResolver;
 import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
@@ -51,18 +47,13 @@
 import javax.annotation.Nullable;
 import java.util.Set;
 
-@ServiceScope(Scopes.Project.class)
+@ServiceScope(Scopes.Build.class)
 public class ProjectDependencyResolver implements ComponentMetaDataResolver, DependencyToComponentIdResolver, ArtifactResolver, OriginArtifactSelector, ComponentResolvers {
     private final LocalComponentRegistry localComponentRegistry;
-    private final ComponentIdentifierFactory componentIdentifierFactory;
     private final ProjectArtifactResolver artifactResolver;
 
-    public ProjectDependencyResolver(LocalComponentRegistry localComponentRegistry,
-                                     ComponentIdentifierFactory componentIdentifierFactory,
-                                     ProjectArtifactResolver artifactResolver
-    ) {
+    public ProjectDependencyResolver(LocalComponentRegistry localComponentRegistry, ProjectArtifactResolver artifactResolver) {
         this.localComponentRegistry = localComponentRegistry;
-        this.componentIdentifierFactory = componentIdentifierFactory;
         this.artifactResolver = artifactResolver;
     }
 
@@ -88,9 +79,9 @@ public OriginArtifactSelector getArtifactSelector() {
 
     @Override
     public void resolve(DependencyMetadata dependency, VersionSelector acceptor, @Nullable VersionSelector rejector, BuildableComponentIdResolveResult result) {
-        if (dependency.getSelector() instanceof ProjectComponentSelector) {
-            ProjectComponentSelector selector = (ProjectComponentSelector) dependency.getSelector();
-            ProjectComponentIdentifier projectId = componentIdentifierFactory.createProjectComponentIdentifier(selector);
+        if (dependency.getSelector() instanceof DefaultProjectComponentSelector) {
+            DefaultProjectComponentSelector selector = (DefaultProjectComponentSelector) dependency.getSelector();
+            ProjectComponentIdentifier projectId = selector.toIdentifier();
             LocalComponentGraphResolveState component = localComponentRegistry.getComponent(projectId);
             if (component == null) {
                 result.failed(new ModuleVersionResolveException(selector, () -> projectId + " not found."));
@@ -123,7 +114,7 @@ public boolean isFetchingMetadataCheap(ComponentIdentifier identifier) {
     }
 
     @Override
-    public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+    public void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
         if (isProjectModule(component.getId())) {
             throw new UnsupportedOperationException("Resolving artifacts by type is not yet supported for project modules");
         }
@@ -131,7 +122,7 @@ public void resolveArtifactsWithType(ComponentResolveMetadata component, Artifac
 
     @Nullable
     @Override
-    public ArtifactSet resolveArtifacts(final ComponentResolveMetadata component, ComponentArtifactResolveVariantState allVariants, Set<ResolvedVariant> legacyVariants, final ExcludeSpec exclusions, final ImmutableAttributes overriddenAttributes) {
+    public ArtifactSet resolveArtifacts(ComponentArtifactResolveMetadata component, ComponentArtifactResolveVariantState allVariants, Set<ResolvedVariant> legacyVariants, final ExcludeSpec exclusions, final ImmutableAttributes overriddenAttributes) {
         if (isProjectModule(component.getId())) {
             return ArtifactSetFactory.createFromVariantMetadata(component.getId(), allVariants, legacyVariants, component.getAttributesSchema(), overriddenAttributes);
         } else {
@@ -140,9 +131,9 @@ public ArtifactSet resolveArtifacts(final ComponentResolveMetadata component, Co
     }
 
     @Override
-    public void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMetadata artifact, ModuleSources moduleSources, final BuildableArtifactResolveResult result) {
+    public void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result) {
         if (isProjectModule(artifact.getComponentId())) {
-            artifactResolver.resolveArtifact(ownerId, artifact, moduleSources, result);
+            artifactResolver.resolveArtifact(component, artifact, result);
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
index 64f076d..068d822 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolutionstrategy/DefaultResolutionStrategy.java
@@ -21,7 +21,6 @@
 import org.gradle.api.artifacts.ComponentSelection;
 import org.gradle.api.artifacts.ComponentSelectionRules;
 import org.gradle.api.artifacts.DependencyResolveDetails;
-import org.gradle.api.artifacts.DependencySubstitution;
 import org.gradle.api.artifacts.DependencySubstitutions;
 import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.artifacts.ResolutionStrategy;
@@ -29,6 +28,7 @@
 import org.gradle.api.capabilities.Capability;
 import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal;
 import org.gradle.api.internal.artifacts.ComponentSelectorConverter;
+import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory;
 import org.gradle.api.internal.artifacts.configurations.ConflictResolution;
@@ -42,8 +42,7 @@
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.Property;
-import org.gradle.internal.Actions;
-import org.gradle.internal.Cast;
+import org.gradle.internal.ImmutableActionSet;
 import org.gradle.internal.locking.NoOpDependencyLockingProvider;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.rules.SpecRuleAction;
@@ -87,7 +86,7 @@ public class DefaultResolutionStrategy implements ResolutionStrategyInternal {
     private boolean failOnChangingVersions;
     private boolean verifyDependencies = true;
     private final Property<Boolean> useGlobalDependencySubstitutionRules;
-
+    private boolean returnAllVariants = false;
 
     public DefaultResolutionStrategy(DependencySubstitutionRules globalDependencySubstitutionRules,
                                      VcsResolver vcsResolver,
@@ -121,6 +120,11 @@ public DefaultResolutionStrategy(DependencySubstitutionRules globalDependencySub
     }
 
     @Override
+    public void discardStateRequiredForGraphResolution() {
+        dependencySubstitutions.discard();
+    }
+
+    @Override
     public void setMutationValidator(MutationValidator validator) {
         mutationValidator = validator;
         cachePolicy.setMutationValidator(validator);
@@ -226,13 +230,17 @@ public ResolutionStrategy eachDependency(Action<? super DependencyResolveDetails
     }
 
     @Override
-    public Action<DependencySubstitution> getDependencySubstitutionRule() {
+    public ImmutableActionSet<DependencySubstitutionInternal> getDependencySubstitutionRule() {
+        ImmutableActionSet<DependencySubstitutionInternal> result = ImmutableActionSet.empty();
         Set<ModuleVersionSelector> forcedModules = getForcedModules();
-        Action<DependencySubstitution> moduleForcingResolveRule = Cast.uncheckedCast(forcedModules.isEmpty() ? Actions.doNothing() : new ModuleForcingResolveRule(forcedModules));
-        Action<DependencySubstitution> localDependencySubstitutionsAction = this.dependencySubstitutions.getRuleAction();
-        Action<DependencySubstitution> globalDependencySubstitutionRulesAction = (useGlobalDependencySubstitutionRules.get() ?
-           globalDependencySubstitutionRules :  DependencySubstitutionRules.NO_OP).getRuleAction();
-        return Actions.composite(moduleForcingResolveRule, localDependencySubstitutionsAction, globalDependencySubstitutionRulesAction);
+        if (!forcedModules.isEmpty()) {
+            result = result.add(new ModuleForcingResolveRule(forcedModules));
+        }
+        result = result.add(dependencySubstitutions.getRuleAction());
+        if (useGlobalDependencySubstitutionRules.get()) {
+            result = result.add(globalDependencySubstitutionRules.getRuleAction());
+        }
+        return result;
     }
 
     @Override
@@ -389,4 +397,15 @@ public ResolutionStrategy enableDependencyVerification() {
         verifyDependencies = true;
         return this;
     }
+
+    @Override
+    public void setReturnAllVariants(boolean returnAllVariants) {
+        mutationValidator.validateMutation(STRATEGY);
+        this.returnAllVariants = returnAllVariants;
+    }
+
+    @Override
+    public boolean getReturnAllVariants() {
+        return this.returnAllVariants;
+    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolversChain.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolversChain.java
index 33ce277..fac2131 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolversChain.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/ComponentResolversChain.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
 
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ComponentResolvers;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingArtifactResolver;
@@ -24,10 +23,9 @@
 import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry;
 import org.gradle.api.internal.component.ArtifactType;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.resolve.resolver.ArtifactResolver;
 import org.gradle.internal.resolve.resolver.ArtifactSelector;
@@ -126,17 +124,17 @@ private ArtifactResolverChain(List<ArtifactResolver> resolvers) {
         }
 
         @Override
-        public void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactResolveResult result) {
+        public void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result) {
             for (ArtifactResolver resolver : resolvers) {
                 if (result.hasResult()) {
                     return;
                 }
-                resolver.resolveArtifact(ownerId, artifact, moduleSources, result);
+                resolver.resolveArtifact(component, artifact, result);
             }
         }
 
         @Override
-        public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+        public void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
             for (ArtifactResolver resolver : resolvers) {
                 if (result.hasResult()) {
                     return;
@@ -165,5 +163,4 @@ public void resolve(DependencyMetadata dependency, VersionSelector acceptor, @Nu
             }
         }
     }
-
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultArtifactDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultArtifactDependencyResolver.java
index 97bdce3..c587123 100755
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultArtifactDependencyResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DefaultArtifactDependencyResolver.java
@@ -16,12 +16,11 @@
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine;
 
 import com.google.common.collect.Lists;
-import org.gradle.api.Action;
 import org.gradle.api.InvalidUserCodeException;
-import org.gradle.api.artifacts.DependencySubstitution;
 import org.gradle.api.attributes.AttributesSchema;
 import org.gradle.api.internal.artifacts.ArtifactDependencyResolver;
 import org.gradle.api.internal.artifacts.ComponentSelectorConverter;
+import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
 import org.gradle.api.internal.artifacts.GlobalDependencyResolutionRules;
 import org.gradle.api.internal.artifacts.ResolveContext;
 import org.gradle.api.internal.artifacts.configurations.ConflictResolution;
@@ -36,7 +35,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyDescriptorFactory;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyMetadataFactory;
 import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyResolver;
 import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.CapabilitiesResolutionInternal;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.DependencyArtifactsVisitor;
@@ -56,10 +55,14 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorFactory;
 import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository;
 import org.gradle.api.internal.artifacts.type.ArtifactTypeRegistry;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.specs.Spec;
-import org.gradle.internal.Actions;
+import org.gradle.internal.ImmutableActionSet;
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveStateFactory;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory;
+import org.gradle.internal.component.model.ComponentIdGenerator;
 import org.gradle.internal.component.model.DependencyMetadata;
 import org.gradle.internal.instantiation.InstantiatorFactory;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
@@ -77,7 +80,7 @@
 
 public class DefaultArtifactDependencyResolver implements ArtifactDependencyResolver {
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultArtifactDependencyResolver.class);
-    private final DependencyDescriptorFactory dependencyDescriptorFactory;
+    private final DependencyMetadataFactory dependencyMetadataFactory;
     private final List<ResolverProviderFactory> resolverFactories;
     private final ResolveIvyFactory ivyFactory;
     private final VersionComparator versionComparator;
@@ -92,12 +95,16 @@ public class DefaultArtifactDependencyResolver implements ArtifactDependencyReso
     private final ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory;
     private final CalculatedValueContainerFactory calculatedValueContainerFactory;
     private final ResolvedVariantCache resolvedVariantCache;
+    private final AttributeDesugaring attributeDesugaring;
+    private final LocalComponentGraphResolveStateFactory localResolveStateFactory;
+    private final ModuleComponentGraphResolveStateFactory moduleResolveStateFactory;
+    private final ComponentIdGenerator idGenerator;
 
     public DefaultArtifactDependencyResolver(
         BuildOperationExecutor buildOperationExecutor,
         List<ResolverProviderFactory> resolverFactories,
         ResolveIvyFactory ivyFactory,
-        DependencyDescriptorFactory dependencyDescriptorFactory,
+        DependencyMetadataFactory dependencyMetadataFactory,
         VersionComparator versionComparator,
         ModuleExclusions moduleExclusions,
         ComponentSelectorConverter componentSelectorConverter,
@@ -108,11 +115,15 @@ public DefaultArtifactDependencyResolver(
         InstantiatorFactory instantiatorFactory,
         ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
         CalculatedValueContainerFactory calculatedValueContainerFactory,
-        ResolvedVariantCache resolvedVariantCache
+        ResolvedVariantCache resolvedVariantCache,
+        AttributeDesugaring attributeDesugaring,
+        LocalComponentGraphResolveStateFactory localResolveStateFactory,
+        ModuleComponentGraphResolveStateFactory moduleResolveStateFactory,
+        ComponentIdGenerator idGenerator
     ) {
         this.resolverFactories = resolverFactories;
         this.ivyFactory = ivyFactory;
-        this.dependencyDescriptorFactory = dependencyDescriptorFactory;
+        this.dependencyMetadataFactory = dependencyMetadataFactory;
         this.versionComparator = versionComparator;
         this.moduleExclusions = moduleExclusions;
         this.buildOperationExecutor = buildOperationExecutor;
@@ -125,6 +136,10 @@ public DefaultArtifactDependencyResolver(
         this.componentSelectionDescriptorFactory = componentSelectionDescriptorFactory;
         this.calculatedValueContainerFactory = calculatedValueContainerFactory;
         this.resolvedVariantCache = resolvedVariantCache;
+        this.attributeDesugaring = attributeDesugaring;
+        this.localResolveStateFactory = localResolveStateFactory;
+        this.moduleResolveStateFactory = moduleResolveStateFactory;
+        this.idGenerator = idGenerator;
     }
 
     @Override
@@ -134,7 +149,7 @@ public void resolve(ResolveContext resolveContext, List<? extends ResolutionAwar
         validateResolutionStrategy(resolveContext.getResolutionStrategy());
 
         ComponentResolversChain resolvers = createResolvers(resolveContext, repositories, metadataHandler, projectDependencyResolver, artifactTypeRegistry, consumerSchema);
-        DependencyGraphBuilder builder = createDependencyGraphBuilder(resolvers, resolveContext.getResolutionStrategy(), metadataHandler, edgeFilter, consumerSchema, moduleExclusions, buildOperationExecutor);
+        DependencyGraphBuilder builder = createDependencyGraphBuilder(resolvers, resolveContext.getResolutionStrategy(), metadataHandler, edgeFilter, consumerSchema, moduleExclusions);
 
         DependencyGraphVisitor artifactsGraphVisitor = new ResolvedArtifactsGraphVisitor(artifactsVisitor, resolvers.getArtifactSelector());
 
@@ -162,24 +177,22 @@ private DependencyGraphBuilder createDependencyGraphBuilder(
         GlobalDependencyResolutionRules globalRules,
         Spec<? super DependencyMetadata> edgeFilter,
         AttributesSchemaInternal attributesSchema,
-        ModuleExclusions moduleExclusions,
-        BuildOperationExecutor buildOperationExecutor
+        ModuleExclusions moduleExclusions
     ) {
-
         DependencyToComponentIdResolver componentIdResolver = componentSource.getComponentIdResolver();
-        ComponentMetaDataResolver componentMetaDataResolver = new ClientModuleResolver(componentSource.getComponentResolver(), dependencyDescriptorFactory);
+        ComponentMetaDataResolver componentMetaDataResolver = new ClientModuleResolver(componentSource.getComponentResolver(), dependencyMetadataFactory, moduleResolveStateFactory);
 
         ModuleConflictHandler conflictHandler = createModuleConflictHandler(resolutionStrategy, globalRules);
         DefaultCapabilitiesConflictHandler capabilitiesConflictHandler = createCapabilitiesConflictHandler(resolutionStrategy.getCapabilitiesResolutionRules());
 
         DependencySubstitutionApplicator applicator = createDependencySubstitutionApplicator(resolutionStrategy);
-        return new DependencyGraphBuilder(componentIdResolver, componentMetaDataResolver, conflictHandler, capabilitiesConflictHandler, edgeFilter, attributesSchema, moduleExclusions, buildOperationExecutor, applicator, componentSelectorConverter, attributesFactory, versionSelectorScheme, versionComparator.asVersionComparator(), versionParser);
+        return new DependencyGraphBuilder(componentIdResolver, componentMetaDataResolver, conflictHandler, capabilitiesConflictHandler, edgeFilter, attributesSchema, moduleExclusions, buildOperationExecutor, applicator, componentSelectorConverter, attributesFactory, attributeDesugaring, versionSelectorScheme, versionComparator.asVersionComparator(), localResolveStateFactory, idGenerator, versionParser);
     }
 
     private DependencySubstitutionApplicator createDependencySubstitutionApplicator(ResolutionStrategyInternal resolutionStrategy) {
-        Action<DependencySubstitution> rule = resolutionStrategy.getDependencySubstitutionRule();
+        ImmutableActionSet<DependencySubstitutionInternal> rule = resolutionStrategy.getDependencySubstitutionRule();
         DependencySubstitutionApplicator applicator;
-        if (Actions.<DependencySubstitution>doNothing() == rule) {
+        if (rule.isEmpty()) {
             applicator = NO_OP;
         } else {
             applicator = new CachingDependencySubstitutionApplicator(new DefaultDependencySubstitutionApplicator(componentSelectionDescriptorFactory, rule, instantiator));
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactSetFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactSetFactory.java
index 242f738..90fc174 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactSetFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactSetFactory.java
@@ -29,8 +29,8 @@
 import org.gradle.internal.component.external.model.DefaultImmutableCapability;
 import org.gradle.internal.component.external.model.ImmutableCapabilities;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentArtifactResolveVariantState;
-import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.component.model.VariantResolveMetadata;
 import org.gradle.internal.resolve.resolver.ArtifactResolver;
 import org.gradle.internal.resolve.result.DefaultBuildableArtifactResolveResult;
@@ -43,41 +43,45 @@
 import java.util.function.Supplier;
 
 public class ArtifactSetFactory {
-    public static ArtifactSet createFromVariantMetadata(ComponentIdentifier componentIdentifier,
-                                                        ComponentArtifactResolveVariantState allVariants,
-                                                        Set<ResolvedVariant> legacyVariants,
-                                                        AttributesSchemaInternal schema,
-                                                        ImmutableAttributes selectionAttributes
+    public static ArtifactSet createFromVariantMetadata(
+        ComponentIdentifier componentIdentifier,
+        ComponentArtifactResolveVariantState allVariants,
+        Set<ResolvedVariant> legacyVariants,
+        AttributesSchemaInternal schema,
+        ImmutableAttributes selectionAttributes
     ) {
         return new DefaultArtifactSet(componentIdentifier, schema, selectionAttributes, allVariants, legacyVariants);
     }
-    public static ArtifactSet adHocVariant(ComponentIdentifier componentIdentifier, ModuleVersionIdentifier ownerId, Collection<? extends ComponentArtifactMetadata> artifacts, ModuleSources moduleSources, AttributesSchemaInternal schema, ArtifactResolver artifactResolver, ImmutableAttributes variantAttributes, ImmutableAttributes selectionAttributes) {
+
+    public static ArtifactSet adHocVariant(ComponentArtifactResolveMetadata component, Collection<? extends ComponentArtifactMetadata> artifacts, AttributesSchemaInternal schema, ArtifactResolver artifactResolver, ImmutableAttributes variantAttributes, ImmutableAttributes selectionAttributes) {
         VariantResolveMetadata.Identifier identifier = null;
         if (artifacts.size() == 1) {
             identifier = new SingleArtifactVariantIdentifier(artifacts.iterator().next().getId());
         }
-        ResolvedVariant resolvedVariant = toResolvedVariant(identifier, Describables.of(componentIdentifier), variantAttributes, ImmutableList.copyOf(artifacts), ImmutableCapabilities.of(DefaultImmutableCapability.defaultCapabilityForComponent(ownerId)), ownerId, moduleSources, artifactResolver);
+        ComponentIdentifier componentIdentifier = component.getId();
+        ModuleVersionIdentifier ownerId = component.getModuleVersionId();
+        ResolvedVariant resolvedVariant = toResolvedVariant(identifier, Describables.of(componentIdentifier), variantAttributes, ImmutableList.copyOf(artifacts), ImmutableCapabilities.of(DefaultImmutableCapability.defaultCapabilityForComponent(ownerId)), component, artifactResolver);
         return new DefaultArtifactSet(componentIdentifier, schema, selectionAttributes, () -> Collections.singleton(resolvedVariant), Collections.singleton(resolvedVariant));
     }
 
-    public static ResolvedVariant toResolvedVariant(@Nullable VariantResolveMetadata.Identifier identifier,
-                                                    DisplayName displayName,
-                                                    ImmutableAttributes variantAttributes,
-                                                    List<? extends ComponentArtifactMetadata> artifacts,
-                                                    CapabilitiesMetadata capabilitiesMetadata,
-                                                    ModuleVersionIdentifier ownerId,
-                                                    ModuleSources moduleSources,
-                                                    ArtifactResolver artifactResolver
+    public static ResolvedVariant toResolvedVariant(
+        @Nullable VariantResolveMetadata.Identifier identifier,
+        DisplayName displayName,
+        ImmutableAttributes variantAttributes,
+        List<? extends ComponentArtifactMetadata> artifacts,
+        CapabilitiesMetadata capabilitiesMetadata,
+        ComponentArtifactResolveMetadata component,
+        ArtifactResolver artifactResolver
     ) {
-        return ArtifactBackedResolvedVariant.create(identifier, displayName, variantAttributes, capabilitiesMetadata, supplyLazilyResolvedArtifacts(ownerId, moduleSources, artifacts, artifactResolver));
+        return ArtifactBackedResolvedVariant.create(identifier, displayName, variantAttributes, capabilitiesMetadata, supplyLazilyResolvedArtifacts(component, artifacts, artifactResolver));
     }
 
-    private static Supplier<Collection<? extends ResolvableArtifact>> supplyLazilyResolvedArtifacts(ModuleVersionIdentifier ownerId, ModuleSources moduleSources, List<? extends ComponentArtifactMetadata> artifacts, ArtifactResolver artifactResolver) {
+    private static Supplier<Collection<? extends ResolvableArtifact>> supplyLazilyResolvedArtifacts(ComponentArtifactResolveMetadata component, List<? extends ComponentArtifactMetadata> artifacts, ArtifactResolver artifactResolver) {
         return () -> {
             ImmutableSet.Builder<ResolvableArtifact> resolvedArtifacts = ImmutableSet.builder();
             for (ComponentArtifactMetadata artifact : artifacts) {
                 DefaultBuildableArtifactResolveResult result = new DefaultBuildableArtifactResolveResult();
-                artifactResolver.resolveArtifact(ownerId, artifact, moduleSources, result);
+                artifactResolver.resolveArtifact(component, artifact, result);
                 if (artifact.isOptionalArtifact()) {
                     try {
                         // probe if the artifact exists
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/BuildDependenciesOnlyVisitedArtifactSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/BuildDependenciesOnlyVisitedArtifactSet.java
index e97be34..58b5361 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/BuildDependenciesOnlyVisitedArtifactSet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/BuildDependenciesOnlyVisitedArtifactSet.java
@@ -19,9 +19,9 @@
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.UnresolvedDependency;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.transform.ArtifactTransforms;
-import org.gradle.api.internal.artifacts.transform.ExtraExecutionGraphDependenciesResolverFactory;
+import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependenciesResolverFactory;
 import org.gradle.api.internal.artifacts.transform.VariantSelector;
+import org.gradle.api.internal.artifacts.transform.VariantSelectorFactory;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
 import org.gradle.api.specs.Spec;
@@ -31,19 +31,24 @@
 public class BuildDependenciesOnlyVisitedArtifactSet implements VisitedArtifactSet {
     private final Set<UnresolvedDependency> unresolvedDependencies;
     private final VisitedArtifactsResults artifactsResults;
-    private final ArtifactTransforms artifactTransforms;
-    private final ExtraExecutionGraphDependenciesResolverFactory resolverFactory;
+    private final VariantSelectorFactory variantSelectorFactory;
+    private final TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory;
 
-    public BuildDependenciesOnlyVisitedArtifactSet(Set<UnresolvedDependency> unresolvedDependencies, VisitedArtifactsResults artifactsResults, ArtifactTransforms artifactTransforms, ExtraExecutionGraphDependenciesResolverFactory resolverFactory) {
+    public BuildDependenciesOnlyVisitedArtifactSet(
+        Set<UnresolvedDependency> unresolvedDependencies,
+        VisitedArtifactsResults artifactsResults,
+        VariantSelectorFactory variantSelectorFactory,
+        TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory
+    ) {
         this.unresolvedDependencies = unresolvedDependencies;
         this.artifactsResults = artifactsResults;
-        this.artifactTransforms = artifactTransforms;
-        this.resolverFactory = resolverFactory;
+        this.variantSelectorFactory = variantSelectorFactory;
+        this.dependenciesResolverFactory = dependenciesResolverFactory;
     }
 
     @Override
     public SelectedArtifactSet select(Spec<? super Dependency> dependencySpec, AttributeContainerInternal requestedAttributes, Spec<? super ComponentIdentifier> componentSpec, boolean allowNoMatchingVariant, boolean selectFromAllVariants) {
-        VariantSelector variantSelector = artifactTransforms.variantSelector(requestedAttributes, allowNoMatchingVariant, selectFromAllVariants, resolverFactory);
+        VariantSelector variantSelector = variantSelectorFactory.create(requestedAttributes, allowNoMatchingVariant, selectFromAllVariants, dependenciesResolverFactory);
         ResolvedArtifactSet selectedArtifacts = artifactsResults.select(componentSpec, variantSelector).getArtifacts();
         return new BuildDependenciesOnlySelectedArtifactSet(unresolvedDependencies, selectedArtifacts);
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSet.java
index 91c7c6a..e5dea36 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSet.java
@@ -19,7 +19,7 @@
 import org.gradle.api.Describable;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.internal.artifacts.transform.ExtraExecutionGraphDependenciesResolverFactory;
+import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependenciesResolverFactory;
 import org.gradle.api.internal.artifacts.transform.TransformedVariantFactory;
 import org.gradle.api.internal.artifacts.transform.VariantDefinition;
 import org.gradle.api.internal.artifacts.transform.VariantSelector;
@@ -70,11 +70,11 @@ public AttributesSchemaInternal getSchema() {
     }
 
     @Override
-    public ResolvedArtifactSet asTransformed(ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolver, TransformedVariantFactory transformedVariantFactory) {
+    public ResolvedArtifactSet asTransformed(ResolvedVariant sourceVariant, VariantDefinition variantDefinition, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory, TransformedVariantFactory transformedVariantFactory) {
         if (componentIdentifier instanceof ProjectComponentIdentifier) {
-            return transformedVariantFactory.transformedProjectArtifacts(componentIdentifier, sourceVariant, variantDefinition, dependenciesResolver);
+            return transformedVariantFactory.transformedProjectArtifacts(componentIdentifier, sourceVariant, variantDefinition, dependenciesResolverFactory);
         } else {
-            return transformedVariantFactory.transformedExternalArtifacts(componentIdentifier, sourceVariant, variantDefinition, dependenciesResolver);
+            return transformedVariantFactory.transformedExternalArtifacts(componentIdentifier, sourceVariant, variantDefinition, dependenciesResolverFactory);
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/LocalFileDependencyBackedArtifactSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/LocalFileDependencyBackedArtifactSet.java
index b9a2040..147edab 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/LocalFileDependencyBackedArtifactSet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/LocalFileDependencyBackedArtifactSet.java
@@ -23,8 +23,8 @@
 import org.gradle.api.capabilities.CapabilitiesMetadata;
 import org.gradle.api.internal.artifacts.DefaultResolvableArtifact;
 import org.gradle.api.internal.artifacts.transform.AbstractTransformedArtifactSet;
-import org.gradle.api.internal.artifacts.transform.ExtraExecutionGraphDependenciesResolverFactory;
-import org.gradle.api.internal.artifacts.transform.Transformation;
+import org.gradle.api.internal.artifacts.transform.TransformChain;
+import org.gradle.api.internal.artifacts.transform.TransformUpstreamDependenciesResolverFactory;
 import org.gradle.api.internal.artifacts.transform.TransformedArtifactSet;
 import org.gradle.api.internal.artifacts.transform.TransformedVariantFactory;
 import org.gradle.api.internal.artifacts.transform.VariantDefinition;
@@ -130,8 +130,8 @@ public void visit(Visitor listener) {
     }
 
     @Override
-    public ResolvedArtifactSet asTransformed(ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolver, TransformedVariantFactory transformedVariantFactory) {
-        return new TransformedLocalFileArtifactSet((SingletonFileResolvedVariant) sourceVariant, variantDefinition.getTargetAttributes(), variantDefinition.getTransformation(), dependenciesResolver, calculatedValueContainerFactory);
+    public ResolvedArtifactSet asTransformed(ResolvedVariant sourceVariant, VariantDefinition variantDefinition, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory, TransformedVariantFactory transformedVariantFactory) {
+        return new TransformedLocalFileArtifactSet((SingletonFileResolvedVariant) sourceVariant, variantDefinition.getTargetAttributes(), variantDefinition.getTransformChain(), dependenciesResolverFactory, calculatedValueContainerFactory);
     }
 
     @Override
@@ -265,10 +265,10 @@ private static class TransformedLocalFileArtifactSet extends AbstractTransformed
 
         public TransformedLocalFileArtifactSet(SingletonFileResolvedVariant delegate,
                                                ImmutableAttributes attributes,
-                                               Transformation transformation,
-                                               ExtraExecutionGraphDependenciesResolverFactory dependenciesResolver,
+                                               TransformChain transformChain,
+                                               TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory,
                                                CalculatedValueContainerFactory calculatedValueContainerFactory) {
-            super(delegate.getComponentId(), delegate, attributes, Collections.emptyList(), transformation, dependenciesResolver, calculatedValueContainerFactory);
+            super(delegate.getComponentId(), delegate, attributes, Collections.emptyList(), transformChain, dependenciesResolverFactory, calculatedValueContainerFactory);
             this.delegate = delegate;
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactSet.java
index 0aeee95..64cce66 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactSet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactSet.java
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact;
 
 import org.gradle.api.Action;
-import org.gradle.api.internal.artifacts.transform.TransformationNode;
+import org.gradle.api.internal.artifacts.transform.TransformStepNode;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.file.FileCollectionStructureVisitor;
 import org.gradle.api.internal.tasks.TaskDependencyContainer;
@@ -39,7 +39,7 @@ public interface ResolvedArtifactSet extends TaskDependencyContainer {
     interface TransformSourceVisitor {
         void visitArtifact(ResolvableArtifact artifact);
 
-        void visitTransform(TransformationNode source);
+        void visitTransform(TransformStepNode source);
     }
 
     /**
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactsGraphVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactsGraphVisitor.java
index f0ce4ce..87177b1 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactsGraphVisitor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ResolvedArtifactsGraphVisitor.java
@@ -84,7 +84,7 @@ public void finish(DependencyGraphNode root) {
 
     private ArtifactsForNode getArtifacts(DependencyGraphEdge dependency, DependencyGraphNode toNode) {
         ComponentArtifactResolveState componentState = toNode.getOwner().getResolveState().prepareForArtifactResolution();
-        VariantArtifactResolveState variantState = componentState.prepareForArtifactResolution(toNode.getMetadata());
+        VariantArtifactResolveState variantState = toNode.getResolveState().prepareForArtifactResolution();
         ImmutableAttributes overriddenAttributes = dependency.getAttributes();
 
         List<? extends ComponentArtifactMetadata> artifacts = dependency.getArtifacts(variantState);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Intersections.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Intersections.java
index a2c2b19..a67fe0b 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Intersections.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/Intersections.java
@@ -212,6 +212,13 @@ private ExcludeSpec intersectModuleId(ModuleIdExclude left, ExcludeSpec right) {
                 return left;
             }
             return factory.nothing();
+        } else if (right instanceof ModuleSetExclude) {
+            ModuleSetExclude moduleSetExclude = (ModuleSetExclude) right;
+            if (moduleSetExclude.getModules().contains(left.getModuleId().getName())) {
+                return left;
+            } else {
+                return factory.nothing();
+            }
         }
         return null;
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphComponent.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphComponent.java
index 4188c5a..0b89109 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphComponent.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphComponent.java
@@ -18,7 +18,6 @@
 
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
-import org.gradle.internal.component.model.ComponentGraphResolveState;
 
 import javax.annotation.Nullable;
 import java.util.Collection;
@@ -36,8 +35,6 @@ public interface DependencyGraphComponent extends ResolvedGraphComponent {
     @Nullable
     ComponentGraphResolveMetadata getMetadataOrNull();
 
-    ComponentGraphResolveState getResolveState();
-
     Collection<? extends DependencyGraphComponent> getDependents();
 
     /**
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphNode.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphNode.java
index abf9919..dbf2dc0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphNode.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/DependencyGraphNode.java
@@ -16,7 +16,6 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph;
 
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
 import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState;
 import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
@@ -26,15 +25,9 @@
 import java.util.Set;
 
 /**
- * A node in the dependency graph. Represents a configuration.
+ * A node in the dependency graph. Represents a variant.
  */
-public interface DependencyGraphNode {
-    /**
-     * Returns a simple id for this node, unique across all nodes in the same graph.
-     * This id cannot be used across graphs.
-     */
-    Long getNodeId();
-
+public interface DependencyGraphNode extends ResolvedGraphVariant {
     boolean isRoot();
 
     ResolvedConfigurationIdentifier getResolvedConfigurationId();
@@ -55,6 +48,4 @@ public interface DependencyGraphNode {
     boolean isSelected();
 
     ComponentResolutionState getComponent();
-
-    ResolvedVariantResult getResolvedVariant();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphComponent.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphComponent.java
index 660de43..e280274 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphComponent.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphComponent.java
@@ -19,9 +19,8 @@
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
+import org.gradle.internal.component.model.ComponentGraphResolveState;
 
-import javax.annotation.Nullable;
 import java.util.List;
 
 /**
@@ -35,6 +34,8 @@ public interface ResolvedGraphComponent {
      */
     Long getResultId();
 
+    ComponentGraphResolveState getResolveState();
+
     /**
      * Returns a unique id for this component.
      */
@@ -51,22 +52,9 @@ public interface ResolvedGraphComponent {
     ComponentSelectionReason getSelectionReason();
 
     /**
-     * Returns the name of the repository used to source this component, or {@code null} if this component was not resolved from a repository.
-     */
-    @Nullable
-    String getRepositoryName();
-
-    /**
      * Returns the resolved/selected variant(s) for this component.
      *
      * @return the resolved/selected variant(s) for this component
      */
-    List<ResolvedVariantResult> getResolvedVariants();
-
-    /**
-     * Returns all variant(s) for this component, including all {@linkplain #getResolvedVariants() resolved variants}.
-     *
-     * @return all variant(s) for this component
-     */
-    List<ResolvedVariantResult> getAllVariants();
+    List<ResolvedGraphVariant> getSelectedVariants();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphDependency.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphDependency.java
index 9f37092..d2338b6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphDependency.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphDependency.java
@@ -18,7 +18,6 @@
 
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 
 import javax.annotation.Nullable;
@@ -35,7 +34,7 @@ public interface ResolvedGraphDependency {
     ModuleVersionResolveException getFailure();
 
     /**
-     * Returns the simple id of the selected component.
+     * Returns the simple id of the selected component, as per {@link ResolvedGraphComponent#getResultId()}.
      */
     @Nullable
     Long getSelected();
@@ -48,8 +47,14 @@ public interface ResolvedGraphDependency {
 
     boolean isConstraint();
 
-    ResolvedVariantResult getFromVariant();
+    /**
+     * Returns the simple id of the source variant, as per {@link ResolvedGraphVariant#getNodeId()}.
+     */
+    Long getFromVariant();
 
+    /**
+     * Returns the simple id of the selected variant, as per {@link ResolvedGraphVariant#getNodeId()}.
+     */
     @Nullable
-    ResolvedVariantResult getSelectedVariant();
+    Long getSelectedVariant();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphVariant.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphVariant.java
new file mode 100644
index 0000000..3ffe6a6
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/ResolvedGraphVariant.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.ivyservice.resolveengine.graph;
+
+import org.gradle.internal.component.model.VariantGraphResolveState;
+
+import javax.annotation.Nullable;
+
+public interface ResolvedGraphVariant {
+    /**
+     * Returns a simple id for this node, unique across all nodes in the same graph.
+     * This id cannot be used across graphs.
+     */
+    Long getNodeId();
+
+    VariantGraphResolveState getResolveState();
+
+    @Nullable
+    ResolvedGraphVariant getExternalVariant();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ComponentState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ComponentState.java
index 9d3099a..e1697e3 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ComponentState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ComponentState.java
@@ -16,7 +16,6 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.gradle.api.artifacts.ModuleIdentifier;
@@ -24,32 +23,26 @@
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
 import org.gradle.api.capabilities.Capability;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChainModuleSource;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.ComponentResolutionState;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphComponent;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphVariant;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.VersionConflictResolutionDetails;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorInternal;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasonInternal;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons;
-import org.gradle.api.internal.artifacts.result.DefaultResolvedVariantResult;
-import org.gradle.api.internal.attributes.AttributeDesugaring;
-import org.gradle.internal.Describables;
 import org.gradle.internal.Pair;
 import org.gradle.internal.component.external.model.DefaultImmutableCapability;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.component.model.ComponentGraphResolveState;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
 import org.gradle.internal.component.model.DefaultComponentOverrideMetadata;
-import org.gradle.internal.component.model.VariantGraphResolveMetadata;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
 import org.gradle.internal.resolve.result.DefaultBuildableComponentResolveResult;
 
 import javax.annotation.Nullable;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -62,7 +55,6 @@ public class ComponentState implements ComponentResolutionState, DependencyGraph
     private final ComponentIdentifier componentIdentifier;
     private final ModuleVersionIdentifier id;
     private final ComponentMetaDataResolver resolver;
-    private final AttributeDesugaring attributeDesugaring;
     private final List<NodeState> nodes = Lists.newLinkedList();
     private final Long resultId;
     private final ModuleResolveState module;
@@ -81,14 +73,13 @@ public class ComponentState implements ComponentResolutionState, DependencyGraph
     private boolean root;
     private Pair<Capability, Collection<NodeState>> capabilityReject;
 
-    ComponentState(Long resultId, ModuleResolveState module, ModuleVersionIdentifier id, ComponentIdentifier componentIdentifier, ComponentMetaDataResolver resolver, AttributeDesugaring attributeDesugaring) {
+    ComponentState(Long resultId, ModuleResolveState module, ModuleVersionIdentifier id, ComponentIdentifier componentIdentifier, ComponentMetaDataResolver resolver) {
         this.resultId = resultId;
         this.module = module;
         this.id = id;
         this.componentIdentifier = componentIdentifier;
         this.resolver = resolver;
         this.implicitCapability = DefaultImmutableCapability.defaultCapabilityForComponent(id);
-        this.attributeDesugaring = attributeDesugaring;
         this.hashCode = 31 * id.hashCode() ^ resultId.hashCode();
     }
 
@@ -113,13 +104,6 @@ public ModuleVersionIdentifier getId() {
     }
 
     @Override
-    public String getRepositoryName() {
-        return resolveState.getSources().withSource(RepositoryChainModuleSource.class, source -> source
-            .map(RepositoryChainModuleSource::getRepositoryName)
-            .orElse(null));
-    }
-
-    @Override
     public ModuleVersionIdentifier getModuleVersion() {
         return id;
     }
@@ -302,68 +286,16 @@ public void setRoot() {
     }
 
     @Override
-    public List<ResolvedVariantResult> getResolvedVariants() {
-        ImmutableList.Builder<ResolvedVariantResult> builder = ImmutableList.builder();
-        addResolvedVariants(builder::add);
+    public List<ResolvedGraphVariant> getSelectedVariants() {
+        ImmutableList.Builder<ResolvedGraphVariant> builder = ImmutableList.builder();
+        addSelectedVariants(builder::add);
         return builder.build();
     }
 
-    @Override
-    public List<ResolvedVariantResult> getAllVariants() {
-        // Without mixing in resolved variants here, we don't return all variants selected in the case of project dependencies.
-        // Additionally, we wouldn't have the external variants that we get from getResolvedVariants().
-        // TODO: Figure out why the variants from getVariantsForGraphTraversal() are different in the case of project dependencies.
-        Set<String> seen = new HashSet<>();
-        ImmutableList.Builder<ResolvedVariantResult> builder = ImmutableList.builder();
-        Consumer<ResolvedVariantResult> resolvedVariantResultConsumer = v -> {
-            if (seen.add(v.getDisplayName())) {
-                builder.add(v);
-            }
-        };
-        addResolvedVariants(resolvedVariantResultConsumer);
-        addOtherVariants(resolvedVariantResultConsumer);
-        return builder.build();
-    }
-
-    private void addResolvedVariants(Consumer<ResolvedVariantResult> consumer) {
+    private void addSelectedVariants(Consumer<ResolvedGraphVariant> consumer) {
         for (NodeState node : nodes) {
             if (node.isSelected()) {
-                consumer.accept(node.getResolvedVariant());
-            }
-        }
-    }
-
-    private void addOtherVariants(Consumer<ResolvedVariantResult> consumer) {
-        Optional<? extends List<? extends VariantGraphResolveMetadata>> variants = resolveState.getMetadata().getVariantsForGraphTraversal();
-        if (variants.isPresent()) {
-            for (VariantGraphResolveMetadata mainVariant : variants.get()) {
-                for (VariantGraphResolveMetadata.Subvariant variant : mainVariant.getVariants()) {
-                    List<? extends Capability> capabilities = variant.getCapabilities().getCapabilities();
-                    if (capabilities.isEmpty()) {
-                        capabilities = ImmutableList.of(getImplicitCapability());
-                    } else {
-                        capabilities = ImmutableList.copyOf(capabilities);
-                    }
-                    consumer.accept(new DefaultResolvedVariantResult(
-                        resolveState.getId(),
-                        Describables.of(variant.getName()),
-                        attributeDesugaring.desugar(variant.getAttributes().asImmutable()),
-                        capabilities,
-                        null
-                    ));
-                }
-            }
-        }
-        // Fall-back if there's no graph data
-        for (NodeState node : nodes) {
-            for (VariantGraphResolveMetadata.Subvariant variant : node.getMetadata().getVariants()) {
-                consumer.accept(new DefaultResolvedVariantResult(
-                    resolveState.getId(),
-                    Describables.of(variant.getName()),
-                    node.desugar(variant.getAttributes().asImmutable()),
-                    ImmutableList.of(),
-                    null
-                ));
+                consumer.accept(node);
             }
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyGraphBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyGraphBuilder.java
index db97ff0..739392f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyGraphBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyGraphBuilder.java
@@ -18,13 +18,9 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.gradle.api.Action;
 import org.gradle.api.GradleException;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
 import org.gradle.api.attributes.Attribute;
@@ -44,19 +40,20 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.DefaultCapabilitiesConflictHandler;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.ModuleConflictHandler;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.PotentialConflict;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.CompatibilityRule;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.specs.Spec;
 import org.gradle.internal.component.IncompatibleVariantsSelectionException;
-import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveState;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory;
 import org.gradle.internal.component.local.model.LocalComponentMetadata;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
+import org.gradle.internal.component.model.ComponentIdGenerator;
 import org.gradle.internal.component.model.DefaultCompatibilityCheckResult;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.id.IdGenerator;
-import org.gradle.internal.id.LongIdGenerator;
 import org.gradle.internal.operations.BuildOperationConstraint;
 import org.gradle.internal.operations.BuildOperationExecutor;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
@@ -72,7 +69,6 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -90,8 +86,11 @@ public class DependencyGraphBuilder {
     private final DependencySubstitutionApplicator dependencySubstitutionApplicator;
     private final ImmutableAttributesFactory attributesFactory;
     private final CapabilitiesConflictHandler capabilitiesConflictHandler;
+    private final AttributeDesugaring attributeDesugaring;
     private final VersionSelectorScheme versionSelectorScheme;
     private final Comparator<Version> versionComparator;
+    private final LocalComponentGraphResolveStateFactory resolveStateFactory;
+    private final ComponentIdGenerator idGenerator;
     private final VersionParser versionParser;
     private final ResolutionConflictTracker conflictTracker;
 
@@ -109,8 +108,11 @@ public DependencyGraphBuilder(DependencyToComponentIdResolver componentIdResolve
                                   DependencySubstitutionApplicator dependencySubstitutionApplicator,
                                   ComponentSelectorConverter componentSelectorConverter,
                                   ImmutableAttributesFactory attributesFactory,
+                                  AttributeDesugaring attributeDesugaring,
                                   VersionSelectorScheme versionSelectorScheme,
                                   Comparator<Version> versionComparator,
+                                  LocalComponentGraphResolveStateFactory resolveStateFactory,
+                                  ComponentIdGenerator idGenerator,
                                   VersionParser versionParser) {
         this.idResolver = componentIdResolver;
         this.metaDataResolver = componentMetaDataResolver;
@@ -123,27 +125,27 @@ public DependencyGraphBuilder(DependencyToComponentIdResolver componentIdResolve
         this.componentSelectorConverter = componentSelectorConverter;
         this.attributesFactory = attributesFactory;
         this.capabilitiesConflictHandler = capabilitiesConflictHandler;
+        this.attributeDesugaring = attributeDesugaring;
         this.versionSelectorScheme = versionSelectorScheme;
         this.versionComparator = versionComparator;
+        this.resolveStateFactory = resolveStateFactory;
+        this.idGenerator = idGenerator;
         this.versionParser = versionParser;
         this.conflictTracker = new ResolutionConflictTracker(moduleConflictHandler, capabilitiesConflictHandler);
     }
 
     public void resolve(final ResolveContext resolveContext, final DependencyGraphVisitor modelVisitor, boolean includeSyntheticDependencies) {
-
-        IdGenerator<Long> idGenerator = new LongIdGenerator();
         LocalComponentMetadata metadata = resolveContext.toRootComponentMetaData();
+        LocalComponentGraphResolveState rootState = resolveStateFactory.stateFor(metadata);
 
-        int graphSize = estimateSize(resolveContext);
+        int graphSize = resolveContext.getEstimatedGraphSize();
         ResolutionStrategyInternal resolutionStrategy = resolveContext.getResolutionStrategy();
 
-        List<? extends DependencyMetadata> syntheticDependencies = includeSyntheticDependencies ?
-            resolveContext.getSyntheticDependencies() : Collections.emptyList();
+        List<? extends DependencyMetadata> syntheticDependencies = includeSyntheticDependencies ? resolveContext.getSyntheticDependencies() : Collections.emptyList();
 
-        final ResolveState resolveState = new ResolveState(idGenerator, metadata, resolveContext.getName(), idResolver, metaDataResolver, edgeFilter, attributesSchema, moduleExclusions, componentSelectorConverter, attributesFactory, dependencySubstitutionApplicator, versionSelectorScheme, versionComparator, versionParser, moduleConflictHandler.getResolver(), graphSize, resolveContext.getResolutionStrategy().getConflictResolution(), syntheticDependencies, conflictTracker);
+        ResolveState resolveState = new ResolveState(idGenerator, rootState, resolveContext.getName(), idResolver, metaDataResolver, edgeFilter, attributesSchema, moduleExclusions, componentSelectorConverter, attributesFactory, attributeDesugaring, dependencySubstitutionApplicator, versionSelectorScheme, versionComparator, versionParser, moduleConflictHandler.getResolver(), graphSize, resolveContext.getResolutionStrategy().getConflictResolution(), syntheticDependencies, conflictTracker);
 
-        Map<ModuleVersionIdentifier, ComponentIdentifier> componentIdentifierCache = Maps.newHashMapWithExpectedSize(graphSize / 2);
-        traverseGraph(resolveState, componentIdentifierCache);
+        traverseGraph(resolveState);
 
         validateGraph(resolveState, resolutionStrategy.isFailingOnDynamicVersions(), resolutionStrategy.isFailingOnChangingVersions());
 
@@ -151,22 +153,9 @@ public void resolve(final ResolveContext resolveContext, final DependencyGraphVi
     }
 
     /**
-     * This method is a heuristic that gives an idea of the "size" of the graph. The larger
-     * the graph is, the higher the risk of internal resizes exists, so we try to estimate
-     * the size of the graph to avoid maps resizing.
-     */
-    private static int estimateSize(ResolveContext resolveContext) {
-        int estimate = 0;
-        if (resolveContext instanceof Configuration) {
-            estimate = (int) (512 * Math.log(((Configuration) resolveContext).getAllDependencies().size()));
-        }
-        return Math.max(10, estimate);
-    }
-
-    /**
      * Traverses the dependency graph, resolving conflicts and building the paths from the root configuration.
      */
-    private void traverseGraph(final ResolveState resolveState, final Map<ModuleVersionIdentifier, ComponentIdentifier> componentIdentifierCache) {
+    private void traverseGraph(final ResolveState resolveState) {
         resolveState.onMoreSelected(resolveState.getRoot());
         final List<EdgeState> dependencies = Lists.newArrayList();
 
@@ -181,9 +170,9 @@ private void traverseGraph(final ResolveState resolveState, final Map<ModuleVers
                 // Initialize and collect any new outgoing edges of this node
                 dependencies.clear();
                 node.visitOutgoingDependencies(dependencies);
-                boolean edgeWasProcessed = resolveEdges(node, dependencies, ENDORSE_STRICT_VERSIONS_DEPENDENCY_SPEC, false, resolveState, componentIdentifierCache);
+                boolean edgeWasProcessed = resolveEdges(node, dependencies, ENDORSE_STRICT_VERSIONS_DEPENDENCY_SPEC, false, resolveState);
                 node.collectEndorsedStrictVersions(dependencies);
-                resolveEdges(node, dependencies, NOT_ENDORSE_STRICT_VERSIONS_DEPENDENCY_SPEC, edgeWasProcessed, resolveState, componentIdentifierCache);
+                resolveEdges(node, dependencies, NOT_ENDORSE_STRICT_VERSIONS_DEPENDENCY_SPEC, edgeWasProcessed, resolveState);
             } else {
                 // We have some batched up conflicts. Resolve the first, and continue traversing the graph
                 if (moduleConflictHandler.hasConflicts()) {
@@ -235,17 +224,17 @@ private boolean doesNotDeclareExplicitCapability(NodeState nodeState) {
         });
     }
 
-    private boolean resolveEdges(final NodeState node,
-                                 final List<EdgeState> dependencies,
-                                 final Spec<EdgeState> dependencyFilter,
-                                 final boolean recomputeSelectors,
-                                 final ResolveState resolveState,
-                                 final Map<ModuleVersionIdentifier, ComponentIdentifier> componentIdentifierCache) {
+    private boolean resolveEdges(
+        final NodeState node,
+        final List<EdgeState> dependencies,
+        final Spec<EdgeState> dependencyFilter,
+        final boolean recomputeSelectors,
+        final ResolveState resolveState) {
         if (dependencies.isEmpty()) {
             return false;
         }
         if (performSelectionSerially(dependencies, dependencyFilter, resolveState, recomputeSelectors)) {
-            maybeDownloadMetadataInParallel(node, componentIdentifierCache, dependencies, dependencyFilter);
+            maybeDownloadMetadataInParallel(node, dependencies, dependencyFilter);
             attachToTargetRevisionsSerially(dependencies, dependencyFilter);
             return true;
         } else {
@@ -319,7 +308,7 @@ private void checkForModuleConflicts(ResolveState resolveState, ModuleResolveSta
      * Prepares the resolution of edges, either serially or concurrently.
      * It uses a simple heuristic to determine if we should perform concurrent resolution, based on the number of edges, and whether they have unresolved metadata.
      */
-    private void maybeDownloadMetadataInParallel(NodeState node, Map<ModuleVersionIdentifier, ComponentIdentifier> componentIdentifierCache, List<EdgeState> dependencies, Spec<EdgeState> dependencyFilter) {
+    private void maybeDownloadMetadataInParallel(NodeState node, List<EdgeState> dependencies, Spec<EdgeState> dependencyFilter) {
         List<ComponentState> requiringDownload = null;
         for (EdgeState dependency : dependencies) {
             if (!dependencyFilter.isSatisfiedBy(dependency)) {
@@ -327,7 +316,7 @@ private void maybeDownloadMetadataInParallel(NodeState node, Map<ModuleVersionId
             }
             ComponentState targetComponent = dependency.getTargetComponent();
             if (targetComponent != null && targetComponent.isSelected() && !targetComponent.alreadyResolved()) {
-                if (!metaDataResolver.isFetchingMetadataCheap(toComponentId(targetComponent.getId(), componentIdentifierCache))) {
+                if (!metaDataResolver.isFetchingMetadataCheap(targetComponent.getComponentId())) {
                     // Avoid initializing the list if there are no components requiring download (a common case)
                     if (requiringDownload == null) {
                         requiringDownload = Lists.newArrayList();
@@ -348,15 +337,6 @@ private void maybeDownloadMetadataInParallel(NodeState node, Map<ModuleVersionId
         }
     }
 
-    private ComponentIdentifier toComponentId(ModuleVersionIdentifier id, Map<ModuleVersionIdentifier, ComponentIdentifier> componentIdentifierCache) {
-        ComponentIdentifier identifier = componentIdentifierCache.get(id);
-        if (identifier == null) {
-            identifier = DefaultModuleComponentIdentifier.newId(id);
-            componentIdentifierCache.put(id, identifier);
-        }
-        return identifier;
-    }
-
     private void attachToTargetRevisionsSerially(List<EdgeState> dependencies, Spec<EdgeState> dependencyFilter) {
         // the following only needs to be done serially to preserve ordering of dependencies in the graph: we have visited the edges
         // but we still didn't add the result to the queue. Doing it from resolve threads would result in non-reproducible graphs, where
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java
index b0ee1f0..46a523c 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/DependencyState.java
@@ -20,6 +20,7 @@
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
 import org.gradle.api.internal.artifacts.ComponentSelectorConverter;
+import org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution.DependencySubstitutionApplicator;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorInternal;
 import org.gradle.internal.Describables;
 import org.gradle.internal.component.model.DefaultIvyArtifactName;
@@ -30,7 +31,10 @@
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasons.BY_ANCESTOR;
@@ -48,6 +52,7 @@ class DependencyState {
     private ModuleIdentifier moduleIdentifier;
     public ModuleVersionResolveException failure;
     private boolean reasonsAlreadyAdded;
+    private Map<DependencySubstitutionApplicator.SubstitutionResult, DependencyState> substitutionResultMap;
 
     DependencyState(DependencyMetadata dependency, ComponentSelectorConverter componentSelectorConverter) {
         this(dependency, dependency.getSelector(), Collections.emptyList(), componentSelectorConverter);
@@ -194,4 +199,11 @@ public boolean equals(Object o) {
     public int hashCode() {
         return hashCode;
     }
+
+    public DependencyState withSubstitution(DependencySubstitutionApplicator.SubstitutionResult substitutionResult, Function<DependencySubstitutionApplicator.SubstitutionResult, DependencyState> mappingFunction) {
+        if (substitutionResultMap == null) {
+            substitutionResultMap = new HashMap<>();
+        }
+        return substitutionResultMap.computeIfAbsent(substitutionResult, mappingFunction);
+    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java
index af36df1..e56b632 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/EdgeState.java
@@ -20,7 +20,6 @@
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
 import org.gradle.api.attributes.Attribute;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec;
@@ -34,7 +33,7 @@
 import org.gradle.internal.component.model.ExcludeMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
 import org.gradle.internal.component.model.VariantArtifactResolveState;
-import org.gradle.internal.component.model.VariantGraphResolveMetadata;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.component.model.VariantSelectionResult;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 
@@ -67,7 +66,7 @@ class EdgeState implements DependencyGraphEdge {
     private ExcludeSpec cachedEdgeExclusions;
     private ExcludeSpec cachedExclusions;
 
-    private ResolvedVariantResult resolvedVariant;
+    private NodeState resolvedVariant;
     private boolean unattached;
     private boolean used;
 
@@ -278,7 +277,7 @@ private void calculateTargetConfigurations(ComponentState targetComponent) {
             targetNodeSelectionFailure = new ModuleVersionResolveException(dependencyState.getRequested(), t);
             return;
         }
-        for (VariantGraphResolveMetadata targetVariant : targetVariants.getVariants()) {
+        for (VariantGraphResolveState targetVariant : targetVariants.getVariants()) {
             NodeState targetNodeState = resolveState.getNode(targetComponent, targetVariant, targetVariants.isSelectedByVariantAwareResolution());
             this.targetNodes.add(targetNodeState);
         }
@@ -371,9 +370,19 @@ boolean hasSelectedVariant() {
         return resolvedVariant != null || !findTargetNodes().isEmpty();
     }
 
-    @Override
     @Nullable
-    public ResolvedVariantResult getSelectedVariant() {
+    @Override
+    public Long getSelectedVariant() {
+        NodeState node = getSelectedNode();
+        if (node == null) {
+            return null;
+        } else {
+            return node.getNodeId();
+        }
+    }
+
+    @Nullable
+    public NodeState getSelectedNode() {
         if (resolvedVariant != null) {
             return resolvedVariant;
         }
@@ -381,7 +390,7 @@ public ResolvedVariantResult getSelectedVariant() {
         assert !targetNodes.isEmpty();
         for (NodeState targetNode : targetNodes) {
             if (targetNode.isSelected()) {
-                resolvedVariant = targetNode.getResolvedVariant();
+                resolvedVariant = targetNode;
                 return resolvedVariant;
             }
         }
@@ -411,8 +420,8 @@ public boolean isConstraint() {
     }
 
     @Override
-    public ResolvedVariantResult getFromVariant() {
-        return from.getResolvedVariant();
+    public Long getFromVariant() {
+        return from.getNodeId();
     }
 
     @Nullable
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformDependencyMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformDependencyMetadata.java
index 332fc7b..bf905ea2 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformDependencyMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformDependencyMetadata.java
@@ -15,9 +15,6 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
 import org.gradle.api.artifacts.VersionConstraint;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ComponentSelector;
@@ -26,25 +23,20 @@
 import org.gradle.api.capabilities.Capability;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.component.external.model.DefaultConfigurationMetadata;
-import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
-import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
-import org.gradle.internal.component.external.model.VariantMetadataRules;
 import org.gradle.internal.component.model.ComponentGraphResolveState;
-import org.gradle.internal.component.model.ConfigurationMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
 import org.gradle.internal.component.model.ExcludeMetadata;
 import org.gradle.internal.component.model.ForcingDependencyMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
 import org.gradle.internal.component.model.LocalComponentDependencyMetadata;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.component.model.VariantSelectionResult;
 
 import javax.annotation.Nullable;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 
 class LenientPlatformDependencyMetadata implements ModuleDependencyMetadata, ForcingDependencyMetadata {
     private final ResolveState resolveState;
@@ -88,8 +80,8 @@ public ModuleDependencyMetadata withEndorseStrictVersions(boolean endorse) {
     @Override
     public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
         if (targetComponentState instanceof LenientPlatformGraphResolveState) {
-            LenientPlatformResolveMetadata platformMetadata = (LenientPlatformResolveMetadata) targetComponentState.getMetadata();
-            return new VariantSelectionResult(Collections.singletonList(new LenientPlatformConfigurationMetadata(platformMetadata.getPlatformState(), platformId)), false);
+            VariantGraphResolveState variant = ((LenientPlatformGraphResolveState) targetComponentState).getDefaultVariant(from, platformId);
+            return new VariantSelectionResult(Collections.singletonList(variant), false);
         }
         // the target component exists, so we need to fallback to the traditional selection process
         return new LocalComponentDependencyMetadata(componentId, cs, null, ImmutableAttributes.EMPTY, ImmutableAttributes.EMPTY, null, Collections.emptyList(), Collections.emptyList(), false, false, true, false, false, null).selectVariants(consumerAttributes, targetComponentState, consumerSchema, explicitRequestedCapabilities);
@@ -135,10 +127,6 @@ public boolean isEndorsingStrictVersions() {
         return false;
     }
 
-    private boolean isExternalVariant() {
-        return false;
-    }
-
     @Override
     public String getReason() {
         return "belongs to platform " + platformId;
@@ -158,68 +146,4 @@ public boolean isForce() {
     public ForcingDependencyMetadata forced() {
         return new LenientPlatformDependencyMetadata(resolveState, from, cs, componentId, platformId, true, transitive);
     }
-
-    private class LenientPlatformConfigurationMetadata extends DefaultConfigurationMetadata implements ConfigurationMetadata {
-
-        private final VirtualPlatformState platformState;
-        private final ComponentIdentifier platformId;
-
-        public LenientPlatformConfigurationMetadata(VirtualPlatformState platform, @Nullable ComponentIdentifier platformId) {
-            super(componentId, "default", true, false, ImmutableSet.of("default"), ImmutableList.of(), VariantMetadataRules.noOp(), ImmutableList.of(), ImmutableAttributes.EMPTY, false);
-            this.platformState = platform;
-            this.platformId = platformId;
-        }
-
-        @Override
-        public List<? extends ModuleDependencyMetadata> getDependencies() {
-            List<ModuleDependencyMetadata> result = null;
-            List<String> candidateVersions = platformState.getCandidateVersions();
-            Set<ModuleResolveState> modules = platformState.getParticipatingModules();
-            for (ModuleResolveState module : modules) {
-                ComponentState selected = module.getSelected();
-                if (selected != null) {
-                    String componentVersion = selected.getId().getVersion();
-                    for (String target : candidateVersions) {
-                        ModuleComponentIdentifier leafId = DefaultModuleComponentIdentifier.newId(module.getId(), target);
-                        ModuleComponentSelector leafSelector = DefaultModuleComponentSelector.newSelector(module.getId(), target);
-                        ComponentIdentifier platformId = platformState.getSelectedPlatformId();
-                        if (platformId == null) {
-                            // Not sure this can happen, unless in error state
-                            platformId = this.platformId;
-                        }
-                        if (!componentVersion.equals(target)) {
-                            // We will only add dependencies to the leaves if there is such a published module
-                            PotentialEdge potentialEdge = PotentialEdge.of(resolveState, from, leafId, leafSelector, platformId, platformState.isForced(), false);
-                            if (potentialEdge.state != null) {
-                                result = registerPlatformEdge(result, modules, leafId, leafSelector, platformId, platformState.isForced());
-                                break;
-                            }
-                        } else {
-                            // at this point we know the component exists
-                            result = registerPlatformEdge(result, modules, leafId, leafSelector, platformId, platformState.isForced());
-                            break;
-                        }
-                    }
-                    platformState.attachOrphanEdges();
-                }
-            }
-            return result == null ? Collections.emptyList() : result;
-        }
-
-        private List<ModuleDependencyMetadata> registerPlatformEdge(@Nullable List<ModuleDependencyMetadata> result, Set<ModuleResolveState> modules, ModuleComponentIdentifier leafId, ModuleComponentSelector leafSelector, ComponentIdentifier platformId, boolean force) {
-            if (result == null) {
-                result = Lists.newArrayListWithExpectedSize(modules.size());
-            }
-            result.add(new LenientPlatformDependencyMetadata(
-                resolveState,
-                from,
-                leafSelector,
-                leafId,
-                platformId,
-                force,
-                false
-            ));
-            return result;
-        }
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformGraphResolveState.java
index e4298d7..2eef40c 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformGraphResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformGraphResolveState.java
@@ -16,15 +16,36 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.artifacts.component.ModuleComponentSelector;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.internal.component.external.model.DefaultConfigurationMetadata;
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
+import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
+import org.gradle.internal.component.external.model.VariantMetadataRules;
 import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.component.model.ComponentIdGenerator;
+import org.gradle.internal.component.model.ConfigurationMetadata;
 import org.gradle.internal.component.model.DefaultComponentGraphResolveState;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 
 import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
 
 public class LenientPlatformGraphResolveState extends DefaultComponentGraphResolveState<LenientPlatformResolveMetadata, LenientPlatformResolveMetadata> {
+    private final ComponentIdGenerator componentIdGenerator;
+    private final ResolveState resolveState;
+
     public static LenientPlatformGraphResolveState of(
+        ComponentIdGenerator componentIdGenerator,
         ModuleComponentIdentifier moduleComponentIdentifier,
         ModuleVersionIdentifier moduleVersionIdentifier,
         VirtualPlatformState platformState,
@@ -32,16 +53,91 @@ public static LenientPlatformGraphResolveState of(
         ResolveState resolveState
     ) {
         LenientPlatformResolveMetadata metadata = new LenientPlatformResolveMetadata(moduleComponentIdentifier, moduleVersionIdentifier, platformState, platformNode, resolveState);
-        return new LenientPlatformGraphResolveState(metadata);
+        return new LenientPlatformGraphResolveState(componentIdGenerator.nextComponentId(), metadata, componentIdGenerator, resolveState);
     }
 
-    private LenientPlatformGraphResolveState(LenientPlatformResolveMetadata metadata) {
-        super(metadata, metadata);
+    private LenientPlatformGraphResolveState(long instanceId, LenientPlatformResolveMetadata metadata, ComponentIdGenerator componentIdGenerator, ResolveState resolveState) {
+        super(instanceId, metadata, metadata, resolveState.getAttributeDesugaring(), componentIdGenerator);
+        this.componentIdGenerator = componentIdGenerator;
+        this.resolveState = resolveState;
     }
 
     @Nullable
     @Override
     public ComponentGraphResolveState maybeAsLenientPlatform(ModuleComponentIdentifier componentIdentifier, ModuleVersionIdentifier moduleVersionIdentifier) {
-        return new LenientPlatformGraphResolveState(getMetadata().withVersion(componentIdentifier, moduleVersionIdentifier));
+        return new LenientPlatformGraphResolveState(componentIdGenerator.nextComponentId(), getMetadata().withVersion(componentIdentifier, moduleVersionIdentifier), componentIdGenerator, resolveState);
     }
+
+    /**
+     * @param platformId The consuming platform.
+     */
+    public VariantGraphResolveState getDefaultVariant(NodeState from, @Nullable ComponentIdentifier platformId) {
+        return newResolveStateFor(new LenientPlatformConfigurationMetadata(getMetadata().getPlatformState(), getMetadata().getId(), from, platformId));
+    }
+
+    private class LenientPlatformConfigurationMetadata extends DefaultConfigurationMetadata implements ConfigurationMetadata {
+        private final VirtualPlatformState platformState;
+        private final NodeState from;
+        private final ComponentIdentifier platformId;
+
+        public LenientPlatformConfigurationMetadata(VirtualPlatformState platform, ModuleComponentIdentifier componentId, NodeState from, @Nullable ComponentIdentifier platformId) {
+            super(componentId, "default", true, false, ImmutableSet.of("default"), ImmutableList.of(), VariantMetadataRules.noOp(), ImmutableList.of(), ImmutableAttributes.EMPTY, false);
+            this.platformState = platform;
+            this.from = from;
+            this.platformId = platformId;
+        }
+
+        @Override
+        public List<? extends ModuleDependencyMetadata> getDependencies() {
+            List<ModuleDependencyMetadata> result = null;
+            List<String> candidateVersions = platformState.getCandidateVersions();
+            Set<ModuleResolveState> modules = platformState.getParticipatingModules();
+            for (ModuleResolveState module : modules) {
+                ComponentState selected = module.getSelected();
+                if (selected != null) {
+                    String componentVersion = selected.getId().getVersion();
+                    for (String target : candidateVersions) {
+                        ModuleComponentIdentifier leafId = DefaultModuleComponentIdentifier.newId(module.getId(), target);
+                        ModuleComponentSelector leafSelector = DefaultModuleComponentSelector.newSelector(module.getId(), target);
+                        ComponentIdentifier platformId = platformState.getSelectedPlatformId();
+                        if (platformId == null) {
+                            // Not sure this can happen, unless in error state
+                            platformId = this.platformId;
+                        }
+                        if (!componentVersion.equals(target)) {
+                            // We will only add dependencies to the leaves if there is such a published module
+                            PotentialEdge potentialEdge = PotentialEdge.of(resolveState, from, leafId, leafSelector, platformId, platformState.isForced(), false);
+                            if (potentialEdge.state != null) {
+                                result = registerPlatformEdge(result, modules, leafId, leafSelector, platformId, platformState.isForced());
+                                break;
+                            }
+                        } else {
+                            // at this point we know the component exists
+                            result = registerPlatformEdge(result, modules, leafId, leafSelector, platformId, platformState.isForced());
+                            break;
+                        }
+                    }
+                    platformState.attachOrphanEdges();
+                }
+            }
+            return result == null ? Collections.emptyList() : result;
+        }
+
+        private List<ModuleDependencyMetadata> registerPlatformEdge(@Nullable List<ModuleDependencyMetadata> result, Set<ModuleResolveState> modules, ModuleComponentIdentifier leafId, ModuleComponentSelector leafSelector, ComponentIdentifier platformId, boolean force) {
+            if (result == null) {
+                result = Lists.newArrayListWithExpectedSize(modules.size());
+            }
+            result.add(new LenientPlatformDependencyMetadata(
+                resolveState,
+                from,
+                leafSelector,
+                leafId,
+                platformId,
+                force,
+                false
+            ));
+            return result;
+        }
+    }
+
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformResolveMetadata.java
index bc16c87..e6cb301 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/LenientPlatformResolveMetadata.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
@@ -37,6 +36,7 @@
 import org.gradle.internal.component.external.model.VariantDerivationStrategy;
 import org.gradle.internal.component.external.model.VariantMetadataRules;
 import org.gradle.internal.component.external.model.VirtualComponentIdentifier;
+import org.gradle.internal.component.model.ImmutableModuleSources;
 import org.gradle.internal.component.model.ModuleConfigurationMetadata;
 import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.component.model.VariantGraphResolveMetadata;
@@ -44,6 +44,7 @@
 import javax.annotation.Nullable;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 class LenientPlatformResolveMetadata implements ModuleComponentResolveMetadata {
@@ -74,7 +75,7 @@ public ModuleVersionIdentifier getModuleVersionId() {
 
     @Override
     public ModuleSources getSources() {
-        return null;
+        return ImmutableModuleSources.of();
     }
 
     @Override
@@ -113,12 +114,12 @@ public ModuleConfigurationMetadata getConfiguration(String name) {
                 ImmutableSet.of(name), ImmutableList.of(), ImmutableList.of(), ImmutableAttributes.EMPTY, ImmutableCapabilities.EMPTY, dependencies.build(), false, false
             );
         }
-        throw new IllegalArgumentException("Undefined configuration '" + name + "'");
+        return null;
     }
 
     @Override
     public Optional<List<? extends VariantGraphResolveMetadata>> getVariantsForGraphTraversal() {
-        return Optional.absent();
+        return Optional.empty();
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/MessageBuilderHelper.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/MessageBuilderHelper.java
index 37b43bc..890e8f4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/MessageBuilderHelper.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/MessageBuilderHelper.java
@@ -80,7 +80,7 @@ static Collection<String> pathTo(EdgeState edge, boolean includeLast) {
 
     @Nullable
     private static String variantDetails(EdgeState e) {
-        ResolvedVariantResult selectedVariant = e.hasSelectedVariant() ? e.getSelectedVariant() : null;
+        ResolvedVariantResult selectedVariant = e.hasSelectedVariant() ? e.getSelectedNode().getResolveState().getVariantResult(null) : null;
         if (selectedVariant != null) {
             return " (" + selectedVariant.getDisplayName() + ")";
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleResolveState.java
index f0ed151..e4a46d8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ModuleResolveState.java
@@ -31,13 +31,12 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.CandidateModule;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolver;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
-import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.attributes.AttributeMergingException;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
+import org.gradle.internal.component.model.ComponentIdGenerator;
 import org.gradle.internal.component.model.DependencyMetadata;
 import org.gradle.internal.component.model.ForcingDependencyMetadata;
-import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -61,13 +60,12 @@ class ModuleResolveState implements CandidateModule {
     private static final int MAX_SELECTION_CHANGE = 1000;
 
     private final ComponentMetaDataResolver metaDataResolver;
-    private final IdGenerator<Long> idGenerator;
+    private final ComponentIdGenerator idGenerator;
     private final ModuleIdentifier id;
     private final List<EdgeState> unattachedDependencies = new LinkedList<>();
     private final Map<ModuleVersionIdentifier, ComponentState> versions = new LinkedHashMap<>();
     private final ModuleSelectors<SelectorState> selectors;
     private final ConflictResolution conflictResolution;
-    private final AttributeDesugaring attributeDesugaring;
     private final ImmutableAttributesFactory attributesFactory;
     private final Comparator<Version> versionComparator;
     private final VersionParser versionParser;
@@ -87,7 +85,7 @@ class ModuleResolveState implements CandidateModule {
     private int selectionChangedCounter;
 
     ModuleResolveState(
-        IdGenerator<Long> idGenerator,
+        ComponentIdGenerator idGenerator,
         ModuleIdentifier id,
         ComponentMetaDataResolver metaDataResolver,
         ImmutableAttributesFactory attributesFactory,
@@ -96,8 +94,7 @@ class ModuleResolveState implements CandidateModule {
         SelectorStateResolver<ComponentState> selectorStateResolver,
         ResolveOptimizations resolveOptimizations,
         boolean rootModule,
-        ConflictResolution conflictResolution,
-        AttributeDesugaring attributeDesugaring
+        ConflictResolution conflictResolution
     ) {
         this.idGenerator = idGenerator;
         this.id = id;
@@ -111,7 +108,6 @@ class ModuleResolveState implements CandidateModule {
         this.selectorStateResolver = selectorStateResolver;
         this.selectors = new ModuleSelectors<>(versionComparator, versionParser);
         this.conflictResolution = conflictResolution;
-        this.attributeDesugaring = attributeDesugaring;
     }
 
     void setSelectorStateResolver(SelectorStateResolver<ComponentState> selectorStateResolver) {
@@ -305,7 +301,7 @@ public ComponentState getVersion(ModuleVersionIdentifier id, ComponentIdentifier
         assert id.getModule().equals(this.id);
         ComponentState moduleRevision = versions.get(id);
         if (moduleRevision == null) {
-            moduleRevision = new ComponentState(idGenerator.generateId(), this, id, componentIdentifier, metaDataResolver, attributeDesugaring);
+            moduleRevision = new ComponentState(idGenerator.nextGraphNodeId(), this, id, componentIdentifier, metaDataResolver);
             versions.put(id, moduleRevision);
         }
         return moduleRevision;
@@ -465,7 +461,7 @@ void maybeCreateVirtualMetadata(ResolveState resolveState) {
         for (ComponentState componentState : versions.values()) {
             if (componentState.getMetadataOrNull() == null) {
                 // TODO LJA Using the root as the NodeState here is a bit of a cheat, investigate if we can track the proper NodeState
-                componentState.setState(LenientPlatformGraphResolveState.of((ModuleComponentIdentifier) componentState.getComponentId(), componentState.getId(), platformState, resolveState.getRoot(), resolveState));
+                componentState.setState(LenientPlatformGraphResolveState.of(idGenerator, (ModuleComponentIdentifier) componentState.getComponentId(), componentState.getId(), platformState, resolveState.getRoot(), resolveState));
             }
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java
index c8bcfaa..076303a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeState.java
@@ -30,8 +30,6 @@
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.capabilities.Capability;
 import org.gradle.api.internal.artifacts.DependencySubstitutionInternal;
 import org.gradle.api.internal.artifacts.ResolvedConfigurationIdentifier;
@@ -40,25 +38,21 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphVariant;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.CapabilitiesConflictHandler;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.strict.StrictVersionConstraints;
-import org.gradle.api.internal.artifacts.result.DefaultResolvedVariantResult;
-import org.gradle.api.internal.attributes.AttributesSchemaInternal;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
-import org.gradle.internal.Describables;
-import org.gradle.internal.DisplayName;
-import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
 import org.gradle.api.internal.capabilities.ShadowedCapability;
+import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
 import org.gradle.internal.component.external.model.VirtualComponentIdentifier;
 import org.gradle.internal.component.local.model.LocalConfigurationGraphResolveMetadata;
 import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
 import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.component.model.DelegatingDependencyMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ExcludeMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
 import org.gradle.internal.component.model.VariantGraphResolveMetadata;
-import org.gradle.internal.component.model.VariantSelectionResult;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.logging.text.TreeFormatter;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 import org.slf4j.Logger;
@@ -79,12 +73,13 @@
  */
 public class NodeState implements DependencyGraphNode {
     private static final Logger LOGGER = LoggerFactory.getLogger(NodeState.class);
-    private final Long resultId;
+    private final Long nodeId;
     private final ComponentState component;
     private final List<EdgeState> incomingEdges = Lists.newArrayList();
     private final List<EdgeState> outgoingEdges = Lists.newArrayList();
     private final ResolvedConfigurationIdentifier id;
 
+    private final VariantGraphResolveState variantState;
     private final VariantGraphResolveMetadata metadata;
     private final ResolveState resolveState;
     private final ModuleExclusions moduleExclusions;
@@ -122,7 +117,6 @@ public class NodeState implements DependencyGraphNode {
     private long previousIncomingHash;
     private long incomingHash;
     private ExcludeSpec cachedModuleResolutionFilter;
-    private ResolvedVariantResult cachedVariantResult;
 
     private StrictVersionConstraints ancestorsStrictVersionConstraints;
     private StrictVersionConstraints ownStrictVersionConstraints;
@@ -131,16 +125,17 @@ public class NodeState implements DependencyGraphNode {
     private boolean findingExternalVariants;
 
     @VisibleForTesting // just for testing purposes
-    public NodeState(Long resultId, ResolvedConfigurationIdentifier id, ComponentState component, VariantGraphResolveMetadata md, boolean selectedByVariantAwareResolution) {
-        this(resultId, id, component, null, md, selectedByVariantAwareResolution);
+    public NodeState(long resultId, ResolvedConfigurationIdentifier id, ComponentState component, VariantGraphResolveState variant, boolean selectedByVariantAwareResolution) {
+        this(resultId, id, component, null, variant, selectedByVariantAwareResolution);
     }
 
-    public NodeState(Long resultId, ResolvedConfigurationIdentifier id, ComponentState component, ResolveState resolveState, VariantGraphResolveMetadata md, boolean selectedByVariantAwareResolution) {
-        this.resultId = resultId;
+    public NodeState(long resultId, ResolvedConfigurationIdentifier id, ComponentState component, ResolveState resolveState, VariantGraphResolveState variant, boolean selectedByVariantAwareResolution) {
+        this.nodeId = resultId;
         this.id = id;
         this.component = component;
         this.resolveState = resolveState;
-        this.metadata = md;
+        this.variantState = variant;
+        this.metadata = variant.getMetadata();
         this.isTransitive = metadata.isTransitive() || metadata.isExternalVariant();
         this.selectedByVariantAwareResolution = selectedByVariantAwareResolution;
         this.moduleExclusions = resolveState == null ? null : resolveState.getModuleExclusions(); // can be null in tests, ResolveState cannot be mocked
@@ -170,7 +165,7 @@ public ComponentState getComponent() {
 
     @Override
     public Long getNodeId() {
-        return resultId;
+        return nodeId;
     }
 
     @Override
@@ -204,6 +199,11 @@ public VariantGraphResolveMetadata getMetadata() {
     }
 
     @Override
+    public VariantGraphResolveState getResolveState() {
+        return variantState;
+    }
+
+    @Override
     public Set<? extends LocalFileDependencyMetadata> getOutgoingFileEdges() {
         if (metadata instanceof LocalConfigurationGraphResolveMetadata) {
             // Only when this node has a transitive incoming edge
@@ -604,7 +604,7 @@ private void addPlatformEdges(Collection<EdgeState> discoveredEdges, ModuleCompo
         }
         if (state == null) {
             // the platform doesn't exist, so we're building a lenient one
-            state = LenientPlatformGraphResolveState.of(platformComponentIdentifier, potentialEdge.toModuleVersionId, virtualPlatformState, this, resolveState);
+            state = LenientPlatformGraphResolveState.of(resolveState.getIdGenerator(), platformComponentIdentifier, potentialEdge.toModuleVersionId, virtualPlatformState, this, resolveState);
             potentialEdge.component.setState(state);
             // And now let's make sure we do not have another version of that virtual platform missing its metadata
             potentialEdge.component.getModule().maybeCreateVirtualMetadata(resolveState);
@@ -634,11 +634,14 @@ static DependencyState maybeSubstitute(DependencyState dependencyState, Dependen
 
         DependencySubstitutionInternal details = substitutionResult.getResult();
         if (details != null && details.isUpdated()) {
-            ArtifactSelectionDetailsInternal artifactSelectionDetails = details.getArtifactSelectionDetails();
-            if (artifactSelectionDetails.isUpdated()) {
-                return dependencyState.withTargetAndArtifacts(details.getTarget(), artifactSelectionDetails.getTargetSelectors(), details.getRuleDescriptors());
-            }
-            return dependencyState.withTarget(details.getTarget(), details.getRuleDescriptors());
+            // This caching works because our substitutionResult are cached themselves
+            return dependencyState.withSubstitution(substitutionResult, result -> {
+                ArtifactSelectionDetailsInternal artifactSelectionDetails = details.getArtifactSelectionDetails();
+                if (artifactSelectionDetails.isUpdated()) {
+                    return dependencyState.withTargetAndArtifacts(details.getTarget(), artifactSelectionDetails.getTargetSelectors(), details.getRuleDescriptors());
+                }
+                return dependencyState.withTarget(details.getTarget(), details.getRuleDescriptors());
+            });
         }
         return dependencyState;
     }
@@ -1248,29 +1251,9 @@ void makePending(EdgeState edgeState) {
         }
     }
 
-    ImmutableAttributes desugar(ImmutableAttributes attributes) {
-        return resolveState.desugar(attributes);
-    }
-
-    public ResolvedVariantResult getResolvedVariant() {
-        if (cachedVariantResult != null) {
-            return cachedVariantResult;
-        }
-        DisplayName name = Describables.of(metadata.getName());
-        List<? extends Capability> capabilities = metadata.getCapabilities().getCapabilities();
-        AttributeContainer attributes = desugar(metadata.getAttributes());
-        List<Capability> resolvedVariantCapabilities = capabilities.isEmpty() ? Collections.singletonList(component.getImplicitCapability()) : ImmutableList.copyOf(capabilities);
-        cachedVariantResult = new DefaultResolvedVariantResult(
-            component.getComponentId(),
-            name,
-            attributes,
-            resolvedVariantCapabilities,
-            findExternalVariant());
-        return cachedVariantResult;
-    }
-
     @Nullable
-    private ResolvedVariantResult findExternalVariant() {
+    @Override
+    public ResolvedGraphVariant getExternalVariant() {
         if (canIgnoreExternalVariant()) {
             return null;
         }
@@ -1289,7 +1272,7 @@ private ResolvedVariantResult findExternalVariant() {
         try {
             for (EdgeState outgoingEdge : outgoingEdges) {
                 //noinspection ConstantConditions
-                return outgoingEdge.getSelectedVariant();
+                return outgoingEdge.getSelectedNode();
             }
             return null;
         } finally {
@@ -1333,34 +1316,15 @@ public void updateTransitiveExcludes() {
         }
     }
 
-    private static class NonTransitiveVariantDependencyMetadata implements DependencyMetadata {
+    private static class NonTransitiveVariantDependencyMetadata extends DelegatingDependencyMetadata {
         private final DependencyMetadata dependencyMetadata;
 
         public NonTransitiveVariantDependencyMetadata(DependencyMetadata dependencyMetadata) {
+            super(dependencyMetadata);
             this.dependencyMetadata = dependencyMetadata;
         }
 
         @Override
-        public ComponentSelector getSelector() {
-            return dependencyMetadata.getSelector();
-        }
-
-        @Override
-        public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
-            return dependencyMetadata.selectVariants(consumerAttributes, targetComponentState, consumerSchema, explicitRequestedCapabilities);
-        }
-
-        @Override
-        public List<ExcludeMetadata> getExcludes() {
-            return dependencyMetadata.getExcludes();
-        }
-
-        @Override
-        public List<IvyArtifactName> getArtifacts() {
-            return dependencyMetadata.getArtifacts();
-        }
-
-        @Override
         public DependencyMetadata withTarget(ComponentSelector target) {
             return makeNonTransitive(dependencyMetadata.withTarget(target));
         }
@@ -1371,31 +1335,11 @@ public DependencyMetadata withTargetAndArtifacts(ComponentSelector target, List<
         }
 
         @Override
-        public boolean isChanging() {
-            return dependencyMetadata.isChanging();
-        }
-
-        @Override
         public boolean isTransitive() {
             return false;
         }
 
         @Override
-        public boolean isConstraint() {
-            return dependencyMetadata.isConstraint();
-        }
-
-        @Override
-        public boolean isEndorsingStrictVersions() {
-            return dependencyMetadata.isEndorsingStrictVersions();
-        }
-
-        @Override
-        public String getReason() {
-            return dependencyMetadata.getReason();
-        }
-
-        @Override
         public DependencyMetadata withReason(String reason) {
             return makeNonTransitive(dependencyMetadata.withReason(reason));
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java
index 45beea6..440b9ce 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/ResolveState.java
@@ -38,15 +38,14 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.selectors.SelectorStateResolver;
 import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.specs.Spec;
-import org.gradle.internal.component.local.model.DefaultLocalComponentGraphResolveState;
-import org.gradle.internal.component.local.model.LocalComponentMetadata;
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveState;
+import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.component.model.ComponentIdGenerator;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.VariantGraphResolveMetadata;
-import org.gradle.internal.id.IdGenerator;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.resolve.resolver.ComponentMetaDataResolver;
 import org.gradle.internal.resolve.resolver.DependencyToComponentIdResolver;
 
@@ -69,7 +68,7 @@ class ResolveState implements ComponentStateFactory<ComponentState> {
     private final Map<ResolvedConfigurationIdentifier, NodeState> nodes;
     private final Map<SelectorCacheKey, SelectorState> selectors;
     private final RootNode root;
-    private final IdGenerator<Long> idGenerator;
+    private final ComponentIdGenerator idGenerator;
     private final DependencyToComponentIdResolver idResolver;
     private final ComponentMetaDataResolver metaDataResolver;
     private final Deque<NodeState> queue;
@@ -91,8 +90,8 @@ class ResolveState implements ComponentStateFactory<ComponentState> {
     private final ResolutionConflictTracker conflictTracker;
 
     public ResolveState(
-        IdGenerator<Long> idGenerator,
-        LocalComponentMetadata rootComponentMetadata,
+        ComponentIdGenerator idGenerator,
+        LocalComponentGraphResolveState rootComponentState,
         String rootConfigurationName,
         DependencyToComponentIdResolver idResolver,
         ComponentMetaDataResolver metaDataResolver,
@@ -101,6 +100,7 @@ public ResolveState(
         ModuleExclusions moduleExclusions,
         ComponentSelectorConverter componentSelectorConverter,
         ImmutableAttributesFactory attributesFactory,
+        AttributeDesugaring attributeDesugaring,
         DependencySubstitutionApplicator dependencySubstitutionApplicator,
         VersionSelectorScheme versionSelectorScheme,
         Comparator<Version> versionComparator,
@@ -130,15 +130,16 @@ public ResolveState(
         this.conflictResolution = conflictResolution;
         this.conflictTracker = conflictTracker;
         this.resolveOptimizations = new ResolveOptimizations();
-        this.attributeDesugaring = new AttributeDesugaring(attributesFactory);
+        this.attributeDesugaring = attributeDesugaring;
         this.replaceSelectionWithConflictResultAction = new ReplaceSelectionWithConflictResultAction(this);
 
+        ComponentGraphResolveMetadata rootComponentMetadata = rootComponentState.getMetadata();
         ModuleVersionIdentifier moduleVersionId = rootComponentMetadata.getModuleVersionId();
 
         // Create root component and module
         ModuleResolveState rootModule = getModule(moduleVersionId.getModule(), true);
         ComponentState rootComponent = rootModule.getVersion(moduleVersionId, rootComponentMetadata.getId());
-        rootComponent.setState(new DefaultLocalComponentGraphResolveState(rootComponentMetadata));
+        rootComponent.setState(rootComponentState);
         rootModule.select(rootComponent);
 
         this.selectorStateResolver = new SelectorStateResolver<>(conflictResolver, this, rootComponent, resolveOptimizations, versionComparator, versionParser);
@@ -146,11 +147,15 @@ public ResolveState(
 
         // Create root node
         ResolvedConfigurationIdentifier rootNodeId = new ResolvedConfigurationIdentifier(moduleVersionId, rootConfigurationName);
-        VariantGraphResolveMetadata rootVariant = rootComponentMetadata.getConfiguration(rootConfigurationName);
-        root = new RootNode(idGenerator.generateId(), rootComponent, rootNodeId, this, syntheticDependencies, rootVariant);
+        VariantGraphResolveState rootVariant = rootComponentState.getConfiguration(rootConfigurationName).asVariant();
+        root = new RootNode(idGenerator.nextGraphNodeId(), rootComponent, rootNodeId, this, syntheticDependencies, rootVariant);
         nodes.put(rootNodeId, root);
     }
 
+    public ComponentIdGenerator getIdGenerator() {
+        return idGenerator;
+    }
+
     public ResolutionConflictTracker getConflictTracker() {
         return conflictTracker;
     }
@@ -172,7 +177,7 @@ public ModuleResolveState getModule(ModuleIdentifier id) {
     }
 
     private ModuleResolveState getModule(ModuleIdentifier id, boolean rootModule) {
-        return modules.computeIfAbsent(id, mid -> new ModuleResolveState(idGenerator, id, metaDataResolver, attributesFactory, versionComparator, versionParser, selectorStateResolver, resolveOptimizations, rootModule, conflictResolution, attributeDesugaring));
+        return modules.computeIfAbsent(id, mid -> new ModuleResolveState(idGenerator, id, metaDataResolver, attributesFactory, versionComparator, versionParser, selectorStateResolver, resolveOptimizations, rootModule, conflictResolution));
     }
 
     @Override
@@ -188,9 +193,9 @@ public Collection<NodeState> getNodes() {
         return nodes.values();
     }
 
-    public NodeState getNode(ComponentState module, VariantGraphResolveMetadata variant, boolean selectedByVariantAwareResolution) {
+    public NodeState getNode(ComponentState module, VariantGraphResolveState variant, boolean selectedByVariantAwareResolution) {
         ResolvedConfigurationIdentifier id = new ResolvedConfigurationIdentifier(module.getId(), variant.getName());
-        return nodes.computeIfAbsent(id, rci -> new NodeState(idGenerator.generateId(), id, module, this, variant, selectedByVariantAwareResolution));
+        return nodes.computeIfAbsent(id, rci -> new NodeState(idGenerator.nextGraphNodeId(), id, module, this, variant, selectedByVariantAwareResolution));
     }
 
     public Collection<SelectorState> getSelectors() {
@@ -201,7 +206,7 @@ public SelectorState getSelector(DependencyState dependencyState, boolean ignore
         boolean isVirtualPlatformEdge = dependencyState.getDependency() instanceof LenientPlatformDependencyMetadata;
         SelectorState selectorState = selectors.computeIfAbsent(new SelectorCacheKey(dependencyState.getRequested(), ignoreVersion, isVirtualPlatformEdge), req -> {
             ModuleIdentifier moduleIdentifier = dependencyState.getModuleIdentifier();
-            return new SelectorState(idGenerator.generateId(), dependencyState, idResolver, this, moduleIdentifier, ignoreVersion);
+            return new SelectorState(idGenerator.nextGraphNodeId(), dependencyState, idResolver, this, moduleIdentifier, ignoreVersion);
         });
         selectorState.update(dependencyState);
         return selectorState;
@@ -282,10 +287,6 @@ ResolvedVersionConstraint resolveVersionConstraint(VersionConstraint vc) {
         return resolvedVersionConstraints.computeIfAbsent(vc, key -> new DefaultResolvedVersionConstraint(key, versionSelectorScheme));
     }
 
-    ImmutableAttributes desugar(ImmutableAttributes attributes) {
-        return attributeDesugaring.desugar(attributes);
-    }
-
     ComponentSelector desugarSelector(ComponentSelector requested) {
         return attributeDesugaring.desugarSelector(requested);
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/RootNode.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/RootNode.java
index 5928644..535ffb3 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/RootNode.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/RootNode.java
@@ -22,7 +22,7 @@
 import org.gradle.internal.component.local.model.LocalConfigurationGraphResolveMetadata;
 import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.VariantGraphResolveMetadata;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 
 import java.util.List;
 import java.util.Set;
@@ -31,8 +31,8 @@ class RootNode extends NodeState implements RootGraphNode {
     private final ResolveOptimizations resolveOptimizations;
     private final List<? extends DependencyMetadata> syntheticDependencies;
 
-    RootNode(Long resultId, ComponentState moduleRevision, ResolvedConfigurationIdentifier id, ResolveState resolveState, List<? extends DependencyMetadata> syntheticDependencies, VariantGraphResolveMetadata configuration) {
-        super(resultId, id, moduleRevision, resolveState, configuration, false);
+    RootNode(long resultId, ComponentState moduleRevision, ResolvedConfigurationIdentifier id, ResolveState resolveState, List<? extends DependencyMetadata> syntheticDependencies, VariantGraphResolveState root) {
+        super(resultId, id, moduleRevision, resolveState, root, false);
         moduleRevision.setRoot();
         this.resolveOptimizations = resolveState.getResolveOptimizations();
         this.syntheticDependencies = syntheticDependencies;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/SelectorState.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/SelectorState.java
index 28447e7..ee9aa37 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/SelectorState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/SelectorState.java
@@ -52,6 +52,8 @@
 import java.util.Collection;
 import java.util.List;
 
+import static org.gradle.util.internal.TextUtil.getPluralEnding;
+
 /**
  * Resolution state for a given module version selector.
  *
@@ -413,7 +415,7 @@ private UnmatchedVersionsReason(Collection<String> rejectedVersions, ComponentSe
         public String getDisplayName() {
             boolean hasCustomDescription = descriptor.hasCustomDescription();
             StringBuilder sb = new StringBuilder(estimateSize(hasCustomDescription));
-            sb.append(rejectedVersions.size() > 1 ? "didn't match versions " : "didn't match version ");
+            sb.append("didn't match version").append(getPluralEnding(rejectedVersions)).append(" ");
             Joiner.on(", ").appendTo(sb, rejectedVersions);
             if (hasCustomDescription) {
                 sb.append(" because ").append(descriptor.getDescription());
@@ -471,7 +473,7 @@ private RejectedBySelectorReason(Collection<String> rejectedVersions, ComponentS
         public String getDisplayName() {
             boolean hasCustomDescription = descriptor.hasCustomDescription();
             StringBuilder sb = new StringBuilder(estimateSize(hasCustomDescription));
-            sb.append(rejectedVersions.size() > 1 ? "rejected versions " : "rejected version ");
+            sb.append("rejected version").append(getPluralEnding(rejectedVersions)).append(" ");
             Joiner.on(", ").appendTo(sb, rejectedVersions);
             if (hasCustomDescription) {
                 sb.append(" because ").append(descriptor.getDescription());
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/Conflict.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/Conflict.java
new file mode 100644
index 0000000..7b14f34
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/Conflict.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+
+import java.util.List;
+
+public class Conflict {
+    private final List<? extends ModuleVersionIdentifier> versions;
+    private final String message;
+
+    public <E> Conflict(List<? extends ModuleVersionIdentifier> versions, String message) {
+        this.versions = versions;
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public List<? extends ModuleVersionIdentifier> getVersions() {
+        return versions;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/FailOnVersionConflictArtifactsVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/FailOnVersionConflictArtifactsVisitor.java
index 7fbee24..baab745 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/FailOnVersionConflictArtifactsVisitor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/FailOnVersionConflictArtifactsVisitor.java
@@ -18,7 +18,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import org.gradle.api.artifacts.ModuleIdentifier;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.result.ComponentSelectionCause;
 import org.gradle.api.artifacts.result.ComponentSelectionDescriptor;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
@@ -27,10 +26,8 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphComponent;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.RootGraphNode;
-import org.gradle.internal.Pair;
 import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
 
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -39,7 +36,7 @@
  */
 public class FailOnVersionConflictArtifactsVisitor implements ValidatingArtifactsVisitor {
 
-    private final Set<Pair<List<? extends ModuleVersionIdentifier>, String>> allConflicts = Sets.newLinkedHashSet();
+    private final Set<Conflict> allConflicts = Sets.newLinkedHashSet();
     private final String projectPath;
     private final String configurationName;
 
@@ -62,9 +59,10 @@ public void visitNode(DependencyGraphNode node) {
         }
     }
 
-    private Pair<List<? extends ModuleVersionIdentifier>, String> buildConflict(DependencyGraphComponent owner, ComponentSelectionReason selectionReason) {
+//    private Pair<List<? extends ModuleVersionIdentifier>, String> buildConflict(DependencyGraphComponent owner, ComponentSelectionReason selectionReason) {
+    private Conflict buildConflict(DependencyGraphComponent owner, ComponentSelectionReason selectionReason) {
         ModuleIdentifier module = owner.getModuleVersion().getModule();
-        return Pair.of(ImmutableList.copyOf(owner.getAllVersions()), buildConflictMessage(module, selectionReason));
+        return new Conflict(ImmutableList.copyOf(owner.getAllVersions()), buildConflictMessage(module, selectionReason));
     }
 
     private String buildConflictMessage(ModuleIdentifier owner, ComponentSelectionReason selectionReason) {
@@ -96,7 +94,7 @@ public void finishArtifacts() {
     @Override
     public void complete() {
         if (!allConflicts.isEmpty()) {
-            throw new VersionConflictException(projectPath, configurationName, allConflicts);
+            throw VersionConflictException.create(projectPath, configurationName, allConflicts);
         }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/VersionConflictException.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/VersionConflictException.java
index cf3151e..17db6b1 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/VersionConflictException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/VersionConflictException.java
@@ -16,57 +16,85 @@
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts;
 
 import com.google.common.collect.ImmutableList;
+import org.gradle.api.GradleException;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.GraphValidationException;
-import org.gradle.internal.Pair;
+import org.gradle.internal.exceptions.ResolutionProvider;
 import org.gradle.internal.logging.text.TreeFormatter;
 
 import java.util.Collection;
 import java.util.List;
 
-public class VersionConflictException extends GraphValidationException {
-    private static final int MAX_SEEN_MODULE_COUNT = 10;
-    private final List<Pair<List<? extends ModuleVersionIdentifier>, String>> conflicts;
+import static org.gradle.util.internal.TextUtil.getPluralEnding;
 
-    public VersionConflictException(String projectPath, String configurationName, Collection<Pair<List<? extends ModuleVersionIdentifier>, String>> conflicts) {
-        super(buildMessage(projectPath, configurationName, conflicts));
-        this.conflicts = ImmutableList.copyOf(conflicts);
+public class VersionConflictException extends GraphValidationException implements ResolutionProvider {
+    private static final int MAX_SEEN_MODULE_COUNT = 10;
+    private final Collection<Conflict> conflicts;
+
+    private final List<String> resolutions;
+
+    private VersionConflictException(
+        String message,
+        Collection<Conflict> conflicts,
+        List<String> resolutions
+    ) {
+        super(message);
+        this.conflicts = conflicts;
+        this.resolutions = resolutions;
     }
 
-    public List<Pair<List<? extends ModuleVersionIdentifier>, String>> getConflicts() {
+    public Collection<Conflict> getConflicts() {
         return conflicts;
     }
 
-    private static String buildMessage(String projectPath, String configurationName, Collection<Pair<List<? extends ModuleVersionIdentifier>, String>> conflicts) {
+    private static String buildMessage(Collection<Conflict> conflicts) {
         TreeFormatter formatter = new TreeFormatter();
-        String dependencyNotation = null;
-        int count = 0;
-        formatter.node("Conflict(s) found for the following module(s)");
+
+        String plural = getPluralEnding(conflicts);
+        formatter.node("Conflict" + plural + " found for the following module" + plural);
         formatter.startChildren();
-        for (Pair<List<? extends ModuleVersionIdentifier>, String> allConflict : conflicts) {
-            if (count > MAX_SEEN_MODULE_COUNT) {
-                formatter.node("... and more");
-                break;
-            }
-            formatter.node(allConflict.right);
-            count++;
-            if (dependencyNotation == null) {
-                ModuleVersionIdentifier identifier = allConflict.getLeft().get(0);
-                dependencyNotation = identifier.getGroup() + ":" + identifier.getName();
-            }
+
+        conflicts.stream().limit(MAX_SEEN_MODULE_COUNT)
+            .forEach(conflict -> formatter.node(conflict.getMessage()));
+
+        if (conflicts.size() > MAX_SEEN_MODULE_COUNT) {
+            formatter.node("... and more");
         }
         formatter.endChildren();
-        appendInsight(projectPath, configurationName, formatter, dependencyNotation);
         return formatter.toString();
     }
 
-    private static void appendInsight(String projectPath, String configurationName, TreeFormatter formatter, String dependencyNotation) {
+    private static String getDependencyNotation(Collection<Conflict> conflicts) {
+        return conflicts.stream()
+            .findFirst()
+            .map(p -> {
+                ModuleVersionIdentifier identifier = p.getVersions().get(0);
+                return identifier.getGroup() + ":" + identifier.getName();
+            })
+            .orElseThrow(() -> new GradleException("This "));
+    }
+
+    private static List<String> createResolutions(String projectPath, String configurationName, String dependencyNotation) {
         if (projectPath.equals(":")) {
             projectPath = "";
         }
-        formatter.node("Run with:");
-        formatter.node("    --scan or");
-        formatter.node("    " + projectPath + ":dependencyInsight --configuration " + configurationName + " --dependency " + dependencyNotation);
-        formatter.node("to get more insight on how to solve the conflict.");
+
+        return ImmutableList.of("Run with " + projectPath + ":dependencyInsight --configuration " +
+            configurationName + " --dependency " + dependencyNotation + " to get more insight on how to solve the conflict.");
+    }
+
+    @Override
+    public List<String> getResolutions() {
+        return resolutions;
+    }
+
+    public static VersionConflictException create(
+        String projectPath,
+        String configurationName,
+        Collection<Conflict> conflicts
+    ) {
+        String message = buildMessage(conflicts);
+        List<String> resolutions = createResolutions(projectPath, configurationName, getDependencyNotation(conflicts));
+        return new VersionConflictException(message, ImmutableList.copyOf(conflicts), resolutions);
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/BuildIdentifierSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/BuildIdentifierSerializer.java
index d9eb8e0..c6eb1d5 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/BuildIdentifierSerializer.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/BuildIdentifierSerializer.java
@@ -22,6 +22,7 @@
 import org.gradle.internal.serialize.AbstractSerializer;
 import org.gradle.internal.serialize.Decoder;
 import org.gradle.internal.serialize.Encoder;
+import org.gradle.util.Path;
 
 import java.io.IOException;
 
@@ -40,9 +41,9 @@ public BuildIdentifier read(Decoder decoder) throws IOException {
             case ROOT:
                 return DefaultBuildIdentifier.ROOT;
             case LOCAL:
-                return new DefaultBuildIdentifier(decoder.readString());
+                return new DefaultBuildIdentifier(Path.path(decoder.readString()));
             case FOREIGN:
-                return new ForeignBuildIdentifier(decoder.readString(), decoder.readString());
+                return new ForeignBuildIdentifier(Path.path(decoder.readString()));
             default:
                 throw new IllegalArgumentException("Unexpected build identifier type.");
         }
@@ -53,13 +54,11 @@ public void write(Encoder encoder, BuildIdentifier value) throws IOException {
         if (value == DefaultBuildIdentifier.ROOT) {
             encoder.writeByte(ROOT);
         } else if (value instanceof ForeignBuildIdentifier) {
-            ForeignBuildIdentifier foreignBuildIdentifier = (ForeignBuildIdentifier) value;
             encoder.writeByte(FOREIGN);
-            encoder.writeString(foreignBuildIdentifier.getIdName());
-            encoder.writeString(value.getName());
+            encoder.writeString(value.getBuildPath());
         } else {
             encoder.writeByte(LOCAL);
-            encoder.writeString(value.getName());
+            encoder.writeString(value.getBuildPath());
         }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
index e1566aa..0ba600d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/CachingDependencyResultFactory.java
@@ -26,6 +26,7 @@
 import org.gradle.api.internal.artifacts.result.DefaultUnresolvedDependencyResult;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 
+import javax.annotation.Nullable;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -49,6 +50,7 @@ public UnresolvedDependencyResult createUnresolvedDependency(ComponentSelector r
     public ResolvedDependencyResult createResolvedDependency(ComponentSelector requested,
                                                              ResolvedComponentResult from,
                                                              ResolvedComponentResult selected,
+                                                             @Nullable
                                                              ResolvedVariantResult resolvedVariant,
                                                              boolean constraint) {
         List<Object> key = asList(requested, from, selected, resolvedVariant, constraint);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentDetailsSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentDetailsSerializer.java
new file mode 100644
index 0000000..481d757
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentDetailsSerializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.ivyservice.resolveengine.result;
+
+import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+
+import java.io.IOException;
+
+public interface ComponentDetailsSerializer {
+    void writeComponentDetails(ComponentGraphResolveState component, boolean requireAllVariants, Encoder encoder) throws IOException;
+
+    void readComponentDetails(Decoder decoder, ResolvedComponentVisitor visitor) throws IOException;
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentResultSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentResultSerializer.java
index d58ee36..97ec5e7 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentResultSerializer.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentResultSerializer.java
@@ -16,115 +16,53 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result;
 
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
-import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
-import org.gradle.api.internal.artifacts.ModuleVersionIdentifierSerializer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphComponent;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphVariant;
 import org.gradle.internal.serialize.Decoder;
 import org.gradle.internal.serialize.Encoder;
-import org.gradle.internal.serialize.Serializer;
 
 import java.io.IOException;
-import java.util.Collection;
+import java.util.List;
 
-public class ComponentResultSerializer implements Serializer<ResolvedGraphComponent> {
-
-    private final ModuleVersionIdentifierSerializer idSerializer;
+public class ComponentResultSerializer {
     private final ComponentSelectionReasonSerializer reasonSerializer;
-    private final ComponentIdentifierSerializer componentIdSerializer;
-    private final ResolvedVariantResultSerializer resolvedVariantResultSerializer;
+    private final ComponentDetailsSerializer componentDetailsSerializer;
+    private final SelectedVariantSerializer selectedVariantSerializer;
     private final boolean returnAllVariants;
 
-    public ComponentResultSerializer(ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-                                     ResolvedVariantResultSerializer resolvedVariantResultSerializer,
-                                     ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
-                                     ComponentIdentifierSerializer componentIdentifierSerializer,
-                                     boolean returnAllVariants) {
-        this.idSerializer = new ModuleVersionIdentifierSerializer(moduleIdentifierFactory);
-        this.resolvedVariantResultSerializer = resolvedVariantResultSerializer;
+    public ComponentResultSerializer(
+        ComponentDetailsSerializer componentDetailsSerializer,
+        SelectedVariantSerializer selectedVariantSerializer,
+        ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
+        boolean returnAllVariants
+    ) {
+        this.componentDetailsSerializer = componentDetailsSerializer;
+        this.selectedVariantSerializer = selectedVariantSerializer;
         this.reasonSerializer = new ComponentSelectionReasonSerializer(componentSelectionDescriptorFactory);
-        this.componentIdSerializer = componentIdentifierSerializer;
         this.returnAllVariants = returnAllVariants;
     }
 
-    void reset() {
-        resolvedVariantResultSerializer.reset();
-    }
-
-    @Override
-    public ResolvedGraphComponent read(Decoder decoder) throws IOException {
+    public void readInto(Decoder decoder, ResolvedComponentVisitor builder) throws Exception {
         long resultId = decoder.readSmallLong();
-        ModuleVersionIdentifier id = idSerializer.read(decoder);
         ComponentSelectionReason reason = reasonSerializer.read(decoder);
-        ComponentIdentifier componentId = componentIdSerializer.read(decoder);
-        int allVariantsSize = decoder.readSmallInt();
-        ImmutableList.Builder<ResolvedVariantResult> allVariants = ImmutableList.builderWithExpectedSize(allVariantsSize);
-        int resolvedVariantsSize = decoder.readSmallInt();
-        ImmutableList.Builder<ResolvedVariantResult> resolvedVariants = ImmutableList.builderWithExpectedSize(resolvedVariantsSize);
-        readVariants(decoder, allVariantsSize, resolvedVariantsSize, allVariants, resolvedVariants);
-        String repositoryName = decoder.readNullableString();
-        return new DetachedComponentResult(resultId, id, reason, componentId, resolvedVariants.build(), allVariants.build(), repositoryName);
-    }
-
-    private void readVariants(
-        Decoder decoder,
-        int allVariantsSize,
-        int resolvedVariantsSize,
-        ImmutableList.Builder<ResolvedVariantResult> allVariants,
-        ImmutableList.Builder<ResolvedVariantResult> resolvedVariants
-    ) throws IOException {
-        boolean returnAllVariants = allVariantsSize != resolvedVariantsSize;
-        for (int i = 0; i < allVariantsSize; i++) {
-            ResolvedVariantResult variant = resolvedVariantResultSerializer.read(decoder);
-            boolean isResolved;
-            if (returnAllVariants) {
-                isResolved = decoder.readBoolean();
-            } else {
-                isResolved = true;
-            }
-            if (isResolved) {
-                resolvedVariants.add(variant);
-            }
-            allVariants.add(variant);
+        builder.startVisitComponent(resultId, reason);
+        componentDetailsSerializer.readComponentDetails(decoder, builder);
+        int variantCount = decoder.readSmallInt();
+        for (int i = 0; i < variantCount; i++) {
+            selectedVariantSerializer.readSelectedVariant(decoder, builder);
         }
+        builder.endVisitComponent();
     }
 
-    @Override
     public void write(Encoder encoder, ResolvedGraphComponent value) throws IOException {
         encoder.writeSmallLong(value.getResultId());
-        idSerializer.write(encoder, value.getModuleVersion());
         reasonSerializer.write(encoder, value.getSelectionReason());
-        componentIdSerializer.write(encoder, value.getComponentId());
-        Collection<ResolvedVariantResult> allVariants;
-        Collection<ResolvedVariantResult> resolvedVariants;
-        if (returnAllVariants) {
-            allVariants = value.getAllVariants();
-            resolvedVariants = value.getResolvedVariants();
-        } else {
-            allVariants = value.getResolvedVariants();
-            resolvedVariants = allVariants;
-        }
-        writeSelectedVariantDetails(encoder, resolvedVariants, allVariants);
-        encoder.writeNullableString(value.getRepositoryName());
-    }
-
-    private void writeSelectedVariantDetails(
-        Encoder encoder, Collection<ResolvedVariantResult> resolvedVariants, Collection<ResolvedVariantResult> variants
-    ) throws IOException {
-        encoder.writeSmallInt(variants.size());
-        encoder.writeSmallInt(resolvedVariants.size());
-        boolean returnAllVariants = variants.size() != resolvedVariants.size();
-        for (ResolvedVariantResult variant : variants) {
-            resolvedVariantResultSerializer.write(encoder, variant);
-            // Optimization for when not writing all variants -- we don't need to mark which ones are resolved
-            if (returnAllVariants) {
-                encoder.writeBoolean(resolvedVariants.contains(variant));
-            }
+        componentDetailsSerializer.writeComponentDetails(value.getResolveState(), returnAllVariants, encoder);
+        List<ResolvedGraphVariant> selectedVariants = value.getSelectedVariants();
+        encoder.writeSmallInt(selectedVariants.size());
+        for (ResolvedGraphVariant variant : selectedVariants) {
+            selectedVariantSerializer.writeVariantResult(variant, encoder);
         }
     }
-
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionDescriptorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionDescriptorFactory.java
index a5dfe1f..a5fd0e6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionDescriptorFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectionDescriptorFactory.java
@@ -17,7 +17,10 @@
 
 import org.gradle.api.artifacts.result.ComponentSelectionCause;
 import org.gradle.api.artifacts.result.ComponentSelectionDescriptor;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
 
+@ServiceScope(Scopes.BuildSession.class)
 public interface ComponentSelectionDescriptorFactory {
 
     ComponentSelectionDescriptor newDescriptor(ComponentSelectionCause cause, String reason);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java
index bcbc95e..409430a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilder.java
@@ -16,6 +16,10 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ModuleVersionSelector;
 import org.gradle.api.artifacts.UnresolvedDependency;
@@ -29,7 +33,6 @@
 import org.gradle.api.artifacts.result.ResolvedDependencyResult;
 import org.gradle.api.artifacts.result.ResolvedVariantResult;
 import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphComponent;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphDependency;
 import org.gradle.api.internal.artifacts.result.DefaultResolutionResult;
 import org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult;
@@ -38,23 +41,34 @@
 import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 
+import javax.annotation.Nullable;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-public class DefaultResolutionResultBuilder {
+public class DefaultResolutionResultBuilder implements ResolvedComponentVisitor {
     private static final DefaultComponentSelectionDescriptor DEPENDENCY_LOCKING = new DefaultComponentSelectionDescriptor(ComponentSelectionCause.CONSTRAINT, Describables.of("Dependency locking"));
-    private final Map<Long, DefaultResolvedComponentResult> modules = new HashMap<>();
+    private final Long2ObjectMap<DefaultResolvedComponentResult> components = new Long2ObjectOpenHashMap<>();
     private final CachingDependencyResultFactory dependencyResultFactory = new CachingDependencyResultFactory();
     private AttributeContainer requestedAttributes;
+    private long id;
+    private ComponentSelectionReason selectionReason;
+    private ComponentIdentifier componentId;
+    private ModuleVersionIdentifier moduleVersion;
+    private String repoId;
+    private ImmutableList<ResolvedVariantResult> allVariants;
+    private final Map<Long, ResolvedVariantResult> selectedVariants = new LinkedHashMap<>();
 
     public static ResolutionResult empty(ModuleVersionIdentifier id, ComponentIdentifier componentIdentifier, AttributeContainer attributes) {
         DefaultResolutionResultBuilder builder = new DefaultResolutionResultBuilder();
         builder.setRequestedAttributes(attributes);
-        builder.visitComponent(new DetachedComponentResult(0L, id, ComponentSelectionReasons.root(), componentIdentifier, Collections.emptyList(), Collections.emptyList(), null));
+        builder.startVisitComponent(0L, ComponentSelectionReasons.root());
+        builder.visitComponentDetails(componentIdentifier, id, null);
+        builder.visitComponentVariants(Collections.emptyList());
+        builder.endVisitComponent();
         return builder.complete(0L);
     }
 
@@ -62,49 +76,84 @@ public void setRequestedAttributes(AttributeContainer attributes) {
         requestedAttributes = attributes;
     }
 
-    public ResolutionResult complete(Long rootId) {
-        return new DefaultResolutionResult(new RootFactory(modules.get(rootId)), requestedAttributes);
+    public ResolutionResult complete(long rootId) {
+        return new DefaultResolutionResult(new RootFactory(components.get(rootId)), requestedAttributes);
     }
 
-    public void visitComponent(ResolvedGraphComponent component) {
-        create(component.getResultId(), component.getModuleVersion(), component.getSelectionReason(), component.getComponentId(), component.getResolvedVariants(), component.getAllVariants(), component.getRepositoryName());
+    @Override
+    public void startVisitComponent(Long id, ComponentSelectionReason selectionReason) {
+        this.id = id;
+        this.selectionReason = selectionReason;
+        this.selectedVariants.clear();
+        this.allVariants = null;
     }
 
-    public void visitOutgoingEdges(Long fromComponent, Collection<? extends ResolvedGraphDependency> dependencies) {
-        DefaultResolvedComponentResult from = modules.get(fromComponent);
+    @Override
+    public void visitComponentDetails(ComponentIdentifier componentId, ModuleVersionIdentifier moduleVersion, @Nullable String repoId) {
+        this.componentId = componentId;
+        this.moduleVersion = moduleVersion;
+        this.repoId = repoId;
+    }
+
+    @Override
+    public void visitSelectedVariant(Long id, ResolvedVariantResult variant) {
+        selectedVariants.put(id, variant);
+    }
+
+    @Override
+    public void visitComponentVariants(List<ResolvedVariantResult> allVariants) {
+        this.allVariants = ImmutableList.copyOf(allVariants);
+    }
+
+    @Override
+    public void endVisitComponent() {
+        // The nodes in the graph represent variants (mostly) and multiple variants of a component may be included in the graph, so a given component may be visited multiple times
+        if (!components.containsKey(id)) {
+            components.put(id, new DefaultResolvedComponentResult(moduleVersion, selectionReason, componentId, ImmutableMap.copyOf(selectedVariants), allVariants, repoId));
+        }
+        selectedVariants.clear();
+        allVariants = null;
+    }
+
+    public void visitOutgoingEdges(long fromComponentId, Collection<? extends ResolvedGraphDependency> dependencies) {
+        DefaultResolvedComponentResult fromComponent = components.get(fromComponentId);
         for (ResolvedGraphDependency d : dependencies) {
             DependencyResult dependencyResult;
-            ResolvedVariantResult fromVariant = d.getFromVariant();
+            ResolvedVariantResult fromVariant = fromComponent.getVariant(d.getFromVariant());
+            if (fromVariant == null) {
+                throw new IllegalStateException("Corrupt serialized resolution result. Cannot find variant (" + d.getFromVariant() + ") for " + (d.isConstraint() ? "constraint " : "") + fromComponent + " -> " + d.getRequested().getDisplayName());
+            }
             if (d.getFailure() != null) {
-                dependencyResult = dependencyResultFactory.createUnresolvedDependency(d.getRequested(), from, d.isConstraint(), d.getReason(), d.getFailure());
+                dependencyResult = dependencyResultFactory.createUnresolvedDependency(d.getRequested(), fromComponent, d.isConstraint(), d.getReason(), d.getFailure());
             } else {
-                DefaultResolvedComponentResult selected = modules.get(d.getSelected());
-                if (selected == null) {
-                    throw new IllegalStateException("Corrupt serialized resolution result. Cannot find selected module (" + d.getSelected() + ") for " + (d.isConstraint() ? "constraint " : "") + fromVariant + " -> " + d.getRequested().getDisplayName());
+                DefaultResolvedComponentResult selectedComponent = components.get(d.getSelected().longValue());
+                if (selectedComponent == null) {
+                    throw new IllegalStateException("Corrupt serialized resolution result. Cannot find selected component (" + d.getSelected() + ") for " + (d.isConstraint() ? "constraint " : "") + fromVariant + " -> " + d.getRequested().getDisplayName());
                 }
-                dependencyResult = dependencyResultFactory.createResolvedDependency(d.getRequested(), from, selected, d.getSelectedVariant(), d.isConstraint());
-                selected.addDependent((ResolvedDependencyResult) dependencyResult);
+                ResolvedVariantResult selectedVariant;
+                if (d.getSelectedVariant() != null) {
+                    selectedVariant = selectedComponent.getVariant(d.getSelectedVariant());
+                    if (selectedVariant == null) {
+                        throw new IllegalStateException("Corrupt serialized resolution result. Cannot find selected variant (" + d.getSelectedVariant() + ") for " + (d.isConstraint() ? "constraint " : "") + fromVariant + " -> " + d.getRequested().getDisplayName());
+                    }
+                } else {
+                    selectedVariant = null;
+                }
+                dependencyResult = dependencyResultFactory.createResolvedDependency(d.getRequested(), fromComponent, selectedComponent, selectedVariant, d.isConstraint());
+                selectedComponent.addDependent((ResolvedDependencyResult) dependencyResult);
             }
-            from.addDependency(dependencyResult);
-            if (fromVariant != null) {
-                from.associateDependencyToVariant(dependencyResult, fromVariant);
-            }
+            fromComponent.addDependency(dependencyResult);
+            fromComponent.associateDependencyToVariant(dependencyResult, fromVariant);
         }
     }
 
-    private void create(Long id, ModuleVersionIdentifier moduleVersion, ComponentSelectionReason selectionReason, ComponentIdentifier componentId, List<ResolvedVariantResult> selectedVariants, List<ResolvedVariantResult> allVariants, String repoName) {
-        if (!modules.containsKey(id)) {
-            modules.put(id, new DefaultResolvedComponentResult(moduleVersion, selectionReason, componentId, selectedVariants, allVariants, repoName));
-        }
-    }
-
-    public void addExtraFailures(Long rootId, Set<UnresolvedDependency> extraFailures) {
-        DefaultResolvedComponentResult root = modules.get(rootId);
+    public void addExtraFailures(long rootId, Set<UnresolvedDependency> extraFailures) {
+        DefaultResolvedComponentResult root = components.get(rootId);
         for (UnresolvedDependency failure : extraFailures) {
             ModuleVersionSelector failureSelector = failure.getSelector();
             ModuleComponentSelector failureComponentSelector = DefaultModuleComponentSelector.newSelector(failureSelector.getModule(), failureSelector.getVersion());
             root.addDependency(dependencyResultFactory.createUnresolvedDependency(failureComponentSelector, root, true,
-                    ComponentSelectionReasons.of(DEPENDENCY_LOCKING),
+                ComponentSelectionReasons.of(DEPENDENCY_LOCKING),
                 new ModuleVersionResolveException(failureComponentSelector, () -> "Dependency lock state out of date", failure.getProblem())));
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DependencyResultSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DependencyResultSerializer.java
index e71b73e..ab956f4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DependencyResultSerializer.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DependencyResultSerializer.java
@@ -18,7 +18,6 @@
 
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphEdge;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphDependency;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
@@ -30,25 +29,27 @@
 
 public class DependencyResultSerializer {
     private final static byte SUCCESSFUL = 0;
-    private final static byte FAILED = 1;
+    private final static byte SUCCESSFUL_NOTHING_SELECTED = 1;
+    private final static byte FAILED = 2;
     private final ComponentSelectionReasonSerializer componentSelectionReasonSerializer;
-    private final ResolvedVariantResultSerializer resolvedVariantResultSerializer;
 
-    public DependencyResultSerializer(ResolvedVariantResultSerializer resolvedVariantResultSerializer, ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory) {
+    public DependencyResultSerializer(ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory) {
         this.componentSelectionReasonSerializer = new ComponentSelectionReasonSerializer(componentSelectionDescriptorFactory);
-        this.resolvedVariantResultSerializer = resolvedVariantResultSerializer;
     }
 
     public ResolvedGraphDependency read(Decoder decoder, Map<Long, ComponentSelector> selectors, Map<ComponentSelector, ModuleVersionResolveException> failures) throws IOException {
-        Long selectorId = decoder.readSmallLong();
+        long selectorId = decoder.readSmallLong();
         ComponentSelector requested = selectors.get(selectorId);
         boolean constraint = decoder.readBoolean();
-        ResolvedVariantResult fromVariant = resolvedVariantResultSerializer.read(decoder);
+        long fromVariant = decoder.readSmallLong();
         byte resultByte = decoder.readByte();
         if (resultByte == SUCCESSFUL) {
-            Long selectedId = decoder.readSmallLong();
-
-            return new DetachedResolvedGraphDependency(requested, selectedId, null, null, constraint, fromVariant, resolvedVariantResultSerializer.read(decoder));
+            long selectedId = decoder.readSmallLong();
+            long selectedVariantId = decoder.readSmallLong();
+            return new DetachedResolvedGraphDependency(requested, selectedId, null, null, constraint, fromVariant, selectedVariantId);
+        } else if (resultByte == SUCCESSFUL_NOTHING_SELECTED) {
+            long selectedId = decoder.readSmallLong();
+            return new DetachedResolvedGraphDependency(requested, selectedId, null, null, constraint, fromVariant, null);
         } else if (resultByte == FAILED) {
             ComponentSelectionReason reason = componentSelectionReasonSerializer.read(decoder);
             ModuleVersionResolveException failure = failures.get(requested);
@@ -61,11 +62,16 @@ public ResolvedGraphDependency read(Decoder decoder, Map<Long, ComponentSelector
     public void write(Encoder encoder, DependencyGraphEdge value) throws IOException {
         encoder.writeSmallLong(value.getSelector().getResultId());
         encoder.writeBoolean(value.isConstraint());
-        resolvedVariantResultSerializer.write(encoder, value.getFromVariant());
+        encoder.writeSmallLong(value.getFromVariant());
         if (value.getFailure() == null) {
-            encoder.writeByte(SUCCESSFUL);
-            encoder.writeSmallLong(value.getSelected());
-            resolvedVariantResultSerializer.write(encoder, value.getSelectedVariant());
+            if (value.getSelectedVariant() != null) {
+                encoder.writeByte(SUCCESSFUL);
+                encoder.writeSmallLong(value.getSelected());
+                encoder.writeSmallLong(value.getSelectedVariant());
+            } else {
+                encoder.writeByte(SUCCESSFUL_NOTHING_SELECTED);
+                encoder.writeSmallLong(value.getSelected());
+            }
         } else {
             encoder.writeByte(FAILED);
             componentSelectionReasonSerializer.write(encoder, value.getReason());
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DetachedComponentResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DetachedComponentResult.java
deleted file mode 100644
index 7511dae..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DetachedComponentResult.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2013 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.api.internal.artifacts.ivyservice.resolveengine.result;
-
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphComponent;
-
-import java.util.List;
-
-/**
- * A {@link ResolvedGraphComponent} implementation that is detached from the original resolution process.
- * Instances are created when de-serializing the resolution result.
- */
-public class DetachedComponentResult implements ResolvedGraphComponent {
-    private final Long resultId;
-    private final ModuleVersionIdentifier id;
-    private final ComponentSelectionReason reason;
-    private final ComponentIdentifier componentIdentifier;
-    private final List<ResolvedVariantResult> resolvedVariants;
-    private final List<ResolvedVariantResult> allVariants;
-    private final String repositoryName;
-
-    public DetachedComponentResult(Long resultId, ModuleVersionIdentifier id, ComponentSelectionReason reason, ComponentIdentifier componentIdentifier, List<ResolvedVariantResult> resolvedVariants, List<ResolvedVariantResult> allVariants, String repositoryName) {
-        this.resultId = resultId;
-        this.id = id;
-        this.reason = reason;
-        this.componentIdentifier = componentIdentifier;
-        this.resolvedVariants = resolvedVariants;
-        this.allVariants = allVariants;
-        this.repositoryName = repositoryName;
-    }
-
-    @Override
-    public Long getResultId() {
-        return resultId;
-    }
-
-    @Override
-    public ModuleVersionIdentifier getModuleVersion() {
-        return id;
-    }
-
-    @Override
-    public ComponentSelectionReason getSelectionReason() {
-        return reason;
-    }
-
-    @Override
-    public ComponentIdentifier getComponentId() {
-        return componentIdentifier;
-    }
-
-    @Override
-    public String getRepositoryName() {
-        return repositoryName;
-    }
-
-    @Override
-    public List<ResolvedVariantResult> getResolvedVariants() {
-        return resolvedVariants;
-    }
-
-    @Override
-    public List<ResolvedVariantResult> getAllVariants() {
-        return allVariants;
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DetachedResolvedGraphDependency.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DetachedResolvedGraphDependency.java
index 1d22379..b819d70 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DetachedResolvedGraphDependency.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DetachedResolvedGraphDependency.java
@@ -18,7 +18,6 @@
 
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.result.ComponentSelectionReason;
-import org.gradle.api.artifacts.result.ResolvedVariantResult;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphDependency;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 
@@ -33,16 +32,16 @@ public class DetachedResolvedGraphDependency implements ResolvedGraphDependency
     private final ComponentSelectionReason reason;
     private final ModuleVersionResolveException failure;
     private final boolean constraint;
-    private final ResolvedVariantResult fromVariant;
-    private final ResolvedVariantResult targetVariant;
+    private final Long fromVariant;
+    private final Long targetVariant;
 
     public DetachedResolvedGraphDependency(ComponentSelector requested,
                                            Long selected,
                                            ComponentSelectionReason reason,
                                            ModuleVersionResolveException failure,
                                            boolean constraint,
-                                           ResolvedVariantResult fromVariant,
-                                           ResolvedVariantResult targetVariant
+                                           Long fromVariant,
+                                           Long targetVariant
     ) {
         assert requested != null;
         assert failure != null || selected != null;
@@ -82,12 +81,12 @@ public boolean isConstraint() {
     }
 
     @Override
-    public ResolvedVariantResult getFromVariant() {
+    public Long getFromVariant() {
         return fromVariant;
     }
 
     @Override
-    public ResolvedVariantResult getSelectedVariant() {
+    public Long getSelectedVariant() {
         return targetVariant;
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedComponentResultSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedComponentResultSerializer.java
index 5cb0755..d34d6f8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedComponentResultSerializer.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedComponentResultSerializer.java
@@ -90,7 +90,7 @@ private void writeComponent(Encoder encoder, ResolvedComponentResult component,
         moduleVersionIdSerializer.write(encoder, component.getModuleVersion());
         componentIdSerializer.write(encoder, component.getId());
         componentSelectionReasonSerializer.write(encoder, component.getSelectionReason());
-        List<ResolvedVariantResult> allVariants = ((ResolvedComponentResultInternal) component).getAllVariants();
+        List<ResolvedVariantResult> allVariants = ((ResolvedComponentResultInternal) component).getAvailableVariants();
         Set<ResolvedVariantResult> resolvedVariants = new HashSet<>(component.getVariants());
         encoder.writeSmallInt(allVariants.size());
         for (ResolvedVariantResult variant : allVariants) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedComponentVisitor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedComponentVisitor.java
new file mode 100644
index 0000000..a004494
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ResolvedComponentVisitor.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.ivyservice.resolveengine.result;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ComponentSelectionReason;
+import org.gradle.api.artifacts.result.ResolvedVariantResult;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public interface ResolvedComponentVisitor {
+    /**
+     * Starts visiting a component.
+     */
+    void startVisitComponent(Long id, ComponentSelectionReason selectionReason);
+
+    /**
+     * Visit graph independent details of the component.
+     */
+    void visitComponentDetails(ComponentIdentifier componentId, ModuleVersionIdentifier moduleVersion, @Nullable String repoName);
+
+    /**
+     * Visit a selected variant of the component.
+     */
+    void visitSelectedVariant(Long id, ResolvedVariantResult variant);
+
+    /**
+     * Visit the graph dependent details of the component.
+     */
+    void visitComponentVariants(List<ResolvedVariantResult> allVariants);
+
+    /**
+     * Finishes visiting a component.
+     */
+    void endVisitComponent();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/SelectedVariantSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/SelectedVariantSerializer.java
new file mode 100644
index 0000000..a10463d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/SelectedVariantSerializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.ivyservice.resolveengine.result;
+
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphVariant;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+
+import java.io.IOException;
+
+public interface SelectedVariantSerializer {
+    void writeVariantResult(ResolvedGraphVariant variant, Encoder encoder) throws IOException;
+
+    void readSelectedVariant(Decoder decoder, ResolvedComponentVisitor visitor) throws IOException;
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java
index 4557327..b3d12c2 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilder.java
@@ -22,13 +22,11 @@
 import org.gradle.api.artifacts.result.ResolutionResult;
 import org.gradle.api.artifacts.result.ResolvedComponentResult;
 import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphComponent;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphEdge;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphSelector;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphVisitor;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphComponent;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphDependency;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.RootGraphNode;
 import org.gradle.api.internal.artifacts.result.DefaultResolutionResult;
@@ -73,17 +71,18 @@ public class StreamingResolutionResultBuilder implements DependencyGraphVisitor
     private AttributeContainer rootAttributes;
     private boolean mayHaveVirtualPlatforms;
 
-    public StreamingResolutionResultBuilder(BinaryStore store,
-                                            Store<ResolvedComponentResult> cache,
-                                            ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-                                            AttributeContainerSerializer attributeContainerSerializer,
-                                            AttributeDesugaring desugaring,
-                                            ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
-                                            boolean returnAllVariants) {
-        ComponentIdentifierSerializer componentIdentifierSerializer = new ComponentIdentifierSerializer();
-        ResolvedVariantResultSerializer resolvedVariantResultSerializer = new ResolvedVariantResultSerializer(componentIdentifierSerializer, attributeContainerSerializer);
-        this.dependencyResultSerializer = new DependencyResultSerializer(resolvedVariantResultSerializer, componentSelectionDescriptorFactory);
-        this.componentResultSerializer = new ComponentResultSerializer(moduleIdentifierFactory, resolvedVariantResultSerializer, componentSelectionDescriptorFactory, componentIdentifierSerializer, returnAllVariants);
+    public StreamingResolutionResultBuilder(
+        BinaryStore store,
+        Store<ResolvedComponentResult> cache,
+        AttributeContainerSerializer attributeContainerSerializer,
+        ComponentDetailsSerializer componentDetailsSerializer,
+        SelectedVariantSerializer selectedVariantSerializer,
+        AttributeDesugaring desugaring,
+        ComponentSelectionDescriptorFactory componentSelectionDescriptorFactory,
+        boolean returnAllVariants
+    ) {
+        this.dependencyResultSerializer = new DependencyResultSerializer(componentSelectionDescriptorFactory);
+        this.componentResultSerializer = new ComponentResultSerializer(componentDetailsSerializer, selectedVariantSerializer, componentSelectionDescriptorFactory, returnAllVariants);
         this.store = store;
         this.cache = cache;
         this.componentSelectorSerializer = new ComponentSelectorSerializer(attributeContainerSerializer);
@@ -201,7 +200,6 @@ public ResolvedComponentResult create() {
 
         private ResolvedComponentResult deserialize(Decoder decoder) {
             componentSelectorSerializer.reset();
-            componentResultSerializer.reset();
             int valuesRead = 0;
             byte type = -1;
             Timer clock = Time.startTimer();
@@ -221,8 +219,7 @@ private ResolvedComponentResult deserialize(Decoder decoder) {
                             LOG.debug("Loaded resolution results ({}) from {}", clock.getElapsed(), data);
                             return root;
                         case COMPONENT:
-                            ResolvedGraphComponent component = componentResultSerializer.read(decoder);
-                            builder.visitComponent(component);
+                            componentResultSerializer.readInto(decoder, builder);
                             break;
                         case SELECTOR:
                             Long id = decoder.readSmallLong();
@@ -244,7 +241,7 @@ private ResolvedComponentResult deserialize(Decoder decoder) {
                             throw new IOException("Unknown value type read from stream: " + type);
                     }
                 }
-            } catch (IOException e) {
+            } catch (Exception e) {
                 throw new RuntimeException("Problems loading the resolution results (" + clock.getElapsed() + "). "
                     + "Read " + valuesRead + " values, last was: " + type, e);
             }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ThisBuildOnlyComponentDetailsSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ThisBuildOnlyComponentDetailsSerializer.java
new file mode 100644
index 0000000..2564416
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ThisBuildOnlyComponentDetailsSerializer.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.ivyservice.resolveengine.result;
+
+import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import org.gradle.api.artifacts.result.ResolvedVariantResult;
+import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * A serializer used for resolution results that will be consumed from the same Gradle invocation that produces them.
+ *
+ * <p>Writes a reference to the {@link ComponentGraphResolveState} instance to build the result from, rather than persisting the associated data.</p>
+ */
+@ThreadSafe
+public class ThisBuildOnlyComponentDetailsSerializer implements ComponentDetailsSerializer {
+    private final Long2ObjectMap<ComponentGraphResolveState> components = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
+
+    @Override
+    public void writeComponentDetails(ComponentGraphResolveState component, boolean requireAllVariants, Encoder encoder) throws IOException {
+        long instanceId = component.getInstanceId();
+        components.putIfAbsent(instanceId, component);
+        encoder.writeSmallLong(instanceId);
+        encoder.writeBoolean(requireAllVariants);
+    }
+
+    @Override
+    public void readComponentDetails(Decoder decoder, ResolvedComponentVisitor visitor) throws IOException {
+        long instanceId = decoder.readSmallLong();
+        ComponentGraphResolveState component = components.get(instanceId);
+        if (component == null) {
+            throw new IllegalStateException("No component with id " + instanceId + " found.");
+        }
+        visitor.visitComponentDetails(component.getId(), component.getMetadata().getModuleVersionId(), component.getRepositoryId());
+        List<ResolvedVariantResult> availableVariants;
+        if (decoder.readBoolean()) {
+            // use all available variants
+            availableVariants = component.getAllSelectableVariantResults();
+        } else {
+            availableVariants = ImmutableList.of();
+        }
+        visitor.visitComponentVariants(availableVariants);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ThisBuildOnlySelectedVariantSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ThisBuildOnlySelectedVariantSerializer.java
new file mode 100644
index 0000000..32c4f9d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ThisBuildOnlySelectedVariantSerializer.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.ivyservice.resolveengine.result;
+
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import org.gradle.api.artifacts.result.ResolvedVariantResult;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphVariant;
+import org.gradle.internal.component.model.VariantGraphResolveState;
+import org.gradle.internal.serialize.Decoder;
+import org.gradle.internal.serialize.Encoder;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.IOException;
+
+/**
+ * A serializer used for resolution results that will be consumed from the same Gradle invocation that produces them.
+ *
+ * <p>Writes a reference to the {@link VariantGraphResolveState} instance to build the result from, rather than persisting the associated data.</p>
+ */
+@ThreadSafe
+public class ThisBuildOnlySelectedVariantSerializer implements SelectedVariantSerializer {
+    private final Long2ObjectMap<VariantGraphResolveState> variants = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
+
+    @Override
+    public void writeVariantResult(ResolvedGraphVariant variant, Encoder encoder) throws IOException {
+        encoder.writeSmallLong(variant.getNodeId());
+        VariantGraphResolveState state = variant.getResolveState();
+        writeVariantReference(encoder, state);
+        ResolvedGraphVariant externalVariant = variant.getExternalVariant();
+        if (externalVariant == null) {
+            encoder.writeBoolean(false);
+        } else {
+            encoder.writeBoolean(true);
+            writeVariantReference(encoder, externalVariant.getResolveState());
+        }
+    }
+
+    private void writeVariantReference(Encoder encoder, VariantGraphResolveState state) throws IOException {
+        long instanceId = state.getInstanceId();
+        variants.putIfAbsent(instanceId, state);
+        encoder.writeSmallLong(instanceId);
+    }
+
+    @Override
+    public void readSelectedVariant(Decoder decoder, ResolvedComponentVisitor visitor) throws IOException {
+        long nodeId = decoder.readSmallLong();
+        VariantGraphResolveState variant = readVariantReference(decoder);
+        ResolvedVariantResult externalVariant;
+        if (decoder.readBoolean()) {
+            externalVariant = readVariantReference(decoder).getVariantResult(null);
+        } else {
+            externalVariant = null;
+        }
+        visitor.visitSelectedVariant(nodeId, variant.getVariantResult(externalVariant));
+    }
+
+    private VariantGraphResolveState readVariantReference(Decoder decoder) throws IOException {
+        long instanceId = decoder.readSmallLong();
+        VariantGraphResolveState variant = variants.get(instanceId);
+        if (variant == null) {
+            throw new IllegalStateException("No variant with id " + instanceId + " found.");
+        }
+        return variant;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java
index 79eca91..5a13dfa 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/query/DefaultArtifactResolutionQuery.java
@@ -192,7 +192,7 @@ private <T extends Artifact> void addArtifacts(DefaultComponentArtifactsResult a
 
         for (ComponentArtifactMetadata artifactMetaData : artifactSetResolveResult.getResult()) {
             BuildableArtifactResolveResult resolveResult = new DefaultBuildableArtifactResolveResult();
-            artifactResolver.resolveArtifact(owner, artifactMetaData, componentState.getSources(), resolveResult);
+            artifactResolver.resolveArtifact(componentState.getResolveMetadata(), artifactMetaData, resolveResult);
             try {
                 artifacts.addArtifact(ivyFactory.verifiedArtifact(new DefaultResolvedArtifactResult(artifactMetaData.getId(), ImmutableAttributes.EMPTY, Collections.emptyList(), Describables.of(componentState.getId().getDisplayName()), type, resolveResult.getResult().getFile())));
             } catch (Exception e) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java
index 00d604c..14024e5 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractAuthenticationSupportedRepository.java
@@ -20,6 +20,7 @@
 import org.gradle.api.artifacts.repositories.PasswordCredentials;
 import org.gradle.api.credentials.Credentials;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
+import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.ProviderFactory;
@@ -35,7 +36,7 @@
 import java.util.Collections;
 import java.util.List;
 
-public abstract class AbstractAuthenticationSupportedRepository extends AbstractResolutionAwareArtifactRepository implements AuthenticationSupportedInternal {
+public abstract class AbstractAuthenticationSupportedRepository<T extends RepositoryDescriptor> extends AbstractResolutionAwareArtifactRepository<T> implements AuthenticationSupportedInternal {
     private final AuthenticationSupporter delegate;
     private final ProviderFactory providerFactory;
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractResolutionAwareArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractResolutionAwareArtifactRepository.java
index 04ee40f..d8f633f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractResolutionAwareArtifactRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/AbstractResolutionAwareArtifactRepository.java
@@ -20,20 +20,19 @@
 import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor;
 import org.gradle.api.model.ObjectFactory;
 
-public abstract class AbstractResolutionAwareArtifactRepository extends AbstractArtifactRepository implements ResolutionAwareRepository {
+public abstract class AbstractResolutionAwareArtifactRepository<T extends RepositoryDescriptor> extends AbstractArtifactRepository implements ResolutionAwareRepository {
 
-    private RepositoryDescriptor descriptor;
+    private T descriptor;
 
     protected AbstractResolutionAwareArtifactRepository(ObjectFactory objectFactory, VersionParser versionParser) {
         super(objectFactory, versionParser);
     }
 
     @Override
-    final public RepositoryDescriptor getDescriptor() {
+    final public T getDescriptor() {
         if (descriptor == null) {
             descriptor = createDescriptor();
         }
-
         return descriptor;
     }
 
@@ -41,7 +40,7 @@ protected void invalidateDescriptor() {
         descriptor = null;
     }
 
-    protected abstract RepositoryDescriptor createDescriptor();
+    protected abstract T createDescriptor();
 
     @Override
     public void setName(String name) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
index aff6f6c..a6e64f5 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepository.java
@@ -16,6 +16,7 @@
 package org.gradle.api.internal.artifacts.repositories;
 
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
 import org.gradle.api.Action;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository;
@@ -23,7 +24,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
 import org.gradle.api.internal.artifacts.repositories.descriptor.FlatDirRepositoryDescriptor;
-import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor;
+import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
 import org.gradle.api.internal.artifacts.repositories.metadata.DefaultArtifactMetadataSource;
 import org.gradle.api.internal.artifacts.repositories.metadata.DefaultImmutableMetadataSources;
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources;
@@ -57,7 +58,7 @@
 import java.util.List;
 import java.util.Set;
 
-public class DefaultFlatDirArtifactRepository extends AbstractResolutionAwareArtifactRepository implements FlatDirectoryArtifactRepository, ResolutionAwareRepository {
+public class DefaultFlatDirArtifactRepository extends AbstractResolutionAwareArtifactRepository<FlatDirRepositoryDescriptor> implements FlatDirectoryArtifactRepository, ResolutionAwareRepository {
     private final FileCollectionFactory fileCollectionFactory;
     private final List<Object> dirs = new ArrayList<>();
     private final RepositoryTransportFactory transportFactory;
@@ -130,33 +131,36 @@ public ConfiguredModuleComponentRepository createResolver() {
     }
 
     @Override
-    protected RepositoryDescriptor createDescriptor() {
-        return new FlatDirRepositoryDescriptor(
-            getName(),
-            getDirs()
-        );
+    protected FlatDirRepositoryDescriptor createDescriptor() {
+        IvyRepositoryDescriptor.Builder builder = new IvyRepositoryDescriptor.Builder(getName(), null);
+        builder.setM2Compatible(false);
+        builder.setLayoutType("Unknown");
+        builder.setMetadataSources(ImmutableList.of());
+        builder.setAuthenticated(false);
+        builder.setAuthenticationSchemes(ImmutableList.of());
+        for (File root : getDirs()) {
+            builder.addArtifactResource(root.toURI(), "/[artifact]-[revision](-[classifier]).[ext]");
+            builder.addArtifactResource(root.toURI(), "/[artifact](-[classifier]).[ext]");
+        }
+        IvyRepositoryDescriptor ivyDescriptor = builder.create();
+        return new FlatDirRepositoryDescriptor(getName(), getDirs(), ivyDescriptor);
     }
 
-
     @Override
     protected RepositoryResourceAccessor createRepositoryAccessor(RepositoryTransport transport, URI rootUri, FileStore<String> externalResourcesFileStore) {
         return new NoOpRepositoryResourceAccessor();
     }
 
     private IvyResolver createRealResolver() {
-        Set<File> dirs = getDirs();
+        FlatDirRepositoryDescriptor descriptor = getDescriptor();
+        List<File> dirs = descriptor.getDirs();
         if (dirs.isEmpty()) {
             throw new InvalidUserDataException("You must specify at least one directory for a flat directory repository.");
         }
 
         RepositoryTransport transport = transportFactory.createFileTransport(getName());
         Instantiator injector = createInjectorForMetadataSuppliers(transport, instantiatorFactory, null, null);
-        IvyResolver resolver = new IvyResolver(getName(), transport, locallyAvailableResourceFinder, false, artifactFileStore, null, null, createMetadataSources(), IvyMetadataArtifactProvider.INSTANCE, injector, checksumService);
-        for (File root : dirs) {
-            resolver.addArtifactLocation(root.toURI(), "/[artifact]-[revision](-[classifier]).[ext]");
-            resolver.addArtifactLocation(root.toURI(), "/[artifact](-[classifier]).[ext]");
-        }
-        return resolver;
+        return new IvyResolver(descriptor.getBackingDescriptor(), transport, locallyAvailableResourceFinder, false, artifactFileStore, null, null, createMetadataSources(), IvyMetadataArtifactProvider.INSTANCE, injector, checksumService);
     }
 
     private ImmutableMetadataSources createMetadataSources() {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java
index 3f45d21..e1f41d4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepository.java
@@ -16,8 +16,6 @@
 package org.gradle.api.internal.artifacts.repositories;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
 import org.gradle.api.Action;
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.ComponentMetadataListerDetails;
@@ -36,7 +34,6 @@
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
 import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
-import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor;
 import org.gradle.api.internal.artifacts.repositories.layout.AbstractRepositoryLayout;
 import org.gradle.api.internal.artifacts.repositories.layout.DefaultIvyPatternRepositoryLayout;
 import org.gradle.api.internal.artifacts.repositories.layout.GradleRepositoryLayout;
@@ -53,7 +50,6 @@
 import org.gradle.api.internal.artifacts.repositories.metadata.MetadataSource;
 import org.gradle.api.internal.artifacts.repositories.metadata.RedirectingGradleMetadataModuleMetadataSource;
 import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver;
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
 import org.gradle.api.internal.file.FileResolver;
@@ -81,7 +77,7 @@
 
 import static java.util.Collections.unmodifiableSet;
 
-public class DefaultIvyArtifactRepository extends AbstractAuthenticationSupportedRepository implements IvyArtifactRepository, ResolutionAwareRepository {
+public class DefaultIvyArtifactRepository extends AbstractAuthenticationSupportedRepository<IvyRepositoryDescriptor> implements IvyArtifactRepository, ResolutionAwareRepository {
     private volatile Set<String> schemes;
     private AbstractRepositoryLayout layout;
     private final DefaultUrlArtifactRepository urlArtifactRepository;
@@ -103,24 +99,25 @@ public class DefaultIvyArtifactRepository extends AbstractAuthenticationSupporte
     private final ChecksumService checksumService;
     private final IvyMetadataSources metadataSources = new IvyMetadataSources();
 
-    public DefaultIvyArtifactRepository(FileResolver fileResolver,
-                                        RepositoryTransportFactory transportFactory,
-                                        LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder,
-                                        FileStore<ModuleComponentArtifactIdentifier> artifactFileStore,
-                                        FileStore<String> externalResourcesFileStore,
-                                        AuthenticationContainer authenticationContainer,
-                                        IvyContextManager ivyContextManager,
-                                        ImmutableModuleIdentifierFactory moduleIdentifierFactory,
-                                        InstantiatorFactory instantiatorFactory,
-                                        FileResourceRepository fileResourceRepository,
-                                        GradleModuleMetadataParser moduleMetadataParser,
-                                        IvyMutableModuleMetadataFactory metadataFactory,
-                                        IsolatableFactory isolatableFactory,
-                                        ObjectFactory objectFactory,
-                                        DefaultUrlArtifactRepository.Factory urlArtifactRepositoryFactory,
-                                        ChecksumService checksumService,
-                                        ProviderFactory providerFactory,
-                                        VersionParser versionParser
+    public DefaultIvyArtifactRepository(
+        FileResolver fileResolver,
+        RepositoryTransportFactory transportFactory,
+        LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder,
+        FileStore<ModuleComponentArtifactIdentifier> artifactFileStore,
+        FileStore<String> externalResourcesFileStore,
+        AuthenticationContainer authenticationContainer,
+        IvyContextManager ivyContextManager,
+        ImmutableModuleIdentifierFactory moduleIdentifierFactory,
+        InstantiatorFactory instantiatorFactory,
+        FileResourceRepository fileResourceRepository,
+        GradleModuleMetadataParser moduleMetadataParser,
+        IvyMutableModuleMetadataFactory metadataFactory,
+        IsolatableFactory isolatableFactory,
+        ObjectFactory objectFactory,
+        DefaultUrlArtifactRepository.Factory urlArtifactRepositoryFactory,
+        ChecksumService checksumService,
+        ProviderFactory providerFactory,
+        VersionParser versionParser
     ) {
         super(instantiatorFactory.decorateLenient(), authenticationContainer, objectFactory, providerFactory, versionParser);
         this.fileResolver = fileResolver;
@@ -163,51 +160,24 @@ public ConfiguredModuleComponentRepository createResolver() {
     }
 
     @Override
-    protected RepositoryDescriptor createDescriptor() {
+    protected IvyRepositoryDescriptor createDescriptor() {
         Set<String> schemes = getSchemes();
         validate(schemes);
 
-        String layoutType;
-        boolean m2Compatible;
-        if (layout instanceof GradleRepositoryLayout) {
-            layoutType = "Gradle";
-            m2Compatible = false;
-        } else if (layout instanceof MavenRepositoryLayout) {
-            layoutType = "Maven";
-            m2Compatible = true;
-        } else if (layout instanceof IvyRepositoryLayout) {
-            layoutType = "Ivy";
-            m2Compatible = false;
-        } else if (layout instanceof DefaultIvyPatternRepositoryLayout) {
-            layoutType = "Pattern";
-            m2Compatible = ((DefaultIvyPatternRepositoryLayout) layout).getM2Compatible();
-        } else {
-            layoutType = "Unknown";
-            m2Compatible = false;
-        }
-
-        return new IvyRepositoryDescriptor.Builder(getName(), urlArtifactRepository.getUrl())
+        URI url = urlArtifactRepository.getUrl();
+        IvyRepositoryDescriptor.Builder builder = new IvyRepositoryDescriptor.Builder(getName(), url)
             .setAuthenticated(usesCredentials())
             .setAuthenticationSchemes(getAuthenticationSchemes())
-            .setMetadataSources(metadataSources.asList())
-            .setLayoutType(layoutType)
-            .setM2Compatible(m2Compatible)
-            .setIvyPatterns(Sets.union(layout.getIvyPatterns(), additionalPatternsLayout.ivyPatterns))
-            .setArtifactPatterns(Sets.union(layout.getArtifactPatterns(), additionalPatternsLayout.artifactPatterns))
-            .create();
+            .setMetadataSources(metadataSources.asList());
+        layout.apply(url, builder);
+        additionalPatternsLayout.apply(url, builder);
+        return builder.create();
     }
 
     private IvyResolver createRealResolver() {
         Set<String> schemes = getSchemes();
         validate(schemes);
-
-        IvyResolver resolver = createResolver(schemes);
-        @Nullable
-        URI uri = urlArtifactRepository.getUrl();
-        layout.apply(uri, resolver);
-        additionalPatternsLayout.apply(uri, resolver);
-
-        return resolver;
+        return createResolver(schemes);
     }
 
     private IvyResolver createResolver(Set<String> schemes) {
@@ -238,7 +208,7 @@ private IvyResolver createResolver(RepositoryTransport transport) {
         Instantiator injector = createInjectorForMetadataSuppliers(transport, instantiatorFactory, getUrl(), externalResourcesFileStore);
         InstantiatingAction<ComponentMetadataSupplierDetails> supplierFactory = createComponentMetadataSupplierFactory(injector, isolatableFactory);
         InstantiatingAction<ComponentMetadataListerDetails> listerFactory = createComponentMetadataVersionLister(injector, isolatableFactory);
-        return new IvyResolver(getName(), transport, locallyAvailableResourceFinder, metaDataProvider.dynamicResolve, artifactFileStore, supplierFactory, listerFactory, createMetadataSources(), IvyMetadataArtifactProvider.INSTANCE, injector, checksumService);
+        return new IvyResolver(getDescriptor(), transport, locallyAvailableResourceFinder, metaDataProvider.dynamicResolve, artifactFileStore, supplierFactory, listerFactory, createMetadataSources(), IvyMetadataArtifactProvider.INSTANCE, injector, checksumService);
     }
 
     @Override
@@ -343,11 +313,11 @@ public void ivyPattern(String pattern) {
     }
 
     public Set<String> additionalArtifactPatterns() {
-        return additionalPatternsLayout.getArtifactPatterns();
+        return additionalPatternsLayout.artifactPatterns;
     }
 
     public Set<String> additionalIvyPatterns() {
-        return additionalPatternsLayout.getIvyPatterns();
+        return additionalPatternsLayout.ivyPatterns;
     }
 
     @Override
@@ -371,6 +341,7 @@ public void layout(String layoutName) {
 
     @Override
     public void patternLayout(Action<? super IvyPatternRepositoryLayout> config) {
+        invalidateDescriptor();
         DefaultIvyPatternRepositoryLayout layout = instantiator.newInstance(DefaultIvyPatternRepositoryLayout.class);
         this.layout = layout;
         config.execute(layout);
@@ -390,6 +361,12 @@ public void setRepositoryLayout(AbstractRepositoryLayout layout) {
         this.layout = layout;
     }
 
+    @Override
+    protected void invalidateDescriptor() {
+        super.invalidateDescriptor();
+        schemes = null;
+    }
+
     /**
      * Layout for applying additional patterns added via {@link #artifactPatterns} and {@link #ivyPatterns}.
      */
@@ -403,16 +380,20 @@ public AdditionalPatternsRepositoryLayout(FileResolver fileResolver) {
         }
 
         @Override
-        public void apply(URI baseUri, PatternBasedResolver resolver) {
+        public void apply(@Nullable URI baseUri, IvyRepositoryDescriptor.Builder builder) {
             for (String artifactPattern : artifactPatterns) {
                 ResolvedPattern resolvedPattern = new ResolvedPattern(artifactPattern, fileResolver);
-                resolver.addArtifactLocation(resolvedPattern.baseUri, resolvedPattern.pattern);
+                builder.addArtifactPattern(artifactPattern);
+                builder.addArtifactResource(resolvedPattern.baseUri, resolvedPattern.pattern);
             }
 
-            Set<String> usedIvyPatterns = ivyPatterns.isEmpty() ? artifactPatterns : ivyPatterns;
-            for (String ivyPattern : usedIvyPatterns) {
+            for (String ivyPattern : ivyPatterns) {
+                builder.addIvyPattern(ivyPattern);
+            }
+            Set<String> effectiveIvyPatterns = ivyPatterns.isEmpty() ? artifactPatterns : ivyPatterns;
+            for (String ivyPattern : effectiveIvyPatterns) {
                 ResolvedPattern resolvedPattern = new ResolvedPattern(ivyPattern, fileResolver);
-                resolver.addDescriptorLocation(resolvedPattern.baseUri, resolvedPattern.pattern);
+                builder.addIvyResource(resolvedPattern.baseUri, resolvedPattern.pattern);
             }
         }
 
@@ -425,16 +406,6 @@ public void addSchemes(URI baseUri, Set<String> schemes) {
                 schemes.add(new ResolvedPattern(pattern, fileResolver).scheme);
             }
         }
-
-        @Override
-        public Set<String> getIvyPatterns() {
-            return ImmutableSet.copyOf(ivyPatterns);
-        }
-
-        @Override
-        public Set<String> getArtifactPatterns() {
-            return ImmutableSet.copyOf(artifactPatterns);
-        }
     }
 
     private static class MetaDataProvider implements IvyArtifactRepositoryMetaDataProvider {
@@ -451,7 +422,7 @@ public void setDynamicMode(boolean mode) {
         }
     }
 
-    private static class IvyMetadataSources implements MetadataSources {
+    private class IvyMetadataSources implements MetadataSources {
         boolean gradleMetadata;
         boolean ivyDescriptor;
         boolean artifact;
@@ -470,8 +441,8 @@ void reset() {
         }
 
         /**
-         * This is used for reporting purposes on build scans.
-         * Changing this means a change of repository for build scans.
+         * This is used to generate the repository id and for reporting purposes on build scans.
+         * Changing this means a change of repository.
          *
          * @return a list of implemented metadata sources, as strings.
          */
@@ -494,21 +465,25 @@ List<String> asList() {
 
         @Override
         public void gradleMetadata() {
+            invalidateDescriptor();
             gradleMetadata = true;
         }
 
         @Override
         public void ivyDescriptor() {
+            invalidateDescriptor();
             ivyDescriptor = true;
         }
 
         @Override
         public void artifact() {
+            invalidateDescriptor();
             artifact = true;
         }
 
         @Override
         public void ignoreGradleMetadataRedirection() {
+            invalidateDescriptor();
             ignoreGradleMetadataRedirection = true;
         }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
index 5e91c00..2bcd7fc 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepository.java
@@ -33,7 +33,6 @@
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MetaDataParser;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
 import org.gradle.api.internal.artifacts.repositories.descriptor.MavenRepositoryDescriptor;
-import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor;
 import org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader;
 import org.gradle.api.internal.artifacts.repositories.metadata.DefaultArtifactMetadataSource;
 import org.gradle.api.internal.artifacts.repositories.metadata.DefaultGradleModuleMetadataSource;
@@ -57,12 +56,12 @@
 import org.gradle.internal.action.InstantiatingAction;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.maven.MutableMavenModuleResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
 import org.gradle.internal.hash.ChecksumService;
-import org.gradle.internal.hash.Hasher;
 import org.gradle.internal.instantiation.InstantiatorFactory;
 import org.gradle.internal.isolation.IsolatableFactory;
 import org.gradle.internal.reflect.Instantiator;
@@ -80,7 +79,7 @@
 import java.util.List;
 import java.util.Set;
 
-public class DefaultMavenArtifactRepository extends AbstractAuthenticationSupportedRepository implements MavenArtifactRepository, ResolutionAwareRepository {
+public class DefaultMavenArtifactRepository extends AbstractAuthenticationSupportedRepository<MavenRepositoryDescriptor> implements MavenArtifactRepository, ResolutionAwareRepository {
     private static final DefaultMavenPomMetadataSource.MavenMetadataValidator NO_OP_VALIDATION_SERVICES = (repoName, metadata, artifactResolver) -> true;
 
     private final Transformer<String, MavenArtifactRepository> describer;
@@ -99,7 +98,7 @@ public class DefaultMavenArtifactRepository extends AbstractAuthenticationSuppor
     private final ChecksumService checksumService;
     private final MavenMetadataSources metadataSources = new MavenMetadataSources();
     private final InstantiatorFactory instantiatorFactory;
-    private VersionParser versionParser;
+    private final VersionParser versionParser;
 
     public DefaultMavenArtifactRepository(FileResolver fileResolver,
                                           RepositoryTransportFactory transportFactory,
@@ -223,7 +222,7 @@ public void setArtifactUrls(Iterable<?> urls) {
     }
 
     @Override
-    protected RepositoryDescriptor createDescriptor() {
+    protected MavenRepositoryDescriptor createDescriptor() {
         URI rootUri = validateUrl();
         return new MavenRepositoryDescriptor.Builder(getName(), rootUri)
             .setAuthenticated(usesCredentials())
@@ -253,12 +252,7 @@ protected URI validateUrl() {
     @Override
     public ConfiguredModuleComponentRepository createResolver() {
         URI rootUrl = validateUrl();
-        MavenResolver resolver = createResolver(rootUrl);
-
-        for (URI repoUrl : getArtifactUrls()) {
-            resolver.addArtifactLocation(repoUrl);
-        }
-        return resolver;
+        return createResolver(rootUrl);
     }
 
     private MavenResolver createResolver(URI rootUri) {
@@ -268,7 +262,7 @@ private MavenResolver createResolver(URI rootUri) {
         Instantiator injector = createInjectorForMetadataSuppliers(transport, instantiatorFactory, getUrl(), resourcesFileStore);
         InstantiatingAction<ComponentMetadataSupplierDetails> supplier = createComponentMetadataSupplierFactory(injector, isolatableFactory);
         InstantiatingAction<ComponentMetadataListerDetails> lister = createComponentMetadataVersionLister(injector, isolatableFactory);
-        return new MavenResolver(getName(), rootUri, transport, locallyAvailableResourceFinder, artifactFileStore, metadataSources, MavenMetadataArtifactProvider.INSTANCE, mavenMetadataLoader, supplier, lister, injector, checksumService);
+        return new MavenResolver(getDescriptor(), rootUri, transport, locallyAvailableResourceFinder, artifactFileStore, metadataSources, MavenMetadataArtifactProvider.INSTANCE, mavenMetadataLoader, supplier, lister, injector, checksumService);
     }
 
     @Override
@@ -370,7 +364,7 @@ public String transform(MavenArtifactRepository repository) {
         }
     }
 
-    private static class MavenMetadataSources implements MetadataSources {
+    private class MavenMetadataSources implements MetadataSources {
         boolean gradleMetadata;
         boolean mavenPom;
         boolean artifact;
@@ -389,8 +383,8 @@ void reset() {
         }
 
         /**
-         * This is used for reporting purposes on build scans.
-         * Changing this means a change of repository for build scans.
+         * This is used to generate the repository id and for reporting purposes on build scans.
+         * Changing this means a change of repository.
          *
          * @return a list of implemented metadata sources, as strings.
          */
@@ -413,21 +407,25 @@ List<String> asList() {
 
         @Override
         public void gradleMetadata() {
+            invalidateDescriptor();
             gradleMetadata = true;
         }
 
         @Override
         public void mavenPom() {
+            invalidateDescriptor();
             mavenPom = true;
         }
 
         @Override
         public void artifact() {
+            invalidateDescriptor();
             artifact = true;
         }
 
         @Override
         public void ignoreGradleMetadataRedirection() {
+            invalidateDescriptor();
             ignoreGradleMetadataRedirection = true;
         }
 
@@ -453,8 +451,6 @@ public boolean isIgnoreGradleMetadataRedirectionEnabled() {
     }
 
     private static class MavenSnapshotDecoratingSource implements MetadataSource<MutableModuleComponentResolveMetadata> {
-        private static final int HASH_ID = 30155977;
-
         private final MetadataSource<MutableModuleComponentResolveMetadata> delegate;
 
         private MavenSnapshotDecoratingSource(MetadataSource<MutableModuleComponentResolveMetadata> delegate) {
@@ -462,7 +458,7 @@ private MavenSnapshotDecoratingSource(MetadataSource<MutableModuleComponentResol
         }
 
         @Override
-        public MutableModuleComponentResolveMetadata create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult result) {
+        public MutableModuleComponentResolveMetadata create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
             MutableModuleComponentResolveMetadata metadata = delegate.create(repositoryName, componentResolvers, moduleComponentIdentifier, prescribedMetaData, artifactResolver, result);
             if (metadata != null) {
                 return MavenResolver.processMetaData((MutableMavenModuleResolveMetadata) metadata);
@@ -474,11 +470,5 @@ public MutableModuleComponentResolveMetadata create(String repositoryName, Compo
         public void listModuleVersions(ModuleDependencyMetadata dependency, ModuleIdentifier module, List<ResourcePattern> ivyPatterns, List<ResourcePattern> artifactPatterns, VersionLister versionLister, BuildableModuleVersionListingResolveResult result) {
             delegate.listModuleVersions(dependency, module, ivyPatterns, artifactPatterns, versionLister, result);
         }
-
-        @Override
-        public void appendId(Hasher hasher) {
-            hasher.putInt(HASH_ID);
-            delegate.appendId(hasher);
-        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java
index e6f080b..2ca48c6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultMavenLocalArtifactRepository.java
@@ -76,8 +76,8 @@ public MavenResolver createResolver() {
         RepositoryTransport transport = getTransport(rootUri.getScheme());
         MavenMetadataLoader mavenMetadataLoader = new MavenMetadataLoader(transport.getResourceAccessor(), getResourcesFileStore());
         Instantiator injector = createInjectorForMetadataSuppliers(transport, getInstantiatorFactory(), rootUri, getResourcesFileStore());
-        MavenResolver resolver = new MavenResolver(
-            getName(),
+        return new MavenResolver(
+            getDescriptor(),
             rootUri,
             transport,
             getLocallyAvailableResourceFinder(),
@@ -89,10 +89,6 @@ public MavenResolver createResolver() {
             null,
             injector,
             checksumService);
-        for (URI repoUrl : getArtifactUrls()) {
-            resolver.addArtifactLocation(repoUrl);
-        }
-        return resolver;
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultUrlArtifactRepository.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultUrlArtifactRepository.java
index a2d7fc5..b246a82 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultUrlArtifactRepository.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/DefaultUrlArtifactRepository.java
@@ -86,50 +86,29 @@ public URI validateUrl() {
     }
 
     private void throwExceptionDueToInsecureProtocol() throws InvalidUserCodeException {
-        String switchToAdvice =
-            String.format(
-                "Switch %s repository '%s' to redirect to a secure protocol (like HTTPS) or allow insecure protocols. ",
-                repositoryType,
-                displayNameSupplier.get()
-            );
-        String dslMessage =
-            Documentation
-                .dslReference(UrlArtifactRepository.class, "allowInsecureProtocol")
-                .consultDocumentationMessage() + " ";
-        String message =
-            "Using insecure protocols with repositories, without explicit opt-in, is unsupported. " +
-                switchToAdvice +
-                dslMessage;
-        throw new InvalidUserCodeException(message);
+        throw new InsecureProtocolException(
+            "Using insecure protocols with repositories, without explicit opt-in, is unsupported.",
+            String.format("Switch %s repository '%s' to redirect to a secure protocol (like HTTPS) or allow insecure protocols.", repositoryType, displayNameSupplier.get()),
+            Documentation.dslReference(UrlArtifactRepository.class, "allowInsecureProtocol").consultDocumentationMessage()
+        );
     }
 
     private void throwExceptionDueToInsecureRedirect(@Nullable URI redirectFrom, URI redirectLocation) throws InvalidUserCodeException {
         final String contextualAdvice;
         if (redirectFrom != null) {
             contextualAdvice = String.format(
-                "'%s' is redirecting to '%s'. ",
+                " '%s' is redirecting to '%s'. ",
                 redirectFrom,
                 redirectLocation
             );
         } else {
             contextualAdvice = "";
         }
-        String switchToAdvice =
-            String.format(
-                "Switch %s repository '%s' to redirect to a secure protocol (like HTTPS) or allow insecure protocols. ",
-                repositoryType,
-                displayNameSupplier.get()
-            );
-        String dslMessage =
-            Documentation
-                .dslReference(UrlArtifactRepository.class, "allowInsecureProtocol")
-                .consultDocumentationMessage() + " ";
-        String message =
-            "Redirecting from secure protocol to insecure protocol, without explict opt-in, is unsupported. " +
-                contextualAdvice +
-                switchToAdvice +
-                dslMessage;
-        throw new InvalidUserCodeException(message);
+        throw new InsecureProtocolException(
+            "Redirecting from secure protocol to insecure protocol, without explicit opt-in, is unsupported." + contextualAdvice,
+            String.format("Switch %s repository '%s' to redirect to a secure protocol (like HTTPS) or allow insecure protocols. ", repositoryType, displayNameSupplier.get()),
+            Documentation.dslReference(UrlArtifactRepository.class, "allowInsecureProtocol").consultDocumentationMessage()
+        );
     }
 
     HttpRedirectVerifier createRedirectVerifier() {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/InsecureProtocolException.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/InsecureProtocolException.java
new file mode 100644
index 0000000..de093c0
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/InsecureProtocolException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.repositories;
+
+import org.gradle.api.InvalidUserCodeException;
+import org.gradle.internal.exceptions.ResolutionProvider;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class InsecureProtocolException extends InvalidUserCodeException implements ResolutionProvider {
+
+    private final List<String> resolutions;
+
+    public InsecureProtocolException(String message, String... resolutions) {
+        super(message);
+        this.resolutions = Arrays.asList(resolutions);
+    }
+
+    @Override
+    public List<String> getResolutions() {
+        return resolutions;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/FlatDirRepositoryDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/FlatDirRepositoryDescriptor.java
index 99a15c1..da287d8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/FlatDirRepositoryDescriptor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/FlatDirRepositoryDescriptor.java
@@ -30,11 +30,13 @@ private enum Property {
         DIRS,
     }
 
-    public final ImmutableList<File> dirs;
+    private final ImmutableList<File> dirs;
+    private final IvyRepositoryDescriptor backingDescriptor;
 
-    public FlatDirRepositoryDescriptor(String name, Collection<File> dirs) {
-        super(name);
+    public FlatDirRepositoryDescriptor(String name, Collection<File> dirs, IvyRepositoryDescriptor backingDescriptor) {
+        super(backingDescriptor.getId(), name);
         this.dirs = ImmutableList.copyOf(dirs);
+        this.backingDescriptor = backingDescriptor;
     }
 
     @Override
@@ -42,6 +44,14 @@ public Type getType() {
         return Type.FLAT_DIR;
     }
 
+    public ImmutableList<File> getDirs() {
+        return dirs;
+    }
+
+    public IvyRepositoryDescriptor getBackingDescriptor() {
+        return backingDescriptor;
+    }
+
     @Override
     protected void addProperties(ImmutableSortedMap.Builder<String, Object> builder) {
         builder.put(Property.DIRS.name(), dirs);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/IvyRepositoryDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/IvyRepositoryDescriptor.java
index 3ad12ea..745916c 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/IvyRepositoryDescriptor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/IvyRepositoryDescriptor.java
@@ -18,10 +18,16 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
+import org.gradle.api.NonNullApi;
+import org.gradle.api.internal.artifacts.repositories.resolver.IvyResolver;
+import org.gradle.api.internal.artifacts.repositories.resolver.IvyResourcePattern;
+import org.gradle.api.internal.artifacts.repositories.resolver.M2ResourcePattern;
+import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
 import org.gradle.internal.scan.UsedByScanPlugin;
 
+import javax.annotation.Nullable;
 import java.net.URI;
-import java.util.Collection;
+import java.util.ArrayList;
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -36,34 +42,59 @@ private enum Property {
         M2_COMPATIBLE
     }
 
-    public final ImmutableList<String> ivyPatterns;
-    public final ImmutableList<String> artifactPatterns;
-    public final String layoutType;
-    public final boolean m2Compatible;
+    private final ImmutableList<String> ivyPatterns;
+    private final ImmutableList<ResourcePattern> ivyResources;
+    private final ImmutableList<String> artifactPatterns;
+    private final ImmutableList<ResourcePattern> artifactResources;
+    private final String layoutType;
+    private final boolean m2Compatible;
 
     private IvyRepositoryDescriptor(
+        String id,
         String name,
         URI url,
         ImmutableList<String> metadataSources,
         boolean authenticated,
         ImmutableList<String> authenticationSchemes,
         ImmutableList<String> ivyPatterns,
+        ImmutableList<ResourcePattern> ivyResources,
         ImmutableList<String> artifactPatterns,
+        ImmutableList<ResourcePattern> artifactResources,
         String layoutType,
         boolean m2Compatible
     ) {
-        super(name, url, metadataSources, authenticated, authenticationSchemes);
+        super(id, name, url, metadataSources, authenticated, authenticationSchemes);
         this.ivyPatterns = ivyPatterns;
+        this.ivyResources = ivyResources;
         this.artifactPatterns = artifactPatterns;
+        this.artifactResources = artifactResources;
         this.layoutType = layoutType;
         this.m2Compatible = m2Compatible;
     }
 
     @Override
+    public ImmutableList<ResourcePattern> getMetadataResources() {
+        return ivyResources;
+    }
+
+    @Override
+    public ImmutableList<ResourcePattern> getArtifactResources() {
+        return artifactResources;
+    }
+
+    @Override
     public Type getType() {
         return Type.IVY;
     }
 
+    public List<String> getArtifactPatterns() {
+        return artifactPatterns;
+    }
+
+    public boolean isM2Compatible() {
+        return m2Compatible;
+    }
+
     @Override
     protected void addProperties(ImmutableSortedMap.Builder<String, Object> builder) {
         super.addProperties(builder);
@@ -73,33 +104,48 @@ protected void addProperties(ImmutableSortedMap.Builder<String, Object> builder)
         builder.put(Property.M2_COMPATIBLE.name(), m2Compatible);
     }
 
-    public List<String> getIvyPatterns() {
-        return ivyPatterns;
-    }
+    @NonNullApi
+    private static class Resource {
+        final URI baseUri;
+        final String pattern;
 
-    public List<String> getArtifactPatterns() {
-        return artifactPatterns;
+        public Resource(URI baseUri, String pattern) {
+            this.baseUri = baseUri;
+            this.pattern = pattern;
+        }
     }
 
     public static class Builder extends UrlRepositoryDescriptor.Builder<Builder> {
-
-        private ImmutableList<String> ivyPatterns;
-        private ImmutableList<String> artifactPatterns;
+        private final List<String> ivyPatterns = new ArrayList<>();
+        private final List<String> artifactPatterns = new ArrayList<>();
         private String layoutType;
         private Boolean m2Compatible;
+        // Artifact resources derived from other configuration
+        private final List<Resource> ivyResources = new ArrayList<>();
+        private final List<Resource> artifactResources = new ArrayList<>();
 
         public Builder(String name, URI url) {
             super(name, url);
         }
 
-        public Builder setIvyPatterns(Collection<String> ivyPatterns) {
-            this.ivyPatterns = ImmutableList.copyOf(ivyPatterns);
-            return this;
+        public void addIvyPattern(String declaredPattern) {
+            ivyPatterns.add(declaredPattern);
         }
 
-        public Builder setArtifactPatterns(Collection<String> artifactPatterns) {
-            this.artifactPatterns = ImmutableList.copyOf(artifactPatterns);
-            return this;
+        public void addIvyResource(@Nullable URI baseUri, String pattern) {
+            if (baseUri != null) {
+                ivyResources.add(new Resource(baseUri, pattern));
+            }
+        }
+
+        public void addArtifactPattern(String declaredPattern) {
+            artifactPatterns.add(declaredPattern);
+        }
+
+        public void addArtifactResource(@Nullable URI rootUri, String pattern) {
+            if (rootUri != null) {
+                artifactResources.add(new Resource(rootUri, pattern));
+            }
         }
 
         public Builder setLayoutType(String layoutType) {
@@ -113,17 +159,41 @@ public Builder setM2Compatible(boolean m2Compatible) {
         }
 
         public IvyRepositoryDescriptor create() {
+            checkNotNull(m2Compatible);
+            checkNotNull(metadataSources);
+
+            ImmutableList.Builder<ResourcePattern> ivyResourcesBuilder = ImmutableList.builderWithExpectedSize(ivyPatterns.size());
+            for (Resource resource : ivyResources) {
+                ivyResourcesBuilder.add(toResourcePattern(resource.baseUri, resource.pattern));
+            }
+            ImmutableList.Builder<ResourcePattern> artifactResourcesBuilder = ImmutableList.builderWithExpectedSize(artifactPatterns.size() + artifactResources.size());
+            for (Resource resource : artifactResources) {
+                artifactResourcesBuilder.add(toResourcePattern(resource.baseUri, resource.pattern));
+            }
+
+            ImmutableList<ResourcePattern> effectiveIvyResources = ivyResourcesBuilder.build();
+            ImmutableList<ResourcePattern> effectiveArtifactResources = artifactResourcesBuilder.build();
+
+            String id = calculateId(IvyResolver.class, effectiveIvyResources, effectiveArtifactResources, metadataSources, hasher -> hasher.putBoolean(m2Compatible));
+
             return new IvyRepositoryDescriptor(
+                id,
                 checkNotNull(name),
                 url,
-                checkNotNull(metadataSources),
+                metadataSources,
                 checkNotNull(authenticated),
                 checkNotNull(authenticationSchemes),
-                checkNotNull(ivyPatterns),
-                checkNotNull(artifactPatterns),
+                ImmutableList.copyOf(ivyPatterns),
+                effectiveIvyResources,
+                ImmutableList.copyOf(artifactPatterns),
+                effectiveArtifactResources,
                 checkNotNull(layoutType),
-                checkNotNull(m2Compatible)
+                m2Compatible
             );
         }
+
+        private ResourcePattern toResourcePattern(URI baseUri, String pattern) {
+            return m2Compatible ? new M2ResourcePattern(baseUri, pattern) : new IvyResourcePattern(baseUri, pattern);
+        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/MavenRepositoryDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/MavenRepositoryDescriptor.java
index a7a6e3f..a035407 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/MavenRepositoryDescriptor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/MavenRepositoryDescriptor.java
@@ -18,6 +18,10 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
+import org.gradle.api.internal.artifacts.repositories.resolver.M2ResourcePattern;
+import org.gradle.api.internal.artifacts.repositories.resolver.MavenPattern;
+import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver;
+import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
 import org.gradle.internal.scan.UsedByScanPlugin;
 
 import java.net.URI;
@@ -25,25 +29,31 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
-public final class MavenRepositoryDescriptor extends UrlRepositoryDescriptor {
-
+public class MavenRepositoryDescriptor extends UrlRepositoryDescriptor {
     @UsedByScanPlugin("doesn't link against this type, but expects these values - See ResolveConfigurationDependenciesBuildOperationType")
     private enum Property {
         ARTIFACT_URLS,
     }
 
-    public final ImmutableList<URI> artifactUrls;
+    private final ImmutableList<ResourcePattern> metadataResources;
+    private final ImmutableList<URI> artifactUrls;
+    private final ImmutableList<ResourcePattern> artifactResources;
 
     private MavenRepositoryDescriptor(
+        String id,
         String name,
         URI url,
+        ImmutableList<ResourcePattern> metadataResources,
+        ImmutableList<ResourcePattern> artifactResources,
         ImmutableList<String> metadataSources,
         boolean authenticated,
         ImmutableList<String> authenticationSchemes,
         ImmutableList<URI> artifactUrls
     ) {
-        super(name, url, metadataSources, authenticated, authenticationSchemes);
+        super(id, name, url, metadataSources, authenticated, authenticationSchemes);
         this.artifactUrls = artifactUrls;
+        this.metadataResources = metadataResources;
+        this.artifactResources = artifactResources;
     }
 
     @Override
@@ -52,6 +62,16 @@ public Type getType() {
     }
 
     @Override
+    public ImmutableList<ResourcePattern> getMetadataResources() {
+        return metadataResources;
+    }
+
+    @Override
+    public ImmutableList<ResourcePattern> getArtifactResources() {
+        return artifactResources;
+    }
+
+    @Override
     protected void addProperties(ImmutableSortedMap.Builder<String, Object> builder) {
         super.addProperties(builder);
         builder.put(Property.ARTIFACT_URLS.name(), artifactUrls);
@@ -71,13 +91,29 @@ public Builder setArtifactUrls(Collection<URI> artifactUrls) {
         }
 
         public MavenRepositoryDescriptor create() {
+            checkNotNull(artifactUrls);
+            checkNotNull(metadataSources);
+
+            ImmutableList<ResourcePattern> metadataResources = ImmutableList.of(new M2ResourcePattern(url, MavenPattern.M2_PATTERN));
+            ImmutableList.Builder<ResourcePattern> artifactResourcesBuilder = ImmutableList.builderWithExpectedSize(1 + artifactUrls.size());
+            artifactResourcesBuilder.add(new M2ResourcePattern(url, MavenPattern.M2_PATTERN));
+            for (URI rootUri : artifactUrls) {
+                artifactResourcesBuilder.add(new M2ResourcePattern(rootUri, MavenPattern.M2_PATTERN));
+            }
+            ImmutableList<ResourcePattern> artifactResources = artifactResourcesBuilder.build();
+
+            String id = calculateId(MavenResolver.class, metadataResources, artifactResources, metadataSources, hasher -> {});
+
             return new MavenRepositoryDescriptor(
+                id,
                 checkNotNull(name),
                 url,
-                checkNotNull(metadataSources),
+                metadataResources,
+                artifactResources,
+                metadataSources,
                 checkNotNull(authenticated),
                 checkNotNull(authenticationSchemes),
-                checkNotNull(artifactUrls)
+                artifactUrls
             );
         }
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/RepositoryDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/RepositoryDescriptor.java
index ca261e9..09b3112 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/RepositoryDescriptor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/RepositoryDescriptor.java
@@ -35,13 +35,23 @@ public enum Type {
         FLAT_DIR
     }
 
-    public final String name;
+    private final String id;
+    private final String name;
     private Map<String, ?> properties;
 
-    RepositoryDescriptor(String name) {
+    RepositoryDescriptor(String id, String name) {
+        this.id = id;
         this.name = name;
     }
 
+    public String getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
     public abstract Type getType();
 
     public final Map<String, ?> getProperties() {
@@ -56,5 +66,4 @@ public enum Type {
 
     protected abstract void addProperties(ImmutableSortedMap.Builder<String, Object> builder);
 
-
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/UrlRepositoryDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/UrlRepositoryDescriptor.java
index 3ca18c2..4a665d6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/UrlRepositoryDescriptor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/descriptor/UrlRepositoryDescriptor.java
@@ -18,12 +18,18 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
+import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver;
+import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
+import org.gradle.api.internal.cache.StringInterner;
+import org.gradle.internal.hash.Hasher;
+import org.gradle.internal.hash.Hashing;
 import org.gradle.internal.scan.UsedByScanPlugin;
 
 import java.net.URI;
 import java.util.List;
+import java.util.function.Consumer;
 
-abstract class UrlRepositoryDescriptor extends RepositoryDescriptor {
+public abstract class UrlRepositoryDescriptor extends RepositoryDescriptor {
 
     @UsedByScanPlugin("doesn't link against this type, but expects these values - See ResolveConfigurationDependenciesBuildOperationType")
     public enum Property {
@@ -39,19 +45,24 @@ public enum Property {
     public final ImmutableList<String> authenticationSchemes;
 
     protected UrlRepositoryDescriptor(
+        String id,
         String name,
         URI url,
         ImmutableList<String> metadataSources,
         boolean authenticated,
         ImmutableList<String> authenticationSchemes
     ) {
-        super(name);
+        super(id, name);
         this.url = url;
         this.metadataSources = metadataSources;
         this.authenticated = authenticated;
         this.authenticationSchemes = authenticationSchemes;
     }
 
+    public abstract ImmutableList<ResourcePattern> getMetadataResources();
+
+    public abstract ImmutableList<ResourcePattern> getArtifactResources();
+
     @Override
     protected void addProperties(ImmutableSortedMap.Builder<String, Object> builder) {
         if (url != null) {
@@ -63,6 +74,7 @@ protected void addProperties(ImmutableSortedMap.Builder<String, Object> builder)
     }
 
     static abstract class Builder<T extends Builder<T>> {
+        private static final StringInterner REPOSITORY_ID_INTERNER = new StringInterner();
 
         final String name;
         final URI url;
@@ -96,5 +108,29 @@ public T setAuthenticationSchemes(List<String> authenticationSchemes) {
             return self();
         }
 
+        protected String calculateId(
+            Class<? extends ExternalResourceResolver<?>> implementation,
+            List<ResourcePattern> metadataResources,
+            List<ResourcePattern> artifactResources,
+            List<String> metadataSources,
+            Consumer<Hasher> additionalInputs
+        ) {
+            Hasher cacheHasher = Hashing.newHasher();
+            cacheHasher.putString(implementation.getName());
+            cacheHasher.putInt(metadataResources.size());
+            for (ResourcePattern ivyPattern : metadataResources) {
+                cacheHasher.putString(ivyPattern.getPattern());
+            }
+            cacheHasher.putInt(artifactResources.size());
+            for (ResourcePattern artifactPattern : artifactResources) {
+                cacheHasher.putString(artifactPattern.getPattern());
+            }
+            cacheHasher.putInt(metadataResources.size());
+            for (String source : metadataSources) {
+                cacheHasher.putString(source);
+            }
+            additionalInputs.accept(cacheHasher);
+            return REPOSITORY_ID_INTERNER.intern(cacheHasher.hash().toString());
+        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/AbstractRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/AbstractRepositoryLayout.java
index 1ecd986..bd9b3f5 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/AbstractRepositoryLayout.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/AbstractRepositoryLayout.java
@@ -16,8 +16,9 @@
 package org.gradle.api.internal.artifacts.repositories.layout;
 
 import org.gradle.api.artifacts.repositories.RepositoryLayout;
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
 
+import javax.annotation.Nullable;
 import java.net.URI;
 import java.util.Set;
 
@@ -29,9 +30,9 @@ public abstract class AbstractRepositoryLayout implements RepositoryLayout {
      * Given the base URI, apply the patterns and other configuration for this layout to the supplied resolver.
      *
      * @param baseUri The base URI for the repository.
-     * @param resolver The ivy resolver that will be used to resolve this layout.
+     * @param builder The configuration builder to apply the changes to.
      */
-    public abstract void apply(URI baseUri, PatternBasedResolver resolver);
+    public abstract void apply(@Nullable URI baseUri, IvyRepositoryDescriptor.Builder builder);
 
     /**
      * Add any schemes registered as patterns in this layout, given the supplied base URI.
@@ -40,13 +41,9 @@ public abstract class AbstractRepositoryLayout implements RepositoryLayout {
      * @param baseUri The baseUri of the repository.
      * @param schemes The set of schemes to add to.
      */
-    public void addSchemes(URI baseUri, Set<String> schemes) {
+    public void addSchemes(@Nullable URI baseUri, Set<String> schemes) {
         if (baseUri != null) {
             schemes.add(baseUri.getScheme());
         }
     }
-
-    public abstract Set<String> getIvyPatterns();
-
-    public abstract Set<String> getArtifactPatterns();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/DefaultIvyPatternRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/DefaultIvyPatternRepositoryLayout.java
index dc8245d..0ce3be1 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/DefaultIvyPatternRepositoryLayout.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/DefaultIvyPatternRepositoryLayout.java
@@ -15,10 +15,10 @@
  */
 package org.gradle.api.internal.artifacts.repositories.layout;
 
-import com.google.common.collect.ImmutableSet;
 import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout;
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
 
+import javax.annotation.Nullable;
 import java.net.URI;
 import java.util.LinkedHashSet;
 import java.util.Set;
@@ -66,30 +66,22 @@ public void setM2compatible(boolean m2compatible) {
     }
 
     @Override
-    public void apply(URI baseUri, PatternBasedResolver resolver) {
-        if (baseUri == null) {
-            return;
+    public void apply(@Nullable URI baseUri, IvyRepositoryDescriptor.Builder builder) {
+        builder.setLayoutType("Pattern");
+        builder.setM2Compatible(m2compatible);
+
+        for (String pattern : artifactPatterns) {
+            builder.addArtifactPattern(pattern);
+            builder.addArtifactResource(baseUri, pattern);
         }
 
-        resolver.setM2compatible(m2compatible);
-
-        for (String artifactPattern : artifactPatterns) {
-            resolver.addArtifactLocation(baseUri, artifactPattern);
+        for (String pattern : ivyPatterns) {
+            builder.addIvyPattern(pattern);
         }
 
-        Set<String> usedIvyPatterns = ivyPatterns.isEmpty() ? artifactPatterns : ivyPatterns;
-        for (String ivyPattern : usedIvyPatterns) {
-            resolver.addDescriptorLocation(baseUri, ivyPattern);
+        Set<String> effectivePatterns = ivyPatterns.isEmpty() ? artifactPatterns : ivyPatterns;
+        for (String pattern : effectivePatterns) {
+            builder.addIvyResource(baseUri, pattern);
         }
     }
-
-    @Override
-    public Set<String> getIvyPatterns() {
-        return ImmutableSet.copyOf(ivyPatterns);
-    }
-
-    @Override
-    public Set<String> getArtifactPatterns() {
-        return ImmutableSet.copyOf(artifactPatterns);
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.java
index a5e4b65..cd89a77 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/GradleRepositoryLayout.java
@@ -15,12 +15,11 @@
  */
 package org.gradle.api.internal.artifacts.repositories.layout;
 
-import com.google.common.collect.ImmutableSet;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
 
+import javax.annotation.Nullable;
 import java.net.URI;
-import java.util.Set;
 
 /**
  * A Repository Layout that applies the following patterns:
@@ -32,24 +31,13 @@
  * Note the pattern is the same for both artifacts and ivy files.
  */
 public class GradleRepositoryLayout extends AbstractRepositoryLayout {
-
     @Override
-    public void apply(URI baseUri, PatternBasedResolver resolver) {
-        if (baseUri == null) {
-            return;
-        }
-
-        resolver.addArtifactLocation(baseUri, IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN);
-        resolver.addDescriptorLocation(baseUri, IvyArtifactRepository.GRADLE_IVY_PATTERN);
-    }
-
-    @Override
-    public Set<String> getIvyPatterns() {
-        return ImmutableSet.of(IvyArtifactRepository.GRADLE_IVY_PATTERN);
-    }
-
-    @Override
-    public Set<String> getArtifactPatterns() {
-        return ImmutableSet.of(IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN);
+    public void apply(@Nullable URI baseUri, IvyRepositoryDescriptor.Builder builder) {
+        builder.setLayoutType("Gradle");
+        builder.setM2Compatible(false);
+        builder.addIvyPattern(IvyArtifactRepository.GRADLE_IVY_PATTERN);
+        builder.addIvyResource(baseUri, IvyArtifactRepository.GRADLE_IVY_PATTERN);
+        builder.addArtifactPattern(IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN);
+        builder.addArtifactResource(baseUri, IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN);
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/IvyRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/IvyRepositoryLayout.java
index 05edf34..bbcaee9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/IvyRepositoryLayout.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/IvyRepositoryLayout.java
@@ -15,12 +15,11 @@
  */
 package org.gradle.api.internal.artifacts.repositories.layout;
 
-import com.google.common.collect.ImmutableSet;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
 
+import javax.annotation.Nullable;
 import java.net.URI;
-import java.util.Set;
 
 /**
  * A Repository Layout that applies the following patterns:
@@ -30,24 +29,13 @@
  * </ul>
  */
 public class IvyRepositoryLayout extends AbstractRepositoryLayout {
-
     @Override
-    public void apply(URI baseUri, PatternBasedResolver resolver) {
-        if (baseUri == null) {
-            return;
-        }
-
-        resolver.addArtifactLocation(baseUri, IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
-        resolver.addDescriptorLocation(baseUri, IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
-    }
-
-    @Override
-    public Set<String> getIvyPatterns() {
-        return ImmutableSet.of(IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
-    }
-
-    @Override
-    public Set<String> getArtifactPatterns() {
-        return ImmutableSet.of(IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
+    public void apply(@Nullable URI baseUri, IvyRepositoryDescriptor.Builder builder) {
+        builder.setLayoutType("Ivy");
+        builder.setM2Compatible(false);
+        builder.addIvyPattern(IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
+        builder.addIvyResource(baseUri, IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
+        builder.addArtifactPattern(IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
+        builder.addArtifactResource(baseUri, IvyArtifactRepository.IVY_ARTIFACT_PATTERN);
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java
index c77003b..1a2d9c3 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/layout/MavenRepositoryLayout.java
@@ -15,12 +15,11 @@
  */
 package org.gradle.api.internal.artifacts.repositories.layout;
 
-import com.google.common.collect.ImmutableSet;
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
-import org.gradle.api.internal.artifacts.repositories.resolver.PatternBasedResolver;
+import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
 
+import javax.annotation.Nullable;
 import java.net.URI;
-import java.util.Set;
 
 /**
  * A Repository Layout that applies the following patterns:
@@ -33,26 +32,13 @@
  * Note that the resolver will follow the layout only, but will <em>not</em> use .pom files for meta data. Ivy metadata files are required/published.
  */
 public class MavenRepositoryLayout extends AbstractRepositoryLayout {
-
     @Override
-    public void apply(URI baseUri, PatternBasedResolver resolver) {
-        if (baseUri == null) {
-            return;
-        }
-
-        resolver.setM2compatible(true); // Replace '.' with '/' in organisation
-
-        resolver.addArtifactLocation(baseUri, IvyArtifactRepository.MAVEN_ARTIFACT_PATTERN);
-        resolver.addDescriptorLocation(baseUri, IvyArtifactRepository.MAVEN_IVY_PATTERN);
-    }
-
-    @Override
-    public Set<String> getIvyPatterns() {
-        return ImmutableSet.of(IvyArtifactRepository.MAVEN_IVY_PATTERN);
-    }
-
-    @Override
-    public Set<String> getArtifactPatterns() {
-        return ImmutableSet.of(IvyArtifactRepository.MAVEN_ARTIFACT_PATTERN);
+    public void apply(@Nullable URI baseUri, IvyRepositoryDescriptor.Builder builder) {
+        builder.setLayoutType("Maven");
+        builder.setM2Compatible(true); // Replace '.' with '/' in organisation
+        builder.addIvyPattern(IvyArtifactRepository.MAVEN_IVY_PATTERN);
+        builder.addIvyResource(baseUri, IvyArtifactRepository.MAVEN_IVY_PATTERN);
+        builder.addArtifactPattern(IvyArtifactRepository.MAVEN_ARTIFACT_PATTERN);
+        builder.addArtifactResource(baseUri, IvyArtifactRepository.MAVEN_ARTIFACT_PATTERN);
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/AbstractMetadataSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/AbstractMetadataSource.java
deleted file mode 100644
index 8ffc720..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/AbstractMetadataSource.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2017 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.api.internal.artifacts.repositories.metadata;
-
-import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
-import org.gradle.internal.hash.Hasher;
-
-abstract class AbstractMetadataSource<S extends MutableModuleComponentResolveMetadata> implements MetadataSource<S> {
-    @Override
-    public void appendId(Hasher hasher) {
-        hasher.putString(this.getClass().getName());
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/AbstractRepositoryMetadataSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/AbstractRepositoryMetadataSource.java
index 667f01b..47cc796 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/AbstractRepositoryMetadataSource.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/AbstractRepositoryMetadataSource.java
@@ -27,6 +27,7 @@
 import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolverDescriptorParseContext;
 import org.gradle.internal.SystemProperties;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
 import org.gradle.internal.component.model.DefaultModuleDescriptorArtifactMetadata;
@@ -48,7 +49,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-abstract class AbstractRepositoryMetadataSource<S extends MutableModuleComponentResolveMetadata> extends AbstractMetadataSource<S> {
+abstract class AbstractRepositoryMetadataSource<S extends MutableModuleComponentResolveMetadata> implements MetadataSource<S> {
     private static final Logger LOGGER = LoggerFactory.getLogger(ExternalResourceResolver.class);
 
     final MetadataArtifactProvider metadataArtifactProvider;
@@ -64,7 +65,7 @@ protected AbstractRepositoryMetadataSource(MetadataArtifactProvider metadataArti
     }
 
     @Override
-    public S create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleVersionIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult result) {
+    public S create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleVersionIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
         S parsedMetadataFromRepository = parseMetaDataFromArtifact(repositoryName, componentResolvers, moduleVersionIdentifier, artifactResolver, result);
         if (parsedMetadataFromRepository != null) {
             LOGGER.debug("Metadata file found for module '{}' in repository '{}'.", moduleVersionIdentifier, repositoryName);
@@ -82,7 +83,7 @@ private S parseMetaDataFromArtifact(String repositoryName, ComponentResolvers co
             if (parseResult != null) {
                 if (parseResult.hasGradleMetadataRedirectionMarker()) {
                     if (result instanceof BuildableModuleComponentMetaDataResolveResult) {
-                        ((BuildableModuleComponentMetaDataResolveResult) result).redirectToGradleMetadata();
+                        ((BuildableModuleComponentMetaDataResolveResult<?>) result).redirectToGradleMetadata();
                     } else {
                         throw new IllegalStateException("Unexpected Gradle metadata redirection answer");
                     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultArtifactMetadataSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultArtifactMetadataSource.java
index 216661f..dbf9dac 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultArtifactMetadataSource.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultArtifactMetadataSource.java
@@ -24,6 +24,7 @@
 import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
 import org.gradle.api.internal.artifacts.repositories.resolver.VersionLister;
 import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
@@ -42,7 +43,7 @@
  * A metadata source which simply verifies the existence of a given artifact and does not
  * attempt to fetch any further metadata from other external sources.
  */
-public class DefaultArtifactMetadataSource extends AbstractMetadataSource<MutableModuleComponentResolveMetadata> {
+public class DefaultArtifactMetadataSource implements MetadataSource<MutableModuleComponentResolveMetadata> {
     private static final Logger LOGGER = LoggerFactory.getLogger(ExternalResourceResolver.class);
     private final MutableModuleMetadataFactory<? extends MutableModuleComponentResolveMetadata> mutableModuleMetadataFactory;
 
@@ -51,7 +52,7 @@ public DefaultArtifactMetadataSource(MutableModuleMetadataFactory<? extends Muta
     }
 
     @Override
-    public MutableModuleComponentResolveMetadata create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult result) {
+    public MutableModuleComponentResolveMetadata create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
         IvyArtifactName artifact = getArtifactName(moduleComponentIdentifier, prescribedMetaData);
         if (!artifactResolver.artifactExists(new DefaultModuleComponentArtifactMetadata(moduleComponentIdentifier, artifact), result)) {
             return null;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultGradleModuleMetadataSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultGradleModuleMetadataSource.java
index f38dee8..4ed25a8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultGradleModuleMetadataSource.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultGradleModuleMetadataSource.java
@@ -25,6 +25,7 @@
 import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
 import org.gradle.api.internal.artifacts.repositories.resolver.VersionLister;
 import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.external.model.MutableComponentVariant;
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
@@ -47,7 +48,7 @@
  * TODO: This class sources Gradle metadata files, but there's no corresponding ModuleComponentResolveMetadata for this metadata yet.
  * Because of this, we will generate an empty instance (either a Ivy or Maven) based on the repository type.
  */
-public class DefaultGradleModuleMetadataSource extends AbstractMetadataSource<MutableModuleComponentResolveMetadata> {
+public class DefaultGradleModuleMetadataSource implements MetadataSource<MutableModuleComponentResolveMetadata> {
     private final GradleModuleMetadataParser metadataParser;
     private final GradleModuleMetadataCompatibilityConverter metadataCompatibilityConverter;
     private final MutableModuleMetadataFactory<? extends MutableModuleComponentResolveMetadata> mutableModuleMetadataFactory;
@@ -64,7 +65,7 @@ public DefaultGradleModuleMetadataSource(GradleModuleMetadataParser metadataPars
     }
 
     @Override
-    public MutableModuleComponentResolveMetadata create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult result) {
+    public MutableModuleComponentResolveMetadata create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
         DefaultIvyArtifactName moduleMetadataArtifact = new DefaultIvyArtifactName(moduleComponentIdentifier.getModule(), "module", "module");
         DefaultModuleComponentArtifactMetadata artifactId = new DefaultModuleComponentArtifactMetadata(moduleComponentIdentifier, moduleMetadataArtifact);
         LocallyAvailableExternalResource gradleMetadataArtifact = artifactResolver.resolveArtifact(artifactId, result);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultImmutableMetadataSources.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultImmutableMetadataSources.java
index de563be..e719801 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultImmutableMetadataSources.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/DefaultImmutableMetadataSources.java
@@ -16,7 +16,6 @@
 package org.gradle.api.internal.artifacts.repositories.metadata;
 
 import com.google.common.collect.ImmutableList;
-import org.gradle.internal.hash.Hasher;
 
 public class DefaultImmutableMetadataSources implements ImmutableMetadataSources {
     private final ImmutableList<MetadataSource<?>> sources;
@@ -30,11 +29,4 @@ public ImmutableList<MetadataSource<?>> sources() {
         return sources;
     }
 
-    @Override
-    public void appendId(Hasher hasher) {
-        hasher.putInt(sources.size());
-        for (MetadataSource<?> source : sources) {
-            source.appendId(hasher);
-        }
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/ImmutableMetadataSources.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/ImmutableMetadataSources.java
index aed440f..3a5e689 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/ImmutableMetadataSources.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/ImmutableMetadataSources.java
@@ -16,13 +16,10 @@
 package org.gradle.api.internal.artifacts.repositories.metadata;
 
 import com.google.common.collect.ImmutableList;
-import org.gradle.internal.hash.Hasher;
 
 /**
  * An immutable, usable representation of metadata sources.
  */
 public interface ImmutableMetadataSources {
     ImmutableList<MetadataSource<?>> sources();
-
-    void appendId(Hasher hasher);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/MetadataSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/MetadataSource.java
index 12fbec6..cb18b69 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/MetadataSource.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/MetadataSource.java
@@ -21,10 +21,10 @@
 import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceArtifactResolver;
 import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
 import org.gradle.api.internal.artifacts.repositories.resolver.VersionLister;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
-import org.gradle.internal.hash.Hasher;
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult;
 import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult;
 
@@ -43,7 +43,7 @@ S create(String repositoryName,
              ModuleComponentIdentifier moduleComponentIdentifier,
              ComponentOverrideMetadata prescribedMetaData,
              ExternalResourceArtifactResolver artifactResolver, // Required for MavenLocal to verify the presence of the artifact
-             BuildableModuleComponentMetaDataResolveResult result);
+             BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result);
 
     /**
      * Use the supplied patterns and version lister to list available versions for the supplied dependency/module.
@@ -55,5 +55,4 @@ S create(String repositoryName,
      */
     void listModuleVersions(ModuleDependencyMetadata dependency, ModuleIdentifier module, List<ResourcePattern> ivyPatterns, List<ResourcePattern> artifactPatterns, VersionLister versionLister, BuildableModuleVersionListingResolveResult result);
 
-    void appendId(Hasher hasher);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/RedirectingGradleMetadataModuleMetadataSource.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/RedirectingGradleMetadataModuleMetadataSource.java
index 3909b5d..053bc32 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/RedirectingGradleMetadataModuleMetadataSource.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/metadata/RedirectingGradleMetadataModuleMetadataSource.java
@@ -21,6 +21,7 @@
 import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceArtifactResolver;
 import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern;
 import org.gradle.api.internal.artifacts.repositories.resolver.VersionLister;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata;
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
@@ -37,7 +38,7 @@
  * It also means that we're going to pay a small price if Gradle metadata is present: we would fetch
  * a POM file and parse it, then fetch Gradle metadata and parse it (doing twice the work).
  */
-public class RedirectingGradleMetadataModuleMetadataSource extends AbstractMetadataSource<MutableModuleComponentResolveMetadata> {
+public class RedirectingGradleMetadataModuleMetadataSource implements MetadataSource<MutableModuleComponentResolveMetadata> {
     private final MetadataSource<?> delegate;
     private final MetadataSource<MutableModuleComponentResolveMetadata> gradleModuleMetadataSource;
 
@@ -47,7 +48,7 @@ public RedirectingGradleMetadataModuleMetadataSource(MetadataSource<?> delegate,
     }
 
     @Override
-    public MutableModuleComponentResolveMetadata create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult result) {
+    public MutableModuleComponentResolveMetadata create(String repositoryName, ComponentResolvers componentResolvers, ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, ExternalResourceArtifactResolver artifactResolver, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
         MutableModuleComponentResolveMetadata metadata = delegate.create(repositoryName, componentResolvers, moduleComponentIdentifier, prescribedMetaData, artifactResolver, result);
         if (result.shouldUseGradleMetatada()) {
             MutableModuleComponentResolveMetadata resolveMetadata = gradleModuleMetadataSource.create(repositoryName, componentResolvers, moduleComponentIdentifier, prescribedMetaData, artifactResolver, result);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
index caec088..577e1e0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolver.java
@@ -17,6 +17,7 @@
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import org.gradle.api.artifacts.ComponentMetadataListerDetails;
 import org.gradle.api.artifacts.ComponentMetadataSupplierDetails;
@@ -27,10 +28,10 @@
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
+import org.gradle.api.internal.artifacts.repositories.descriptor.UrlRepositoryDescriptor;
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources;
 import org.gradle.api.internal.artifacts.repositories.metadata.MetadataArtifactProvider;
 import org.gradle.api.internal.artifacts.repositories.metadata.MetadataSource;
-import org.gradle.api.internal.cache.StringInterner;
 import org.gradle.api.internal.component.ArtifactType;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.action.InstantiatingAction;
@@ -48,8 +49,6 @@
 import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.hash.ChecksumService;
 import org.gradle.internal.hash.HashCode;
-import org.gradle.internal.hash.Hasher;
-import org.gradle.internal.hash.Hashing;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.resolve.ArtifactResolveException;
 import org.gradle.internal.resolve.result.BuildableArtifactFileResolveResult;
@@ -74,7 +73,6 @@
 import javax.annotation.Nullable;
 import java.io.File;
 import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -82,11 +80,10 @@
 
 public abstract class ExternalResourceResolver<T extends ModuleComponentResolveMetadata> implements ConfiguredModuleComponentRepository {
     private static final Logger LOGGER = LoggerFactory.getLogger(ExternalResourceResolver.class);
-    private static final StringInterner REPOSITORY_ID_INTERNER = new StringInterner();
 
     private final String name;
-    private final List<ResourcePattern> ivyPatterns = new ArrayList<>();
-    private final List<ResourcePattern> artifactPatterns = new ArrayList<>();
+    private final ImmutableList<ResourcePattern> ivyPatterns;
+    private final ImmutableList<ResourcePattern> artifactPatterns;
     private ComponentResolvers componentResolvers;
 
     private final ExternalResourceRepository repository;
@@ -103,22 +100,27 @@ public abstract class ExternalResourceResolver<T extends ModuleComponentResolveM
     private final Instantiator injector;
     private final ChecksumService checksumService;
 
-    private String id;
+    private final String id;
     private ExternalResourceArtifactResolver cachedArtifactResolver;
 
-    protected ExternalResourceResolver(String name,
-                                       boolean local,
-                                       ExternalResourceRepository repository,
-                                       CacheAwareExternalResourceAccessor cachingResourceAccessor,
-                                       LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder,
-                                       FileStore<ModuleComponentArtifactIdentifier> artifactFileStore,
-                                       ImmutableMetadataSources metadataSources,
-                                       MetadataArtifactProvider metadataArtifactProvider,
-                                       @Nullable InstantiatingAction<ComponentMetadataSupplierDetails> componentMetadataSupplierFactory,
-                                       @Nullable InstantiatingAction<ComponentMetadataListerDetails> providedVersionLister,
-                                       Instantiator injector,
-                                       ChecksumService checksumService) {
-        this.name = name;
+    protected ExternalResourceResolver(
+        UrlRepositoryDescriptor descriptor,
+        boolean local,
+        ExternalResourceRepository repository,
+        CacheAwareExternalResourceAccessor cachingResourceAccessor,
+        LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder,
+        FileStore<ModuleComponentArtifactIdentifier> artifactFileStore,
+        ImmutableMetadataSources metadataSources,
+        MetadataArtifactProvider metadataArtifactProvider,
+        @Nullable InstantiatingAction<ComponentMetadataSupplierDetails> componentMetadataSupplierFactory,
+        @Nullable InstantiatingAction<ComponentMetadataListerDetails> providedVersionLister,
+        Instantiator injector,
+        ChecksumService checksumService
+    ) {
+        this.id = descriptor.getId();
+        this.name = descriptor.getName();
+        this.ivyPatterns = descriptor.getMetadataResources();
+        this.artifactPatterns = descriptor.getArtifactResources();
         this.local = local;
         this.cachingResourceAccessor = cachingResourceAccessor;
         this.repository = repository;
@@ -134,17 +136,9 @@ protected ExternalResourceResolver(String name,
 
     @Override
     public String getId() {
-        if (id != null) {
-            return id;
-        }
-        id = generateId(this);
         return id;
     }
 
-    public ImmutableMetadataSources getMetadataSources() {
-        return metadataSources;
-    }
-
     @Override
     public String getName() {
         return name;
@@ -229,11 +223,11 @@ private List<ResourcePattern> filterComplete(List<ResourcePattern> ivyPatterns,
         return CollectionUtils.filter(ivyPatterns, element -> element.isComplete(module));
     }
 
-    protected void doResolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+    protected void doResolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
         resolveStaticDependency(moduleComponentIdentifier, prescribedMetaData, result, createArtifactResolver());
     }
 
-    protected final void resolveStaticDependency(ModuleComponentIdentifier moduleVersionIdentifier, ComponentOverrideMetadata prescribedMetaData, BuildableModuleComponentMetaDataResolveResult result, ExternalResourceArtifactResolver artifactResolver) {
+    protected final void resolveStaticDependency(ModuleComponentIdentifier moduleVersionIdentifier, ComponentOverrideMetadata prescribedMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result, ExternalResourceArtifactResolver artifactResolver) {
         for (MetadataSource<?> source : metadataSources.sources()) {
             MutableModuleComponentResolveMetadata value = source.create(name, componentResolvers, moduleVersionIdentifier, prescribedMetaData, artifactResolver, result);
             if (value != null) {
@@ -344,21 +338,6 @@ private void publishChecksum(ExternalResourceName destination, File content, Str
         }
     }
 
-    protected void addIvyPattern(ResourcePattern pattern) {
-        invalidateCaches();
-        ivyPatterns.add(pattern);
-    }
-
-    private void invalidateCaches() {
-        id = null;
-        cachedArtifactResolver = null;
-    }
-
-    protected void addArtifactPattern(ResourcePattern pattern) {
-        invalidateCaches();
-        artifactPatterns.add(pattern);
-    }
-
     public List<String> getIvyPatterns() {
         return CollectionUtils.collect(ivyPatterns, ResourcePattern::getPattern);
     }
@@ -367,19 +346,7 @@ public List<String> getArtifactPatterns() {
         return CollectionUtils.collect(artifactPatterns, ResourcePattern::getPattern);
     }
 
-    protected void setIvyPatterns(Iterable<? extends ResourcePattern> patterns) {
-        invalidateCaches();
-        ivyPatterns.clear();
-        CollectionUtils.addAll(ivyPatterns, patterns);
-    }
-
-    protected void setArtifactPatterns(List<ResourcePattern> patterns) {
-        invalidateCaches();
-        artifactPatterns.clear();
-        CollectionUtils.addAll(artifactPatterns, patterns);
-    }
-
-    protected abstract class AbstractRepositoryAccess implements ModuleComponentRepositoryAccess {
+    protected abstract class AbstractRepositoryAccess implements ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> {
         @Override
         public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
             T moduleMetaData = getSupportedMetadataType().cast(component);
@@ -402,7 +369,7 @@ public void resolveArtifactsWithType(ComponentResolveMetadata component, Artifac
     protected abstract class LocalRepositoryAccess extends AbstractRepositoryAccess {
         @Override
         public String toString() {
-            return "local > " + ExternalResourceResolver.this.toString();
+            return "local > " + ExternalResourceResolver.this;
         }
 
         @Override
@@ -410,7 +377,7 @@ public final void listModuleVersions(ModuleDependencyMetadata dependency, Builda
         }
 
         @Override
-        public final void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public final void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
         }
 
         @Override
@@ -442,7 +409,7 @@ public final void listModuleVersions(ModuleDependencyMetadata dependency, Builda
         }
 
         @Override
-        public final void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+        public final void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
             doResolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result);
         }
 
@@ -518,25 +485,6 @@ public MetadataFetchingCost estimateMetadataFetchingCost(ModuleComponentIdentifi
         }
     }
 
-    private String generateId(ExternalResourceResolver<?> resolver) {
-        Hasher cacheHasher = Hashing.newHasher();
-        cacheHasher.putString(getClass().getName());
-        cacheHasher.putInt(resolver.ivyPatterns.size());
-        for (ResourcePattern ivyPattern : ivyPatterns) {
-            cacheHasher.putString(ivyPattern.getPattern());
-        }
-        cacheHasher.putInt(artifactPatterns.size());
-        for (ResourcePattern artifactPattern : artifactPatterns) {
-            cacheHasher.putString(artifactPattern.getPattern());
-        }
-        appendId(cacheHasher);
-        return REPOSITORY_ID_INTERNER.intern(cacheHasher.hash().toString());
-    }
-
-    protected void appendId(Hasher hasher) {
-        getMetadataSources().appendId(hasher);
-    }
-
     private static class NoOpResourceAwareResolveResult implements ResourceAwareResolveResult {
 
         private static final NoOpResourceAwareResolveResult INSTANCE = new NoOpResourceAwareResolveResult();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java
index 3739c7b..8b9e7c0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverDescriptorParseContext.java
@@ -84,7 +84,7 @@ private LocallyAvailableExternalResource resolveMetaDataArtifactFile(ModuleCompo
 
         BuildableArtifactResolveResult artifactResolveResult = new DefaultBuildableArtifactResolveResult();
         ComponentArtifactMetadata artifactMetaData = moduleArtifactsResolveResult.getResult().iterator().next();
-        artifactResolver.resolveArtifact(moduleVersionResolveResult.getModuleVersionId(), artifactMetaData, moduleVersionResolveResult.getState().getSources(), artifactResolveResult);
+        artifactResolver.resolveArtifact(artifactResolveState.getResolveMetadata(), artifactMetaData, artifactResolveResult);
         File file = artifactResolveResult.getResult().getFile();
         LocallyAvailableExternalResource resource = fileResourceRepository.resource(file);
         ComponentArtifactIdentifier id = artifactMetaData.getId();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
index 8be93b6..f1e0599 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolver.java
@@ -18,6 +18,7 @@
 import org.gradle.api.artifacts.ComponentMetadataListerDetails;
 import org.gradle.api.artifacts.ComponentMetadataSupplierDetails;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess;
+import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources;
 import org.gradle.api.internal.artifacts.repositories.metadata.MetadataArtifactProvider;
 import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransport;
@@ -25,51 +26,51 @@
 import org.gradle.internal.action.InstantiatingAction;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata;
 import org.gradle.internal.component.model.ConfigurationMetadata;
 import org.gradle.internal.hash.ChecksumService;
-import org.gradle.internal.hash.Hasher;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
 import org.gradle.internal.resource.local.FileStore;
 import org.gradle.internal.resource.local.LocallyAvailableResourceFinder;
 
 import javax.annotation.Nullable;
-import java.net.URI;
 
-public class IvyResolver extends ExternalResourceResolver<IvyModuleResolveMetadata> implements PatternBasedResolver {
+public class IvyResolver extends ExternalResourceResolver<IvyModuleResolveMetadata> {
 
     private final boolean dynamicResolve;
-    private boolean m2Compatible;
+    private final boolean m2Compatible;
     private final IvyLocalRepositoryAccess localRepositoryAccess;
     private final IvyRemoteRepositoryAccess remoteRepositoryAccess;
 
     public IvyResolver(
-        String name,
+        IvyRepositoryDescriptor descriptor,
         RepositoryTransport transport,
         LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder,
         boolean dynamicResolve,
         FileStore<ModuleComponentArtifactIdentifier> artifactFileStore,
         @Nullable InstantiatingAction<ComponentMetadataSupplierDetails> componentMetadataSupplierFactory,
         @Nullable InstantiatingAction<ComponentMetadataListerDetails> componentMetadataVersionListerFactory,
-        ImmutableMetadataSources repositoryContentFilter,
+        ImmutableMetadataSources metadataSources,
         MetadataArtifactProvider metadataArtifactProvider,
         Instantiator injector, ChecksumService checksumService
     ) {
         super(
-            name,
+            descriptor,
             transport.isLocal(),
             transport.getRepository(),
             transport.getResourceAccessor(),
             locallyAvailableResourceFinder,
             artifactFileStore,
-            repositoryContentFilter,
+            metadataSources,
             metadataArtifactProvider,
             componentMetadataSupplierFactory,
             componentMetadataVersionListerFactory,
             injector,
             checksumService);
         this.dynamicResolve = dynamicResolve;
+        this.m2Compatible = descriptor.isM2Compatible();
         this.localRepositoryAccess = new IvyLocalRepositoryAccess();
         this.remoteRepositoryAccess = new IvyRemoteRepositoryAccess();
     }
@@ -80,12 +81,6 @@ public String toString() {
     }
 
     @Override
-    protected void appendId(Hasher hasher) {
-        super.appendId(hasher);
-        hasher.putBoolean(isM2compatible());
-    }
-
-    @Override
     protected Class<IvyModuleResolveMetadata> getSupportedMetadataType() {
         return IvyModuleResolveMetadata.class;
     }
@@ -105,31 +100,12 @@ public boolean isM2compatible() {
     }
 
     @Override
-    public void setM2compatible(boolean m2compatible) {
-        this.m2Compatible = m2compatible;
-    }
-
-    @Override
-    public void addArtifactLocation(URI baseUri, String pattern) {
-        addArtifactPattern(toResourcePattern(baseUri, pattern));
-    }
-
-    @Override
-    public void addDescriptorLocation(URI baseUri, String pattern) {
-        addIvyPattern(toResourcePattern(baseUri, pattern));
-    }
-
-    private ResourcePattern toResourcePattern(URI baseUri, String pattern) {
-        return isM2compatible() ? new M2ResourcePattern(baseUri, pattern) : new IvyResourcePattern(baseUri, pattern);
-    }
-
-    @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> getLocalAccess() {
         return localRepositoryAccess;
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> getRemoteAccess() {
         return remoteRepositoryAccess;
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
index 7117261..4fe333a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolver.java
@@ -19,6 +19,7 @@
 import org.gradle.api.artifacts.ComponentMetadataSupplierDetails;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess;
+import org.gradle.api.internal.artifacts.repositories.descriptor.MavenRepositoryDescriptor;
 import org.gradle.api.internal.artifacts.repositories.maven.MavenMetadata;
 import org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader;
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources;
@@ -29,6 +30,7 @@
 import org.gradle.internal.action.InstantiatingAction;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.maven.MavenModuleResolveMetadata;
 import org.gradle.internal.component.external.model.maven.MutableMavenModuleResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
@@ -45,34 +47,31 @@
 
 import javax.annotation.Nullable;
 import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class MavenResolver extends ExternalResourceResolver<MavenModuleResolveMetadata> {
     private final URI root;
-    private final List<URI> artifactRoots = new ArrayList<>();
     private final MavenMetadataLoader mavenMetaDataLoader;
 
     private static final Pattern UNIQUE_SNAPSHOT = Pattern.compile("(?:.+)-(\\d{8}\\.\\d{6}-\\d+)");
     private final MavenLocalRepositoryAccess localAccess = new MavenLocalRepositoryAccess();
     private final MavenRemoteRepositoryAccess remoteAccess = new MavenRemoteRepositoryAccess();
 
-    public MavenResolver(String name,
-                         URI rootUri,
-                         RepositoryTransport transport,
-                         LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder,
-                         FileStore<ModuleComponentArtifactIdentifier> artifactFileStore,
-                         ImmutableMetadataSources metadataSources,
-                         MetadataArtifactProvider metadataArtifactProvider,
-                         MavenMetadataLoader mavenMetadataLoader,
-                         @Nullable InstantiatingAction<ComponentMetadataSupplierDetails> componentMetadataSupplierFactory,
-                         @Nullable InstantiatingAction<ComponentMetadataListerDetails> versionListerFactory,
-                         Instantiator injector,
-                         ChecksumService checksumService) {
-        super(name, transport.isLocal(),
+    public MavenResolver(
+        MavenRepositoryDescriptor descriptor,
+        URI rootUri,
+        RepositoryTransport transport,
+        LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder,
+        FileStore<ModuleComponentArtifactIdentifier> artifactFileStore,
+        ImmutableMetadataSources metadataSources,
+        MetadataArtifactProvider metadataArtifactProvider,
+        MavenMetadataLoader mavenMetadataLoader,
+        @Nullable InstantiatingAction<ComponentMetadataSupplierDetails> componentMetadataSupplierFactory,
+        @Nullable InstantiatingAction<ComponentMetadataListerDetails> versionListerFactory,
+        Instantiator injector,
+        ChecksumService checksumService) {
+        super(descriptor, transport.isLocal(),
             transport.getRepository(),
             transport.getResourceAccessor(),
             locallyAvailableResourceFinder,
@@ -85,7 +84,6 @@ public MavenResolver(String name,
             checksumService);
         this.mavenMetaDataLoader = mavenMetadataLoader;
         this.root = rootUri;
-        updatePatterns();
     }
 
     @Override
@@ -103,7 +101,7 @@ public URI getRoot() {
     }
 
     @Override
-    protected void doResolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, BuildableModuleComponentMetaDataResolveResult result) {
+    protected void doResolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata prescribedMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result) {
         MavenUniqueSnapshotModuleSource uniqueSnapshotVersion = isNonUniqueSnapshot(moduleComponentIdentifier)
             ? findUniqueSnapshotVersion(moduleComponentIdentifier, result)
             : composeUniqueSnapshotVersion(moduleComponentIdentifier);
@@ -121,7 +119,7 @@ protected boolean isMetaDataArtifact(ArtifactType artifactType) {
         return artifactType == ArtifactType.MAVEN_POM;
     }
 
-    private void resolveUniqueSnapshotDependency(MavenUniqueSnapshotComponentIdentifier module, ComponentOverrideMetadata prescribedMetaData, BuildableModuleComponentMetaDataResolveResult result, MavenUniqueSnapshotModuleSource snapshotSource) {
+    private void resolveUniqueSnapshotDependency(MavenUniqueSnapshotComponentIdentifier module, ComponentOverrideMetadata prescribedMetaData, BuildableModuleComponentMetaDataResolveResult<ModuleComponentResolveMetadata> result, MavenUniqueSnapshotModuleSource snapshotSource) {
         resolveStaticDependency(module, prescribedMetaData, result, createArtifactResolver(MutableModuleSources.of(snapshotSource)));
     }
 
@@ -136,26 +134,10 @@ protected ExternalResourceArtifactResolver createArtifactResolver(final ModuleSo
         });
     }
 
-    public void addArtifactLocation(URI baseUri) {
-        artifactRoots.add(baseUri);
-        updatePatterns();
-    }
-
     private M2ResourcePattern getWholePattern() {
         return new M2ResourcePattern(root, MavenPattern.M2_PATTERN);
     }
 
-    private void updatePatterns() {
-        setIvyPatterns(Collections.singletonList(getWholePattern()));
-
-        List<ResourcePattern> artifactPatterns = new ArrayList<>();
-        artifactPatterns.add(getWholePattern());
-        for (URI artifactRoot : artifactRoots) {
-            artifactPatterns.add(new M2ResourcePattern(artifactRoot, MavenPattern.M2_PATTERN));
-        }
-        setArtifactPatterns(artifactPatterns);
-    }
-
     @Nullable
     private MavenUniqueSnapshotModuleSource findUniqueSnapshotVersion(ModuleComponentIdentifier module, ResourceAwareResolveResult result) {
         M2ResourcePattern wholePattern = getWholePattern();
@@ -193,12 +175,12 @@ private MavenMetadata parseMavenMetadata(ExternalResourceName metadataLocation)
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> getLocalAccess() {
         return localAccess;
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> getRemoteAccess() {
         return remoteAccess;
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java
deleted file mode 100644
index f952dcc..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/repositories/resolver/PatternBasedResolver.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2011 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.api.internal.artifacts.repositories.resolver;
-
-import java.net.URI;
-
-public interface PatternBasedResolver {
-    /**
-     * Must be called before any patterns are added.
-     */
-    void setM2compatible(boolean b);
-
-    void addArtifactLocation(URI baseUri, String pattern);
-
-    void addDescriptorLocation(URI baseUri, String pattern);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
index 8e5576c..7fd71f4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolutionResult.java
@@ -19,10 +19,10 @@
 import groovy.lang.Closure;
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.result.DependencyResult;
-import org.gradle.api.artifacts.result.ResolutionResult;
 import org.gradle.api.artifacts.result.ResolvedComponentResult;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.provider.DefaultProvider;
+import org.gradle.api.internal.provider.Providers;
 import org.gradle.api.provider.Provider;
 import org.gradle.internal.Actions;
 import org.gradle.internal.Factory;
@@ -35,7 +35,7 @@
 import static org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult.eachElement;
 
 @SuppressWarnings("rawtypes")
-public class DefaultResolutionResult implements ResolutionResult {
+public class DefaultResolutionResult implements ResolutionResultInternal {
 
     private final Factory<ResolvedComponentResult> rootSource;
     private final AttributeContainer requestedAttributes;
@@ -57,6 +57,11 @@ public Provider<ResolvedComponentResult> getRootComponent() {
     }
 
     @Override
+    public Provider<Throwable> getNonFatalFailure() {
+        return Providers.notDefined();
+    }
+
+    @Override
     public Set<? extends DependencyResult> getAllDependencies() {
         final Set<DependencyResult> out = new LinkedHashSet<>();
         allDependencies(out::add);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResult.java
index 925e1ba..7c4ad7a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResult.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/DefaultResolvedComponentResult.java
@@ -33,6 +33,7 @@
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 
@@ -43,20 +44,22 @@ public class DefaultResolvedComponentResult implements ResolvedComponentResultIn
     private final ComponentSelectionReason selectionReason;
     private final ComponentIdentifier componentId;
     private final List<ResolvedVariantResult> selectedVariants;
+    private final Map<Long, ResolvedVariantResult> selectedVariantsById;
     private final List<ResolvedVariantResult> allVariants;
-    private final String repositoryName;
+    private final String repositoryId;
     private final Multimap<ResolvedVariantResult, DependencyResult> variantDependencies = ArrayListMultimap.create();
 
     public DefaultResolvedComponentResult(
         ModuleVersionIdentifier moduleVersion, ComponentSelectionReason selectionReason, ComponentIdentifier componentId,
-        List<ResolvedVariantResult> selectedVariants, List<ResolvedVariantResult> allVariants, String repositoryName
+        Map<Long, ResolvedVariantResult> selectedVariants, List<ResolvedVariantResult> allVariants, @Nullable String repositoryId
     ) {
         this.moduleVersion = moduleVersion;
         this.selectionReason = selectionReason;
         this.componentId = componentId;
-        this.selectedVariants = selectedVariants;
-        this.allVariants = allVariants;
-        this.repositoryName = repositoryName;
+        this.selectedVariantsById = selectedVariants;
+        this.selectedVariants = ImmutableList.copyOf(selectedVariants.values());
+        this.allVariants = allVariants.isEmpty() ? this.selectedVariants : allVariants;
+        this.repositoryId = repositoryId;
     }
 
     @Override
@@ -64,10 +67,16 @@ public ComponentIdentifier getId() {
         return componentId;
     }
 
+    @Override
+    @Deprecated
+    public String getRepositoryName() {
+        return repositoryId;
+    }
+
     @Nullable
     @Override
-    public String getRepositoryName() {
-        return repositoryName;
+    public String getRepositoryId() {
+        return repositoryId;
     }
 
     @Override
@@ -112,7 +121,7 @@ public List<ResolvedVariantResult> getVariants() {
     }
 
     @Override
-    public List<ResolvedVariantResult> getAllVariants() {
+    public List<ResolvedVariantResult> getAvailableVariants() {
         return allVariants;
     }
 
@@ -134,6 +143,11 @@ private void reportInvalidVariant(ResolvedVariantResult variant) {
         throw new InvalidUserCodeException("Variant '" + variant.getDisplayName() + "' doesn't belong to resolved component '" + this + "'. " + moreInfo + " Most likely you are using a variant from another component to get the dependencies of this component.");
     }
 
+    @Nullable
+    public ResolvedVariantResult getVariant(Long id) {
+        return selectedVariantsById.get(id);
+    }
+
     public void associateDependencyToVariant(DependencyResult dependencyResult, ResolvedVariantResult fromVariant) {
         variantDependencies.put(fromVariant, dependencyResult);
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/ResolutionResultInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/ResolutionResultInternal.java
new file mode 100644
index 0000000..b3f58c3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/ResolutionResultInternal.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.result;
+
+import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.provider.Provider;
+
+/**
+ * Internal counterpart to {@link ResolutionResult}.
+ */
+public interface ResolutionResultInternal extends ResolutionResult {
+
+    /**
+     * An optional non-fatal failure which may be attached to a resolution result.
+     */
+    Provider<Throwable> getNonFatalFailure();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/ResolvedComponentResultInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/ResolvedComponentResultInternal.java
index e0ab720..190d674 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/ResolvedComponentResultInternal.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/result/ResolvedComponentResultInternal.java
@@ -23,25 +23,30 @@
 import java.util.List;
 
 public interface ResolvedComponentResultInternal extends ResolvedComponentResult {
-
     /**
-     * <p>Returns the name of the repository used to source this component, or {@code null} if this component was not resolved from a repository.
+     * Used by the Android plugin. Do not use this method.
      */
-    @Nullable
+    @Deprecated
     String getRepositoryName();
 
     /**
-     * Returns all the variants for this component, even ones that weren't selected.
+     * <p>Returns the id of the repository used to source this component, or {@code null} if this component was not resolved from a repository.
+     */
+    @Nullable
+    String getRepositoryId();
+
+    /**
+     * Returns all the variants of this component available for selection. Does not include variants that cannot be consumed, which means this
+     * may not include all the variants returned by {@link #getVariants()}.
      *
      * <p>
      * Note: for performance reasons,
-     * {@link org.gradle.api.internal.artifacts.configurations.ConfigurationInternal#setReturnAllVariants(boolean)}
+     * {@link org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal#setReturnAllVariants(boolean)}
      * must be set to {@code true} for this to actually return all variants in all cases.
      * </p>
      *
      * @return all variants for this component
      * @since 7.5
      */
-    List<ResolvedVariantResult> getAllVariants();
-
+    List<ResolvedVariantResult> getAvailableVariants();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AbstractTransformedArtifactSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AbstractTransformedArtifactSet.java
index 07c995e..c09afc2 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AbstractTransformedArtifactSet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AbstractTransformedArtifactSet.java
@@ -36,7 +36,7 @@
 import java.util.List;
 
 /**
- * Transformed artifact set that performs the transformation itself when visited.
+ * Transformed artifact set that performs the transform itself when visited.
  */
 public abstract class AbstractTransformedArtifactSet implements TransformedArtifactSet, FileCollectionInternal.Source {
     private final CalculatedValueContainer<ImmutableList<ResolvedArtifactSet.Artifacts>, CalculateArtifacts> result;
@@ -46,14 +46,14 @@ public AbstractTransformedArtifactSet(
         ResolvedArtifactSet delegate,
         ImmutableAttributes targetVariantAttributes,
         List<? extends Capability> capabilities,
-        Transformation transformation,
-        ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory,
+        TransformChain transformChain,
+        TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory,
         CalculatedValueContainerFactory calculatedValueContainerFactory
     ) {
-        TransformUpstreamDependenciesResolver dependenciesResolver = dependenciesResolverFactory.create(componentIdentifier, transformation);
-        ImmutableList.Builder<BoundTransformationStep> builder = ImmutableList.builder();
-        transformation.visitTransformationSteps(transformationStep -> builder.add(new BoundTransformationStep(transformationStep, dependenciesResolver.dependenciesFor(transformationStep))));
-        ImmutableList<BoundTransformationStep> steps = builder.build();
+        TransformUpstreamDependenciesResolver dependenciesResolver = dependenciesResolverFactory.create(componentIdentifier, transformChain);
+        ImmutableList.Builder<BoundTransformStep> builder = ImmutableList.builder();
+        transformChain.visitTransformSteps(step -> builder.add(new BoundTransformStep(step, dependenciesResolver.dependenciesFor(step))));
+        ImmutableList<BoundTransformStep> steps = builder.build();
         this.result = calculatedValueContainerFactory.create(Describables.of(componentIdentifier), new CalculateArtifacts(componentIdentifier, delegate, targetVariantAttributes, capabilities, steps));
     }
 
@@ -103,11 +103,11 @@ public void visitExternalArtifacts(Action<ResolvableArtifact> visitor) {
     public static class CalculateArtifacts implements ValueCalculator<ImmutableList<Artifacts>> {
         private final ComponentIdentifier ownerId;
         private final ResolvedArtifactSet delegate;
-        private final ImmutableList<BoundTransformationStep> steps;
+        private final ImmutableList<BoundTransformStep> steps;
         private final ImmutableAttributes targetVariantAttributes;
         private final List<? extends Capability> capabilities;
 
-        public CalculateArtifacts(ComponentIdentifier ownerId, ResolvedArtifactSet delegate, ImmutableAttributes targetVariantAttributes, List<? extends Capability> capabilities, ImmutableList<BoundTransformationStep> steps) {
+        public CalculateArtifacts(ComponentIdentifier ownerId, ResolvedArtifactSet delegate, ImmutableAttributes targetVariantAttributes, List<? extends Capability> capabilities, ImmutableList<BoundTransformStep> steps) {
             this.ownerId = ownerId;
             this.delegate = delegate;
             this.steps = steps;
@@ -123,7 +123,7 @@ public ResolvedArtifactSet getDelegate() {
             return delegate;
         }
 
-        public ImmutableList<BoundTransformationStep> getSteps() {
+        public ImmutableList<BoundTransformStep> getSteps() {
             return steps;
         }
 
@@ -137,16 +137,16 @@ public List<? extends Capability> getCapabilities() {
 
         @Override
         public void visitDependencies(TaskDependencyResolveContext context) {
-            for (BoundTransformationStep step : steps) {
+            for (BoundTransformStep step : steps) {
                 context.add(step.getUpstreamDependencies());
             }
         }
 
         @Override
         public ImmutableList<Artifacts> calculateValue(NodeExecutionContext context) {
-            // Isolate the transformation parameters, if not already done
-            for (BoundTransformationStep step : steps) {
-                step.getTransformation().isolateParametersIfNotAlready();
+            // Isolate the transform parameters, if not already done
+            for (BoundTransformStep step : steps) {
+                step.getTransformStep().isolateParametersIfNotAlready();
                 step.getUpstreamDependencies().finalizeIfNotAlready();
             }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AmbiguousTransformException.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AmbiguousTransformException.java
index f83917e..3361910 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AmbiguousTransformException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AmbiguousTransformException.java
@@ -42,7 +42,7 @@ private static String format(String producerDisplayName, AttributeContainerInter
         formatter.node("Found the following transforms");
 
         Comparator<TransformedVariant> variantComparator =
-            Comparator.<TransformedVariant, String>comparing(x -> x.getTransformation().getDisplayName())
+            Comparator.<TransformedVariant, String>comparing(x -> x.getTransformChain().getDisplayName())
                 .thenComparing(x -> x.getAttributes().toString());
         Map<ResolvedVariant, List<TransformedVariant>> variantToTransforms = candidates.stream().collect(Collectors.groupingBy(
             TransformedVariant::getRoot,
@@ -58,7 +58,7 @@ private static String format(String producerDisplayName, AttributeContainerInter
             formatter.node("Candidate transform(s)");
             formatter.startChildren();
             for (TransformedVariant variant : entry.getValue()) {
-                formatter.node("Transform '" + variant.getTransformation().getDisplayName() + "' producing attributes:");
+                formatter.node("Transform '" + variant.getTransformChain().getDisplayName() + "' producing attributes:");
                 formatAttributes(formatter, variant.getAttributes());
             }
             formatter.endChildren();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformActionScheme.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformActionScheme.java
deleted file mode 100644
index 02768a1..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformActionScheme.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2019 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.api.internal.artifacts.transform;
-
-import org.gradle.api.artifacts.transform.TransformAction;
-import org.gradle.api.internal.tasks.properties.InspectionScheme;
-import org.gradle.api.internal.tasks.properties.TypeScheme;
-import org.gradle.internal.instantiation.InstantiationScheme;
-import org.gradle.internal.properties.annotations.TypeMetadataStore;
-
-public class ArtifactTransformActionScheme implements TypeScheme {
-    private final InstantiationScheme instantiationScheme;
-    private final InspectionScheme inspectionScheme;
-
-    public ArtifactTransformActionScheme(InstantiationScheme instantiationScheme, InspectionScheme inspectionScheme) {
-        this.instantiationScheme = instantiationScheme;
-        this.inspectionScheme = inspectionScheme;
-    }
-
-    @Override
-    public TypeMetadataStore getMetadataStore() {
-        return inspectionScheme.getMetadataStore();
-    }
-
-    @Override
-    public boolean appliesTo(Class<?> type) {
-        return TransformAction.class.isAssignableFrom(type);
-    }
-
-    public InspectionScheme getInspectionScheme() {
-        return inspectionScheme;
-    }
-
-    public InstantiationScheme getInstantiationScheme() {
-        return instantiationScheme;
-    }
-}
-
-
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformDependencies.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformDependencies.java
deleted file mode 100644
index a5a2598..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformDependencies.java
+++ /dev/null
@@ -1,29 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.file.FileCollection;
-
-import java.util.Optional;
-
-public interface ArtifactTransformDependencies {
-    /**
-     * Returns the dependency artifacts of the artifact being transformed.
-     * The order of the files match that of the dependencies in the source artifact view.
-     */
-    Optional<FileCollection> getFiles();
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformParameterScheme.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformParameterScheme.java
deleted file mode 100644
index feb1bdf..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransformParameterScheme.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019 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.api.internal.artifacts.transform;
-
-import org.gradle.api.artifacts.transform.TransformParameters;
-import org.gradle.api.internal.tasks.properties.InspectionScheme;
-import org.gradle.api.internal.tasks.properties.TypeScheme;
-import org.gradle.internal.instantiation.InstantiationScheme;
-import org.gradle.internal.properties.annotations.TypeMetadataStore;
-
-public class ArtifactTransformParameterScheme implements TypeScheme {
-    private final InstantiationScheme instantiationScheme;
-    private final InspectionScheme inspectionScheme;
-
-    public ArtifactTransformParameterScheme(InstantiationScheme instantiationScheme, InspectionScheme inspectionScheme) {
-        this.instantiationScheme = instantiationScheme;
-        this.inspectionScheme = inspectionScheme;
-    }
-
-    @Override
-    public TypeMetadataStore getMetadataStore() {
-        return inspectionScheme.getMetadataStore();
-    }
-
-    @Override
-    public boolean appliesTo(Class<?> type) {
-        return TransformParameters.class.isAssignableFrom(type);
-    }
-
-    public InstantiationScheme getInstantiationScheme() {
-        return instantiationScheme;
-    }
-
-    public InspectionScheme getInspectionScheme() {
-        return inspectionScheme;
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransforms.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransforms.java
deleted file mode 100644
index 28f71f3..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ArtifactTransforms.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2016 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.api.internal.artifacts.transform;
-
-import org.gradle.api.internal.attributes.AttributeContainerInternal;
-
-public interface ArtifactTransforms {
-    /**
-     * Returns a selector that selects the variant matching the supplied attributes, or which can be transformed to match.
-     */
-    VariantSelector variantSelector(AttributeContainerInternal consumerAttributes, boolean allowNoMatchingVariants, boolean selectFromAllVariants, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolver);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelector.java
index 7e703a3..e1902a4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelector.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelector.java
@@ -45,7 +45,7 @@
 /**
  * A {@link VariantSelector} which uses attribute matching to select a matching variant. If no producer variant
  * is compatible with the requested attributes, this selector will attempt to construct a chain of artifact
- * transformations that can produce a variant compatible with the requested attributes.
+ * transforms that can produce a variant compatible with the requested attributes.
  */
 class AttributeMatchingVariantSelector implements VariantSelector {
     private final ConsumerProvidedVariantFinder consumerProvidedVariantFinder;
@@ -55,7 +55,7 @@ class AttributeMatchingVariantSelector implements VariantSelector {
     private final ImmutableAttributes requested;
     private final boolean ignoreWhenNoMatches;
     private final boolean selectFromAllVariants;
-    private final ExtraExecutionGraphDependenciesResolverFactory dependenciesResolver;
+    private final TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory;
 
     AttributeMatchingVariantSelector(
         ConsumerProvidedVariantFinder consumerProvidedVariantFinder,
@@ -65,7 +65,7 @@ class AttributeMatchingVariantSelector implements VariantSelector {
         ImmutableAttributes requested,
         boolean ignoreWhenNoMatches,
         boolean selectFromAllVariants,
-        ExtraExecutionGraphDependenciesResolverFactory dependenciesResolver
+        TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory
     ) {
         this.consumerProvidedVariantFinder = consumerProvidedVariantFinder;
         this.schema = schema;
@@ -74,7 +74,7 @@ class AttributeMatchingVariantSelector implements VariantSelector {
         this.requested = requested;
         this.ignoreWhenNoMatches = ignoreWhenNoMatches;
         this.selectFromAllVariants = selectFromAllVariants;
-        this.dependenciesResolver = dependenciesResolver;
+        this.dependenciesResolverFactory = dependenciesResolverFactory;
     }
 
     @Override
@@ -89,8 +89,17 @@ public ImmutableAttributes getRequestedAttributes() {
 
     @Override
     public ResolvedArtifactSet select(ResolvedVariantSet producer, Factory factory) {
+        return selectAndWrapFailures(producer, ignoreWhenNoMatches, factory);
+    }
+
+    @Override
+    public ResolvedArtifactSet maybeSelect(ResolvedVariantSet candidates, Factory factory) {
+        return selectAndWrapFailures(candidates, true, factory);
+    }
+
+    private ResolvedArtifactSet selectAndWrapFailures(ResolvedVariantSet producer, boolean ignoreWhenNoMatches, Factory factory) {
         try {
-            return doSelect(producer, factory, AttributeMatchingExplanationBuilder.logging());
+            return doSelect(producer, ignoreWhenNoMatches, factory, AttributeMatchingExplanationBuilder.logging());
         } catch (VariantSelectionException t) {
             return new BrokenResolvedArtifactSet(t);
         } catch (Exception t) {
@@ -98,7 +107,7 @@ public ResolvedArtifactSet select(ResolvedVariantSet producer, Factory factory)
         }
     }
 
-    private ResolvedArtifactSet doSelect(ResolvedVariantSet producer, Factory factory, AttributeMatchingExplanationBuilder explanationBuilder) {
+    private ResolvedArtifactSet doSelect(ResolvedVariantSet producer, boolean ignoreWhenNoMatches, Factory factory, AttributeMatchingExplanationBuilder explanationBuilder) {
         AttributeMatcher matcher = schema.withProducer(producer.getSchema());
         ImmutableAttributes componentRequested = attributesFactory.concat(requested, producer.getOverriddenAttributes());
         final List<ResolvedVariant> variants;
@@ -131,7 +140,7 @@ private ResolvedArtifactSet doSelect(ResolvedVariantSet producer, Factory factor
 
         if (transformedVariants.size() == 1) {
             TransformedVariant result = transformedVariants.get(0);
-            return factory.asTransformed(result.getRoot(), result.getVariantChain(), dependenciesResolver, transformedVariantFactory);
+            return factory.asTransformed(result.getRoot(), result.getTransformedVariantDefinition(), dependenciesResolverFactory, transformedVariantFactory);
         }
 
         if (!transformedVariants.isEmpty()) {
@@ -146,7 +155,7 @@ private ResolvedArtifactSet doSelect(ResolvedVariantSet producer, Factory factor
     }
 
     /**
-     * Attempt to disambiguate between multiple potential transformation candidates. This first performs attribute matching on the {@code candidates}.
+     * Attempt to disambiguate between multiple potential transform candidates. This first performs attribute matching on the {@code candidates}.
      * If that does not produce a single result, then a subset of the results of attribute matching is returned, where the candidates which have
      * incompatible attributes values with the <strong>last</strong> candidate are included.
      */
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/BoundTransformStep.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/BoundTransformStep.java
new file mode 100644
index 0000000..fa8ad77
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/BoundTransformStep.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 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.api.internal.artifacts.transform;
+
+/**
+ * A transform step with all of its parameters bound to their providers.
+ * <p>
+ * A transform step may take parameters from a parameters object specified when the transform is registered
+ * and may also take the upstream dependencies of the source artifact as a parameter.
+ */
+public class BoundTransformStep {
+    private final TransformStep transformStep;
+    private final TransformUpstreamDependencies upstreamDependencies;
+
+    public BoundTransformStep(TransformStep transformStep, TransformUpstreamDependencies upstreamDependencies) {
+        this.transformStep = transformStep;
+        this.upstreamDependencies = upstreamDependencies;
+    }
+
+    public TransformStep getTransformStep() {
+        return transformStep;
+    }
+
+    public TransformUpstreamDependencies getUpstreamDependencies() {
+        return upstreamDependencies;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/BoundTransformationStep.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/BoundTransformationStep.java
deleted file mode 100644
index 9b1883a..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/BoundTransformationStep.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2020 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.api.internal.artifacts.transform;
-
-/**
- * A transformation with all of its parameters bound to their providers. A transformer may take parameters from a parameters
- * object specified when the transformation is registered and may also take the upstream dependencies of the source artifact
- * as a parameter.
- */
-public class BoundTransformationStep {
-    private final TransformationStep transformation;
-    private final TransformUpstreamDependencies upstreamDependencies;
-
-    public BoundTransformationStep(TransformationStep transformation, TransformUpstreamDependencies upstreamDependencies) {
-        this.transformation = transformation;
-        this.upstreamDependencies = upstreamDependencies;
-    }
-
-    public TransformationStep getTransformation() {
-        return transformation;
-    }
-
-    public TransformUpstreamDependencies getUpstreamDependencies() {
-        return upstreamDependencies;
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinder.java
index 13bc60d..2389dbd 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinder.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.artifacts.transform;
 
-import org.gradle.api.internal.artifacts.ArtifactTransformRegistration;
+import org.gradle.api.internal.artifacts.TransformRegistration;
 import org.gradle.api.internal.artifacts.VariantTransformRegistry;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
@@ -34,7 +34,7 @@
 
 /**
  * Finds all the variants that can be created from a given set of producer variants using
- * the consumer's variant transformations. Transformations can be chained. If multiple
+ * the consumer's variant transforms. Transforms can be chained. If multiple
  * chains can lead to the same outcome, the shortest paths are selected.
  *
  * Caches the results, as often the same request is made for many components in a
@@ -44,7 +44,7 @@ public class ConsumerProvidedVariantFinder {
     private final VariantTransformRegistry variantTransforms;
     private final ImmutableAttributesFactory attributesFactory;
     private final CachingAttributeMatcher matcher;
-    private final TransformationCache transformationCache;
+    private final TransformCache transformCache;
 
     public ConsumerProvidedVariantFinder(
         VariantTransformRegistry variantTransforms,
@@ -54,12 +54,12 @@ public ConsumerProvidedVariantFinder(
         this.variantTransforms = variantTransforms;
         this.attributesFactory = attributesFactory;
         this.matcher = new CachingAttributeMatcher(schema.matcher());
-        this.transformationCache = new TransformationCache(this::doFindTransformedVariants);
+        this.transformCache = new TransformCache(this::doFindTransformedVariants);
     }
 
     /**
-     * Executes the transformation chain detection algorithm given a set of producer variants and the requested
-     * attributes. Only the transform chains of the shortest transformation depth are returned, and all results are
+     * Executes the transform chain detection algorithm given a set of producer variants and the requested
+     * attributes. Only the transform chains of the shortest depth are returned, and all results are
      * guaranteed to have the same depth.
      *
      * @param sources The set of producer variants.
@@ -69,37 +69,37 @@ public ConsumerProvidedVariantFinder(
      *      variant compatible with the requested attributes.
      */
     public List<TransformedVariant> findTransformedVariants(List<ResolvedVariant> sources, ImmutableAttributes requested) {
-        return transformationCache.query(sources, requested);
+        return transformCache.query(sources, requested);
     }
 
     /**
-     * A node in a chain of artifact transformations.
+     * A node in a chain of artifact transforms.
      */
     private static class ChainNode {
         final ChainNode next;
-        final ArtifactTransformRegistration transform;
-        public ChainNode(@Nullable ChainNode next, ArtifactTransformRegistration transform) {
+        final TransformRegistration transform;
+        public ChainNode(@Nullable ChainNode next, TransformRegistration transform) {
             this.next = next;
             this.transform = transform;
         }
     }
 
     /**
-     * Represents the intermediate state of a potential transformation solution. Many instances of this state may simultaneously exist
+     * Represents the intermediate state of a potential transform solution. Many instances of this state may simultaneously exist
      * for different potential solutions.
      */
     private static class ChainState {
         final ChainNode chain;
         final ImmutableAttributes requested;
-        final ImmutableFilteredList<ArtifactTransformRegistration> transforms;
+        final ImmutableFilteredList<TransformRegistration> transforms;
 
         /**
-         * @param chain The candidate transformation chain.
+         * @param chain The candidate transform chain.
          * @param requested The attribute set which must be produced by any previous variant in order to achieve the
          *      original user-requested attribute set after {@code chain} is applied to that previous variant.
          * @param transforms The remaining transforms which may be prepended to {@code chain} to produce a solution.
          */
-        public ChainState(@Nullable ChainNode chain, ImmutableAttributes requested, ImmutableFilteredList<ArtifactTransformRegistration> transforms) {
+        public ChainState(@Nullable ChainNode chain, ImmutableAttributes requested, ImmutableFilteredList<TransformRegistration> transforms) {
             this.chain = chain;
             this.requested = requested;
             this.transforms = transforms;
@@ -107,7 +107,7 @@ public ChainState(@Nullable ChainNode chain, ImmutableAttributes requested, Immu
     }
 
     /**
-     * A cached result of the transformation chain detection algorithm. References an index within the source variant
+     * A cached result of the transform chain detection algorithm. References an index within the source variant
      * list instead of an actual variant itself, so that this result can be cached and used for distinct variant sets
      * that otherwise share the same attributes.
      */
@@ -121,32 +121,32 @@ public CachedVariant(int sourceIndex, VariantDefinition chain) {
     }
 
     /**
-     * The algorithm itself. Performs a breadth-first search on the set of potential transformation solutions in order to find
-     * all solutions at a given transformation depth. The search begins at the final node of the chain. At each depth, a candidate
-     * transformation is applied to the beginning of the chain. Then, if a source variant can be used as a root of that chain,
+     * The algorithm itself. Performs a breadth-first search on the set of potential transform solutions in order to find
+     * all solutions at a given transform chain depth. The search begins at the final node of the chain. At each depth, a candidate
+     * transform is applied to the beginning of the chain. Then, if a source variant can be used as a root of that chain,
      * we have found a solution. Otherwise, if no solutions are found at this depth, we run the search at the next depth, with all
      * candidate transforms linked to the previous level's chains.
      */
     private List<CachedVariant> doFindTransformedVariants(List<ImmutableAttributes> sources, ImmutableAttributes requested) {
         List<ChainState> toProcess = new ArrayList<>();
         List<ChainState> nextDepth = new ArrayList<>();
-        toProcess.add(new ChainState(null, requested, ImmutableFilteredList.allOf(variantTransforms.getTransforms())));
+        toProcess.add(new ChainState(null, requested, ImmutableFilteredList.allOf(variantTransforms.getRegistrations())));
 
         List<CachedVariant> results = new ArrayList<>(1);
         while (results.isEmpty() && !toProcess.isEmpty()) {
             for (ChainState state : toProcess) {
                 // The set of transforms which could potentially produce a variant compatible with `requested`.
-                ImmutableFilteredList<ArtifactTransformRegistration> candidates =
+                ImmutableFilteredList<TransformRegistration> candidates =
                     state.transforms.matching(transform -> matcher.isMatching(transform.getTo(), state.requested));
 
-                // For each candidate, attempt to find a source variant that the transformation can use as its root.
-                for (ArtifactTransformRegistration candidate : candidates) {
+                // For each candidate, attempt to find a source variant that the transform can use as its root.
+                for (TransformRegistration candidate : candidates) {
                     for (int i = 0; i < sources.size(); i++) {
                         ImmutableAttributes sourceAttrs = sources.get(i);
                         if (matcher.isMatching(sourceAttrs, candidate.getFrom())) {
                             ImmutableAttributes rootAttrs = attributesFactory.concat(sourceAttrs, candidate.getTo());
                             if (matcher.isMatching(rootAttrs, state.requested)) {
-                                DefaultVariantDefinition rootTransformedVariant = new DefaultVariantDefinition(null, rootAttrs, candidate.getTransformationStep());
+                                DefaultVariantDefinition rootTransformedVariant = new DefaultVariantDefinition(null, rootAttrs, candidate.getTransformStep());
                                 VariantDefinition variantChain = createVariantChain(state.chain, rootTransformedVariant);
                                 results.add(new CachedVariant(i, variantChain));
                             }
@@ -161,7 +161,7 @@ private List<CachedVariant> doFindTransformedVariants(List<ImmutableAttributes>
 
                 // Construct new states for processing at the next depth in case we can't find any solutions at this depth.
                 for (int i = 0; i < candidates.size(); i++) {
-                    ArtifactTransformRegistration candidate = candidates.get(i);
+                    TransformRegistration candidate = candidates.get(i);
                     nextDepth.add(new ChainState(
                         new ChainNode(state.chain, candidate),
                         attributesFactory.concat(state.requested, candidate.getFrom()),
@@ -183,7 +183,7 @@ private List<CachedVariant> doFindTransformedVariants(List<ImmutableAttributes>
      * Constructs a complete cacheable variant chain given a root transformed variant and the chain of variants
      * to apply to that root variant.
      *
-     * @param stateChain The transformation chain from the search state to apply to the root transformed variant.
+     * @param stateChain The transform chain from the search state to apply to the root transformed variant.
      * @param root The root variant to apply the chain to.
      *
      * @return A variant chain representing the final transformed variant.
@@ -195,7 +195,7 @@ private VariantDefinition createVariantChain(final ChainNode stateChain, Default
             last = new DefaultVariantDefinition(
                 last,
                 attributesFactory.concat(last.getTargetAttributes(), node.transform.getTo()),
-                node.transform.getTransformationStep()
+                node.transform.getTransformStep()
             );
             node = node.next;
         }
@@ -203,16 +203,16 @@ private VariantDefinition createVariantChain(final ChainNode stateChain, Default
     }
 
     /**
-     * Caches calls to the transformation chain selection algorithm. The cached results are stored in
+     * Caches calls to the transform chain selection algorithm. The cached results are stored in
      * a variant-independent manner, such that only the attributes of the input variants are cached.
      * This way, if multiple calls are made with different variants but those variants have the same
      * attributes, the cached results may be used.
      */
-    private static class TransformationCache {
+    private static class TransformCache {
         private final ConcurrentHashMap<CacheKey, List<CachedVariant>> cache = new ConcurrentHashMap<>();
         private final BiFunction<List<ImmutableAttributes>, ImmutableAttributes, List<CachedVariant>> action;
 
-        public TransformationCache(BiFunction<List<ImmutableAttributes>, ImmutableAttributes, List<CachedVariant>> action) {
+        public TransformCache(BiFunction<List<ImmutableAttributes>, ImmutableAttributes, List<CachedVariant>> action) {
             this.action = action;
         }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformDependencies.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformDependencies.java
deleted file mode 100644
index bdbb179..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformDependencies.java
+++ /dev/null
@@ -1,34 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.file.FileCollection;
-
-import java.util.Optional;
-
-public class DefaultArtifactTransformDependencies implements ArtifactTransformDependencies {
-    private final FileCollection files;
-
-    public DefaultArtifactTransformDependencies(FileCollection files) {
-        this.files = files;
-    }
-
-    @Override
-    public Optional<FileCollection> getFiles() {
-        return Optional.of(files);
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransforms.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransforms.java
deleted file mode 100644
index 7457beb..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransforms.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2016 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.api.internal.artifacts.transform;
-
-import org.gradle.api.internal.attributes.AttributeContainerInternal;
-import org.gradle.api.internal.attributes.AttributesSchemaInternal;
-import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
-
-public class DefaultArtifactTransforms implements ArtifactTransforms {
-    private final ConsumerProvidedVariantFinder consumerProvidedVariantFinder;
-    private final AttributesSchemaInternal schema;
-    private final ImmutableAttributesFactory attributesFactory;
-    private final TransformedVariantFactory transformedVariantFactory;
-
-    public DefaultArtifactTransforms(
-        ConsumerProvidedVariantFinder consumerProvidedVariantFinder,
-        AttributesSchemaInternal schema,
-        ImmutableAttributesFactory attributesFactory,
-        TransformedVariantFactory transformedVariantFactory
-    ) {
-        this.consumerProvidedVariantFinder = consumerProvidedVariantFinder;
-        this.schema = schema;
-        this.attributesFactory = attributesFactory;
-        this.transformedVariantFactory = transformedVariantFactory;
-    }
-
-    @Override
-    public VariantSelector variantSelector(AttributeContainerInternal consumerAttributes, boolean allowNoMatchingVariants, boolean selectFromAllVariants, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolver) {
-        return new AttributeMatchingVariantSelector(consumerProvidedVariantFinder, schema, attributesFactory, transformedVariantFactory, consumerAttributes.asImmutable(), allowNoMatchingVariants, selectFromAllVariants, dependenciesResolver);
-    }
-
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultExtraExecutionGraphDependenciesResolverFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultExtraExecutionGraphDependenciesResolverFactory.java
deleted file mode 100644
index 5ad76c2..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultExtraExecutionGraphDependenciesResolverFactory.java
+++ /dev/null
@@ -1,55 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.result.ResolutionResult;
-import org.gradle.api.internal.DomainObjectContext;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationIdentity;
-import org.gradle.api.internal.artifacts.configurations.ResolutionResultProvider;
-import org.gradle.internal.model.CalculatedValueContainerFactory;
-
-public class DefaultExtraExecutionGraphDependenciesResolverFactory implements ExtraExecutionGraphDependenciesResolverFactory {
-    public static final TransformUpstreamDependenciesResolver NO_DEPENDENCIES_RESOLVER = transformationStep -> DefaultTransformUpstreamDependenciesResolver.NO_DEPENDENCIES;
-
-    private final DomainObjectContext owner;
-    private final FilteredResultFactory filteredResultFactory;
-    private final CalculatedValueContainerFactory calculatedValueContainerFactory;
-    private final ConfigurationIdentity configurationIdentity;
-    private final ResolutionResultProvider<ResolutionResult> resolutionResultProvider;
-
-    public DefaultExtraExecutionGraphDependenciesResolverFactory(
-        ConfigurationIdentity configurationIdentity, ResolutionResultProvider<ResolutionResult> resolutionResultProvider,
-        DomainObjectContext owner,
-        CalculatedValueContainerFactory calculatedValueContainerFactory,
-        FilteredResultFactory filteredResultFactory
-    ) {
-        this.configurationIdentity = configurationIdentity;
-        this.resolutionResultProvider = resolutionResultProvider;
-        this.owner = owner;
-        this.filteredResultFactory = filteredResultFactory;
-        this.calculatedValueContainerFactory = calculatedValueContainerFactory;
-    }
-
-    @Override
-    public TransformUpstreamDependenciesResolver create(ComponentIdentifier componentIdentifier, Transformation transformation) {
-        if (!transformation.requiresDependencies()) {
-            return NO_DEPENDENCIES_RESOLVER;
-        }
-        return new DefaultTransformUpstreamDependenciesResolver(componentIdentifier, configurationIdentity, resolutionResultProvider, owner, filteredResultFactory, calculatedValueContainerFactory);
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransform.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransform.java
deleted file mode 100644
index 8e8558f..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransform.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.transform;
-
-import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType;
-import org.gradle.internal.taskgraph.NodeIdentity;
-
-import java.util.List;
-
-/**
- * A {@link CalculateTaskGraphBuildOperationType.PlannedNode} for a {@link TransformationNode}.
- */
-public class DefaultPlannedTransform implements CalculateTaskGraphBuildOperationType.PlannedNode {
-
-    private final TransformationIdentity transformationIdentity;
-    private final List<? extends NodeIdentity> dependencies;
-
-    public DefaultPlannedTransform(
-        TransformationIdentity transformationIdentity,
-        List<? extends NodeIdentity> dependencies
-    ) {
-        this.transformationIdentity = transformationIdentity;
-        this.dependencies = dependencies;
-    }
-
-    @Override
-    public TransformationIdentity getNodeIdentity() {
-        return transformationIdentity;
-    }
-
-    @Override
-    public List<? extends NodeIdentity> getNodeDependencies() {
-        return dependencies;
-    }
-
-    @Override
-    public String toString() {
-        return transformationIdentity.toString();
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransformStep.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransformStep.java
new file mode 100644
index 0000000..79bd787
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransformStep.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.transform;
+
+import org.gradle.execution.plan.PlannedNodeInternal;
+import org.gradle.internal.taskgraph.NodeIdentity;
+import org.gradle.operations.dependencies.transforms.PlannedTransformStepIdentity;
+
+import java.util.List;
+
+/**
+ * A planned node for a {@link TransformStepNode}.
+ */
+public class DefaultPlannedTransformStep implements PlannedNodeInternal {
+
+    private final PlannedTransformStepIdentity identity;
+    private final List<? extends NodeIdentity> dependencies;
+
+    public DefaultPlannedTransformStep(
+        PlannedTransformStepIdentity identity,
+        List<? extends NodeIdentity> dependencies
+    ) {
+        this.identity = identity;
+        this.dependencies = dependencies;
+    }
+
+    @Override
+    public PlannedTransformStepIdentity getNodeIdentity() {
+        return identity;
+    }
+
+    @Override
+    public List<? extends NodeIdentity> getNodeDependencies() {
+        return dependencies;
+    }
+
+    @Override
+    public String toString() {
+        return identity.toString();
+    }
+
+    @Override
+    public DefaultPlannedTransformStep withNodeDependencies(List<? extends NodeIdentity> nodeDependencies) {
+        return new DefaultPlannedTransformStep(identity, nodeDependencies);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransformStepIdentity.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransformStepIdentity.java
new file mode 100644
index 0000000..f230a3b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultPlannedTransformStepIdentity.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.transform;
+
+import org.gradle.operations.dependencies.configurations.ConfigurationIdentity;
+import org.gradle.operations.dependencies.transforms.PlannedTransformStepIdentity;
+import org.gradle.operations.dependencies.variants.Capability;
+import org.gradle.operations.dependencies.variants.ComponentIdentifier;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class DefaultPlannedTransformStepIdentity implements PlannedTransformStepIdentity {
+
+    private final String consumerBuildPath;
+    private final String consumerProjectPath;
+    private final ComponentIdentifier componentId;
+    private final Map<String, String> sourceAttributes;
+    private final Map<String, String> targetAttributes;
+    private final List<Capability> capabilities;
+    private final String artifactName;
+    private final ConfigurationIdentity dependenciesConfigurationIdentity;
+    private final long transformStepNodeId;
+
+    public DefaultPlannedTransformStepIdentity(
+        String consumerBuildPath,
+        String consumerProjectPath,
+        ComponentIdentifier componentId,
+        Map<String, String> sourceAttributes,
+        Map<String, String> targetAttributes,
+        List<Capability> capabilities,
+        String artifactName,
+        @Nullable
+        ConfigurationIdentity dependenciesConfigurationIdentity,
+        long transformStepNodeId
+    ) {
+        this.consumerBuildPath = consumerBuildPath;
+        this.consumerProjectPath = consumerProjectPath;
+        this.componentId = componentId;
+        this.sourceAttributes = sourceAttributes;
+        this.targetAttributes = targetAttributes;
+        this.capabilities = capabilities;
+        this.artifactName = artifactName;
+        this.dependenciesConfigurationIdentity = dependenciesConfigurationIdentity;
+        this.transformStepNodeId = transformStepNodeId;
+    }
+
+    @Override
+    public NodeType getNodeType() {
+        return NodeType.TRANSFORM_STEP;
+    }
+
+    @Override
+    public String getConsumerBuildPath() {
+        return consumerBuildPath;
+    }
+
+    @Override
+    public String getConsumerProjectPath() {
+        return consumerProjectPath;
+    }
+
+    @Override
+    public ComponentIdentifier getComponentId() {
+        return componentId;
+    }
+
+    @Override
+    public Map<String, String> getSourceAttributes() {
+        return sourceAttributes;
+    }
+
+    @Override
+    public Map<String, String> getTargetAttributes() {
+        return targetAttributes;
+    }
+
+    @Override
+    public List<? extends Capability> getCapabilities() {
+        return capabilities;
+    }
+
+    @Override
+    public String getArtifactName() {
+        return artifactName;
+    }
+
+    @Override
+    public ConfigurationIdentity getDependenciesConfigurationIdentity() {
+        return dependenciesConfigurationIdentity;
+    }
+
+    @Override
+    public long getTransformStepNodeId() {
+        return transformStepNodeId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DefaultPlannedTransformStepIdentity)) {
+            return false;
+        }
+        DefaultPlannedTransformStepIdentity that = (DefaultPlannedTransformStepIdentity) o;
+        return transformStepNodeId == that.transformStepNodeId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(transformStepNodeId);
+    }
+
+    @Override
+    public String toString() {
+        return "Transform '" + componentId + "' to " + targetAttributes;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransform.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransform.java
new file mode 100644
index 0000000..119c6ce
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransform.java
@@ -0,0 +1,723 @@
+/*
+ * Copyright 2019 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.api.internal.artifacts.transform;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.reflect.TypeToken;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.artifacts.transform.InputArtifact;
+import org.gradle.api.artifacts.transform.InputArtifactDependencies;
+import org.gradle.api.artifacts.transform.TransformAction;
+import org.gradle.api.artifacts.transform.TransformParameters;
+import org.gradle.api.artifacts.transform.VariantTransformConfigurationException;
+import org.gradle.api.file.FileSystemLocation;
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.api.internal.file.FileCollectionFactory;
+import org.gradle.api.internal.file.FileLookup;
+import org.gradle.api.internal.plugins.DslObject;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.NodeExecutionContext;
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
+import org.gradle.api.internal.tasks.properties.FileParameterUtils;
+import org.gradle.api.internal.tasks.properties.InputParameterUtils;
+import org.gradle.api.provider.Provider;
+import org.gradle.api.reflect.InjectionPointQualifier;
+import org.gradle.internal.Describables;
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
+import org.gradle.internal.execution.InputFingerprinter;
+import org.gradle.internal.execution.UnitOfWork.InputFileValueSupplier;
+import org.gradle.internal.execution.model.InputNormalizer;
+import org.gradle.internal.fingerprint.CurrentFileCollectionFingerprint;
+import org.gradle.internal.fingerprint.DirectorySensitivity;
+import org.gradle.internal.fingerprint.FileNormalizer;
+import org.gradle.internal.fingerprint.LineEndingSensitivity;
+import org.gradle.internal.hash.ClassLoaderHierarchyHasher;
+import org.gradle.internal.hash.HashCode;
+import org.gradle.internal.hash.Hasher;
+import org.gradle.internal.hash.Hashing;
+import org.gradle.internal.instantiation.InstanceFactory;
+import org.gradle.internal.instantiation.InstantiationScheme;
+import org.gradle.internal.isolated.IsolationScheme;
+import org.gradle.internal.isolation.Isolatable;
+import org.gradle.internal.isolation.IsolatableFactory;
+import org.gradle.internal.logging.text.TreeFormatter;
+import org.gradle.internal.model.CalculatedValueContainer;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
+import org.gradle.internal.model.ModelContainer;
+import org.gradle.internal.model.ValueCalculator;
+import org.gradle.internal.operations.BuildOperationContext;
+import org.gradle.internal.operations.BuildOperationDescriptor;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.operations.BuildOperationType;
+import org.gradle.internal.operations.RunnableBuildOperation;
+import org.gradle.internal.properties.InputBehavior;
+import org.gradle.internal.properties.InputFilePropertyType;
+import org.gradle.internal.properties.OutputFilePropertyType;
+import org.gradle.internal.properties.PropertyValue;
+import org.gradle.internal.properties.PropertyVisitor;
+import org.gradle.internal.properties.bean.PropertyWalker;
+import org.gradle.internal.reflect.DefaultTypeValidationContext;
+import org.gradle.internal.reflect.problems.ValidationProblemId;
+import org.gradle.internal.reflect.validation.Severity;
+import org.gradle.internal.reflect.validation.TypeValidationContext;
+import org.gradle.internal.service.ServiceLookup;
+import org.gradle.internal.service.ServiceLookupException;
+import org.gradle.internal.service.UnknownServiceException;
+import org.gradle.internal.snapshot.ValueSnapshot;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.work.InputChanges;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import static org.gradle.api.internal.tasks.properties.AbstractValidatingProperty.reportValueNotSet;
+
+public class DefaultTransform implements Transform {
+
+    private final Class<? extends TransformAction<?>> implementationClass;
+    private final ImmutableAttributes fromAttributes;
+    private final ImmutableAttributes toAttributes;
+    private final FileNormalizer fileNormalizer;
+    private final FileNormalizer dependenciesNormalizer;
+    private final FileLookup fileLookup;
+    private final ServiceLookup internalServices;
+    private final boolean requiresDependencies;
+    private final boolean requiresInputChanges;
+    private final InstanceFactory<? extends TransformAction<?>> instanceFactory;
+    private final boolean cacheable;
+    private final CalculatedValueContainer<IsolatedParameters, IsolateTransformParameters> isolatedParameters;
+    private final DirectorySensitivity artifactDirectorySensitivity;
+    private final DirectorySensitivity dependenciesDirectorySensitivity;
+    private final LineEndingSensitivity artifactLineEndingSensitivity;
+    private final LineEndingSensitivity dependenciesLineEndingSensitivity;
+
+    public DefaultTransform(
+        Class<? extends TransformAction<?>> implementationClass,
+        @Nullable TransformParameters parameterObject,
+        ImmutableAttributes fromAttributes,
+        ImmutableAttributes toAttributes,
+        FileNormalizer inputArtifactNormalizer,
+        FileNormalizer dependenciesNormalizer,
+        boolean cacheable,
+        DirectorySensitivity artifactDirectorySensitivity,
+        DirectorySensitivity dependenciesDirectorySensitivity,
+        LineEndingSensitivity artifactLineEndingSensitivity,
+        LineEndingSensitivity dependenciesLineEndingSensitivity,
+        BuildOperationExecutor buildOperationExecutor,
+        ClassLoaderHierarchyHasher classLoaderHierarchyHasher,
+        IsolatableFactory isolatableFactory,
+        FileCollectionFactory fileCollectionFactory,
+        FileLookup fileLookup,
+        PropertyWalker parameterPropertyWalker,
+        InstantiationScheme actionInstantiationScheme,
+        DomainObjectContext owner,
+        CalculatedValueContainerFactory calculatedValueContainerFactory,
+        ServiceLookup internalServices,
+        DocumentationRegistry documentationRegistry
+    ) {
+        this.implementationClass = implementationClass;
+        this.fromAttributes = fromAttributes;
+        this.toAttributes = toAttributes;
+        this.fileNormalizer = inputArtifactNormalizer;
+        this.dependenciesNormalizer = dependenciesNormalizer;
+        this.fileLookup = fileLookup;
+        this.internalServices = internalServices;
+        this.instanceFactory = actionInstantiationScheme.forType(implementationClass);
+        this.requiresDependencies = instanceFactory.serviceInjectionTriggeredByAnnotation(InputArtifactDependencies.class);
+        this.requiresInputChanges = instanceFactory.requiresService(InputChanges.class);
+        this.cacheable = cacheable;
+        this.artifactDirectorySensitivity = artifactDirectorySensitivity;
+        this.dependenciesDirectorySensitivity = dependenciesDirectorySensitivity;
+        this.artifactLineEndingSensitivity = artifactLineEndingSensitivity;
+        this.dependenciesLineEndingSensitivity = dependenciesLineEndingSensitivity;
+        this.isolatedParameters = calculatedValueContainerFactory.create(Describables.of("parameters of", this),
+            new IsolateTransformParameters(parameterObject, implementationClass, cacheable, owner, parameterPropertyWalker, isolatableFactory, buildOperationExecutor, classLoaderHierarchyHasher,
+                fileCollectionFactory, documentationRegistry));
+    }
+
+    /**
+     * Used to recreate a transformer from the configuration cache.
+     */
+    public DefaultTransform(
+        Class<? extends TransformAction<?>> implementationClass,
+        CalculatedValueContainer<IsolatedParameters, IsolateTransformParameters> isolatedParameters,
+        ImmutableAttributes fromAttributes,
+        ImmutableAttributes toAttributes,
+        FileNormalizer inputArtifactNormalizer,
+        FileNormalizer dependenciesNormalizer,
+        boolean cacheable,
+        FileLookup fileLookup,
+        InstantiationScheme actionInstantiationScheme,
+        ServiceLookup internalServices,
+        DirectorySensitivity artifactDirectorySensitivity,
+        DirectorySensitivity dependenciesDirectorySensitivity,
+        LineEndingSensitivity artifactLineEndingSensitivity,
+        LineEndingSensitivity dependenciesLineEndingSensitivity
+    ) {
+        this.implementationClass = implementationClass;
+        this.fromAttributes = fromAttributes;
+        this.toAttributes = toAttributes;
+        this.fileNormalizer = inputArtifactNormalizer;
+        this.dependenciesNormalizer = dependenciesNormalizer;
+        this.fileLookup = fileLookup;
+        this.internalServices = internalServices;
+        this.instanceFactory = actionInstantiationScheme.forType(implementationClass);
+        this.requiresDependencies = instanceFactory.serviceInjectionTriggeredByAnnotation(InputArtifactDependencies.class);
+        this.requiresInputChanges = instanceFactory.requiresService(InputChanges.class);
+        this.cacheable = cacheable;
+        this.isolatedParameters = isolatedParameters;
+        this.artifactDirectorySensitivity = artifactDirectorySensitivity;
+        this.dependenciesDirectorySensitivity = dependenciesDirectorySensitivity;
+        this.artifactLineEndingSensitivity = artifactLineEndingSensitivity;
+        this.dependenciesLineEndingSensitivity = dependenciesLineEndingSensitivity;
+    }
+
+    public static void validateInputFileNormalizer(String propertyName, @Nullable FileNormalizer normalizer, boolean cacheable, TypeValidationContext validationContext) {
+        if (cacheable) {
+            if (normalizer == InputNormalizer.ABSOLUTE_PATH) {
+                validationContext.visitPropertyProblem(problem ->
+                    problem.withId(ValidationProblemId.CACHEABLE_TRANSFORM_CANT_USE_ABSOLUTE_SENSITIVITY)
+                        .reportAs(Severity.ERROR)
+                        .forProperty(propertyName)
+                        .withDescription("is declared to be sensitive to absolute paths")
+                        .happensBecause("This is not allowed for cacheable transforms")
+                        .withLongDescription("Absolute path sensitivity does not allow sharing the transform result between different machines, although that is the goal of cacheable transforms.")
+                        .addPossibleSolution("Use a different normalization strategy via @PathSensitive, @Classpath or @CompileClasspath")
+                        .documentedAt("validation_problems", "cacheable_transform_cant_use_absolute_sensitivity"));
+            }
+        }
+    }
+
+    @Override
+    public FileNormalizer getInputArtifactNormalizer() {
+        return fileNormalizer;
+    }
+
+    @Override
+    public FileNormalizer getInputArtifactDependenciesNormalizer() {
+        return dependenciesNormalizer;
+    }
+
+    @Override
+    public boolean isIsolated() {
+        return isolatedParameters.getOrNull() != null;
+    }
+
+    @Override
+    public boolean requiresDependencies() {
+        return requiresDependencies;
+    }
+
+    @Override
+    public boolean requiresInputChanges() {
+        return requiresInputChanges;
+    }
+
+    @Override
+    public boolean isCacheable() {
+        return cacheable;
+    }
+
+    @Override
+    public DirectorySensitivity getInputArtifactDirectorySensitivity() {
+        return artifactDirectorySensitivity;
+    }
+
+    @Override
+    public DirectorySensitivity getInputArtifactDependenciesDirectorySensitivity() {
+        return dependenciesDirectorySensitivity;
+    }
+
+    @Override
+    public LineEndingSensitivity getInputArtifactLineEndingNormalization() {
+        return artifactLineEndingSensitivity;
+    }
+
+    @Override
+    public LineEndingSensitivity getInputArtifactDependenciesLineEndingNormalization() {
+        return dependenciesLineEndingSensitivity;
+    }
+
+    @Override
+    public HashCode getSecondaryInputHash() {
+        return isolatedParameters.get().getSecondaryInputsHash();
+    }
+
+    @Override
+    public TransformExecutionResult transform(Provider<FileSystemLocation> inputArtifactProvider, File outputDir, TransformDependencies dependencies, @Nullable InputChanges inputChanges) {
+        TransformAction<?> transformAction = newTransformAction(inputArtifactProvider, dependencies, inputChanges);
+        DefaultTransformOutputs transformOutputs = new DefaultTransformOutputs(inputArtifactProvider.get().getAsFile(), outputDir, fileLookup);
+        transformAction.transform(transformOutputs);
+        return transformOutputs.getRegisteredOutputs();
+    }
+
+    @Override
+    public void visitDependencies(TaskDependencyResolveContext context) {
+        context.add(isolatedParameters);
+    }
+
+    @Override
+    public void isolateParametersIfNotAlready() {
+        isolatedParameters.finalizeIfNotAlready();
+    }
+
+    private static void fingerprintParameters(
+        DocumentationRegistry documentationRegistry,
+        InputFingerprinter inputFingerprinter,
+        FileCollectionFactory fileCollectionFactory,
+        PropertyWalker propertyWalker,
+        Hasher hasher,
+        Object parameterObject,
+        boolean cacheable
+    ) {
+        DefaultTypeValidationContext validationContext = DefaultTypeValidationContext.withoutRootType(documentationRegistry, cacheable);
+        InputFingerprinter.Result result = inputFingerprinter.fingerprintInputProperties(
+            ImmutableSortedMap.of(),
+            ImmutableSortedMap.of(),
+            ImmutableSortedMap.of(),
+            ImmutableSortedMap.of(),
+            visitor -> propertyWalker.visitProperties(parameterObject, validationContext, new PropertyVisitor() {
+                @Override
+                public void visitInputProperty(
+                    String propertyName,
+                    PropertyValue value,
+                    boolean optional
+                ) {
+                    try {
+                        Object preparedValue = InputParameterUtils.prepareInputParameterValue(value);
+
+                        if (preparedValue == null && !optional) {
+                            reportValueNotSet(propertyName, validationContext);
+                        }
+                        visitor.visitInputProperty(propertyName, () -> preparedValue);
+                    } catch (Throwable e) {
+                        throw new InvalidUserDataException(String.format(
+                            "Error while evaluating property '%s' of %s",
+                            propertyName,
+                            getParameterObjectDisplayName(parameterObject)
+                        ), e);
+                    }
+                }
+
+                @Override
+                public void visitInputFileProperty(
+                    String propertyName,
+                    boolean optional,
+                    InputBehavior behavior,
+                    DirectorySensitivity directorySensitivity,
+                    LineEndingSensitivity lineEndingNormalization,
+                    @Nullable FileNormalizer normalizer,
+                    PropertyValue value,
+                    InputFilePropertyType filePropertyType
+                ) {
+                    validateInputFileNormalizer(propertyName, normalizer, cacheable, validationContext);
+                    visitor.visitInputFileProperty(
+                        propertyName,
+                        behavior,
+                        new InputFileValueSupplier(
+                            value,
+                            normalizer == null ? InputNormalizer.ABSOLUTE_PATH : normalizer,
+                            directorySensitivity,
+                            lineEndingNormalization,
+                            () -> FileParameterUtils.resolveInputFileValue(fileCollectionFactory, filePropertyType, value)));
+                }
+
+                @Override
+                public void visitOutputFileProperty(
+                    String propertyName,
+                    boolean optional,
+                    PropertyValue value,
+                    OutputFilePropertyType filePropertyType
+                ) {
+                    validationContext.visitPropertyProblem(problem ->
+                        problem.withId(ValidationProblemId.ARTIFACT_TRANSFORM_SHOULD_NOT_DECLARE_OUTPUT)
+                            .reportAs(Severity.ERROR)
+                            .forProperty(propertyName)
+                            .withDescription("declares an output")
+                            .happensBecause("is annotated with an output annotation")
+                            .addPossibleSolution("Remove the output property and use the TransformOutputs parameter from transform(TransformOutputs) instead")
+                            .documentedAt("validation_problems", "artifact_transform_should_not_declare_output")
+                    );
+                }
+            })
+        );
+
+        ImmutableMap<String, Severity> validationMessages = validationContext.getProblems();
+        if (!validationMessages.isEmpty()) {
+            throw new DefaultMultiCauseException(
+                String.format(validationMessages.size() == 1
+                        ? "A problem was found with the configuration of the artifact transform parameter %s."
+                        : "Some problems were found with the configuration of the artifact transform parameter %s.",
+                    getParameterObjectDisplayName(parameterObject)),
+                validationMessages.keySet().stream()
+                    .sorted()
+                    .map(InvalidUserDataException::new)
+                    .collect(Collectors.toList())
+            );
+        }
+
+        for (Map.Entry<String, ValueSnapshot> entry : result.getValueSnapshots().entrySet()) {
+            hasher.putString(entry.getKey());
+            entry.getValue().appendToHasher(hasher);
+        }
+        for (Map.Entry<String, CurrentFileCollectionFingerprint> entry : result.getFileFingerprints().entrySet()) {
+            hasher.putString(entry.getKey());
+            hasher.putHash(entry.getValue().getHash());
+        }
+    }
+
+    private static String getParameterObjectDisplayName(Object parameterObject) {
+        return ModelType.of(new DslObject(parameterObject).getDeclaredType()).getDisplayName();
+    }
+
+    private TransformAction<?> newTransformAction(Provider<FileSystemLocation> inputArtifactProvider, TransformDependencies transformDependencies, @Nullable InputChanges inputChanges) {
+        TransformParameters parameters = isolatedParameters.get().getIsolatedParameterObject().isolate();
+        ServiceLookup services = new IsolationScheme<>(TransformAction.class, TransformParameters.class, TransformParameters.None.class).servicesForImplementation(parameters, internalServices);
+        services = new TransformServiceLookup(inputArtifactProvider, requiresDependencies ? transformDependencies : null, inputChanges, services);
+        return instanceFactory.newInstance(services);
+    }
+
+    public CalculatedValueContainer<IsolatedParameters, IsolateTransformParameters> getIsolatedParameters() {
+        return isolatedParameters;
+    }
+
+    @Override
+    public ImmutableAttributes getFromAttributes() {
+        return fromAttributes;
+    }
+
+    @Override
+    public ImmutableAttributes getToAttributes() {
+        return toAttributes;
+    }
+
+    @Override
+    public Class<? extends TransformAction<?>> getImplementationClass() {
+        return implementationClass;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return implementationClass.getSimpleName();
+    }
+
+    private static class TransformServiceLookup implements ServiceLookup {
+        private static final Type FILE_SYSTEM_LOCATION_PROVIDER = new TypeToken<Provider<FileSystemLocation>>() {
+        }.getType();
+
+        private final ImmutableList<InjectionPoint> injectionPoints;
+        private final ServiceLookup delegate;
+
+        public TransformServiceLookup(Provider<FileSystemLocation> inputFileProvider, @Nullable TransformDependencies transformDependencies, @Nullable InputChanges inputChanges, ServiceLookup delegate) {
+            this.delegate = delegate;
+            ImmutableList.Builder<InjectionPoint> builder = ImmutableList.builder();
+            builder.add(InjectionPoint.injectedByAnnotation(InputArtifact.class, FILE_SYSTEM_LOCATION_PROVIDER, () -> inputFileProvider));
+            if (transformDependencies != null) {
+                builder.add(InjectionPoint.injectedByAnnotation(InputArtifactDependencies.class, () -> transformDependencies.getFiles().orElseThrow(() -> new IllegalStateException("Transform does not use artifact dependencies."))));
+            }
+            if (inputChanges != null) {
+                builder.add(InjectionPoint.injectedByType(InputChanges.class, () -> inputChanges));
+            }
+            this.injectionPoints = builder.build();
+        }
+
+        @Nullable
+        private Object find(Type serviceType, @Nullable Class<? extends Annotation> annotatedWith) {
+            TypeToken<?> serviceTypeToken = TypeToken.of(serviceType);
+            for (InjectionPoint injectionPoint : injectionPoints) {
+                if (annotatedWith == injectionPoint.getAnnotation() && serviceTypeToken.isSupertypeOf(injectionPoint.getInjectedType())) {
+                    return injectionPoint.getValueToInject();
+                }
+            }
+            return null;
+        }
+
+        @Nullable
+        @Override
+        public Object find(Type serviceType) throws ServiceLookupException {
+            Object result = find(serviceType, null);
+            if (result != null) {
+                return result;
+            }
+            return delegate.find(serviceType);
+        }
+
+        @Override
+        public Object get(Type serviceType) throws UnknownServiceException, ServiceLookupException {
+            Object result = find(serviceType);
+            if (result == null) {
+                throw new UnknownServiceException(serviceType, "No service of type " + serviceType + " available.");
+            }
+            return result;
+        }
+
+        @Override
+        public Object get(Type serviceType, Class<? extends Annotation> annotatedWith) throws UnknownServiceException, ServiceLookupException {
+            Object result = find(serviceType, annotatedWith);
+            if (result != null) {
+                return result;
+            }
+            return delegate.get(serviceType, annotatedWith);
+        }
+
+        private static class InjectionPoint {
+            private final Class<? extends Annotation> annotation;
+            private final Type injectedType;
+            private final Supplier<Object> valueToInject;
+
+            public static InjectionPoint injectedByAnnotation(Class<? extends Annotation> annotation, Supplier<Object> valueToInject) {
+                return new InjectionPoint(annotation, determineTypeFromAnnotation(annotation), valueToInject);
+            }
+
+            public static InjectionPoint injectedByAnnotation(Class<? extends Annotation> annotation, Type injectedType, Supplier<Object> valueToInject) {
+                return new InjectionPoint(annotation, injectedType, valueToInject);
+            }
+
+            public static InjectionPoint injectedByType(Class<?> injectedType, Supplier<Object> valueToInject) {
+                return new InjectionPoint(null, injectedType, valueToInject);
+            }
+
+            private InjectionPoint(@Nullable Class<? extends Annotation> annotation, Type injectedType, Supplier<Object> valueToInject) {
+                this.annotation = annotation;
+                this.injectedType = injectedType;
+                this.valueToInject = valueToInject;
+            }
+
+            private static Class<?> determineTypeFromAnnotation(Class<? extends Annotation> annotation) {
+                Class<?>[] supportedTypes = annotation.getAnnotation(InjectionPointQualifier.class).supportedTypes();
+                if (supportedTypes.length != 1) {
+                    throw new IllegalArgumentException("Cannot determine supported type for annotation " + annotation.getName());
+                }
+                return supportedTypes[0];
+            }
+
+            @Nullable
+            public Class<? extends Annotation> getAnnotation() {
+                return annotation;
+            }
+
+            public Type getInjectedType() {
+                return injectedType;
+            }
+
+            public Object getValueToInject() {
+                return valueToInject.get();
+            }
+        }
+    }
+
+    public static class IsolatedParameters {
+        private final HashCode secondaryInputsHash;
+        private final Isolatable<? extends TransformParameters> isolatedParameterObject;
+
+        public IsolatedParameters(Isolatable<? extends TransformParameters> isolatedParameterObject, HashCode secondaryInputsHash) {
+            this.secondaryInputsHash = secondaryInputsHash;
+            this.isolatedParameterObject = isolatedParameterObject;
+        }
+
+        public HashCode getSecondaryInputsHash() {
+            return secondaryInputsHash;
+        }
+
+        public Isolatable<? extends TransformParameters> getIsolatedParameterObject() {
+            return isolatedParameterObject;
+        }
+    }
+
+    public static class IsolateTransformParameters implements ValueCalculator<IsolatedParameters> {
+        private final TransformParameters parameterObject;
+        private final DomainObjectContext owner;
+        private final IsolatableFactory isolatableFactory;
+        private final PropertyWalker parameterPropertyWalker;
+        private final BuildOperationExecutor buildOperationExecutor;
+        private final ClassLoaderHierarchyHasher classLoaderHierarchyHasher;
+        private final FileCollectionFactory fileCollectionFactory;
+        private final DocumentationRegistry documentationRegistry;
+        private final boolean cacheable;
+        private final Class<?> implementationClass;
+
+        public IsolateTransformParameters(
+            @Nullable TransformParameters parameterObject,
+            Class<?> implementationClass,
+            boolean cacheable,
+            DomainObjectContext owner,
+            PropertyWalker parameterPropertyWalker,
+            IsolatableFactory isolatableFactory,
+            BuildOperationExecutor buildOperationExecutor,
+            ClassLoaderHierarchyHasher classLoaderHierarchyHasher,
+            FileCollectionFactory fileCollectionFactory,
+            DocumentationRegistry documentationRegistry
+        ) {
+            this.parameterObject = parameterObject;
+            this.implementationClass = implementationClass;
+            this.cacheable = cacheable;
+            this.owner = owner;
+            this.parameterPropertyWalker = parameterPropertyWalker;
+            this.isolatableFactory = isolatableFactory;
+            this.buildOperationExecutor = buildOperationExecutor;
+            this.classLoaderHierarchyHasher = classLoaderHierarchyHasher;
+            this.fileCollectionFactory = fileCollectionFactory;
+            this.documentationRegistry = documentationRegistry;
+        }
+
+        @Nullable
+        public TransformParameters getParameterObject() {
+            return parameterObject;
+        }
+
+        public boolean isCacheable() {
+            return cacheable;
+        }
+
+        public Class<?> getImplementationClass() {
+            return implementationClass;
+        }
+
+        @Override
+        public boolean usesMutableProjectState() {
+            return owner.getProject() != null;
+        }
+
+        @Nullable
+        @Override
+        public ProjectInternal getOwningProject() {
+            return owner.getProject();
+        }
+
+        @Override
+        public void visitDependencies(TaskDependencyResolveContext context) {
+            if (parameterObject != null) {
+                parameterPropertyWalker.visitProperties(parameterObject, TypeValidationContext.NOOP, new PropertyVisitor() {
+                    @Override
+                    public void visitInputFileProperty(
+                        String propertyName,
+                        boolean optional,
+                        InputBehavior behavior,
+                        DirectorySensitivity directorySensitivity,
+                        LineEndingSensitivity lineEndingSensitivity,
+                        @Nullable FileNormalizer fileNormalizer,
+                        PropertyValue value,
+                        InputFilePropertyType filePropertyType
+                    ) {
+                        context.add(value.getTaskDependencies());
+                    }
+                });
+            }
+        }
+
+        @Override
+        public IsolatedParameters calculateValue(NodeExecutionContext context) {
+            InputFingerprinter inputFingerprinter = context.getService(InputFingerprinter.class);
+            return isolateParameters(inputFingerprinter);
+        }
+
+        private IsolatedParameters isolateParameters(InputFingerprinter inputFingerprinter) {
+            ModelContainer<?> model = owner.getModel();
+            if (!model.hasMutableState()) {
+                // This may happen when a task visits artifacts using a FileCollection instance created from a Configuration instance in a different project (not an artifact produced by a different project, these work fine)
+                // There is a check in DefaultConfiguration that deprecates resolving dependencies via FileCollection instance created by a different project, however that check may not
+                // necessarily be triggered. For example, the configuration may be legitimately resolved by some other task prior to the problematic task running
+                // TODO - hoist this up into configuration file collection visiting (and not when visiting the upstream dependencies of a transform), and deprecate this in Gradle 7.x
+                //
+                // This may also happen when a transform takes upstream dependencies and the dependencies are transformed using a different transform
+                // In this case, the main thread that schedules the work should isolate the transform parameters prior to scheduling the work. However, the dependencies may
+                // be filtered from the result, so that the transform is not visited by the main thread, or the transform worker may start work before the main thread
+                // has a chance to isolate the upstream transform
+                // TODO - ensure all transform parameters required by a transform worker are isolated prior to starting the worker
+                //
+                // Force access to the state of the owner, regardless of whether any other thread has access. This is because attempting to acquire a lock for a project may deadlock
+                // when performed from a worker thread (see DefaultBuildOperationQueue.waitForCompletion() which intentionally does not release the project locks while waiting)
+                // TODO - add validation to fail eagerly when a worker attempts to lock a project
+                //
+                return model.forceAccessToMutableState(o -> doIsolateParameters(inputFingerprinter));
+            } else {
+                return doIsolateParameters(inputFingerprinter);
+            }
+        }
+
+        private IsolatedParameters doIsolateParameters(InputFingerprinter inputFingerprinter) {
+            try {
+                return isolateParametersExclusively(inputFingerprinter);
+            } catch (Exception e) {
+                TreeFormatter formatter = new TreeFormatter();
+                formatter.node("Could not isolate parameters ").appendValue(parameterObject).append(" of artifact transform ").appendType(implementationClass);
+                throw new VariantTransformConfigurationException(formatter.toString(), e);
+            }
+        }
+
+        private IsolatedParameters isolateParametersExclusively(InputFingerprinter inputFingerprinter) {
+            Isolatable<TransformParameters> isolatedParameterObject = isolatableFactory.isolate(parameterObject);
+
+            Hasher hasher = Hashing.newHasher();
+            hasher.putString(implementationClass.getName());
+            hasher.putHash(classLoaderHierarchyHasher.getClassLoaderHash(implementationClass.getClassLoader()));
+
+            if (parameterObject != null) {
+                TransformParameters isolatedTransformParameters = isolatedParameterObject.isolate();
+                buildOperationExecutor.run(new RunnableBuildOperation() {
+                    @Override
+                    public void run(BuildOperationContext context) {
+                        // TODO wolfs - schedule fingerprinting separately, it can be done without having the project lock
+                        fingerprintParameters(
+                            documentationRegistry,
+                            inputFingerprinter,
+                            fileCollectionFactory,
+                            parameterPropertyWalker,
+                            hasher,
+                            isolatedTransformParameters,
+                            cacheable
+                        );
+                        context.setResult(FingerprintTransformInputsOperation.Result.INSTANCE);
+                    }
+
+                    @Override
+                    public BuildOperationDescriptor.Builder description() {
+                        return BuildOperationDescriptor
+                            .displayName("Fingerprint transform inputs")
+                            .details(FingerprintTransformInputsOperation.Details.INSTANCE);
+                    }
+                });
+            }
+            HashCode secondaryInputsHash = hasher.hash();
+            return new IsolatedParameters(isolatedParameterObject, secondaryInputsHash);
+        }
+    }
+
+    /*
+     * This operation is only used here temporarily. Should be replaced with a more stable operation in the long term.
+     */
+    public interface FingerprintTransformInputsOperation extends BuildOperationType<FingerprintTransformInputsOperation.Details, FingerprintTransformInputsOperation.Result> {
+        interface Details {
+            Details INSTANCE = new Details() {
+            };
+        }
+
+        interface Result {
+            Result INSTANCE = new Result() {
+            };
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformDependencies.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformDependencies.java
new file mode 100644
index 0000000..0469c95
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformDependencies.java
@@ -0,0 +1,34 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.file.FileCollection;
+
+import java.util.Optional;
+
+public class DefaultTransformDependencies implements TransformDependencies {
+    private final FileCollection files;
+
+    public DefaultTransformDependencies(FileCollection files) {
+        this.files = files;
+    }
+
+    @Override
+    public Optional<FileCollection> getFiles() {
+        return Optional.of(files);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformInvocationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformInvocationFactory.java
new file mode 100644
index 0000000..e5fedd5
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformInvocationFactory.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2017 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.api.internal.artifacts.transform;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.file.FileSystemLocation;
+import org.gradle.api.internal.file.DefaultFileSystemLocation;
+import org.gradle.api.internal.file.FileCollectionFactory;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.project.ProjectStateRegistry;
+import org.gradle.api.internal.provider.Providers;
+import org.gradle.api.provider.Provider;
+import org.gradle.internal.Deferrable;
+import org.gradle.internal.Try;
+import org.gradle.internal.execution.ExecutionEngine;
+import org.gradle.internal.execution.InputFingerprinter;
+import org.gradle.internal.execution.UnitOfWork;
+import org.gradle.internal.execution.caching.CachingDisabledReason;
+import org.gradle.internal.execution.caching.CachingDisabledReasonCategory;
+import org.gradle.internal.execution.history.OverlappingOutputs;
+import org.gradle.internal.execution.history.changes.InputChangesInternal;
+import org.gradle.internal.execution.model.InputNormalizer;
+import org.gradle.internal.execution.workspace.WorkspaceProvider;
+import org.gradle.internal.fingerprint.CurrentFileCollectionFingerprint;
+import org.gradle.internal.hash.HashCode;
+import org.gradle.internal.hash.Hasher;
+import org.gradle.internal.hash.Hashing;
+import org.gradle.internal.operations.BuildOperationContext;
+import org.gradle.internal.operations.BuildOperationDescriptor;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.operations.CallableBuildOperation;
+import org.gradle.internal.operations.UncategorizedBuildOperations;
+import org.gradle.internal.snapshot.FileSystemLocationSnapshot;
+import org.gradle.internal.snapshot.ValueSnapshot;
+import org.gradle.internal.vfs.FileSystemAccess;
+
+import javax.annotation.Nullable;
+import javax.annotation.OverridingMethodsMustInvokeSuper;
+import java.io.File;
+import java.time.Duration;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.gradle.internal.file.TreeType.DIRECTORY;
+import static org.gradle.internal.file.TreeType.FILE;
+import static org.gradle.internal.properties.InputBehavior.INCREMENTAL;
+import static org.gradle.internal.properties.InputBehavior.NON_INCREMENTAL;
+
+public class DefaultTransformInvocationFactory implements TransformInvocationFactory {
+    private static final CachingDisabledReason NOT_CACHEABLE = new CachingDisabledReason(CachingDisabledReasonCategory.NOT_CACHEABLE, "Caching not enabled.");
+    private static final String INPUT_ARTIFACT_PROPERTY_NAME = "inputArtifact";
+    private static final String INPUT_ARTIFACT_PATH_PROPERTY_NAME = "inputArtifactPath";
+    private static final String INPUT_ARTIFACT_SNAPSHOT_PROPERTY_NAME = "inputArtifactSnapshot";
+    private static final String DEPENDENCIES_PROPERTY_NAME = "inputArtifactDependencies";
+    private static final String SECONDARY_INPUTS_HASH_PROPERTY_NAME = "inputPropertiesHash";
+    private static final String OUTPUT_DIRECTORY_PROPERTY_NAME = "outputDirectory";
+    private static final String RESULTS_FILE_PROPERTY_NAME = "resultsFile";
+
+    private final ExecutionEngine executionEngine;
+    private final FileSystemAccess fileSystemAccess;
+    private final TransformExecutionListener transformExecutionListener;
+    private final TransformWorkspaceServices immutableWorkspaceProvider;
+    private final FileCollectionFactory fileCollectionFactory;
+    private final ProjectStateRegistry projectStateRegistry;
+    private final BuildOperationExecutor buildOperationExecutor;
+
+    public DefaultTransformInvocationFactory(
+        ExecutionEngine executionEngine,
+        FileSystemAccess fileSystemAccess,
+        TransformExecutionListener transformExecutionListener,
+        TransformWorkspaceServices immutableWorkspaceProvider,
+        FileCollectionFactory fileCollectionFactory,
+        ProjectStateRegistry projectStateRegistry,
+        BuildOperationExecutor buildOperationExecutor
+    ) {
+        this.executionEngine = executionEngine;
+        this.fileSystemAccess = fileSystemAccess;
+        this.transformExecutionListener = transformExecutionListener;
+        this.immutableWorkspaceProvider = immutableWorkspaceProvider;
+        this.fileCollectionFactory = fileCollectionFactory;
+        this.projectStateRegistry = projectStateRegistry;
+        this.buildOperationExecutor = buildOperationExecutor;
+    }
+
+    @Override
+    public Deferrable<Try<ImmutableList<File>>> createInvocation(
+        Transform transform,
+        File inputArtifact,
+        TransformDependencies dependencies,
+        TransformStepSubject subject,
+        InputFingerprinter inputFingerprinter
+    ) {
+        ProjectInternal producerProject = determineProducerProject(subject);
+        TransformWorkspaceServices workspaceServices = determineWorkspaceServices(producerProject);
+
+        UnitOfWork execution;
+        if (producerProject == null) {
+            execution = new ImmutableTransformExecution(
+                transform,
+                inputArtifact,
+                dependencies,
+                subject,
+
+                transformExecutionListener,
+                buildOperationExecutor,
+                fileCollectionFactory,
+                inputFingerprinter,
+                fileSystemAccess,
+                workspaceServices
+            );
+        } else {
+            execution = new MutableTransformExecution(
+                transform,
+                inputArtifact,
+                dependencies,
+                subject,
+
+                transformExecutionListener,
+                buildOperationExecutor,
+                fileCollectionFactory,
+                inputFingerprinter,
+                workspaceServices
+            );
+        }
+
+        return executionEngine.createRequest(execution)
+            .executeDeferred(workspaceServices.getIdentityCache())
+            .map(result -> result
+                .map(successfulResult -> successfulResult.resolveOutputsForInputArtifact(inputArtifact))
+                .mapFailure(failure -> new TransformException(String.format("Execution failed for %s.", execution.getDisplayName()), failure)));
+    }
+
+    private TransformWorkspaceServices determineWorkspaceServices(@Nullable ProjectInternal producerProject) {
+        if (producerProject == null) {
+            return immutableWorkspaceProvider;
+        }
+        return producerProject.getServices().get(TransformWorkspaceServices.class);
+    }
+
+    @Nullable
+    private ProjectInternal determineProducerProject(TransformStepSubject subject) {
+        ComponentIdentifier componentIdentifier = subject.getInitialComponentIdentifier();
+        if (componentIdentifier instanceof ProjectComponentIdentifier) {
+            return projectStateRegistry.stateFor((ProjectComponentIdentifier) componentIdentifier).getMutableModel();
+        } else {
+            return null;
+        }
+    }
+
+    private static class ImmutableTransformExecution extends AbstractTransformExecution {
+        private final FileSystemAccess fileSystemAccess;
+
+        public ImmutableTransformExecution(
+            Transform transform,
+            File inputArtifact,
+            TransformDependencies dependencies,
+            TransformStepSubject subject,
+
+            TransformExecutionListener transformExecutionListener,
+            BuildOperationExecutor buildOperationExecutor,
+            FileCollectionFactory fileCollectionFactory,
+            InputFingerprinter inputFingerprinter,
+            FileSystemAccess fileSystemAccess,
+            TransformWorkspaceServices workspaceServices
+        ) {
+            super(
+                transform, inputArtifact, dependencies, subject,
+                transformExecutionListener, buildOperationExecutor, fileCollectionFactory, inputFingerprinter, workspaceServices
+            );
+            this.fileSystemAccess = fileSystemAccess;
+        }
+
+        @Override
+        public void visitIdentityInputs(InputVisitor visitor) {
+            super.visitIdentityInputs(visitor);
+            // This is a performance hack. We could use the regular fingerprint of the input artifact, but that takes longer than
+            // capturing the normalized path and the snapshot of the raw contents, so we are using these to determine the identity
+            FileSystemLocationSnapshot inputArtifactSnapshot = fileSystemAccess.read(inputArtifact.getAbsolutePath());
+            visitor.visitInputProperty(INPUT_ARTIFACT_SNAPSHOT_PROPERTY_NAME, inputArtifactSnapshot::getHash);
+        }
+
+        @Override
+        public Identity identify(Map<String, ValueSnapshot> identityInputs, Map<String, CurrentFileCollectionFingerprint> identityFileInputs) {
+            return new ImmutableTransformWorkspaceIdentity(
+                identityInputs.get(INPUT_ARTIFACT_PATH_PROPERTY_NAME),
+                identityInputs.get(INPUT_ARTIFACT_SNAPSHOT_PROPERTY_NAME),
+                identityInputs.get(SECONDARY_INPUTS_HASH_PROPERTY_NAME),
+                identityFileInputs.get(DEPENDENCIES_PROPERTY_NAME).getHash()
+            );
+        }
+    }
+
+    private static class MutableTransformExecution extends AbstractTransformExecution {
+        public MutableTransformExecution(
+            Transform transform,
+            File inputArtifact,
+            TransformDependencies dependencies,
+            TransformStepSubject subject,
+
+            TransformExecutionListener transformExecutionListener,
+            BuildOperationExecutor buildOperationExecutor,
+            FileCollectionFactory fileCollectionFactory,
+            InputFingerprinter inputFingerprinter,
+            TransformWorkspaceServices workspaceServices
+        ) {
+            super(
+                transform, inputArtifact, dependencies, subject,
+                transformExecutionListener, buildOperationExecutor, fileCollectionFactory, inputFingerprinter, workspaceServices
+            );
+        }
+
+        @Override
+        public Identity identify(Map<String, ValueSnapshot> identityInputs, Map<String, CurrentFileCollectionFingerprint> identityFileInputs) {
+            return new MutableTransformWorkspaceIdentity(
+                inputArtifact.getAbsolutePath(),
+                identityInputs.get(SECONDARY_INPUTS_HASH_PROPERTY_NAME),
+                identityFileInputs.get(DEPENDENCIES_PROPERTY_NAME).getHash()
+            );
+        }
+    }
+
+    private abstract static class AbstractTransformExecution implements UnitOfWork {
+        protected final Transform transform;
+        protected final File inputArtifact;
+        private final TransformDependencies dependencies;
+        private final TransformStepSubject subject;
+
+        private final TransformExecutionListener transformExecutionListener;
+        private final BuildOperationExecutor buildOperationExecutor;
+        private final FileCollectionFactory fileCollectionFactory;
+
+        private final Provider<FileSystemLocation> inputArtifactProvider;
+        protected final InputFingerprinter inputFingerprinter;
+        private final TransformWorkspaceServices workspaceServices;
+
+        public AbstractTransformExecution(
+            Transform transform,
+            File inputArtifact,
+            TransformDependencies dependencies,
+            TransformStepSubject subject,
+
+            TransformExecutionListener transformExecutionListener,
+            BuildOperationExecutor buildOperationExecutor,
+            FileCollectionFactory fileCollectionFactory,
+            InputFingerprinter inputFingerprinter,
+            TransformWorkspaceServices workspaceServices
+        ) {
+            this.transform = transform;
+            this.inputArtifact = inputArtifact;
+            this.dependencies = dependencies;
+            this.inputArtifactProvider = Providers.of(new DefaultFileSystemLocation(inputArtifact));
+            this.subject = subject;
+            this.transformExecutionListener = transformExecutionListener;
+
+            this.buildOperationExecutor = buildOperationExecutor;
+            this.fileCollectionFactory = fileCollectionFactory;
+            this.inputFingerprinter = inputFingerprinter;
+            this.workspaceServices = workspaceServices;
+        }
+
+        @Override
+        public WorkOutput execute(ExecutionRequest executionRequest) {
+            transformExecutionListener.beforeTransformExecution(transform, subject);
+            try {
+                return executeWithinTransformerListener(executionRequest);
+            } finally {
+                transformExecutionListener.afterTransformExecution(transform, subject);
+            }
+        }
+
+        private WorkOutput executeWithinTransformerListener(ExecutionRequest executionRequest) {
+            TransformExecutionResult result = buildOperationExecutor.call(new CallableBuildOperation<TransformExecutionResult>() {
+                @Override
+                public TransformExecutionResult call(BuildOperationContext context) {
+                    File workspace = executionRequest.getWorkspace();
+                    InputChangesInternal inputChanges = executionRequest.getInputChanges().orElse(null);
+                    TransformExecutionResult result = transform.transform(inputArtifactProvider, getOutputDir(workspace), dependencies, inputChanges);
+                    TransformExecutionResultSerializer resultSerializer = new TransformExecutionResultSerializer(getOutputDir(workspace));
+                    resultSerializer.writeToFile(getResultsFile(workspace), result);
+                    return result;
+                }
+
+                @Override
+                public BuildOperationDescriptor.Builder description() {
+                    String displayName = transform.getDisplayName() + " " + inputArtifact.getName();
+                    return BuildOperationDescriptor.displayName(displayName)
+                        .metadata(UncategorizedBuildOperations.TRANSFORM_ACTION)
+                        .progressDisplayName(displayName);
+                }
+            });
+
+            return new WorkOutput() {
+                @Override
+                public WorkResult getDidWork() {
+                    return WorkResult.DID_WORK;
+                }
+
+                @Override
+                public Object getOutput() {
+                    return result;
+                }
+            };
+        }
+
+        @Override
+        public Object loadAlreadyProducedOutput(File workspace) {
+            TransformExecutionResultSerializer resultSerializer = new TransformExecutionResultSerializer(getOutputDir(workspace));
+            return resultSerializer.readResultsFile(getResultsFile(workspace));
+        }
+
+        @Override
+        public WorkspaceProvider getWorkspaceProvider() {
+            return workspaceServices.getWorkspaceProvider();
+        }
+
+        @Override
+        public InputFingerprinter getInputFingerprinter() {
+            return inputFingerprinter;
+        }
+
+        private static File getOutputDir(File workspace) {
+            return new File(workspace, "transformed");
+        }
+
+        private static File getResultsFile(File workspace) {
+            return new File(workspace, "results.bin");
+        }
+
+        @Override
+        public Optional<Duration> getTimeout() {
+            return Optional.empty();
+        }
+
+        @Override
+        public ExecutionBehavior getExecutionBehavior() {
+            return transform.requiresInputChanges()
+                ? ExecutionBehavior.INCREMENTAL
+                : ExecutionBehavior.NON_INCREMENTAL;
+        }
+
+        @Override
+        public void visitImplementations(ImplementationVisitor visitor) {
+            visitor.visitImplementation(transform.getImplementationClass());
+        }
+
+        @Override
+        @OverridingMethodsMustInvokeSuper
+        public void visitIdentityInputs(InputVisitor visitor) {
+            // Emulate secondary inputs as a single property for now
+            visitor.visitInputProperty(SECONDARY_INPUTS_HASH_PROPERTY_NAME, transform::getSecondaryInputHash);
+            visitor.visitInputProperty(INPUT_ARTIFACT_PATH_PROPERTY_NAME, () ->
+                // We always need the name as an input to the artifact transform,
+                // since it is part of the ComponentArtifactIdentifier returned by the transform.
+                // For absolute paths, the name is already part of the normalized path,
+                // and for all the other normalization strategies we use the name directly.
+                transform.getInputArtifactNormalizer() == InputNormalizer.ABSOLUTE_PATH
+                    ? inputArtifact.getAbsolutePath()
+                    : inputArtifact.getName());
+            visitor.visitInputFileProperty(DEPENDENCIES_PROPERTY_NAME, NON_INCREMENTAL,
+                new InputFileValueSupplier(
+                    dependencies,
+                    transform.getInputArtifactDependenciesNormalizer(),
+                    transform.getInputArtifactDependenciesDirectorySensitivity(),
+                    transform.getInputArtifactDependenciesLineEndingNormalization(),
+                    () -> dependencies.getFiles()
+                        .orElse(FileCollectionFactory.empty())));
+        }
+
+        @Override
+        @OverridingMethodsMustInvokeSuper
+        public void visitRegularInputs(InputVisitor visitor) {
+            visitor.visitInputFileProperty(INPUT_ARTIFACT_PROPERTY_NAME, INCREMENTAL,
+                new InputFileValueSupplier(
+                    inputArtifactProvider,
+                    transform.getInputArtifactNormalizer(),
+                    transform.getInputArtifactDirectorySensitivity(),
+                    transform.getInputArtifactLineEndingNormalization(),
+                    () -> fileCollectionFactory.fixed(inputArtifact)));
+        }
+
+        @Override
+        public void visitOutputs(File workspace, OutputVisitor visitor) {
+            File outputDir = getOutputDir(workspace);
+            File resultsFile = getResultsFile(workspace);
+            visitor.visitOutputProperty(OUTPUT_DIRECTORY_PROPERTY_NAME, DIRECTORY,
+                OutputFileValueSupplier.fromStatic(outputDir, fileCollectionFactory.fixed(outputDir)));
+            visitor.visitOutputProperty(RESULTS_FILE_PROPERTY_NAME, FILE,
+                OutputFileValueSupplier.fromStatic(resultsFile, fileCollectionFactory.fixed(resultsFile)));
+        }
+
+        @Override
+        public Optional<CachingDisabledReason> shouldDisableCaching(@Nullable OverlappingOutputs detectedOverlappingOutputs) {
+            return transform.isCacheable()
+                ? Optional.empty()
+                : Optional.of(NOT_CACHEABLE);
+        }
+
+        @Override
+        public String getDisplayName() {
+            return transform.getDisplayName() + ": " + inputArtifact;
+        }
+    }
+
+    private static class ImmutableTransformWorkspaceIdentity implements UnitOfWork.Identity {
+        private final ValueSnapshot inputArtifactPath;
+        private final ValueSnapshot inputArtifactSnapshot;
+        private final ValueSnapshot secondaryInputSnapshot;
+        private final HashCode dependenciesHash;
+
+        public ImmutableTransformWorkspaceIdentity(ValueSnapshot inputArtifactPath, ValueSnapshot inputArtifactSnapshot, ValueSnapshot secondaryInputSnapshot, HashCode dependenciesHash) {
+            this.inputArtifactPath = inputArtifactPath;
+            this.inputArtifactSnapshot = inputArtifactSnapshot;
+            this.secondaryInputSnapshot = secondaryInputSnapshot;
+            this.dependenciesHash = dependenciesHash;
+        }
+
+        @Override
+        public String getUniqueId() {
+            Hasher hasher = Hashing.newHasher();
+            inputArtifactPath.appendToHasher(hasher);
+            inputArtifactSnapshot.appendToHasher(hasher);
+            secondaryInputSnapshot.appendToHasher(hasher);
+            hasher.putHash(dependenciesHash);
+            return hasher.hash().toString();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            ImmutableTransformWorkspaceIdentity that = (ImmutableTransformWorkspaceIdentity) o;
+
+            if (!inputArtifactPath.equals(that.inputArtifactPath)) {
+                return false;
+            }
+            if (!inputArtifactSnapshot.equals(that.inputArtifactSnapshot)) {
+                return false;
+            }
+            if (!secondaryInputSnapshot.equals(that.secondaryInputSnapshot)) {
+                return false;
+            }
+            return dependenciesHash.equals(that.dependenciesHash);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = inputArtifactPath.hashCode();
+            result = 31 * result + inputArtifactSnapshot.hashCode();
+            result = 31 * result + secondaryInputSnapshot.hashCode();
+            result = 31 * result + dependenciesHash.hashCode();
+            return result;
+        }
+    }
+
+    public static class MutableTransformWorkspaceIdentity implements UnitOfWork.Identity {
+        private final String inputArtifactAbsolutePath;
+        private final ValueSnapshot secondaryInputsSnapshot;
+        private final HashCode dependenciesHash;
+
+        public MutableTransformWorkspaceIdentity(String inputArtifactAbsolutePath, ValueSnapshot secondaryInputsSnapshot, HashCode dependenciesHash) {
+            this.inputArtifactAbsolutePath = inputArtifactAbsolutePath;
+            this.secondaryInputsSnapshot = secondaryInputsSnapshot;
+            this.dependenciesHash = dependenciesHash;
+        }
+
+        @Override
+        public String getUniqueId() {
+            Hasher hasher = Hashing.newHasher();
+            hasher.putString(inputArtifactAbsolutePath);
+            secondaryInputsSnapshot.appendToHasher(hasher);
+            hasher.putHash(dependenciesHash);
+            return hasher.hash().toString();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            MutableTransformWorkspaceIdentity that = (MutableTransformWorkspaceIdentity) o;
+
+            if (!secondaryInputsSnapshot.equals(that.secondaryInputsSnapshot)) {
+                return false;
+            }
+            if (!dependenciesHash.equals(that.dependenciesHash)) {
+                return false;
+            }
+            return inputArtifactAbsolutePath.equals(that.inputArtifactAbsolutePath);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = inputArtifactAbsolutePath.hashCode();
+            result = 31 * result + secondaryInputsSnapshot.hashCode();
+            result = 31 * result + dependenciesHash.hashCode();
+            return result;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformNodeDependency.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformNodeDependency.java
new file mode 100644
index 0000000..ec322a4
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformNodeDependency.java
@@ -0,0 +1,34 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.NonNullApi;
+
+import java.util.Collection;
+
+@NonNullApi
+public class DefaultTransformNodeDependency implements TransformNodeDependency {
+    private final Collection<TransformStepNode> nodes;
+
+    public DefaultTransformNodeDependency(Collection<TransformStepNode> nodes) {
+        this.nodes = nodes;
+    }
+
+    public Collection<TransformStepNode> getNodes() {
+        return nodes;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformOutputs.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformOutputs.java
index 7d1a922..aeb52be 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformOutputs.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformOutputs.java
@@ -28,7 +28,7 @@
 
 public class DefaultTransformOutputs implements TransformOutputsInternal {
 
-    private final TransformationResult.OutputTypeInferringBuilder resultBuilder;
+    private final TransformExecutionResult.OutputTypeInferringBuilder resultBuilder;
     private final Set<File> outputDirectories = new HashSet<>();
     private final Set<File> outputFiles = new HashSet<>();
     private final PathToFileResolver resolver;
@@ -39,13 +39,13 @@ public DefaultTransformOutputs(File inputArtifact, File outputDir, FileLookup fi
         this.resolver = fileLookup.getPathToFileResolver(outputDir);
         this.inputArtifact = inputArtifact;
         this.outputDir = outputDir;
-        this.resultBuilder = TransformationResult.builderFor(inputArtifact, outputDir);
+        this.resultBuilder = TransformExecutionResult.builderFor(inputArtifact, outputDir);
     }
 
     @Override
-    public TransformationResult getRegisteredOutputs() {
-        TransformationResult result = resultBuilder.build();
-        result.visitOutputs(new TransformationResult.TransformationOutputVisitor() {
+    public TransformExecutionResult getRegisteredOutputs() {
+        TransformExecutionResult result = resultBuilder.build();
+        result.visitOutputs(new TransformExecutionResult.OutputVisitor() {
             @Override
             public void visitEntireInputArtifact() {
                 validate(inputArtifact);
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformRegistrationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformRegistrationFactory.java
new file mode 100644
index 0000000..c3c0f05
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformRegistrationFactory.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2019 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.api.internal.artifacts.transform;
+
+import org.gradle.api.artifacts.transform.CacheableTransform;
+import org.gradle.api.artifacts.transform.InputArtifact;
+import org.gradle.api.artifacts.transform.InputArtifactDependencies;
+import org.gradle.api.artifacts.transform.TransformAction;
+import org.gradle.api.artifacts.transform.TransformParameters;
+import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.artifacts.TransformRegistration;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.api.internal.file.FileCollectionFactory;
+import org.gradle.api.internal.file.FileLookup;
+import org.gradle.api.internal.tasks.properties.FileParameterUtils;
+import org.gradle.internal.execution.InputFingerprinter;
+import org.gradle.internal.fingerprint.DirectorySensitivity;
+import org.gradle.internal.fingerprint.FileNormalizer;
+import org.gradle.internal.fingerprint.LineEndingSensitivity;
+import org.gradle.internal.hash.ClassLoaderHierarchyHasher;
+import org.gradle.internal.instantiation.InstantiationScheme;
+import org.gradle.internal.isolation.IsolatableFactory;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.properties.InputBehavior;
+import org.gradle.internal.properties.InputFilePropertyType;
+import org.gradle.internal.properties.PropertyValue;
+import org.gradle.internal.properties.PropertyVisitor;
+import org.gradle.internal.properties.annotations.PropertyMetadata;
+import org.gradle.internal.properties.annotations.TypeMetadata;
+import org.gradle.internal.properties.annotations.TypeMetadataStore;
+import org.gradle.internal.properties.bean.PropertyWalker;
+import org.gradle.internal.reflect.DefaultTypeValidationContext;
+import org.gradle.internal.service.ServiceLookup;
+
+import javax.annotation.Nullable;
+import java.lang.annotation.Annotation;
+
+public class DefaultTransformRegistrationFactory implements TransformRegistrationFactory {
+
+    private final BuildOperationExecutor buildOperationExecutor;
+    private final IsolatableFactory isolatableFactory;
+    private final ClassLoaderHierarchyHasher classLoaderHierarchyHasher;
+    private final TransformInvocationFactory transformInvocationFactory;
+    private final PropertyWalker parametersPropertyWalker;
+    private final ServiceLookup internalServices;
+    private final TypeMetadataStore actionMetadataStore;
+    private final FileCollectionFactory fileCollectionFactory;
+    private final FileLookup fileLookup;
+    private final InputFingerprinter inputFingerprinter;
+    private final CalculatedValueContainerFactory calculatedValueContainerFactory;
+    private final DomainObjectContext owner;
+    private final InstantiationScheme actionInstantiationScheme;
+    private final DocumentationRegistry documentationRegistry;
+
+    public DefaultTransformRegistrationFactory(
+        BuildOperationExecutor buildOperationExecutor,
+        IsolatableFactory isolatableFactory,
+        ClassLoaderHierarchyHasher classLoaderHierarchyHasher,
+        TransformInvocationFactory transformInvocationFactory,
+        FileCollectionFactory fileCollectionFactory,
+        FileLookup fileLookup,
+        InputFingerprinter inputFingerprinter,
+        CalculatedValueContainerFactory calculatedValueContainerFactory,
+        DomainObjectContext owner,
+        TransformParameterScheme parameterScheme,
+        TransformActionScheme actionScheme,
+        ServiceLookup internalServices,
+        DocumentationRegistry documentationRegistry
+    ) {
+        this.buildOperationExecutor = buildOperationExecutor;
+        this.isolatableFactory = isolatableFactory;
+        this.classLoaderHierarchyHasher = classLoaderHierarchyHasher;
+        this.transformInvocationFactory = transformInvocationFactory;
+        this.fileCollectionFactory = fileCollectionFactory;
+        this.fileLookup = fileLookup;
+        this.inputFingerprinter = inputFingerprinter;
+        this.calculatedValueContainerFactory = calculatedValueContainerFactory;
+        this.owner = owner;
+        this.actionInstantiationScheme = actionScheme.getInstantiationScheme();
+        this.actionMetadataStore = actionScheme.getInspectionScheme().getMetadataStore();
+        this.parametersPropertyWalker = parameterScheme.getInspectionScheme().getPropertyWalker();
+        this.internalServices = internalServices;
+        this.documentationRegistry = documentationRegistry;
+    }
+
+    @Override
+    public TransformRegistration create(ImmutableAttributes from, ImmutableAttributes to, Class<? extends TransformAction<?>> implementation, @Nullable TransformParameters parameterObject) {
+        TypeMetadata actionMetadata = actionMetadataStore.getTypeMetadata(implementation);
+        boolean cacheable = implementation.isAnnotationPresent(CacheableTransform.class);
+        DefaultTypeValidationContext validationContext = DefaultTypeValidationContext.withoutRootType(documentationRegistry, cacheable);
+        actionMetadata.visitValidationFailures(null, validationContext);
+
+        // Should retain this on the metadata rather than calculate on each invocation
+        FileNormalizer inputArtifactNormalizer = null;
+        FileNormalizer dependenciesNormalizer = null;
+        DirectorySensitivity artifactDirectorySensitivity = DirectorySensitivity.DEFAULT;
+        DirectorySensitivity dependenciesDirectorySensitivity = DirectorySensitivity.DEFAULT;
+        LineEndingSensitivity artifactLineEndingSensitivity = LineEndingSensitivity.DEFAULT;
+        LineEndingSensitivity dependenciesLineEndingSensitivity = LineEndingSensitivity.DEFAULT;
+        for (PropertyMetadata propertyMetadata : actionMetadata.getPropertiesMetadata()) {
+            // Should ask the annotation handler to figure this out instead
+            Class<? extends Annotation> propertyType = propertyMetadata.getPropertyType();
+            NormalizerCollectingVisitor visitor = new NormalizerCollectingVisitor();
+            if (propertyType.equals(InputArtifact.class)) {
+                actionMetadata.getAnnotationHandlerFor(propertyMetadata).visitPropertyValue(propertyMetadata.getPropertyName(), PropertyValue.ABSENT, propertyMetadata, visitor);
+                inputArtifactNormalizer = visitor.normalizer;
+                artifactDirectorySensitivity = visitor.directorySensitivity;
+                artifactLineEndingSensitivity = visitor.lineEndingSensitivity;
+                DefaultTransform.validateInputFileNormalizer(propertyMetadata.getPropertyName(), inputArtifactNormalizer, cacheable, validationContext);
+            } else if (propertyType.equals(InputArtifactDependencies.class)) {
+                actionMetadata.getAnnotationHandlerFor(propertyMetadata).visitPropertyValue(propertyMetadata.getPropertyName(), PropertyValue.ABSENT, propertyMetadata, visitor);
+                dependenciesNormalizer = visitor.normalizer;
+                dependenciesDirectorySensitivity = visitor.directorySensitivity;
+                dependenciesLineEndingSensitivity = visitor.lineEndingSensitivity;
+                DefaultTransform.validateInputFileNormalizer(propertyMetadata.getPropertyName(), dependenciesNormalizer, cacheable, validationContext);
+            }
+        }
+        DefaultTypeValidationContext.throwOnProblemsOf(implementation, validationContext.getProblems());
+        Transform transform = new DefaultTransform(
+            implementation,
+            parameterObject,
+            from,
+            to,
+            FileParameterUtils.normalizerOrDefault(inputArtifactNormalizer),
+            FileParameterUtils.normalizerOrDefault(dependenciesNormalizer),
+            cacheable,
+            artifactDirectorySensitivity,
+            dependenciesDirectorySensitivity,
+            artifactLineEndingSensitivity,
+            dependenciesLineEndingSensitivity,
+            buildOperationExecutor,
+            classLoaderHierarchyHasher,
+            isolatableFactory,
+            fileCollectionFactory,
+            fileLookup,
+            parametersPropertyWalker,
+            actionInstantiationScheme,
+            owner,
+            calculatedValueContainerFactory,
+            internalServices,
+            documentationRegistry);
+
+        return new DefaultTransformRegistration(from, to, new TransformStep(transform, transformInvocationFactory, owner, inputFingerprinter));
+    }
+
+    private static class DefaultTransformRegistration implements TransformRegistration {
+        private final ImmutableAttributes from;
+        private final ImmutableAttributes to;
+        private final TransformStep transformStep;
+
+        public DefaultTransformRegistration(ImmutableAttributes from, ImmutableAttributes to, TransformStep transformStep) {
+            this.from = from;
+            this.to = to;
+            this.transformStep = transformStep;
+        }
+
+        @Override
+        public ImmutableAttributes getFrom() {
+            return from;
+        }
+
+        @Override
+        public ImmutableAttributes getTo() {
+            return to;
+        }
+
+        @Override
+        public TransformStep getTransformStep() {
+            return transformStep;
+        }
+
+        @Override
+        public String toString() {
+            return transformStep + " transform from " + from + " to " + to;
+        }
+    }
+
+    private static class NormalizerCollectingVisitor implements PropertyVisitor {
+        private FileNormalizer normalizer;
+        private DirectorySensitivity directorySensitivity = DirectorySensitivity.DEFAULT;
+        private LineEndingSensitivity lineEndingSensitivity = LineEndingSensitivity.DEFAULT;
+
+        @Override
+        public void visitInputFileProperty(
+            String propertyName,
+            boolean optional,
+            InputBehavior behavior,
+            DirectorySensitivity directorySensitivity,
+            LineEndingSensitivity lineEndingSensitivity,
+            @Nullable FileNormalizer fileNormalizer,
+            PropertyValue value,
+            InputFilePropertyType filePropertyType
+        ) {
+            this.normalizer = fileNormalizer;
+            this.directorySensitivity = directorySensitivity;
+            this.lineEndingSensitivity = lineEndingSensitivity;
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformUpstreamDependenciesResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformUpstreamDependenciesResolver.java
index 133b14c..b642773 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformUpstreamDependenciesResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformUpstreamDependenciesResolver.java
@@ -25,7 +25,6 @@
 import org.gradle.api.artifacts.result.ResolvedDependencyResult;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.DomainObjectContext;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationIdentity;
 import org.gradle.api.internal.artifacts.configurations.ResolutionResultProvider;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
@@ -42,6 +41,7 @@
 import org.gradle.internal.model.CalculatedValueContainer;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.model.ValueCalculator;
+import org.gradle.operations.dependencies.configurations.ConfigurationIdentity;
 
 import javax.annotation.Nullable;
 import java.util.ArrayList;
@@ -55,7 +55,7 @@
 import static org.gradle.api.internal.lambdas.SerializableLambdas.spec;
 
 public class DefaultTransformUpstreamDependenciesResolver implements TransformUpstreamDependenciesResolver {
-    public static final ArtifactTransformDependencies NO_RESULT = new ArtifactTransformDependencies() {
+    public static final TransformDependencies NO_RESULT = new TransformDependencies() {
         @Override
         public Optional<FileCollection> getFiles() {
             return Optional.empty();
@@ -79,7 +79,7 @@ public void finalizeIfNotAlready() {
         }
 
         @Override
-        public Try<ArtifactTransformDependencies> computeArtifacts() {
+        public Try<TransformDependencies> computeArtifacts() {
             return Try.successful(NO_RESULT);
         }
 
@@ -118,11 +118,11 @@ private static IllegalStateException failure() {
     }
 
     @Override
-    public TransformUpstreamDependencies dependenciesFor(TransformationStep transformationStep) {
-        if (!transformationStep.requiresDependencies()) {
+    public TransformUpstreamDependencies dependenciesFor(TransformStep transformStep) {
+        if (!transformStep.requiresDependencies()) {
             return NO_DEPENDENCIES;
         }
-        return new TransformUpstreamDependenciesImpl(configurationIdentity, transformationStep, calculatedValueContainerFactory);
+        return new TransformUpstreamDependenciesImpl(configurationIdentity, transformStep, calculatedValueContainerFactory);
     }
 
     private FileCollectionInternal selectedArtifactsFor(ImmutableAttributes fromAttributes) {
@@ -187,15 +187,15 @@ private static void collectDependenciesIdentifiers(Set<ComponentIdentifier> depe
      * This is a separate node so that this work can access project state to do the resolution and to discover additional dependencies for the transform
      * during resolution of upstream dependencies. It also allows the work of resolution to be attributed separately to the work of the transform.
      */
-    public static abstract class FinalizeTransformDependencies implements ValueCalculator<ArtifactTransformDependencies> {
+    public static abstract class FinalizeTransformDependencies implements ValueCalculator<TransformDependencies> {
         public abstract FileCollection selectedArtifacts();
 
         @Override
-        public ArtifactTransformDependencies calculateValue(NodeExecutionContext context) {
+        public TransformDependencies calculateValue(NodeExecutionContext context) {
             FileCollection files = selectedArtifacts();
             // Trigger resolution, including any failures
             files.getFiles();
-            return new DefaultArtifactTransformDependencies(files);
+            return new DefaultTransformDependencies(files);
         }
     }
 
@@ -296,14 +296,14 @@ public Task getTask() {
 
     private class TransformUpstreamDependenciesImpl implements TransformUpstreamDependencies {
         private final ConfigurationIdentity configurationIdentity;
-        private final CalculatedValueContainer<ArtifactTransformDependencies, FinalizeTransformDependencies> transformDependencies;
+        private final CalculatedValueContainer<TransformDependencies, FinalizeTransformDependencies> transformDependencies;
         private final ImmutableAttributes fromAttributes;
 
-        public TransformUpstreamDependenciesImpl(ConfigurationIdentity configurationIdentity, TransformationStep transformationStep, CalculatedValueContainerFactory calculatedValueContainerFactory) {
+        public TransformUpstreamDependenciesImpl(ConfigurationIdentity configurationIdentity, TransformStep transformStep, CalculatedValueContainerFactory calculatedValueContainerFactory) {
             this.configurationIdentity = configurationIdentity;
-            this.fromAttributes = transformationStep.getFromAttributes();
+            this.fromAttributes = transformStep.getFromAttributes();
             transformDependencies = calculatedValueContainerFactory.create(Describables.of("dependencies for", componentIdentifier, fromAttributes),
-                new FinalizeTransformDependenciesFromSelectedArtifacts(transformationStep.getFromAttributes()));
+                new FinalizeTransformDependenciesFromSelectedArtifacts(transformStep.getFromAttributes()));
         }
 
         @Override
@@ -317,7 +317,7 @@ public FileCollection selectedArtifacts() {
         }
 
         @Override
-        public Try<ArtifactTransformDependencies> computeArtifacts() {
+        public Try<TransformDependencies> computeArtifacts() {
             return transformDependencies.getValue();
         }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformUpstreamDependenciesResolverFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformUpstreamDependenciesResolverFactory.java
new file mode 100644
index 0000000..70a7441
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformUpstreamDependenciesResolverFactory.java
@@ -0,0 +1,55 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ResolutionResult;
+import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.artifacts.configurations.ResolutionResultProvider;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
+import org.gradle.operations.dependencies.configurations.ConfigurationIdentity;
+
+public class DefaultTransformUpstreamDependenciesResolverFactory implements TransformUpstreamDependenciesResolverFactory {
+    public static final TransformUpstreamDependenciesResolver NO_DEPENDENCIES_RESOLVER = transformStep -> DefaultTransformUpstreamDependenciesResolver.NO_DEPENDENCIES;
+
+    private final DomainObjectContext owner;
+    private final FilteredResultFactory filteredResultFactory;
+    private final CalculatedValueContainerFactory calculatedValueContainerFactory;
+    private final ConfigurationIdentity configurationIdentity;
+    private final ResolutionResultProvider<ResolutionResult> resolutionResultProvider;
+
+    public DefaultTransformUpstreamDependenciesResolverFactory(
+        ConfigurationIdentity configurationIdentity, ResolutionResultProvider<ResolutionResult> resolutionResultProvider,
+        DomainObjectContext owner,
+        CalculatedValueContainerFactory calculatedValueContainerFactory,
+        FilteredResultFactory filteredResultFactory
+    ) {
+        this.configurationIdentity = configurationIdentity;
+        this.resolutionResultProvider = resolutionResultProvider;
+        this.owner = owner;
+        this.filteredResultFactory = filteredResultFactory;
+        this.calculatedValueContainerFactory = calculatedValueContainerFactory;
+    }
+
+    @Override
+    public TransformUpstreamDependenciesResolver create(ComponentIdentifier componentIdentifier, TransformChain transformChain) {
+        if (!transformChain.requiresDependencies()) {
+            return NO_DEPENDENCIES_RESOLVER;
+        }
+        return new DefaultTransformUpstreamDependenciesResolver(componentIdentifier, configurationIdentity, resolutionResultProvider, owner, filteredResultFactory, calculatedValueContainerFactory);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationDependency.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationDependency.java
deleted file mode 100644
index af1ad8e..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationDependency.java
+++ /dev/null
@@ -1,34 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.NonNullApi;
-
-import java.util.Collection;
-
-@NonNullApi
-public class DefaultTransformationDependency implements TransformationDependency {
-    private final Collection<TransformationNode> nodes;
-
-    public DefaultTransformationDependency(Collection<TransformationNode> nodes) {
-        this.nodes = nodes;
-    }
-
-    public Collection<TransformationNode> getNodes() {
-        return nodes;
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationNodeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationNodeFactory.java
deleted file mode 100644
index 9550097..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationNodeFactory.java
+++ /dev/null
@@ -1,63 +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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet;
-import org.gradle.internal.model.CalculatedValueContainerFactory;
-import org.gradle.internal.operations.BuildOperationExecutor;
-
-import java.util.Collection;
-
-public class DefaultTransformationNodeFactory implements TransformationNodeFactory {
-    private final BuildOperationExecutor buildOperationExecutor;
-    private final CalculatedValueContainerFactory calculatedValueContainerFactory;
-
-    public DefaultTransformationNodeFactory(BuildOperationExecutor buildOperationExecutor, CalculatedValueContainerFactory calculatedValueContainerFactory) {
-        this.buildOperationExecutor = buildOperationExecutor;
-        this.calculatedValueContainerFactory = calculatedValueContainerFactory;
-    }
-
-    @Override
-    public Collection<TransformationNode> create(
-        ResolvedArtifactSet artifactSet,
-        ComponentVariantIdentifier targetComponentVariant,
-        TransformationStep transformationStep,
-        AttributeContainer sourceAttributes,
-        TransformUpstreamDependenciesResolver dependenciesResolver
-    ) {
-        final ImmutableList.Builder<TransformationNode> builder = ImmutableList.builder();
-        artifactSet.visitTransformSources(new ResolvedArtifactSet.TransformSourceVisitor() {
-            @Override
-            public void visitArtifact(ResolvableArtifact artifact) {
-                TransformUpstreamDependencies upstreamDependencies = dependenciesResolver.dependenciesFor(transformationStep);
-                TransformationNode transformationNode = TransformationNode.initial(targetComponentVariant, sourceAttributes, transformationStep, artifact, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
-                builder.add(transformationNode);
-            }
-
-            @Override
-            public void visitTransform(TransformationNode source) {
-                TransformUpstreamDependencies upstreamDependencies = dependenciesResolver.dependenciesFor(transformationStep);
-                TransformationNode transformationNode = TransformationNode.chained(targetComponentVariant, sourceAttributes, transformationStep, source, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
-                builder.add(transformationNode);
-            }
-        });
-        return builder.build();
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationRegistrationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationRegistrationFactory.java
deleted file mode 100644
index d96947e..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformationRegistrationFactory.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright 2019 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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableMap;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.transform.CacheableTransform;
-import org.gradle.api.artifacts.transform.InputArtifact;
-import org.gradle.api.artifacts.transform.InputArtifactDependencies;
-import org.gradle.api.artifacts.transform.TransformAction;
-import org.gradle.api.artifacts.transform.TransformParameters;
-import org.gradle.api.internal.DocumentationRegistry;
-import org.gradle.api.internal.DomainObjectContext;
-import org.gradle.api.internal.artifacts.ArtifactTransformRegistration;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.api.internal.file.FileCollectionFactory;
-import org.gradle.api.internal.file.FileLookup;
-import org.gradle.api.internal.tasks.properties.FileParameterUtils;
-import org.gradle.internal.exceptions.DefaultMultiCauseException;
-import org.gradle.internal.execution.InputFingerprinter;
-import org.gradle.internal.fingerprint.DirectorySensitivity;
-import org.gradle.internal.fingerprint.FileNormalizer;
-import org.gradle.internal.fingerprint.LineEndingSensitivity;
-import org.gradle.internal.hash.ClassLoaderHierarchyHasher;
-import org.gradle.internal.instantiation.InstantiationScheme;
-import org.gradle.internal.isolation.IsolatableFactory;
-import org.gradle.internal.model.CalculatedValueContainerFactory;
-import org.gradle.internal.operations.BuildOperationExecutor;
-import org.gradle.internal.properties.InputBehavior;
-import org.gradle.internal.properties.InputFilePropertyType;
-import org.gradle.internal.properties.PropertyValue;
-import org.gradle.internal.properties.PropertyVisitor;
-import org.gradle.internal.properties.annotations.PropertyMetadata;
-import org.gradle.internal.properties.annotations.TypeMetadata;
-import org.gradle.internal.properties.annotations.TypeMetadataStore;
-import org.gradle.internal.properties.bean.PropertyWalker;
-import org.gradle.internal.reflect.DefaultTypeValidationContext;
-import org.gradle.internal.reflect.validation.Severity;
-import org.gradle.internal.service.ServiceLookup;
-import org.gradle.model.internal.type.ModelType;
-
-import javax.annotation.Nullable;
-import java.lang.annotation.Annotation;
-import java.util.stream.Collectors;
-
-public class DefaultTransformationRegistrationFactory implements TransformationRegistrationFactory {
-
-    private final BuildOperationExecutor buildOperationExecutor;
-    private final IsolatableFactory isolatableFactory;
-    private final ClassLoaderHierarchyHasher classLoaderHierarchyHasher;
-    private final TransformerInvocationFactory transformerInvocationFactory;
-    private final PropertyWalker parametersPropertyWalker;
-    private final ServiceLookup internalServices;
-    private final TypeMetadataStore actionMetadataStore;
-    private final FileCollectionFactory fileCollectionFactory;
-    private final FileLookup fileLookup;
-    private final InputFingerprinter inputFingerprinter;
-    private final CalculatedValueContainerFactory calculatedValueContainerFactory;
-    private final DomainObjectContext owner;
-    private final InstantiationScheme actionInstantiationScheme;
-    private final DocumentationRegistry documentationRegistry;
-
-    public DefaultTransformationRegistrationFactory(
-        BuildOperationExecutor buildOperationExecutor,
-        IsolatableFactory isolatableFactory,
-        ClassLoaderHierarchyHasher classLoaderHierarchyHasher,
-        TransformerInvocationFactory transformerInvocationFactory,
-        FileCollectionFactory fileCollectionFactory,
-        FileLookup fileLookup,
-        InputFingerprinter inputFingerprinter,
-        CalculatedValueContainerFactory calculatedValueContainerFactory,
-        DomainObjectContext owner,
-        ArtifactTransformParameterScheme parameterScheme,
-        ArtifactTransformActionScheme actionScheme,
-        ServiceLookup internalServices,
-        DocumentationRegistry documentationRegistry
-    ) {
-        this.buildOperationExecutor = buildOperationExecutor;
-        this.isolatableFactory = isolatableFactory;
-        this.classLoaderHierarchyHasher = classLoaderHierarchyHasher;
-        this.transformerInvocationFactory = transformerInvocationFactory;
-        this.fileCollectionFactory = fileCollectionFactory;
-        this.fileLookup = fileLookup;
-        this.inputFingerprinter = inputFingerprinter;
-        this.calculatedValueContainerFactory = calculatedValueContainerFactory;
-        this.owner = owner;
-        this.actionInstantiationScheme = actionScheme.getInstantiationScheme();
-        this.actionMetadataStore = actionScheme.getInspectionScheme().getMetadataStore();
-        this.parametersPropertyWalker = parameterScheme.getInspectionScheme().getPropertyWalker();
-        this.internalServices = internalServices;
-        this.documentationRegistry = documentationRegistry;
-    }
-
-    @Override
-    public ArtifactTransformRegistration create(ImmutableAttributes from, ImmutableAttributes to, Class<? extends TransformAction<?>> implementation, @Nullable TransformParameters parameterObject) {
-        TypeMetadata actionMetadata = actionMetadataStore.getTypeMetadata(implementation);
-        boolean cacheable = implementation.isAnnotationPresent(CacheableTransform.class);
-        DefaultTypeValidationContext validationContext = DefaultTypeValidationContext.withoutRootType(documentationRegistry, cacheable);
-        actionMetadata.visitValidationFailures(null, validationContext);
-
-        // Should retain this on the metadata rather than calculate on each invocation
-        FileNormalizer inputArtifactNormalizer = null;
-        FileNormalizer dependenciesNormalizer = null;
-        DirectorySensitivity artifactDirectorySensitivity = DirectorySensitivity.DEFAULT;
-        DirectorySensitivity dependenciesDirectorySensitivity = DirectorySensitivity.DEFAULT;
-        LineEndingSensitivity artifactLineEndingSensitivity = LineEndingSensitivity.DEFAULT;
-        LineEndingSensitivity dependenciesLineEndingSensitivity = LineEndingSensitivity.DEFAULT;
-        for (PropertyMetadata propertyMetadata : actionMetadata.getPropertiesMetadata()) {
-            // Should ask the annotation handler to figure this out instead
-            Class<? extends Annotation> propertyType = propertyMetadata.getPropertyType();
-            NormalizerCollectingVisitor visitor = new NormalizerCollectingVisitor();
-            if (propertyType.equals(InputArtifact.class)) {
-                actionMetadata.getAnnotationHandlerFor(propertyMetadata).visitPropertyValue(propertyMetadata.getPropertyName(), PropertyValue.ABSENT, propertyMetadata, visitor);
-                inputArtifactNormalizer = visitor.normalizer;
-                artifactDirectorySensitivity = visitor.directorySensitivity;
-                artifactLineEndingSensitivity = visitor.lineEndingSensitivity;
-                DefaultTransformer.validateInputFileNormalizer(propertyMetadata.getPropertyName(), inputArtifactNormalizer, cacheable, validationContext);
-            } else if (propertyType.equals(InputArtifactDependencies.class)) {
-                actionMetadata.getAnnotationHandlerFor(propertyMetadata).visitPropertyValue(propertyMetadata.getPropertyName(), PropertyValue.ABSENT, propertyMetadata, visitor);
-                dependenciesNormalizer = visitor.normalizer;
-                dependenciesDirectorySensitivity = visitor.directorySensitivity;
-                dependenciesLineEndingSensitivity = visitor.lineEndingSensitivity;
-                DefaultTransformer.validateInputFileNormalizer(propertyMetadata.getPropertyName(), dependenciesNormalizer, cacheable, validationContext);
-            }
-        }
-        ImmutableMap<String, Severity> validationMessages = validationContext.getProblems();
-        if (!validationMessages.isEmpty()) {
-            String formatString = validationMessages.size() == 1
-                ? "A problem was found with the configuration of %s."
-                : "Some problems were found with the configuration of %s.";
-            throw new DefaultMultiCauseException(
-                String.format(formatString, ModelType.of(implementation).getDisplayName()),
-                validationMessages.keySet().stream()
-                    .sorted()
-                    .map(InvalidUserDataException::new)
-                    .collect(Collectors.toList())
-            );
-        }
-        Transformer transformer = new DefaultTransformer(
-            implementation,
-            parameterObject,
-            from,
-            to,
-            FileParameterUtils.normalizerOrDefault(inputArtifactNormalizer),
-            FileParameterUtils.normalizerOrDefault(dependenciesNormalizer),
-            cacheable,
-            artifactDirectorySensitivity,
-            dependenciesDirectorySensitivity,
-            artifactLineEndingSensitivity,
-            dependenciesLineEndingSensitivity,
-            buildOperationExecutor,
-            classLoaderHierarchyHasher,
-            isolatableFactory,
-            fileCollectionFactory,
-            fileLookup,
-            parametersPropertyWalker,
-            actionInstantiationScheme,
-            owner,
-            calculatedValueContainerFactory,
-            internalServices,
-            documentationRegistry);
-
-        return new DefaultArtifactTransformRegistration(from, to, new TransformationStep(transformer, transformerInvocationFactory, owner, inputFingerprinter));
-    }
-
-    private static class DefaultArtifactTransformRegistration implements ArtifactTransformRegistration {
-        private final ImmutableAttributes from;
-        private final ImmutableAttributes to;
-        private final TransformationStep transformationStep;
-
-        public DefaultArtifactTransformRegistration(ImmutableAttributes from, ImmutableAttributes to, TransformationStep transformationStep) {
-            this.from = from;
-            this.to = to;
-            this.transformationStep = transformationStep;
-        }
-
-        @Override
-        public ImmutableAttributes getFrom() {
-            return from;
-        }
-
-        @Override
-        public ImmutableAttributes getTo() {
-            return to;
-        }
-
-        @Override
-        public TransformationStep getTransformationStep() {
-            return transformationStep;
-        }
-
-        @Override
-        public String toString() {
-            return transformationStep + " transform from " + from + " to " + to;
-        }
-    }
-
-    private static class NormalizerCollectingVisitor implements PropertyVisitor {
-        private FileNormalizer normalizer;
-        private DirectorySensitivity directorySensitivity = DirectorySensitivity.DEFAULT;
-        private LineEndingSensitivity lineEndingSensitivity = LineEndingSensitivity.DEFAULT;
-
-        @Override
-        public void visitInputFileProperty(
-                String propertyName,
-                boolean optional,
-                InputBehavior behavior,
-                DirectorySensitivity directorySensitivity,
-                LineEndingSensitivity lineEndingSensitivity,
-                @Nullable FileNormalizer fileNormalizer,
-                PropertyValue value,
-                InputFilePropertyType filePropertyType
-        ) {
-            this.normalizer = fileNormalizer;
-            this.directorySensitivity = directorySensitivity;
-            this.lineEndingSensitivity = lineEndingSensitivity;
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformedVariantFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformedVariantFactory.java
index a9523a6..61ac45a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformedVariantFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformedVariantFactory.java
@@ -16,8 +16,10 @@
 
 package org.gradle.api.internal.artifacts.transform;
 
+import com.google.common.collect.ImmutableList;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
@@ -26,42 +28,45 @@
 import org.gradle.internal.operations.BuildOperationExecutor;
 
 import javax.annotation.concurrent.ThreadSafe;
+import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 @ThreadSafe
 public class DefaultTransformedVariantFactory implements TransformedVariantFactory {
-    private final TransformationNodeFactory transformationNodeFactory;
+    private final BuildOperationExecutor buildOperationExecutor;
     private final CalculatedValueContainerFactory calculatedValueContainerFactory;
+    private final TransformStepNodeFactory transformStepNodeFactory;
     private final ConcurrentMap<VariantKey, ResolvedArtifactSet> variants = new ConcurrentHashMap<>();
     private final Factory externalFactory = this::doCreateExternal;
     private final Factory projectFactory = this::doCreateProject;
 
-    public DefaultTransformedVariantFactory(BuildOperationExecutor buildOperationExecutor, CalculatedValueContainerFactory calculatedValueContainerFactory) {
+    public DefaultTransformedVariantFactory(BuildOperationExecutor buildOperationExecutor, CalculatedValueContainerFactory calculatedValueContainerFactory, TransformStepNodeFactory transformStepNodeFactory) {
+        this.buildOperationExecutor = buildOperationExecutor;
         this.calculatedValueContainerFactory = calculatedValueContainerFactory;
-        this.transformationNodeFactory = new DefaultTransformationNodeFactory(buildOperationExecutor, calculatedValueContainerFactory);
+        this.transformStepNodeFactory = transformStepNodeFactory;
     }
 
     @Override
-    public ResolvedArtifactSet transformedExternalArtifacts(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory) {
+    public ResolvedArtifactSet transformedExternalArtifacts(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory) {
         return locateOrCreate(externalFactory, componentIdentifier, sourceVariant, variantDefinition, dependenciesResolverFactory);
     }
 
     @Override
-    public ResolvedArtifactSet transformedProjectArtifacts(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory) {
+    public ResolvedArtifactSet transformedProjectArtifacts(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory) {
         return locateOrCreate(projectFactory, componentIdentifier, sourceVariant, variantDefinition, dependenciesResolverFactory);
     }
 
-    private ResolvedArtifactSet locateOrCreate(Factory factory, ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory) {
+    private ResolvedArtifactSet locateOrCreate(Factory factory, ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory) {
         ImmutableAttributes target = variantDefinition.getTargetAttributes();
-        Transformation transformation = variantDefinition.getTransformation();
+        TransformChain transformChain = variantDefinition.getTransformChain();
         VariantResolveMetadata.Identifier identifier = sourceVariant.getIdentifier();
         if (identifier == null) {
             // An ad hoc variant, do not cache the result
             return factory.create(componentIdentifier, sourceVariant, variantDefinition, dependenciesResolverFactory);
         }
         VariantKey variantKey;
-        if (transformation.requiresDependencies()) {
+        if (transformChain.requiresDependencies()) {
             variantKey = new VariantWithUpstreamDependenciesKey(identifier, target, dependenciesResolverFactory);
         } else {
             variantKey = new VariantKey(identifier, target);
@@ -79,11 +84,11 @@ private ResolvedArtifactSet locateOrCreate(Factory factory, ComponentIdentifier
         return result;
     }
 
-    private TransformedExternalArtifactSet doCreateExternal(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory) {
-        return new TransformedExternalArtifactSet(componentIdentifier, sourceVariant.getArtifacts(), variantDefinition.getTargetAttributes(), sourceVariant.getCapabilities().getCapabilities(), variantDefinition.getTransformation(), dependenciesResolverFactory, calculatedValueContainerFactory);
+    private TransformedExternalArtifactSet doCreateExternal(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory) {
+        return new TransformedExternalArtifactSet(componentIdentifier, sourceVariant.getArtifacts(), variantDefinition.getTargetAttributes(), sourceVariant.getCapabilities().getCapabilities(), variantDefinition.getTransformChain(), dependenciesResolverFactory, calculatedValueContainerFactory);
     }
 
-    private TransformedProjectArtifactSet doCreateProject(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory) {
+    private TransformedProjectArtifactSet doCreateProject(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory) {
         AttributeContainer sourceAttributes;
         ResolvedArtifactSet sourceArtifacts;
         VariantDefinition previous = variantDefinition.getPrevious();
@@ -95,11 +100,41 @@ private TransformedProjectArtifactSet doCreateProject(ComponentIdentifier compon
             sourceArtifacts = sourceVariant.getArtifacts();
         }
         ComponentVariantIdentifier targetComponentVariant = new ComponentVariantIdentifier(componentIdentifier, variantDefinition.getTargetAttributes(), sourceVariant.getCapabilities().getCapabilities());
-        return new TransformedProjectArtifactSet(targetComponentVariant, sourceArtifacts, sourceAttributes, variantDefinition, dependenciesResolverFactory, transformationNodeFactory);
+        List<TransformStepNode> transformStepNodes = createTransformStepNodes(sourceArtifacts, sourceAttributes, targetComponentVariant, variantDefinition, dependenciesResolverFactory);
+        return new TransformedProjectArtifactSet(targetComponentVariant, transformStepNodes);
+    }
+
+    private List<TransformStepNode> createTransformStepNodes(
+        ResolvedArtifactSet sourceArtifacts,
+        AttributeContainer sourceAttributes,
+        ComponentVariantIdentifier targetComponentVariant,
+        VariantDefinition variantDefinition,
+        TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory
+    ) {
+        TransformUpstreamDependenciesResolver dependenciesResolver = dependenciesResolverFactory.create(targetComponentVariant.getComponentId(), variantDefinition.getTransformChain());
+        TransformStep transformStep = variantDefinition.getTransformStep();
+
+        ImmutableList.Builder<TransformStepNode> builder = ImmutableList.builder();
+        sourceArtifacts.visitTransformSources(new ResolvedArtifactSet.TransformSourceVisitor() {
+            @Override
+            public void visitArtifact(ResolvableArtifact artifact) {
+                TransformUpstreamDependencies upstreamDependencies = dependenciesResolver.dependenciesFor(transformStep);
+                TransformStepNode transformStepNode = transformStepNodeFactory.createInitial(targetComponentVariant, sourceAttributes, transformStep, artifact, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
+                builder.add(transformStepNode);
+            }
+
+            @Override
+            public void visitTransform(TransformStepNode source) {
+                TransformUpstreamDependencies upstreamDependencies = dependenciesResolver.dependenciesFor(transformStep);
+                TransformStepNode transformStepNode = transformStepNodeFactory.createChained(targetComponentVariant, sourceAttributes, transformStep, source, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
+                builder.add(transformStepNode);
+            }
+        });
+        return builder.build();
     }
 
     private interface Factory {
-        ResolvedArtifactSet create(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory);
+        ResolvedArtifactSet create(ComponentIdentifier componentIdentifier, ResolvedVariant sourceVariant, VariantDefinition variantDefinition, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory);
     }
 
     private static class VariantKey {
@@ -130,16 +165,16 @@ public boolean equals(Object obj) {
     }
 
     private static class VariantWithUpstreamDependenciesKey extends VariantKey {
-        private final ExtraExecutionGraphDependenciesResolverFactory dependencies;
+        private final TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory;
 
-        public VariantWithUpstreamDependenciesKey(VariantResolveMetadata.Identifier sourceVariant, ImmutableAttributes target, ExtraExecutionGraphDependenciesResolverFactory dependencies) {
+        public VariantWithUpstreamDependenciesKey(VariantResolveMetadata.Identifier sourceVariant, ImmutableAttributes target, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory) {
             super(sourceVariant, target);
-            this.dependencies = dependencies;
+            this.dependenciesResolverFactory = dependenciesResolverFactory;
         }
 
         @Override
         public int hashCode() {
-            return super.hashCode() ^ dependencies.hashCode();
+            return super.hashCode() ^ dependenciesResolverFactory.hashCode();
         }
 
         @Override
@@ -148,7 +183,7 @@ public boolean equals(Object obj) {
                 return false;
             }
             VariantWithUpstreamDependenciesKey other = (VariantWithUpstreamDependenciesKey) obj;
-            return dependencies == other.dependencies;
+            return dependenciesResolverFactory == other.dependenciesResolverFactory;
         }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformer.java
deleted file mode 100644
index 8caa769..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformer.java
+++ /dev/null
@@ -1,723 +0,0 @@
-/*
- * Copyright 2019 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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedMap;
-import com.google.common.reflect.TypeToken;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.artifacts.transform.InputArtifact;
-import org.gradle.api.artifacts.transform.InputArtifactDependencies;
-import org.gradle.api.artifacts.transform.TransformAction;
-import org.gradle.api.artifacts.transform.TransformParameters;
-import org.gradle.api.artifacts.transform.VariantTransformConfigurationException;
-import org.gradle.api.file.FileSystemLocation;
-import org.gradle.api.internal.DocumentationRegistry;
-import org.gradle.api.internal.DomainObjectContext;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.api.internal.file.FileCollectionFactory;
-import org.gradle.api.internal.file.FileLookup;
-import org.gradle.api.internal.plugins.DslObject;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.NodeExecutionContext;
-import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
-import org.gradle.api.internal.tasks.properties.FileParameterUtils;
-import org.gradle.api.internal.tasks.properties.InputParameterUtils;
-import org.gradle.api.provider.Provider;
-import org.gradle.api.reflect.InjectionPointQualifier;
-import org.gradle.internal.Describables;
-import org.gradle.internal.exceptions.DefaultMultiCauseException;
-import org.gradle.internal.execution.InputFingerprinter;
-import org.gradle.internal.execution.UnitOfWork.InputFileValueSupplier;
-import org.gradle.internal.execution.model.InputNormalizer;
-import org.gradle.internal.fingerprint.CurrentFileCollectionFingerprint;
-import org.gradle.internal.fingerprint.DirectorySensitivity;
-import org.gradle.internal.fingerprint.FileNormalizer;
-import org.gradle.internal.fingerprint.LineEndingSensitivity;
-import org.gradle.internal.hash.ClassLoaderHierarchyHasher;
-import org.gradle.internal.hash.HashCode;
-import org.gradle.internal.hash.Hasher;
-import org.gradle.internal.hash.Hashing;
-import org.gradle.internal.instantiation.InstanceFactory;
-import org.gradle.internal.instantiation.InstantiationScheme;
-import org.gradle.internal.isolated.IsolationScheme;
-import org.gradle.internal.isolation.Isolatable;
-import org.gradle.internal.isolation.IsolatableFactory;
-import org.gradle.internal.logging.text.TreeFormatter;
-import org.gradle.internal.model.CalculatedValueContainer;
-import org.gradle.internal.model.CalculatedValueContainerFactory;
-import org.gradle.internal.model.ModelContainer;
-import org.gradle.internal.model.ValueCalculator;
-import org.gradle.internal.operations.BuildOperationContext;
-import org.gradle.internal.operations.BuildOperationDescriptor;
-import org.gradle.internal.operations.BuildOperationExecutor;
-import org.gradle.internal.operations.BuildOperationType;
-import org.gradle.internal.operations.RunnableBuildOperation;
-import org.gradle.internal.properties.InputBehavior;
-import org.gradle.internal.properties.InputFilePropertyType;
-import org.gradle.internal.properties.OutputFilePropertyType;
-import org.gradle.internal.properties.PropertyValue;
-import org.gradle.internal.properties.PropertyVisitor;
-import org.gradle.internal.properties.bean.PropertyWalker;
-import org.gradle.internal.reflect.DefaultTypeValidationContext;
-import org.gradle.internal.reflect.problems.ValidationProblemId;
-import org.gradle.internal.reflect.validation.Severity;
-import org.gradle.internal.reflect.validation.TypeValidationContext;
-import org.gradle.internal.service.ServiceLookup;
-import org.gradle.internal.service.ServiceLookupException;
-import org.gradle.internal.service.UnknownServiceException;
-import org.gradle.internal.snapshot.ValueSnapshot;
-import org.gradle.model.internal.type.ModelType;
-import org.gradle.work.InputChanges;
-
-import javax.annotation.Nullable;
-import java.io.File;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.Map;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import static org.gradle.api.internal.tasks.properties.AbstractValidatingProperty.reportValueNotSet;
-
-public class DefaultTransformer implements Transformer {
-
-    private final Class<? extends TransformAction<?>> implementationClass;
-    private final ImmutableAttributes fromAttributes;
-    private final ImmutableAttributes toAttributes;
-    private final FileNormalizer fileNormalizer;
-    private final FileNormalizer dependenciesNormalizer;
-    private final FileLookup fileLookup;
-    private final ServiceLookup internalServices;
-    private final boolean requiresDependencies;
-    private final boolean requiresInputChanges;
-    private final InstanceFactory<? extends TransformAction<?>> instanceFactory;
-    private final boolean cacheable;
-    private final CalculatedValueContainer<IsolatedParameters, IsolateTransformerParameters> isolatedParameters;
-    private final DirectorySensitivity artifactDirectorySensitivity;
-    private final DirectorySensitivity dependenciesDirectorySensitivity;
-    private final LineEndingSensitivity artifactLineEndingSensitivity;
-    private final LineEndingSensitivity dependenciesLineEndingSensitivity;
-
-    public DefaultTransformer(
-        Class<? extends TransformAction<?>> implementationClass,
-        @Nullable TransformParameters parameterObject,
-        ImmutableAttributes fromAttributes,
-        ImmutableAttributes toAttributes,
-        FileNormalizer inputArtifactNormalizer,
-        FileNormalizer dependenciesNormalizer,
-        boolean cacheable,
-        DirectorySensitivity artifactDirectorySensitivity,
-        DirectorySensitivity dependenciesDirectorySensitivity,
-        LineEndingSensitivity artifactLineEndingSensitivity,
-        LineEndingSensitivity dependenciesLineEndingSensitivity,
-        BuildOperationExecutor buildOperationExecutor,
-        ClassLoaderHierarchyHasher classLoaderHierarchyHasher,
-        IsolatableFactory isolatableFactory,
-        FileCollectionFactory fileCollectionFactory,
-        FileLookup fileLookup,
-        PropertyWalker parameterPropertyWalker,
-        InstantiationScheme actionInstantiationScheme,
-        DomainObjectContext owner,
-        CalculatedValueContainerFactory calculatedValueContainerFactory,
-        ServiceLookup internalServices,
-        DocumentationRegistry documentationRegistry
-    ) {
-        this.implementationClass = implementationClass;
-        this.fromAttributes = fromAttributes;
-        this.toAttributes = toAttributes;
-        this.fileNormalizer = inputArtifactNormalizer;
-        this.dependenciesNormalizer = dependenciesNormalizer;
-        this.fileLookup = fileLookup;
-        this.internalServices = internalServices;
-        this.instanceFactory = actionInstantiationScheme.forType(implementationClass);
-        this.requiresDependencies = instanceFactory.serviceInjectionTriggeredByAnnotation(InputArtifactDependencies.class);
-        this.requiresInputChanges = instanceFactory.requiresService(InputChanges.class);
-        this.cacheable = cacheable;
-        this.artifactDirectorySensitivity = artifactDirectorySensitivity;
-        this.dependenciesDirectorySensitivity = dependenciesDirectorySensitivity;
-        this.artifactLineEndingSensitivity = artifactLineEndingSensitivity;
-        this.dependenciesLineEndingSensitivity = dependenciesLineEndingSensitivity;
-        this.isolatedParameters = calculatedValueContainerFactory.create(Describables.of("parameters of", this),
-            new IsolateTransformerParameters(parameterObject, implementationClass, cacheable, owner, parameterPropertyWalker, isolatableFactory, buildOperationExecutor, classLoaderHierarchyHasher,
-                fileCollectionFactory, documentationRegistry));
-    }
-
-    /**
-     * Used to recreate a transformer from the configuration cache.
-     */
-    public DefaultTransformer(
-        Class<? extends TransformAction<?>> implementationClass,
-        CalculatedValueContainer<IsolatedParameters, IsolateTransformerParameters> isolatedParameters,
-        ImmutableAttributes fromAttributes,
-        ImmutableAttributes toAttributes,
-        FileNormalizer inputArtifactNormalizer,
-        FileNormalizer dependenciesNormalizer,
-        boolean cacheable,
-        FileLookup fileLookup,
-        InstantiationScheme actionInstantiationScheme,
-        ServiceLookup internalServices,
-        DirectorySensitivity artifactDirectorySensitivity,
-        DirectorySensitivity dependenciesDirectorySensitivity,
-        LineEndingSensitivity artifactLineEndingSensitivity,
-        LineEndingSensitivity dependenciesLineEndingSensitivity
-    ) {
-        this.implementationClass = implementationClass;
-        this.fromAttributes = fromAttributes;
-        this.toAttributes = toAttributes;
-        this.fileNormalizer = inputArtifactNormalizer;
-        this.dependenciesNormalizer = dependenciesNormalizer;
-        this.fileLookup = fileLookup;
-        this.internalServices = internalServices;
-        this.instanceFactory = actionInstantiationScheme.forType(implementationClass);
-        this.requiresDependencies = instanceFactory.serviceInjectionTriggeredByAnnotation(InputArtifactDependencies.class);
-        this.requiresInputChanges = instanceFactory.requiresService(InputChanges.class);
-        this.cacheable = cacheable;
-        this.isolatedParameters = isolatedParameters;
-        this.artifactDirectorySensitivity = artifactDirectorySensitivity;
-        this.dependenciesDirectorySensitivity = dependenciesDirectorySensitivity;
-        this.artifactLineEndingSensitivity = artifactLineEndingSensitivity;
-        this.dependenciesLineEndingSensitivity = dependenciesLineEndingSensitivity;
-    }
-
-    public static void validateInputFileNormalizer(String propertyName, @Nullable FileNormalizer normalizer, boolean cacheable, TypeValidationContext validationContext) {
-        if (cacheable) {
-            if (normalizer == InputNormalizer.ABSOLUTE_PATH) {
-                validationContext.visitPropertyProblem(problem ->
-                    problem.withId(ValidationProblemId.CACHEABLE_TRANSFORM_CANT_USE_ABSOLUTE_SENSITIVITY)
-                        .reportAs(Severity.ERROR)
-                        .forProperty(propertyName)
-                        .withDescription("is declared to be sensitive to absolute paths")
-                        .happensBecause("This is not allowed for cacheable transforms")
-                        .withLongDescription("Absolute path sensitivity does not allow sharing the transform result between different machines, although that is the goal of cacheable transforms.")
-                        .addPossibleSolution("Use a different normalization strategy via @PathSensitive, @Classpath or @CompileClasspath")
-                        .documentedAt("validation_problems", "cacheable_transform_cant_use_absolute_sensitivity"));
-            }
-        }
-    }
-
-    @Override
-    public FileNormalizer getInputArtifactNormalizer() {
-        return fileNormalizer;
-    }
-
-    @Override
-    public FileNormalizer getInputArtifactDependenciesNormalizer() {
-        return dependenciesNormalizer;
-    }
-
-    @Override
-    public boolean isIsolated() {
-        return isolatedParameters.getOrNull() != null;
-    }
-
-    @Override
-    public boolean requiresDependencies() {
-        return requiresDependencies;
-    }
-
-    @Override
-    public boolean requiresInputChanges() {
-        return requiresInputChanges;
-    }
-
-    @Override
-    public boolean isCacheable() {
-        return cacheable;
-    }
-
-    @Override
-    public DirectorySensitivity getInputArtifactDirectorySensitivity() {
-        return artifactDirectorySensitivity;
-    }
-
-    @Override
-    public DirectorySensitivity getInputArtifactDependenciesDirectorySensitivity() {
-        return dependenciesDirectorySensitivity;
-    }
-
-    @Override
-    public LineEndingSensitivity getInputArtifactLineEndingNormalization() {
-        return artifactLineEndingSensitivity;
-    }
-
-    @Override
-    public LineEndingSensitivity getInputArtifactDependenciesLineEndingNormalization() {
-        return dependenciesLineEndingSensitivity;
-    }
-
-    @Override
-    public HashCode getSecondaryInputHash() {
-        return isolatedParameters.get().getSecondaryInputsHash();
-    }
-
-    @Override
-    public TransformationResult transform(Provider<FileSystemLocation> inputArtifactProvider, File outputDir, ArtifactTransformDependencies dependencies, @Nullable InputChanges inputChanges) {
-        TransformAction<?> transformAction = newTransformAction(inputArtifactProvider, dependencies, inputChanges);
-        DefaultTransformOutputs transformOutputs = new DefaultTransformOutputs(inputArtifactProvider.get().getAsFile(), outputDir, fileLookup);
-        transformAction.transform(transformOutputs);
-        return transformOutputs.getRegisteredOutputs();
-    }
-
-    @Override
-    public void visitDependencies(TaskDependencyResolveContext context) {
-        context.add(isolatedParameters);
-    }
-
-    @Override
-    public void isolateParametersIfNotAlready() {
-        isolatedParameters.finalizeIfNotAlready();
-    }
-
-    private static void fingerprintParameters(
-        DocumentationRegistry documentationRegistry,
-        InputFingerprinter inputFingerprinter,
-        FileCollectionFactory fileCollectionFactory,
-        PropertyWalker propertyWalker,
-        Hasher hasher,
-        Object parameterObject,
-        boolean cacheable
-    ) {
-        DefaultTypeValidationContext validationContext = DefaultTypeValidationContext.withoutRootType(documentationRegistry, cacheable);
-        InputFingerprinter.Result result = inputFingerprinter.fingerprintInputProperties(
-            ImmutableSortedMap.of(),
-            ImmutableSortedMap.of(),
-            ImmutableSortedMap.of(),
-            ImmutableSortedMap.of(),
-            visitor -> propertyWalker.visitProperties(parameterObject, validationContext, new PropertyVisitor() {
-                @Override
-                public void visitInputProperty(
-                    String propertyName,
-                    PropertyValue value,
-                    boolean optional
-                ) {
-                    try {
-                        Object preparedValue = InputParameterUtils.prepareInputParameterValue(value);
-
-                        if (preparedValue == null && !optional) {
-                            reportValueNotSet(propertyName, validationContext);
-                        }
-                        visitor.visitInputProperty(propertyName, () -> preparedValue);
-                    } catch (Throwable e) {
-                        throw new InvalidUserDataException(String.format(
-                            "Error while evaluating property '%s' of %s",
-                            propertyName,
-                            getParameterObjectDisplayName(parameterObject)
-                        ), e);
-                    }
-                }
-
-                @Override
-                public void visitInputFileProperty(
-                    String propertyName,
-                    boolean optional,
-                    InputBehavior behavior,
-                    DirectorySensitivity directorySensitivity,
-                    LineEndingSensitivity lineEndingNormalization,
-                    @Nullable FileNormalizer normalizer,
-                    PropertyValue value,
-                    InputFilePropertyType filePropertyType
-                ) {
-                    validateInputFileNormalizer(propertyName, normalizer, cacheable, validationContext);
-                    visitor.visitInputFileProperty(
-                        propertyName,
-                        behavior,
-                        new InputFileValueSupplier(
-                            value,
-                            normalizer == null ? InputNormalizer.ABSOLUTE_PATH : normalizer,
-                            directorySensitivity,
-                            lineEndingNormalization,
-                            () -> FileParameterUtils.resolveInputFileValue(fileCollectionFactory, filePropertyType, value)));
-                }
-
-                @Override
-                public void visitOutputFileProperty(
-                    String propertyName,
-                    boolean optional,
-                    PropertyValue value,
-                    OutputFilePropertyType filePropertyType
-                ) {
-                    validationContext.visitPropertyProblem(problem ->
-                        problem.withId(ValidationProblemId.ARTIFACT_TRANSFORM_SHOULD_NOT_DECLARE_OUTPUT)
-                            .reportAs(Severity.ERROR)
-                            .forProperty(propertyName)
-                            .withDescription("declares an output")
-                            .happensBecause("is annotated with an output annotation")
-                            .addPossibleSolution("Remove the output property and use the TransformOutputs parameter from transform(TransformOutputs) instead")
-                            .documentedAt("validation_problems", "artifact_transform_should_not_declare_output")
-                    );
-                }
-            })
-        );
-
-        ImmutableMap<String, Severity> validationMessages = validationContext.getProblems();
-        if (!validationMessages.isEmpty()) {
-            throw new DefaultMultiCauseException(
-                String.format(validationMessages.size() == 1
-                        ? "A problem was found with the configuration of the artifact transform parameter %s."
-                        : "Some problems were found with the configuration of the artifact transform parameter %s.",
-                    getParameterObjectDisplayName(parameterObject)),
-                validationMessages.keySet().stream()
-                    .sorted()
-                    .map(InvalidUserDataException::new)
-                    .collect(Collectors.toList())
-            );
-        }
-
-        for (Map.Entry<String, ValueSnapshot> entry : result.getValueSnapshots().entrySet()) {
-            hasher.putString(entry.getKey());
-            entry.getValue().appendToHasher(hasher);
-        }
-        for (Map.Entry<String, CurrentFileCollectionFingerprint> entry : result.getFileFingerprints().entrySet()) {
-            hasher.putString(entry.getKey());
-            hasher.putHash(entry.getValue().getHash());
-        }
-    }
-
-    private static String getParameterObjectDisplayName(Object parameterObject) {
-        return ModelType.of(new DslObject(parameterObject).getDeclaredType()).getDisplayName();
-    }
-
-    private TransformAction<?> newTransformAction(Provider<FileSystemLocation> inputArtifactProvider, ArtifactTransformDependencies artifactTransformDependencies, @Nullable InputChanges inputChanges) {
-        TransformParameters parameters = isolatedParameters.get().getIsolatedParameterObject().isolate();
-        ServiceLookup services = new IsolationScheme<>(TransformAction.class, TransformParameters.class, TransformParameters.None.class).servicesForImplementation(parameters, internalServices);
-        services = new TransformServiceLookup(inputArtifactProvider, requiresDependencies ? artifactTransformDependencies : null, inputChanges, services);
-        return instanceFactory.newInstance(services);
-    }
-
-    public CalculatedValueContainer<IsolatedParameters, IsolateTransformerParameters> getIsolatedParameters() {
-        return isolatedParameters;
-    }
-
-    @Override
-    public ImmutableAttributes getFromAttributes() {
-        return fromAttributes;
-    }
-
-    @Override
-    public ImmutableAttributes getToAttributes() {
-        return toAttributes;
-    }
-
-    @Override
-    public Class<? extends TransformAction<?>> getImplementationClass() {
-        return implementationClass;
-    }
-
-    @Override
-    public String getDisplayName() {
-        return implementationClass.getSimpleName();
-    }
-
-    private static class TransformServiceLookup implements ServiceLookup {
-        private static final Type FILE_SYSTEM_LOCATION_PROVIDER = new TypeToken<Provider<FileSystemLocation>>() {
-        }.getType();
-
-        private final ImmutableList<InjectionPoint> injectionPoints;
-        private final ServiceLookup delegate;
-
-        public TransformServiceLookup(Provider<FileSystemLocation> inputFileProvider, @Nullable ArtifactTransformDependencies artifactTransformDependencies, @Nullable InputChanges inputChanges, ServiceLookup delegate) {
-            this.delegate = delegate;
-            ImmutableList.Builder<InjectionPoint> builder = ImmutableList.builder();
-            builder.add(InjectionPoint.injectedByAnnotation(InputArtifact.class, FILE_SYSTEM_LOCATION_PROVIDER, () -> inputFileProvider));
-            if (artifactTransformDependencies != null) {
-                builder.add(InjectionPoint.injectedByAnnotation(InputArtifactDependencies.class, () -> artifactTransformDependencies.getFiles().orElseThrow(() -> new IllegalStateException("Transform does not use artifact dependencies."))));
-            }
-            if (inputChanges != null) {
-                builder.add(InjectionPoint.injectedByType(InputChanges.class, () -> inputChanges));
-            }
-            this.injectionPoints = builder.build();
-        }
-
-        @Nullable
-        private Object find(Type serviceType, @Nullable Class<? extends Annotation> annotatedWith) {
-            TypeToken<?> serviceTypeToken = TypeToken.of(serviceType);
-            for (InjectionPoint injectionPoint : injectionPoints) {
-                if (annotatedWith == injectionPoint.getAnnotation() && serviceTypeToken.isSupertypeOf(injectionPoint.getInjectedType())) {
-                    return injectionPoint.getValueToInject();
-                }
-            }
-            return null;
-        }
-
-        @Nullable
-        @Override
-        public Object find(Type serviceType) throws ServiceLookupException {
-            Object result = find(serviceType, null);
-            if (result != null) {
-                return result;
-            }
-            return delegate.find(serviceType);
-        }
-
-        @Override
-        public Object get(Type serviceType) throws UnknownServiceException, ServiceLookupException {
-            Object result = find(serviceType);
-            if (result == null) {
-                throw new UnknownServiceException(serviceType, "No service of type " + serviceType + " available.");
-            }
-            return result;
-        }
-
-        @Override
-        public Object get(Type serviceType, Class<? extends Annotation> annotatedWith) throws UnknownServiceException, ServiceLookupException {
-            Object result = find(serviceType, annotatedWith);
-            if (result != null) {
-                return result;
-            }
-            return delegate.get(serviceType, annotatedWith);
-        }
-
-        private static class InjectionPoint {
-            private final Class<? extends Annotation> annotation;
-            private final Type injectedType;
-            private final Supplier<Object> valueToInject;
-
-            public static InjectionPoint injectedByAnnotation(Class<? extends Annotation> annotation, Supplier<Object> valueToInject) {
-                return new InjectionPoint(annotation, determineTypeFromAnnotation(annotation), valueToInject);
-            }
-
-            public static InjectionPoint injectedByAnnotation(Class<? extends Annotation> annotation, Type injectedType, Supplier<Object> valueToInject) {
-                return new InjectionPoint(annotation, injectedType, valueToInject);
-            }
-
-            public static InjectionPoint injectedByType(Class<?> injectedType, Supplier<Object> valueToInject) {
-                return new InjectionPoint(null, injectedType, valueToInject);
-            }
-
-            private InjectionPoint(@Nullable Class<? extends Annotation> annotation, Type injectedType, Supplier<Object> valueToInject) {
-                this.annotation = annotation;
-                this.injectedType = injectedType;
-                this.valueToInject = valueToInject;
-            }
-
-            private static Class<?> determineTypeFromAnnotation(Class<? extends Annotation> annotation) {
-                Class<?>[] supportedTypes = annotation.getAnnotation(InjectionPointQualifier.class).supportedTypes();
-                if (supportedTypes.length != 1) {
-                    throw new IllegalArgumentException("Cannot determine supported type for annotation " + annotation.getName());
-                }
-                return supportedTypes[0];
-            }
-
-            @Nullable
-            public Class<? extends Annotation> getAnnotation() {
-                return annotation;
-            }
-
-            public Type getInjectedType() {
-                return injectedType;
-            }
-
-            public Object getValueToInject() {
-                return valueToInject.get();
-            }
-        }
-    }
-
-    public static class IsolatedParameters {
-        private final HashCode secondaryInputsHash;
-        private final Isolatable<? extends TransformParameters> isolatedParameterObject;
-
-        public IsolatedParameters(Isolatable<? extends TransformParameters> isolatedParameterObject, HashCode secondaryInputsHash) {
-            this.secondaryInputsHash = secondaryInputsHash;
-            this.isolatedParameterObject = isolatedParameterObject;
-        }
-
-        public HashCode getSecondaryInputsHash() {
-            return secondaryInputsHash;
-        }
-
-        public Isolatable<? extends TransformParameters> getIsolatedParameterObject() {
-            return isolatedParameterObject;
-        }
-    }
-
-    public static class IsolateTransformerParameters implements ValueCalculator<IsolatedParameters> {
-        private final TransformParameters parameterObject;
-        private final DomainObjectContext owner;
-        private final IsolatableFactory isolatableFactory;
-        private final PropertyWalker parameterPropertyWalker;
-        private final BuildOperationExecutor buildOperationExecutor;
-        private final ClassLoaderHierarchyHasher classLoaderHierarchyHasher;
-        private final FileCollectionFactory fileCollectionFactory;
-        private final DocumentationRegistry documentationRegistry;
-        private final boolean cacheable;
-        private final Class<?> implementationClass;
-
-        public IsolateTransformerParameters(
-            @Nullable TransformParameters parameterObject,
-            Class<?> implementationClass,
-            boolean cacheable,
-            DomainObjectContext owner,
-            PropertyWalker parameterPropertyWalker,
-            IsolatableFactory isolatableFactory,
-            BuildOperationExecutor buildOperationExecutor,
-            ClassLoaderHierarchyHasher classLoaderHierarchyHasher,
-            FileCollectionFactory fileCollectionFactory,
-            DocumentationRegistry documentationRegistry
-        ) {
-            this.parameterObject = parameterObject;
-            this.implementationClass = implementationClass;
-            this.cacheable = cacheable;
-            this.owner = owner;
-            this.parameterPropertyWalker = parameterPropertyWalker;
-            this.isolatableFactory = isolatableFactory;
-            this.buildOperationExecutor = buildOperationExecutor;
-            this.classLoaderHierarchyHasher = classLoaderHierarchyHasher;
-            this.fileCollectionFactory = fileCollectionFactory;
-            this.documentationRegistry = documentationRegistry;
-        }
-
-        @Nullable
-        public TransformParameters getParameterObject() {
-            return parameterObject;
-        }
-
-        public boolean isCacheable() {
-            return cacheable;
-        }
-
-        public Class<?> getImplementationClass() {
-            return implementationClass;
-        }
-
-        @Override
-        public boolean usesMutableProjectState() {
-            return owner.getProject() != null;
-        }
-
-        @Nullable
-        @Override
-        public ProjectInternal getOwningProject() {
-            return owner.getProject();
-        }
-
-        @Override
-        public void visitDependencies(TaskDependencyResolveContext context) {
-            if (parameterObject != null) {
-                parameterPropertyWalker.visitProperties(parameterObject, TypeValidationContext.NOOP, new PropertyVisitor() {
-                    @Override
-                    public void visitInputFileProperty(
-                        String propertyName,
-                        boolean optional,
-                        InputBehavior behavior,
-                        DirectorySensitivity directorySensitivity,
-                        LineEndingSensitivity lineEndingSensitivity,
-                        @Nullable FileNormalizer fileNormalizer,
-                        PropertyValue value,
-                        InputFilePropertyType filePropertyType
-                    ) {
-                        context.add(value.getTaskDependencies());
-                    }
-                });
-            }
-        }
-
-        @Override
-        public IsolatedParameters calculateValue(NodeExecutionContext context) {
-            InputFingerprinter inputFingerprinter = context.getService(InputFingerprinter.class);
-            return isolateParameters(inputFingerprinter);
-        }
-
-        private IsolatedParameters isolateParameters(InputFingerprinter inputFingerprinter) {
-            ModelContainer<?> model = owner.getModel();
-            if (!model.hasMutableState()) {
-                // This may happen when a task visits artifacts using a FileCollection instance created from a Configuration instance in a different project (not an artifact produced by a different project, these work fine)
-                // There is a check in DefaultConfiguration that deprecates resolving dependencies via FileCollection instance created by a different project, however that check may not
-                // necessarily be triggered. For example, the configuration may be legitimately resolved by some other task prior to the problematic task running
-                // TODO - hoist this up into configuration file collection visiting (and not when visiting the upstream dependencies of a transform), and deprecate this in Gradle 7.x
-                //
-                // This may also happen when a transform takes upstream dependencies and the dependencies are transformed using a different transform
-                // In this case, the main thread that schedules the work should isolate the transform parameters prior to scheduling the work. However, the dependencies may
-                // be filtered from the result, so that the transform is not visited by the main thread, or the transform worker may start work before the main thread
-                // has a chance to isolate the upstream transform
-                // TODO - ensure all transform parameters required by a transform worker are isolated prior to starting the worker
-                //
-                // Force access to the state of the owner, regardless of whether any other thread has access. This is because attempting to acquire a lock for a project may deadlock
-                // when performed from a worker thread (see DefaultBuildOperationQueue.waitForCompletion() which intentionally does not release the project locks while waiting)
-                // TODO - add validation to fail eagerly when a worker attempts to lock a project
-                //
-                return model.forceAccessToMutableState(o -> doIsolateParameters(inputFingerprinter));
-            } else {
-                return doIsolateParameters(inputFingerprinter);
-            }
-        }
-
-        private IsolatedParameters doIsolateParameters(InputFingerprinter inputFingerprinter) {
-            try {
-                return isolateParametersExclusively(inputFingerprinter);
-            } catch (Exception e) {
-                TreeFormatter formatter = new TreeFormatter();
-                formatter.node("Could not isolate parameters ").appendValue(parameterObject).append(" of artifact transform ").appendType(implementationClass);
-                throw new VariantTransformConfigurationException(formatter.toString(), e);
-            }
-        }
-
-        private IsolatedParameters isolateParametersExclusively(InputFingerprinter inputFingerprinter) {
-            Isolatable<TransformParameters> isolatedParameterObject = isolatableFactory.isolate(parameterObject);
-
-            Hasher hasher = Hashing.newHasher();
-            hasher.putString(implementationClass.getName());
-            hasher.putHash(classLoaderHierarchyHasher.getClassLoaderHash(implementationClass.getClassLoader()));
-
-            if (parameterObject != null) {
-                TransformParameters isolatedTransformParameters = isolatedParameterObject.isolate();
-                buildOperationExecutor.run(new RunnableBuildOperation() {
-                    @Override
-                    public void run(BuildOperationContext context) {
-                        // TODO wolfs - schedule fingerprinting separately, it can be done without having the project lock
-                        fingerprintParameters(
-                            documentationRegistry,
-                            inputFingerprinter,
-                            fileCollectionFactory,
-                            parameterPropertyWalker,
-                            hasher,
-                            isolatedTransformParameters,
-                            cacheable
-                        );
-                        context.setResult(FingerprintTransformInputsOperation.Result.INSTANCE);
-                    }
-
-                    @Override
-                    public BuildOperationDescriptor.Builder description() {
-                        return BuildOperationDescriptor
-                            .displayName("Fingerprint transformation inputs")
-                            .details(FingerprintTransformInputsOperation.Details.INSTANCE);
-                    }
-                });
-            }
-            HashCode secondaryInputsHash = hasher.hash();
-            return new IsolatedParameters(isolatedParameterObject, secondaryInputsHash);
-        }
-    }
-
-    /*
-     * This operation is only used here temporarily. Should be replaced with a more stable operation in the long term.
-     */
-    public interface FingerprintTransformInputsOperation extends BuildOperationType<FingerprintTransformInputsOperation.Details, FingerprintTransformInputsOperation.Result> {
-        interface Details {
-            Details INSTANCE = new Details() {
-            };
-        }
-
-        interface Result {
-            Result INSTANCE = new Result() {
-            };
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformerInvocationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformerInvocationFactory.java
deleted file mode 100644
index 458d7d7..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultTransformerInvocationFactory.java
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright 2017 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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.file.FileSystemLocation;
-import org.gradle.api.internal.file.DefaultFileSystemLocation;
-import org.gradle.api.internal.file.FileCollectionFactory;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.project.ProjectStateRegistry;
-import org.gradle.api.internal.provider.Providers;
-import org.gradle.api.provider.Provider;
-import org.gradle.internal.Deferrable;
-import org.gradle.internal.Try;
-import org.gradle.internal.execution.ExecutionEngine;
-import org.gradle.internal.execution.InputFingerprinter;
-import org.gradle.internal.execution.UnitOfWork;
-import org.gradle.internal.execution.caching.CachingDisabledReason;
-import org.gradle.internal.execution.caching.CachingDisabledReasonCategory;
-import org.gradle.internal.execution.history.OverlappingOutputs;
-import org.gradle.internal.execution.history.changes.InputChangesInternal;
-import org.gradle.internal.execution.model.InputNormalizer;
-import org.gradle.internal.execution.workspace.WorkspaceProvider;
-import org.gradle.internal.fingerprint.CurrentFileCollectionFingerprint;
-import org.gradle.internal.hash.HashCode;
-import org.gradle.internal.hash.Hasher;
-import org.gradle.internal.hash.Hashing;
-import org.gradle.internal.operations.BuildOperationContext;
-import org.gradle.internal.operations.BuildOperationDescriptor;
-import org.gradle.internal.operations.BuildOperationExecutor;
-import org.gradle.internal.operations.CallableBuildOperation;
-import org.gradle.internal.snapshot.FileSystemLocationSnapshot;
-import org.gradle.internal.snapshot.ValueSnapshot;
-import org.gradle.internal.vfs.FileSystemAccess;
-
-import javax.annotation.Nullable;
-import javax.annotation.OverridingMethodsMustInvokeSuper;
-import java.io.File;
-import java.time.Duration;
-import java.util.Map;
-import java.util.Optional;
-
-import static org.gradle.internal.file.TreeType.DIRECTORY;
-import static org.gradle.internal.file.TreeType.FILE;
-import static org.gradle.internal.properties.InputBehavior.INCREMENTAL;
-import static org.gradle.internal.properties.InputBehavior.NON_INCREMENTAL;
-
-public class DefaultTransformerInvocationFactory implements TransformerInvocationFactory {
-    private static final CachingDisabledReason NOT_CACHEABLE = new CachingDisabledReason(CachingDisabledReasonCategory.NOT_CACHEABLE, "Caching not enabled.");
-    private static final String INPUT_ARTIFACT_PROPERTY_NAME = "inputArtifact";
-    private static final String INPUT_ARTIFACT_PATH_PROPERTY_NAME = "inputArtifactPath";
-    private static final String INPUT_ARTIFACT_SNAPSHOT_PROPERTY_NAME = "inputArtifactSnapshot";
-    private static final String DEPENDENCIES_PROPERTY_NAME = "inputArtifactDependencies";
-    private static final String SECONDARY_INPUTS_HASH_PROPERTY_NAME = "inputPropertiesHash";
-    private static final String OUTPUT_DIRECTORY_PROPERTY_NAME = "outputDirectory";
-    private static final String RESULTS_FILE_PROPERTY_NAME = "resultsFile";
-
-    private final ExecutionEngine executionEngine;
-    private final FileSystemAccess fileSystemAccess;
-    private final ArtifactTransformListener artifactTransformListener;
-    private final TransformationWorkspaceServices immutableWorkspaceProvider;
-    private final FileCollectionFactory fileCollectionFactory;
-    private final ProjectStateRegistry projectStateRegistry;
-    private final BuildOperationExecutor buildOperationExecutor;
-
-    public DefaultTransformerInvocationFactory(
-        ExecutionEngine executionEngine,
-        FileSystemAccess fileSystemAccess,
-        ArtifactTransformListener artifactTransformListener,
-        TransformationWorkspaceServices immutableWorkspaceProvider,
-        FileCollectionFactory fileCollectionFactory,
-        ProjectStateRegistry projectStateRegistry,
-        BuildOperationExecutor buildOperationExecutor
-    ) {
-        this.executionEngine = executionEngine;
-        this.fileSystemAccess = fileSystemAccess;
-        this.artifactTransformListener = artifactTransformListener;
-        this.immutableWorkspaceProvider = immutableWorkspaceProvider;
-        this.fileCollectionFactory = fileCollectionFactory;
-        this.projectStateRegistry = projectStateRegistry;
-        this.buildOperationExecutor = buildOperationExecutor;
-    }
-
-    @Override
-    public Deferrable<Try<ImmutableList<File>>> createInvocation(
-        Transformer transformer,
-        File inputArtifact,
-        ArtifactTransformDependencies dependencies,
-        TransformationSubject subject,
-        InputFingerprinter inputFingerprinter
-    ) {
-        ProjectInternal producerProject = determineProducerProject(subject);
-        TransformationWorkspaceServices workspaceServices = determineWorkspaceServices(producerProject);
-
-        UnitOfWork execution;
-        if (producerProject == null) {
-            execution = new ImmutableTransformerExecution(
-                transformer,
-                inputArtifact,
-                dependencies,
-                subject,
-
-                artifactTransformListener,
-                buildOperationExecutor,
-                fileCollectionFactory,
-                inputFingerprinter,
-                fileSystemAccess,
-                workspaceServices
-            );
-        } else {
-            execution = new MutableTransformerExecution(
-                transformer,
-                inputArtifact,
-                dependencies,
-                subject,
-
-                artifactTransformListener,
-                buildOperationExecutor,
-                fileCollectionFactory,
-                inputFingerprinter,
-                workspaceServices
-            );
-        }
-
-        return executionEngine.createRequest(execution)
-            .executeDeferred(workspaceServices.getIdentityCache())
-            .map(result -> result
-                .map(successfulResult -> successfulResult.resolveOutputsForInputArtifact(inputArtifact))
-                .mapFailure(failure -> new TransformException(String.format("Execution failed for %s.", execution.getDisplayName()), failure)));
-    }
-
-    private TransformationWorkspaceServices determineWorkspaceServices(@Nullable ProjectInternal producerProject) {
-        if (producerProject == null) {
-            return immutableWorkspaceProvider;
-        }
-        return producerProject.getServices().get(TransformationWorkspaceServices.class);
-    }
-
-    @Nullable
-    private ProjectInternal determineProducerProject(TransformationSubject subject) {
-        ComponentIdentifier componentIdentifier = subject.getInitialComponentIdentifier();
-        if (componentIdentifier instanceof ProjectComponentIdentifier) {
-            return projectStateRegistry.stateFor((ProjectComponentIdentifier) componentIdentifier).getMutableModel();
-        } else {
-            return null;
-        }
-    }
-
-    private static class ImmutableTransformerExecution extends AbstractTransformerExecution {
-        private final FileSystemAccess fileSystemAccess;
-
-        public ImmutableTransformerExecution(
-            Transformer transformer,
-            File inputArtifact,
-            ArtifactTransformDependencies dependencies,
-            TransformationSubject subject,
-
-            ArtifactTransformListener artifactTransformListener,
-            BuildOperationExecutor buildOperationExecutor,
-            FileCollectionFactory fileCollectionFactory,
-            InputFingerprinter inputFingerprinter,
-            FileSystemAccess fileSystemAccess,
-            TransformationWorkspaceServices workspaceServices
-        ) {
-            super(
-                transformer, inputArtifact, dependencies, subject,
-                artifactTransformListener, buildOperationExecutor, fileCollectionFactory, inputFingerprinter, workspaceServices
-            );
-            this.fileSystemAccess = fileSystemAccess;
-        }
-
-        @Override
-        public void visitIdentityInputs(InputVisitor visitor) {
-            super.visitIdentityInputs(visitor);
-            // This is a performance hack. We could use the regular fingerprint of the input artifact, but that takes longer than
-            // capturing the normalized path and the snapshot of the raw contents, so we are using these to determine the identity
-            FileSystemLocationSnapshot inputArtifactSnapshot = fileSystemAccess.read(inputArtifact.getAbsolutePath());
-            visitor.visitInputProperty(INPUT_ARTIFACT_SNAPSHOT_PROPERTY_NAME, inputArtifactSnapshot::getHash);
-        }
-
-        @Override
-        public Identity identify(Map<String, ValueSnapshot> identityInputs, Map<String, CurrentFileCollectionFingerprint> identityFileInputs) {
-            return new ImmutableTransformationWorkspaceIdentity(
-                identityInputs.get(INPUT_ARTIFACT_PATH_PROPERTY_NAME),
-                identityInputs.get(INPUT_ARTIFACT_SNAPSHOT_PROPERTY_NAME),
-                identityInputs.get(SECONDARY_INPUTS_HASH_PROPERTY_NAME),
-                identityFileInputs.get(DEPENDENCIES_PROPERTY_NAME).getHash()
-            );
-        }
-    }
-
-    private static class MutableTransformerExecution extends AbstractTransformerExecution {
-        public MutableTransformerExecution(
-            Transformer transformer,
-            File inputArtifact,
-            ArtifactTransformDependencies dependencies,
-            TransformationSubject subject,
-
-            ArtifactTransformListener artifactTransformListener,
-            BuildOperationExecutor buildOperationExecutor,
-            FileCollectionFactory fileCollectionFactory,
-            InputFingerprinter inputFingerprinter,
-            TransformationWorkspaceServices workspaceServices
-        ) {
-            super(
-                transformer, inputArtifact, dependencies, subject,
-                artifactTransformListener, buildOperationExecutor, fileCollectionFactory, inputFingerprinter, workspaceServices
-            );
-        }
-
-        @Override
-        public Identity identify(Map<String, ValueSnapshot> identityInputs, Map<String, CurrentFileCollectionFingerprint> identityFileInputs) {
-            return new MutableTransformationWorkspaceIdentity(
-                inputArtifact.getAbsolutePath(),
-                identityInputs.get(SECONDARY_INPUTS_HASH_PROPERTY_NAME),
-                identityFileInputs.get(DEPENDENCIES_PROPERTY_NAME).getHash()
-            );
-        }
-    }
-
-    private abstract static class AbstractTransformerExecution implements UnitOfWork {
-        protected final Transformer transformer;
-        protected final File inputArtifact;
-        private final ArtifactTransformDependencies dependencies;
-        private final TransformationSubject subject;
-
-        private final ArtifactTransformListener artifactTransformListener;
-        private final BuildOperationExecutor buildOperationExecutor;
-        private final FileCollectionFactory fileCollectionFactory;
-
-        private final Provider<FileSystemLocation> inputArtifactProvider;
-        protected final InputFingerprinter inputFingerprinter;
-        private final TransformationWorkspaceServices workspaceServices;
-
-        public AbstractTransformerExecution(
-            Transformer transformer,
-            File inputArtifact,
-            ArtifactTransformDependencies dependencies,
-            TransformationSubject subject,
-
-            ArtifactTransformListener artifactTransformListener,
-            BuildOperationExecutor buildOperationExecutor,
-            FileCollectionFactory fileCollectionFactory,
-            InputFingerprinter inputFingerprinter,
-            TransformationWorkspaceServices workspaceServices
-        ) {
-            this.transformer = transformer;
-            this.inputArtifact = inputArtifact;
-            this.dependencies = dependencies;
-            this.inputArtifactProvider = Providers.of(new DefaultFileSystemLocation(inputArtifact));
-            this.subject = subject;
-            this.artifactTransformListener = artifactTransformListener;
-
-            this.buildOperationExecutor = buildOperationExecutor;
-            this.fileCollectionFactory = fileCollectionFactory;
-            this.inputFingerprinter = inputFingerprinter;
-            this.workspaceServices = workspaceServices;
-        }
-
-        @Override
-        public WorkOutput execute(ExecutionRequest executionRequest) {
-            artifactTransformListener.beforeTransformerInvocation(transformer, subject);
-            try {
-                return executeWithinTransformerListener(executionRequest);
-            } finally {
-                artifactTransformListener.afterTransformerInvocation(transformer, subject);
-            }
-        }
-
-        private WorkOutput executeWithinTransformerListener(ExecutionRequest executionRequest) {
-            TransformationResult result = buildOperationExecutor.call(new CallableBuildOperation<TransformationResult>() {
-                @Override
-                public TransformationResult call(BuildOperationContext context) {
-                    File workspace = executionRequest.getWorkspace();
-                    InputChangesInternal inputChanges = executionRequest.getInputChanges().orElse(null);
-                    TransformationResult result = transformer.transform(inputArtifactProvider, getOutputDir(workspace), dependencies, inputChanges);
-                    TransformationResultSerializer resultSerializer = new TransformationResultSerializer(getOutputDir(workspace));
-                    resultSerializer.writeToFile(getResultsFile(workspace), result);
-                    return result;
-                }
-
-                @Override
-                public BuildOperationDescriptor.Builder description() {
-                    String displayName = transformer.getDisplayName() + " " + inputArtifact.getName();
-                    return BuildOperationDescriptor.displayName(displayName)
-                        .progressDisplayName(displayName);
-                }
-            });
-
-            return new WorkOutput() {
-                @Override
-                public WorkResult getDidWork() {
-                    return WorkResult.DID_WORK;
-                }
-
-                @Override
-                public Object getOutput() {
-                    return result;
-                }
-            };
-        }
-
-        @Override
-        public Object loadAlreadyProducedOutput(File workspace) {
-            TransformationResultSerializer resultSerializer = new TransformationResultSerializer(getOutputDir(workspace));
-            return resultSerializer.readResultsFile(getResultsFile(workspace));
-        }
-
-        @Override
-        public WorkspaceProvider getWorkspaceProvider() {
-            return workspaceServices.getWorkspaceProvider();
-        }
-
-        @Override
-        public InputFingerprinter getInputFingerprinter() {
-            return inputFingerprinter;
-        }
-
-        private static File getOutputDir(File workspace) {
-            return new File(workspace, "transformed");
-        }
-
-        private static File getResultsFile(File workspace) {
-            return new File(workspace, "results.bin");
-        }
-
-        @Override
-        public Optional<Duration> getTimeout() {
-            return Optional.empty();
-        }
-
-        @Override
-        public ExecutionBehavior getExecutionBehavior() {
-            return transformer.requiresInputChanges()
-                ? ExecutionBehavior.INCREMENTAL
-                : ExecutionBehavior.NON_INCREMENTAL;
-        }
-
-        @Override
-        public void visitImplementations(ImplementationVisitor visitor) {
-            visitor.visitImplementation(transformer.getImplementationClass());
-        }
-
-        @Override
-        @OverridingMethodsMustInvokeSuper
-        public void visitIdentityInputs(InputVisitor visitor) {
-            // Emulate secondary inputs as a single property for now
-            visitor.visitInputProperty(SECONDARY_INPUTS_HASH_PROPERTY_NAME, transformer::getSecondaryInputHash);
-            visitor.visitInputProperty(INPUT_ARTIFACT_PATH_PROPERTY_NAME, () ->
-                // We always need the name as an input to the artifact transform,
-                // since it is part of the ComponentArtifactIdentifier returned by the transform.
-                // For absolute paths, the name is already part of the normalized path,
-                // and for all the other normalization strategies we use the name directly.
-                transformer.getInputArtifactNormalizer() == InputNormalizer.ABSOLUTE_PATH
-                    ? inputArtifact.getAbsolutePath()
-                    : inputArtifact.getName());
-            visitor.visitInputFileProperty(DEPENDENCIES_PROPERTY_NAME, NON_INCREMENTAL,
-                new InputFileValueSupplier(
-                    dependencies,
-                    transformer.getInputArtifactDependenciesNormalizer(),
-                    transformer.getInputArtifactDependenciesDirectorySensitivity(),
-                    transformer.getInputArtifactDependenciesLineEndingNormalization(),
-                    () -> dependencies.getFiles()
-                        .orElse(FileCollectionFactory.empty())));
-        }
-
-        @Override
-        @OverridingMethodsMustInvokeSuper
-        public void visitRegularInputs(InputVisitor visitor) {
-            visitor.visitInputFileProperty(INPUT_ARTIFACT_PROPERTY_NAME, INCREMENTAL,
-                new InputFileValueSupplier(
-                    inputArtifactProvider,
-                    transformer.getInputArtifactNormalizer(),
-                    transformer.getInputArtifactDirectorySensitivity(),
-                    transformer.getInputArtifactLineEndingNormalization(),
-                    () -> fileCollectionFactory.fixed(inputArtifact)));
-        }
-
-        @Override
-        public void visitOutputs(File workspace, OutputVisitor visitor) {
-            File outputDir = getOutputDir(workspace);
-            File resultsFile = getResultsFile(workspace);
-            visitor.visitOutputProperty(OUTPUT_DIRECTORY_PROPERTY_NAME, DIRECTORY,
-                OutputFileValueSupplier.fromStatic(outputDir, fileCollectionFactory.fixed(outputDir)));
-            visitor.visitOutputProperty(RESULTS_FILE_PROPERTY_NAME, FILE,
-                OutputFileValueSupplier.fromStatic(resultsFile, fileCollectionFactory.fixed(resultsFile)));
-        }
-
-        @Override
-        public Optional<CachingDisabledReason> shouldDisableCaching(@Nullable OverlappingOutputs detectedOverlappingOutputs) {
-            return transformer.isCacheable()
-                ? Optional.empty()
-                : Optional.of(NOT_CACHEABLE);
-        }
-
-        @Override
-        public String getDisplayName() {
-            return transformer.getDisplayName() + ": " + inputArtifact;
-        }
-    }
-
-    private static class ImmutableTransformationWorkspaceIdentity implements UnitOfWork.Identity {
-        private final ValueSnapshot inputArtifactPath;
-        private final ValueSnapshot inputArtifactSnapshot;
-        private final ValueSnapshot secondaryInputSnapshot;
-        private final HashCode dependenciesHash;
-
-        public ImmutableTransformationWorkspaceIdentity(ValueSnapshot inputArtifactPath, ValueSnapshot inputArtifactSnapshot, ValueSnapshot secondaryInputSnapshot, HashCode dependenciesHash) {
-            this.inputArtifactPath = inputArtifactPath;
-            this.inputArtifactSnapshot = inputArtifactSnapshot;
-            this.secondaryInputSnapshot = secondaryInputSnapshot;
-            this.dependenciesHash = dependenciesHash;
-        }
-
-        @Override
-        public String getUniqueId() {
-            Hasher hasher = Hashing.newHasher();
-            inputArtifactPath.appendToHasher(hasher);
-            inputArtifactSnapshot.appendToHasher(hasher);
-            secondaryInputSnapshot.appendToHasher(hasher);
-            hasher.putHash(dependenciesHash);
-            return hasher.hash().toString();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            ImmutableTransformationWorkspaceIdentity that = (ImmutableTransformationWorkspaceIdentity) o;
-
-            if (!inputArtifactPath.equals(that.inputArtifactPath)) {
-                return false;
-            }
-            if (!inputArtifactSnapshot.equals(that.inputArtifactSnapshot)) {
-                return false;
-            }
-            if (!secondaryInputSnapshot.equals(that.secondaryInputSnapshot)) {
-                return false;
-            }
-            return dependenciesHash.equals(that.dependenciesHash);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = inputArtifactPath.hashCode();
-            result = 31 * result + inputArtifactSnapshot.hashCode();
-            result = 31 * result + secondaryInputSnapshot.hashCode();
-            result = 31 * result + dependenciesHash.hashCode();
-            return result;
-        }
-    }
-
-    public static class MutableTransformationWorkspaceIdentity implements UnitOfWork.Identity {
-        private final String inputArtifactAbsolutePath;
-        private final ValueSnapshot secondaryInputsSnapshot;
-        private final HashCode dependenciesHash;
-
-        public MutableTransformationWorkspaceIdentity(String inputArtifactAbsolutePath, ValueSnapshot secondaryInputsSnapshot, HashCode dependenciesHash) {
-            this.inputArtifactAbsolutePath = inputArtifactAbsolutePath;
-            this.secondaryInputsSnapshot = secondaryInputsSnapshot;
-            this.dependenciesHash = dependenciesHash;
-        }
-
-        @Override
-        public String getUniqueId() {
-            Hasher hasher = Hashing.newHasher();
-            hasher.putString(inputArtifactAbsolutePath);
-            secondaryInputsSnapshot.appendToHasher(hasher);
-            hasher.putHash(dependenciesHash);
-            return hasher.hash().toString();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            MutableTransformationWorkspaceIdentity that = (MutableTransformationWorkspaceIdentity) o;
-
-            if (!secondaryInputsSnapshot.equals(that.secondaryInputsSnapshot)) {
-                return false;
-            }
-            if (!dependenciesHash.equals(that.dependenciesHash)) {
-                return false;
-            }
-            return inputArtifactAbsolutePath.equals(that.inputArtifactAbsolutePath);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = inputArtifactAbsolutePath.hashCode();
-            result = 31 * result + secondaryInputsSnapshot.hashCode();
-            result = 31 * result + dependenciesHash.hashCode();
-            return result;
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantDefinition.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantDefinition.java
index 1ebae4e..8533497 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantDefinition.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantDefinition.java
@@ -26,14 +26,14 @@
 public class DefaultVariantDefinition implements VariantDefinition {
     private final DefaultVariantDefinition previous;
     private final ImmutableAttributes attributes;
-    private final Transformation transformation;
-    private final TransformationStep transformationStep;
+    private final TransformChain transformChain;
+    private final TransformStep transformStep;
 
-    public DefaultVariantDefinition(@Nullable DefaultVariantDefinition previous, ImmutableAttributes attributes, TransformationStep transformationStep) {
+    public DefaultVariantDefinition(@Nullable DefaultVariantDefinition previous, ImmutableAttributes attributes, TransformStep transformStep) {
         this.previous = previous;
         this.attributes = attributes;
-        this.transformation = previous != null ? new TransformationChain(previous.getTransformation(), transformationStep) : transformationStep;
-        this.transformationStep = transformationStep;
+        this.transformChain = new TransformChain(previous == null ? null : previous.getTransformChain(), transformStep);
+        this.transformStep = transformStep;
     }
 
     @Override
@@ -42,13 +42,13 @@ public ImmutableAttributes getTargetAttributes() {
     }
 
     @Override
-    public Transformation getTransformation() {
-        return transformation;
+    public TransformChain getTransformChain() {
+        return transformChain;
     }
 
     @Override
-    public TransformationStep getTransformationStep() {
-        return transformationStep;
+    public TransformStep getTransformStep() {
+        return transformStep;
     }
 
     @Nullable
@@ -63,9 +63,9 @@ private int getDepth() {
 
     public String toString() {
         if (previous != null) {
-            return previous + " <- (" + getDepth() + ") " + transformationStep;
+            return previous + " <- (" + getDepth() + ") " + transformStep;
         } else {
-            return "(" + getDepth() + ") " + transformationStep;
+            return "(" + getDepth() + ") " + transformStep;
         }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantSelectorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantSelectorFactory.java
new file mode 100644
index 0000000..87faeaa
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantSelectorFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 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.api.internal.artifacts.transform;
+
+import org.gradle.api.internal.attributes.AttributeContainerInternal;
+import org.gradle.api.internal.attributes.AttributesSchemaInternal;
+import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
+
+public class DefaultVariantSelectorFactory implements VariantSelectorFactory {
+    private final ConsumerProvidedVariantFinder consumerProvidedVariantFinder;
+    private final AttributesSchemaInternal schema;
+    private final ImmutableAttributesFactory attributesFactory;
+    private final TransformedVariantFactory transformedVariantFactory;
+
+    public DefaultVariantSelectorFactory(
+        ConsumerProvidedVariantFinder consumerProvidedVariantFinder,
+        AttributesSchemaInternal schema,
+        ImmutableAttributesFactory attributesFactory,
+        TransformedVariantFactory transformedVariantFactory
+    ) {
+        this.consumerProvidedVariantFinder = consumerProvidedVariantFinder;
+        this.schema = schema;
+        this.attributesFactory = attributesFactory;
+        this.transformedVariantFactory = transformedVariantFactory;
+    }
+
+    @Override
+    public VariantSelector create(AttributeContainerInternal consumerAttributes, boolean allowNoMatchingVariants, boolean selectFromAllVariants, TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory) {
+        return new AttributeMatchingVariantSelector(consumerProvidedVariantFinder, schema, attributesFactory, transformedVariantFactory, consumerAttributes.asImmutable(), allowNoMatchingVariants, selectFromAllVariants, dependenciesResolverFactory);
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistry.java
index 3304da4..dbad872 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistry.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistry.java
@@ -24,7 +24,7 @@
 import org.gradle.api.artifacts.transform.TransformSpec;
 import org.gradle.api.artifacts.transform.VariantTransformConfigurationException;
 import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.ArtifactTransformRegistration;
+import org.gradle.api.internal.artifacts.TransformRegistration;
 import org.gradle.api.internal.artifacts.VariantTransformRegistry;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
@@ -39,16 +39,16 @@
 import java.util.List;
 
 public class DefaultVariantTransformRegistry implements VariantTransformRegistry {
-    private final List<ArtifactTransformRegistration> transforms = Lists.newArrayList();
+    private final List<TransformRegistration> registrations = Lists.newArrayList();
     private final ImmutableAttributesFactory immutableAttributesFactory;
     private final ServiceRegistry services;
     private final InstantiatorFactory instantiatorFactory;
     private final InstantiationScheme parametersInstantiationScheme;
-    private final TransformationRegistrationFactory registrationFactory;
+    private final TransformRegistrationFactory registrationFactory;
     @SuppressWarnings("unchecked")
     private final IsolationScheme<TransformAction<?>, TransformParameters> isolationScheme = new IsolationScheme<TransformAction<?>, TransformParameters>((Class)TransformAction.class, TransformParameters.class, TransformParameters.None.class);
 
-    public DefaultVariantTransformRegistry(InstantiatorFactory instantiatorFactory, ImmutableAttributesFactory immutableAttributesFactory, ServiceRegistry services, TransformationRegistrationFactory registrationFactory, InstantiationScheme parametersInstantiationScheme) {
+    public DefaultVariantTransformRegistry(InstantiatorFactory instantiatorFactory, ImmutableAttributesFactory immutableAttributesFactory, ServiceRegistry services, TransformRegistrationFactory registrationFactory, InstantiationScheme parametersInstantiationScheme) {
         this.instantiatorFactory = instantiatorFactory;
         this.immutableAttributesFactory = immutableAttributesFactory;
         this.services = services;
@@ -95,8 +95,8 @@ private <T extends TransformParameters> void register(RecordingRegistration regi
         validateActionType(actionType);
         try {
             validateAttributes(registration);
-            ArtifactTransformRegistration finalizedRegistration = registrationFactory.create(registration.from.asImmutable(), registration.to.asImmutable(), actionType, parameterObject);
-            transforms.add(finalizedRegistration);
+            TransformRegistration finalizedRegistration = registrationFactory.create(registration.from.asImmutable(), registration.to.asImmutable(), actionType, parameterObject);
+            registrations.add(finalizedRegistration);
         } catch (Exception e) {
             TreeFormatter formatter = new TreeFormatter();
             formatter.node("Could not register artifact transform ");
@@ -129,8 +129,8 @@ private static void validateAttributes(RecordingRegistration registration) {
     }
 
     @Override
-    public List<ArtifactTransformRegistration> getTransforms() {
-        return transforms;
+    public List<TransformRegistration> getRegistrations() {
+        return registrations;
     }
 
     public static abstract class RecordingRegistration {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExecutePlannedTransformStepBuildOperationDetails.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExecutePlannedTransformStepBuildOperationDetails.java
new file mode 100644
index 0000000..d7824f3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExecutePlannedTransformStepBuildOperationDetails.java
@@ -0,0 +1,70 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import com.google.common.collect.ImmutableMap;
+import org.gradle.internal.operations.trace.CustomOperationTraceSerialization;
+import org.gradle.operations.dependencies.transforms.ExecutePlannedTransformStepBuildOperationType;
+import org.gradle.operations.dependencies.transforms.PlannedTransformStepIdentity;
+
+public class ExecutePlannedTransformStepBuildOperationDetails implements ExecutePlannedTransformStepBuildOperationType.Details, CustomOperationTraceSerialization {
+
+    private final TransformStepNode transformStepNode;
+    private final String transformerName;
+    private final String subjectName;
+
+    public ExecutePlannedTransformStepBuildOperationDetails(TransformStepNode transformStepNode, String transformerName, String subjectName) {
+        this.transformStepNode = transformStepNode;
+        this.transformerName = transformerName;
+        this.subjectName = subjectName;
+    }
+
+    public TransformStepNode getTransformStepNode() {
+        return transformStepNode;
+    }
+
+    @Override
+    public PlannedTransformStepIdentity getPlannedTransformStepIdentity() {
+        return transformStepNode.getNodeIdentity();
+    }
+
+    @Override
+    public Class<?> getTransformActionClass() {
+        return transformStepNode.getTransformStep().getTransform().getImplementationClass();
+    }
+
+    @Override
+    public String getTransformerName() {
+        return transformerName;
+    }
+
+    @Override
+    public String getSubjectName() {
+        return subjectName;
+    }
+
+    @Override
+    public Object getCustomOperationTraceSerializableModel() {
+        ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
+        builder.put("plannedTransformStepIdentity", getPlannedTransformStepIdentity());
+        builder.put("transformActionClass", getTransformActionClass());
+        builder.put("transformerName", transformerName);
+        builder.put("subjectName", subjectName);
+        return builder.build();
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExecuteScheduledTransformationStepBuildOperationDetails.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExecuteScheduledTransformationStepBuildOperationDetails.java
deleted file mode 100644
index c4a6cde..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExecuteScheduledTransformationStepBuildOperationDetails.java
+++ /dev/null
@@ -1,88 +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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableMap;
-import org.gradle.internal.operations.trace.CustomOperationTraceSerialization;
-
-import java.util.Map;
-
-public class ExecuteScheduledTransformationStepBuildOperationDetails implements ExecuteScheduledTransformationStepBuildOperationType.Details, CustomOperationTraceSerialization {
-
-    private final TransformationNode transformationNode;
-    private final String transformerName;
-    private final String subjectName;
-
-    public ExecuteScheduledTransformationStepBuildOperationDetails(TransformationNode transformationNode, String transformerName, String subjectName) {
-        this.transformationNode = transformationNode;
-        this.transformerName = transformerName;
-        this.subjectName = subjectName;
-    }
-
-    public TransformationNode getTransformationNode() {
-        return transformationNode;
-    }
-
-    @Override
-    public TransformationIdentity getTransformationIdentity() {
-        return transformationNode.getNodeIdentity();
-    }
-
-    @Override
-    public Map<String, String> getSourceAttributes() {
-        return AttributesToMapConverter.convertToMap(transformationNode.getSourceAttributes());
-    }
-
-    @Override
-    public Map<String, String> getFromAttributes() {
-        return AttributesToMapConverter.convertToMap(transformationNode.getTransformationStep().getFromAttributes());
-    }
-
-    @Override
-    public Map<String, String> getToAttributes() {
-        return AttributesToMapConverter.convertToMap(transformationNode.getTransformationStep().getToAttributes());
-    }
-
-    @Override
-    public Class<?> getTransformType() {
-        return transformationNode.getTransformationStep().getTransformer().getImplementationClass();
-    }
-
-    @Override
-    public String getTransformerName() {
-        return transformerName;
-    }
-
-    @Override
-    public String getSubjectName() {
-        return subjectName;
-    }
-
-    @Override
-    public Object getCustomOperationTraceSerializableModel() {
-        ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
-        builder.put("transformationIdentity", getTransformationIdentity());
-        builder.put("sourceAttributes", getSourceAttributes());
-        builder.put("fromAttributes", getFromAttributes());
-        builder.put("toAttributes", getToAttributes());
-        builder.put("transformType", getTransformType());
-        builder.put("transformerName", transformerName);
-        builder.put("subjectName", subjectName);
-        return builder.build();
-    }
-
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExtraExecutionGraphDependenciesResolverFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExtraExecutionGraphDependenciesResolverFactory.java
deleted file mode 100644
index c2ac08f..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ExtraExecutionGraphDependenciesResolverFactory.java
+++ /dev/null
@@ -1,34 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-
-/**
- * Factory for {@link TransformUpstreamDependenciesResolver} that relies on the information provided to its {@code create} method.
- */
-public interface ExtraExecutionGraphDependenciesResolverFactory {
-    /**
-     * Creates a {@link TransformUpstreamDependenciesResolver} for the given {@code ComponentIdentifier} and {@code Transformation}.
-     *
-     * @param componentIdentifier the identifier of the component whose artifacts will be transformed.
-     * @param transformation the transformation that will be applied.
-     *
-     * @return an {@code ExecutionGraphDependenciesResolver} based on the provided parameters
-     */
-    TransformUpstreamDependenciesResolver create(ComponentIdentifier componentIdentifier, Transformation transformation);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ImmutableTransformWorkspaceServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ImmutableTransformWorkspaceServices.java
new file mode 100644
index 0000000..eb45f85
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ImmutableTransformWorkspaceServices.java
@@ -0,0 +1,61 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.internal.cache.CacheConfigurationsInternal;
+import org.gradle.cache.CacheBuilder;
+import org.gradle.cache.internal.CrossBuildInMemoryCache;
+import org.gradle.internal.Try;
+import org.gradle.internal.execution.UnitOfWork;
+import org.gradle.internal.execution.history.ExecutionHistoryStore;
+import org.gradle.internal.execution.workspace.impl.DefaultImmutableWorkspaceProvider;
+import org.gradle.internal.file.FileAccessTimeJournal;
+
+import javax.annotation.concurrent.NotThreadSafe;
+import java.io.Closeable;
+
+@NotThreadSafe
+public class ImmutableTransformWorkspaceServices implements TransformWorkspaceServices, Closeable {
+    private final CrossBuildInMemoryCache<UnitOfWork.Identity, Try<TransformExecutionResult>> identityCache;
+    private final DefaultImmutableWorkspaceProvider workspaceProvider;
+
+    public ImmutableTransformWorkspaceServices(
+        CacheBuilder cacheBuilder,
+        FileAccessTimeJournal fileAccessTimeJournal,
+        ExecutionHistoryStore executionHistoryStore,
+        CrossBuildInMemoryCache<UnitOfWork.Identity, Try<TransformExecutionResult>> identityCache,
+        CacheConfigurationsInternal cacheConfigurations
+    ) {
+        this.workspaceProvider = DefaultImmutableWorkspaceProvider.withExternalHistory(cacheBuilder, fileAccessTimeJournal, executionHistoryStore, cacheConfigurations);
+        this.identityCache = identityCache;
+    }
+
+    @Override
+    public DefaultImmutableWorkspaceProvider getWorkspaceProvider() {
+        return workspaceProvider;
+    }
+
+    @Override
+    public CrossBuildInMemoryCache<UnitOfWork.Identity, Try<TransformExecutionResult>> getIdentityCache() {
+        return identityCache;
+    }
+
+    @Override
+    public void close() {
+        workspaceProvider.close();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ImmutableTransformationWorkspaceServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ImmutableTransformationWorkspaceServices.java
deleted file mode 100644
index eba6a16..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ImmutableTransformationWorkspaceServices.java
+++ /dev/null
@@ -1,61 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.internal.cache.CacheConfigurationsInternal;
-import org.gradle.cache.CacheBuilder;
-import org.gradle.cache.internal.CrossBuildInMemoryCache;
-import org.gradle.internal.Try;
-import org.gradle.internal.execution.UnitOfWork;
-import org.gradle.internal.execution.history.ExecutionHistoryStore;
-import org.gradle.internal.execution.workspace.impl.DefaultImmutableWorkspaceProvider;
-import org.gradle.internal.file.FileAccessTimeJournal;
-
-import javax.annotation.concurrent.NotThreadSafe;
-import java.io.Closeable;
-
-@NotThreadSafe
-public class ImmutableTransformationWorkspaceServices implements TransformationWorkspaceServices, Closeable {
-    private final CrossBuildInMemoryCache<UnitOfWork.Identity, Try<TransformationResult>> identityCache;
-    private final DefaultImmutableWorkspaceProvider workspaceProvider;
-
-    public ImmutableTransformationWorkspaceServices(
-        CacheBuilder cacheBuilder,
-        FileAccessTimeJournal fileAccessTimeJournal,
-        ExecutionHistoryStore executionHistoryStore,
-        CrossBuildInMemoryCache<UnitOfWork.Identity, Try<TransformationResult>> identityCache,
-        CacheConfigurationsInternal cacheConfigurations
-    ) {
-        this.workspaceProvider = DefaultImmutableWorkspaceProvider.withExternalHistory(cacheBuilder, fileAccessTimeJournal, executionHistoryStore, cacheConfigurations);
-        this.identityCache = identityCache;
-    }
-
-    @Override
-    public DefaultImmutableWorkspaceProvider getWorkspaceProvider() {
-        return workspaceProvider;
-    }
-
-    @Override
-    public CrossBuildInMemoryCache<UnitOfWork.Identity, Try<TransformationResult>> getIdentityCache() {
-        return identityCache;
-    }
-
-    @Override
-    public void close() {
-        workspaceProvider.close();
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/MutableTransformWorkspaceServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/MutableTransformWorkspaceServices.java
new file mode 100644
index 0000000..4b9b708
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/MutableTransformWorkspaceServices.java
@@ -0,0 +1,69 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.file.Directory;
+import org.gradle.api.file.FileSystemLocation;
+import org.gradle.api.provider.Provider;
+import org.gradle.cache.Cache;
+import org.gradle.cache.ManualEvictionInMemoryCache;
+import org.gradle.internal.Try;
+import org.gradle.internal.execution.UnitOfWork;
+import org.gradle.internal.execution.history.ExecutionHistoryStore;
+import org.gradle.internal.execution.workspace.WorkspaceProvider;
+import org.gradle.internal.file.ReservedFileSystemLocation;
+
+import javax.annotation.concurrent.NotThreadSafe;
+import java.io.File;
+
+@NotThreadSafe
+public class MutableTransformWorkspaceServices implements TransformWorkspaceServices, ReservedFileSystemLocation {
+
+    private final Cache<UnitOfWork.Identity, Try<TransformExecutionResult>> identityCache = new ManualEvictionInMemoryCache<>();
+    private final Provider<Directory> baseDirectory;
+    private final WorkspaceProvider workspaceProvider;
+    private final ExecutionHistoryStore executionHistoryStore;
+
+    public MutableTransformWorkspaceServices(Provider<Directory> baseDirectory, ExecutionHistoryStore executionHistoryStore) {
+        this.baseDirectory = baseDirectory;
+        this.workspaceProvider = new MutableTransformWorkspaceProvider();
+        this.executionHistoryStore = executionHistoryStore;
+    }
+
+    @Override
+    public WorkspaceProvider getWorkspaceProvider() {
+        return workspaceProvider;
+    }
+
+    @Override
+    public Cache<UnitOfWork.Identity, Try<TransformExecutionResult>> getIdentityCache() {
+        return identityCache;
+    }
+
+    @Override
+    public Provider<? extends FileSystemLocation> getReservedFileSystemLocation() {
+        return baseDirectory;
+    }
+
+    private class MutableTransformWorkspaceProvider implements WorkspaceProvider {
+        @Override
+        public <T> T withWorkspace(String path, WorkspaceAction<T> action) {
+            File workspaceDir = new File(baseDirectory.get().getAsFile(), path);
+            return action.executeInWorkspace(workspaceDir, executionHistoryStore);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/MutableTransformationWorkspaceServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/MutableTransformationWorkspaceServices.java
deleted file mode 100644
index bcbc098..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/MutableTransformationWorkspaceServices.java
+++ /dev/null
@@ -1,69 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.file.Directory;
-import org.gradle.api.file.FileSystemLocation;
-import org.gradle.api.provider.Provider;
-import org.gradle.cache.Cache;
-import org.gradle.cache.ManualEvictionInMemoryCache;
-import org.gradle.internal.Try;
-import org.gradle.internal.execution.UnitOfWork;
-import org.gradle.internal.execution.history.ExecutionHistoryStore;
-import org.gradle.internal.execution.workspace.WorkspaceProvider;
-import org.gradle.internal.file.ReservedFileSystemLocation;
-
-import javax.annotation.concurrent.NotThreadSafe;
-import java.io.File;
-
-@NotThreadSafe
-public class MutableTransformationWorkspaceServices implements TransformationWorkspaceServices, ReservedFileSystemLocation {
-
-    private final Cache<UnitOfWork.Identity, Try<TransformationResult>> identityCache = new ManualEvictionInMemoryCache<>();
-    private final Provider<Directory> baseDirectory;
-    private final WorkspaceProvider workspaceProvider;
-    private final ExecutionHistoryStore executionHistoryStore;
-
-    public MutableTransformationWorkspaceServices(Provider<Directory> baseDirectory, ExecutionHistoryStore executionHistoryStore) {
-        this.baseDirectory = baseDirectory;
-        this.workspaceProvider = new MutableTransformationWorkspaceProvider();
-        this.executionHistoryStore = executionHistoryStore;
-    }
-
-    @Override
-    public WorkspaceProvider getWorkspaceProvider() {
-        return workspaceProvider;
-    }
-
-    @Override
-    public Cache<UnitOfWork.Identity, Try<TransformationResult>> getIdentityCache() {
-        return identityCache;
-    }
-
-    @Override
-    public Provider<? extends FileSystemLocation> getReservedFileSystemLocation() {
-        return baseDirectory;
-    }
-
-    private class MutableTransformationWorkspaceProvider implements WorkspaceProvider {
-        @Override
-        public <T> T withWorkspace(String path, WorkspaceAction<T> action) {
-            File workspaceDir = new File(baseDirectory.get().getAsFile(), path);
-            return action.executeInWorkspace(workspaceDir, executionHistoryStore);
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ToPlannedTransformConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ToPlannedTransformConverter.java
deleted file mode 100644
index 926bed5..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ToPlannedTransformConverter.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.transform;
-
-import org.gradle.execution.plan.Node;
-import org.gradle.execution.plan.ToPlannedNodeConverter;
-import org.gradle.execution.plan.ToPlannedTaskConverter;
-import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType;
-
-/**
- * A {@link ToPlannedTaskConverter} for {@link TransformationNode}s.
- */
-public class ToPlannedTransformConverter implements ToPlannedNodeConverter {
-
-    @Override
-    public Class<? extends Node> getSupportedNodeType() {
-        return TransformationNode.class;
-    }
-
-    @Override
-    public TransformationIdentity getNodeIdentity(Node node) {
-        TransformationNode transformationNode = (TransformationNode) node;
-        return transformationNode.getNodeIdentity();
-    }
-
-    @Override
-    public boolean isInSamePlan(Node node) {
-        return true;
-    }
-
-    @Override
-    public CalculateTaskGraphBuildOperationType.PlannedNode convert(Node node, DependencyLookup dependencyLookup) {
-        TransformationNode transformationNode = (TransformationNode) node;
-        return new DefaultPlannedTransform(
-            transformationNode.getNodeIdentity(),
-            dependencyLookup.findNodeDependencies(transformationNode)
-        );
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ToPlannedTransformStepConverter.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ToPlannedTransformStepConverter.java
new file mode 100644
index 0000000..e90409a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ToPlannedTransformStepConverter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.transform;
+
+import org.gradle.api.NonNullApi;
+import org.gradle.execution.plan.Node;
+import org.gradle.execution.plan.ToPlannedNodeConverter;
+import org.gradle.internal.taskgraph.CalculateTaskGraphBuildOperationType.PlannedNode;
+import org.gradle.internal.taskgraph.NodeIdentity;
+import org.gradle.operations.dependencies.transforms.PlannedTransformStepIdentity;
+
+import java.util.List;
+
+/**
+ * A converter from {@link TransformStepNode} to {@link PlannedNode}.
+ */
+@NonNullApi
+public class ToPlannedTransformStepConverter implements ToPlannedNodeConverter {
+
+    @Override
+    public Class<? extends Node> getSupportedNodeType() {
+        return TransformStepNode.class;
+    }
+
+    @Override
+    public NodeIdentity.NodeType getConvertedNodeType() {
+        return NodeIdentity.NodeType.TRANSFORM_STEP;
+    }
+
+    @Override
+    public PlannedTransformStepIdentity getNodeIdentity(Node node) {
+        TransformStepNode transformStepNode = (TransformStepNode) node;
+        return transformStepNode.getNodeIdentity();
+    }
+
+    @Override
+    public boolean isInSamePlan(Node node) {
+        return true;
+    }
+
+    @Override
+    public DefaultPlannedTransformStep convert(Node node, List<? extends NodeIdentity> nodeDependencies) {
+        TransformStepNode transformStepNode = (TransformStepNode) node;
+        return new DefaultPlannedTransformStep(transformStepNode.getNodeIdentity(), nodeDependencies);
+    }
+
+    @Override
+    public String toString() {
+        return "ToPlannedTransformStepConverter(" + getSupportedNodeType().getSimpleName() + ")";
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transform.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transform.java
new file mode 100644
index 0000000..371ed40
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transform.java
@@ -0,0 +1,84 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.Describable;
+import org.gradle.api.file.FileSystemLocation;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.api.internal.tasks.TaskDependencyContainer;
+import org.gradle.api.provider.Provider;
+import org.gradle.internal.fingerprint.DirectorySensitivity;
+import org.gradle.internal.fingerprint.FileNormalizer;
+import org.gradle.internal.fingerprint.LineEndingSensitivity;
+import org.gradle.internal.hash.HashCode;
+import org.gradle.work.InputChanges;
+
+import javax.annotation.Nullable;
+import java.io.File;
+
+/**
+ * The actual code which needs to be executed to transform a file.
+ *
+ * This encapsulates the public interface {@link org.gradle.api.artifacts.transform.TransformAction} into an internal type.
+ */
+public interface Transform extends Describable, TaskDependencyContainer {
+    Class<?> getImplementationClass();
+
+    ImmutableAttributes getFromAttributes();
+
+    ImmutableAttributes getToAttributes();
+
+    /**
+     * Whether the transformer requires dependencies of the transformed artifact to be injected.
+     */
+    boolean requiresDependencies();
+
+    /**
+     * Whether the transformer requires {@link InputChanges} to be injected.
+     */
+    boolean requiresInputChanges();
+
+    /**
+     * Whether the transformer is cacheable.
+     */
+    boolean isCacheable();
+
+    TransformExecutionResult transform(Provider<FileSystemLocation> inputArtifactProvider, File outputDir, TransformDependencies dependencies, @Nullable InputChanges inputChanges);
+
+    /**
+     * The hash of the secondary inputs of the transformer.
+     *
+     * This includes the parameters and the implementation.
+     */
+    HashCode getSecondaryInputHash();
+
+    void isolateParametersIfNotAlready();
+
+    FileNormalizer getInputArtifactNormalizer();
+
+    FileNormalizer getInputArtifactDependenciesNormalizer();
+
+    boolean isIsolated();
+
+    DirectorySensitivity getInputArtifactDirectorySensitivity();
+
+    DirectorySensitivity getInputArtifactDependenciesDirectorySensitivity();
+
+    LineEndingSensitivity getInputArtifactLineEndingNormalization();
+
+    LineEndingSensitivity getInputArtifactDependenciesLineEndingNormalization();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformActionScheme.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformActionScheme.java
new file mode 100644
index 0000000..5b12b67
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformActionScheme.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 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.api.internal.artifacts.transform;
+
+import org.gradle.api.artifacts.transform.TransformAction;
+import org.gradle.api.internal.tasks.properties.InspectionScheme;
+import org.gradle.api.internal.tasks.properties.TypeScheme;
+import org.gradle.internal.instantiation.InstantiationScheme;
+import org.gradle.internal.properties.annotations.TypeMetadataStore;
+
+public class TransformActionScheme implements TypeScheme {
+    private final InstantiationScheme instantiationScheme;
+    private final InspectionScheme inspectionScheme;
+
+    public TransformActionScheme(InstantiationScheme instantiationScheme, InspectionScheme inspectionScheme) {
+        this.instantiationScheme = instantiationScheme;
+        this.inspectionScheme = inspectionScheme;
+    }
+
+    @Override
+    public TypeMetadataStore getMetadataStore() {
+        return inspectionScheme.getMetadataStore();
+    }
+
+    @Override
+    public boolean appliesTo(Class<?> type) {
+        return TransformAction.class.isAssignableFrom(type);
+    }
+
+    public InspectionScheme getInspectionScheme() {
+        return inspectionScheme;
+    }
+
+    public InstantiationScheme getInstantiationScheme() {
+        return instantiationScheme;
+    }
+}
+
+
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformChain.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformChain.java
new file mode 100644
index 0000000..5432301
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformChain.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 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.api.internal.artifacts.transform;
+
+import org.gradle.api.Action;
+
+import javax.annotation.Nullable;
+
+/**
+ * A series of {@link TransformStep}s.
+ */
+public class TransformChain {
+
+    private final TransformChain init;
+    private final TransformStep last;
+
+    /**
+     * @param init The initial steps of this chain, or null if this chain only contains one step.
+     * @param last The last step of this chain.
+     */
+    public TransformChain(@Nullable TransformChain init, TransformStep last) {
+        this.init = init;
+        this.last = last;
+    }
+
+    /**
+     * @return The initial steps of this chain, or null if this chain only contains one step.
+     */
+    @Nullable
+    public TransformChain getInit() {
+        return init;
+    }
+
+    /**
+     * @return The last step of this chain.
+     */
+    public TransformStep getLast() {
+        return last;
+    }
+
+    public boolean requiresDependencies() {
+        return (init != null && init.requiresDependencies()) || last.requiresDependencies();
+    }
+
+    public String getDisplayName() {
+        String lastDisplayName = last.getDisplayName();
+        return init == null
+            ? lastDisplayName
+            : init.getDisplayName() + " -> " + lastDisplayName;
+    }
+
+    public void visitTransformSteps(Action<? super TransformStep> action) {
+        if (init != null) {
+            init.visitTransformSteps(action);
+        }
+        action.execute(last);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformDependencies.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformDependencies.java
new file mode 100644
index 0000000..3232652
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformDependencies.java
@@ -0,0 +1,29 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.file.FileCollection;
+
+import java.util.Optional;
+
+public interface TransformDependencies {
+    /**
+     * Returns the dependency artifacts of the artifact being transformed.
+     * The order of the files match that of the dependencies in the source artifact view.
+     */
+    Optional<FileCollection> getFiles();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionResult.java
new file mode 100644
index 0000000..f3d0e3e
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionResult.java
@@ -0,0 +1,276 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.file.RelativePath;
+
+import java.io.File;
+import java.util.function.Consumer;
+
+/**
+ * The result of running a single transform action on a single input artifact.
+ *
+ * The result of running a transform is a list of outputs.
+ * There are two kinds of outputs for a transform:
+ * - Produced outputs in the workspace. Those are absolute paths which do not change depending on the input artifact.
+ * - Selected parts of the input artifact. These are relative paths of locations selected in the input artifact.
+ */
+public interface TransformExecutionResult {
+    /**
+     * Resolves location of the outputs of this result for a given input artifact.
+     *
+     * Produced outputs don't need to be resolved to locations, since they are absolute paths and can be returned as is.
+     * The relative paths of selected parts of the input artifact need to resolved based on the provided input artifact location.
+     */
+    ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact);
+
+    void visitOutputs(OutputVisitor visitor);
+
+    int size();
+
+    static OutputTypeInferringBuilder builderFor(File inputArtifact, File outputDir) {
+        return new OutputTypeInferringBuilder(inputArtifact, outputDir);
+    }
+
+    static Builder builder() {
+        return new Builder();
+    }
+
+    class Builder {
+        private final ImmutableList.Builder<TransformOutput> builder = ImmutableList.builder();
+        private boolean onlyProducedOutputs = true;
+
+        public void addEntireInputArtifact() {
+            onlyProducedOutputs = false;
+            builder.add(EntireInputArtifact.INSTANCE);
+        }
+
+        public void addPartOfInputArtifact(String relativePath) {
+            onlyProducedOutputs = false;
+            builder.add(new PartOfInputArtifact(relativePath));
+        }
+
+        public void addProducedOutput(File outputLocation) {
+            builder.add(new ProducedOutput(outputLocation));
+        }
+
+        public TransformExecutionResult build() {
+            ImmutableList<TransformOutput> transformOutputs = builder.build();
+            return onlyProducedOutputs
+                ? new ProducedOutputOnlyResult(convertToProducedOutputLocations(transformOutputs))
+                : new FilteredResult(transformOutputs);
+        }
+
+        private static ImmutableList<File> convertToProducedOutputLocations(ImmutableList<TransformOutput> transformOutputs) {
+            ImmutableList.Builder<File> builder = new ImmutableList.Builder<>();
+            transformOutputs.forEach(output -> builder.add(((ProducedOutput) output).getOutputLocation()));
+            return builder.build();
+        }
+
+        /**
+         * Optimized variant for a transform whose results are all produced by the transform,
+         * and don't include any of its input artifact.
+         */
+        private static class ProducedOutputOnlyResult implements TransformExecutionResult {
+            private final ImmutableList<File> producedOutputLocations;
+
+            public ProducedOutputOnlyResult(ImmutableList<File> producedOutputLocations) {
+                this.producedOutputLocations = producedOutputLocations;
+            }
+
+            @Override
+            public ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact) {
+                return producedOutputLocations;
+            }
+
+            @Override
+            public void visitOutputs(OutputVisitor visitor) {
+                producedOutputLocations.forEach(visitor::visitProducedOutput);
+            }
+
+            @Override
+            public int size() {
+                return producedOutputLocations.size();
+            }
+        }
+
+        /**
+         * Results of a transform that includes parts or the whole of its input artifact.
+         * It might also include outputs produced by the transform.
+         */
+        private static class FilteredResult implements TransformExecutionResult {
+            private final ImmutableList<TransformOutput> transformOutputs;
+
+            public FilteredResult(ImmutableList<TransformOutput> transformOutputs) {
+                this.transformOutputs = transformOutputs;
+            }
+
+            @Override
+            public ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact) {
+                ImmutableList.Builder<File> builder = ImmutableList.builderWithExpectedSize(transformOutputs.size());
+                transformOutputs.forEach(output -> builder.add(output.resolveForInputArtifact(inputArtifact)));
+                return builder.build();
+            }
+
+            @Override
+            public void visitOutputs(OutputVisitor visitor) {
+                transformOutputs.forEach(output -> output.visitOutput(visitor));
+            }
+
+            @Override
+            public int size() {
+                return transformOutputs.size();
+            }
+        }
+
+        /**
+         * A single output in a transform result.
+         *
+         * Can be either
+         * - the entire input artifact {@link EntireInputArtifact}
+         * - a part of the input artifact {@link PartOfInputArtifact}
+         * - a produced output in the workspace {@link ProducedOutput}
+         *
+         * Only outputs related to the input artifact need resolving.
+         */
+        private interface TransformOutput {
+            File resolveForInputArtifact(File inputArtifact);
+
+            void visitOutput(OutputVisitor visitor);
+        }
+
+        private static class PartOfInputArtifact implements TransformOutput {
+            private final String relativePath;
+
+            public PartOfInputArtifact(String relativePath) {
+                this.relativePath = relativePath;
+            }
+
+            @Override
+            public File resolveForInputArtifact(File inputArtifact) {
+                return new File(inputArtifact, relativePath);
+            }
+
+            @Override
+            public void visitOutput(OutputVisitor visitor) {
+                visitor.visitPartOfInputArtifact(relativePath);
+            }
+        }
+
+        private static class EntireInputArtifact implements TransformOutput {
+            public static final EntireInputArtifact INSTANCE = new EntireInputArtifact();
+
+            @Override
+            public File resolveForInputArtifact(File inputArtifact) {
+                return inputArtifact;
+            }
+
+            @Override
+            public void visitOutput(OutputVisitor visitor) {
+                visitor.visitEntireInputArtifact();
+            }
+        }
+
+        private static class ProducedOutput implements TransformOutput {
+            private final File outputFile;
+
+            public ProducedOutput(File outputFile) {
+                this.outputFile = outputFile;
+            }
+
+            public File getOutputLocation() {
+                return outputFile;
+            }
+
+            @Override
+            public File resolveForInputArtifact(File inputArtifact) {
+                return outputFile;
+            }
+
+            @Override
+            public void visitOutput(OutputVisitor visitor) {
+                visitor.visitProducedOutput(outputFile);
+            }
+        }
+    }
+
+    interface OutputVisitor {
+        /**
+         * Called when the result is the full input artifact.
+         */
+        void visitEntireInputArtifact();
+
+        /**
+         * Called when the result is inside the input artifact.
+         *
+         * @param relativePath the relative path from the input artifact to the selected location in the input artifact.
+         */
+        void visitPartOfInputArtifact(String relativePath);
+
+        /**
+         * Called when the result is a produced output in the workspace.
+         *
+         * @param outputLocation the absolute {@link File} location of the output in the workspace.
+         */
+        void visitProducedOutput(File outputLocation);
+    }
+
+    /**
+     * A {@link TransformExecutionResult} builder which accepts absolute locations of results.
+     * <p>
+     * The builder then infers if the result is (in) the input artifact or a produced output in the workspace.
+     */
+    class OutputTypeInferringBuilder {
+        private final File inputArtifact;
+        private final File workspaceDir;
+        private final String inputArtifactPrefix;
+        private final String workspaceDirPrefix;
+        private final Builder delegate = TransformExecutionResult.builder();
+
+        public OutputTypeInferringBuilder(File inputArtifact, File workspaceDir) {
+            this.inputArtifact = inputArtifact;
+            this.workspaceDir = workspaceDir;
+            this.inputArtifactPrefix = inputArtifact.getPath() + File.separator;
+            this.workspaceDirPrefix = workspaceDir.getPath() + File.separator;
+        }
+
+        /**
+         * Adds an output location to the result.
+         *
+         * @param workspaceAction an action to run when the output is a produced output in the workspace.
+         */
+        public void addOutput(File output, Consumer<File> workspaceAction) {
+            if (output.equals(inputArtifact)) {
+                delegate.addEntireInputArtifact();
+            } else if (output.equals(workspaceDir) || output.getPath().startsWith(workspaceDirPrefix)) {
+                delegate.addProducedOutput(output);
+                workspaceAction.accept(output);
+            } else if (output.getPath().startsWith(inputArtifactPrefix)) {
+                String relativePath = RelativePath.parse(true, output.getPath().substring(inputArtifactPrefix.length())).getPathString();
+                delegate.addPartOfInputArtifact(relativePath);
+            } else {
+                throw new InvalidUserDataException("Transform output " + output.getPath() + " must be a part of the input artifact or refer to a relative path.");
+            }
+        }
+
+        public TransformExecutionResult build() {
+            return delegate.build();
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionResultSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionResultSerializer.java
new file mode 100644
index 0000000..84bf6f3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformExecutionResultSerializer.java
@@ -0,0 +1,95 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.UncheckedIOException;
+import org.gradle.api.file.RelativePath;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.gradle.internal.UncheckedException.unchecked;
+
+public class TransformExecutionResultSerializer {
+    private static final String INPUT_FILE_PATH_PREFIX = "i/";
+    private static final String OUTPUT_FILE_PATH_PREFIX = "o/";
+
+    private final File outputDir;
+
+    public TransformExecutionResultSerializer(File outputDir) {
+        this.outputDir = outputDir;
+    }
+
+    public void writeToFile(File target, TransformExecutionResult result) {
+        String outputDirPrefix = outputDir.getPath() + File.separator;
+        List<String> resultFileContents = new ArrayList<>(result.size());
+
+        result.visitOutputs(new TransformExecutionResult.OutputVisitor() {
+            @Override
+            public void visitEntireInputArtifact() {
+                resultFileContents.add(INPUT_FILE_PATH_PREFIX);
+            }
+
+            @Override
+            public void visitPartOfInputArtifact(String relativePath) {
+                resultFileContents.add(INPUT_FILE_PATH_PREFIX + relativePath);
+            }
+
+            @Override
+            public void visitProducedOutput(File outputLocation) {
+                if (outputLocation.equals(outputDir)) {
+                    resultFileContents.add(OUTPUT_FILE_PATH_PREFIX);
+                } else {
+                    resultFileContents.add(OUTPUT_FILE_PATH_PREFIX + RelativePath.parse(true,
+                        outputLocation.getAbsolutePath().substring(outputDirPrefix.length())
+                    ).getPathString());
+                }
+            }
+        });
+        unchecked(() -> Files.write(target.toPath(), resultFileContents));
+    }
+
+    public TransformExecutionResult readResultsFile(File resultsFile) {
+        Path transformerResultsPath = resultsFile.toPath();
+        try {
+            TransformExecutionResult.Builder builder = TransformExecutionResult.builder();
+            List<String> paths = Files.readAllLines(transformerResultsPath, StandardCharsets.UTF_8);
+            for (String path : paths) {
+                if (path.startsWith(OUTPUT_FILE_PATH_PREFIX)) {
+                    builder.addProducedOutput(new File(outputDir, path.substring(2)));
+                } else if (path.startsWith(INPUT_FILE_PATH_PREFIX)) {
+                    String relativePathString = path.substring(2);
+                    if (relativePathString.isEmpty()) {
+                        builder.addEntireInputArtifact();
+                    } else {
+                        builder.addPartOfInputArtifact(relativePathString);
+                    }
+                } else {
+                    throw new IllegalStateException("Cannot parse result path string: " + path);
+                }
+            }
+            return builder.build();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformInvocationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformInvocationFactory.java
new file mode 100644
index 0000000..c4b619b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformInvocationFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 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.api.internal.artifacts.transform;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.internal.Deferrable;
+import org.gradle.internal.Try;
+import org.gradle.internal.execution.InputFingerprinter;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.io.File;
+
+@ThreadSafe
+public interface TransformInvocationFactory {
+    /**
+     * Returns an invocation which allows invoking the actual transformer.
+     */
+    Deferrable<Try<ImmutableList<File>>> createInvocation(
+        Transform transform,
+        File inputArtifact,
+        TransformDependencies dependencies,
+        TransformStepSubject subject,
+        InputFingerprinter inputFingerprinter);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformOutputsInternal.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformOutputsInternal.java
index 9a170d7..e09cdf6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformOutputsInternal.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformOutputsInternal.java
@@ -20,5 +20,5 @@
 
 public interface TransformOutputsInternal extends TransformOutputs {
 
-    TransformationResult getRegisteredOutputs();
+    TransformExecutionResult getRegisteredOutputs();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformParameterScheme.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformParameterScheme.java
new file mode 100644
index 0000000..2f2854d
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformParameterScheme.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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.api.internal.artifacts.transform;
+
+import org.gradle.api.artifacts.transform.TransformParameters;
+import org.gradle.api.internal.tasks.properties.InspectionScheme;
+import org.gradle.api.internal.tasks.properties.TypeScheme;
+import org.gradle.internal.instantiation.InstantiationScheme;
+import org.gradle.internal.properties.annotations.TypeMetadataStore;
+
+public class TransformParameterScheme implements TypeScheme {
+    private final InstantiationScheme instantiationScheme;
+    private final InspectionScheme inspectionScheme;
+
+    public TransformParameterScheme(InstantiationScheme instantiationScheme, InspectionScheme inspectionScheme) {
+        this.instantiationScheme = instantiationScheme;
+        this.inspectionScheme = inspectionScheme;
+    }
+
+    @Override
+    public TypeMetadataStore getMetadataStore() {
+        return inspectionScheme.getMetadataStore();
+    }
+
+    @Override
+    public boolean appliesTo(Class<?> type) {
+        return TransformParameters.class.isAssignableFrom(type);
+    }
+
+    public InstantiationScheme getInstantiationScheme() {
+        return instantiationScheme;
+    }
+
+    public InspectionScheme getInspectionScheme() {
+        return inspectionScheme;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformRegistrationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformRegistrationFactory.java
new file mode 100644
index 0000000..00caa30
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformRegistrationFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 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.api.internal.artifacts.transform;
+
+import org.gradle.api.artifacts.transform.TransformAction;
+import org.gradle.api.artifacts.transform.TransformParameters;
+import org.gradle.api.internal.artifacts.TransformRegistration;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+
+import javax.annotation.Nullable;
+
+public interface TransformRegistrationFactory {
+    TransformRegistration create(ImmutableAttributes from, ImmutableAttributes to, Class<? extends TransformAction<?>> implementation, @Nullable TransformParameters parameterObject);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStep.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStep.java
new file mode 100644
index 0000000..b6b7c76
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStep.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2017 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.api.internal.artifacts.transform;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.Describable;
+import org.gradle.api.internal.DomainObjectContext;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.NodeExecutionContext;
+import org.gradle.api.internal.tasks.TaskDependencyContainer;
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
+import org.gradle.internal.Cast;
+import org.gradle.internal.Deferrable;
+import org.gradle.internal.Try;
+import org.gradle.internal.execution.InputFingerprinter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.io.File;
+
+/**
+ * A single transform step in a transform chain.
+ * <p>
+ * Transforms a subject by invoking a transform on each of the subjects files.
+ *
+ * @see TransformChain
+ */
+public class TransformStep implements TaskDependencyContainer, Describable {
+    private static final Logger LOGGER = LoggerFactory.getLogger(TransformStep.class);
+
+    private final Transform transform;
+    private final TransformInvocationFactory transformInvocationFactory;
+    private final ProjectInternal owningProject;
+    private final InputFingerprinter globalInputFingerprinter;
+
+    public TransformStep(Transform transform, TransformInvocationFactory transformInvocationFactory, DomainObjectContext owner, InputFingerprinter globalInputFingerprinter) {
+        this.transform = transform;
+        this.transformInvocationFactory = transformInvocationFactory;
+        this.globalInputFingerprinter = globalInputFingerprinter;
+        this.owningProject = owner.getProject();
+    }
+
+    public Transform getTransform() {
+        return transform;
+    }
+
+    @Nullable
+    public ProjectInternal getOwningProject() {
+        return owningProject;
+    }
+
+    public Deferrable<Try<TransformStepSubject>> createInvocation(TransformStepSubject subjectToTransform, TransformUpstreamDependencies upstreamDependencies, @Nullable NodeExecutionContext context) {
+        if (LOGGER.isInfoEnabled()) {
+            LOGGER.info("Transforming {} with {}", subjectToTransform.getDisplayName(), transform.getDisplayName());
+        }
+
+        InputFingerprinter inputFingerprinter = context != null ? context.getService(InputFingerprinter.class) : globalInputFingerprinter;
+
+        Try<TransformDependencies> resolvedDependencies = upstreamDependencies.computeArtifacts();
+        return resolvedDependencies
+            .map(dependencies -> {
+                ImmutableList<File> inputArtifacts = subjectToTransform.getFiles();
+                if (inputArtifacts.isEmpty()) {
+                    return Deferrable.completed(Try.successful(subjectToTransform.createSubjectFromResult(ImmutableList.of())));
+                } else if (inputArtifacts.size() > 1) {
+                    return Deferrable.deferred(() ->
+                        doTransform(subjectToTransform, inputFingerprinter, dependencies, inputArtifacts)
+                    );
+                } else {
+                    File inputArtifact = inputArtifacts.get(0);
+                    return transformInvocationFactory.createInvocation(transform, inputArtifact, dependencies, subjectToTransform, inputFingerprinter)
+                        .map(result -> result.map(subjectToTransform::createSubjectFromResult));
+                }
+            })
+            .getOrMapFailure(failure -> Deferrable.completed(Try.failure(failure)));
+    }
+
+    private Try<TransformStepSubject> doTransform(TransformStepSubject subjectToTransform, InputFingerprinter inputFingerprinter, TransformDependencies dependencies, ImmutableList<File> inputArtifacts) {
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        for (File inputArtifact : inputArtifacts) {
+            Try<ImmutableList<File>> result = transformInvocationFactory
+                .createInvocation(transform, inputArtifact, dependencies, subjectToTransform, inputFingerprinter)
+                .completeAndGet();
+
+            if (result.getFailure().isPresent()) {
+                return Cast.uncheckedCast(result);
+            }
+            builder.addAll(result.get());
+        }
+        return Try.successful(subjectToTransform.createSubjectFromResult(builder.build()));
+    }
+
+    public void isolateParametersIfNotAlready() {
+        transform.isolateParametersIfNotAlready();
+    }
+
+    public boolean requiresDependencies() {
+        return transform.requiresDependencies();
+    }
+
+    @Override
+    public String getDisplayName() {
+        return transform.getDisplayName();
+    }
+
+    public ImmutableAttributes getFromAttributes() {
+        return transform.getFromAttributes();
+    }
+
+    @Override
+    public String toString() {
+        return transform.getDisplayName();
+    }
+
+    @Override
+    public void visitDependencies(TaskDependencyResolveContext context) {
+        transform.visitDependencies(context);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNode.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNode.java
new file mode 100644
index 0000000..3ed0aba
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNode.java
@@ -0,0 +1,441 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.Describable;
+import org.gradle.api.artifacts.ResolveException;
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
+import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.NodeExecutionContext;
+import org.gradle.api.internal.tasks.TaskDependencyContainer;
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
+import org.gradle.execution.plan.CreationOrderedNode;
+import org.gradle.execution.plan.Node;
+import org.gradle.execution.plan.SelfExecutingNode;
+import org.gradle.execution.plan.TaskDependencyResolver;
+import org.gradle.internal.Describables;
+import org.gradle.internal.Try;
+import org.gradle.internal.model.CalculatedValueContainer;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
+import org.gradle.internal.model.ValueCalculator;
+import org.gradle.internal.operations.BuildOperationCategory;
+import org.gradle.internal.operations.BuildOperationContext;
+import org.gradle.internal.operations.BuildOperationDescriptor;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.operations.CallableBuildOperation;
+import org.gradle.internal.scan.UsedByScanPlugin;
+import org.gradle.operations.dependencies.transforms.ExecutePlannedTransformStepBuildOperationType;
+import org.gradle.operations.dependencies.transforms.PlannedTransformStepIdentity;
+import org.gradle.operations.dependencies.variants.Capability;
+import org.gradle.operations.dependencies.variants.ComponentIdentifier;
+import org.gradle.operations.dependencies.variants.OpaqueComponentIdentifier;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public abstract class TransformStepNode extends CreationOrderedNode implements SelfExecutingNode {
+
+    protected final TransformStep transformStep;
+    protected final ResolvableArtifact artifact;
+    private final ComponentVariantIdentifier targetComponentVariant;
+    private final AttributeContainer sourceAttributes;
+    protected final TransformUpstreamDependencies upstreamDependencies;
+    private final long transformStepNodeId;
+
+    private PlannedTransformStepIdentity cachedIdentity;
+
+    protected TransformStepNode(
+        long transformStepNodeId,
+        ComponentVariantIdentifier targetComponentVariant,
+        AttributeContainer sourceAttributes,
+        TransformStep transformStep,
+        ResolvableArtifact artifact,
+        TransformUpstreamDependencies upstreamDependencies
+    ) {
+        this.targetComponentVariant = targetComponentVariant;
+        this.sourceAttributes = sourceAttributes;
+        this.transformStep = transformStep;
+        this.artifact = artifact;
+        this.upstreamDependencies = upstreamDependencies;
+        this.transformStepNodeId = transformStepNodeId;
+    }
+
+    public long getTransformStepNodeId() {
+        return transformStepNodeId;
+    }
+
+    public ComponentVariantIdentifier getTargetComponentVariant() {
+        return targetComponentVariant;
+    }
+
+    public AttributeContainer getSourceAttributes() {
+        return sourceAttributes;
+    }
+
+    public PlannedTransformStepIdentity getNodeIdentity() {
+        if (cachedIdentity == null) {
+            cachedIdentity = createIdentity();
+        }
+        return cachedIdentity;
+    }
+
+    private PlannedTransformStepIdentity createIdentity() {
+        String consumerBuildPath = transformStep.getOwningProject().getBuildPath().toString();
+        String consumerProjectPath = transformStep.getOwningProject().getProjectPath().toString();
+        ComponentIdentifier componentId = getComponentIdentifier(targetComponentVariant.getComponentId());
+        Map<String, String> sourceAttributes = AttributesToMapConverter.convertToMap(this.sourceAttributes);
+        Map<String, String> targetAttributes = AttributesToMapConverter.convertToMap(targetComponentVariant.getAttributes());
+        List<Capability> capabilities = targetComponentVariant.getCapabilities().stream()
+            .map(TransformStepNode::convertCapability)
+            .collect(Collectors.toList());
+
+        return new DefaultPlannedTransformStepIdentity(
+            consumerBuildPath,
+            consumerProjectPath,
+            componentId,
+            sourceAttributes,
+            targetAttributes,
+            capabilities,
+            artifact.getArtifactName().toString(),
+            upstreamDependencies.getConfigurationIdentity(),
+            transformStepNodeId
+        );
+    }
+
+    private static Capability convertCapability(org.gradle.api.capabilities.Capability capability) {
+        return new Capability() {
+            @Override
+            public String getGroup() {
+                return capability.getGroup();
+            }
+
+            @Override
+            public String getName() {
+                return capability.getName();
+            }
+
+            @Override
+            public String getVersion() {
+                return capability.getVersion();
+            }
+
+            @Override
+            public String toString() {
+                return getGroup() + ":" + getName() + (getVersion() == null ? "" : (":" + getVersion()));
+            }
+        };
+    }
+
+    private static ComponentIdentifier getComponentIdentifier(org.gradle.api.artifacts.component.ComponentIdentifier componentId) {
+        if (componentId instanceof ProjectComponentIdentifier) {
+            ProjectComponentIdentifier projectComponentIdentifier = (ProjectComponentIdentifier) componentId;
+            return new org.gradle.operations.dependencies.variants.ProjectComponentIdentifier() {
+                @Override
+                public String getBuildPath() {
+                    return projectComponentIdentifier.getBuild().getBuildPath();
+                }
+
+                @Override
+                public String getProjectPath() {
+                    return projectComponentIdentifier.getProjectPath();
+                }
+
+                @Override
+                public String toString() {
+                    return projectComponentIdentifier.getDisplayName();
+                }
+            };
+        } else if (componentId instanceof ModuleComponentIdentifier) {
+            ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier) componentId;
+            return new org.gradle.operations.dependencies.variants.ModuleComponentIdentifier() {
+                @Override
+                public String getGroup() {
+                    return moduleComponentIdentifier.getGroup();
+                }
+
+                @Override
+                public String getModule() {
+                    return moduleComponentIdentifier.getModule();
+                }
+
+                @Override
+                public String getVersion() {
+                    return moduleComponentIdentifier.getVersion();
+                }
+
+                @Override
+                public String toString() {
+                    return moduleComponentIdentifier.getDisplayName();
+                }
+            };
+        } else {
+            return new OpaqueComponentIdentifier() {
+                @Override
+                public String getDisplayName() {
+                    return componentId.getDisplayName();
+                }
+
+                @Override
+                public String getClassName() {
+                    return componentId.getClass().getName();
+                }
+
+                @Override
+                public String toString() {
+                    return componentId.getDisplayName();
+                }
+            };
+        }
+    }
+
+    public ResolvableArtifact getInputArtifact() {
+        return artifact;
+    }
+
+    public TransformUpstreamDependencies getUpstreamDependencies() {
+        return upstreamDependencies;
+    }
+
+    @Nullable
+    @Override
+    public ProjectInternal getOwningProject() {
+        return transformStep.getOwningProject();
+    }
+
+    @Override
+    public boolean isPublicNode() {
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return transformStep.getDisplayName();
+    }
+
+    public TransformStep getTransformStep() {
+        return transformStep;
+    }
+
+    public Try<TransformStepSubject> getTransformedSubject() {
+        return getTransformedArtifacts().getValue();
+    }
+
+    @Override
+    public void execute(NodeExecutionContext context) {
+        getTransformedArtifacts().run(context);
+    }
+
+    public void executeIfNotAlready() {
+        transformStep.isolateParametersIfNotAlready();
+        upstreamDependencies.finalizeIfNotAlready();
+        getTransformedArtifacts().finalizeIfNotAlready();
+    }
+
+    protected abstract CalculatedValueContainer<TransformStepSubject, ?> getTransformedArtifacts();
+
+    @Override
+    public Throwable getNodeFailure() {
+        return null;
+    }
+
+    @Override
+    public void resolveDependencies(TaskDependencyResolver dependencyResolver) {
+        processDependencies(dependencyResolver.resolveDependenciesFor(null, (TaskDependencyContainer) context -> getTransformedArtifacts().visitDependencies(context)));
+    }
+
+    protected void processDependencies(Set<Node> dependencies) {
+        for (Node dependency : dependencies) {
+            addDependencySuccessor(dependency);
+        }
+    }
+
+    public static class InitialTransformStepNode extends TransformStepNode {
+        private final CalculatedValueContainer<TransformStepSubject, TransformInitialArtifact> result;
+
+        public InitialTransformStepNode(
+            long transformStepNodeId,
+            ComponentVariantIdentifier targetComponentVariant,
+            AttributeContainer sourceAttributes,
+            TransformStep transformStep,
+            ResolvableArtifact artifact,
+            TransformUpstreamDependencies upstreamDependencies,
+            BuildOperationExecutor buildOperationExecutor,
+            CalculatedValueContainerFactory calculatedValueContainerFactory
+        ) {
+            super(transformStepNodeId, targetComponentVariant, sourceAttributes, transformStep, artifact, upstreamDependencies);
+            result = calculatedValueContainerFactory.create(Describables.of(this), new TransformInitialArtifact(buildOperationExecutor));
+        }
+
+        @Override
+        protected CalculatedValueContainer<TransformStepSubject, TransformInitialArtifact> getTransformedArtifacts() {
+            return result;
+        }
+
+        private class TransformInitialArtifact implements ValueCalculator<TransformStepSubject> {
+            private final BuildOperationExecutor buildOperationExecutor;
+
+            public TransformInitialArtifact(BuildOperationExecutor buildOperationExecutor) {
+                this.buildOperationExecutor = buildOperationExecutor;
+            }
+
+            @Override
+            public void visitDependencies(TaskDependencyResolveContext context) {
+                context.add(transformStep);
+                context.add(upstreamDependencies);
+                context.add(artifact);
+            }
+
+            @Override
+            public TransformStepSubject calculateValue(NodeExecutionContext context) {
+                return buildOperationExecutor.call(new TransformStepBuildOperation() {
+                    @Override
+                    protected TransformStepSubject transform() {
+                        TransformStepSubject initialSubject;
+                        try {
+                            initialSubject = TransformStepSubject.initial(artifact);
+                        } catch (ResolveException e) {
+                            throw e;
+                        } catch (RuntimeException e) {
+                            throw new DefaultLenientConfiguration.ArtifactResolveException("artifacts", transformStep.getDisplayName(), "artifact transform", Collections.singleton(e));
+                        }
+
+                        return transformStep
+                            .createInvocation(initialSubject, upstreamDependencies, context)
+                            .completeAndGet()
+                            .get();
+                    }
+
+                    @Override
+                    protected String describeSubject() {
+                        return artifact.getId().getDisplayName();
+                    }
+                });
+            }
+        }
+    }
+
+    public static class ChainedTransformStepNode extends TransformStepNode {
+        private final TransformStepNode previousTransformStepNode;
+        private final CalculatedValueContainer<TransformStepSubject, TransformPreviousArtifacts> result;
+
+        public ChainedTransformStepNode(
+            long transformStepNodeId,
+            ComponentVariantIdentifier targetComponentVariant,
+            AttributeContainer sourceAttributes,
+            TransformStep transformStep,
+            TransformStepNode previousTransformStepNode,
+            TransformUpstreamDependencies upstreamDependencies,
+            BuildOperationExecutor buildOperationExecutor,
+            CalculatedValueContainerFactory calculatedValueContainerFactory
+        ) {
+            super(transformStepNodeId, targetComponentVariant, sourceAttributes, transformStep, previousTransformStepNode.artifact, upstreamDependencies);
+            this.previousTransformStepNode = previousTransformStepNode;
+            result = calculatedValueContainerFactory.create(Describables.of(this), new TransformPreviousArtifacts(buildOperationExecutor));
+        }
+
+        public TransformStepNode getPreviousTransformStepNode() {
+            return previousTransformStepNode;
+        }
+
+        @Override
+        protected CalculatedValueContainer<TransformStepSubject, TransformPreviousArtifacts> getTransformedArtifacts() {
+            return result;
+        }
+
+        @Override
+        public void executeIfNotAlready() {
+            // Only finalize the previous node when executing this node on demand
+            previousTransformStepNode.executeIfNotAlready();
+            super.executeIfNotAlready();
+        }
+
+        private class TransformPreviousArtifacts implements ValueCalculator<TransformStepSubject> {
+            private final BuildOperationExecutor buildOperationExecutor;
+
+            public TransformPreviousArtifacts(BuildOperationExecutor buildOperationExecutor) {
+                this.buildOperationExecutor = buildOperationExecutor;
+            }
+
+            @Override
+            public void visitDependencies(TaskDependencyResolveContext context) {
+                context.add(transformStep);
+                context.add(upstreamDependencies);
+                context.add(new DefaultTransformNodeDependency(Collections.singletonList(previousTransformStepNode)));
+            }
+
+            @Override
+            public TransformStepSubject calculateValue(NodeExecutionContext context) {
+                return buildOperationExecutor.call(new TransformStepBuildOperation() {
+                    @Override
+                    protected TransformStepSubject transform() {
+                        return previousTransformStepNode.getTransformedSubject()
+                            .flatMap(transformedSubject -> transformStep
+                                .createInvocation(transformedSubject, upstreamDependencies, context)
+                                .completeAndGet())
+                            .get();
+                    }
+
+                    @Override
+                    protected String describeSubject() {
+                        return previousTransformStepNode.getTransformedSubject()
+                            .map(Describable::getDisplayName)
+                            .getOrMapFailure(Throwable::getMessage);
+                    }
+                });
+            }
+        }
+    }
+
+    private abstract class TransformStepBuildOperation implements CallableBuildOperation<TransformStepSubject> {
+
+        @UsedByScanPlugin("The string is used for filtering out artifact transform logs in Gradle Enterprise")
+        private static final String TRANSFORMING_PROGRESS_PREFIX = "Transforming ";
+
+        @Override
+        public final BuildOperationDescriptor.Builder description() {
+            String transformStepName = transformStep.getDisplayName();
+            String subjectName = describeSubject();
+            String basicName = subjectName + " with " + transformStepName;
+            return BuildOperationDescriptor.displayName("Transform " + basicName)
+                .progressDisplayName(TRANSFORMING_PROGRESS_PREFIX + basicName)
+                .metadata(BuildOperationCategory.TRANSFORM)
+                .details(new ExecutePlannedTransformStepBuildOperationDetails(TransformStepNode.this, transformStepName, subjectName));
+        }
+
+        protected abstract String describeSubject();
+
+        @Override
+        public TransformStepSubject call(BuildOperationContext context) {
+            context.setResult(RESULT);
+            return transform();
+        }
+
+        protected abstract TransformStepSubject transform();
+    }
+
+    private static final ExecutePlannedTransformStepBuildOperationType.Result RESULT = new ExecutePlannedTransformStepBuildOperationType.Result() {
+    };
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNodeDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNodeDependencyResolver.java
new file mode 100644
index 0000000..df96207
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNodeDependencyResolver.java
@@ -0,0 +1,42 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.execution.plan.DependencyResolver;
+import org.gradle.execution.plan.Node;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+/**
+ * Resolves dependencies to {@link TransformStepNode} objects.
+ */
+@ServiceScope(Scopes.Build.class)
+public class TransformStepNodeDependencyResolver implements DependencyResolver {
+    @Override
+    public boolean resolve(Task task, Object node, Action<? super Node> resolveAction) {
+        if (node instanceof DefaultTransformNodeDependency) {
+            DefaultTransformNodeDependency transformNodeDependency = (DefaultTransformNodeDependency) node;
+            for (TransformStepNode transformStepNode : transformNodeDependency.getNodes()) {
+                resolveAction.execute(transformStepNode);
+            }
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNodeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNodeFactory.java
new file mode 100644
index 0000000..ec61a59
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepNodeFactory.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.transform;
+
+import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
+import org.gradle.internal.id.ConfigurationCacheableIdFactory;
+import org.gradle.internal.model.CalculatedValueContainerFactory;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Transform step node factory that ensures that unique ids are used correctly
+ * when creating new instances and/or loading from the configuration cache.
+ */
+@ThreadSafe
+@ServiceScope(Scopes.BuildTree.class)
+public class TransformStepNodeFactory {
+
+    private final ConfigurationCacheableIdFactory idFactory;
+
+    public TransformStepNodeFactory(ConfigurationCacheableIdFactory idFactory) {
+        this.idFactory = idFactory;
+    }
+
+    /**
+     * Create an initial transform step node.
+     */
+    public TransformStepNode.InitialTransformStepNode createInitial(
+        ComponentVariantIdentifier targetComponentVariant,
+        AttributeContainer sourceAttributes,
+        TransformStep initial,
+        ResolvableArtifact artifact,
+        TransformUpstreamDependencies upstreamDependencies,
+        BuildOperationExecutor buildOperationExecutor,
+        CalculatedValueContainerFactory calculatedValueContainerFactory
+    ) {
+        long transformStepNodeId = idFactory.createId();
+        return new TransformStepNode.InitialTransformStepNode(transformStepNodeId, targetComponentVariant, sourceAttributes, initial, artifact, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
+    }
+
+    /**
+     * Create an initial transform step node.
+     * <p>
+     * Should only be used when loading from the configuration cache to set the node id.
+     */
+    public TransformStepNode.InitialTransformStepNode recreateInitial(
+        long transformStepNodeId,
+        ComponentVariantIdentifier targetComponentVariant,
+        AttributeContainer sourceAttributes,
+        TransformStep initial,
+        ResolvableArtifact artifact,
+        TransformUpstreamDependencies upstreamDependencies,
+        BuildOperationExecutor buildOperationExecutor,
+        CalculatedValueContainerFactory calculatedValueContainerFactory
+    ) {
+        idFactory.idRecreated();
+        return new TransformStepNode.InitialTransformStepNode(transformStepNodeId, targetComponentVariant, sourceAttributes, initial, artifact, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
+    }
+
+    /**
+     * Create a chained transform step node.
+     */
+    public TransformStepNode.ChainedTransformStepNode createChained(
+        ComponentVariantIdentifier targetComponentVariant,
+        AttributeContainer sourceAttributes,
+        TransformStep current,
+        TransformStepNode previous,
+        TransformUpstreamDependencies upstreamDependencies,
+        BuildOperationExecutor buildOperationExecutor,
+        CalculatedValueContainerFactory calculatedValueContainerFactory
+    ) {
+        long transformStepNodeId = idFactory.createId();
+        return new TransformStepNode.ChainedTransformStepNode(transformStepNodeId, targetComponentVariant, sourceAttributes, current, previous, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
+    }
+
+    /**
+     * Create a chained transform step node.
+     * <p>
+     * Should only be used when loading from the configuration cache to set the node id.
+     */
+    public TransformStepNode.ChainedTransformStepNode recreateChained(
+        long transformStepNodeId,
+        ComponentVariantIdentifier targetComponentVariant,
+        AttributeContainer sourceAttributes,
+        TransformStep current,
+        TransformStepNode previous,
+        TransformUpstreamDependencies upstreamDependencies,
+        BuildOperationExecutor buildOperationExecutor,
+        CalculatedValueContainerFactory calculatedValueContainerFactory
+    ) {
+        idFactory.idRecreated();
+        return new TransformStepNode.ChainedTransformStepNode(transformStepNodeId, targetComponentVariant, sourceAttributes, current, previous, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepSubject.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepSubject.java
new file mode 100644
index 0000000..669868a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformStepSubject.java
@@ -0,0 +1,104 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.Describable;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
+
+import java.io.File;
+
+/**
+ * Transform subject is either an initial artifact for the transform chain or a result of a previous transform step.
+ */
+public abstract class TransformStepSubject implements Describable {
+
+    public static TransformStepSubject initial(ResolvableArtifact artifact) {
+        return new Initial(artifact);
+    }
+
+    /**
+     * The files which should be transformed.
+     */
+    public abstract ImmutableList<File> getFiles();
+
+    /**
+     * Component identifier of the initial subject.
+     */
+    public abstract ComponentIdentifier getInitialComponentIdentifier();
+
+    /**
+     * Creates a subsequent subject by having transformed this subject.
+     */
+    public TransformStepSubject createSubjectFromResult(ImmutableList<File> result) {
+        return new Transformed(this, result);
+    }
+
+    private static class Initial extends TransformStepSubject {
+        private final ResolvableArtifact artifact;
+
+        public Initial(ResolvableArtifact artifact) {
+            this.artifact = artifact;
+        }
+
+        @Override
+        public ImmutableList<File> getFiles() {
+            return ImmutableList.of(artifact.getFile());
+        }
+
+        @Override
+        public String getDisplayName() {
+            return artifact.getId().getDisplayName();
+        }
+
+        @Override
+        public ComponentIdentifier getInitialComponentIdentifier() {
+            return artifact.getId().getComponentIdentifier();
+        }
+    }
+
+    private static class Transformed extends TransformStepSubject {
+        private final TransformStepSubject previous;
+        private final ImmutableList<File> files;
+
+        public Transformed(TransformStepSubject previous, ImmutableList<File> files) {
+            this.previous = previous;
+            this.files = files;
+        }
+
+        @Override
+        public ImmutableList<File> getFiles() {
+            return files;
+        }
+
+        @Override
+        public ComponentIdentifier getInitialComponentIdentifier() {
+            return previous.getInitialComponentIdentifier();
+        }
+
+        @Override
+        public String getDisplayName() {
+            return previous.getDisplayName();
+        }
+
+        @Override
+        public String toString() {
+            return getDisplayName();
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependencies.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependencies.java
index 3d88561..2beb9f3 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependencies.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependencies.java
@@ -17,9 +17,9 @@
 package org.gradle.api.internal.artifacts.transform;
 
 import org.gradle.api.file.FileCollection;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationIdentity;
 import org.gradle.api.internal.tasks.TaskDependencyContainer;
 import org.gradle.internal.Try;
+import org.gradle.operations.dependencies.configurations.ConfigurationIdentity;
 
 import javax.annotation.Nullable;
 
@@ -29,14 +29,14 @@ public interface TransformUpstreamDependencies extends TaskDependencyContainer {
     ConfigurationIdentity getConfigurationIdentity();
 
     /**
-     * Returns a collection containing the future artifacts for the given transformation step.
+     * Returns a collection containing the future artifacts for the given transform step.
      */
     FileCollection selectedArtifacts();
 
     /**
-     * Computes the finalized dependency artifacts for the given transformation step.
+     * Computes the finalized dependency artifacts for the given transform step.
      */
-    Try<ArtifactTransformDependencies> computeArtifacts();
+    Try<TransformDependencies> computeArtifacts();
 
     void finalizeIfNotAlready();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependenciesResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependenciesResolver.java
index e1d95cf..a5cddab 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependenciesResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependenciesResolver.java
@@ -17,14 +17,14 @@
 package org.gradle.api.internal.artifacts.transform;
 
 /**
- * Companion type to {@link TransformationNode} that knows how to compute extra dependent nodes aside from the to be transformed artifact.
- * Instances of this type should not be shared beyond a single transformation chain.
+ * Companion type to {@link TransformStepNode} that knows how to compute extra dependent nodes aside from the to be transformed artifact.
+ * Instances of this type should not be shared beyond a single transform chain.
  *
- * @see ExtraExecutionGraphDependenciesResolverFactory
+ * @see TransformUpstreamDependenciesResolverFactory
  */
 public interface TransformUpstreamDependenciesResolver {
     /**
-     * Returns the dependencies that should be applied to the given transformation step.
+     * Returns the dependencies that should be applied to the given transform step.
      */
-    TransformUpstreamDependencies dependenciesFor(TransformationStep transformationStep);
+    TransformUpstreamDependencies dependenciesFor(TransformStep transformStep);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependenciesResolverFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependenciesResolverFactory.java
new file mode 100644
index 0000000..78a77d7
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformUpstreamDependenciesResolverFactory.java
@@ -0,0 +1,34 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+
+/**
+ * Factory for {@link TransformUpstreamDependenciesResolver} that relies on the information provided to its {@code create} method.
+ */
+public interface TransformUpstreamDependenciesResolverFactory {
+    /**
+     * Creates a {@link TransformUpstreamDependenciesResolver} for the given {@code ComponentIdentifier} and {@code TransformChain}.
+     *
+     * @param componentIdentifier the identifier of the component whose artifacts will be transformed.
+     * @param transformChain the transform chain that will be applied.
+     *
+     * @return an {@code ExecutionGraphDependenciesResolver} based on the provided parameters
+     */
+    TransformUpstreamDependenciesResolver create(ComponentIdentifier componentIdentifier, TransformChain transformChain);
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformWorkspaceServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformWorkspaceServices.java
new file mode 100644
index 0000000..26cd0c3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformWorkspaceServices.java
@@ -0,0 +1,27 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.cache.Cache;
+import org.gradle.internal.Try;
+import org.gradle.internal.execution.UnitOfWork;
+import org.gradle.internal.execution.workspace.WorkspaceProvider;
+
+public interface TransformWorkspaceServices {
+    WorkspaceProvider getWorkspaceProvider();
+    Cache<UnitOfWork.Identity, Try<TransformExecutionResult>> getIdentityCache();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transformation.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transformation.java
deleted file mode 100644
index aadec66..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transformation.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017 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.api.internal.artifacts.transform;
-
-import org.gradle.api.Action;
-import org.gradle.api.Describable;
-
-/**
- * The internal API equivalent of {@link org.gradle.api.artifacts.transform.TransformAction}, which is also aware of our cache infrastructure.
- *
- * This can encapsulate a single transformation step using a single transformer or a chain of transformation steps.
- */
-public interface Transformation extends Describable {
-
-    boolean endsWith(Transformation otherTransform);
-
-    int stepsCount();
-
-    /**
-     * Whether the transformation requires upstream dependencies of the transformed artifact to be injected.
-     */
-    boolean requiresDependencies();
-
-    /**
-     * Extract the transformation steps from this transformation.
-     */
-    void visitTransformationSteps(Action<? super TransformationStep> action);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationChain.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationChain.java
deleted file mode 100644
index 4c969d1..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationChain.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2017 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.api.internal.artifacts.transform;
-
-import org.gradle.api.Action;
-
-/**
- * A series of {@link TransformationStep}s.
- */
-public class TransformationChain implements Transformation {
-
-    private final Transformation first;
-    private final Transformation second;
-    private final int stepsCount;
-
-    public TransformationChain(Transformation first, Transformation second) {
-        this.first = first;
-        this.second = second;
-        this.stepsCount = first.stepsCount() + second.stepsCount();
-    }
-
-    public Transformation getFirst() {
-        return first;
-    }
-
-    public Transformation getSecond() {
-        return second;
-    }
-
-    @Override
-    public boolean endsWith(Transformation otherTransform) {
-        int otherStepsCount = otherTransform.stepsCount();
-        if (otherStepsCount > this.stepsCount) {
-            return false;
-        } else if (otherStepsCount == 1) {
-            return second == otherTransform;
-        }
-
-        TransformationChain otherChain = (TransformationChain) otherTransform;
-        if (otherChain.second != second) {
-            return false;
-        } else {
-            return first.endsWith(otherChain.first);
-        }
-    }
-
-    @Override
-    public int stepsCount() {
-        return stepsCount;
-    }
-
-    @Override
-    public boolean requiresDependencies() {
-        return first.requiresDependencies() || second.requiresDependencies();
-    }
-
-    @Override
-    public String getDisplayName() {
-        return first.getDisplayName() + " -> " + second.getDisplayName();
-    }
-
-    @Override
-    public void visitTransformationSteps(Action<? super TransformationStep> action) {
-        first.visitTransformationSteps(action);
-        second.visitTransformationSteps(action);
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNode.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNode.java
deleted file mode 100644
index ad8b5b0..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNode.java
+++ /dev/null
@@ -1,498 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.Describable;
-import org.gradle.api.artifacts.ResolveException;
-import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.component.OpaqueComponentIdentifier;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationIdentity;
-import org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
-import org.gradle.api.internal.capabilities.Capability;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.NodeExecutionContext;
-import org.gradle.api.internal.tasks.TaskDependencyContainer;
-import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
-import org.gradle.execution.plan.CreationOrderedNode;
-import org.gradle.execution.plan.Node;
-import org.gradle.execution.plan.SelfExecutingNode;
-import org.gradle.execution.plan.TaskDependencyResolver;
-import org.gradle.internal.Describables;
-import org.gradle.internal.Try;
-import org.gradle.internal.model.CalculatedValueContainer;
-import org.gradle.internal.model.CalculatedValueContainerFactory;
-import org.gradle.internal.model.ValueCalculator;
-import org.gradle.internal.operations.BuildOperationCategory;
-import org.gradle.internal.operations.BuildOperationContext;
-import org.gradle.internal.operations.BuildOperationDescriptor;
-import org.gradle.internal.operations.BuildOperationExecutor;
-import org.gradle.internal.operations.CallableBuildOperation;
-import org.gradle.internal.scan.UsedByScanPlugin;
-
-import javax.annotation.Nullable;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.stream.Collectors;
-
-public abstract class TransformationNode extends CreationOrderedNode implements SelfExecutingNode {
-
-    private static final AtomicLong SEQUENCE = new AtomicLong();
-
-    protected final TransformationStep transformationStep;
-    protected final ResolvableArtifact artifact;
-    private final ComponentVariantIdentifier targetComponentVariant;
-    private final AttributeContainer sourceAttributes;
-    protected final TransformUpstreamDependencies upstreamDependencies;
-    private final long transformationNodeId;
-
-    private TransformationIdentity cachedIdentity;
-
-    public static ChainedTransformationNode chained(
-        ComponentVariantIdentifier targetComponentVariant,
-        AttributeContainer sourceAttributes,
-        TransformationStep current,
-        TransformationNode previous,
-        TransformUpstreamDependencies upstreamDependencies,
-        BuildOperationExecutor buildOperationExecutor,
-        CalculatedValueContainerFactory calculatedValueContainerFactory
-    ) {
-        return new ChainedTransformationNode(targetComponentVariant, sourceAttributes, current, previous, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
-    }
-
-    public static InitialTransformationNode initial(
-        ComponentVariantIdentifier targetComponentVariant,
-        AttributeContainer sourceAttributes,
-        TransformationStep initial,
-        ResolvableArtifact artifact,
-        TransformUpstreamDependencies upstreamDependencies,
-        BuildOperationExecutor buildOperationExecutor,
-        CalculatedValueContainerFactory calculatedValueContainerFactory
-    ) {
-        return new InitialTransformationNode(targetComponentVariant, sourceAttributes, initial, artifact, upstreamDependencies, buildOperationExecutor, calculatedValueContainerFactory);
-    }
-
-    private static long createId() {
-        return SEQUENCE.incrementAndGet();
-    }
-
-    protected TransformationNode(
-        ComponentVariantIdentifier targetComponentVariant,
-        AttributeContainer sourceAttributes,
-        TransformationStep transformationStep,
-        ResolvableArtifact artifact,
-        TransformUpstreamDependencies upstreamDependencies
-    ) {
-        this.targetComponentVariant = targetComponentVariant;
-        this.sourceAttributes = sourceAttributes;
-        this.transformationStep = transformationStep;
-        this.artifact = artifact;
-        this.upstreamDependencies = upstreamDependencies;
-        this.transformationNodeId = createId();
-    }
-
-    public ComponentVariantIdentifier getTargetComponentVariant() {
-        return targetComponentVariant;
-    }
-
-    public AttributeContainer getSourceAttributes() {
-        return sourceAttributes;
-    }
-
-    public TransformationIdentity getNodeIdentity() {
-        if (cachedIdentity == null) {
-            cachedIdentity = createIdentity();
-        }
-        return cachedIdentity;
-    }
-
-    private TransformationIdentity createIdentity() {
-        String consumerBuildPath = transformationStep.getOwningProject().getBuildPath().toString();
-        String consumerProjectPath = transformationStep.getOwningProject().getIdentityPath().toString();
-        ComponentIdentifier componentId = getComponentIdentifier(targetComponentVariant.getComponentId());
-        Map<String, String> targetAttributes = AttributesToMapConverter.convertToMap(targetComponentVariant.getAttributes());
-        List<Capability> capabilities = targetComponentVariant.getCapabilities().stream()
-            .map(TransformationNode::convertCapability)
-            .collect(Collectors.toList());
-
-        return new TransformationIdentity() {
-            @Override
-            public String getBuildPath() {
-                return consumerBuildPath;
-            }
-
-            @Override
-            public String getProjectPath() {
-                return consumerProjectPath;
-            }
-
-            @Override
-            public ComponentIdentifier getComponentId() {
-                return componentId;
-            }
-
-            @Override
-            public Map<String, String> getTargetAttributes() {
-                return targetAttributes;
-            }
-
-            @Override
-            public List<? extends Capability> getCapabilities() {
-                return capabilities;
-            }
-
-            @Override
-            public String getArtifactName() {
-                return artifact.getArtifactName().toString();
-            }
-
-            @Override
-            public ConfigurationIdentity getDependenciesConfigurationIdentity() {
-                return upstreamDependencies.getConfigurationIdentity();
-            }
-
-            @Override
-            public long getTransformationNodeId() {
-                return transformationNodeId;
-            }
-
-            @Override
-            public String toString() {
-                return "Transform '" + targetComponentVariant.getComponentId() + "' with " + transformationStep.getTransformer().getImplementationClass().getName();
-            }
-        };
-    }
-
-    private static Capability convertCapability(org.gradle.api.capabilities.Capability capability) {
-        return new Capability() {
-            @Override
-            public String getGroup() {
-                return capability.getGroup();
-            }
-
-            @Override
-            public String getName() {
-                return capability.getName();
-            }
-
-            @Override
-            public String getVersion() {
-                return capability.getVersion();
-            }
-
-            @Override
-            public String toString() {
-                return getGroup() + ":" + getName() + (getVersion() == null ? "" : (":" + getVersion()));
-            }
-        };
-    }
-
-    private static ComponentIdentifier getComponentIdentifier(org.gradle.api.artifacts.component.ComponentIdentifier componentId) {
-        if (componentId instanceof ProjectComponentIdentifier) {
-            ProjectComponentIdentifier projectComponentIdentifier = (ProjectComponentIdentifier) componentId;
-            return new org.gradle.api.internal.artifacts.component.ProjectComponentIdentifier() {
-                @Override
-                public String getBuildPath() {
-                    return projectComponentIdentifier.getBuild().getName();
-                }
-
-                @Override
-                public String getProjectPath() {
-                    return projectComponentIdentifier.getProjectPath();
-                }
-
-                @Override
-                public String toString() {
-                    return projectComponentIdentifier.getDisplayName();
-                }
-            };
-        } else if (componentId instanceof ModuleComponentIdentifier) {
-            ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier) componentId;
-            return new org.gradle.api.internal.artifacts.component.ModuleComponentIdentifier() {
-                @Override
-                public String getGroup() {
-                    return moduleComponentIdentifier.getGroup();
-                }
-
-                @Override
-                public String getModule() {
-                    return moduleComponentIdentifier.getModule();
-                }
-
-                @Override
-                public String getVersion() {
-                    return moduleComponentIdentifier.getVersion();
-                }
-
-                @Override
-                public String toString() {
-                    return moduleComponentIdentifier.getDisplayName();
-                }
-            };
-        } else {
-            return new OpaqueComponentIdentifier() {
-                @Override
-                public String getDisplayName() {
-                    return componentId.getDisplayName();
-                }
-
-                @Override
-                public String getClassName() {
-                    return componentId.getClass().getName();
-                }
-
-                @Override
-                public String toString() {
-                    return componentId.getDisplayName();
-                }
-            };
-        }
-    }
-
-    public ResolvableArtifact getInputArtifact() {
-        return artifact;
-    }
-
-    public TransformUpstreamDependencies getUpstreamDependencies() {
-        return upstreamDependencies;
-    }
-
-    @Nullable
-    @Override
-    public ProjectInternal getOwningProject() {
-        return transformationStep.getOwningProject();
-    }
-
-    @Override
-    public boolean isPublicNode() {
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return transformationStep.getDisplayName();
-    }
-
-    public TransformationStep getTransformationStep() {
-        return transformationStep;
-    }
-
-    public Try<TransformationSubject> getTransformedSubject() {
-        return getTransformedArtifacts().getValue();
-    }
-
-    @Override
-    public void execute(NodeExecutionContext context) {
-        getTransformedArtifacts().run(context);
-    }
-
-    public void executeIfNotAlready() {
-        transformationStep.isolateParametersIfNotAlready();
-        upstreamDependencies.finalizeIfNotAlready();
-        getTransformedArtifacts().finalizeIfNotAlready();
-    }
-
-    protected abstract CalculatedValueContainer<TransformationSubject, ?> getTransformedArtifacts();
-
-    @Override
-    public Throwable getNodeFailure() {
-        return null;
-    }
-
-    @Override
-    public void resolveDependencies(TaskDependencyResolver dependencyResolver) {
-        processDependencies(dependencyResolver.resolveDependenciesFor(null, (TaskDependencyContainer) context -> getTransformedArtifacts().visitDependencies(context)));
-    }
-
-    protected void processDependencies(Set<Node> dependencies) {
-        for (Node dependency : dependencies) {
-            addDependencySuccessor(dependency);
-        }
-    }
-
-    public static class InitialTransformationNode extends TransformationNode {
-        private final CalculatedValueContainer<TransformationSubject, TransformInitialArtifact> result;
-
-        public InitialTransformationNode(
-            ComponentVariantIdentifier targetComponentVariant,
-            AttributeContainer sourceAttributes,
-            TransformationStep transformationStep,
-            ResolvableArtifact artifact,
-            TransformUpstreamDependencies upstreamDependencies,
-            BuildOperationExecutor buildOperationExecutor,
-            CalculatedValueContainerFactory calculatedValueContainerFactory
-        ) {
-            super(targetComponentVariant, sourceAttributes, transformationStep, artifact, upstreamDependencies);
-            result = calculatedValueContainerFactory.create(Describables.of(this), new TransformInitialArtifact(buildOperationExecutor));
-        }
-
-        @Override
-        protected CalculatedValueContainer<TransformationSubject, TransformInitialArtifact> getTransformedArtifacts() {
-            return result;
-        }
-
-        private class TransformInitialArtifact implements ValueCalculator<TransformationSubject> {
-            private final BuildOperationExecutor buildOperationExecutor;
-
-            public TransformInitialArtifact(BuildOperationExecutor buildOperationExecutor) {
-                this.buildOperationExecutor = buildOperationExecutor;
-            }
-
-            @Override
-            public void visitDependencies(TaskDependencyResolveContext context) {
-                context.add(transformationStep);
-                context.add(upstreamDependencies);
-                context.add(artifact);
-            }
-
-            @Override
-            public TransformationSubject calculateValue(NodeExecutionContext context) {
-                return buildOperationExecutor.call(new ArtifactTransformationStepBuildOperation() {
-                    @Override
-                    protected TransformationSubject transform() {
-                        TransformationSubject initialArtifactTransformationSubject;
-                        try {
-                            initialArtifactTransformationSubject = TransformationSubject.initial(artifact);
-                        } catch (ResolveException e) {
-                            throw e;
-                        } catch (RuntimeException e) {
-                            throw new DefaultLenientConfiguration.ArtifactResolveException("artifacts", transformationStep.getDisplayName(), "artifact transform", Collections.singleton(e));
-                        }
-
-                        return transformationStep
-                            .createInvocation(initialArtifactTransformationSubject, upstreamDependencies, context)
-                            .completeAndGet()
-                            .get();
-                    }
-
-                    @Override
-                    protected String describeSubject() {
-                        return artifact.getId().getDisplayName();
-                    }
-                });
-            }
-        }
-    }
-
-    public static class ChainedTransformationNode extends TransformationNode {
-        private final TransformationNode previousTransformationNode;
-        private final CalculatedValueContainer<TransformationSubject, TransformPreviousArtifacts> result;
-
-        public ChainedTransformationNode(
-            ComponentVariantIdentifier targetComponentVariant,
-            AttributeContainer sourceAttributes,
-            TransformationStep transformationStep,
-            TransformationNode previousTransformationNode,
-            TransformUpstreamDependencies upstreamDependencies,
-            BuildOperationExecutor buildOperationExecutor,
-            CalculatedValueContainerFactory calculatedValueContainerFactory
-        ) {
-            super(targetComponentVariant, sourceAttributes, transformationStep, previousTransformationNode.artifact, upstreamDependencies);
-            this.previousTransformationNode = previousTransformationNode;
-            result = calculatedValueContainerFactory.create(Describables.of(this), new TransformPreviousArtifacts(buildOperationExecutor));
-        }
-
-        public TransformationNode getPreviousTransformationNode() {
-            return previousTransformationNode;
-        }
-
-        @Override
-        protected CalculatedValueContainer<TransformationSubject, TransformPreviousArtifacts> getTransformedArtifacts() {
-            return result;
-        }
-
-        @Override
-        public void executeIfNotAlready() {
-            // Only finalize the previous node when executing this node on demand
-            previousTransformationNode.executeIfNotAlready();
-            super.executeIfNotAlready();
-        }
-
-        private class TransformPreviousArtifacts implements ValueCalculator<TransformationSubject> {
-            private final BuildOperationExecutor buildOperationExecutor;
-
-            public TransformPreviousArtifacts(BuildOperationExecutor buildOperationExecutor) {
-                this.buildOperationExecutor = buildOperationExecutor;
-            }
-
-            @Override
-            public void visitDependencies(TaskDependencyResolveContext context) {
-                context.add(transformationStep);
-                context.add(upstreamDependencies);
-                context.add(new DefaultTransformationDependency(Collections.singletonList(previousTransformationNode)));
-            }
-
-            @Override
-            public TransformationSubject calculateValue(NodeExecutionContext context) {
-                return buildOperationExecutor.call(new ArtifactTransformationStepBuildOperation() {
-                    @Override
-                    protected TransformationSubject transform() {
-                        return previousTransformationNode.getTransformedSubject()
-                            .flatMap(transformedSubject -> transformationStep
-                                .createInvocation(transformedSubject, upstreamDependencies, context)
-                                .completeAndGet())
-                            .get();
-                    }
-
-                    @Override
-                    protected String describeSubject() {
-                        return previousTransformationNode.getTransformedSubject()
-                            .map(Describable::getDisplayName)
-                            .getOrMapFailure(Throwable::getMessage);
-                    }
-                });
-            }
-        }
-    }
-
-    private abstract class ArtifactTransformationStepBuildOperation implements CallableBuildOperation<TransformationSubject> {
-
-        @UsedByScanPlugin("The string is used for filtering out artifact transform logs in Gradle Enterprise")
-        private static final String TRANSFORMING_PROGRESS_PREFIX = "Transforming ";
-
-        @Override
-        public final BuildOperationDescriptor.Builder description() {
-            String transformerName = transformationStep.getDisplayName();
-            String subjectName = describeSubject();
-            String basicName = subjectName + " with " + transformerName;
-            return BuildOperationDescriptor.displayName("Transform " + basicName)
-                .progressDisplayName(TRANSFORMING_PROGRESS_PREFIX + basicName)
-                .metadata(BuildOperationCategory.TRANSFORM)
-                .details(new ExecuteScheduledTransformationStepBuildOperationDetails(TransformationNode.this, transformerName, subjectName));
-        }
-
-        protected abstract String describeSubject();
-
-        @Override
-        public TransformationSubject call(BuildOperationContext context) {
-            context.setResult(RESULT);
-            return transform();
-        }
-
-        protected abstract TransformationSubject transform();
-    }
-
-    private static final ExecuteScheduledTransformationStepBuildOperationType.Result RESULT = new ExecuteScheduledTransformationStepBuildOperationType.Result() {
-    };
-
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNodeDependencyResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNodeDependencyResolver.java
deleted file mode 100644
index 7538641..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNodeDependencyResolver.java
+++ /dev/null
@@ -1,42 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.Action;
-import org.gradle.api.Task;
-import org.gradle.execution.plan.DependencyResolver;
-import org.gradle.execution.plan.Node;
-import org.gradle.internal.service.scopes.Scopes;
-import org.gradle.internal.service.scopes.ServiceScope;
-
-/**
- * Resolves dependencies to {@link TransformationNode} objects.
- */
-@ServiceScope(Scopes.Build.class)
-public class TransformationNodeDependencyResolver implements DependencyResolver {
-    @Override
-    public boolean resolve(Task task, Object node, Action<? super Node> resolveAction) {
-        if (node instanceof DefaultTransformationDependency) {
-            DefaultTransformationDependency transformation = (DefaultTransformationDependency) node;
-            for (TransformationNode transformationNode : transformation.getNodes()) {
-                resolveAction.execute(transformationNode);
-            }
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNodeFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNodeFactory.java
deleted file mode 100644
index af62ab1..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationNodeFactory.java
+++ /dev/null
@@ -1,37 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.attributes.AttributeContainer;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet;
-
-import java.util.Collection;
-
-/**
- * Creates a transformation node per artifact in the given artifact set.
- */
-public interface TransformationNodeFactory {
-
-    Collection<TransformationNode> create(
-        ResolvedArtifactSet artifactSet,
-        ComponentVariantIdentifier targetComponentVariant,
-        TransformationStep transformationStep,
-        AttributeContainer sourceAttributes,
-        TransformUpstreamDependenciesResolver dependenciesResolver
-    );
-
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationRegistrationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationRegistrationFactory.java
deleted file mode 100644
index 4eec227..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationRegistrationFactory.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2019 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.api.internal.artifacts.transform;
-
-import org.gradle.api.artifacts.transform.TransformAction;
-import org.gradle.api.artifacts.transform.TransformParameters;
-import org.gradle.api.internal.artifacts.ArtifactTransformRegistration;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-
-import javax.annotation.Nullable;
-
-public interface TransformationRegistrationFactory {
-    ArtifactTransformRegistration create(ImmutableAttributes from, ImmutableAttributes to, Class<? extends TransformAction<?>> implementation, @Nullable TransformParameters parameterObject);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationResult.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationResult.java
deleted file mode 100644
index 0d390f4..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationResult.java
+++ /dev/null
@@ -1,276 +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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.file.RelativePath;
-
-import java.io.File;
-import java.util.function.Consumer;
-
-/**
- * The result of running a transformation.
- *
- * The result of running a transformation is a list of outputs.
- * There are two kinds of outputs for a transformation:
- * - Produced outputs in the workspace. Those are absolute paths which do not change depending on the input artifact.
- * - Selected parts of the input artifact. These are relative paths of locations selected in the input artifact.
- */
-public interface TransformationResult {
-    /**
-     * Resolves location of the outputs of this result for a given input artifact.
-     *
-     * Produced outputs don't need to be resolved to locations, since they are absolute paths and can be returned as is.
-     * The relative paths of selected parts of the input artifact need to resolved based on the provided input artifact location.
-     */
-    ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact);
-
-    void visitOutputs(TransformationOutputVisitor visitor);
-
-    int size();
-
-    static OutputTypeInferringBuilder builderFor(File inputArtifact, File outputDir) {
-        return new OutputTypeInferringBuilder(inputArtifact, outputDir);
-    }
-
-    static Builder builder() {
-        return new Builder();
-    }
-
-    class Builder {
-        private final ImmutableList.Builder<TransformationOutput> builder = ImmutableList.builder();
-        private boolean onlyProducedOutputs = true;
-
-        public void addEntireInputArtifact() {
-            onlyProducedOutputs = false;
-            builder.add(EntireInputArtifact.INSTANCE);
-        }
-
-        public void addPartOfInputArtifact(String relativePath) {
-            onlyProducedOutputs = false;
-            builder.add(new PartOfInputArtifact(relativePath));
-        }
-
-        public void addProducedOutput(File outputLocation) {
-            builder.add(new ProducedOutput(outputLocation));
-        }
-
-        public TransformationResult build() {
-            ImmutableList<TransformationOutput> transformationOutputs = builder.build();
-            return onlyProducedOutputs
-                ? new ProducedOutputOnlyTransformationResult(convertToProducedOutputLocations(transformationOutputs))
-                : new FilteredTransformationResult(transformationOutputs);
-        }
-
-        private static ImmutableList<File> convertToProducedOutputLocations(ImmutableList<TransformationOutput> transformationOutputs) {
-            ImmutableList.Builder<File> builder = new ImmutableList.Builder<>();
-            transformationOutputs.forEach(output -> builder.add(((ProducedOutput) output).getOutputLocation()));
-            return builder.build();
-        }
-
-        /**
-         * Optimized variant for a transform whose results are all produced by the transform,
-         * and don't include any of its input artifact.
-         */
-        private static class ProducedOutputOnlyTransformationResult implements TransformationResult {
-            private final ImmutableList<File> producedOutputLocations;
-
-            public ProducedOutputOnlyTransformationResult(ImmutableList<File> producedOutputLocations) {
-                this.producedOutputLocations = producedOutputLocations;
-            }
-
-            @Override
-            public ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact) {
-                return producedOutputLocations;
-            }
-
-            @Override
-            public void visitOutputs(TransformationOutputVisitor visitor) {
-                producedOutputLocations.forEach(visitor::visitProducedOutput);
-            }
-
-            @Override
-            public int size() {
-                return producedOutputLocations.size();
-            }
-        }
-
-        /**
-         * Results of a transform that includes parts or the whole of its input artifact.
-         * It might also include outputs produced by the transform.
-         */
-        private static class FilteredTransformationResult implements TransformationResult {
-            private final ImmutableList<TransformationOutput> transformationOutputs;
-
-            public FilteredTransformationResult(ImmutableList<TransformationOutput> transformationOutputs) {
-                this.transformationOutputs = transformationOutputs;
-            }
-
-            @Override
-            public ImmutableList<File> resolveOutputsForInputArtifact(File inputArtifact) {
-                ImmutableList.Builder<File> builder = ImmutableList.builderWithExpectedSize(transformationOutputs.size());
-                transformationOutputs.forEach(output -> builder.add(output.resolveForInputArtifact(inputArtifact)));
-                return builder.build();
-            }
-
-            @Override
-            public void visitOutputs(TransformationOutputVisitor visitor) {
-                transformationOutputs.forEach(output -> output.visitOutput(visitor));
-            }
-
-            @Override
-            public int size() {
-                return transformationOutputs.size();
-            }
-        }
-
-        /**
-         * A single output in a transformation result.
-         *
-         * Can be either
-         * - the entire input artifact {@link EntireInputArtifact}
-         * - a part of the input artifact {@link PartOfInputArtifact}
-         * - a produced output in the workspace {@link ProducedOutput}
-         *
-         * Only outputs related to the input artifact need resolving.
-         */
-        private interface TransformationOutput {
-            File resolveForInputArtifact(File inputArtifact);
-
-            void visitOutput(TransformationOutputVisitor visitor);
-        }
-
-        private static class PartOfInputArtifact implements TransformationOutput {
-            private final String relativePath;
-
-            public PartOfInputArtifact(String relativePath) {
-                this.relativePath = relativePath;
-            }
-
-            @Override
-            public File resolveForInputArtifact(File inputArtifact) {
-                return new File(inputArtifact, relativePath);
-            }
-
-            @Override
-            public void visitOutput(TransformationOutputVisitor visitor) {
-                visitor.visitPartOfInputArtifact(relativePath);
-            }
-        }
-
-        private static class EntireInputArtifact implements TransformationOutput {
-            public static final EntireInputArtifact INSTANCE = new EntireInputArtifact();
-
-            @Override
-            public File resolveForInputArtifact(File inputArtifact) {
-                return inputArtifact;
-            }
-
-            @Override
-            public void visitOutput(TransformationOutputVisitor visitor) {
-                visitor.visitEntireInputArtifact();
-            }
-        }
-
-        private static class ProducedOutput implements TransformationOutput {
-            private final File outputFile;
-
-            public ProducedOutput(File outputFile) {
-                this.outputFile = outputFile;
-            }
-
-            public File getOutputLocation() {
-                return outputFile;
-            }
-
-            @Override
-            public File resolveForInputArtifact(File inputArtifact) {
-                return outputFile;
-            }
-
-            @Override
-            public void visitOutput(TransformationOutputVisitor visitor) {
-                visitor.visitProducedOutput(outputFile);
-            }
-        }
-    }
-
-    interface TransformationOutputVisitor {
-        /**
-         * Called when the result is the full input artifact.
-         */
-        void visitEntireInputArtifact();
-
-        /**
-         * Called when the result is inside the input artifact.
-         *
-         * @param relativePath the relative path from the input artifact to the selected location in the input artifact.
-         */
-        void visitPartOfInputArtifact(String relativePath);
-
-        /**
-         * Called when the result is a produced output in the workspace.
-         *
-         * @param outputLocation the absolute {@link File} location of the output in the workspace.
-         */
-        void visitProducedOutput(File outputLocation);
-    }
-
-    /**
-     * A {@link TransformationResult} builder which accepts absolute locations of results.
-     * <p>
-     * The builder then infers if the result is (in) the input artifact or a produced output in the workspace.
-     */
-    class OutputTypeInferringBuilder {
-        private final File inputArtifact;
-        private final File workspaceDir;
-        private final String inputArtifactPrefix;
-        private final String workspaceDirPrefix;
-        private final Builder delegate = TransformationResult.builder();
-
-        public OutputTypeInferringBuilder(File inputArtifact, File workspaceDir) {
-            this.inputArtifact = inputArtifact;
-            this.workspaceDir = workspaceDir;
-            this.inputArtifactPrefix = inputArtifact.getPath() + File.separator;
-            this.workspaceDirPrefix = workspaceDir.getPath() + File.separator;
-        }
-
-        /**
-         * Adds an output location to the result.
-         *
-         * @param workspaceAction an action to run when the output is a produced output in the workspace.
-         */
-        public void addOutput(File output, Consumer<File> workspaceAction) {
-            if (output.equals(inputArtifact)) {
-                delegate.addEntireInputArtifact();
-            } else if (output.equals(workspaceDir) || output.getPath().startsWith(workspaceDirPrefix)) {
-                delegate.addProducedOutput(output);
-                workspaceAction.accept(output);
-            } else if (output.getPath().startsWith(inputArtifactPrefix)) {
-                String relativePath = RelativePath.parse(true, output.getPath().substring(inputArtifactPrefix.length())).getPathString();
-                delegate.addPartOfInputArtifact(relativePath);
-            } else {
-                throw new InvalidUserDataException("Transform output " + output.getPath() + " must be a part of the input artifact or refer to a relative path.");
-            }
-        }
-
-        public TransformationResult build() {
-            return delegate.build();
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationResultSerializer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationResultSerializer.java
deleted file mode 100644
index f033240..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationResultSerializer.java
+++ /dev/null
@@ -1,95 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.UncheckedIOException;
-import org.gradle.api.file.RelativePath;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.gradle.internal.UncheckedException.unchecked;
-
-public class TransformationResultSerializer {
-    private static final String INPUT_FILE_PATH_PREFIX = "i/";
-    private static final String OUTPUT_FILE_PATH_PREFIX = "o/";
-
-    private final File outputDir;
-
-    public TransformationResultSerializer(File outputDir) {
-        this.outputDir = outputDir;
-    }
-
-    public void writeToFile(File target, TransformationResult result) {
-        String outputDirPrefix = outputDir.getPath() + File.separator;
-        List<String> resultFileContents = new ArrayList<>(result.size());
-
-        result.visitOutputs(new TransformationResult.TransformationOutputVisitor() {
-            @Override
-            public void visitEntireInputArtifact() {
-                resultFileContents.add(INPUT_FILE_PATH_PREFIX);
-            }
-
-            @Override
-            public void visitPartOfInputArtifact(String relativePath) {
-                resultFileContents.add(INPUT_FILE_PATH_PREFIX + relativePath);
-            }
-
-            @Override
-            public void visitProducedOutput(File outputLocation) {
-                if (outputLocation.equals(outputDir)) {
-                    resultFileContents.add(OUTPUT_FILE_PATH_PREFIX);
-                } else {
-                    resultFileContents.add(OUTPUT_FILE_PATH_PREFIX + RelativePath.parse(true,
-                        outputLocation.getAbsolutePath().substring(outputDirPrefix.length())
-                    ).getPathString());
-                }
-            }
-        });
-        unchecked(() -> Files.write(target.toPath(), resultFileContents));
-    }
-
-    public TransformationResult readResultsFile(File resultsFile) {
-        Path transformerResultsPath = resultsFile.toPath();
-        try {
-            TransformationResult.Builder builder = TransformationResult.builder();
-            List<String> paths = Files.readAllLines(transformerResultsPath, StandardCharsets.UTF_8);
-            for (String path : paths) {
-                if (path.startsWith(OUTPUT_FILE_PATH_PREFIX)) {
-                    builder.addProducedOutput(new File(outputDir, path.substring(2)));
-                } else if (path.startsWith(INPUT_FILE_PATH_PREFIX)) {
-                    String relativePathString = path.substring(2);
-                    if (relativePathString.isEmpty()) {
-                        builder.addEntireInputArtifact();
-                    } else {
-                        builder.addPartOfInputArtifact(relativePathString);
-                    }
-                } else {
-                    throw new IllegalStateException("Cannot parse result path string: " + path);
-                }
-            }
-            return builder.build();
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationStep.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationStep.java
deleted file mode 100644
index e859794..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationStep.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2017 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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.Action;
-import org.gradle.api.internal.DomainObjectContext;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.NodeExecutionContext;
-import org.gradle.api.internal.tasks.TaskDependencyContainer;
-import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
-import org.gradle.internal.Cast;
-import org.gradle.internal.Deferrable;
-import org.gradle.internal.Try;
-import org.gradle.internal.execution.InputFingerprinter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.Nullable;
-import java.io.File;
-
-/**
- * A single transformation step.
- *
- * Transforms a subject by invoking a transformer on each of the subjects files.
- */
-public class TransformationStep implements Transformation, TaskDependencyContainer {
-    private static final Logger LOGGER = LoggerFactory.getLogger(TransformationStep.class);
-
-    private final Transformer transformer;
-    private final TransformerInvocationFactory transformerInvocationFactory;
-    private final ProjectInternal owningProject;
-    private final InputFingerprinter globalInputFingerprinter;
-
-    public TransformationStep(Transformer transformer, TransformerInvocationFactory transformerInvocationFactory, DomainObjectContext owner, InputFingerprinter globalInputFingerprinter) {
-        this.transformer = transformer;
-        this.transformerInvocationFactory = transformerInvocationFactory;
-        this.globalInputFingerprinter = globalInputFingerprinter;
-        this.owningProject = owner.getProject();
-    }
-
-    public Transformer getTransformer() {
-        return transformer;
-    }
-
-    @Nullable
-    public ProjectInternal getOwningProject() {
-        return owningProject;
-    }
-
-    @Override
-    public boolean endsWith(Transformation otherTransform) {
-        return this == otherTransform;
-    }
-
-    @Override
-    public int stepsCount() {
-        return 1;
-    }
-
-    public Deferrable<Try<TransformationSubject>> createInvocation(TransformationSubject subjectToTransform, TransformUpstreamDependencies upstreamDependencies, @Nullable NodeExecutionContext context) {
-        if (LOGGER.isInfoEnabled()) {
-            LOGGER.info("Transforming {} with {}", subjectToTransform.getDisplayName(), transformer.getDisplayName());
-        }
-
-        InputFingerprinter inputFingerprinter = context != null ? context.getService(InputFingerprinter.class) : globalInputFingerprinter;
-
-        Try<ArtifactTransformDependencies> resolvedDependencies = upstreamDependencies.computeArtifacts();
-        return resolvedDependencies
-            .map(dependencies -> {
-                ImmutableList<File> inputArtifacts = subjectToTransform.getFiles();
-                if (inputArtifacts.isEmpty()) {
-                    return Deferrable.completed(Try.successful(subjectToTransform.createSubjectFromResult(ImmutableList.of())));
-                } else if (inputArtifacts.size() > 1) {
-                    return Deferrable.deferred(() ->
-                        doTransform(subjectToTransform, inputFingerprinter, dependencies, inputArtifacts)
-                    );
-                } else {
-                    File inputArtifact = inputArtifacts.get(0);
-                    return transformerInvocationFactory.createInvocation(transformer, inputArtifact, dependencies, subjectToTransform, inputFingerprinter)
-                        .map(result -> result.map(subjectToTransform::createSubjectFromResult));
-                }
-            })
-            .getOrMapFailure(failure -> Deferrable.completed(Try.failure(failure)));
-    }
-
-    private Try<TransformationSubject> doTransform(TransformationSubject subjectToTransform, InputFingerprinter inputFingerprinter, ArtifactTransformDependencies dependencies, ImmutableList<File> inputArtifacts) {
-        ImmutableList.Builder<File> builder = ImmutableList.builder();
-        for (File inputArtifact : inputArtifacts) {
-            Try<ImmutableList<File>> result = transformerInvocationFactory
-                .createInvocation(transformer, inputArtifact, dependencies, subjectToTransform, inputFingerprinter)
-                .completeAndGet();
-
-            if (result.getFailure().isPresent()) {
-                return Cast.uncheckedCast(result);
-            }
-            builder.addAll(result.get());
-        }
-        return Try.successful(subjectToTransform.createSubjectFromResult(builder.build()));
-    }
-
-    public void isolateParametersIfNotAlready() {
-        transformer.isolateParametersIfNotAlready();
-    }
-
-    @Override
-    public boolean requiresDependencies() {
-        return transformer.requiresDependencies();
-    }
-
-    @Override
-    public String getDisplayName() {
-        return transformer.getDisplayName();
-    }
-
-    @Override
-    public void visitTransformationSteps(Action<? super TransformationStep> action) {
-        action.execute(this);
-    }
-
-    public ImmutableAttributes getFromAttributes() {
-        return transformer.getFromAttributes();
-    }
-
-    public ImmutableAttributes getToAttributes() {
-        return transformer.getToAttributes();
-    }
-
-    @Override
-    public String toString() {
-        return transformer.getDisplayName();
-    }
-
-    @Override
-    public void visitDependencies(TaskDependencyResolveContext context) {
-        transformer.visitDependencies(context);
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationSubject.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationSubject.java
deleted file mode 100644
index e8795b4..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationSubject.java
+++ /dev/null
@@ -1,104 +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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.api.Describable;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
-
-import java.io.File;
-
-/**
- * Subject which is transformed or the result of a transformation.
- */
-public abstract class TransformationSubject implements Describable {
-
-    public static TransformationSubject initial(ResolvableArtifact artifact) {
-        return new InitialArtifactTransformationSubject(artifact);
-    }
-
-    /**
-     * The files which should be transformed.
-     */
-    public abstract ImmutableList<File> getFiles();
-
-    /**
-     * Component identifier of the initial subject.
-     */
-    public abstract ComponentIdentifier getInitialComponentIdentifier();
-
-    /**
-     * Creates a subsequent subject by having transformed this subject.
-     */
-    public TransformationSubject createSubjectFromResult(ImmutableList<File> result) {
-        return new SubsequentTransformationSubject(this, result);
-    }
-
-    private static class InitialArtifactTransformationSubject extends TransformationSubject {
-        private final ResolvableArtifact artifact;
-
-        public InitialArtifactTransformationSubject(ResolvableArtifact artifact) {
-            this.artifact = artifact;
-        }
-
-        @Override
-        public ImmutableList<File> getFiles() {
-            return ImmutableList.of(artifact.getFile());
-        }
-
-        @Override
-        public String getDisplayName() {
-            return artifact.getId().getDisplayName();
-        }
-
-        @Override
-        public ComponentIdentifier getInitialComponentIdentifier() {
-            return artifact.getId().getComponentIdentifier();
-        }
-    }
-
-    private static class SubsequentTransformationSubject extends TransformationSubject {
-        private final TransformationSubject previous;
-        private final ImmutableList<File> files;
-
-        public SubsequentTransformationSubject(TransformationSubject previous, ImmutableList<File> files) {
-            this.previous = previous;
-            this.files = files;
-        }
-
-        @Override
-        public ImmutableList<File> getFiles() {
-            return files;
-        }
-
-        @Override
-        public ComponentIdentifier getInitialComponentIdentifier() {
-            return previous.getInitialComponentIdentifier();
-        }
-
-        @Override
-        public String getDisplayName() {
-            return previous.getDisplayName();
-        }
-
-        @Override
-        public String toString() {
-            return getDisplayName();
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationWorkspaceServices.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationWorkspaceServices.java
deleted file mode 100644
index e730573..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationWorkspaceServices.java
+++ /dev/null
@@ -1,27 +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.api.internal.artifacts.transform;
-
-import org.gradle.cache.Cache;
-import org.gradle.internal.Try;
-import org.gradle.internal.execution.UnitOfWork;
-import org.gradle.internal.execution.workspace.WorkspaceProvider;
-
-public interface TransformationWorkspaceServices {
-    WorkspaceProvider getWorkspaceProvider();
-    Cache<UnitOfWork.Identity, Try<TransformationResult>> getIdentityCache();
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedExternalArtifactSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedExternalArtifactSet.java
index 0526530..ca619e3 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedExternalArtifactSet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedExternalArtifactSet.java
@@ -35,11 +35,11 @@ public TransformedExternalArtifactSet(
         ResolvedArtifactSet delegate,
         ImmutableAttributes target,
         List<? extends Capability> capabilities,
-        Transformation transformation,
-        ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory,
+        TransformChain transformChain,
+        TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory,
         CalculatedValueContainerFactory calculatedValueContainerFactory
     ) {
-        super(componentIdentifier, delegate, target, capabilities, transformation, dependenciesResolverFactory, calculatedValueContainerFactory);
+        super(componentIdentifier, delegate, target, capabilities, transformChain, dependenciesResolverFactory, calculatedValueContainerFactory);
     }
 
     public TransformedExternalArtifactSet(CalculatedValueContainer<ImmutableList<Artifacts>, AbstractTransformedArtifactSet.CalculateArtifacts> result) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedProjectArtifactSet.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedProjectArtifactSet.java
index 4524713..d85ca44 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedProjectArtifactSet.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedProjectArtifactSet.java
@@ -17,7 +17,6 @@
 package org.gradle.api.internal.artifacts.transform;
 
 import org.gradle.api.Action;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.EndCollection;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
@@ -40,24 +39,11 @@
 public class TransformedProjectArtifactSet implements TransformedArtifactSet, FileCollectionInternal.Source, ResolvedArtifactSet.Artifacts {
 
     private final ComponentVariantIdentifier targetVariant;
-    private final Collection<TransformationNode> transformedArtifacts;
+    private final Collection<TransformStepNode> transformedArtifacts;
 
     public TransformedProjectArtifactSet(
         ComponentVariantIdentifier targetVariant,
-        ResolvedArtifactSet delegate,
-        AttributeContainer sourceAttributes,
-        VariantDefinition variantDefinition,
-        ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory,
-        TransformationNodeFactory transformationNodeFactory
-    ) {
-        this.targetVariant = targetVariant;
-        TransformUpstreamDependenciesResolver dependenciesResolver = dependenciesResolverFactory.create(targetVariant.getComponentId(), variantDefinition.getTransformation());
-        this.transformedArtifacts = transformationNodeFactory.create(delegate, targetVariant, variantDefinition.getTransformationStep(), sourceAttributes, dependenciesResolver);
-    }
-
-    public TransformedProjectArtifactSet(
-        ComponentVariantIdentifier targetVariant,
-        Collection<TransformationNode> transformedArtifacts
+        Collection<TransformStepNode> transformedArtifacts
     ) {
         this.targetVariant = targetVariant;
         this.transformedArtifacts = transformedArtifacts;
@@ -67,7 +53,7 @@ public ComponentVariantIdentifier getTargetVariant() {
         return targetVariant;
     }
 
-    public Collection<TransformationNode> getTransformedArtifacts() {
+    public Collection<TransformStepNode> getTransformedArtifacts() {
         return transformedArtifacts;
     }
 
@@ -89,9 +75,9 @@ public void startFinalization(BuildOperationQueue<RunnableBuildOperation> action
     @Override
     public void visit(ArtifactVisitor visitor) {
         DisplayName displayName = Describables.of(targetVariant.getComponentId());
-        for (TransformationNode node : transformedArtifacts) {
+        for (TransformStepNode node : transformedArtifacts) {
             node.executeIfNotAlready();
-            Try<TransformationSubject> transformedSubject = node.getTransformedSubject();
+            Try<TransformStepSubject> transformedSubject = node.getTransformedSubject();
             if (transformedSubject.isSuccessful()) {
                 for (File file : transformedSubject.get().getFiles()) {
                     visitor.visitArtifact(displayName, targetVariant.getAttributes(), targetVariant.getCapabilities(), node.getInputArtifact().transformedTo(file));
@@ -107,14 +93,14 @@ public void visit(ArtifactVisitor visitor) {
     @Override
     public void visitDependencies(TaskDependencyResolveContext context) {
         if (!transformedArtifacts.isEmpty()) {
-            context.add(new DefaultTransformationDependency(transformedArtifacts));
+            context.add(new DefaultTransformNodeDependency(transformedArtifacts));
         }
     }
 
     @Override
     public void visitTransformSources(TransformSourceVisitor visitor) {
-        for (TransformationNode transformationNode : transformedArtifacts) {
-            visitor.visitTransform(transformationNode);
+        for (TransformStepNode transformStepNode : transformedArtifacts) {
+            visitor.visitTransform(transformStepNode);
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedVariant.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedVariant.java
index b347823..7584e29 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedVariant.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedVariant.java
@@ -21,7 +21,7 @@
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 
 /**
- * Represents a variant which is produced as the result of applying an artifact transformation chain
+ * Represents a variant which is produced as the result of applying an artifact transform chain
  * to a root producer variant.
  */
 public class TransformedVariant implements HasAttributes {
@@ -34,17 +34,17 @@ public TransformedVariant(ResolvedVariant root, VariantDefinition chain) {
     }
 
     /**
-     * @return The chain of variants which result from applying the transformation chain to the root variant.
+     * @return The transformed variant which results from applying the transform chain to the root variant.
      */
-    public VariantDefinition getVariantChain() {
+    public VariantDefinition getTransformedVariantDefinition() {
         return chain;
     }
 
     /**
-     * @return The transformation chain to apply to the root producer variant.
+     * @return The transform chain to apply to the root producer variant.
      */
-    public Transformation getTransformation() {
-        return chain.getTransformation();
+    public TransformChain getTransformChain() {
+        return chain.getTransformChain();
     }
 
     /**
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedVariantFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedVariantFactory.java
index 34fd51b..017ede4 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedVariantFactory.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformedVariantFactory.java
@@ -24,10 +24,10 @@ public interface TransformedVariantFactory {
     ResolvedArtifactSet transformedExternalArtifacts(ComponentIdentifier componentIdentifier,
                                                      ResolvedVariant sourceVariant,
                                                      VariantDefinition variantDefinition,
-                                                     ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory);
+                                                     TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory);
 
     ResolvedArtifactSet transformedProjectArtifacts(ComponentIdentifier componentIdentifier,
                                                     ResolvedVariant sourceVariant,
                                                     VariantDefinition variantDefinition,
-                                                    ExtraExecutionGraphDependenciesResolverFactory dependenciesResolverFactory);
+                                                    TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transformer.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transformer.java
deleted file mode 100644
index 9e31ee7..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/Transformer.java
+++ /dev/null
@@ -1,84 +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.api.internal.artifacts.transform;
-
-import org.gradle.api.Describable;
-import org.gradle.api.file.FileSystemLocation;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.api.internal.tasks.TaskDependencyContainer;
-import org.gradle.api.provider.Provider;
-import org.gradle.internal.fingerprint.DirectorySensitivity;
-import org.gradle.internal.fingerprint.FileNormalizer;
-import org.gradle.internal.fingerprint.LineEndingSensitivity;
-import org.gradle.internal.hash.HashCode;
-import org.gradle.work.InputChanges;
-
-import javax.annotation.Nullable;
-import java.io.File;
-
-/**
- * The actual code which needs to be executed to transform a file.
- *
- * This encapsulates the public interface {@link org.gradle.api.artifacts.transform.TransformAction} into an internal type.
- */
-public interface Transformer extends Describable, TaskDependencyContainer {
-    Class<?> getImplementationClass();
-
-    ImmutableAttributes getFromAttributes();
-
-    ImmutableAttributes getToAttributes();
-
-    /**
-     * Whether the transformer requires dependencies of the transformed artifact to be injected.
-     */
-    boolean requiresDependencies();
-
-    /**
-     * Whether the transformer requires {@link InputChanges} to be injected.
-     */
-    boolean requiresInputChanges();
-
-    /**
-     * Whether the transformer is cacheable.
-     */
-    boolean isCacheable();
-
-    TransformationResult transform(Provider<FileSystemLocation> inputArtifactProvider, File outputDir, ArtifactTransformDependencies dependencies, @Nullable InputChanges inputChanges);
-
-    /**
-     * The hash of the secondary inputs of the transformer.
-     *
-     * This includes the parameters and the implementation.
-     */
-    HashCode getSecondaryInputHash();
-
-    void isolateParametersIfNotAlready();
-
-    FileNormalizer getInputArtifactNormalizer();
-
-    FileNormalizer getInputArtifactDependenciesNormalizer();
-
-    boolean isIsolated();
-
-    DirectorySensitivity getInputArtifactDirectorySensitivity();
-
-    DirectorySensitivity getInputArtifactDependenciesDirectorySensitivity();
-
-    LineEndingSensitivity getInputArtifactLineEndingNormalization();
-
-    LineEndingSensitivity getInputArtifactDependenciesLineEndingNormalization();
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformerInvocationFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformerInvocationFactory.java
deleted file mode 100644
index 29e4127..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformerInvocationFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2017 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.api.internal.artifacts.transform;
-
-import com.google.common.collect.ImmutableList;
-import org.gradle.internal.Deferrable;
-import org.gradle.internal.Try;
-import org.gradle.internal.execution.InputFingerprinter;
-
-import javax.annotation.concurrent.ThreadSafe;
-import java.io.File;
-
-@ThreadSafe
-public interface TransformerInvocationFactory {
-    /**
-     * Returns an invocation which allows invoking the actual transformer.
-     */
-    Deferrable<Try<ImmutableList<File>>> createInvocation(
-        Transformer transformer,
-        File inputArtifact,
-        ArtifactTransformDependencies dependencies,
-        TransformationSubject subject,
-        InputFingerprinter inputFingerprinter);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListener.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListener.java
index 7b4da48..22ae2b7 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListener.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListener.java
@@ -39,18 +39,18 @@
 import java.util.List;
 
 public class TransformingAsyncArtifactListener implements ResolvedArtifactSet.Visitor {
-    private final List<BoundTransformationStep> transformationSteps;
+    private final List<BoundTransformStep> transformSteps;
     private final ImmutableAttributes target;
     private final List<? extends Capability> capabilities;
     private final ImmutableList.Builder<ResolvedArtifactSet.Artifacts> result;
 
     public TransformingAsyncArtifactListener(
-        List<BoundTransformationStep> transformationSteps,
+        List<BoundTransformStep> transformSteps,
         ImmutableAttributes target,
         List<? extends Capability> capabilities,
         ImmutableList.Builder<ResolvedArtifactSet.Artifacts> result
     ) {
-        this.transformationSteps = transformationSteps;
+        this.transformSteps = transformSteps;
         this.target = target;
         this.capabilities = capabilities;
         this.result = result;
@@ -61,7 +61,7 @@ public void visitArtifacts(ResolvedArtifactSet.Artifacts artifacts) {
         artifacts.visit(new ArtifactVisitor() {
             @Override
             public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, List<? extends Capability> variantCapabilities, ResolvableArtifact artifact) {
-                TransformedArtifact transformedArtifact = new TransformedArtifact(variantName, target, capabilities, artifact, transformationSteps);
+                TransformedArtifact transformedArtifact = new TransformedArtifact(variantName, target, capabilities, artifact, transformSteps);
                 result.add(transformedArtifact);
             }
 
@@ -88,16 +88,16 @@ public static class TransformedArtifact implements ResolvedArtifactSet.Artifacts
         private final List<? extends Capability> capabilities;
         private final ResolvableArtifact artifact;
         private final ImmutableAttributes target;
-        private final List<BoundTransformationStep> transformationSteps;
-        private Try<TransformationSubject> transformedSubject;
-        private Deferrable<Try<TransformationSubject>> invocation;
+        private final List<BoundTransformStep> transformSteps;
+        private Try<TransformStepSubject> transformedSubject;
+        private Deferrable<Try<TransformStepSubject>> invocation;
 
-        public TransformedArtifact(DisplayName variantName, ImmutableAttributes target, List<? extends Capability> capabilities, ResolvableArtifact artifact, List<BoundTransformationStep> transformationSteps) {
+        public TransformedArtifact(DisplayName variantName, ImmutableAttributes target, List<? extends Capability> capabilities, ResolvableArtifact artifact, List<BoundTransformStep> transformSteps) {
             this.variantName = variantName;
             this.artifact = artifact;
             this.target = target;
             this.capabilities = capabilities;
-            this.transformationSteps = transformationSteps;
+            this.transformSteps = transformSteps;
         }
 
         public DisplayName getVariantName() {
@@ -116,8 +116,8 @@ public List<? extends Capability> getCapabilities() {
             return capabilities;
         }
 
-        public List<BoundTransformationStep> getTransformationSteps() {
-            return transformationSteps;
+        public List<BoundTransformStep> getTransformSteps() {
+            return transformSteps;
         }
 
         @Override
@@ -125,8 +125,8 @@ public void prepareForVisitingIfNotAlready() {
             // The parameters of the transforms should already be isolated prior to visiting this set.
             // However, in certain cases, the transform's parameters may not be isolated (eg https://github.com/gradle/gradle/issues/23116), so do this now
             // Those cases should be improved so that the parameters are always isolated, for example by always using work nodes to do this work
-            for (BoundTransformationStep step : transformationSteps) {
-                step.getTransformation().isolateParametersIfNotAlready();
+            for (BoundTransformStep step : transformSteps) {
+                step.getTransformStep().isolateParametersIfNotAlready();
             }
         }
 
@@ -169,7 +169,7 @@ private boolean prepareInvocation() {
                 }
             }
 
-            Deferrable<Try<TransformationSubject>> invocation = createInvocation();
+            Deferrable<Try<TransformStepSubject>> invocation = createInvocation();
             synchronized (this) {
                 this.invocation = invocation;
                 if (invocation.getCompleted().isPresent()) {
@@ -183,7 +183,7 @@ private boolean prepareInvocation() {
             }
         }
 
-        private Try<TransformationSubject> finalizeValue() {
+        private Try<TransformStepSubject> finalizeValue() {
             synchronized (this) {
                 if (transformedSubject != null) {
                     return transformedSubject;
@@ -198,7 +198,7 @@ private Try<TransformationSubject> finalizeValue() {
                 }
             }
 
-            Deferrable<Try<TransformationSubject>> invocation;
+            Deferrable<Try<TransformStepSubject>> invocation;
             synchronized (this) {
                 invocation = this.invocation;
             }
@@ -206,23 +206,23 @@ private Try<TransformationSubject> finalizeValue() {
             if (invocation == null) {
                 invocation = createInvocation();
             }
-            Try<TransformationSubject> result = invocation.completeAndGet();
+            Try<TransformStepSubject> result = invocation.completeAndGet();
             synchronized (this) {
                 transformedSubject = result;
                 return result;
             }
         }
 
-        private Deferrable<Try<TransformationSubject>> createInvocation() {
-            TransformationSubject initialSubject = TransformationSubject.initial(artifact);
-            BoundTransformationStep initialStep = transformationSteps.get(0);
-            Deferrable<Try<TransformationSubject>> invocation = initialStep.getTransformation()
+        private Deferrable<Try<TransformStepSubject>> createInvocation() {
+            TransformStepSubject initialSubject = TransformStepSubject.initial(artifact);
+            BoundTransformStep initialStep = transformSteps.get(0);
+            Deferrable<Try<TransformStepSubject>> invocation = initialStep.getTransformStep()
                 .createInvocation(initialSubject, initialStep.getUpstreamDependencies(), null);
-            for (int i = 1; i < transformationSteps.size(); i++) {
-                BoundTransformationStep nextStep = transformationSteps.get(i);
+            for (int i = 1; i < transformSteps.size(); i++) {
+                BoundTransformStep nextStep = transformSteps.get(i);
                 invocation = invocation
                     .flatMap(intermediateResult -> intermediateResult
-                        .map(intermediateSubject -> nextStep.getTransformation()
+                        .map(intermediateSubject -> nextStep.getTransformStep()
                             .createInvocation(intermediateSubject, nextStep.getUpstreamDependencies(), null))
                         .getOrMapFailure(failure -> Deferrable.completed(Try.failure(failure))));
             }
@@ -231,7 +231,7 @@ private Deferrable<Try<TransformationSubject>> createInvocation() {
 
         @Override
         public void visit(ArtifactVisitor visitor) {
-            Try<TransformationSubject> transformedSubject = finalizeValue();
+            Try<TransformStepSubject> transformedSubject = finalizeValue();
             transformedSubject.ifSuccessfulOrElse(
                 subject -> {
                     for (File output : subject.getFiles()) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantDefinition.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantDefinition.java
index 53f181e..2702e1b 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantDefinition.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantDefinition.java
@@ -21,26 +21,26 @@
 import javax.annotation.Nullable;
 
 /**
- * Defines a variant that is the result of applying a transformation chain to produce a variant with the given attributes.
+ * Defines a variant that is the result of applying a transform chain to produce a variant with the given attributes.
  */
 public interface VariantDefinition {
     /**
-     * @return This variant's attributes after transformations are applied.
+     * @return This variant's attributes after all chain transforms are applied.
      */
     ImmutableAttributes getTargetAttributes();
 
     /**
-     * @return The transformation chain which transforms the root variant to this variant.
+     * @return The transform chain which transforms the root variant to this variant.
      */
-    Transformation getTransformation();
+    TransformChain getTransformChain();
 
     /**
-     * The single transformation step which transforms the previous variant to this variant.
+     * The single transform step which transforms the previous variant to this variant.
      */
-    TransformationStep getTransformationStep();
+    TransformStep getTransformStep();
 
     /**
-     * @return The previous variant in the transformation chain. If null, this variant uses the producer variant as its root.
+     * @return The previous variant in the transform chain. If null, this variant uses the producer variant as its root.
      */
     @Nullable
     VariantDefinition getPrevious();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantSelector.java
index 119cbcf..6da6b66 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantSelector.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantSelector.java
@@ -29,9 +29,21 @@ public interface VariantSelector {
      */
     ResolvedArtifactSet select(ResolvedVariantSet candidates, Factory factory);
 
+    /**
+     * As per {@link #select(ResolvedVariantSet, Factory)} but ignores no matching variants.
+     */
+    default ResolvedArtifactSet maybeSelect(ResolvedVariantSet candidates, Factory factory) {
+        return select(candidates, factory);
+    }
+
     ImmutableAttributes getRequestedAttributes();
 
     interface Factory {
-        ResolvedArtifactSet asTransformed(ResolvedVariant sourceVariant, VariantDefinition variantDefinition, ExtraExecutionGraphDependenciesResolverFactory dependenciesResolver, TransformedVariantFactory transformedVariantFactory);
+        ResolvedArtifactSet asTransformed(
+            ResolvedVariant sourceVariant,
+            VariantDefinition variantDefinition,
+            TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory,
+            TransformedVariantFactory transformedVariantFactory
+        );
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantSelectorFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantSelectorFactory.java
new file mode 100644
index 0000000..1e1573b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/VariantSelectorFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 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.api.internal.artifacts.transform;
+
+import org.gradle.api.internal.attributes.AttributeContainerInternal;
+
+public interface VariantSelectorFactory {
+    /**
+     * Returns a selector that selects the variant matching the supplied attributes, or which can be transformed to match.
+     */
+    VariantSelector create(
+        AttributeContainerInternal consumerAttributes,
+        boolean allowNoMatchingVariants,
+        boolean selectFromAllVariants,
+        TransformUpstreamDependenciesResolverFactory dependenciesResolverFactory
+    );
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/type/DefaultArtifactTypeRegistry.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/type/DefaultArtifactTypeRegistry.java
index a8a9bc3..a6562fc 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/type/DefaultArtifactTypeRegistry.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/type/DefaultArtifactTypeRegistry.java
@@ -20,7 +20,7 @@
 import org.gradle.api.artifacts.type.ArtifactTypeContainer;
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
 import org.gradle.api.internal.CollectionCallbackActionDecorator;
-import org.gradle.api.internal.artifacts.ArtifactTransformRegistration;
+import org.gradle.api.internal.artifacts.TransformRegistration;
 import org.gradle.api.internal.artifacts.VariantTransformRegistry;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
@@ -51,29 +51,32 @@ public DefaultArtifactTypeRegistry(Instantiator instantiator, ImmutableAttribute
 
     @Override
     public void visitArtifactTypes(Consumer<? super ImmutableAttributes> action) {
-        Set<ImmutableAttributes> seen = new HashSet<>();
+        Set<String> seen = new HashSet<>();
 
         if (artifactTypeDefinitions != null) {
             for (ArtifactTypeDefinition artifactTypeDefinition : artifactTypeDefinitions) {
-                ImmutableAttributes attributes = ((AttributeContainerInternal) artifactTypeDefinition.getAttributes()).asImmutable();
-                attributes = attributesFactory.concat(attributesFactory.of(ARTIFACT_TYPE_ATTRIBUTE, artifactTypeDefinition.getName()), attributes);
-                if (seen.add(attributes)) {
+                if (seen.add(artifactTypeDefinition.getName())) {
+                    ImmutableAttributes attributes = ((AttributeContainerInternal) artifactTypeDefinition.getAttributes()).asImmutable();
+                    attributes = attributesFactory.concat(attributesFactory.of(ARTIFACT_TYPE_ATTRIBUTE, artifactTypeDefinition.getName()), attributes);
                     action.accept(attributes);
                 }
             }
         }
 
-        for (ArtifactTransformRegistration transform : transformRegistry.getTransforms()) {
-            AttributeContainerInternal sourceAttributes = transform.getFrom();
+        for (TransformRegistration registration : transformRegistry.getRegistrations()) {
+            AttributeContainerInternal sourceAttributes = registration.getFrom();
             String format = sourceAttributes.getAttribute(ARTIFACT_TYPE_ATTRIBUTE);
-            // Some format that is not already registered
-            if (format != null) {
-                ImmutableAttributes attributes = sourceAttributes.asImmutable();
-                if (seen.add(attributes)) {
-                    action.accept(attributes);
-                }
+            if (format != null && seen.add(format)) {
+                // Some artifact type that has not already been visited
+                ImmutableAttributes attributes = attributesFactory.of(ARTIFACT_TYPE_ATTRIBUTE, format);
+                action.accept(attributes);
             }
         }
+
+        if (seen.add(ArtifactTypeDefinition.DIRECTORY_TYPE)) {
+            ImmutableAttributes directory = attributesFactory.of(ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.DIRECTORY_TYPE);
+            action.accept(directory);
+        }
     }
 
     @Override
@@ -86,17 +89,16 @@ public ArtifactTypeContainer create() {
 
     @Override
     public ImmutableAttributes mapAttributesFor(File file) {
-        ImmutableAttributes attributes = ImmutableAttributes.EMPTY;
         if (file.isDirectory()) {
-            attributes = attributesFactory.of(ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.DIRECTORY_TYPE);
+            return attributesFactory.of(ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.DIRECTORY_TYPE);
         } else {
+            ImmutableAttributes attributes = ImmutableAttributes.EMPTY;
             String extension = Files.getFileExtension(file.getName());
             if (artifactTypeDefinitions != null) {
                 attributes = applyForExtension(attributes, extension);
             }
-            attributes = attributesFactory.concat(attributesFactory.of(ARTIFACT_TYPE_ATTRIBUTE, extension), attributes);
+            return attributesFactory.concat(attributesFactory.of(ARTIFACT_TYPE_ATTRIBUTE, extension), attributes);
         }
-        return attributes;
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/verification/exceptions/InvalidGpgKeyIdsException.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/verification/exceptions/InvalidGpgKeyIdsException.java
index a1ec04c..c8f5416 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/verification/exceptions/InvalidGpgKeyIdsException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/verification/exceptions/InvalidGpgKeyIdsException.java
@@ -47,12 +47,10 @@ public InvalidGpgKeyIdsException(List<String> wrongKeys) {
      * Idea for this method is that you can pass a higher-level {@link TreeFormatter} into here, and get a coherent, nice error message printed out - so the user will see a nice chain of causes.
      */
     public void formatMessage(TreeFormatter formatter) {
-        final DocumentationRegistry documentationRegistry = new DocumentationRegistry();
-        final String documentLink = documentationRegistry.getDocumentationFor("dependency_verification", "sec:understanding-signature-verification");
+        final String documentLink = new DocumentationRegistry()
+            .getDocumentationRecommendationFor("on this", "dependency_verification", "sec:understanding-signature-verification");
 
-        formatter.node(
-            String.format("The following trusted GPG IDs are not in a minimum 160-bit fingerprint format (see: %s):", documentLink)
-        );
+        formatter.node(String.format("The following trusted GPG IDs are not in a minimum 160-bit fingerprint format (%s):", documentLink));
         formatter.startChildren();
         wrongKeys
             .stream()
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/verification/verifier/SignatureVerificationFailure.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/verification/verifier/SignatureVerificationFailure.java
index ec4dbd2..70be5de 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/verification/verifier/SignatureVerificationFailure.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/artifacts/verification/verifier/SignatureVerificationFailure.java
@@ -18,8 +18,8 @@
 import com.google.common.collect.Sets;
 import org.bouncycastle.openpgp.PGPPublicKey;
 import org.bouncycastle.openpgp.PGPPublicKeyRing;
-import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.verification.utils.PGPUtils;
 import org.gradle.internal.logging.text.TreeFormatter;
+import org.gradle.security.internal.PGPUtils;
 import org.gradle.security.internal.PublicKeyResultBuilder;
 import org.gradle.security.internal.PublicKeyService;
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/attributes/AttributeDesugaring.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/attributes/AttributeDesugaring.java
index f31792d..a17f07d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/attributes/AttributeDesugaring.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/attributes/AttributeDesugaring.java
@@ -23,10 +23,13 @@
 import org.gradle.internal.Cast;
 import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
 import org.gradle.internal.component.local.model.DefaultProjectComponentSelector;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
 
 import java.util.Map;
 import java.util.Set;
 
+@ServiceScope(Scopes.BuildTree.class)
 public class AttributeDesugaring {
     private final Map<ImmutableAttributes, ImmutableAttributes> desugared = Maps.newIdentityHashMap();
     private final ImmutableAttributesFactory attributesFactory;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/attributes/DefaultAttributesSchema.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/attributes/DefaultAttributesSchema.java
index b4b1d9a..d38dd16 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/attributes/DefaultAttributesSchema.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/attributes/DefaultAttributesSchema.java
@@ -24,7 +24,7 @@
 import org.gradle.internal.component.model.AttributeMatcher;
 import org.gradle.internal.component.model.AttributeSelectionSchema;
 import org.gradle.internal.component.model.AttributeSelectionUtils;
-import org.gradle.internal.component.model.ComponentAttributeMatcher;
+import org.gradle.internal.component.model.DefaultAttributeMatcher;
 import org.gradle.internal.component.model.DefaultCompatibilityCheckResult;
 import org.gradle.internal.component.model.DefaultMultipleCandidateResult;
 import org.gradle.internal.instantiation.InstantiatorFactory;
@@ -100,7 +100,7 @@ public boolean hasAttribute(Attribute<?> key) {
     @Override
     public AttributeMatcher withProducer(AttributesSchemaInternal producerSchema) {
         return matcherCache.computeIfAbsent(producerSchema, key ->
-            new ComponentAttributeMatcher(new DefaultAttributeSelectionSchema(this, producerSchema)));
+            new DefaultAttributeMatcher(new DefaultAttributeSelectionSchema(this, producerSchema)));
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/catalog/DefaultVersionCatalogBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/catalog/DefaultVersionCatalogBuilder.java
index 75bcdf5..bab8ec1 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/api/internal/catalog/DefaultVersionCatalogBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/api/internal/catalog/DefaultVersionCatalogBuilder.java
@@ -21,7 +21,6 @@
 import com.google.common.collect.Interner;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import com.google.common.io.Files;
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.Configuration;
@@ -32,6 +31,7 @@
 import org.gradle.api.attributes.Usage;
 import org.gradle.api.internal.artifacts.DependencyResolutionServices;
 import org.gradle.api.internal.artifacts.ImmutableVersionConstraint;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.artifacts.dependencies.DefaultImmutableVersionConstraint;
 import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint;
 import org.gradle.api.internal.catalog.parser.DependenciesModelHelper;
@@ -50,7 +50,6 @@
 
 import javax.annotation.Nullable;
 import javax.inject.Inject;
-import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -234,14 +233,12 @@ private Configuration createResolvableConfiguration(DependencyResolutionServices
         // The zero at the end of the configuration comes from the previous implementation;
         // Multiple files could be imported, and all members of the list were given their own configuration, postfixed by the index in the array.
         // After moving this into a single-file import, we didn't want to break the lock files generated for the configuration, so we simply kept the zero.
-        Configuration cnf = drs.getConfigurationContainer().create("incomingCatalogFor" + StringUtils.capitalize(name) + "0");
+        Configuration cnf = ((RoleBasedConfigurationContainerInternal) drs.getConfigurationContainer()).resolvableBucket("incomingCatalogFor" + StringUtils.capitalize(name) + "0");
         cnf.getResolutionStrategy().activateDependencyLocking();
         cnf.attributes(attrs -> {
             attrs.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.class, Category.REGULAR_PLATFORM));
             attrs.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.VERSION_CATALOG));
         });
-        cnf.setCanBeResolved(true);
-        cnf.setCanBeConsumed(false);
         return cnf;
     }
 
@@ -258,7 +255,6 @@ public void from(Object dependencyNotation) {
             );
         }
     }
-
     private void importCatalogFromFile(File modelFile) {
         if (!FileUtils.hasExtensionIgnoresCase(modelFile.getName(), "toml")) {
             throwVersionCatalogProblem(VersionCatalogProblemId.UNSUPPORTED_FILE_FORMAT, spec ->
@@ -276,9 +272,10 @@ private void importCatalogFromFile(File modelFile) {
                     .documented()
             );
         }
-        Instrumented.fileObserved(modelFile, getClass().getName());
+
+        Instrumented.fileOpened(modelFile, getClass().getName());
         try {
-            TomlCatalogFileParser.parse(new ByteArrayInputStream(Files.toByteArray(modelFile)), this);
+            TomlCatalogFileParser.parse(modelFile.toPath(), this);
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/AmbiguousConfigurationSelectionException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/AmbiguousConfigurationSelectionException.java
index 7204b65..dcf6348 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/AmbiguousConfigurationSelectionException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/AmbiguousConfigurationSelectionException.java
@@ -26,6 +26,7 @@
 import org.gradle.internal.component.model.AttributeMatcher;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.component.model.VariantGraphResolveMetadata;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.exceptions.StyledException;
 import org.gradle.internal.logging.text.StyledTextOutput;
 import org.gradle.internal.logging.text.TreeFormatter;
@@ -39,16 +40,16 @@
 public class AmbiguousConfigurationSelectionException extends StyledException {
     public AmbiguousConfigurationSelectionException(AttributeDescriber describer, AttributeContainerInternal fromConfigurationAttributes,
                                                     AttributeMatcher attributeMatcher,
-                                                    List<? extends VariantGraphResolveMetadata> matches,
+                                                    List<? extends VariantGraphResolveState> matches,
                                                     ComponentGraphResolveMetadata targetComponent,
                                                     boolean variantAware,
-                                                    Set<VariantGraphResolveMetadata> discarded) {
+                                                    Set<VariantGraphResolveState> discarded) {
         super(generateMessage(new StyledDescriber(describer), fromConfigurationAttributes, attributeMatcher, matches, discarded, targetComponent, variantAware));
     }
 
-    private static String generateMessage(AttributeDescriber describer, AttributeContainerInternal fromConfigurationAttributes, AttributeMatcher attributeMatcher, List<? extends VariantGraphResolveMetadata> matches, Set<VariantGraphResolveMetadata> discarded, ComponentGraphResolveMetadata targetComponent, boolean variantAware) {
-        Map<String, VariantGraphResolveMetadata> ambiguousVariants = new TreeMap<>();
-        for (VariantGraphResolveMetadata match : matches) {
+    private static String generateMessage(AttributeDescriber describer, AttributeContainerInternal fromConfigurationAttributes, AttributeMatcher attributeMatcher, List<? extends VariantGraphResolveState> matches, Set<VariantGraphResolveState> discarded, ComponentGraphResolveMetadata targetComponent, boolean variantAware) {
+        Map<String, VariantGraphResolveState> ambiguousVariants = new TreeMap<>();
+        for (VariantGraphResolveState match : matches) {
             ambiguousVariants.put(match.getName(), match);
         }
         TreeFormatter formatter = new TreeFormatter();
@@ -68,16 +69,16 @@ private static String generateMessage(AttributeDescriber describer, AttributeCon
         // We're sorting the names of the variants and later attributes
         // to make sure the output is consistently the same between invocations
         formatter.startChildren();
-        for (VariantGraphResolveMetadata ambiguousVariant : ambiguousVariants.values()) {
-            formatConfiguration(formatter, targetComponent, fromConfigurationAttributes, attributeMatcher, ambiguousVariant, variantAware, true, describer);
+        for (VariantGraphResolveState ambiguousVariant : ambiguousVariants.values()) {
+            formatConfiguration(formatter, targetComponent, fromConfigurationAttributes, attributeMatcher, ambiguousVariant.getMetadata(), variantAware, true, describer);
         }
         formatter.endChildren();
         if (!discarded.isEmpty()) {
             formatter.node("The following " + configTerm + " were also considered but didn't match the requested attributes:");
             formatter.startChildren();
             discarded.stream()
-                .sorted(Comparator.comparing(VariantGraphResolveMetadata::getName))
-                .forEach(discardedConf -> formatConfiguration(formatter, targetComponent, fromConfigurationAttributes, attributeMatcher, discardedConf, variantAware, false, describer));
+                .sorted(Comparator.comparing(VariantGraphResolveState::getName))
+                .forEach(discardedConf -> formatConfiguration(formatter, targetComponent, fromConfigurationAttributes, attributeMatcher, discardedConf.getMetadata(), variantAware, false, describer));
             formatter.endChildren();
         }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/IncompatibleConfigurationSelectionException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/IncompatibleConfigurationSelectionException.java
index 7b64c73..7aeb085 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/IncompatibleConfigurationSelectionException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/IncompatibleConfigurationSelectionException.java
@@ -20,6 +20,7 @@
 import org.gradle.api.internal.attributes.AttributeDescriber;
 import org.gradle.internal.component.model.AttributeMatcher;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
+import org.gradle.internal.component.model.ConfigurationGraphResolveState;
 import org.gradle.internal.exceptions.StyledException;
 import org.gradle.internal.logging.text.StyledTextOutput;
 import org.gradle.internal.logging.text.TreeFormatter;
@@ -31,7 +32,7 @@ public IncompatibleConfigurationSelectionException(
         AttributeContainerInternal fromConfigurationAttributes,
         AttributeMatcher attributeMatcher,
         ComponentGraphResolveMetadata targetComponent,
-        String targetConfiguration,
+        ConfigurationGraphResolveState targetConfiguration,
         boolean variantAware,
         AttributeDescriber describer) {
         super(generateMessage(fromConfigurationAttributes, attributeMatcher, targetComponent, targetConfiguration, variantAware, describer));
@@ -40,12 +41,12 @@ public IncompatibleConfigurationSelectionException(
     private static String generateMessage(AttributeContainerInternal fromConfigurationAttributes,
                                           AttributeMatcher attributeMatcher,
                                           ComponentGraphResolveMetadata targetComponent,
-                                          String targetConfiguration,
+                                          ConfigurationGraphResolveState targetConfiguration,
                                           boolean variantAware,
                                           AttributeDescriber describer) {
         TreeFormatter formatter = new TreeFormatter();
-        formatter.node((variantAware ? "Variant '" : "Configuration '") + targetConfiguration + "' in " + style(StyledTextOutput.Style.Info, targetComponent.getId().getDisplayName()) + " does not match the consumer attributes");
-        formatConfiguration(formatter, targetComponent, fromConfigurationAttributes, attributeMatcher, targetComponent.getConfiguration(targetConfiguration), variantAware, false, describer);
+        formatter.node("Configuration '" + targetConfiguration.getName() + "' in " + style(StyledTextOutput.Style.Info, targetComponent.getId().getDisplayName()) + " does not match the consumer attributes");
+        formatConfiguration(formatter, targetComponent, fromConfigurationAttributes, attributeMatcher, targetConfiguration.asVariant().getMetadata(), variantAware, false, describer);
         return formatter.toString();
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/LegacyConfigurationsSupplier.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/LegacyConfigurationsSupplier.java
deleted file mode 100644
index f303359..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/LegacyConfigurationsSupplier.java
+++ /dev/null
@@ -1,44 +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.internal.component;
-
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableList;
-import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
-import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata;
-
-import java.util.Set;
-
-public class LegacyConfigurationsSupplier implements Supplier<ImmutableList<? extends ConfigurationGraphResolveMetadata>> {
-    private final ComponentGraphResolveMetadata targetComponent;
-
-    public LegacyConfigurationsSupplier(ComponentGraphResolveMetadata targetComponent) {
-        this.targetComponent = targetComponent;
-    }
-
-    @Override
-    public ImmutableList<? extends ConfigurationGraphResolveMetadata> get() {
-        Set<String> configurationNames = targetComponent.getConfigurationNames();
-        ImmutableList.Builder<ConfigurationGraphResolveMetadata> builder = new ImmutableList.Builder<>();
-        for (String configurationName : configurationNames) {
-            ConfigurationGraphResolveMetadata configuration = targetComponent.getConfiguration(configurationName);
-            if (configuration.isCanBeConsumed()) {
-                builder.add(configuration);
-            }
-        }
-        return builder.build();
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/NoMatchingCapabilitiesException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/NoMatchingCapabilitiesException.java
index d593853..b198133 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/NoMatchingCapabilitiesException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/NoMatchingCapabilitiesException.java
@@ -17,22 +17,22 @@
 
 import org.gradle.api.capabilities.Capability;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
-import org.gradle.internal.component.model.VariantGraphResolveMetadata;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 
 import java.util.Collection;
 import java.util.List;
 
 public class NoMatchingCapabilitiesException extends RuntimeException {
-    public NoMatchingCapabilitiesException(ComponentGraphResolveMetadata targetComponent, Collection<? extends Capability> requestedCapabilities, List<? extends VariantGraphResolveMetadata> candidates) {
+    public NoMatchingCapabilitiesException(ComponentGraphResolveMetadata targetComponent, Collection<? extends Capability> requestedCapabilities, List<? extends VariantGraphResolveState> candidates) {
         super(buildMessage(targetComponent, requestedCapabilities, candidates));
     }
 
-    private static String buildMessage(ComponentGraphResolveMetadata targetComponent, Collection<? extends Capability> requestedCapabilities, List<? extends VariantGraphResolveMetadata> candidates) {
+    private static String buildMessage(ComponentGraphResolveMetadata targetComponent, Collection<? extends Capability> requestedCapabilities, List<? extends VariantGraphResolveState> candidates) {
         StringBuilder sb = new StringBuilder("Unable to find a variant of ");
         sb.append(targetComponent.getId()).append(" providing the requested ");
         sb.append(CapabilitiesSupport.prettifyCapabilities(targetComponent, requestedCapabilities));
         sb.append(":\n");
-        for (VariantGraphResolveMetadata candidate : candidates) {
+        for (VariantGraphResolveState candidate : candidates) {
             sb.append("   - Variant ").append(candidate.getName()).append(" provides ");
             sb.append(CapabilitiesSupport.sortedCapabilityList(targetComponent, candidate.getCapabilities().getCapabilities())).append("\n");
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/NoMatchingConfigurationSelectionException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/NoMatchingConfigurationSelectionException.java
index 2ee89ca..bbae02f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/NoMatchingConfigurationSelectionException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/NoMatchingConfigurationSelectionException.java
@@ -16,17 +16,18 @@
 
 package org.gradle.internal.component;
 
-import com.google.common.base.Optional;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.attributes.AttributeDescriber;
 import org.gradle.internal.component.model.AttributeMatcher;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
+import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata;
+import org.gradle.internal.component.model.GraphSelectionCandidates;
 import org.gradle.internal.component.model.VariantGraphResolveMetadata;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.exceptions.StyledException;
 import org.gradle.internal.logging.text.StyledTextOutput;
 import org.gradle.internal.logging.text.TreeFormatter;
 
-import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
@@ -38,16 +39,22 @@ public NoMatchingConfigurationSelectionException(
         AttributeContainerInternal fromConfigurationAttributes,
         AttributeMatcher attributeMatcher,
         ComponentGraphResolveMetadata targetComponent,
-        boolean variantAware) {
-        super(generateMessage(new StyledDescriber(describer), fromConfigurationAttributes, attributeMatcher, targetComponent, variantAware));
+        GraphSelectionCandidates candidates
+    ) {
+        super(generateMessage(new StyledDescriber(describer), fromConfigurationAttributes, attributeMatcher, targetComponent, candidates));
     }
 
-    private static String generateMessage(AttributeDescriber describer, AttributeContainerInternal fromConfigurationAttributes, AttributeMatcher attributeMatcher, final ComponentGraphResolveMetadata targetComponent, boolean variantAware) {
+    private static String generateMessage(AttributeDescriber describer, AttributeContainerInternal fromConfigurationAttributes, AttributeMatcher attributeMatcher, final ComponentGraphResolveMetadata targetComponent, GraphSelectionCandidates candidates) {
+        boolean variantAware = candidates.isUseVariants();
         Map<String, VariantGraphResolveMetadata> variants = new TreeMap<>();
-        Optional<List<? extends VariantGraphResolveMetadata>> variantsForGraphTraversal = targetComponent.getVariantsForGraphTraversal();
-        List<? extends VariantGraphResolveMetadata> variantsParticipatingInSelection = variantsForGraphTraversal.or(new LegacyConfigurationsSupplier(targetComponent));
-        for (VariantGraphResolveMetadata variant : variantsParticipatingInSelection) {
-            variants.put(variant.getName(), variant);
+        if (variantAware) {
+            for (VariantGraphResolveState variant : candidates.getVariants()) {
+                variants.put(variant.getName(), variant.getMetadata());
+            }
+        } else {
+            for (ConfigurationGraphResolveMetadata configuration : candidates.getCandidateConfigurations()) {
+                variants.put(configuration.getName(), configuration);
+            }
         }
         TreeFormatter formatter = new TreeFormatter();
         String targetVariantText = style(StyledTextOutput.Style.Info, targetComponent.getId().getDisplayName());
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractConfigurationMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractConfigurationMetadata.java
index 2d816d9..c89ad88 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractConfigurationMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractConfigurationMetadata.java
@@ -30,7 +30,6 @@
 import org.gradle.internal.component.model.IvyArtifactName;
 import org.gradle.internal.component.model.ModuleConfigurationMetadata;
 import org.gradle.internal.component.model.VariantResolveMetadata;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
 
 import java.util.List;
 import java.util.Set;
@@ -47,6 +46,7 @@ public abstract class AbstractConfigurationMetadata implements ModuleConfigurati
     private final ImmutableCapabilities capabilities;
     private final boolean externalVariant;
 
+    private final Object lock = new Object();
     // Should be final, and set in constructor
     private ImmutableList<ModuleDependencyMetadata> configDependencies;
     private Factory<List<ModuleDependencyMetadata>> configDependenciesFactory;
@@ -135,11 +135,6 @@ public boolean isCanBeConsumed() {
     }
 
     @Override
-    public DeprecationMessageBuilder.WithDocumentation getConsumptionDeprecation() {
-        return null;
-    }
-
-    @Override
     public boolean isCanBeResolved() {
         return false;
     }
@@ -150,14 +145,18 @@ public boolean isExternalVariant() {
     }
 
     public void setDependencies(List<ModuleDependencyMetadata> dependencies) {
-        assert this.configDependencies == null; // Can only set once: should really be part of the constructor
-        this.configDependencies = ImmutableList.copyOf(dependencies);
+        synchronized (lock) {
+            assert this.configDependencies == null; // Can only set once: should really be part of the constructor
+            this.configDependencies = ImmutableList.copyOf(dependencies);
+        }
     }
 
     public void setConfigDependenciesFactory(Factory<List<ModuleDependencyMetadata>> dependenciesFactory) {
-        assert this.configDependencies == null; // Can only set once: should really be part of the constructor
-        assert this.configDependenciesFactory == null; // Can only set once: should really be part of the constructor
-        this.configDependenciesFactory = dependenciesFactory;
+        synchronized (lock) {
+            assert this.configDependencies == null; // Can only set once: should really be part of the constructor
+            assert this.configDependenciesFactory == null; // Can only set once: should really be part of the constructor
+            this.configDependenciesFactory = dependenciesFactory;
+        }
     }
 
     @Override
@@ -191,11 +190,13 @@ public CapabilitiesMetadata getCapabilities() {
     }
 
     ImmutableList<ModuleDependencyMetadata> getConfigDependencies() {
-        if (configDependenciesFactory != null) {
-            configDependencies = ImmutableList.copyOf(configDependenciesFactory.create());
-            configDependenciesFactory = null;
+        synchronized (lock) {
+            if (configDependenciesFactory != null) {
+                configDependencies = ImmutableList.copyOf(configDependenciesFactory.create());
+                configDependenciesFactory = null;
+            }
+            return configDependencies;
         }
-        return configDependencies;
     }
 
     protected ModuleComponentIdentifier getComponentId() {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractLazyModuleComponentResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractLazyModuleComponentResolveMetadata.java
index d8e1693..82e5ef6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractLazyModuleComponentResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractLazyModuleComponentResolveMetadata.java
@@ -17,7 +17,6 @@
 package org.gradle.internal.component.external.model;
 
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -33,6 +32,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetadata.java
index e4bafc4..e024956 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractModuleComponentResolveMetadata.java
@@ -17,7 +17,6 @@
 package org.gradle.internal.component.external.model;
 
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
@@ -34,6 +33,7 @@
 
 import javax.annotation.Nullable;
 import java.util.List;
+import java.util.Optional;
 
 abstract class AbstractModuleComponentResolveMetadata implements ModuleComponentResolveMetadata {
     private final ImmutableAttributesFactory attributesFactory;
@@ -146,7 +146,6 @@ public String toString() {
         return componentIdentifier.getDisplayName();
     }
 
-    @Nullable
     @Override
     public AttributesSchemaInternal getAttributesSchema() {
         return schema;
@@ -184,7 +183,7 @@ public ModuleComponentArtifactMetadata optionalArtifact(String type, @Nullable S
      * If it can not provide variants, absent must be returned to fall back to traditional configuration selection.
      */
     protected Optional<List<? extends VariantGraphResolveMetadata>> maybeDeriveVariants() {
-        return Optional.absent();
+        return Optional.empty();
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractMutableModuleComponentResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractMutableModuleComponentResolveMetadata.java
index e3bd6d0..ae36689 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractMutableModuleComponentResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractMutableModuleComponentResolveMetadata.java
@@ -526,12 +526,14 @@ public boolean equals(Object o) {
                 && Objects.equal(excludes, that.excludes)
                 && Objects.equal(reason, that.reason)
                 && Objects.equal(attributes, that.attributes)
-                && Objects.equal(requestedCapabilities, that.requestedCapabilities);
+                && Objects.equal(requestedCapabilities, that.requestedCapabilities)
+                && Objects.equal(endorsing, that.endorsing)
+                && Objects.equal(dependencyArtifact, that.dependencyArtifact);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hashCode(group, module, versionConstraint, excludes, reason, attributes);
+            return Objects.hashCode(group, module, versionConstraint, excludes, reason, attributes, endorsing, dependencyArtifact);
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractRealisedModuleComponentResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractRealisedModuleComponentResolveMetadata.java
index 87e81c3..b0b209f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractRealisedModuleComponentResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractRealisedModuleComponentResolveMetadata.java
@@ -17,7 +17,6 @@
 package org.gradle.internal.component.external.model;
 
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
@@ -34,6 +33,7 @@
 import javax.annotation.Nullable;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractVariantBackedConfigurationMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractVariantBackedConfigurationMetadata.java
index cb4a68f..b1e8cbf 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractVariantBackedConfigurationMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/AbstractVariantBackedConfigurationMetadata.java
@@ -32,7 +32,6 @@
 import org.gradle.internal.component.model.IvyArtifactName;
 import org.gradle.internal.component.model.ModuleConfigurationMetadata;
 import org.gradle.internal.component.model.VariantResolveMetadata;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -122,11 +121,6 @@ public boolean isCanBeConsumed() {
     }
 
     @Override
-    public DeprecationMessageBuilder.WithDocumentation getConsumptionDeprecation() {
-        return null;
-    }
-
-    @Override
     public boolean isCanBeResolved() {
         return false;
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ComponentVariant.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ComponentVariant.java
index 5d5d9b9..89d0ced 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ComponentVariant.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ComponentVariant.java
@@ -58,7 +58,6 @@ interface Dependency {
 
         @Nullable
         IvyArtifactName getDependencyArtifact();
-
     }
 
     interface DependencyConstraint {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ConfigurationBoundExternalDependencyMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ConfigurationBoundExternalDependencyMetadata.java
index a649034..7d91763 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ConfigurationBoundExternalDependencyMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ConfigurationBoundExternalDependencyMetadata.java
@@ -26,7 +26,6 @@
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.component.local.model.DefaultProjectDependencyMetadata;
 import org.gradle.internal.component.model.AttributeConfigurationSelector;
-import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.component.model.ComponentGraphResolveState;
 import org.gradle.internal.component.model.ConfigurationMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
@@ -106,16 +105,12 @@ public ExternalDependencyDescriptor getDependencyDescriptor() {
     public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
         // This is a slight different condition than that used for a dependency declared in a Gradle project,
         // which is (targetHasVariants || consumerHasAttributes), relying on the fallback to 'default' for consumer attributes without any variants.
-        if (alwaysUseAttributeMatching || hasVariants(targetComponentState.getMetadata())) {
+        if (alwaysUseAttributeMatching || targetComponentState.getCandidatesForGraphVariantSelection().isUseVariants()) {
             return AttributeConfigurationSelector.selectVariantsUsingAttributeMatching(consumerAttributes, explicitRequestedCapabilities, targetComponentState, consumerSchema, getArtifacts());
         }
         return dependencyDescriptor.selectLegacyConfigurations(componentId, configuration, targetComponentState);
     }
 
-    private boolean hasVariants(ComponentGraphResolveMetadata targetComponent) {
-        return targetComponent.getVariantsForGraphTraversal().isPresent();
-    }
-
     @Override
     public List<IvyArtifactName> getArtifacts() {
         return artifacts;
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentGraphResolveState.java
new file mode 100644
index 0000000..60081dd
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/DefaultModuleComponentGraphResolveState.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.component.external.model;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
+import org.gradle.internal.component.model.ComponentIdGenerator;
+import org.gradle.internal.component.model.DefaultComponentGraphResolveState;
+
+/**
+ * <p>The aim is to create only a single instance of this type per component and reuse that for all resolution that happens in a build tree. This isn't quite the case yet.
+ */
+public class DefaultModuleComponentGraphResolveState extends DefaultComponentGraphResolveState<ModuleComponentGraphResolveMetadata, ModuleComponentResolveMetadata> implements ModuleComponentGraphResolveState {
+    public DefaultModuleComponentGraphResolveState(long instanceId, ModuleComponentResolveMetadata metadata, AttributeDesugaring attributeDesugaring, ComponentIdGenerator idGenerator) {
+        super(instanceId, metadata, metadata, attributeDesugaring, idGenerator);
+    }
+
+    @Override
+    public ModuleComponentIdentifier getId() {
+        return getArtifactMetadata().getId();
+    }
+
+    @Override
+    public ModuleComponentResolveMetadata getModuleResolveMetadata() {
+        return getArtifactMetadata();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ExternalComponentResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ExternalComponentResolveMetadata.java
new file mode 100644
index 0000000..24ae65a
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ExternalComponentResolveMetadata.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.component.external.model;
+
+import org.gradle.internal.component.model.ComponentResolveMetadata;
+import org.gradle.internal.component.model.ModuleSources;
+
+public interface ExternalComponentResolveMetadata extends ComponentResolveMetadata {
+    ModuleSources getSources();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ForcedDependencyMetadataWrapper.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ForcedDependencyMetadataWrapper.java
index 1c083b2..b4f5864 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ForcedDependencyMetadataWrapper.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ForcedDependencyMetadataWrapper.java
@@ -18,24 +18,19 @@
 import org.gradle.api.artifacts.VersionConstraint;
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
-import org.gradle.api.capabilities.Capability;
-import org.gradle.api.internal.attributes.AttributesSchemaInternal;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.component.local.model.DefaultProjectDependencyMetadata;
-import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.component.model.DelegatingDependencyMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ExcludeMetadata;
 import org.gradle.internal.component.model.ForcingDependencyMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
-import org.gradle.internal.component.model.VariantSelectionResult;
 
-import java.util.Collection;
 import java.util.List;
 
-public class ForcedDependencyMetadataWrapper implements ForcingDependencyMetadata, ModuleDependencyMetadata {
+public class ForcedDependencyMetadataWrapper extends DelegatingDependencyMetadata implements ForcingDependencyMetadata, ModuleDependencyMetadata {
     private final ModuleDependencyMetadata delegate;
 
     public ForcedDependencyMetadataWrapper(ModuleDependencyMetadata delegate) {
+        super(delegate);
         this.delegate = delegate;
     }
 
@@ -60,21 +55,6 @@ public ModuleDependencyMetadata withEndorseStrictVersions(boolean endorse) {
     }
 
     @Override
-    public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
-        return delegate.selectVariants(consumerAttributes, targetComponentState, consumerSchema, explicitRequestedCapabilities);
-    }
-
-    @Override
-    public List<ExcludeMetadata> getExcludes() {
-        return delegate.getExcludes();
-    }
-
-    @Override
-    public List<IvyArtifactName> getArtifacts() {
-        return delegate.getArtifacts();
-    }
-
-    @Override
     public DependencyMetadata withTarget(ComponentSelector target) {
         DependencyMetadata dependencyMetadata = delegate.withTarget(target);
         if (dependencyMetadata instanceof DefaultProjectDependencyMetadata) {
@@ -93,31 +73,6 @@ public DependencyMetadata withTargetAndArtifacts(ComponentSelector target, List<
     }
 
     @Override
-    public boolean isChanging() {
-        return delegate.isChanging();
-    }
-
-    @Override
-    public boolean isTransitive() {
-        return delegate.isTransitive();
-    }
-
-    @Override
-    public boolean isConstraint() {
-        return delegate.isConstraint();
-    }
-
-    @Override
-    public boolean isEndorsingStrictVersions() {
-        return delegate.isEndorsingStrictVersions();
-    }
-
-    @Override
-    public String getReason() {
-        return delegate.getReason();
-    }
-
-    @Override
     public boolean isForce() {
         return true;
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/LazyRuleAwareWithBaseConfigurationMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/LazyRuleAwareWithBaseConfigurationMetadata.java
index 8babbb7..847c272 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/LazyRuleAwareWithBaseConfigurationMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/LazyRuleAwareWithBaseConfigurationMetadata.java
@@ -30,7 +30,6 @@
 import org.gradle.internal.component.model.IvyArtifactName;
 import org.gradle.internal.component.model.ModuleConfigurationMetadata;
 import org.gradle.internal.component.model.VariantResolveMetadata;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
 
 import javax.annotation.Nullable;
 import java.util.List;
@@ -163,11 +162,6 @@ public boolean isCanBeResolved() {
     }
 
     @Override
-    public DeprecationMessageBuilder.WithDocumentation getConsumptionDeprecation() {
-        return null;
-    }
-
-    @Override
     public boolean isExternalVariant() {
         return externalVariant;
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveMetadata.java
new file mode 100644
index 0000000..255d648
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveMetadata.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.component.external.model;
+
+import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
+
+public interface ModuleComponentGraphResolveMetadata extends ComponentGraphResolveMetadata {
+    /**
+     * Was the metadata artifact for this component missing? When true, the metadata for this component was generated using some defaults.
+     */
+    boolean isMissing();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveState.java
new file mode 100644
index 0000000..02f0011
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveState.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 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.component.external.model;
+
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.internal.component.model.ComponentGraphResolveState;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * A specialized {@link ComponentGraphResolveState} for external components.
+ *
+ * <p>Instances of this type are cached and reused for multiple graph resolutions, possibly in parallel. This means that the implementation must be thread-safe.
+ */
+@ThreadSafe
+public interface ModuleComponentGraphResolveState extends ComponentGraphResolveState {
+    @Override
+    ModuleComponentIdentifier getId();
+
+    @Override
+    ModuleComponentGraphResolveMetadata getMetadata();
+
+    // Try to avoid using this, this is here to allow migration away from ModuleComponentResolveMetadata
+    ModuleComponentResolveMetadata getModuleResolveMetadata();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveStateFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveStateFactory.java
new file mode 100644
index 0000000..acc4fb0
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentGraphResolveStateFactory.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 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.component.external.model;
+
+import org.gradle.api.internal.attributes.AttributeDesugaring;
+import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
+import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.component.model.ComponentIdGenerator;
+import org.gradle.internal.component.model.DefaultComponentGraphResolveState;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+@ServiceScope(Scopes.BuildTree.class)
+public class ModuleComponentGraphResolveStateFactory {
+    private final ComponentIdGenerator idGenerator;
+    private final AttributeDesugaring attributeDesugaring;
+
+    public ModuleComponentGraphResolveStateFactory(ComponentIdGenerator idFactory, AttributeDesugaring attributeDesugaring) {
+        this.idGenerator = idFactory;
+        this.attributeDesugaring = attributeDesugaring;
+    }
+
+    public ModuleComponentGraphResolveState stateFor(ModuleComponentResolveMetadata metadata) {
+        return new DefaultModuleComponentGraphResolveState(idGenerator.nextComponentId(), metadata, attributeDesugaring, idGenerator);
+    }
+
+    public ComponentGraphResolveState stateFor(ComponentGraphResolveMetadata graphMetadata, ExternalComponentResolveMetadata artifactMetadata) {
+        return new DefaultComponentGraphResolveState<>(idGenerator.nextComponentId(), graphMetadata, artifactMetadata, attributeDesugaring, idGenerator);
+    }
+}
+
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentResolveMetadata.java
index 89fa549..42e42f6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleComponentResolveMetadata.java
@@ -18,8 +18,6 @@
 import com.google.common.collect.ImmutableList;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
-import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 import org.gradle.internal.component.model.ModuleConfigurationMetadata;
 import org.gradle.internal.component.model.ModuleSources;
 
@@ -30,7 +28,7 @@
  *
  * <p>Implementations of this type should be immutable and thread safe.</p>
  */
-public interface ModuleComponentResolveMetadata extends ComponentResolveMetadata, ComponentGraphResolveMetadata {
+public interface ModuleComponentResolveMetadata extends ExternalComponentResolveMetadata, ModuleComponentGraphResolveMetadata {
     /**
      * {@inheritDoc}
      */
@@ -45,9 +43,8 @@ public interface ModuleComponentResolveMetadata extends ComponentResolveMetadata
     MutableModuleComponentResolveMetadata asMutable();
 
     /**
-     * {@inheritDoc}
+     * Creates a copy of this meta-data with the given sources.
      */
-    @Override
     ModuleComponentResolveMetadata withSources(ModuleSources sources);
 
     /**
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleDependencyMetadataWrapper.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleDependencyMetadataWrapper.java
index 187b751..77f4dc7 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleDependencyMetadataWrapper.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ModuleDependencyMetadataWrapper.java
@@ -16,32 +16,16 @@
 package org.gradle.internal.component.external.model;
 
 import org.gradle.api.artifacts.VersionConstraint;
-import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
-import org.gradle.api.capabilities.Capability;
-import org.gradle.api.internal.attributes.AttributesSchemaInternal;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.component.model.DelegatingDependencyMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ExcludeMetadata;
-import org.gradle.internal.component.model.IvyArtifactName;
-import org.gradle.internal.component.model.VariantSelectionResult;
 
-import java.util.Collection;
-import java.util.List;
-
-public class ModuleDependencyMetadataWrapper implements ModuleDependencyMetadata {
+public class ModuleDependencyMetadataWrapper extends DelegatingDependencyMetadata implements ModuleDependencyMetadata {
     private final DependencyMetadata delegate;
-    private final boolean isTransitive;
 
     public ModuleDependencyMetadataWrapper(DependencyMetadata delegate) {
+        super(delegate);
         this.delegate = delegate;
-        this.isTransitive = delegate.isTransitive();
-    }
-
-    @Override
-    public List<IvyArtifactName> getArtifacts() {
-        return delegate.getArtifacts();
     }
 
     @Override
@@ -65,52 +49,7 @@ public ModuleDependencyMetadata withEndorseStrictVersions(boolean endorse) {
     }
 
     @Override
-    public DependencyMetadata withTarget(ComponentSelector target) {
-        return delegate.withTarget(target);
-    }
-
-    @Override
-    public DependencyMetadata withTargetAndArtifacts(ComponentSelector target, List<IvyArtifactName> artifacts) {
-        return delegate.withTargetAndArtifacts(target, artifacts);
-    }
-
-    @Override
     public ModuleComponentSelector getSelector() {
         return (ModuleComponentSelector) delegate.getSelector();
     }
-
-    @Override
-    public List<ExcludeMetadata> getExcludes() {
-        return delegate.getExcludes();
-    }
-
-    @Override
-    public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
-        return delegate.selectVariants(consumerAttributes, targetComponentState, consumerSchema, explicitRequestedCapabilities);
-    }
-
-    @Override
-    public boolean isChanging() {
-        return delegate.isChanging();
-    }
-
-    @Override
-    public boolean isTransitive() {
-        return isTransitive;
-    }
-
-    @Override
-    public boolean isConstraint() {
-        return delegate.isConstraint();
-    }
-
-    @Override
-    public boolean isEndorsingStrictVersions() {
-        return delegate.isEndorsingStrictVersions();
-    }
-
-    @Override
-    public String getReason() {
-        return delegate.getReason();
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyDependencyDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyDependencyDescriptor.java
index baf5639..6897deb 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyDependencyDescriptor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyDependencyDescriptor.java
@@ -27,14 +27,14 @@
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
 import org.gradle.internal.component.external.descriptor.Artifact;
 import org.gradle.internal.component.external.model.ExternalDependencyDescriptor;
-import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.component.model.ComponentGraphResolveState;
-import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata;
+import org.gradle.internal.component.model.ConfigurationGraphResolveState;
 import org.gradle.internal.component.model.ConfigurationMetadata;
 import org.gradle.internal.component.model.ConfigurationNotFoundException;
 import org.gradle.internal.component.model.Exclude;
 import org.gradle.internal.component.model.ExcludeMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.component.model.VariantSelectionResult;
 
 import java.util.Collection;
@@ -125,10 +125,9 @@ protected IvyDependencyDescriptor withRequested(ModuleComponentSelector newReque
      *   - '@' and '#' are special values for matching target configurations. See <a href="http://ant.apache.org/ivy/history/latest-milestone/ivyfile/dependency.html">the Ivy docs</a> for details.
      */
     @Override
-    public VariantSelectionResult selectLegacyConfigurations(ComponentIdentifier fromComponent, ConfigurationMetadata fromConfiguration, ComponentGraphResolveState targetComponentState) {
-        ComponentGraphResolveMetadata targetComponent = targetComponentState.getMetadata();
+    public VariantSelectionResult selectLegacyConfigurations(ComponentIdentifier fromComponent, ConfigurationMetadata fromConfiguration, ComponentGraphResolveState targetComponent) {
         // TODO - all this matching stuff is constant for a given DependencyMetadata instance
-        List<ConfigurationGraphResolveMetadata> targets = Lists.newLinkedList();
+        List<ConfigurationGraphResolveState> targets = Lists.newLinkedList();
         boolean matched = false;
         String fromConfigName = fromConfiguration.getName();
         for (String config : fromConfiguration.getHierarchy()) {
@@ -165,15 +164,20 @@ public VariantSelectionResult selectLegacyConfigurations(ComponentIdentifier fro
             }
         }
 
-        return new VariantSelectionResult(ImmutableList.copyOf(targets), false);
+        ImmutableList.Builder<VariantGraphResolveState> builder = ImmutableList.builderWithExpectedSize(targets.size());
+        for (ConfigurationGraphResolveState target : targets) {
+            builder.add(target.asVariant());
+        }
+
+        return new VariantSelectionResult(builder.build(), false);
     }
 
-    private void findMatches(ComponentIdentifier fromComponent, ComponentGraphResolveMetadata targetComponent, String fromConfiguration, String patternConfiguration, String targetPattern, List<ConfigurationGraphResolveMetadata> targetConfigurations) {
+    private void findMatches(ComponentIdentifier fromComponent, ComponentGraphResolveState targetComponent, String fromConfiguration, String patternConfiguration, String targetPattern, List<ConfigurationGraphResolveState> targetConfigurations) {
         int startFallback = targetPattern.indexOf('(');
         if (startFallback >= 0) {
             if (targetPattern.endsWith(")")) {
                 String preferred = targetPattern.substring(0, startFallback);
-                ConfigurationGraphResolveMetadata configuration = targetComponent.getConfiguration(preferred);
+                ConfigurationGraphResolveState configuration = targetComponent.getConfiguration(preferred);
                 if (configuration != null) {
                     maybeAddConfiguration(targetConfigurations, configuration);
                     return;
@@ -183,9 +187,9 @@ private void findMatches(ComponentIdentifier fromComponent, ComponentGraphResolv
         }
 
         if (targetPattern.equals("*")) {
-            for (String targetName : targetComponent.getConfigurationNames()) {
-                ConfigurationGraphResolveMetadata configuration = targetComponent.getConfiguration(targetName);
-                if (configuration.isVisible()) {
+            for (String targetName : targetComponent.getMetadata().getConfigurationNames()) {
+                ConfigurationGraphResolveState configuration = targetComponent.getConfiguration(targetName);
+                if (configuration.getMetadata().isVisible()) {
                     maybeAddConfiguration(targetConfigurations, configuration);
                 }
             }
@@ -198,22 +202,22 @@ private void findMatches(ComponentIdentifier fromComponent, ComponentGraphResolv
             targetPattern = fromConfiguration;
         }
 
-        ConfigurationGraphResolveMetadata configuration = targetComponent.getConfiguration(targetPattern);
+        ConfigurationGraphResolveState configuration = targetComponent.getConfiguration(targetPattern);
         if (configuration == null) {
             throw new ConfigurationNotFoundException(fromComponent, fromConfiguration, targetPattern, targetComponent.getId());
         }
         maybeAddConfiguration(targetConfigurations, configuration);
     }
 
-    private void maybeAddConfiguration(List<ConfigurationGraphResolveMetadata> configurations, ConfigurationGraphResolveMetadata toAdd) {
-        Iterator<ConfigurationGraphResolveMetadata> iter = configurations.iterator();
+    private void maybeAddConfiguration(List<ConfigurationGraphResolveState> configurations, ConfigurationGraphResolveState toAdd) {
+        Iterator<ConfigurationGraphResolveState> iter = configurations.iterator();
         while (iter.hasNext()) {
-            ConfigurationGraphResolveMetadata configuration = iter.next();
-            if (configuration.getHierarchy().contains(toAdd.getName())) {
+            ConfigurationGraphResolveState configuration = iter.next();
+            if (configuration.getMetadata().getHierarchy().contains(toAdd.getName())) {
                 // this configuration is a child of toAdd, so no need to add it
                 return;
             }
-            if (toAdd.getHierarchy().contains(configuration.getName())) {
+            if (toAdd.getMetadata().getHierarchy().contains(configuration.getName())) {
                 // toAdd is a child, so implies this configuration
                 iter.remove();
             }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyModuleResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyModuleResolveMetadata.java
index 6988604..258b827 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyModuleResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/IvyModuleResolveMetadata.java
@@ -35,7 +35,7 @@ public interface IvyModuleResolveMetadata extends ModuleComponentResolveMetadata
     @Override
     MutableIvyModuleResolveMetadata asMutable();
 
-    /***
+    /**
      * Returns the branch attribute for the module.
      *
      * @return the branch attribute for the module
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/RealisedIvyModuleResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/RealisedIvyModuleResolveMetadata.java
index ee3bf53..0f90ad9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/RealisedIvyModuleResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/ivy/RealisedIvyModuleResolveMetadata.java
@@ -16,7 +16,6 @@
 
 package org.gradle.internal.component.external.model.ivy;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -54,6 +53,7 @@
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -242,7 +242,7 @@ protected Optional<List<? extends VariantGraphResolveMetadata>> maybeDeriveVaria
             // if there are more configurations than definitions, configurations have been added by rules and thus they are variants
             derivedVariants = Optional.of(allConfigurationsThatAreVariants());
         } else {
-            derivedVariants = Optional.absent();
+            derivedVariants = Optional.empty();
         }
         return derivedVariants;
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/DefaultMavenModuleResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/DefaultMavenModuleResolveMetadata.java
index 838ca90..87468d9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/DefaultMavenModuleResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/DefaultMavenModuleResolveMetadata.java
@@ -17,7 +17,6 @@
 package org.gradle.internal.component.external.model.maven;
 
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
@@ -44,6 +43,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -107,11 +107,11 @@ public List<ModuleDependencyMetadata> create() {
 
     @Override
     protected Optional<List<? extends VariantGraphResolveMetadata>> maybeDeriveVariants() {
-        return Optional.fromNullable(getDerivedVariants());
+        return Optional.ofNullable(getDerivedVariants());
     }
 
     protected Optional<List<? extends ModuleConfigurationMetadata>> deriveVariants() {
-        return Optional.fromNullable(getDerivedVariants());
+        return Optional.ofNullable(getDerivedVariants());
     }
 
     private ImmutableList<? extends ModuleConfigurationMetadata> getDerivedVariants() {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/MavenDependencyDescriptor.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/MavenDependencyDescriptor.java
index f87535b..bd12b05 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/MavenDependencyDescriptor.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/MavenDependencyDescriptor.java
@@ -22,13 +22,13 @@
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
 import org.gradle.internal.component.external.descriptor.MavenScope;
 import org.gradle.internal.component.external.model.ExternalDependencyDescriptor;
-import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
 import org.gradle.internal.component.model.ComponentGraphResolveState;
-import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata;
+import org.gradle.internal.component.model.ConfigurationGraphResolveState;
 import org.gradle.internal.component.model.ConfigurationMetadata;
 import org.gradle.internal.component.model.ConfigurationNotFoundException;
 import org.gradle.internal.component.model.ExcludeMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.component.model.VariantSelectionResult;
 
 import javax.annotation.Nullable;
@@ -85,28 +85,28 @@ public boolean isTransitive() {
      */
     @Override
     public VariantSelectionResult selectLegacyConfigurations(ComponentIdentifier fromComponent, ConfigurationMetadata fromConfiguration, ComponentGraphResolveState targetComponentState) {
-        ComponentGraphResolveMetadata targetComponent = targetComponentState.getMetadata();
-        ImmutableList.Builder<ConfigurationGraphResolveMetadata> result = ImmutableList.builder();
+        ImmutableList.Builder<VariantGraphResolveState> result = ImmutableList.builder();
         boolean requiresCompile = fromConfiguration.getName().equals("compile");
         if (!requiresCompile) {
             // From every configuration other than compile, include both the runtime and compile dependencies
-            ConfigurationGraphResolveMetadata runtime = findTargetConfiguration(fromComponent, fromConfiguration, targetComponent, "runtime");
-            result.add(runtime);
-            requiresCompile = !runtime.getHierarchy().contains("compile");
+            ConfigurationGraphResolveState runtime = findTargetConfiguration(fromComponent, fromConfiguration, targetComponentState, "runtime");
+            result.add(runtime.asVariant());
+            requiresCompile = !runtime.getMetadata().getHierarchy().contains("compile");
         }
         if (requiresCompile) {
             // From compile configuration, or when the target's runtime configuration does not extend from compile, include the compile dependencies
-            result.add(findTargetConfiguration(fromComponent, fromConfiguration, targetComponent, "compile"));
+            ConfigurationGraphResolveState compile = findTargetConfiguration(fromComponent, fromConfiguration, targetComponentState, "compile");
+            result.add(compile.asVariant());
         }
-        ConfigurationGraphResolveMetadata master = targetComponent.getConfiguration("master");
-        if (master != null && (!master.getDependencies().isEmpty() || !targetComponentState.resolveArtifactsFor(master).getArtifacts().isEmpty())) {
-            result.add(master);
+        ConfigurationGraphResolveState master = targetComponentState.getConfiguration("master");
+        if (master != null && (!master.getMetadata().getDependencies().isEmpty() || !master.asVariant().resolveArtifacts().getArtifacts().isEmpty())) {
+            result.add(master.asVariant());
         }
         return new VariantSelectionResult(result.build(), false);
     }
 
-    private ConfigurationGraphResolveMetadata findTargetConfiguration(ComponentIdentifier fromComponentId, ConfigurationMetadata fromConfiguration, ComponentGraphResolveMetadata targetComponent, String target) {
-        ConfigurationGraphResolveMetadata configuration = targetComponent.getConfiguration(target);
+    private ConfigurationGraphResolveState findTargetConfiguration(ComponentIdentifier fromComponentId, ConfigurationMetadata fromConfiguration, ComponentGraphResolveState targetComponent, String target) {
+        ConfigurationGraphResolveState configuration = targetComponent.getConfiguration(target);
         if (configuration == null) {
             configuration = targetComponent.getConfiguration("default");
             if (configuration == null) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/RealisedMavenModuleResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/RealisedMavenModuleResolveMetadata.java
index 98c790a..0719ca8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/RealisedMavenModuleResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/external/model/maven/RealisedMavenModuleResolveMetadata.java
@@ -17,7 +17,6 @@
 package org.gradle.internal.component.external.model.maven;
 
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -52,6 +51,7 @@
 import javax.annotation.Nullable;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/BuildableLocalComponentMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/BuildableLocalComponentMetadata.java
deleted file mode 100644
index 581f8d8..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/BuildableLocalComponentMetadata.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2013 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.component.local.model;
-
-import com.google.common.collect.ImmutableSet;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.component.external.model.ImmutableCapabilities;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
-
-import javax.annotation.Nullable;
-import java.util.Set;
-
-public interface BuildableLocalComponentMetadata {
-    /**
-     * Returns the identifier for this component.
-     */
-    ComponentIdentifier getId();
-
-    /**
-     * Adds a configuration to this component.
-     *
-     * @param hierarchy Must include name
-     * @param attributes the attributes of the configuration.
-     */
-    BuildableLocalConfigurationMetadata addConfiguration(String name, @Nullable String description, Set<String> extendsFrom, ImmutableSet<String> hierarchy, boolean visible, boolean transitive, ImmutableAttributes attributes, boolean canBeConsumed, @Nullable DeprecationMessageBuilder.WithDocumentation consumptionDeprecation, boolean canBeResolved, ImmutableCapabilities capabilities);
-
-    /**
-     * Provides a backing configuration instance from which dependencies and excludes will be sourced.
-     *
-     * @param configuration The configuration instance that provides dependencies and excludes
-     * @param localConfigurationMetadataBuilder A builder for translating Configuration to LocalConfigurationMetadata
-     */
-    void addDependenciesAndExcludesForConfiguration(ConfigurationInternal configuration, LocalConfigurationMetadataBuilder localConfigurationMetadataBuilder);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/BuildableLocalConfigurationMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/BuildableLocalConfigurationMetadata.java
deleted file mode 100644
index cbcd475..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/BuildableLocalConfigurationMetadata.java
+++ /dev/null
@@ -1,61 +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.internal.component.local.model;
-
-import org.gradle.api.artifacts.PublishArtifact;
-import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.DisplayName;
-import org.gradle.internal.component.external.model.ImmutableCapabilities;
-import org.gradle.internal.component.model.ExcludeMetadata;
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
-import org.gradle.internal.component.model.VariantResolveMetadata;
-
-import java.util.Collection;
-
-public interface BuildableLocalConfigurationMetadata {
-    /**
-     * Returns the identifier for the component that owns this configuration.
-     */
-    ComponentIdentifier getComponentId();
-
-    /**
-     * Adds a dependency to this configuration.
-     */
-    void addDependency(LocalOriginDependencyMetadata dependency);
-
-    /**
-     * Adds an exclude rule to this configuration.
-     */
-    void addExclude(ExcludeMetadata exclude);
-
-    /**
-     * Adds some file dependencies to this configuration.
-     *
-     * These files should be treated as regular dependencies, however they are currently treated separately as a migration step.
-     */
-    void addFiles(LocalFileDependencyMetadata files);
-
-    /**
-     * Adds some artifacts to this configuration. Artifacts are attached to the this configuration and each of its children.
-     */
-    void addArtifacts(Collection<? extends PublishArtifact> artifacts);
-
-    /**
-     * Adds a variant to this component, extending from the given configuration. Every configuration should include at least one variant.
-     */
-    void addVariant(String name, VariantResolveMetadata.Identifier identifier, DisplayName displayName, ImmutableAttributes attributes, ImmutableCapabilities capabilities, Collection<? extends PublishArtifact> artifacts);
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentGraphResolveState.java
index 7e2c96c..6cd712f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentGraphResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentGraphResolveState.java
@@ -16,30 +16,89 @@
 
 package org.gradle.internal.component.local.model;
 
-import com.google.common.base.Optional;
 import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ResolvedVariantResult;
+import org.gradle.api.capabilities.CapabilitiesMetadata;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec;
+import org.gradle.api.internal.artifacts.result.DefaultResolvedVariantResult;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
+import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.internal.Describables;
 import org.gradle.internal.component.model.AbstractComponentGraphResolveState;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
+import org.gradle.internal.component.model.ComponentIdGenerator;
+import org.gradle.internal.component.model.ComponentResolveMetadata;
+import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata;
+import org.gradle.internal.component.model.ConfigurationGraphResolveState;
 import org.gradle.internal.component.model.IvyArtifactName;
+import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.component.model.VariantArtifactGraphResolveMetadata;
 import org.gradle.internal.component.model.VariantArtifactResolveState;
-import org.gradle.internal.component.model.VariantGraphResolveMetadata;
+import org.gradle.internal.component.model.VariantArtifactSelectionCandidates;
+import org.gradle.internal.component.model.VariantGraphResolveState;
 import org.gradle.internal.component.model.VariantResolveMetadata;
+import org.gradle.internal.lazy.Lazy;
 import org.gradle.internal.resolve.resolver.ArtifactSelector;
 
+import javax.annotation.Nullable;
+import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.stream.Collectors;
 
+/**
+ * Holds the resolution state for a local component. The state is calculated as required, and an instance can be used for multiple resolutions across a build tree.
+ *
+ * <p>The aim is to create only a single instance of this type per project and reuse that for all resolution that happens in a build tree. This isn't quite the case yet.
+ */
 public class DefaultLocalComponentGraphResolveState extends AbstractComponentGraphResolveState<LocalComponentMetadata, LocalComponentMetadata> implements LocalComponentGraphResolveState {
-    private final ConcurrentMap<LocalConfigurationGraphResolveMetadata, DefaultLocalVariantArtifactResolveState> variants = new ConcurrentHashMap<>();
+    private final ComponentIdGenerator idGenerator;
+
+    // The graph resolve state for each configuration of this component
+    private final ConcurrentMap<String, DefaultLocalConfigurationGraphResolveState> configurations = new ConcurrentHashMap<>();
+
+    // The variants to use for variant selection during graph resolution
+    private final Lazy<Optional<List<? extends VariantGraphResolveState>>> allVariantsForGraphResolution;
+
+    // The variants of this component to use for artifact selection when variant reselection is enabled
+    private final Lazy<Optional<Set<? extends VariantResolveMetadata>>> allVariantsForArtifactSelection;
+
+    // The public view of all selectable variants of this component
+    private final Lazy<List<ResolvedVariantResult>> selectableVariantResults;
+
+    public DefaultLocalComponentGraphResolveState(long instanceId, LocalComponentMetadata metadata, AttributeDesugaring attributeDesugaring, ComponentIdGenerator idGenerator) {
+        super(instanceId, metadata, metadata, attributeDesugaring);
+        allVariantsForGraphResolution = Lazy.locking().of(() -> metadata.getVariantsForGraphTraversal().map(variants ->
+            variants.stream()
+                .map(variant -> getConfiguration(variant.getName()).asVariant())
+                .collect(Collectors.toList())));
+        allVariantsForArtifactSelection = Lazy.locking().of(() -> metadata.getVariantsForGraphTraversal().map(variants ->
+            variants.stream().
+                map(LocalConfigurationGraphResolveMetadata.class::cast).
+                map(LocalConfigurationGraphResolveMetadata::prepareToResolveArtifacts).
+                flatMap(variant -> variant.getVariants().stream()).
+                collect(Collectors.toSet())));
+        this.idGenerator = idGenerator;
+        selectableVariantResults = Lazy.locking().of(() -> metadata.getVariantsForGraphTraversal().orElse(Collections.emptyList()).stream().
+            map(LocalConfigurationGraphResolveMetadata.class::cast).
+            flatMap(variant -> variant.getVariants().stream()).
+            map(variant -> new DefaultResolvedVariantResult(
+                getId(),
+                Describables.of(variant.getName()),
+                attributeDesugaring.desugar(variant.getAttributes().asImmutable()),
+                capabilitiesFor(variant.getCapabilities()),
+                null
+            )).
+            collect(Collectors.toList()));
+    }
 
     @Override
     public ModuleVersionIdentifier getModuleVersionId() {
@@ -51,63 +110,175 @@ public LocalComponentMetadata copy(ComponentIdentifier componentIdentifier, Tran
         return getMetadata().copy(componentIdentifier, artifacts);
     }
 
-    public DefaultLocalComponentGraphResolveState(LocalComponentMetadata metadata) {
-        super(metadata, metadata);
+    @Override
+    public ComponentArtifactResolveMetadata getResolveMetadata() {
+        return new LocalComponentArtifactResolveMetadata(getMetadata());
     }
 
     @Override
-    public VariantArtifactGraphResolveMetadata resolveArtifactsFor(VariantGraphResolveMetadata variant) {
-        return stateFor((LocalConfigurationGraphResolveMetadata) variant);
+    public ModuleSources getSources() {
+        return getMetadata().getSources();
     }
 
     @Override
-    public VariantArtifactResolveState prepareForArtifactResolution(VariantGraphResolveMetadata variant) {
-        return stateFor((LocalConfigurationGraphResolveMetadata) variant);
+    public List<ResolvedVariantResult> getAllSelectableVariantResults() {
+        return selectableVariantResults.get();
     }
 
-    private DefaultLocalVariantArtifactResolveState stateFor(LocalConfigurationGraphResolveMetadata variant) {
-        return variants.computeIfAbsent(variant, c -> new DefaultLocalVariantArtifactResolveState(getMetadata(), variant));
+    @Override
+    protected Optional<List<? extends VariantGraphResolveState>> getVariantsForGraphTraversal() {
+        return allVariantsForGraphResolution.get();
     }
 
-    private static class DefaultLocalVariantArtifactResolveState implements VariantArtifactResolveState, VariantArtifactGraphResolveMetadata {
+    @Nullable
+    @Override
+    public ConfigurationGraphResolveState getConfiguration(String configurationName) {
+        return configurations.computeIfAbsent(configurationName, n -> {
+            LocalConfigurationMetadata configuration = getMetadata().getConfiguration(configurationName);
+            if (configuration == null) {
+                return null;
+            } else {
+                return new DefaultLocalConfigurationGraphResolveState(idGenerator.nextVariantId(), getMetadata(), configuration, allVariantsForArtifactSelection);
+            }
+        });
+    }
+
+    private class DefaultLocalConfigurationGraphResolveState extends AbstractVariantGraphResolveState implements VariantGraphResolveState, ConfigurationGraphResolveState {
+        private final long instanceId;
+        private final LocalConfigurationMetadata configuration;
+        private final Lazy<DefaultLocalConfigurationArtifactResolveState> artifactResolveState;
+
+        public DefaultLocalConfigurationGraphResolveState(long instanceId, LocalComponentMetadata component, LocalConfigurationMetadata configuration, Lazy<Optional<Set<? extends VariantResolveMetadata>>> allVariantsForArtifactSelection) {
+            this.instanceId = instanceId;
+            this.configuration = configuration;
+            this.artifactResolveState = Lazy.locking().of(() -> new DefaultLocalConfigurationArtifactResolveState(component, configuration, allVariantsForArtifactSelection));
+        }
+
+        @Override
+        public long getInstanceId() {
+            return instanceId;
+        }
+
+        @Override
+        public String getName() {
+            return configuration.getName();
+        }
+
+        @Override
+        public ConfigurationGraphResolveMetadata getMetadata() {
+            return configuration;
+        }
+
+        @Override
+        public ImmutableAttributes getAttributes() {
+            return configuration.getAttributes();
+        }
+
+        @Override
+        public CapabilitiesMetadata getCapabilities() {
+            return configuration.getCapabilities();
+        }
+
+        @Override
+        public VariantGraphResolveState asVariant() {
+            return this;
+        }
+
+        @Override
+        public VariantArtifactGraphResolveMetadata resolveArtifacts() {
+            return artifactResolveState.get();
+        }
+
+        @Override
+        public VariantArtifactResolveState prepareForArtifactResolution() {
+            return artifactResolveState.get();
+        }
+    }
+
+    private static class DefaultLocalConfigurationArtifactResolveState implements VariantArtifactResolveState, VariantArtifactGraphResolveMetadata {
         private final LocalComponentMetadata component;
-        private final LocalConfigurationGraphResolveMetadata graphSelectedVariant;
+        private final LocalConfigurationGraphResolveMetadata graphSelectedConfiguration;
+        private final Set<? extends VariantResolveMetadata> legacyVariants;
+        private final Lazy<Optional<Set<? extends VariantResolveMetadata>>> allVariants;
 
-        public DefaultLocalVariantArtifactResolveState(LocalComponentMetadata component, LocalConfigurationGraphResolveMetadata graphSelectedVariant) {
+        public DefaultLocalConfigurationArtifactResolveState(LocalComponentMetadata component, LocalConfigurationGraphResolveMetadata graphSelectedConfiguration, Lazy<Optional<Set<? extends VariantResolveMetadata>>> allVariantsForArtifactSelection) {
             this.component = component;
-            this.graphSelectedVariant = graphSelectedVariant;
+            this.graphSelectedConfiguration = graphSelectedConfiguration;
+            this.legacyVariants = graphSelectedConfiguration.prepareToResolveArtifacts().getVariants();
+            this.allVariants = allVariantsForArtifactSelection;
         }
 
         @Override
         public List<? extends ComponentArtifactMetadata> getArtifacts() {
-            return graphSelectedVariant.prepareToResolveArtifacts().getArtifacts();
+            return graphSelectedConfiguration.prepareToResolveArtifacts().getArtifacts();
         }
 
         @Override
         public ComponentArtifactMetadata resolveArtifact(IvyArtifactName artifact) {
-            return graphSelectedVariant.prepareToResolveArtifacts().artifact(artifact);
+            return graphSelectedConfiguration.prepareToResolveArtifacts().artifact(artifact);
         }
 
         @Override
         public ArtifactSet resolveArtifacts(ArtifactSelector artifactSelector, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) {
-            LocalConfigurationMetadata configuration = graphSelectedVariant.prepareToResolveArtifacts();
-            Set<? extends VariantResolveMetadata> fallbackVariants = configuration.getVariants();
-            Optional<List<? extends VariantGraphResolveMetadata>> variantsForGraphTraversal = component.getVariantsForGraphTraversal();
-            return artifactSelector.resolveArtifacts(component, () -> buildAllVariants(fallbackVariants, variantsForGraphTraversal), fallbackVariants, exclusions, overriddenAttributes);
+            return artifactSelector.resolveArtifacts(new LocalComponentArtifactResolveMetadata(component), new LocalVariantArtifactSelectionCandidates(allVariants, legacyVariants), exclusions, overriddenAttributes);
+        }
+    }
+
+    private static class LocalVariantArtifactSelectionCandidates implements VariantArtifactSelectionCandidates {
+        private final Lazy<Optional<Set<? extends VariantResolveMetadata>>> allVariants;
+        private final Set<? extends VariantResolveMetadata> legacyVariants;
+
+        public LocalVariantArtifactSelectionCandidates(Lazy<Optional<Set<? extends VariantResolveMetadata>>> allVariants, Set<? extends VariantResolveMetadata> legacyVariants) {
+            this.allVariants = allVariants;
+            this.legacyVariants = legacyVariants;
         }
 
-        private static Set<? extends VariantResolveMetadata> buildAllVariants(Set<? extends VariantResolveMetadata> fallbackVariants, Optional<List<? extends VariantGraphResolveMetadata>> variantsForGraphTraversal) {
-            final Set<? extends VariantResolveMetadata> allVariants;
-            if (variantsForGraphTraversal.isPresent()) {
-                allVariants = variantsForGraphTraversal.get().stream().
-                    map(LocalConfigurationGraphResolveMetadata.class::cast).
-                    map(LocalConfigurationGraphResolveMetadata::prepareToResolveArtifacts).
-                    flatMap(variant -> variant.getVariants().stream()).
-                    collect(Collectors.toSet());
-            } else {
-                allVariants = fallbackVariants;
-            }
-            return allVariants;
+        @Override
+        public Set<? extends VariantResolveMetadata> getAllVariants() {
+            return allVariants.get().orElse(legacyVariants);
+        }
+
+        @Override
+        public Set<? extends VariantResolveMetadata> getLegacyVariants() {
+            return legacyVariants;
+        }
+    }
+
+    private static class LocalComponentArtifactResolveMetadata implements ComponentArtifactResolveMetadata {
+        private final LocalComponentMetadata metadata;
+
+        public LocalComponentArtifactResolveMetadata(LocalComponentMetadata metadata) {
+            this.metadata = metadata;
+        }
+
+        @Override
+        public ComponentIdentifier getId() {
+            return metadata.getId();
+        }
+
+        @Override
+        public ModuleVersionIdentifier getModuleVersionId() {
+            return metadata.getModuleVersionId();
+        }
+
+        @Override
+        public ModuleSources getSources() {
+            return metadata.getSources();
+        }
+
+        @Override
+        public ImmutableAttributes getAttributes() {
+            return metadata.getAttributes();
+        }
+
+        @Override
+        public AttributesSchemaInternal getAttributesSchema() {
+            return metadata.getAttributesSchema();
+        }
+
+        @Override
+        public ComponentResolveMetadata getMetadata() {
+            return metadata;
         }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentMetadata.java
index e455676..996561f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalComponentMetadata.java
@@ -16,52 +16,71 @@
 
 package org.gradle.internal.component.local.model;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
 import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationsProvider;
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DefaultLocalConfigurationMetadataBuilder;
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.Describables;
-import org.gradle.internal.component.external.model.ImmutableCapabilities;
 import org.gradle.internal.component.external.model.VirtualComponentIdentifier;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 import org.gradle.internal.component.model.ImmutableModuleSources;
 import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.component.model.VariantGraphResolveMetadata;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.model.ModelContainer;
 
+import javax.annotation.Nullable;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.Consumer;
 
-public class DefaultLocalComponentMetadata implements LocalComponentMetadata, BuildableLocalComponentMetadata {
-    private final Map<String, DefaultLocalConfigurationMetadata> allConfigurations = Maps.newLinkedHashMap();
+/**
+ * Default implementation of {@link LocalComponentMetadata}. This component is lazy in that it
+ * will only initialize {@link LocalConfigurationMetadata} instances on-demand as they are needed.
+ * <p>
+ * TODO: Eventually, this class should be updated to only create metadata instances for consumable configurations.
+ * However, we currently need to expose resolvable configuration since the
+ * {@link org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.ResolveState} implementation
+ * sources its root component metadata, for a resolvable configuration, from this component metadata.
+ */
+@SuppressWarnings("JavadocReference")
+public final class DefaultLocalComponentMetadata implements LocalComponentMetadata {
+
+    private static final ModuleSources MODULE_SOURCES = ImmutableModuleSources.of();
+
     private final ComponentIdentifier componentId;
     private final ModuleVersionIdentifier moduleVersionId;
     private final String status;
     private final AttributesSchemaInternal attributesSchema;
-    private final ModelContainer<?> model;
-    private final CalculatedValueContainerFactory calculatedValueContainerFactory;
-    private final ModuleSources moduleSources = ImmutableModuleSources.of();
+    private final ConfigurationMetadataFactory configurationFactory;
+    private final Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> artifactTransformer;
 
+    private final Map<String, LocalConfigurationMetadata> allConfigurations = new LinkedHashMap<>();
     private Optional<List<? extends VariantGraphResolveMetadata>> consumableConfigurations;
 
-    public DefaultLocalComponentMetadata(ModuleVersionIdentifier moduleVersionId, ComponentIdentifier componentId, String status, AttributesSchemaInternal attributesSchema, ModelContainer<?> model, CalculatedValueContainerFactory calculatedValueContainerFactory) {
+    public DefaultLocalComponentMetadata(
+        ModuleVersionIdentifier moduleVersionId,
+        ComponentIdentifier componentId,
+        String status,
+        AttributesSchemaInternal attributesSchema,
+        ConfigurationMetadataFactory configurationFactory,
+        @Nullable Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> artifactTransformer
+    ) {
         this.moduleVersionId = moduleVersionId;
         this.componentId = componentId;
         this.status = status;
         this.attributesSchema = attributesSchema;
-        this.model = model;
-        this.calculatedValueContainerFactory = calculatedValueContainerFactory;
+        this.configurationFactory = configurationFactory;
+        this.artifactTransformer = artifactTransformer;
     }
 
     @Override
@@ -75,80 +94,16 @@ public ModuleVersionIdentifier getModuleVersionId() {
     }
 
     /**
-     * Creates a copy of this metadata, transforming the artifacts and dependencies of this component.
+     * Creates a copy of this metadata, transforming the artifacts of this component.
      */
     @Override
-    public DefaultLocalComponentMetadata copy(ComponentIdentifier componentIdentifier, Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> artifacts) {
-        DefaultLocalComponentMetadata copy = new DefaultLocalComponentMetadata(moduleVersionId, componentIdentifier, status, attributesSchema, model, calculatedValueContainerFactory);
-        for (DefaultLocalConfigurationMetadata configuration : allConfigurations.values()) {
-            copy.addConfiguration(configuration.getName(), configuration.getDescription(), configuration.getExtendsFrom(), configuration.getHierarchy(), configuration.isVisible(), configuration.isTransitive(), configuration.getAttributes(), configuration.isCanBeConsumed(), configuration.getConsumptionDeprecation(), configuration.isCanBeResolved(), configuration.getCapabilities());
-        }
-
-        // Artifacts
+    public DefaultLocalComponentMetadata copy(ComponentIdentifier componentIdentifier, Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformer) {
         // Keep track of transformed artifacts as a given artifact may appear in multiple variants and configurations
         Map<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformedArtifacts = new HashMap<>();
+        Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> cachedTransformer = oldArtifact ->
+            transformedArtifacts.computeIfAbsent(oldArtifact, transformer::transform);
 
-        for (DefaultLocalConfigurationMetadata configuration : allConfigurations.values()) {
-            configuration.realizeDependencies();
-            configuration.prepareToResolveArtifacts();
-            DefaultLocalConfigurationMetadata configurationCopy = copy.allConfigurations.get(configuration.getName());
-
-            // Dependencies
-            configurationCopy.definedDependencies.addAll(configuration.definedDependencies);
-            configurationCopy.definedFiles.addAll(configuration.definedFiles);
-
-            // Exclude rules
-            configurationCopy.definedExcludes.addAll(configuration.definedExcludes);
-
-            // Artifacts
-            ImmutableList<LocalComponentArtifactMetadata> newArtifacts = copyArtifacts(configuration.getArtifacts(), artifacts, transformedArtifacts);
-            configurationCopy.artifacts = calculatedValueContainerFactory.create(Describables.of(configurationCopy.getDescription(), "artifacts"), newArtifacts);
-            configurationCopy.sourceArtifacts = null;
-
-            for (LocalVariantMetadata oldVariant : configuration.getVariants()) {
-                oldVariant.prepareToResolveArtifacts();
-                ImmutableList<LocalComponentArtifactMetadata> newVariantArtifacts = copyArtifacts(oldVariant.getArtifacts(), artifacts, transformedArtifacts);
-                configurationCopy.variants.add(new LocalVariantMetadata(oldVariant.getName(), oldVariant.getIdentifier(), oldVariant.asDescribable(), oldVariant.getAttributes(), newVariantArtifacts, (ImmutableCapabilities) oldVariant.getCapabilities(), calculatedValueContainerFactory));
-            }
-        }
-
-        return copy;
-    }
-
-    private ImmutableList<LocalComponentArtifactMetadata> copyArtifacts(List<LocalComponentArtifactMetadata> artifacts, Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformer, Map<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformedArtifacts) {
-        if (artifacts.isEmpty()) {
-            return ImmutableList.of();
-        }
-
-        ImmutableList.Builder<LocalComponentArtifactMetadata> newArtifacts = new ImmutableList.Builder<>();
-        for (LocalComponentArtifactMetadata oldArtifact : artifacts) {
-            newArtifacts.add(copyArtifact(oldArtifact, transformer, transformedArtifacts));
-        }
-        return newArtifacts.build();
-    }
-
-    private LocalComponentArtifactMetadata copyArtifact(LocalComponentArtifactMetadata oldArtifact, Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformer, Map<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformedArtifacts) {
-        LocalComponentArtifactMetadata newArtifact = transformedArtifacts.get(oldArtifact);
-        if (newArtifact == null) {
-            newArtifact = transformer.transform(oldArtifact);
-            transformedArtifacts.put(oldArtifact, newArtifact);
-        }
-        return newArtifact;
-    }
-
-    @Override
-    public BuildableLocalConfigurationMetadata addConfiguration(String name, String description, Set<String> extendsFrom, ImmutableSet<String> hierarchy, boolean visible, boolean transitive, ImmutableAttributes attributes, boolean canBeConsumed, DeprecationMessageBuilder.WithDocumentation consumptionDeprecation, boolean canBeResolved, ImmutableCapabilities capabilities) {
-        assert hierarchy.contains(name);
-        DefaultLocalConfigurationMetadata conf = new DefaultLocalConfigurationMetadata(name, description, visible, transitive, extendsFrom, hierarchy, attributes, canBeConsumed, consumptionDeprecation, canBeResolved, capabilities, model, calculatedValueContainerFactory, this);
-        allConfigurations.put(name, conf);
-        return conf;
-    }
-
-    @Override
-    public void addDependenciesAndExcludesForConfiguration(ConfigurationInternal configuration, LocalConfigurationMetadataBuilder localConfigurationMetadataBuilder) {
-        DefaultLocalConfigurationMetadata configurationMetadata = allConfigurations.get(configuration.getName());
-        configurationMetadata.configurationMetadataBuilder = localConfigurationMetadataBuilder;
-        configurationMetadata.backingConfiguration = configuration;
+        return new DefaultLocalComponentMetadata(moduleVersionId, componentIdentifier, status, attributesSchema, configurationFactory, cachedTransformer);
     }
 
     @Override
@@ -158,12 +113,7 @@ public String toString() {
 
     @Override
     public ModuleSources getSources() {
-        return moduleSources;
-    }
-
-    @Override
-    public ComponentResolveMetadata withSources(ModuleSources source) {
-        throw new UnsupportedOperationException();
+        return MODULE_SOURCES;
     }
 
     @Override
@@ -193,7 +143,7 @@ public ImmutableList<? extends VirtualComponentIdentifier> getPlatformOwners() {
 
     @Override
     public Set<String> getConfigurationNames() {
-        return allConfigurations.keySet();
+        return configurationFactory.getConfigurationNames();
     }
 
     /**
@@ -203,25 +153,32 @@ public Set<String> getConfigurationNames() {
     public synchronized Optional<List<? extends VariantGraphResolveMetadata>> getVariantsForGraphTraversal() {
         if (consumableConfigurations == null) {
             ImmutableList.Builder<VariantGraphResolveMetadata> builder = new ImmutableList.Builder<>();
-            boolean hasAtLeastOneConsumableConfiguration = false;
-            for (DefaultLocalConfigurationMetadata configuration : allConfigurations.values()) {
-                if (configuration.isCanBeConsumed() && !configuration.getAttributes().isEmpty()) {
-                    hasAtLeastOneConsumableConfiguration = true;
-                    builder.add(configuration);
+            configurationFactory.visitConfigurations(candidate -> {
+                if (candidate.isConsumable() && candidate.hasAttributes()) {
+                    builder.add(getConfiguration(candidate.getName()));
                 }
-            }
-            if (hasAtLeastOneConsumableConfiguration) {
-                consumableConfigurations = Optional.of(builder.build());
-            } else {
-                consumableConfigurations = Optional.absent();
-            }
+            });
+
+            ImmutableList<VariantGraphResolveMetadata> variants = builder.build();
+            consumableConfigurations = !variants.isEmpty() ? Optional.of(variants) : Optional.empty();
         }
         return consumableConfigurations;
     }
 
     @Override
-    public DefaultLocalConfigurationMetadata getConfiguration(final String name) {
-        return allConfigurations.get(name);
+    public LocalConfigurationMetadata getConfiguration(final String name) {
+        LocalConfigurationMetadata md = allConfigurations.get(name);
+        if (md == null) {
+            md = configurationFactory.getConfiguration(name, this);
+            if (md == null) {
+                return null;
+            }
+            if (artifactTransformer != null) {
+                md = md.copy(artifactTransformer);
+            }
+            allConfigurations.put(name, md);
+        }
+        return md;
     }
 
     @Override
@@ -238,8 +195,174 @@ public ImmutableAttributes getAttributes() {
 
     @Override
     public void reevaluate() {
-        for (DefaultLocalConfigurationMetadata conf : allConfigurations.values()) {
-            conf.reevaluate();
+        allConfigurations.clear();
+        configurationFactory.invalidate();
+        synchronized (this) {
+            consumableConfigurations = null;
+        }
+    }
+
+    @Override
+    public boolean isConfigurationRealized(String configName) {
+        return allConfigurations.get(configName) != null;
+    }
+
+    /**
+     * Constructs {@link LocalConfigurationMetadata} given a configuration's name. This allows
+     * the component metadata to source configuration data from multiple sources, both lazy and eager.
+     */
+    public interface ConfigurationMetadataFactory {
+        void visitConfigurations(Consumer<Candidate> visitor);
+
+        /**
+         * Get the names of all configurations which this factory can produce.
+         */
+        Set<String> getConfigurationNames();
+
+        /**
+         * Invalidates any caching used for producing configuration metadata.
+         */
+        void invalidate();
+
+        /**
+         * Produces a configuration metadata instance from the configuration with the given {@code name}.
+         *
+         * @return Null if the configuration with the given name does not exist.
+         */
+        @Nullable
+        LocalConfigurationMetadata getConfiguration(
+            String name,
+            DefaultLocalComponentMetadata parent
+        );
+
+        interface Candidate {
+            String getName();
+
+            boolean isConsumable();
+
+            boolean hasAttributes();
+        }
+    }
+
+    /**
+     * A {@link ConfigurationMetadataFactory} which uses a map of pre-constructed configuration
+     * metadata as its data source.
+     */
+    public static class ConfigurationsMapMetadataFactory implements ConfigurationMetadataFactory {
+        private final Map<String, LocalConfigurationMetadata> metadata;
+
+        public ConfigurationsMapMetadataFactory(Map<String, LocalConfigurationMetadata> metadata) {
+            this.metadata = metadata;
+        }
+
+        @Override
+        public Set<String> getConfigurationNames() {
+            return metadata.keySet();
+        }
+
+        @Override
+        public void visitConfigurations(Consumer<Candidate> visitor) {
+            for (LocalConfigurationMetadata configuration : metadata.values()) {
+                visitor.accept(new Candidate() {
+                    @Override
+                    public String getName() {
+                        return configuration.getName();
+                    }
+
+                    @Override
+                    public boolean isConsumable() {
+                        return configuration.isCanBeConsumed();
+                    }
+
+                    @Override
+                    public boolean hasAttributes() {
+                        return !configuration.getAttributes().isEmpty();
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void invalidate() {}
+
+        @Override
+        public LocalConfigurationMetadata getConfiguration(
+            String name,
+            DefaultLocalComponentMetadata parent
+        ) {
+            return metadata.get(name);
+        }
+    }
+
+    /**
+     * A {@link ConfigurationMetadataFactory} which uses a {@link ConfigurationsProvider} as its data source.
+     */
+    public static class ConfigurationsProviderMetadataFactory implements ConfigurationMetadataFactory {
+
+        private final ConfigurationsProvider configurationsProvider;
+        private final LocalConfigurationMetadataBuilder metadataBuilder;
+        private final ModelContainer<?> model;
+        private final CalculatedValueContainerFactory calculatedValueContainerFactory;
+        private final DefaultLocalConfigurationMetadataBuilder.DependencyCache cache;
+
+        public ConfigurationsProviderMetadataFactory(
+            ConfigurationsProvider configurationsProvider,
+            LocalConfigurationMetadataBuilder metadataBuilder,
+            ModelContainer<?> model,
+            CalculatedValueContainerFactory calculatedValueContainerFactory
+        ) {
+            this.configurationsProvider = configurationsProvider;
+            this.metadataBuilder = metadataBuilder;
+            this.model = model;
+            this.calculatedValueContainerFactory = calculatedValueContainerFactory;
+            this.cache = new LocalConfigurationMetadataBuilder.DependencyCache();
+        }
+
+        @Override
+        public Set<String> getConfigurationNames() {
+            ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+            configurationsProvider.visitAll(configuration -> builder.add(configuration.getName()));
+            return builder.build();
+        }
+
+        @Override
+        public void visitConfigurations(Consumer<Candidate> visitor) {
+            configurationsProvider.visitAll(configuration -> {
+                visitor.accept(new Candidate() {
+                    @Override
+                    public String getName() {
+                        return configuration.getName();
+                    }
+
+                    @Override
+                    public boolean isConsumable() {
+                        return configuration.isCanBeConsumed();
+                    }
+
+                    @Override
+                    public boolean hasAttributes() {
+                        return !configuration.getAttributes().isEmpty();
+                    }
+                });
+            });
+        }
+
+        @Override
+        public void invalidate() {
+            cache.invalidate();
+        }
+
+        @Override
+        public LocalConfigurationMetadata getConfiguration(
+            String name,
+            DefaultLocalComponentMetadata parent
+        ) {
+            ConfigurationInternal configuration = configurationsProvider.findByName(name);
+            if (configuration == null) {
+                return null;
+            }
+
+            return metadataBuilder.create(configuration, configurationsProvider, parent, cache, model, calculatedValueContainerFactory);
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalConfigurationMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalConfigurationMetadata.java
index 7f7ef2a..b5db879 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalConfigurationMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultLocalConfigurationMetadata.java
@@ -18,13 +18,9 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
+import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.attributes.Category;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder;
-import org.gradle.api.internal.attributes.AttributeValue;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.Describables;
 import org.gradle.internal.DisplayName;
@@ -33,99 +29,166 @@
 import org.gradle.internal.component.model.ExcludeMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
 import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
-import org.gradle.internal.component.model.VariantResolveMetadata;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
 import org.gradle.internal.model.CalculatedValueContainer;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.model.ModelContainer;
 
-import java.util.Collection;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
-public class DefaultLocalConfigurationMetadata implements LocalConfigurationMetadata, BuildableLocalConfigurationMetadata, LocalConfigurationGraphResolveMetadata {
+/**
+ * Default implementation of {@link LocalConfigurationMetadata} used to represent a single Configuration.
+ * <p>
+ * TODO: This class should be split up into a separate Metadata and State type in order to track
+ * artifact resolution state separately.
+ */
+public final class DefaultLocalConfigurationMetadata implements LocalConfigurationMetadata, LocalConfigurationGraphResolveMetadata {
+
     private final String name;
     private final String description;
+    private final ComponentIdentifier componentId;
     private final boolean transitive;
     private final boolean visible;
     private final ImmutableSet<String> hierarchy;
-    private final Set<String> extendsFrom;
     private final ImmutableAttributes attributes;
     private final boolean canBeConsumed;
-    private final DeprecationMessageBuilder.WithDocumentation consumptionDeprecation;
+    private final boolean deprecatedForConsumption;
     private final boolean canBeResolved;
     private final ImmutableCapabilities capabilities;
-    private final ModelContainer<?> model;
+    private final List<LocalOriginDependencyMetadata> configurationDependencies;
+    private final Set<LocalFileDependencyMetadata> configurationFileDependencies;
+    private final ImmutableList<ExcludeMetadata> configurationExcludes;
+
+    // TODO: Move all this lazy artifact stuff to a "State" type.
+    private final Set<LocalVariantMetadata> variants;
     private final CalculatedValueContainerFactory factory;
-    private final DefaultLocalComponentMetadata component;
+    private final CalculatedValueContainer<ImmutableList<LocalComponentArtifactMetadata>, ?> artifacts;
 
-    public ConfigurationInternal backingConfiguration;
-    private boolean reevaluate = true;
-    public LocalConfigurationMetadataBuilder configurationMetadataBuilder;
-
-    public final List<LocalOriginDependencyMetadata> definedDependencies = Lists.newArrayList();
-    public final List<ExcludeMetadata> definedExcludes = Lists.newArrayList();
-    public final List<LocalFileDependencyMetadata> definedFiles = Lists.newArrayList();
-
-    private ImmutableList<LocalOriginDependencyMetadata> configurationDependencies;
-    private ImmutableSet<LocalFileDependencyMetadata> configurationFileDependencies;
-    private ImmutableList<ExcludeMetadata> configurationExcludes;
-
-    public List<PublishArtifact> sourceArtifacts = Lists.newArrayList();
-    public CalculatedValueContainer<ImmutableList<LocalComponentArtifactMetadata>, ?> artifacts;
-
-    public final Set<LocalVariantMetadata> variants = new LinkedHashSet<>();
-
+    /**
+     * Creates a configuration metadata with lazily constructed artifact metadata.
+     */
     public DefaultLocalConfigurationMetadata(
         String name,
         String description,
+        ComponentIdentifier componentId,
         boolean visible,
         boolean transitive,
-        Set<String> extendsFrom,
-        ImmutableSet<String> hierarchy,
+        Set<String> hierarchy,
         ImmutableAttributes attributes,
-        boolean canBeConsumed,
-        DeprecationMessageBuilder.WithDocumentation consumptionDeprecation,
-        boolean canBeResolved,
         ImmutableCapabilities capabilities,
+        boolean canBeConsumed,
+        boolean deprecatedForConsumption,
+        boolean canBeResolved,
+        List<LocalOriginDependencyMetadata> configurationDependencies,
+        Set<LocalFileDependencyMetadata> configurationFileDependencies,
+        List<ExcludeMetadata> configurationExcludes,
+        Set<LocalVariantMetadata> variants,
+        final List<PublishArtifact> definedArtifacts,
         ModelContainer<?> model,
         CalculatedValueContainerFactory factory,
-        DefaultLocalComponentMetadata component
+        LocalComponentMetadata component
+    ) {
+        this(
+            name, description, componentId, visible, transitive, hierarchy, attributes, capabilities, canBeConsumed, deprecatedForConsumption,
+            canBeResolved, configurationDependencies, configurationFileDependencies, configurationExcludes, variants, factory,
+            getLazyArtifacts(definedArtifacts, name, description, hierarchy, model, factory, component)
+        );
+    }
+
+    /**
+     * Creates a configuration metadata with eagerly constructed artifact metadata.
+     */
+    public DefaultLocalConfigurationMetadata(
+        String name,
+        String description,
+        ComponentIdentifier componentId,
+        boolean visible,
+        boolean transitive,
+        Set<String> hierarchy,
+        ImmutableAttributes attributes,
+        ImmutableCapabilities capabilities,
+        boolean canBeConsumed,
+        boolean deprecatedForConsumption,
+        boolean canBeResolved,
+        List<LocalOriginDependencyMetadata> configurationDependencies,
+        Set<LocalFileDependencyMetadata> configurationFileDependencies,
+        List<ExcludeMetadata> configurationExcludes,
+        Set<LocalVariantMetadata> variants,
+        CalculatedValueContainerFactory factory,
+        List<LocalComponentArtifactMetadata> artifacts
+    ) {
+        this(
+            name, description, componentId, visible, transitive, hierarchy, attributes, capabilities, canBeConsumed, deprecatedForConsumption,
+            canBeResolved, configurationDependencies, configurationFileDependencies, configurationExcludes, variants, factory,
+            factory.create(Describables.of(description, "artifacts"), ImmutableList.copyOf(artifacts))
+        );
+    }
+
+    private DefaultLocalConfigurationMetadata(
+        String name,
+        String description,
+        ComponentIdentifier componentId,
+        boolean visible,
+        boolean transitive,
+        Set<String> hierarchy,
+        ImmutableAttributes attributes,
+        ImmutableCapabilities capabilities,
+        boolean canBeConsumed,
+        boolean deprecatedForConsumption,
+        boolean canBeResolved,
+        List<LocalOriginDependencyMetadata> configurationDependencies,
+        Set<LocalFileDependencyMetadata> configurationFileDependencies,
+        List<ExcludeMetadata> configurationExcludes,
+        Set<LocalVariantMetadata> variants,
+        CalculatedValueContainerFactory factory,
+        CalculatedValueContainer<ImmutableList<LocalComponentArtifactMetadata>, ?> artifacts
     ) {
         this.name = name;
         this.description = description;
-        this.transitive = transitive;
+        this.componentId = componentId;
         this.visible = visible;
-        this.hierarchy = hierarchy;
-        this.extendsFrom = extendsFrom;
+        this.transitive = transitive;
+        this.hierarchy = ImmutableSet.copyOf(hierarchy);
         this.attributes = attributes;
-        this.canBeConsumed = canBeConsumed;
-        this.consumptionDeprecation = consumptionDeprecation;
-        this.canBeResolved = canBeResolved;
         this.capabilities = capabilities;
-        this.model = model;
+        this.canBeConsumed = canBeConsumed;
+        this.deprecatedForConsumption = deprecatedForConsumption;
+        this.canBeResolved = canBeResolved;
+        this.configurationDependencies = configurationDependencies;
+        this.configurationFileDependencies = configurationFileDependencies;
+        this.configurationExcludes = ImmutableList.copyOf(configurationExcludes);
+        this.variants = variants;
         this.factory = factory;
-        this.component = component;
-        this.artifacts = factory.create(Describables.of(description, "artifacts"), context -> {
+        this.artifacts = artifacts;
+    }
+
+    /**
+     * Creates a calculated value container which lazily constructs this configuration's artifacts
+     * by traversing all configurations in the hierarchy and collecting their artifacts.
+     */
+    private static CalculatedValueContainer<ImmutableList<LocalComponentArtifactMetadata>, ?> getLazyArtifacts(
+        List<PublishArtifact> sourceArtifacts, String name, String description, Set<String> hierarchy,
+        ModelContainer<?> model, CalculatedValueContainerFactory factory, LocalComponentMetadata component
+    ) {
+        return factory.create(Describables.of(description, "artifacts"), context -> {
             if (sourceArtifacts.isEmpty() && hierarchy.isEmpty()) {
-                sourceArtifacts = null;
                 return ImmutableList.of();
             } else {
                 return model.fromMutableState(m -> {
                     Set<LocalComponentArtifactMetadata> result = new LinkedHashSet<>(sourceArtifacts.size());
                     for (PublishArtifact sourceArtifact : sourceArtifacts) {
+                        // The following line may realize tasks, so we wrap this code in a CalculatedValue.
                         result.add(new PublishArtifactLocalArtifactMetadata(component.getId(), sourceArtifact));
                     }
                     for (String config : hierarchy) {
                         if (config.equals(name)) {
                             continue;
                         }
-                        DefaultLocalConfigurationMetadata parent = component.getConfiguration(config);
-                        parent.prepareToResolveArtifacts();
-                        result.addAll(parent.getArtifacts());
+                        // TODO: Deprecate the behavior of inheriting artifacts from parent configurations.
+                        LocalConfigurationMetadata parent = component.getConfiguration(config);
+                        result.addAll(parent.prepareToResolveArtifacts().getArtifacts());
                     }
-                    sourceArtifacts = null;
                     return ImmutableList.copyOf(result);
                 });
             }
@@ -133,23 +196,34 @@ public DefaultLocalConfigurationMetadata(
     }
 
     @Override
-    public ComponentIdentifier getComponentId() {
-        return component.getId();
-    }
+    public LocalConfigurationMetadata copy(Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> artifactTransformer) {
+        // TODO: This method is implemented very inefficiently. We should apply the transformer to the artifacts
+        // lazily so that we don't need to prepareToResolveArtifacts here.
 
-    @Override
-    public void addDependency(LocalOriginDependencyMetadata dependency) {
-        definedDependencies.add(dependency);
-    }
+        ImmutableSet.Builder<LocalVariantMetadata> copiedVariants = ImmutableSet.builder();
+        for (LocalVariantMetadata oldVariant : variants) {
+            ImmutableList<LocalComponentArtifactMetadata> newArtifacts =
+                oldVariant.prepareToResolveArtifacts().getArtifacts().stream()
+                    .map(artifactTransformer::transform)
+                    .collect(ImmutableList.toImmutableList());
 
-    @Override
-    public void addExclude(ExcludeMetadata exclude) {
-        definedExcludes.add(exclude);
-    }
+            copiedVariants.add(new LocalVariantMetadata(
+                oldVariant.getName(), oldVariant.getIdentifier(), oldVariant.asDescribable(), oldVariant.getAttributes(),
+                (ImmutableCapabilities) oldVariant.getCapabilities(), newArtifacts, factory)
+            );
+        }
 
-    @Override
-    public void addFiles(LocalFileDependencyMetadata files) {
-        definedFiles.add(files);
+        ImmutableList<LocalComponentArtifactMetadata> copiedArtifacts =
+            prepareToResolveArtifacts().getArtifacts().stream()
+                .map(artifactTransformer::transform)
+                .collect(ImmutableList.toImmutableList());
+
+        return new DefaultLocalConfigurationMetadata(
+            name, description, componentId, visible, transitive, hierarchy, attributes, capabilities,
+            canBeConsumed, deprecatedForConsumption, canBeResolved,
+            configurationDependencies, configurationFileDependencies, configurationExcludes,
+            copiedVariants.build(), factory, copiedArtifacts
+        );
     }
 
     @Override
@@ -159,17 +233,7 @@ public String toString() {
 
     @Override
     public DisplayName asDescribable() {
-        return Describables.of(component.getId(), "configuration", name);
-    }
-
-    @Override
-    public String getDescription() {
-        return description;
-    }
-
-    @Override
-    public Set<String> getExtendsFrom() {
-        return extendsFrom;
+        return Describables.of(componentId, "configuration", name);
     }
 
     @Override
@@ -208,8 +272,8 @@ public boolean isCanBeConsumed() {
     }
 
     @Override
-    public DeprecationMessageBuilder.WithDocumentation getConsumptionDeprecation() {
-        return consumptionDeprecation;
+    public boolean isDeprecatedForConsumption() {
+        return deprecatedForConsumption;
     }
 
     @Override
@@ -219,75 +283,19 @@ public boolean isCanBeResolved() {
 
     @Override
     public List<? extends LocalOriginDependencyMetadata> getDependencies() {
-        if (configurationDependencies == null) {
-            ImmutableList.Builder<LocalOriginDependencyMetadata> result = ImmutableList.builder();
-            for (String configurationName : component.getConfigurationNames()) {
-                if (include(configurationName)) {
-                    component.getConfiguration(configurationName).addDefinedDependencies(result);
-                }
-            }
-            AttributeValue<Category> attributeValue = this.getAttributes().findEntry(Category.CATEGORY_ATTRIBUTE);
-            if (attributeValue.isPresent() && attributeValue.get().getName().equals(Category.ENFORCED_PLATFORM)) {
-                // need to wrap all dependencies to force them
-                ImmutableList<LocalOriginDependencyMetadata> rawDependencies = result.build();
-                result = ImmutableList.builder();
-                for (LocalOriginDependencyMetadata rawDependency : rawDependencies) {
-                    result.add(rawDependency.forced());
-                }
-            }
-            configurationDependencies = result.build();
-        }
         return configurationDependencies;
     }
 
-    void addDefinedDependencies(ImmutableList.Builder<LocalOriginDependencyMetadata> result) {
-        realizeDependencies();
-        result.addAll(definedDependencies);
-    }
-
     @Override
     public Set<LocalFileDependencyMetadata> getFiles() {
-        if (configurationFileDependencies == null) {
-            ImmutableSet.Builder<LocalFileDependencyMetadata> result = ImmutableSet.builder();
-            for (String configurationName : component.getConfigurationNames()) {
-                if (include(configurationName)) {
-                    component.getConfiguration(configurationName).addDefinedFiles(result);
-                }
-            }
-            configurationFileDependencies = result.build();
-        }
         return configurationFileDependencies;
     }
 
-    void addDefinedFiles(ImmutableSet.Builder<LocalFileDependencyMetadata> result) {
-        realizeDependencies();
-        result.addAll(definedFiles);
-    }
-
     @Override
     public ImmutableList<ExcludeMetadata> getExcludes() {
-        if (configurationExcludes == null) {
-            ImmutableList.Builder<ExcludeMetadata> result = ImmutableList.builder();
-            for (String configurationName : component.getConfigurationNames()) {
-                if (include(configurationName)) {
-                    component.getConfiguration(configurationName).addDefinedExcludes(result);
-                }
-            }
-            configurationExcludes = result.build();
-        }
         return configurationExcludes;
     }
 
-    void addDefinedExcludes(ImmutableList.Builder<ExcludeMetadata> result) {
-        realizeDependencies();
-        result.addAll(definedExcludes);
-    }
-
-    @Override
-    public void addArtifacts(Collection<? extends PublishArtifact> artifacts) {
-        sourceArtifacts.addAll(artifacts);
-    }
-
     @Override
     public LocalConfigurationMetadata prepareToResolveArtifacts() {
         artifacts.finalizeIfNotAlready();
@@ -310,7 +318,7 @@ public ComponentArtifactMetadata artifact(IvyArtifactName ivyArtifactName) {
             }
         }
 
-        return new MissingLocalArtifactMetadata(component.getId(), ivyArtifactName);
+        return new MissingLocalArtifactMetadata(componentId, ivyArtifactName);
     }
 
     @Override
@@ -323,39 +331,4 @@ public boolean isExternalVariant() {
         return false;
     }
 
-    private boolean include(String configurationName) {
-        return hierarchy.contains(configurationName);
-    }
-
-    @Override
-    public void addVariant(String name, VariantResolveMetadata.Identifier identifier, DisplayName displayName, ImmutableAttributes attributes, ImmutableCapabilities capabilities, Collection<? extends PublishArtifact> artifacts) {
-        variants.add(new LocalVariantMetadata(name, identifier, component.getId(), displayName, attributes, artifacts, capabilities, model, factory));
-    }
-
-    synchronized void realizeDependencies() {
-        if (reevaluate && backingConfiguration != null) {
-            backingConfiguration.runDependencyActions();
-            configurationMetadataBuilder.addDependenciesAndExcludes(this, backingConfiguration);
-        }
-        reevaluate = false;
-    }
-
-    /**
-     * When the backing configuration could have been modified, we need to clear our retained cache/state,
-     * so that the next evaluation is clean.
-     */
-    synchronized void reevaluate() {
-        definedDependencies.clear();
-        definedFiles.clear();
-        definedExcludes.clear();
-        configurationDependencies = null;
-        configurationExcludes = null;
-        configurationFileDependencies = null;
-        reevaluate = true;
-    }
-
-    @Override
-    public boolean needsReevaluate() {
-        return reevaluate;
-    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentSelector.java
index f698096..d89c35f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentSelector.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectComponentSelector.java
@@ -26,6 +26,7 @@
 import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.util.Path;
 
 import java.util.Collections;
@@ -68,8 +69,19 @@ public BuildIdentifier getBuildIdentifier() {
     }
 
     @Override
+    public String getBuildPath() {
+        return buildIdentifier.getBuildPath();
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
     public String getBuildName() {
-        return buildIdentifier.getName();
+        DeprecationLogger.deprecateMethod(ProjectComponentSelector.class, "getBuildName()")
+            .withAdvice("Use getBuildPath() to get a unique identifier for the build.")
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "build_identifier_name_and_current_deprecation")
+            .nagUser();
+        return DeprecationLogger.whileDisabled(buildIdentifier::getName);
     }
 
     public Path getIdentityPath() {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectDependencyMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectDependencyMetadata.java
index 36ecdd5..7dc7296 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectDependencyMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DefaultProjectDependencyMetadata.java
@@ -18,29 +18,23 @@
 
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.component.ProjectComponentSelector;
-import org.gradle.api.capabilities.Capability;
-import org.gradle.api.internal.attributes.AttributesSchemaInternal;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.component.model.ComponentGraphResolveState;
+import org.gradle.internal.component.model.DelegatingDependencyMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
 import org.gradle.internal.component.model.ExcludeMetadata;
 import org.gradle.internal.component.model.ForcingDependencyMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
-import org.gradle.internal.component.model.VariantSelectionResult;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
-public class DefaultProjectDependencyMetadata implements ForcingDependencyMetadata {
+public class DefaultProjectDependencyMetadata extends DelegatingDependencyMetadata implements ForcingDependencyMetadata {
     private final ProjectComponentSelector selector;
     private final DependencyMetadata delegate;
-    private final boolean isTransitive;
 
     public DefaultProjectDependencyMetadata(ProjectComponentSelector selector, DependencyMetadata delegate) {
+        super(delegate);
         this.selector = selector;
         this.delegate = delegate;
-        this.isTransitive = delegate.isTransitive();
     }
 
     @Override
@@ -70,46 +64,6 @@ public DependencyMetadata withTargetAndArtifacts(ComponentSelector target, List<
     }
 
     @Override
-    public boolean isChanging() {
-        return delegate.isChanging();
-    }
-
-    @Override
-    public boolean isConstraint() {
-        return delegate.isConstraint();
-    }
-
-    @Override
-    public boolean isEndorsingStrictVersions() {
-        return delegate.isEndorsingStrictVersions();
-    }
-
-    @Override
-    public String getReason() {
-        return delegate.getReason();
-    }
-
-    @Override
-    public boolean isTransitive() {
-        return isTransitive;
-    }
-
-    @Override
-    public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
-        return delegate.selectVariants(consumerAttributes, targetComponentState, consumerSchema, explicitRequestedCapabilities);
-    }
-
-    @Override
-    public List<IvyArtifactName> getArtifacts() {
-        return delegate.getArtifacts();
-    }
-
-    @Override
-    public DependencyMetadata withReason(String reason) {
-        return delegate.withReason(reason);
-    }
-
-    @Override
     public boolean isForce() {
         if (delegate instanceof ForcingDependencyMetadata) {
             return ((ForcingDependencyMetadata) delegate).isForce();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetadataWrapper.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetadataWrapper.java
index 826d95c..1944df0 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetadataWrapper.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/DslOriginDependencyMetadataWrapper.java
@@ -18,36 +18,28 @@
 
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.component.ComponentSelector;
-import org.gradle.api.capabilities.Capability;
-import org.gradle.api.internal.attributes.AttributesSchemaInternal;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.component.model.ComponentGraphResolveState;
-import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ExcludeMetadata;
+import org.gradle.internal.component.model.DelegatingDependencyMetadata;
 import org.gradle.internal.component.model.IvyArtifactName;
 import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
-import org.gradle.internal.component.model.VariantSelectionResult;
 
-import java.util.Collection;
 import java.util.List;
 
-public class DslOriginDependencyMetadataWrapper implements DslOriginDependencyMetadata, LocalOriginDependencyMetadata {
+public class DslOriginDependencyMetadataWrapper extends DelegatingDependencyMetadata implements DslOriginDependencyMetadata, LocalOriginDependencyMetadata {
     private final LocalOriginDependencyMetadata delegate;
     private final Dependency source;
-    private final boolean isTransitive;
-    private List<IvyArtifactName> artifacts;
+    private final List<IvyArtifactName> artifacts;
 
     public DslOriginDependencyMetadataWrapper(LocalOriginDependencyMetadata delegate, Dependency source) {
+        super(delegate);
         this.delegate = delegate;
         this.source = source;
-        this.isTransitive = delegate.isTransitive();
         this.artifacts = delegate.getArtifacts();
     }
 
     private DslOriginDependencyMetadataWrapper(LocalOriginDependencyMetadata delegate, Dependency source, List<IvyArtifactName> artifacts) {
+        super(delegate);
         this.delegate = delegate;
         this.source = source;
-        this.isTransitive = delegate.isTransitive();
         this.artifacts = artifacts;
     }
 
@@ -67,60 +59,21 @@ public String getModuleConfiguration() {
     }
 
     @Override
-    public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
-        return delegate.selectVariants(consumerAttributes, targetComponentState, consumerSchema, explicitRequestedCapabilities);
-    }
-
-    @Override
     public String getDependencyConfiguration() {
         return delegate.getDependencyConfiguration();
     }
 
     @Override
-    public List<ExcludeMetadata> getExcludes() {
-        return delegate.getExcludes();
-    }
-
-    @Override
-    public boolean isChanging() {
-        return delegate.isChanging();
-    }
-
-    @Override
-    public boolean isTransitive() {
-        return isTransitive;
-    }
-
-    @Override
     public boolean isForce() {
         return delegate.isForce();
     }
 
     @Override
-    public boolean isConstraint() {
-        return delegate.isConstraint();
-    }
-
-    @Override
-    public boolean isEndorsingStrictVersions() {
-        return delegate.isEndorsingStrictVersions();
-    }
-
-    private boolean isExternalVariant() {
-        return false;
-    }
-
-    @Override
     public boolean isFromLock() {
         return delegate.isFromLock();
     }
 
     @Override
-    public String getReason() {
-        return delegate.getReason();
-    }
-
-    @Override
     public List<IvyArtifactName> getArtifacts() {
         return artifacts;
     }
@@ -136,16 +89,6 @@ public LocalOriginDependencyMetadata withTargetAndArtifacts(ComponentSelector ta
     }
 
     @Override
-    public DependencyMetadata withReason(String reason) {
-        return delegate.withReason(reason);
-    }
-
-    @Override
-    public ComponentSelector getSelector() {
-        return delegate.getSelector();
-    }
-
-    @Override
     public LocalOriginDependencyMetadata forced() {
         return new DslOriginDependencyMetadataWrapper(delegate.forced(), source);
     }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentGraphResolveState.java
index e8490a8..1152465 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentGraphResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentGraphResolveState.java
@@ -24,10 +24,9 @@
 import javax.annotation.concurrent.ThreadSafe;
 
 /**
- * <p>Instances of this type are cached and reused for multiple graph resolutions, possibly in parallel. This means that the implementation must be thread-safe.
+ * A specialized {@link ComponentGraphResolveState} for local components (ie project dependencies).
  *
- * <p>Currently, instances of this type are cached once per project per build in the tree (ie a copy is created for each build in the tree where the project is referenced as a dependency.
- * This is because some of the composite build infrastructure assumes a specialized copy per build. This should be changed to remove the need for multiple copies.</p>
+ * <p>Instances of this type are cached and reused for multiple graph resolutions, possibly in parallel. This means that the implementation must be thread-safe.
  */
 @ThreadSafe
 public interface LocalComponentGraphResolveState extends ComponentGraphResolveState {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentGraphResolveStateFactory.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentGraphResolveStateFactory.java
new file mode 100644
index 0000000..d9f72e3
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentGraphResolveStateFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 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.component.local.model;
+
+import org.gradle.api.internal.attributes.AttributeDesugaring;
+import org.gradle.internal.component.model.ComponentIdGenerator;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+@ServiceScope(Scopes.BuildTree.class)
+public class LocalComponentGraphResolveStateFactory {
+    private final AttributeDesugaring attributeDesugaring;
+    private final ComponentIdGenerator idGenerator;
+
+    public LocalComponentGraphResolveStateFactory(AttributeDesugaring attributeDesugaring, ComponentIdGenerator idGenerator) {
+        this.attributeDesugaring = attributeDesugaring;
+        this.idGenerator = idGenerator;
+    }
+
+    public LocalComponentGraphResolveState stateFor(LocalComponentMetadata metadata) {
+        return new DefaultLocalComponentGraphResolveState(idGenerator.nextComponentId(), metadata, attributeDesugaring, idGenerator);
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentMetadata.java
index 2c00d7c..61ad35e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalComponentMetadata.java
@@ -16,6 +16,7 @@
 
 package org.gradle.internal.component.local.model;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata;
@@ -26,9 +27,9 @@
 public interface LocalComponentMetadata extends ComponentResolveMetadata, ComponentGraphResolveMetadata {
     @Nullable
     @Override
-    LocalConfigurationGraphResolveMetadata getConfiguration(String name);
+    LocalConfigurationMetadata getConfiguration(String name);
 
-    LocalComponentMetadata copy(ComponentIdentifier componentIdentifier, Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> artifacts);
+    LocalComponentMetadata copy(ComponentIdentifier componentIdentifier, Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> transformer);
 
     /**
      * We currently allow a configuration that has been partially observed for resolution to be modified
@@ -43,4 +44,10 @@ public interface LocalComponentMetadata extends ComponentResolveMetadata, Compon
      * @see org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder.MetadataHolder#tryCached(ComponentIdentifier)
      */
     void reevaluate();
+
+    /**
+     * Returns if the configuration with the given name has been realized.
+     */
+    @VisibleForTesting
+    boolean isConfigurationRealized(String configName);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalConfigurationGraphResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalConfigurationGraphResolveMetadata.java
index d73f4bd..c1c93ed 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalConfigurationGraphResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalConfigurationGraphResolveMetadata.java
@@ -16,7 +16,6 @@
 
 package org.gradle.internal.component.local.model;
 
-import com.google.common.annotations.VisibleForTesting;
 import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata;
 
 import java.util.Set;
@@ -34,10 +33,4 @@ public interface LocalConfigurationGraphResolveMetadata extends ConfigurationGra
      * <p>Note that this may be expensive, and should be called only when required.</p>
      */
     LocalConfigurationMetadata prepareToResolveArtifacts();
-
-    /**
-     * Returns if this metadata needs to be re-evaluated
-     */
-    @VisibleForTesting
-    boolean needsReevaluate();
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalConfigurationMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalConfigurationMetadata.java
index 981b91a..a6f6a0d 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalConfigurationMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalConfigurationMetadata.java
@@ -17,26 +17,14 @@
 package org.gradle.internal.component.local.model;
 
 import com.google.common.collect.ImmutableList;
+import org.gradle.api.Transformer;
 import org.gradle.internal.component.model.ConfigurationMetadata;
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata;
 
-import java.util.List;
-import java.util.Set;
-
-public interface LocalConfigurationMetadata extends ConfigurationMetadata {
-
-    String getDescription();
-
-    Set<String> getExtendsFrom();
-
-    @Override
-    List<? extends LocalOriginDependencyMetadata> getDependencies();
+public interface LocalConfigurationMetadata extends ConfigurationMetadata, LocalConfigurationGraphResolveMetadata {
 
     @Override
     ImmutableList<? extends LocalComponentArtifactMetadata> getArtifacts();
 
-    /**
-     * Returns the files attached to this configuration, if any. These should be represented as dependencies, but are currently represented as files as a migration step.
-     */
-    Set<LocalFileDependencyMetadata> getFiles();
+    LocalConfigurationMetadata copy(Transformer<LocalComponentArtifactMetadata, LocalComponentArtifactMetadata> artifactTransformer);
+
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalVariantMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalVariantMetadata.java
index 619a205..34a1de8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalVariantMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/local/model/LocalVariantMetadata.java
@@ -16,7 +16,6 @@
 
 package org.gradle.internal.component.local.model;
 
-
 import com.google.common.collect.ImmutableList;
 import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
@@ -25,18 +24,23 @@
 import org.gradle.internal.DisplayName;
 import org.gradle.internal.component.external.model.ImmutableCapabilities;
 import org.gradle.internal.component.model.DefaultVariantMetadata;
+import org.gradle.internal.component.model.VariantResolveMetadata;
 import org.gradle.internal.model.CalculatedValueContainer;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.internal.model.ModelContainer;
 
 import java.util.Collection;
+import java.util.List;
 
-public class LocalVariantMetadata extends DefaultVariantMetadata {
+/**
+ * Implementation of {@link VariantResolveMetadata} which allows variant artifacts to be calculated lazily
+ * while holding a project lock.
+ */
+public final class LocalVariantMetadata extends DefaultVariantMetadata {
     private final CalculatedValueContainer<ImmutableList<LocalComponentArtifactMetadata>, ?> artifacts;
 
     public LocalVariantMetadata(String name, Identifier identifier, ComponentIdentifier componentId, DisplayName displayName, ImmutableAttributes attributes, Collection<? extends PublishArtifact> sourceArtifacts, ImmutableCapabilities capabilities, ModelContainer<?> model, CalculatedValueContainerFactory calculatedValueContainerFactory) {
-        super(name, identifier, displayName, attributes, ImmutableList.of(), capabilities);
-        artifacts = calculatedValueContainerFactory.create(Describables.of(displayName, "artifacts"), context -> {
+        this(name, identifier, displayName, attributes, capabilities, calculatedValueContainerFactory.create(Describables.of(displayName, "artifacts"), context -> {
             if (sourceArtifacts.isEmpty()) {
                 return ImmutableList.of();
             } else {
@@ -48,16 +52,21 @@ public LocalVariantMetadata(String name, Identifier identifier, ComponentIdentif
                     return result.build();
                 });
             }
-        });
+        }));
     }
 
-    public LocalVariantMetadata(String name, Identifier identifier, DisplayName displayName, ImmutableAttributes attributes, ImmutableList<LocalComponentArtifactMetadata> artifacts, ImmutableCapabilities capabilities, CalculatedValueContainerFactory calculatedValueContainerFactory) {
+    public LocalVariantMetadata(String name, Identifier identifier, DisplayName displayName, ImmutableAttributes attributes, ImmutableCapabilities capabilities, List<LocalComponentArtifactMetadata> artifacts, CalculatedValueContainerFactory calculatedValueContainerFactory) {
+        this(name, identifier, displayName, attributes, capabilities, calculatedValueContainerFactory.create(Describables.of(displayName, "artifacts"), ImmutableList.copyOf(artifacts)));
+    }
+
+    private LocalVariantMetadata(String name, Identifier identifier, DisplayName displayName, ImmutableAttributes attributes, ImmutableCapabilities capabilities, CalculatedValueContainer<ImmutableList<LocalComponentArtifactMetadata>, ?> artifacts) {
         super(name, identifier, displayName, attributes, ImmutableList.of(), capabilities);
-        this.artifacts = calculatedValueContainerFactory.create(Describables.of(displayName, "artifacts"), artifacts);
+        this.artifacts = artifacts;
     }
 
-    public void prepareToResolveArtifacts() {
+    public LocalVariantMetadata prepareToResolveArtifacts() {
         artifacts.finalizeIfNotAlready();
+        return this;
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AbstractComponentGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AbstractComponentGraphResolveState.java
index 5a6e5cc..1c9fb37 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AbstractComponentGraphResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AbstractComponentGraphResolveState.java
@@ -16,26 +16,49 @@
 
 package org.gradle.internal.component.model;
 
+import com.google.common.collect.ImmutableList;
+import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.artifacts.result.ResolvedVariantResult;
+import org.gradle.api.capabilities.CapabilitiesMetadata;
+import org.gradle.api.capabilities.Capability;
+import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChainModuleSource;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet;
+import org.gradle.api.internal.artifacts.result.DefaultResolvedVariantResult;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.component.ArtifactType;
+import org.gradle.internal.Describables;
+import org.gradle.internal.component.external.model.DefaultImmutableCapability;
+import org.gradle.internal.lazy.Lazy;
 import org.gradle.internal.resolve.resolver.ArtifactResolver;
 import org.gradle.internal.resolve.resolver.ArtifactSelector;
 import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
 
 import javax.annotation.Nullable;
 import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
 
 public abstract class AbstractComponentGraphResolveState<T extends ComponentGraphResolveMetadata, S extends ComponentResolveMetadata> implements ComponentGraphResolveState, ComponentArtifactResolveState {
+    private final long instanceId;
     private final T graphMetadata;
     private final S artifactMetadata;
+    private final AttributeDesugaring attributeDesugaring;
 
-    public AbstractComponentGraphResolveState(T graphMetadata, S artifactMetadata) {
+    public AbstractComponentGraphResolveState(long instanceId, T graphMetadata, S artifactMetadata, AttributeDesugaring attributeDesugaring) {
+        this.instanceId = instanceId;
         this.graphMetadata = graphMetadata;
         this.artifactMetadata = artifactMetadata;
+        this.attributeDesugaring = attributeDesugaring;
+    }
+
+    @Override
+    public long getInstanceId() {
+        return instanceId;
     }
 
     @Override
@@ -45,8 +68,10 @@ public ComponentIdentifier getId() {
 
     @Nullable
     @Override
-    public ModuleSources getSources() {
-        return artifactMetadata.getSources();
+    public String getRepositoryId() {
+        return getSources().withSource(RepositoryChainModuleSource.class, source -> source
+            .map(RepositoryChainModuleSource::getRepositoryId)
+            .orElse(null));
     }
 
     @Override
@@ -58,6 +83,14 @@ public S getArtifactMetadata() {
         return artifactMetadata;
     }
 
+    @Override
+    public GraphSelectionCandidates getCandidatesForGraphVariantSelection() {
+        Optional<List<? extends VariantGraphResolveState>> variants = getVariantsForGraphTraversal();
+        return new DefaultGraphSelectionCandidates(variants);
+    }
+
+    protected abstract Optional<List<? extends VariantGraphResolveState>> getVariantsForGraphTraversal();
+
     @Nullable
     @Override
     public ComponentGraphResolveState maybeAsLenientPlatform(ModuleComponentIdentifier componentIdentifier, ModuleVersionIdentifier moduleVersionIdentifier) {
@@ -70,11 +103,87 @@ public ComponentArtifactResolveState prepareForArtifactResolution() {
     }
 
     public void resolveArtifactsWithType(ArtifactResolver artifactResolver, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
-        artifactResolver.resolveArtifactsWithType(artifactMetadata, artifactType, result);
+        artifactResolver.resolveArtifactsWithType(getResolveMetadata(), artifactType, result);
     }
 
     @Override
     public ArtifactSet prepareForArtifactResolution(ArtifactSelector artifactSelector, Collection<? extends ComponentArtifactMetadata> artifacts, ImmutableAttributes overriddenAttributes) {
-        return artifactSelector.resolveArtifacts(artifactMetadata, artifacts, overriddenAttributes);
+        return artifactSelector.resolveArtifacts(getResolveMetadata(), artifacts, overriddenAttributes);
+    }
+
+    protected List<? extends Capability> capabilitiesFor(CapabilitiesMetadata variantCapabilities) {
+        List<? extends Capability> capabilities = variantCapabilities.getCapabilities();
+        if (capabilities.isEmpty()) {
+            capabilities = ImmutableList.of(DefaultImmutableCapability.defaultCapabilityForComponent(getMetadata().getModuleVersionId()));
+        } else {
+            capabilities = ImmutableList.copyOf(capabilities);
+        }
+        return capabilities;
+    }
+
+    protected abstract class AbstractVariantGraphResolveState implements VariantGraphResolveState {
+        private final Lazy<ResolvedVariantResult> publicView;
+
+        public AbstractVariantGraphResolveState() {
+            this.publicView = Lazy.locking().of(() -> createVariantResult(null));
+        }
+
+        @Override
+        public ResolvedVariantResult getVariantResult(@Nullable ResolvedVariantResult externalVariant) {
+            if (externalVariant != null) {
+                // Don't cache the result
+                // Note that the external variant is a function of the metadata of the component, so should be constructed by this state object and cached rather than passed in
+                return createVariantResult(externalVariant);
+            } else {
+                return publicView.get();
+            }
+        }
+
+        private DefaultResolvedVariantResult createVariantResult(@Nullable ResolvedVariantResult externalVariant) {
+            VariantGraphResolveMetadata metadata = getMetadata();
+            return new DefaultResolvedVariantResult(
+                getId(),
+                Describables.of(metadata.getName()),
+                attributeDesugaring.desugar(metadata.getAttributes()),
+                capabilitiesFor(metadata.getCapabilities()),
+                externalVariant);
+        }
+    }
+
+    private class DefaultGraphSelectionCandidates implements GraphSelectionCandidates {
+        private final Optional<List<? extends VariantGraphResolveState>> variants;
+
+        public DefaultGraphSelectionCandidates(Optional<List<? extends VariantGraphResolveState>> variants) {
+            this.variants = variants;
+        }
+
+        @Override
+        public boolean isUseVariants() {
+            return variants.isPresent() && !variants.get().isEmpty();
+        }
+
+        @Override
+        public List<? extends VariantGraphResolveState> getVariants() {
+            return variants.get();
+        }
+
+        @Nullable
+        @Override
+        public ConfigurationGraphResolveState getLegacyConfiguration() {
+            return getConfiguration(Dependency.DEFAULT_CONFIGURATION);
+        }
+
+        @Override
+        public List<? extends ConfigurationGraphResolveMetadata> getCandidateConfigurations() {
+            Set<String> configurationNames = graphMetadata.getConfigurationNames();
+            ImmutableList.Builder<ConfigurationGraphResolveMetadata> builder = new ImmutableList.Builder<>();
+            for (String configurationName : configurationNames) {
+                ConfigurationGraphResolveMetadata configuration = graphMetadata.getConfiguration(configurationName);
+                if (configuration.isCanBeConsumed()) {
+                    builder.add(configuration);
+                }
+            }
+            return builder.build();
+        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeConfigurationSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeConfigurationSelector.java
index 2fba30d..7fa7d77 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeConfigurationSelector.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeConfigurationSelector.java
@@ -16,12 +16,11 @@
 
 package org.gradle.internal.component.model;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import org.gradle.api.artifacts.ArtifactIdentifier;
-import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.artifacts.ModuleIdentifier;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.attributes.Attribute;
 import org.gradle.api.attributes.HasAttributes;
@@ -32,14 +31,15 @@
 import org.gradle.api.internal.attributes.AttributeValue;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.api.internal.capabilities.CapabilitiesMetadataInternal;
+import org.gradle.api.internal.capabilities.ShadowedCapability;
 import org.gradle.internal.Cast;
 import org.gradle.internal.component.AmbiguousConfigurationSelectionException;
 import org.gradle.internal.component.NoMatchingCapabilitiesException;
 import org.gradle.internal.component.NoMatchingConfigurationSelectionException;
-import org.gradle.api.internal.capabilities.CapabilitiesMetadataInternal;
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
-import org.gradle.api.internal.capabilities.ShadowedCapability;
 
+import javax.annotation.Nullable;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -53,72 +53,91 @@ public static VariantSelectionResult selectVariantsUsingAttributeMatching(Immuta
 
     private static VariantSelectionResult selectVariantsUsingAttributeMatching(ImmutableAttributes consumerAttributes, Collection<? extends Capability> explicitRequestedCapabilities, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, List<IvyArtifactName> requestedArtifacts, AttributeMatchingExplanationBuilder explanationBuilder) {
         ComponentGraphResolveMetadata targetComponent = targetComponentState.getMetadata();
-        Optional<List<? extends VariantGraphResolveMetadata>> variantsForGraphTraversal = targetComponent.getVariantsForGraphTraversal();
-        List<? extends VariantGraphResolveMetadata> consumableVariants = variantsForGraphTraversal.or(ImmutableList.of());
-        AttributesSchemaInternal producerAttributeSchema = targetComponent.getAttributesSchema();
-        AttributeMatcher attributeMatcher = consumerSchema.withProducer(producerAttributeSchema);
-        ConfigurationGraphResolveMetadata fallbackConfiguration = targetComponent.getConfiguration(Dependency.DEFAULT_CONFIGURATION);
-        if (fallbackConfiguration != null && !fallbackConfiguration.isCanBeConsumed()) {
-            fallbackConfiguration = null;
+        GraphSelectionCandidates candidates = targetComponentState.getCandidatesForGraphVariantSelection();
+        AttributeMatcher attributeMatcher = consumerSchema.withProducer(targetComponent.getAttributesSchema());
+
+        boolean variantAware = candidates.isUseVariants();
+
+        // Fallback to the default configuration if there are no variants or if variant aware resolution is not supported.
+        if (!variantAware) {
+            return selectDefaultConfiguration(consumerAttributes, consumerSchema, targetComponent, attributeMatcher, candidates);
         }
-        ModuleVersionIdentifier versionId = targetComponent.getModuleVersionId();
-        if (!consumableVariants.isEmpty()) {
-            ImmutableList<VariantGraphResolveMetadata> variantsProvidingRequestedCapabilities = filterVariantsByRequestedCapabilities(targetComponent, explicitRequestedCapabilities, consumableVariants, versionId.getGroup(), versionId.getName(), true);
-            if (variantsProvidingRequestedCapabilities.isEmpty()) {
-                throw new NoMatchingCapabilitiesException(targetComponent, explicitRequestedCapabilities, consumableVariants);
-            }
-            consumableVariants = variantsProvidingRequestedCapabilities;
+
+        List<? extends VariantGraphResolveState> allConsumableVariants = candidates.getVariants();
+        ImmutableList<VariantGraphResolveState> variantsProvidingRequestedCapabilities = filterVariantsByRequestedCapabilities(targetComponent, explicitRequestedCapabilities, allConsumableVariants, true);
+        if (variantsProvidingRequestedCapabilities.isEmpty()) {
+            throw new NoMatchingCapabilitiesException(targetComponent, explicitRequestedCapabilities, allConsumableVariants);
         }
-        List<VariantGraphResolveMetadata> matches = attributeMatcher.matches(consumableVariants, consumerAttributes, fallbackConfiguration, explanationBuilder);
+
+        List<VariantGraphResolveState> matches = attributeMatcher.matches(variantsProvidingRequestedCapabilities, consumerAttributes, explanationBuilder);
         if (matches.size() > 1) {
             // there's an ambiguity, but we may have several variants matching the requested capabilities.
             // Here we're going to check if in the candidates, there's a single one _strictly_ matching the requested capabilities.
-            List<VariantGraphResolveMetadata> strictlyMatchingCapabilities = filterVariantsByRequestedCapabilities(targetComponent, explicitRequestedCapabilities, matches, versionId.getGroup(), versionId.getName(), false);
+            List<VariantGraphResolveState> strictlyMatchingCapabilities = filterVariantsByRequestedCapabilities(targetComponent, explicitRequestedCapabilities, matches, false);
             if (strictlyMatchingCapabilities.size() == 1) {
-                return singleVariant(variantsForGraphTraversal, strictlyMatchingCapabilities);
+                return singleVariant(true, strictlyMatchingCapabilities);
             } else if (strictlyMatchingCapabilities.size() > 1) {
                 // there are still more than one candidate, but this time we know only a subset strictly matches the required attributes
                 // so we perform another round of selection on the remaining candidates
-                strictlyMatchingCapabilities = attributeMatcher.matches(strictlyMatchingCapabilities, consumerAttributes, fallbackConfiguration, explanationBuilder);
+                strictlyMatchingCapabilities = attributeMatcher.matches(strictlyMatchingCapabilities, consumerAttributes, explanationBuilder);
                 if (strictlyMatchingCapabilities.size() == 1) {
-                    return singleVariant(variantsForGraphTraversal, strictlyMatchingCapabilities);
+                    return singleVariant(true, strictlyMatchingCapabilities);
                 }
             }
+
             if (requestedArtifacts.size() == 1) {
                 // Here, we know that the user requested a specific classifier. There may be multiple
                 // candidate variants left, but maybe only one of them provides the classified artifact
                 // we're looking for.
                 String classifier = requestedArtifacts.get(0).getClassifier();
                 if (classifier != null) {
-                    List<VariantGraphResolveMetadata> sameClassifier = findVariantsProvidingExactlySameClassifier(matches, classifier, targetComponentState);
+                    List<VariantGraphResolveState> sameClassifier = findVariantsProvidingExactlySameClassifier(matches, classifier);
                     if (sameClassifier != null && sameClassifier.size() == 1) {
-                        return singleVariant(variantsForGraphTraversal, sameClassifier);
+                        return singleVariant(true, sameClassifier);
                     }
                 }
             }
         }
+
         if (matches.size() == 1) {
-            return singleVariant(variantsForGraphTraversal, matches);
+            return singleVariant(true, matches);
         } else if (!matches.isEmpty()) {
             AttributeDescriber describer = DescriberSelector.selectDescriber(consumerAttributes, consumerSchema);
             if (explanationBuilder instanceof TraceDiscardedConfigurations) {
-                Set<VariantGraphResolveMetadata> discarded = Cast.uncheckedCast(((TraceDiscardedConfigurations) explanationBuilder).discarded);
-                throw new AmbiguousConfigurationSelectionException(describer, consumerAttributes, attributeMatcher, matches, targetComponent, variantsForGraphTraversal.isPresent(), discarded);
+                Set<VariantGraphResolveState> discarded = Cast.uncheckedCast(((TraceDiscardedConfigurations) explanationBuilder).discarded);
+                throw new AmbiguousConfigurationSelectionException(describer, consumerAttributes, attributeMatcher, matches, targetComponent, true, discarded);
             } else {
                 // Perform a second resolution with tracing
                 return selectVariantsUsingAttributeMatching(consumerAttributes, explicitRequestedCapabilities, targetComponentState, consumerSchema, requestedArtifacts, new TraceDiscardedConfigurations());
             }
         } else {
             AttributeDescriber describer = DescriberSelector.selectDescriber(consumerAttributes, consumerSchema);
-            throw new NoMatchingConfigurationSelectionException(describer, consumerAttributes, attributeMatcher, targetComponent, variantsForGraphTraversal.isPresent());
+            throw new NoMatchingConfigurationSelectionException(describer, consumerAttributes, attributeMatcher, targetComponent, candidates);
         }
     }
 
-    private static List<VariantGraphResolveMetadata> findVariantsProvidingExactlySameClassifier(List<VariantGraphResolveMetadata> matches, String classifier, ComponentGraphResolveState targetComponent) {
-        List<VariantGraphResolveMetadata> sameClassifier = null;
+    private static VariantSelectionResult selectDefaultConfiguration(
+        ImmutableAttributes consumerAttributes, AttributesSchemaInternal consumerSchema,
+        ComponentGraphResolveMetadata targetComponent, AttributeMatcher attributeMatcher, GraphSelectionCandidates candidates
+    ) {
+        ConfigurationGraphResolveState fallbackConfiguration = candidates.getLegacyConfiguration();
+        if (fallbackConfiguration != null &&
+            fallbackConfiguration.getMetadata().isCanBeConsumed() &&
+            attributeMatcher.isMatching(fallbackConfiguration.getAttributes(), consumerAttributes)
+        ) {
+            return singleVariant(candidates.isUseVariants(), ImmutableList.of(fallbackConfiguration.asVariant()));
+        }
+
+        AttributeDescriber describer = DescriberSelector.selectDescriber(consumerAttributes, consumerSchema);
+        throw new NoMatchingConfigurationSelectionException(describer, consumerAttributes, attributeMatcher, targetComponent, candidates);
+    }
+
+    @Nullable
+    private static List<VariantGraphResolveState> findVariantsProvidingExactlySameClassifier(List<VariantGraphResolveState> matches, String classifier) {
+        List<VariantGraphResolveState> sameClassifier = null;
         // let's see if we can find a single variant which has exactly the requested artifacts
-        for (VariantGraphResolveMetadata match : matches) {
-            List<? extends ComponentArtifactMetadata> artifacts = targetComponent.resolveArtifactsFor(match).getArtifacts();
+        for (VariantGraphResolveState match : matches) {
+            List<? extends ComponentArtifactMetadata> artifacts = match.resolveArtifacts().getArtifacts();
             if (artifacts.size() == 1) {
                 ComponentArtifactMetadata componentArtifactMetadata = artifacts.get(0);
                 if (componentArtifactMetadata instanceof ModuleComponentArtifactMetadata) {
@@ -137,17 +156,19 @@ private static List<VariantGraphResolveMetadata> findVariantsProvidingExactlySam
         return sameClassifier;
     }
 
-    private static VariantSelectionResult singleVariant(Optional<List<? extends VariantGraphResolveMetadata>> variantsForGraphTraversal, List<VariantGraphResolveMetadata> matches) {
-        return new VariantSelectionResult(ImmutableList.of(matches.get(0)), variantsForGraphTraversal.isPresent());
+    private static VariantSelectionResult singleVariant(boolean variantAware, List<VariantGraphResolveState> matches) {
+        assert matches.size() == 1;
+        return new VariantSelectionResult(ImmutableList.of(matches.get(0)), variantAware);
     }
 
-    private static ImmutableList<VariantGraphResolveMetadata> filterVariantsByRequestedCapabilities(ComponentGraphResolveMetadata targetComponent, Collection<? extends Capability> explicitRequestedCapabilities, Collection<? extends VariantGraphResolveMetadata> consumableVariants, String group, String name, boolean lenient) {
+    private static ImmutableList<VariantGraphResolveState> filterVariantsByRequestedCapabilities(ComponentGraphResolveMetadata targetComponent, Collection<? extends Capability> explicitRequestedCapabilities, Collection<? extends VariantGraphResolveState> consumableVariants, boolean lenient) {
         if (consumableVariants.isEmpty()) {
             return ImmutableList.of();
         }
-        ImmutableList.Builder<VariantGraphResolveMetadata> builder = ImmutableList.builderWithExpectedSize(consumableVariants.size());
+        ImmutableList.Builder<VariantGraphResolveState> builder = ImmutableList.builderWithExpectedSize(consumableVariants.size());
         boolean explicitlyRequested = !explicitRequestedCapabilities.isEmpty();
-        for (VariantGraphResolveMetadata variant : consumableVariants) {
+        ModuleIdentifier moduleId = targetComponent.getModuleVersionId().getModule();
+        for (VariantGraphResolveState variant : consumableVariants) {
             CapabilitiesMetadata capabilitiesMetadata = variant.getCapabilities();
             List<? extends Capability> capabilities = capabilitiesMetadata.getCapabilities();
             MatchResult result;
@@ -157,7 +178,7 @@ private static ImmutableList<VariantGraphResolveMetadata> filterVariantsByReques
                 result = providesAllCapabilities(targetComponent, explicitRequestedCapabilities, capabilities);
             } else {
                 // we need to make sure the variants we consider provide the implicit capability
-                result = containsImplicitCapability(capabilitiesMetadata, capabilities, group, name);
+                result = containsImplicitCapability(capabilitiesMetadata, capabilities, moduleId.getGroup(), moduleId.getName());
             }
             if (result.matches) {
                 if (lenient || result == MatchResult.EXACT_MATCH) {
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeMatcher.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeMatcher.java
index 826ebd1..460ac25 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeMatcher.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeMatcher.java
@@ -22,7 +22,6 @@
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.attributes.AttributeValue;
 
-import javax.annotation.Nullable;
 import java.util.Collection;
 import java.util.List;
 
@@ -38,12 +37,10 @@ public interface AttributeMatcher {
 
     <T> boolean isMatching(Attribute<T> attribute, T candidate, T requested);
 
-    <T extends HasAttributes> List<T> matches(Collection<? extends T> candidates, AttributeContainerInternal requested, AttributeMatchingExplanationBuilder builder);
-
     /**
-     * Selects the candidates from the given set that are compatible with the requested criteria.
+     * Selects all matches from {@code candidates} that are compatible with the {@code requested} criteria attributes.
      */
-    <T extends HasAttributes, E extends T> List<T> matches(Collection<E> candidates, AttributeContainerInternal requested, @Nullable T fallback, AttributeMatchingExplanationBuilder builder);
+    <T extends HasAttributes> List<T> matches(Collection<? extends T> candidates, AttributeContainerInternal requested, AttributeMatchingExplanationBuilder builder);
 
     List<MatchingDescription<?>> describeMatching(AttributeContainerInternal candidate, AttributeContainerInternal requested);
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeMatchingExplanationBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeMatchingExplanationBuilder.java
index 89b69e2..7d0fcea 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeMatchingExplanationBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/AttributeMatchingExplanationBuilder.java
@@ -31,10 +31,7 @@ static AttributeMatchingExplanationBuilder logging() {
 
     boolean canSkipExplanation();
 
-    default <T extends HasAttributes> void selectedFallbackConfiguration(AttributeContainerInternal requested, T fallback) {
-    }
-
-    default <T extends HasAttributes> void noCandidates(AttributeContainerInternal requested, T fallback) {
+    default <T extends HasAttributes> void noCandidates(AttributeContainerInternal requested) {
 
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveMetadata.java
new file mode 100644
index 0000000..9d8d558
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveMetadata.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023 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.component.model;
+
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.internal.attributes.AttributesSchemaInternal;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+
+import javax.annotation.Nullable;
+
+/**
+ * Metadata used to resolve artifacts of a component.
+ */
+public interface ComponentArtifactResolveMetadata {
+    ComponentIdentifier getId();
+
+    ModuleVersionIdentifier getModuleVersionId();
+
+    @Nullable
+    ModuleSources getSources();
+
+    ImmutableAttributes getAttributes();
+
+    AttributesSchemaInternal getAttributesSchema();
+
+    // Try to avoid using this, it's here to transition away from using ComponentResolveMetadata everywhere
+    ComponentResolveMetadata getMetadata();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveState.java
index 2de3219..3a24d71 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveState.java
@@ -24,7 +24,6 @@
 import org.gradle.internal.resolve.resolver.ArtifactSelector;
 import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
 
-import javax.annotation.Nullable;
 import java.util.Collection;
 
 /**
@@ -40,18 +39,12 @@
  *
  * <p>This interface says nothing about thread safety, however some subtypes may be required to be thread safe.</p>
  *
- * <p>Instances of this type are located using {@link ComponentGraphResolveState#prepareForArtifactResolution()}.</p>
+ * <p>Instances of this type are created using {@link ComponentGraphResolveState#prepareForArtifactResolution()}.</p>
  */
 public interface ComponentArtifactResolveState {
     ComponentIdentifier getId();
 
-    @Nullable
-    ModuleSources getSources();
-
-    /**
-     * Returns the state required to resolve artifacts, given a variant that was selected during graph resolution.
-     */
-    VariantArtifactResolveState prepareForArtifactResolution(VariantGraphResolveMetadata variant);
+    ComponentArtifactResolveMetadata getResolveMetadata();
 
     /**
      * Discovers the set of artifacts belonging to this component, with the type specified. Does not download the artifacts. Any failures are packaged up in the result.
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveVariantState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveVariantState.java
index 53836e0..bd5cdf9 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveVariantState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentArtifactResolveVariantState.java
@@ -22,8 +22,6 @@
 
 /**
  * Represents all variants available for artifact selection.
- *
- * TODO: Combine this with ComponentArtifactResolveState
  */
 public interface ComponentArtifactResolveVariantState {
     Set<ResolvedVariant> getAllVariants();
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentAttributeMatcher.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentAttributeMatcher.java
deleted file mode 100644
index ae5aad2..0000000
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentAttributeMatcher.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright 2016 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.component.model;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.primitives.Ints;
-import org.gradle.api.attributes.Attribute;
-import org.gradle.api.attributes.HasAttributes;
-import org.gradle.api.internal.attributes.AttributeContainerInternal;
-import org.gradle.api.internal.attributes.AttributeValue;
-import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-/**
- * An {@link AttributeMatcher}, which optimizes for the case of only comparing 0 or 1 candidates and delegates to {@link MultipleCandidateMatcher} for all other cases.
- */
-public class ComponentAttributeMatcher implements AttributeMatcher {
-    private static final Logger LOGGER = LoggerFactory.getLogger(ComponentAttributeMatcher.class);
-
-    private final AttributeSelectionSchema schema;
-
-    /**
-     * Attribute matching can be very expensive. In case there are multiple candidates, we
-     * cache the result of the query, because it's often the case that we ask for the same
-     * disambiguation of attributes several times in a row (but with different candidates).
-     */
-    private final ConcurrentMap<CachedQuery, int[]> cachedQueries = new ConcurrentHashMap<>();
-
-    public ComponentAttributeMatcher(AttributeSelectionSchema schema) {
-        this.schema = schema;
-    }
-
-    @Override
-    public AttributeSelectionSchema getSelectionSchema() {
-        return schema;
-    }
-
-    @Override
-    public boolean isMatching(AttributeContainerInternal candidate, AttributeContainerInternal requested) {
-        if (requested.isEmpty() || candidate.isEmpty()) {
-            return true;
-        }
-
-        ImmutableAttributes requestedAttributes = requested.asImmutable();
-        ImmutableAttributes candidateAttributes = candidate.asImmutable();
-
-        for (Attribute<?> attribute : requestedAttributes.keySet()) {
-            AttributeValue<?> requestedValue = requestedAttributes.findEntry(attribute);
-            AttributeValue<?> candidateValue = candidateAttributes.findEntry(attribute.getName());
-            if (candidateValue.isPresent()) {
-                Object coercedValue = candidateValue.coerce(attribute);
-                boolean match = schema.matchValue(attribute, requestedValue.get(), coercedValue);
-                if (!match) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public <T> boolean isMatching(Attribute<T> attribute, T candidate, T requested) {
-        return schema.matchValue(attribute, requested, candidate);
-    }
-
-    @Override
-    @SuppressWarnings({"unchecked", "rawtypes"})
-    public List<AttributeMatcher.MatchingDescription<?>> describeMatching(AttributeContainerInternal candidate, AttributeContainerInternal requested) {
-        if (requested.isEmpty() || candidate.isEmpty()) {
-            return Collections.emptyList();
-        }
-
-        ImmutableAttributes requestedAttributes = requested.asImmutable();
-        ImmutableAttributes candidateAttributes = candidate.asImmutable();
-
-        ImmutableSet<Attribute<?>> attributes = requestedAttributes.keySet();
-        List<AttributeMatcher.MatchingDescription<?>> result = new ArrayList<>(attributes.size());
-        for (Attribute<?> attribute : attributes) {
-            AttributeValue<?> requestedValue = requestedAttributes.findEntry(attribute);
-            AttributeValue<?> candidateValue = candidateAttributes.findEntry(attribute.getName());
-            if (candidateValue.isPresent()) {
-                Object coercedValue = candidateValue.coerce(attribute);
-                boolean match = schema.matchValue(attribute, requestedValue.get(), coercedValue);
-                result.add(new AttributeMatcher.MatchingDescription(attribute, requestedValue, candidateValue, match));
-            } else {
-                result.add(new AttributeMatcher.MatchingDescription(attribute, requestedValue, candidateValue, false));
-            }
-        }
-        return result;
-    }
-
-    @Override
-    public <T extends HasAttributes> List<T> matches(Collection<? extends T> candidates, AttributeContainerInternal requested, AttributeMatchingExplanationBuilder explanationBuilder) {
-        return matches(candidates, requested, null, explanationBuilder);
-    }
-
-    @Override
-    public <T extends HasAttributes, E extends T> List<T> matches(Collection<E> candidates, AttributeContainerInternal requested, @Nullable T fallback, AttributeMatchingExplanationBuilder explanationBuilder) {
-        if (candidates.size() == 0) {
-            if (fallback != null && isMatching((AttributeContainerInternal) fallback.getAttributes(), requested)) {
-                explanationBuilder.selectedFallbackConfiguration(requested, fallback);
-                return ImmutableList.of(fallback);
-            }
-            explanationBuilder.noCandidates(requested, fallback);
-            return ImmutableList.of();
-        }
-
-        if (candidates.size() == 1) {
-            T candidate = candidates.iterator().next();
-            if (isMatching((AttributeContainerInternal) candidate.getAttributes(), requested)) {
-                explanationBuilder.singleMatch(candidate, candidates, requested);
-                return Collections.singletonList(candidate);
-            }
-            explanationBuilder.candidateDoesNotMatchAttributes(candidate, requested);
-            return ImmutableList.of();
-        }
-
-        ImmutableAttributes requestedAttributes = requested.asImmutable();
-        List<E> candidateList = (candidates instanceof List) ? (List<E>) candidates : ImmutableList.copyOf(candidates);
-
-        // Often times, collections of candidates will themselves differ even though their attributes are the same.
-        // Disambiguating two different candidate lists which map to the same attribute lists in reality performs
-        // the same work, so instead we cache disambiguation results based on the attributes being disambiguated.
-        // The result of this is a list of indices into the original candidate list from which the
-        // attributes-to-disambiguate are derived. When retrieving a result from the cache, we use the resulting
-        // indices to index back into the original candidates list.
-        CachedQuery query = CachedQuery.from(requestedAttributes, candidateList);
-        int[] indices = cachedQueries.compute(query, (key, value) -> {
-            if (value == null || !explanationBuilder.canSkipExplanation()) {
-                int[] matches = new MultipleCandidateMatcher<T>(schema, candidateList, requestedAttributes, explanationBuilder).getMatches();
-                LOGGER.debug("Selected matches {} from candidates {} for {}", Ints.asList(matches), candidateList, requested);
-                return matches;
-            }
-            return value;
-        });
-
-        return CachedQuery.getMatchesFromCandidateIndices(indices, candidateList);
-    }
-
-    private static class CachedQuery {
-        private final ImmutableAttributes requestedAttributes;
-        private final ImmutableAttributes[] candidates;
-        private final int hashCode;
-
-        private CachedQuery(ImmutableAttributes requestedAttributes, ImmutableAttributes[] candidates) {
-            this.requestedAttributes = requestedAttributes;
-            this.candidates = candidates;
-            this.hashCode = computeHashCode(requestedAttributes, candidates);
-        }
-
-        private int computeHashCode(ImmutableAttributes requestedAttributes, ImmutableAttributes[] candidates) {
-            int hash = requestedAttributes.hashCode();
-            for (ImmutableAttributes candidate : candidates) {
-                hash = 31 * hash + candidate.hashCode();
-            }
-            return hash;
-        }
-
-        public static <T extends HasAttributes> CachedQuery from(ImmutableAttributes requestedAttributes, List<T> candidates) {
-            ImmutableAttributes[] attributes = new ImmutableAttributes[candidates.size()];
-            for (int i = 0; i < candidates.size(); i++) {
-                attributes[i] = ((AttributeContainerInternal) candidates.get(i).getAttributes()).asImmutable();
-            }
-            return new CachedQuery(requestedAttributes, attributes);
-        }
-
-        public static <T extends HasAttributes> List<T> getMatchesFromCandidateIndices(int[] indices, List<? extends T> candidates) {
-            if (indices.length == 0) {
-                return Collections.emptyList();
-            }
-
-            List<T> matches = new ArrayList<>(indices.length);
-            for (int index : indices) {
-                matches.add(candidates.get(index));
-            }
-
-            return matches;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            CachedQuery that = (CachedQuery) o;
-            return hashCode == that.hashCode &&
-                requestedAttributes.equals(that.requestedAttributes) &&
-                Arrays.equals(candidates, that.candidates);
-        }
-
-        @Override
-        public int hashCode() {
-            return hashCode;
-        }
-
-        @Override
-        public String toString() {
-            return "CachedQuery{" +
-                "requestedAttributes=" + requestedAttributes +
-                ", candidates=" + Arrays.toString(candidates) +
-                '}';
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentGraphResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentGraphResolveMetadata.java
index 799c03f..73dda26 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentGraphResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentGraphResolveMetadata.java
@@ -16,8 +16,6 @@
 
 package org.gradle.internal.component.model;
 
-import com.google.common.base.Optional;
-import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
@@ -25,6 +23,7 @@
 
 import javax.annotation.Nullable;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -45,7 +44,6 @@ public interface ComponentGraphResolveMetadata {
 
     ModuleVersionIdentifier getModuleVersionId();
 
-    @Nullable
     AttributesSchemaInternal getAttributesSchema();
 
     boolean isChanging();
@@ -60,6 +58,9 @@ public interface ComponentGraphResolveMetadata {
     @Nullable
     ConfigurationGraphResolveMetadata getConfiguration(String name);
 
+    /**
+     * Returns the platforms that this component belongs to.
+     */
     List<? extends VirtualComponentIdentifier> getPlatformOwners();
 
     @Nullable
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentGraphResolveState.java
index afa78c9..de2254c 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentGraphResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentGraphResolveState.java
@@ -19,43 +19,69 @@
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
+import org.gradle.api.artifacts.result.ResolvedVariantResult;
 
 import javax.annotation.Nullable;
+import java.util.List;
 
 /**
- * State for a component instance (eg version of a component) that is used to perform dependency graph resolution.
+ * State for a component instance (e.g. version of a component) that is used to perform dependency graph resolution.
  *
  * <p>Resolution happens in multiple steps. The first step is to calculate the dependency graph, which involves selecting component instances and one or more variants of each instance.
  * This type exposes only the information and operations required to do this. In particular, it does not expose any information about artifacts unless this is actually required for graph resolution,
- * which is only required in certain specific cases (and something we should deprecate).</p>
+ * which only happens in certain specific cases (and something we should deprecate).</p>
  *
- * <p>The subsequent resolution steps, to select artifacts, are performed using the instance returned by {@link #prepareForArtifactResolution()}.</p>
+ * <p>The subsequent resolution steps to select artifacts, are performed using the instance returned by {@link #prepareForArtifactResolution()}.</p>
  *
  * <p>This interface says nothing about thread safety, however some subtypes may be required to be thread safe.</p>
  */
 public interface ComponentGraphResolveState {
+    /**
+     * A unique id for this component within the current build tree. Note that this id is not stable across Gradle invocations.
+     */
+    long getInstanceId();
+
+    /**
+     * The component identifier for this component. This identifier is stable but may not be unique.
+     */
     ComponentIdentifier getId();
 
-    @Nullable
+    /**
+     * Information about the origin of this component.
+     */
     ModuleSources getSources();
 
+    @Nullable
+    String getRepositoryId();
+
+    /**
+     * The immutable metadata for this component.
+     */
     ComponentGraphResolveMetadata getMetadata();
 
     /**
-     * When this component is a lenient platform, create a copy with the given ids. Otherwise returns {@code null}.
+     * Returns the public view of all variants of this component that are available for variant selection, either during graph resolution or artifact resolution.
+     */
+    List<ResolvedVariantResult> getAllSelectableVariantResults();
+
+    /**
+     * Returns the candidates for variant selection during graph resolution.
+     */
+    GraphSelectionCandidates getCandidatesForGraphVariantSelection();
+
+    /**
+     * Returns the configuration with the given name. A component does not necessarily define any configurations.
+     */
+    @Nullable
+    ConfigurationGraphResolveState getConfiguration(String configurationName);
+
+    /**
+     * When this component is a lenient platform, create a copy with the given ids. Otherwise, returns {@code null}.
      */
     @Nullable
     ComponentGraphResolveState maybeAsLenientPlatform(ModuleComponentIdentifier componentIdentifier, ModuleVersionIdentifier moduleVersionIdentifier);
 
     /**
-     * Determines the set of artifacts for the given variant of this component.
-     *
-     * <p>Note that this may be expensive, for example it may block waiting for access to the source project or for network or IO requests to the source repository, and should be used only when
-     * required.
-     */
-    VariantArtifactGraphResolveMetadata resolveArtifactsFor(VariantGraphResolveMetadata variant);
-
-    /**
      * Creates the state that can be used for artifact resolution for this component instance.
      *
      * <p>Note that this may be expensive, and should be used only when required.</p>
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentIdGenerator.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentIdGenerator.java
new file mode 100644
index 0000000..84a396b
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentIdGenerator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 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.component.model;
+
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A generator of ids for various model and result objects.
+ */
+@ServiceScope(Scopes.BuildTree.class)
+public class ComponentIdGenerator {
+    private final AtomicLong nextId = new AtomicLong();
+
+    public long nextComponentId() {
+        return nextId.getAndIncrement();
+    }
+
+    public long nextVariantId() {
+        return nextId.getAndIncrement();
+    }
+
+    public long nextGraphNodeId() {
+        return nextId.getAndIncrement();
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentResolveMetadata.java
index 3f55f37..81ce7dd 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ComponentResolveMetadata.java
@@ -61,18 +61,11 @@ public interface ComponentResolveMetadata extends HasAttributes {
     /**
      * @return the sources information for this component.
      */
-    @Nullable
     ModuleSources getSources();
 
     /**
-     * Creates a copy of this meta-data with the given sources.
-     */
-    ComponentResolveMetadata withSources(ModuleSources sources);
-
-    /**
      * Returns the schema used by this component.
      */
-    @Nullable
     AttributesSchemaInternal getAttributesSchema();
 
     /**
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationGraphResolveMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationGraphResolveMetadata.java
index 17e6121..8e5210a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationGraphResolveMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationGraphResolveMetadata.java
@@ -16,9 +16,6 @@
 
 package org.gradle.internal.component.model;
 
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
-
-import javax.annotation.Nullable;
 import java.util.Set;
 
 /**
@@ -31,6 +28,7 @@ public interface ConfigurationGraphResolveMetadata extends VariantGraphResolveMe
 
     boolean isVisible();
 
-    @Nullable
-    DeprecationMessageBuilder.WithDocumentation getConsumptionDeprecation();
+    default boolean isDeprecatedForConsumption() {
+        return false;
+    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationGraphResolveState.java
new file mode 100644
index 0000000..d9699ad
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationGraphResolveState.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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.component.model;
+
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+
+/**
+ * The state for a configuration used for graph resolution.
+ */
+public interface ConfigurationGraphResolveState {
+    String getName();
+
+    ImmutableAttributes getAttributes();
+
+    /**
+     * Returns a view of this configuration that can be used as a variant.
+     */
+    VariantGraphResolveState asVariant();
+
+    ConfigurationGraphResolveMetadata getMetadata();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationMetadata.java
index 67013c0..3511b87 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/ConfigurationMetadata.java
@@ -23,9 +23,7 @@
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.DisplayName;
 import org.gradle.internal.component.external.model.maven.MavenDependencyDescriptor;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
 
-import javax.annotation.Nullable;
 import java.util.List;
 import java.util.Set;
 
@@ -89,9 +87,6 @@ public interface ConfigurationMetadata extends VariantArtifactGraphResolveMetada
 
     boolean isCanBeConsumed();
 
-    @Nullable
-    DeprecationMessageBuilder.WithDocumentation getConsumptionDeprecation();
-
     boolean isCanBeResolved();
 
     /**
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultAttributeMatcher.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultAttributeMatcher.java
new file mode 100644
index 0000000..86d9df5
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultAttributeMatcher.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2016 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.component.model;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.primitives.Ints;
+import org.gradle.api.attributes.Attribute;
+import org.gradle.api.attributes.HasAttributes;
+import org.gradle.api.internal.attributes.AttributeContainerInternal;
+import org.gradle.api.internal.attributes.AttributeValue;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.internal.Cast;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * An {@link AttributeMatcher}, which optimizes for the case of only comparing 0 or 1 candidates
+ * and delegates to {@link MultipleCandidateMatcher} for all other cases.
+ */
+public class DefaultAttributeMatcher implements AttributeMatcher {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAttributeMatcher.class);
+
+    private final AttributeSelectionSchema schema;
+
+    /**
+     * Attribute matching can be very expensive. In case there are multiple candidates, we
+     * cache the result of the query, because it's often the case that we ask for the same
+     * disambiguation of attributes several times in a row (but with different candidates).
+     */
+    private final ConcurrentMap<CachedQuery, int[]> cachedQueries = new ConcurrentHashMap<>();
+
+    public DefaultAttributeMatcher(AttributeSelectionSchema schema) {
+        this.schema = schema;
+    }
+
+    @Override
+    public AttributeSelectionSchema getSelectionSchema() {
+        return schema;
+    }
+
+    @Override
+    public boolean isMatching(AttributeContainerInternal candidate, AttributeContainerInternal requested) {
+        if (requested.isEmpty() || candidate.isEmpty()) {
+            return true;
+        }
+
+        ImmutableAttributes requestedAttributes = requested.asImmutable();
+        ImmutableAttributes candidateAttributes = candidate.asImmutable();
+
+        for (Attribute<?> attribute : requestedAttributes.keySet()) {
+            AttributeValue<?> requestedValue = requestedAttributes.findEntry(attribute);
+            AttributeValue<?> candidateValue = candidateAttributes.findEntry(attribute.getName());
+            if (candidateValue.isPresent()) {
+                Object coercedValue = candidateValue.coerce(attribute);
+                boolean match = schema.matchValue(attribute, requestedValue.get(), coercedValue);
+                if (!match) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public <T> boolean isMatching(Attribute<T> attribute, T candidate, T requested) {
+        return schema.matchValue(attribute, requested, candidate);
+    }
+
+    @Override
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public List<AttributeMatcher.MatchingDescription<?>> describeMatching(AttributeContainerInternal candidate, AttributeContainerInternal requested) {
+        if (requested.isEmpty() || candidate.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        ImmutableAttributes requestedAttributes = requested.asImmutable();
+        ImmutableAttributes candidateAttributes = candidate.asImmutable();
+
+        ImmutableSet<Attribute<?>> attributes = requestedAttributes.keySet();
+        List<AttributeMatcher.MatchingDescription<?>> result = new ArrayList<>(attributes.size());
+        for (Attribute<?> attribute : attributes) {
+            AttributeValue<?> requestedValue = requestedAttributes.findEntry(attribute);
+            AttributeValue<?> candidateValue = candidateAttributes.findEntry(attribute.getName());
+            if (candidateValue.isPresent()) {
+                Object coercedValue = candidateValue.coerce(attribute);
+                boolean match = schema.matchValue(attribute, requestedValue.get(), coercedValue);
+                result.add(new AttributeMatcher.MatchingDescription(attribute, requestedValue, candidateValue, match));
+            } else {
+                result.add(new AttributeMatcher.MatchingDescription(attribute, requestedValue, candidateValue, false));
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public <T extends HasAttributes> List<T> matches(Collection<? extends T> candidates, AttributeContainerInternal requested, AttributeMatchingExplanationBuilder explanationBuilder) {
+        if (candidates.size() == 0) {
+            explanationBuilder.noCandidates(requested);
+            return ImmutableList.of();
+        }
+
+        if (candidates.size() == 1) {
+            T candidate = candidates.iterator().next();
+            if (isMatching((AttributeContainerInternal) candidate.getAttributes(), requested)) {
+                explanationBuilder.singleMatch(candidate, candidates, requested);
+                return Collections.singletonList(candidate);
+            }
+            explanationBuilder.candidateDoesNotMatchAttributes(candidate, requested);
+            return ImmutableList.of();
+        }
+
+        ImmutableAttributes requestedAttributes = requested.asImmutable();
+        List<T> candidateList = (candidates instanceof List) ? Cast.uncheckedCast(candidates) : ImmutableList.copyOf(candidates);
+
+        // Often times, collections of candidates will themselves differ even though their attributes are the same.
+        // Disambiguating two different candidate lists which map to the same attribute lists in reality performs
+        // the same work, so instead we cache disambiguation results based on the attributes being disambiguated.
+        // The result of this is a list of indices into the original candidate list from which the
+        // attributes-to-disambiguate are derived. When retrieving a result from the cache, we use the resulting
+        // indices to index back into the original candidates list.
+        CachedQuery query = CachedQuery.from(requestedAttributes, candidateList);
+        int[] indices = cachedQueries.compute(query, (key, value) -> {
+            if (value == null || !explanationBuilder.canSkipExplanation()) {
+                int[] matches = new MultipleCandidateMatcher<>(schema, candidateList, requestedAttributes, explanationBuilder).getMatches();
+                LOGGER.debug("Selected matches {} from candidates {} for {}", Ints.asList(matches), candidateList, requested);
+                return matches;
+            }
+            return value;
+        });
+
+        return CachedQuery.getMatchesFromCandidateIndices(indices, candidateList);
+    }
+
+    private static class CachedQuery {
+        private final ImmutableAttributes requestedAttributes;
+        private final ImmutableAttributes[] candidates;
+        private final int hashCode;
+
+        private CachedQuery(ImmutableAttributes requestedAttributes, ImmutableAttributes[] candidates) {
+            this.requestedAttributes = requestedAttributes;
+            this.candidates = candidates;
+            this.hashCode = computeHashCode(requestedAttributes, candidates);
+        }
+
+        private static int computeHashCode(ImmutableAttributes requestedAttributes, ImmutableAttributes[] candidates) {
+            int hash = requestedAttributes.hashCode();
+            for (ImmutableAttributes candidate : candidates) {
+                hash = 31 * hash + candidate.hashCode();
+            }
+            return hash;
+        }
+
+        public static <T extends HasAttributes> CachedQuery from(ImmutableAttributes requestedAttributes, List<T> candidates) {
+            ImmutableAttributes[] attributes = new ImmutableAttributes[candidates.size()];
+            for (int i = 0; i < candidates.size(); i++) {
+                attributes[i] = ((AttributeContainerInternal) candidates.get(i).getAttributes()).asImmutable();
+            }
+            return new CachedQuery(requestedAttributes, attributes);
+        }
+
+        public static <T extends HasAttributes> List<T> getMatchesFromCandidateIndices(int[] indices, List<? extends T> candidates) {
+            if (indices.length == 0) {
+                return Collections.emptyList();
+            }
+
+            List<T> matches = new ArrayList<>(indices.length);
+            for (int index : indices) {
+                matches.add(candidates.get(index));
+            }
+
+            return matches;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            CachedQuery that = (CachedQuery) o;
+            return hashCode == that.hashCode &&
+                requestedAttributes.equals(that.requestedAttributes) &&
+                Arrays.equals(candidates, that.candidates);
+        }
+
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+
+        @Override
+        public String toString() {
+            return "CachedQuery{" +
+                "requestedAttributes=" + requestedAttributes +
+                ", candidates=" + Arrays.toString(candidates) +
+                '}';
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentGraphResolveState.java
index 0058ae7..3e2f411 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentGraphResolveState.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DefaultComponentGraphResolveState.java
@@ -16,68 +16,247 @@
 
 package org.gradle.internal.component.model;
 
-import com.google.common.base.Optional;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.artifacts.component.ComponentIdentifier;
+import org.gradle.api.artifacts.result.ResolvedVariantResult;
+import org.gradle.api.capabilities.CapabilitiesMetadata;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec;
+import org.gradle.api.internal.artifacts.result.DefaultResolvedVariantResult;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
+import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
+import org.gradle.internal.Describables;
+import org.gradle.internal.component.external.model.ExternalComponentResolveMetadata;
+import org.gradle.internal.lazy.Lazy;
 import org.gradle.internal.resolve.resolver.ArtifactSelector;
 
+import javax.annotation.Nullable;
+import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.stream.Collectors;
 
-public class DefaultComponentGraphResolveState<T extends ComponentGraphResolveMetadata, S extends ComponentResolveMetadata> extends AbstractComponentGraphResolveState<T, S> {
-    public DefaultComponentGraphResolveState(T graphMetadata, S artifactMetadata) {
-        super(graphMetadata, artifactMetadata);
-    }
+/**
+ * Holds the resolution state for an external component.
+ */
+public class DefaultComponentGraphResolveState<T extends ComponentGraphResolveMetadata, S extends ExternalComponentResolveMetadata> extends AbstractComponentGraphResolveState<T, S> {
+    private final ComponentIdGenerator idGenerator;
 
-    public static ComponentGraphResolveState of(ModuleComponentResolveMetadata metadata) {
-        return new DefaultComponentGraphResolveState<>(metadata, metadata);
+    // The resolve state for each configuration of this component
+    private final ConcurrentMap<ModuleConfigurationMetadata, DefaultConfigurationGraphResolveState> variants = new ConcurrentHashMap<>();
+
+    // The variants to use for variant selection during graph resolution
+    private final Lazy<Optional<List<? extends VariantGraphResolveState>>> allVariantsForGraphResolution;
+
+    // The variants of this component to use when variant reselection is enabled
+    private final Optional<Set<? extends VariantResolveMetadata>> allVariantsForArtifactSelection;
+
+    // The public view of all selectable variants of this component
+    private final List<ResolvedVariantResult> selectableVariantResults;
+
+    public DefaultComponentGraphResolveState(long instanceId, T graphMetadata, S artifactMetadata, AttributeDesugaring attributeDesugaring, ComponentIdGenerator idGenerator) {
+        super(instanceId, graphMetadata, artifactMetadata, attributeDesugaring);
+        allVariantsForGraphResolution = Lazy.locking().of(() -> graphMetadata.getVariantsForGraphTraversal().map(variants ->
+            variants.stream()
+                .map(ModuleConfigurationMetadata.class::cast)
+                .map(variant -> resolveStateFor(variant).asVariant())
+                .collect(Collectors.toList())));
+        allVariantsForArtifactSelection = graphMetadata.getVariantsForGraphTraversal().map(variants ->
+            variants.stream()
+                .map(ModuleConfigurationMetadata.class::cast)
+                .flatMap(variant -> variant.getVariants().stream())
+                .collect(Collectors.toSet()));
+        this.idGenerator = idGenerator;
+        selectableVariantResults = graphMetadata.getVariantsForGraphTraversal().orElse(Collections.emptyList()).stream().
+            flatMap(variant -> variant.getVariants().stream()).
+            map(variant -> new DefaultResolvedVariantResult(
+                getId(),
+                Describables.of(variant.getName()),
+                attributeDesugaring.desugar(variant.getAttributes().asImmutable()),
+                capabilitiesFor(variant.getCapabilities()),
+                null
+            )).
+            collect(Collectors.toList());
     }
 
     @Override
-    public VariantArtifactGraphResolveMetadata resolveArtifactsFor(VariantGraphResolveMetadata variant) {
-        return (VariantArtifactGraphResolveMetadata) variant;
+    public ComponentArtifactResolveMetadata getResolveMetadata() {
+        return new ExternalArtifactResolveMetadata(getArtifactMetadata());
     }
 
     @Override
-    public VariantArtifactResolveState prepareForArtifactResolution(VariantGraphResolveMetadata variant) {
-        ConfigurationMetadata configurationMetadata = (ConfigurationMetadata) variant;
-        return new DefaultVariantArtifactResolveState(getMetadata(), getArtifactMetadata(), configurationMetadata);
+    public ModuleSources getSources() {
+        return getArtifactMetadata().getSources();
     }
 
-    private static class DefaultVariantArtifactResolveState implements VariantArtifactResolveState {
-        private final ComponentGraphResolveMetadata graphMetadata;
-        private final ComponentResolveMetadata artifactMetadata;
-        private final ConfigurationMetadata graphSelectedVariant;
+    @Override
+    public List<ResolvedVariantResult> getAllSelectableVariantResults() {
+        return selectableVariantResults;
+    }
 
-        public DefaultVariantArtifactResolveState(ComponentGraphResolveMetadata graphMetadata, ComponentResolveMetadata artifactMetadata, ConfigurationMetadata graphSelectedVariant) {
-            this.graphMetadata = graphMetadata;
+    @Override
+    protected Optional<List<? extends VariantGraphResolveState>> getVariantsForGraphTraversal() {
+        return allVariantsForGraphResolution.get();
+    }
+
+    @Nullable
+    @Override
+    public ConfigurationGraphResolveState getConfiguration(String configurationName) {
+        ModuleConfigurationMetadata configuration = (ModuleConfigurationMetadata) getMetadata().getConfiguration(configurationName);
+        if (configuration == null) {
+            return null;
+        } else {
+            return resolveStateFor(configuration);
+        }
+    }
+
+    private DefaultConfigurationGraphResolveState resolveStateFor(ModuleConfigurationMetadata configuration) {
+        return variants.computeIfAbsent(configuration, c -> newVariantState(configuration));
+    }
+
+    protected VariantGraphResolveState newResolveStateFor(ModuleConfigurationMetadata configuration) {
+        return newVariantState(configuration);
+    }
+
+    private DefaultConfigurationGraphResolveState newVariantState(ModuleConfigurationMetadata configuration) {
+        return new DefaultConfigurationGraphResolveState(idGenerator.nextVariantId(), getArtifactMetadata(), configuration, allVariantsForArtifactSelection);
+    }
+
+    private class DefaultConfigurationGraphResolveState extends AbstractVariantGraphResolveState implements VariantGraphResolveState, ConfigurationGraphResolveState {
+        private final long instanceId;
+        private final ModuleConfigurationMetadata configuration;
+        private final Lazy<DefaultConfigurationArtifactResolveState> artifactResolveState;
+
+        public DefaultConfigurationGraphResolveState(long instanceId, ExternalComponentResolveMetadata component, ModuleConfigurationMetadata configuration, Optional<Set<? extends VariantResolveMetadata>> allVariantsForArtifactSelection) {
+            this.instanceId = instanceId;
+            this.configuration = configuration;
+            this.artifactResolveState = Lazy.locking().of(() -> new DefaultConfigurationArtifactResolveState(component, configuration, allVariantsForArtifactSelection));
+        }
+
+        @Override
+        public long getInstanceId() {
+            return instanceId;
+        }
+
+        @Override
+        public String getName() {
+            return configuration.getName();
+        }
+
+        @Override
+        public ImmutableAttributes getAttributes() {
+            return configuration.getAttributes();
+        }
+
+        @Override
+        public CapabilitiesMetadata getCapabilities() {
+            return configuration.getCapabilities();
+        }
+
+        @Override
+        public ConfigurationGraphResolveMetadata getMetadata() {
+            return configuration;
+        }
+
+        @Override
+        public VariantGraphResolveState asVariant() {
+            return this;
+        }
+
+        @Override
+        public VariantArtifactGraphResolveMetadata resolveArtifacts() {
+            return configuration;
+        }
+
+        @Override
+        public VariantArtifactResolveState prepareForArtifactResolution() {
+            return artifactResolveState.get();
+        }
+    }
+
+    private static class DefaultConfigurationArtifactResolveState implements VariantArtifactResolveState {
+        private final ExternalComponentResolveMetadata artifactMetadata;
+        private final ConfigurationMetadata graphSelectedConfiguration;
+        private final Set<? extends VariantResolveMetadata> legacyVariants;
+        private final Set<? extends VariantResolveMetadata> allVariants;
+
+        public DefaultConfigurationArtifactResolveState(ExternalComponentResolveMetadata artifactMetadata, ConfigurationMetadata graphSelectedConfiguration, Optional<Set<? extends VariantResolveMetadata>> allVariantsForArtifactSelection) {
             this.artifactMetadata = artifactMetadata;
-            this.graphSelectedVariant = graphSelectedVariant;
+            this.graphSelectedConfiguration = graphSelectedConfiguration;
+            this.legacyVariants = graphSelectedConfiguration.getVariants();
+            allVariants = allVariantsForArtifactSelection.orElse(legacyVariants);
         }
 
         @Override
         public ComponentArtifactMetadata resolveArtifact(IvyArtifactName artifact) {
-            return graphSelectedVariant.artifact(artifact);
+            return graphSelectedConfiguration.artifact(artifact);
         }
 
         @Override
         public ArtifactSet resolveArtifacts(ArtifactSelector artifactSelector, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) {
-            Set<? extends VariantResolveMetadata> fallbackVariants = graphSelectedVariant.getVariants();
-            Optional<List<? extends VariantGraphResolveMetadata>> variantsForGraphTraversal = graphMetadata.getVariantsForGraphTraversal();
-            return artifactSelector.resolveArtifacts(artifactMetadata, () -> buildAllVariants(fallbackVariants, variantsForGraphTraversal), fallbackVariants, exclusions, overriddenAttributes);
+            return artifactSelector.resolveArtifacts(new ExternalArtifactResolveMetadata(artifactMetadata), new ExternalVariantArtifactSelectionCandidates(allVariants, legacyVariants), exclusions, overriddenAttributes);
+        }
+    }
+
+    private static class ExternalVariantArtifactSelectionCandidates implements VariantArtifactSelectionCandidates {
+        private final Set<? extends VariantResolveMetadata> allVariants;
+        private final Set<? extends VariantResolveMetadata> legacyVariants;
+
+        public ExternalVariantArtifactSelectionCandidates(Set<? extends VariantResolveMetadata> allVariants, Set<? extends VariantResolveMetadata> legacyVariants) {
+            this.allVariants = allVariants;
+            this.legacyVariants = legacyVariants;
         }
 
-        private static Set<? extends VariantResolveMetadata> buildAllVariants(Set<? extends VariantResolveMetadata> fallbackVariants, Optional<List<? extends VariantGraphResolveMetadata>> variantsForGraphTraversal) {
-            final Set<? extends VariantResolveMetadata> allVariants;
-            if (variantsForGraphTraversal.isPresent()) {
-                allVariants = variantsForGraphTraversal.get().stream().map(ModuleConfigurationMetadata.class::cast).flatMap(variant -> variant.getVariants().stream()).collect(Collectors.toSet());
-            } else {
-                allVariants = fallbackVariants;
-            }
+        @Override
+        public Set<? extends VariantResolveMetadata> getAllVariants() {
             return allVariants;
         }
+
+        @Override
+        public Set<? extends VariantResolveMetadata> getLegacyVariants() {
+            return legacyVariants;
+        }
+    }
+
+    private static class ExternalArtifactResolveMetadata implements ComponentArtifactResolveMetadata {
+        private final ExternalComponentResolveMetadata metadata;
+
+        public ExternalArtifactResolveMetadata(ExternalComponentResolveMetadata metadata) {
+            this.metadata = metadata;
+        }
+
+        @Override
+        public ComponentIdentifier getId() {
+            return metadata.getId();
+        }
+
+        @Override
+        public ModuleVersionIdentifier getModuleVersionId() {
+            return metadata.getModuleVersionId();
+        }
+
+        @Override
+        public ModuleSources getSources() {
+            return metadata.getSources();
+        }
+
+        @Override
+        public ImmutableAttributes getAttributes() {
+            return metadata.getAttributes();
+        }
+
+        @Override
+        public AttributesSchemaInternal getAttributesSchema() {
+            return metadata.getAttributesSchema();
+        }
+
+        @Override
+        public ComponentResolveMetadata getMetadata() {
+            return metadata;
+        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DelegatingDependencyMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DelegatingDependencyMetadata.java
new file mode 100644
index 0000000..5d53ebf
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/DelegatingDependencyMetadata.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2023 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.component.model;
+
+import org.gradle.api.artifacts.component.ComponentSelector;
+import org.gradle.api.capabilities.Capability;
+import org.gradle.api.internal.attributes.AttributesSchemaInternal;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * A {@link DependencyMetadata} implementation which delegates all method calls to a provided {@code delegate}.
+ */
+public abstract class DelegatingDependencyMetadata implements DependencyMetadata {
+
+    private final DependencyMetadata delegate;
+
+    public DelegatingDependencyMetadata(DependencyMetadata delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public ComponentSelector getSelector() {
+        return delegate.getSelector();
+    }
+
+    @Override
+    public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
+        return delegate.selectVariants(consumerAttributes, targetComponentState, consumerSchema, explicitRequestedCapabilities);
+    }
+
+    @Override
+    public List<ExcludeMetadata> getExcludes() {
+        return delegate.getExcludes();
+    }
+
+    @Override
+    public List<IvyArtifactName> getArtifacts() {
+        return delegate.getArtifacts();
+    }
+
+    @Override
+    public DependencyMetadata withTarget(ComponentSelector target) {
+        return delegate.withTarget(target);
+    }
+
+    @Override
+    public DependencyMetadata withTargetAndArtifacts(ComponentSelector target, List<IvyArtifactName> artifacts) {
+        return delegate.withTargetAndArtifacts(target, artifacts);
+    }
+
+    @Override
+    public boolean isChanging() {
+        return delegate.isChanging();
+    }
+
+    @Override
+    public boolean isTransitive() {
+        return delegate.isTransitive();
+    }
+
+    @Override
+    public boolean isConstraint() {
+        return delegate.isConstraint();
+    }
+
+    @Override
+    public boolean isEndorsingStrictVersions() {
+        return delegate.isEndorsingStrictVersions();
+    }
+
+    @Nullable
+    @Override
+    public String getReason() {
+        return delegate.getReason();
+    }
+
+    @Override
+    public DependencyMetadata withReason(String reason) {
+        return delegate.withReason(reason);
+    }
+
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/GraphSelectionCandidates.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/GraphSelectionCandidates.java
new file mode 100644
index 0000000..dd8e292
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/GraphSelectionCandidates.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 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.component.model;
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public interface GraphSelectionCandidates {
+    /**
+     * Should variant selection be used?
+     * @return true when variant selection should be used, false when the legacy variant should be used.
+     */
+    boolean isUseVariants();
+
+    /**
+     * Returns the set of variants to select from. Should only be called when {@link #isUseVariants()} returns true.
+     */
+    List<? extends VariantGraphResolveState> getVariants();
+
+    /**
+     * Returns the configuration to use when variant selection is not being used.
+     */
+    @Nullable
+    ConfigurationGraphResolveState getLegacyConfiguration();
+
+    /**
+     * The set of consumable configurations available. Used for diagnostics.
+     */
+    List<? extends ConfigurationGraphResolveMetadata> getCandidateConfigurations();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LocalComponentDependencyMetadata.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LocalComponentDependencyMetadata.java
index 855d1e3..0049e76 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LocalComponentDependencyMetadata.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LocalComponentDependencyMetadata.java
@@ -17,7 +17,6 @@
 package org.gradle.internal.component.model;
 
 import com.google.common.base.Objects;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.artifacts.component.ComponentIdentifier;
@@ -28,7 +27,7 @@
 import org.gradle.api.internal.attributes.AttributesSchemaInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.component.IncompatibleConfigurationSelectionException;
-import org.gradle.internal.deprecation.DeprecationMessageBuilder;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.exceptions.ConfigurationNotConsumableException;
 import org.gradle.util.internal.GUtil;
 
@@ -142,15 +141,15 @@ private static String getOrDefaultConfiguration(String configuration) {
     @Override
     public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttributes, ComponentGraphResolveState targetComponentState, AttributesSchemaInternal consumerSchema, Collection<? extends Capability> explicitRequestedCapabilities) {
         ComponentGraphResolveMetadata targetComponent = targetComponentState.getMetadata();
+        GraphSelectionCandidates candidates = targetComponentState.getCandidatesForGraphVariantSelection();
         boolean consumerHasAttributes = !consumerAttributes.isEmpty();
-        Optional<List<? extends VariantGraphResolveMetadata>> targetVariants = targetComponent.getVariantsForGraphTraversal();
-        boolean useConfigurationAttributes = dependencyConfiguration == null && (consumerHasAttributes || targetVariants.isPresent());
+        boolean useConfigurationAttributes = dependencyConfiguration == null && (consumerHasAttributes || candidates.isUseVariants());
         if (useConfigurationAttributes) {
             return AttributeConfigurationSelector.selectVariantsUsingAttributeMatching(consumerAttributes, explicitRequestedCapabilities, targetComponentState, consumerSchema, getArtifacts());
         }
 
         String targetConfiguration = GUtil.elvis(dependencyConfiguration, Dependency.DEFAULT_CONFIGURATION);
-        ConfigurationGraphResolveMetadata toConfiguration = targetComponent.getConfiguration(targetConfiguration);
+        ConfigurationGraphResolveState toConfiguration = targetComponentState.getConfiguration(targetConfiguration);
         if (toConfiguration == null) {
             throw new ConfigurationNotFoundException(componentId, moduleConfiguration, targetConfiguration, targetComponent.getId());
         }
@@ -160,19 +159,24 @@ public VariantSelectionResult selectVariants(ImmutableAttributes consumerAttribu
             // Note that this validation only occurs when `dependencyConfiguration != null` (otherwise we would select with attribute matching)
             AttributesSchemaInternal producerAttributeSchema = targetComponent.getAttributesSchema();
             if (!consumerSchema.withProducer(producerAttributeSchema).isMatching(toConfiguration.getAttributes(), consumerAttributes)) {
-                throw new IncompatibleConfigurationSelectionException(consumerAttributes, consumerSchema.withProducer(producerAttributeSchema), targetComponent, targetConfiguration, targetVariants.isPresent(), DescriberSelector.selectDescriber(consumerAttributes, consumerSchema));
+                throw new IncompatibleConfigurationSelectionException(consumerAttributes, consumerSchema.withProducer(producerAttributeSchema), targetComponent, toConfiguration, false, DescriberSelector.selectDescriber(consumerAttributes, consumerSchema));
             }
         }
-        return new VariantSelectionResult(ImmutableList.of(toConfiguration), false);
+        return new VariantSelectionResult(ImmutableList.of(toConfiguration.asVariant()), false);
     }
 
-    private void verifyConsumability(ComponentGraphResolveMetadata targetComponent, ConfigurationGraphResolveMetadata toConfiguration) {
-        if (!toConfiguration.isCanBeConsumed()) {
-            throw new ConfigurationNotConsumableException(targetComponent.toString(), toConfiguration.getName());
+    private void verifyConsumability(ComponentGraphResolveMetadata targetComponent, ConfigurationGraphResolveState toConfiguration) {
+        ConfigurationGraphResolveMetadata metadata = toConfiguration.getMetadata();
+        if (!metadata.isCanBeConsumed()) {
+            throw new ConfigurationNotConsumableException(targetComponent.getId().getDisplayName(), toConfiguration.getName());
         }
-        DeprecationMessageBuilder.WithDocumentation consumptionDeprecation = toConfiguration.getConsumptionDeprecation();
-        if (consumptionDeprecation != null) {
-            consumptionDeprecation.nagUser();
+
+        if (metadata.isDeprecatedForConsumption()) {
+            DeprecationLogger.deprecateConfiguration(toConfiguration.getName())
+                .forConsumption()
+                .willBecomeAnErrorInGradle9()
+                .withUserManual("dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+                .nagUser();
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LoggingAttributeMatchingExplanationBuilder.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LoggingAttributeMatchingExplanationBuilder.java
index bdbf7da..cfe4e8c 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LoggingAttributeMatchingExplanationBuilder.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/LoggingAttributeMatchingExplanationBuilder.java
@@ -41,13 +41,8 @@ public boolean canSkipExplanation() {
     }
 
     @Override
-    public <T extends HasAttributes> void selectedFallbackConfiguration(AttributeContainerInternal requested, T fallback) {
-        LOGGER.debug("No candidates for {}, selected matching fallback {}", requested, fallback);
-    }
-
-    @Override
-    public <T extends HasAttributes> void noCandidates(AttributeContainerInternal requested, T fallback) {
-        LOGGER.debug("No candidates for {} and fallback {} does not match. Select nothing.", requested, fallback);
+    public <T extends HasAttributes> void noCandidates(AttributeContainerInternal requested) {
+        LOGGER.debug("No candidates for {}. Select nothing.", requested);
     }
 
     @Override
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantArtifactSelectionCandidates.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantArtifactSelectionCandidates.java
new file mode 100644
index 0000000..acaa921
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantArtifactSelectionCandidates.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.component.model;
+
+import org.gradle.api.artifacts.ArtifactView;
+
+import java.util.Set;
+
+/**
+ * Metadata used to select the artifacts given a particular variant selected during graph resolution.
+ */
+public interface VariantArtifactSelectionCandidates {
+    /**
+     * The variants available for artifact selection when {@link ArtifactView.ViewConfiguration#withVariantReselection()} is enabled.
+     * Generally contains all variants of the target component.
+     */
+    Set<? extends VariantResolveMetadata> getAllVariants();
+
+    /**
+     * The variants available for artifact selection when variant reselection is not enabled.
+     * Generally contains the target variant, plus any 'sub-variants'.
+     */
+    Set<? extends VariantResolveMetadata> getLegacyVariants();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantGraphResolveState.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantGraphResolveState.java
new file mode 100644
index 0000000..b52d8fc
--- /dev/null
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantGraphResolveState.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.component.model;
+
+import org.gradle.api.artifacts.result.ResolvedVariantResult;
+import org.gradle.api.attributes.HasAttributes;
+import org.gradle.api.capabilities.CapabilitiesMetadata;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
+
+import javax.annotation.Nullable;
+
+/**
+ * State for a component variant that is used to perform dependency graph resolution.
+ *
+ * <p>This does not include any information about the artifacts of the variant, which are generally not required during graph resolution.</p>
+ */
+public interface VariantGraphResolveState extends HasAttributes {
+    /**
+     * A unique id for this variant within the current build tree. Note that this id is not stable across Gradle invocations.
+     */
+    long getInstanceId();
+
+    String getName();
+
+    ImmutableAttributes getAttributes();
+
+    CapabilitiesMetadata getCapabilities();
+
+    VariantGraphResolveMetadata getMetadata();
+
+    /**
+     * Returns the public view for this variant.
+     */
+    ResolvedVariantResult getVariantResult(@Nullable ResolvedVariantResult externalVariant);
+
+    /**
+     * Determines the set of artifacts for this variant, if required during graph resolution. Does not necessarily download the artifacts.
+     *
+     * <p>Note that this may be expensive, for example it may block waiting for access to the source project or for network or IO requests to the source repository, and should be used only when
+     * required.
+     */
+    VariantArtifactGraphResolveMetadata resolveArtifacts();
+
+    /**
+     * Returns the state required to select and resolve artifacts for this variant.
+     */
+    VariantArtifactResolveState prepareForArtifactResolution();
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantSelectionResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantSelectionResult.java
index d203a3e..eba9895 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantSelectionResult.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/component/model/VariantSelectionResult.java
@@ -19,15 +19,15 @@
 import java.util.List;
 
 public class VariantSelectionResult {
-    private final List<? extends VariantGraphResolveMetadata> variants;
+    private final List<? extends VariantGraphResolveState> variants;
     private final boolean selectedByVariantAwareResolution;
 
-    public VariantSelectionResult(List<? extends VariantGraphResolveMetadata> variants, boolean selectedByVariantAwareResolution) {
+    public VariantSelectionResult(List<? extends VariantGraphResolveState> variants, boolean selectedByVariantAwareResolution) {
         this.variants = variants;
         this.selectedByVariantAwareResolution = selectedByVariantAwareResolution;
     }
 
-    public List<? extends VariantGraphResolveMetadata> getVariants() {
+    public List<? extends VariantGraphResolveState> getVariants() {
         return variants;
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/locking/DefaultDependencyLockingProvider.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/locking/DefaultDependencyLockingProvider.java
index bda9ca1..fadb840 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/locking/DefaultDependencyLockingProvider.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/locking/DefaultDependencyLockingProvider.java
@@ -192,8 +192,8 @@ public void persistResolvedDependencies(String configurationName, Set<ModuleComp
         if (writeLocks) {
             List<String> modulesOrdered = getModulesOrdered(resolvedModules);
             if (!changingResolvedModules.isEmpty()) {
-                LOGGER.warn("Dependency lock state for configuration '{}' contains changing modules: {}. This means that dependencies content may still change over time. See {} for details.",
-                    context.identityPath(configurationName), getModulesOrdered(changingResolvedModules), DOC_REG.getDocumentationFor("dependency_locking"));
+                LOGGER.warn("Dependency lock state for configuration '{}' contains changing modules: {}. This means that dependencies content may still change over time. {}",
+                    context.identityPath(configurationName), getModulesOrdered(changingResolvedModules), DOC_REG.getDocumentationRecommendationFor("details", "dependency_locking"));
             }
             allLockState.put(configurationName, modulesOrdered);
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/locking/LockFileReaderWriter.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/locking/LockFileReaderWriter.java
index 068ea24..d6e730a 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/locking/LockFileReaderWriter.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/locking/LockFileReaderWriter.java
@@ -46,7 +46,7 @@
 public class LockFileReaderWriter {
 
     private static final Logger LOGGER = Logging.getLogger(LockFileReaderWriter.class);
-    private static final DocumentationRegistry DOC_REG = new DocumentationRegistry();
+    private static final String LIMITATIONS_DOC_LINK = " " + new DocumentationRegistry().getDocumentationRecommendationFor("information on limitations", "dependency_locking", "locking_limitations");
 
     static final String UNIQUE_LOCKFILE_NAME = "gradle.lockfile";
     static final String FILE_SUFFIX = ".lockfile";
@@ -111,14 +111,14 @@ private String decorate(String configurationName) {
     private void checkValidRoot() {
         if (lockFilesRoot == null) {
             throw new IllegalStateException("Dependency locking cannot be used for project '" + context.getProjectPath() + "'." +
-                " See limitations in the documentation (" + DOC_REG.getDocumentationFor("dependency_locking", "locking_limitations") +").");
+                LIMITATIONS_DOC_LINK);
         }
     }
 
     private void checkValidRoot(String configurationName) {
         if (lockFilesRoot == null) {
             throw new IllegalStateException("Dependency locking cannot be used for configuration '" + context.identityPath(configurationName) + "'." +
-                " See limitations in the documentation (" + DOC_REG.getDocumentationFor("dependency_locking", "locking_limitations") +").");
+                LIMITATIONS_DOC_LINK);
         }
     }
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionNotFoundException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionNotFoundException.java
index b06c5a3..1194928 100755
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionNotFoundException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionNotFoundException.java
@@ -15,35 +15,46 @@
  */
 package org.gradle.internal.resolve;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.component.ModuleComponentSelector;
 import org.gradle.internal.Factory;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.exceptions.ResolutionProvider;
 import org.gradle.internal.logging.text.TreeFormatter;
 
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 
-public class ModuleVersionNotFoundException extends ModuleVersionResolveException {
+public class ModuleVersionNotFoundException extends ModuleVersionResolveException implements ResolutionProvider {
+    private List<String> resolutions = ImmutableList.of();
+
     /**
      * This is used by {@link ModuleVersionResolveException#withIncomingPaths(java.util.Collection)}.
      */
     @SuppressWarnings("UnusedDeclaration")
-    public ModuleVersionNotFoundException(ComponentSelector selector, Factory<String> message) {
+    public ModuleVersionNotFoundException(ComponentSelector selector, Factory<String> message, Collection<String> resolutions) {
         super(selector, message);
+        this.resolutions = ImmutableList.copyOf(resolutions);
     }
 
+
     public ModuleVersionNotFoundException(ModuleComponentSelector selector, Collection<String> attemptedLocations, Collection<String> unmatchedVersions, Collection<RejectedVersion> rejectedVersions) {
         super(selector, format(selector, attemptedLocations, unmatchedVersions, rejectedVersions));
+        recordPossibleResolution(attemptedLocations);
     }
 
     public ModuleVersionNotFoundException(ModuleVersionIdentifier id, Collection<String> attemptedLocations) {
         super(id, format(id, attemptedLocations));
+        recordPossibleResolution(attemptedLocations);
     }
 
     public ModuleVersionNotFoundException(ModuleComponentSelector selector, Collection<String> attemptedLocations) {
         super(selector, format(selector, attemptedLocations));
+        recordPossibleResolution(attemptedLocations);
     }
 
     private static Factory<String> format(ModuleComponentSelector selector, Collection<String> locations, Collection<String> unmatchedVersions, Collection<RejectedVersion> rejectedVersions) {
@@ -128,29 +139,46 @@ private static void addLocations(TreeFormatter builder, Collection<String> locat
         }
         builder.node("Searched in the following locations");
         builder.startChildren();
-        for (String location : locations) {
-            builder.node(location);
-        }
+
+        locations.forEach(builder::node);
+
         builder.endChildren();
-        tryHintAboutArtifactSource(builder, locations);
     }
 
-    // This method should ideally use more data to figure out if the message should be displayed
-    // or not. In particular, the ivy patterns can make it difficult to find out if an Ivy artifact
-    // source should be configured. At this stage, this information is lost, so we do a best effort
-    // based on the file locations.
-    private static void tryHintAboutArtifactSource(TreeFormatter builder, Collection<String> locations) {
+    /**
+     * This method should ideally use more data to figure out if the message should be displayed
+     * or not. In particular, the ivy patterns can make it difficult to find out if an Ivy artifact
+     * source should be configured. At this stage, this information is lost, so we do a best effort
+     * based on the file locations.
+     */
+    private void recordPossibleResolution(Collection<String> locations) {
         if (locations.size() == 1) {
             String singleLocation = locations.iterator().next();
-            boolean isPom = singleLocation.endsWith(".pom");
-            boolean isIvy = singleLocation.contains("ivy-") && singleLocation.endsWith(".xml");
-            boolean isModule = singleLocation.endsWith(".module");
-            String format = isPom ? "Maven POM" : (isIvy ? "ivy.xml" : (isModule ? "Gradle module" : null));
+            String format = getFormatName(singleLocation);
             if (format != null) {
-                builder.node(
-                    String.format("If the artifact you are trying to retrieve can be found in the repository but without metadata in '%s' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.", format)
-                );
+                resolutions = ImmutableList.of(String.format("If the artifact you are trying to retrieve can be found in the repository but without metadata in '%s' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.", format));
             }
         }
     }
+
+    private static String getFormatName(String singleLocation) {
+        boolean isPom = singleLocation.endsWith(".pom");
+        boolean isIvy = singleLocation.contains("ivy-") && singleLocation.endsWith(".xml");
+        boolean isModule = singleLocation.endsWith(".module");
+        return isPom ? "Maven POM" : (isIvy ? "ivy.xml" : (isModule ? "Gradle module" : null));
+    }
+
+    @Override
+    public List<String> getResolutions() {
+        return resolutions;
+    }
+
+    protected ModuleVersionResolveException createCopy() {
+        try {
+            String message = getMessage();
+            return getClass().getConstructor(ComponentSelector.class, Factory.class, Collection.class).newInstance(getSelector(), (Factory<String>) () -> message, resolutions);
+        } catch (Exception e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionResolveException.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionResolveException.java
index 750dc99..9241e7e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionResolveException.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/ModuleVersionResolveException.java
@@ -132,7 +132,7 @@ private String toString(ComponentIdentifier identifier) {
     protected ModuleVersionResolveException createCopy() {
         try {
             String message = getMessage();
-            return getClass().getConstructor(ComponentSelector.class, Factory.class).newInstance(selector, (Factory<String>) () -> message);
+            return getClass().getConstructor(ComponentSelector.class, Factory.class).newInstance(getSelector(), (Factory<String>) () -> message);
         } catch (Exception e) {
             throw UncheckedException.throwAsUncheckedException(e);
         }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactResolver.java
index 103014b..e43b71f 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactResolver.java
@@ -15,11 +15,9 @@
  */
 package org.gradle.internal.resolve.resolver;
 
-import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.internal.component.ArtifactType;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
-import org.gradle.internal.component.model.ModuleSources;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.resolve.result.BuildableArtifactResolveResult;
 import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
 
@@ -27,11 +25,10 @@ public interface ArtifactResolver {
     /**
      * Discovers the set of artifacts belonging to the given component, with the type specified. Does not download the artifacts. Any failures are packaged up in the result.
      */
-    void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result);
+    void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result);
 
     /**
      * Resolves the given artifact. Any failures are packaged up in the result.
      */
-    void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactResolveResult result);
-
+    void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactSelector.java
index 5fd77ff..cc859e8 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactSelector.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ArtifactSelector.java
@@ -21,23 +21,21 @@
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
-import org.gradle.internal.component.model.VariantResolveMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
+import org.gradle.internal.component.model.VariantArtifactSelectionCandidates;
 
 import java.util.Collection;
-import java.util.Set;
-import java.util.function.Supplier;
 
 public interface ArtifactSelector {
     /**
      * Creates a set that will resolve the artifacts of the given configuration, minus those artifacts that are excluded.
      */
-    ArtifactSet resolveArtifacts(ComponentResolveMetadata component, Supplier<Set<? extends VariantResolveMetadata>> allVariants, Set<? extends VariantResolveMetadata> legacyVariants, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes);
+    ArtifactSet resolveArtifacts(ComponentArtifactResolveMetadata component, VariantArtifactSelectionCandidates variant, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes);
 
     /**
      * Creates a set that will resolve the given artifacts of the given component.
      */
-    ArtifactSet resolveArtifacts(ComponentResolveMetadata component, Collection<? extends ComponentArtifactMetadata> artifacts, ImmutableAttributes overriddenAttributes);
+    ArtifactSet resolveArtifacts(ComponentArtifactResolveMetadata component, Collection<? extends ComponentArtifactMetadata> artifacts, ImmutableAttributes overriddenAttributes);
 
     /**
      * Creates a set that will resolve the artifacts of the file dependency.
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ComponentMetaDataResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ComponentMetaDataResolver.java
index 189353e..d89d883 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ComponentMetaDataResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/ComponentMetaDataResolver.java
@@ -20,9 +20,12 @@
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
 import org.gradle.internal.resolve.result.BuildableComponentResolveResult;
 
+/**
+ * Responsible for resolving a {@link ComponentIdentifier} to a {@link org.gradle.internal.component.model.ComponentGraphResolveState} instance for that component.
+ */
 public interface ComponentMetaDataResolver {
     /**
-     * Resolves the metadata for a component instance. Failures should be attached to the returned result.
+     * Resolves the resolution state for a component instance. Failures should be attached to the returned result.
      */
     void resolve(ComponentIdentifier identifier, ComponentOverrideMetadata componentOverrideMetadata, BuildableComponentResolveResult result);
 
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultArtifactSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultArtifactSelector.java
index 7d407e3..254fe76 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultArtifactSelector.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultArtifactSelector.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import org.gradle.api.NonNullApi;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.capabilities.CapabilitiesMetadata;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSet;
@@ -33,18 +34,18 @@
 import org.gradle.internal.component.external.model.ImmutableCapabilities;
 import org.gradle.internal.component.local.model.LocalFileDependencyMetadata;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentArtifactResolveVariantState;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
-import org.gradle.internal.component.model.ModuleSources;
+import org.gradle.internal.component.model.VariantArtifactSelectionCandidates;
 import org.gradle.internal.component.model.VariantResolveMetadata;
 import org.gradle.internal.component.model.VariantWithOverloadAttributes;
+import org.gradle.internal.lazy.Lazy;
 import org.gradle.internal.model.CalculatedValueContainerFactory;
 import org.gradle.util.internal.CollectionUtils;
 
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
-import java.util.function.Supplier;
 
 public class DefaultArtifactSelector implements ArtifactSelector {
     private final List<OriginArtifactSelector> selectors;
@@ -68,12 +69,9 @@ public ArtifactSet resolveArtifacts(LocalFileDependencyMetadata fileDependencyMe
     }
 
     @Override
-    public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, Supplier<Set<? extends VariantResolveMetadata>> allVariants, Set<? extends VariantResolveMetadata> legacyVariants, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) {
-        ModuleVersionIdentifier moduleVersionId = component.getModuleVersionId();
-        ModuleSources sources = component.getSources();
-
-        ImmutableSet<ResolvedVariant> legacyResolvedVariants = buildResolvedVariants(moduleVersionId, sources, legacyVariants, exclusions);
-        ComponentArtifactResolveVariantState allResolvedVariants = () -> buildResolvedVariants(moduleVersionId, sources, allVariants.get(), exclusions);
+    public ArtifactSet resolveArtifacts(ComponentArtifactResolveMetadata component, VariantArtifactSelectionCandidates variant, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes) {
+        ImmutableSet<ResolvedVariant> legacyResolvedVariants = buildResolvedVariants(component, variant.getLegacyVariants(), exclusions);
+        ComponentArtifactResolveVariantState allResolvedVariants = new MemoizingComponentArtifactResolveVariantState(component, variant, exclusions);
 
         for (OriginArtifactSelector selector : selectors) {
             ArtifactSet artifacts = selector.resolveArtifacts(component, allResolvedVariants, legacyResolvedVariants, exclusions, overriddenAttributes);
@@ -84,38 +82,39 @@ public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, Supplier
         throw new IllegalStateException("No artifacts selected.");
     }
 
-    private ImmutableSet<ResolvedVariant> buildResolvedVariants(ModuleVersionIdentifier moduleVersionId, ModuleSources sources, Set<? extends VariantResolveMetadata> allVariants, ExcludeSpec exclusions) {
+    private ImmutableSet<ResolvedVariant> buildResolvedVariants(ComponentArtifactResolveMetadata component, Set<? extends VariantResolveMetadata> allVariants, ExcludeSpec exclusions) {
         ImmutableSet.Builder<ResolvedVariant> resolvedVariantBuilder = ImmutableSet.builder();
         for (VariantResolveMetadata variant : allVariants) {
-            ResolvedVariant resolvedVariant = toResolvedVariant(variant.getIdentifier(), variant.asDescribable(), variant.getAttributes(), variant.getArtifacts(), withImplicitCapability(variant.getCapabilities(), moduleVersionId), exclusions, moduleVersionId, sources, resolvedVariantCache, variant.isEligibleForCaching());
+            ResolvedVariant resolvedVariant = toResolvedVariant(variant.getIdentifier(), variant.asDescribable(), variant.getAttributes(), variant.getArtifacts(), withImplicitCapability(variant.getCapabilities(), component.getModuleVersionId()), exclusions, component, resolvedVariantCache, variant.isEligibleForCaching());
             resolvedVariantBuilder.add(resolvedVariant);
         }
         return resolvedVariantBuilder.build();
     }
 
-    private ResolvedVariant toResolvedVariant(VariantResolveMetadata.Identifier identifier,
-                                              DisplayName displayName,
-                                              ImmutableAttributes variantAttributes,
-                                              ImmutableList<? extends ComponentArtifactMetadata> artifacts,
-                                              ImmutableCapabilities capabilities,
-                                              ExcludeSpec exclusions,
-                                              ModuleVersionIdentifier ownerId,
-                                              ModuleSources moduleSources,
-                                              ResolvedVariantCache resolvedVariantCache,
-                                              boolean eligibleForCaching) {
+    private ResolvedVariant toResolvedVariant(
+        VariantResolveMetadata.Identifier identifier,
+        DisplayName displayName,
+        ImmutableAttributes variantAttributes,
+        ImmutableList<? extends ComponentArtifactMetadata> artifacts,
+        ImmutableCapabilities capabilities,
+        ExcludeSpec exclusions,
+        ComponentArtifactResolveMetadata component,
+        ResolvedVariantCache resolvedVariantCache,
+        boolean eligibleForCaching
+    ) {
         // artifactsToResolve are those not excluded by their owning module
         List<? extends ComponentArtifactMetadata> artifactsToResolve = CollectionUtils.filter(artifacts,
-                artifact -> !exclusions.excludesArtifact(ownerId.getModule(), artifact.getName())
+            artifact -> !exclusions.excludesArtifact(component.getModuleVersionId().getModule(), artifact.getName())
         );
 
         boolean hasExcludedArtifact = artifactsToResolve.size() < artifacts.size();
-        ImmutableAttributes attributes = artifactTypeRegistry.mapAttributesFor(variantAttributes, artifacts);
+        ImmutableAttributes attributes = artifactTypeRegistry.mapAttributesFor(variantAttributes, artifactsToResolve);
 
         if (hasExcludedArtifact) {
             // An ad hoc variant, has no identifier
-            return ArtifactSetFactory.toResolvedVariant(null, displayName, attributes, artifactsToResolve, capabilities, ownerId, moduleSources, artifactResolver);
+            return ArtifactSetFactory.toResolvedVariant(null, displayName, attributes, artifactsToResolve, capabilities, component, artifactResolver);
         } else if (!eligibleForCaching) {
-            return ArtifactSetFactory.toResolvedVariant(identifier, displayName, attributes, artifactsToResolve, capabilities, ownerId, moduleSources, artifactResolver);
+            return ArtifactSetFactory.toResolvedVariant(identifier, displayName, attributes, artifactsToResolve, capabilities, component, artifactResolver);
         } else {
             // This is a bit of a hack because we allow the artifactType registry to be different in every resolution scope.
             // This means it's not safe to assume a variant resolved in one consumer can be reused in another consumer with the same key.
@@ -123,7 +122,7 @@ private ResolvedVariant toResolvedVariant(VariantResolveMetadata.Identifier iden
             // It might be better to tighten this up by either requiring a single artifactType registry for the entire build or eliminating this feature
             // entirely.
             VariantWithOverloadAttributes key = new VariantWithOverloadAttributes(identifier, attributes);
-            return resolvedVariantCache.computeIfAbsent(key, id -> ArtifactSetFactory.toResolvedVariant(identifier, displayName, attributes, artifactsToResolve, capabilities, ownerId, moduleSources, artifactResolver));
+            return resolvedVariantCache.computeIfAbsent(key, id -> ArtifactSetFactory.toResolvedVariant(identifier, displayName, attributes, artifactsToResolve, capabilities, component, artifactResolver));
         }
     }
 
@@ -139,8 +138,22 @@ private static ImmutableCapabilities withImplicitCapability(CapabilitiesMetadata
     }
 
     @Override
-    public ArtifactSet resolveArtifacts(ComponentResolveMetadata component, Collection<? extends ComponentArtifactMetadata> artifacts, ImmutableAttributes overriddenAttributes) {
+    public ArtifactSet resolveArtifacts(ComponentArtifactResolveMetadata component, Collection<? extends ComponentArtifactMetadata> artifacts, ImmutableAttributes overriddenAttributes) {
         ImmutableAttributes attributes = artifactTypeRegistry.mapAttributesFor(component.getAttributes(), artifacts);
-        return ArtifactSetFactory.adHocVariant(component.getId(), component.getModuleVersionId(), artifacts, component.getSources(), component.getAttributesSchema(), artifactResolver, attributes, overriddenAttributes);
+        return ArtifactSetFactory.adHocVariant(component, artifacts, component.getAttributesSchema(), artifactResolver, attributes, overriddenAttributes);
+    }
+
+    @NonNullApi
+    private class MemoizingComponentArtifactResolveVariantState implements ComponentArtifactResolveVariantState {
+        private final Lazy<Set<ResolvedVariant>> variants;
+
+        public MemoizingComponentArtifactResolveVariantState(ComponentArtifactResolveMetadata component, VariantArtifactSelectionCandidates variant, ExcludeSpec exclusions) {
+            variants = Lazy.locking().of(() -> buildResolvedVariants(component, variant.getAllVariants(), exclusions));
+        }
+
+        @Override
+        public Set<ResolvedVariant> getAllVariants() {
+            return variants.get();
+        }
     }
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentIdResolver.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentIdResolver.java
index a1a6044..f900996 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentIdResolver.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DependencyToComponentIdResolver.java
@@ -22,6 +22,12 @@
 
 import javax.annotation.Nullable;
 
+/**
+ * Responsible for taking a dependency declaration and locating the matching component. The component can be returned either the resolution state for the component, if this state is cheaply available
+ * to this resolver, or an id for the component if not.
+ *
+ * <p>At some point in the future, this should resolve to a set of candidates rather than a single instance.
+ */
 public interface DependencyToComponentIdResolver {
     /**
      * Resolves the given dependency to a component instance. Failures should be attached to the result.
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/OriginArtifactSelector.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/OriginArtifactSelector.java
index 6e88ff7..043534e 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/OriginArtifactSelector.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/OriginArtifactSelector.java
@@ -20,8 +20,8 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.specs.ExcludeSpec;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentArtifactResolveVariantState;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 
 import javax.annotation.Nullable;
 import java.util.Set;
@@ -31,5 +31,5 @@ public interface OriginArtifactSelector {
      * Creates a set that will resolve the artifacts of the given configuration, minus those artifacts that are excluded.
      */
     @Nullable
-    ArtifactSet resolveArtifacts(ComponentResolveMetadata component, ComponentArtifactResolveVariantState allVariants, Set<ResolvedVariant> legacyVariants, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes);
+    ArtifactSet resolveArtifacts(ComponentArtifactResolveMetadata component, ComponentArtifactResolveVariantState allVariants, Set<ResolvedVariant> legacyVariants, ExcludeSpec exclusions, ImmutableAttributes overriddenAttributes);
 }
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactFileResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactFileResolveResult.java
index 2073279..438e555 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactFileResolveResult.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableArtifactFileResolveResult.java
@@ -42,4 +42,4 @@ public interface BuildableArtifactFileResolveResult extends ResolveResult, Build
      * Marks the artifact as not found.
      */
     void notFound(ComponentArtifactIdentifier artifact);
-}
\ No newline at end of file
+}
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleComponentMetaDataResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleComponentMetaDataResolveResult.java
index 1f41e9e..b429648 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleComponentMetaDataResolveResult.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/BuildableModuleComponentMetaDataResolveResult.java
@@ -16,15 +16,16 @@
 
 package org.gradle.internal.resolve.result;
 
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 
 import javax.annotation.Nullable;
 
 /**
  * The result of attempting to resolve a component id to the meta-data for the component.
+ *
+ * @param <T> the resolution result type
  */
-public interface BuildableModuleComponentMetaDataResolveResult extends ResourceAwareResolveResult, ErroringResolveResult<ModuleVersionResolveException> {
+public interface BuildableModuleComponentMetaDataResolveResult<T> extends ResourceAwareResolveResult, ErroringResolveResult<ModuleVersionResolveException> {
     enum State {
         Resolved, Missing, Failed, Unknown, Redirect
     }
@@ -39,7 +40,7 @@ enum State {
      *
      * @throws ModuleVersionResolveException If the resolution was not successful.
      */
-    ModuleComponentResolveMetadata getMetaData() throws ModuleVersionResolveException;
+    T getMetaData() throws ModuleVersionResolveException;
 
     @Override
     @Nullable
@@ -48,12 +49,12 @@ enum State {
     /**
      * Marks the module version as resolved, with the given meta-data.
      */
-    void resolved(ModuleComponentResolveMetadata metaData);
+    void resolved(T metaData);
 
     /**
-     * Replaces the meta-data for this result. Must be resolved.
+     * Replaces the meta-data for this result. This result must already be resolved.
      */
-    void setMetadata(ModuleComponentResolveMetadata metaData);
+    void setMetadata(T metaData);
 
     /**
      * Marks the resolve as failed with the given exception.
diff --git a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResult.java b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResult.java
index 673d31c..36145b6 100644
--- a/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResult.java
+++ b/subprojects/dependency-management/src/main/java/org/gradle/internal/resolve/result/DefaultBuildableModuleComponentMetaDataResolveResult.java
@@ -15,14 +15,15 @@
  */
 package org.gradle.internal.resolve.result;
 
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 
-public class DefaultBuildableModuleComponentMetaDataResolveResult extends DefaultResourceAwareResolveResult implements BuildableModuleComponentMetaDataResolveResult {
+import java.util.function.Function;
+
+public class DefaultBuildableModuleComponentMetaDataResolveResult<T> extends DefaultResourceAwareResolveResult implements BuildableModuleComponentMetaDataResolveResult<T> {
 
     private State state = State.Unknown;
     private ModuleVersionResolveException failure;
-    private ModuleComponentResolveMetadata metaData;
+    private T metaData;
     private boolean authoritative;
 
     private void reset(State state) {
@@ -37,14 +38,15 @@ public void reset() {
     }
 
     @Override
-    public void resolved(ModuleComponentResolveMetadata metaData) {
+    public void resolved(T metaData) {
         reset(State.Resolved);
+        this.failure = null;
         this.metaData = metaData;
         authoritative = true;
     }
 
     @Override
-    public void setMetadata(ModuleComponentResolveMetadata metaData) {
+    public void setMetadata(T metaData) {
         assertResolved();
         this.metaData = metaData;
     }
@@ -52,12 +54,15 @@ public void setMetadata(ModuleComponentResolveMetadata metaData) {
     @Override
     public void missing() {
         reset(State.Missing);
+        this.metaData = null;
+        this.failure = null;
         authoritative = true;
     }
 
     @Override
     public void failed(ModuleVersionResolveException failure) {
         reset(State.Failed);
+        this.metaData = null;
         this.failure = failure;
         authoritative = true;
     }
@@ -79,7 +84,7 @@ public ModuleVersionResolveException getFailure() {
     }
 
     @Override
-    public ModuleComponentResolveMetadata getMetaData() throws ModuleVersionResolveException {
+    public T getMetaData() throws ModuleVersionResolveException {
         assertResolved();
         return metaData;
     }
@@ -106,6 +111,22 @@ public boolean shouldUseGradleMetatada() {
         return state == State.Redirect;
     }
 
+    public <S> void applyTo(BuildableModuleComponentMetaDataResolveResult<S> target, Function<T, S> resultMapper) {
+        if (state == State.Resolved) {
+            target.resolved(resultMapper.apply(metaData));
+        } else if (state == State.Failed) {
+            target.failed(failure);
+        } else if (state == State.Redirect) {
+            target.redirectToGradleMetadata();
+        } else if (state == State.Missing) {
+            target.missing();
+        } else {
+            throw new IllegalStateException();
+        }
+        target.setAuthoritative(authoritative);
+        applyTo(target);
+    }
+
     private void assertHasResult() {
         if (!hasResult()) {
             throw new IllegalStateException("No result has been specified.");
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy
index 61dbe88..0f3786f 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultArtifactIdentifierTest.groovy
@@ -43,4 +43,4 @@
         base != badExtension
         base != badClassifier
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolverResultsSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolverResultsSpec.groovy
index a685099..320404f 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolverResultsSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/DefaultResolverResultsSpec.groovy
@@ -19,6 +19,7 @@
 import org.gradle.api.artifacts.ResolveException
 import org.gradle.api.artifacts.ResolvedConfiguration
 import org.gradle.api.artifacts.result.ResolutionResult
+import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveState
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactSet
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedLocalComponentsResult
 import spock.lang.Specification
@@ -28,12 +29,12 @@
     private resolutionResult = Mock(ResolutionResult)
     private projectConfigurationResult = Mock(ResolvedLocalComponentsResult)
     private visitedArtifactsSet = Mock(VisitedArtifactSet)
+    private artifactResolveState = Mock(ArtifactResolveState)
     private fatalFailure = Mock(ResolveException)
-    private results = new DefaultResolverResults()
 
     def "does not provide result in case of fatal failure"() {
         when:
-        results.failed(fatalFailure)
+        def results = DefaultResolverResults.failed(fatalFailure, fatalFailure)
 
         and:
         results.resolutionResult
@@ -57,17 +58,28 @@
         ex3 == fatalFailure
     }
 
-    def "provides resolve results"() {
+    def "provides build dependencies results"() {
         when:
-        results.graphResolved(resolutionResult, projectConfigurationResult, visitedArtifactsSet)
+        def results = DefaultResolverResults.buildDependenciesResolved(resolutionResult, projectConfigurationResult, visitedArtifactsSet)
 
         then:
         results.resolutionResult == resolutionResult
         results.resolvedLocalComponents == projectConfigurationResult
         results.visitedArtifacts == visitedArtifactsSet
+    }
+
+    def "provides resolve results"() {
+        when:
+        def results = DefaultResolverResults.graphResolved(resolutionResult, projectConfigurationResult, visitedArtifactsSet, artifactResolveState)
+
+        then:
+        results.resolutionResult == resolutionResult
+        results.resolvedLocalComponents == projectConfigurationResult
+        results.visitedArtifacts == visitedArtifactsSet
+        results.artifactResolveState == artifactResolveState
 
         when:
-        results.artifactsResolved(resolvedConfiguration, visitedArtifactsSet)
+        results = DefaultResolverResults.artifactsResolved(resolutionResult, projectConfigurationResult, resolvedConfiguration, visitedArtifactsSet)
 
         then:
         results.resolutionResult == resolutionResult
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationRoleSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationRoleSpec.groovy
index a7f8489..fffde51 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationRoleSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationRoleSpec.groovy
@@ -18,110 +18,18 @@
 
 import spock.lang.Specification
 
-import static org.gradle.api.internal.artifacts.configurations.ConfigurationRole.RoleDescriber.*
-
 class ConfigurationRoleSpec extends Specification {
-    def "can find predefined role #role"() {
-        expect:
-        ConfigurationRole.forUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated) == role
-
-        where:
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated  || role
-        true        | true          | true              | false                 | false                 | false                         || ConfigurationRoles.LEGACY
-        true        | false         | false             | false                 | false                 | false                         || ConfigurationRoles.INTENDED_CONSUMABLE
-        false       | true          | false             | false                 | false                 | false                         || ConfigurationRoles.INTENDED_RESOLVABLE
-        false       | true          | true              | false                 | false                 | false                         || ConfigurationRoles.INTENDED_RESOLVABLE_BUCKET
-        false       | false         | true              | false                 | false                 | false                         || ConfigurationRoles.INTENDED_BUCKET
-        true        | true          | true              | false                 | true                  | true                          || ConfigurationRoles.DEPRECATED_CONSUMABLE
-        true        | true          | true              | true                  | false                 | true                          || ConfigurationRoles.DEPRECATED_RESOLVABLE
-    }
-
-    def "can create role for unknown usage combinations consumable=#consumable, resolvable=#resolvable, declarableAgainst=#declarableAgainst, consumptionDeprecated=#consumptionDeprecated, resolutionDeprecated=#resolutionDeprecated, declarationAgainstDeprecated=#declarationAgainstDeprecated"() {
-        expect:
-        ConfigurationRole role = ConfigurationRole.forUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated)
-        role.isConsumable() == consumable
-        role.isResolvable() == resolvable
-        role.isDeclarableAgainst() == declarableAgainst
-        role.isConsumptionDeprecated() == consumptionDeprecated
-        role.isResolutionDeprecated() == resolutionDeprecated
-        role.isDeclarationAgainstDeprecated() == declarationAgainstDeprecated
-
-        where:
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
-        true        | true          | false             | false                 | false                 | false
-        false       | true          | true              | false                 | true                  | false
-        true        | true          | true              | true                  | true                  | true
-        true        | false         | true              | true                  | false                 | true
-        true        | true          | true              | false                 | true                  | false
-    }
-
-    def "custom role is named correctly"() {
-        when:
-        def customRole = ConfigurationRole.forUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated)
-
-        then:
-        customRole !in ConfigurationRoles.values()
-        customRole.name == DEFAULT_CUSTOM_ROLE_NAME
-
-        where:
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
-        true        | true          | false             | false                 | false                 | false
-        false       | true          | true              | false                 | true                  | false
-        true        | true          | true              | true                  | true                  | true
-        true        | false         | true              | true                  | false                 | true
-        true        | true          | true              | false                 | true                  | false
-    }
-
-    def "custom role can be given custom description"() {
-        when:
-        def customRole = ConfigurationRole.forUsage('custom', consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated, 'custom description', false)
-
-        then:
-        customRole !in ConfigurationRoles.values()
-        customRole.name == 'custom'
-        customRole.describeUsage() == 'custom description'
-
-        where:
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
-        true        | true          | false             | false                 | false                 | false
-        false       | true          | true              | false                 | true                  | false
-        true        | true          | true              | true                  | true                  | true
-        true        | false         | true              | true                  | false                 | true
-        true        | true          | true              | false                 | true                  | false
-    }
-
     def "roles can describe themselves #role"() {
         expect:
         assertDescriptionContains(role, usages)
 
         where:
         role                                            || usages
-        ConfigurationRoles.LEGACY                       || [CONSUMABLE, RESOLVABLE, DECLARABLE_AGAINST]
-        ConfigurationRoles.INTENDED_CONSUMABLE          || [CONSUMABLE]
-        ConfigurationRoles.INTENDED_RESOLVABLE          || [RESOLVABLE]
-        ConfigurationRoles.INTENDED_RESOLVABLE_BUCKET   || [RESOLVABLE, DECLARABLE_AGAINST]
-        ConfigurationRoles.INTENDED_BUCKET              || [DECLARABLE_AGAINST]
-        ConfigurationRoles.DEPRECATED_CONSUMABLE        || [CONSUMABLE, deprecatedFor(RESOLVABLE), deprecatedFor(DECLARABLE_AGAINST)]
-        ConfigurationRoles.DEPRECATED_RESOLVABLE        || [RESOLVABLE, deprecatedFor(CONSUMABLE), deprecatedFor(DECLARABLE_AGAINST)]
-    }
-
-    def "custom role can't deprecate what it doesn't allow"() {
-        when:
-        ConfigurationRole.forUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated)
-
-        then:
-        def e = thrown(IllegalArgumentException)
-        e.message == 'Cannot create a role that deprecates a usage that is not allowed'
-
-        where:
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
-        false       | false         | false             | true                  | false                 | false
-        false       | false         | false             | false                 | true                  | false
-        false       | false         | false             | false                 | false                 | true
-    }
-
-    private String deprecatedFor(String usage) {
-        return usage + describeDeprecation(true)
+        ConfigurationRoles.LEGACY                       || [UsageDescriber.CONSUMABLE, UsageDescriber.RESOLVABLE, UsageDescriber.DECLARABLE_AGAINST]
+        ConfigurationRoles.CONSUMABLE                   || [UsageDescriber.CONSUMABLE]
+        ConfigurationRoles.RESOLVABLE                   || [UsageDescriber.RESOLVABLE]
+        ConfigurationRoles.RESOLVABLE_BUCKET            || [UsageDescriber.RESOLVABLE, UsageDescriber.DECLARABLE_AGAINST]
+        ConfigurationRoles.BUCKET                       || [UsageDescriber.DECLARABLE_AGAINST]
     }
 
     private void assertDescriptionContains(ConfigurationRole role, List<String> usages) {
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationRolesSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationRolesSpec.groovy
index 43cb741..2ac7897 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationRolesSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/ConfigurationRolesSpec.groovy
@@ -21,33 +21,29 @@
 class ConfigurationRolesSpec extends Specification {
     def "can find predefined role #role"() {
         when:
-        def result = ConfigurationRoles.byUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated)
+        def result = ConfigurationRoles.byUsage(consumable, resolvable, declarable)
 
         then:
         result.isPresent()
         result.get() == role
 
         where:
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated  || role
-        true        | true          | true              | false                 | false                 | false                         || ConfigurationRoles.LEGACY
-        true        | false         | false             | false                 | false                 | false                         || ConfigurationRoles.INTENDED_CONSUMABLE
-        false       | true          | false             | false                 | false                 | false                         || ConfigurationRoles.INTENDED_RESOLVABLE
-        false       | true          | true              | false                 | false                 | false                         || ConfigurationRoles.INTENDED_RESOLVABLE_BUCKET
-        false       | false         | true              | false                 | false                 | false                         || ConfigurationRoles.INTENDED_BUCKET
-        true        | true          | true              | false                 | true                  | true                          || ConfigurationRoles.DEPRECATED_CONSUMABLE
-        true        | true          | true              | true                  | false                 | true                          || ConfigurationRoles.DEPRECATED_RESOLVABLE
+        consumable  | resolvable    | declarable        || role
+        true        | true          | true              || ConfigurationRoles.LEGACY
+        true        | false         | false             || ConfigurationRoles.CONSUMABLE
+        false       | true          | false             || ConfigurationRoles.RESOLVABLE
+        false       | true          | true              || ConfigurationRoles.RESOLVABLE_BUCKET
+        false       | false         | true              || ConfigurationRoles.BUCKET
     }
 
-    def "can not find unknown usage combinations consumable=#consumable, resolvable=#resolvable, declarableAgainst=#declarableAgainst, consumptionDeprecated=#consumptionDeprecated, resolutionDeprecated=#resolutionDeprecated, declarationAgainstDeprecated=#declarationAgainstDeprecated"() {
+    def "can not find unknown usage combinations consumable=#consumable, resolvable=#resolvable, declarable=#declarable"() {
         expect:
-        !ConfigurationRoles.byUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated).isPresent()
+        !ConfigurationRoles.byUsage(consumable, resolvable, declarable).isPresent()
 
         where:
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
-        false       | false         | false             | false                 | false                 | false
-        true        | true          | true              | true                  | true                  | true
-        true        | false         | true              | true                  | false                 | true
-        true        | false         | true              | false                 | true                  | false
+        consumable  | resolvable    | declarable
+        false       | false         | false
+        true        | true          | false
     }
 
     def "predefined roles are named"() {
@@ -56,12 +52,10 @@
 
         where:
         role                                            || name
-        ConfigurationRoles.INTENDED_BUCKET              || "Intended Bucket"
-        ConfigurationRoles.INTENDED_CONSUMABLE          || "Intended Consumable"
-        ConfigurationRoles.INTENDED_RESOLVABLE          || "Intended Resolvable"
-        ConfigurationRoles.INTENDED_RESOLVABLE_BUCKET   || "Intended Resolvable Bucket"
-        ConfigurationRoles.DEPRECATED_CONSUMABLE        || "Deprecated Consumable"
-        ConfigurationRoles.DEPRECATED_RESOLVABLE        || "Deprecated Resolvable"
+        ConfigurationRoles.BUCKET                       || "Bucket"
+        ConfigurationRoles.CONSUMABLE                   || "Consumable"
+        ConfigurationRoles.RESOLVABLE                   || "Resolvable"
+        ConfigurationRoles.RESOLVABLE_BUCKET            || "Resolvable Bucket"
         ConfigurationRoles.LEGACY                       || "Legacy"
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
index 6301bd3..268c51c 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerSpec.groovy
@@ -15,21 +15,21 @@
  */
 package org.gradle.api.internal.artifacts.configurations
 
+import org.gradle.api.Action
 import org.gradle.api.artifacts.UnknownConfigurationException
 import org.gradle.api.internal.CollectionCallbackActionDecorator
-import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.internal.DomainObjectContext
 import org.gradle.api.internal.artifacts.ComponentSelectorConverter
 import org.gradle.api.internal.artifacts.ConfigurationResolver
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory
+import org.gradle.api.internal.artifacts.ResolveExceptionContextualizer
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
 import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactNotationParserFactory
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyLockingProvider
 import org.gradle.api.internal.artifacts.ivyservice.dependencysubstitution.DependencySubstitutionRules
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.LocalComponentMetadataBuilder
 import org.gradle.api.internal.file.FileCollectionFactory
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.api.internal.initialization.RootScriptDomainObjectContext
@@ -55,7 +55,6 @@
     private DomainObjectContext domainObjectContext = Mock()
     private ListenerManager listenerManager = Mock()
     private DependencyMetaDataProvider metaDataProvider = Mock()
-    private LocalComponentMetadataBuilder metaDataBuilder = Mock()
     private FileCollectionFactory fileCollectionFactory = Mock()
     private ComponentIdentifierFactory componentIdentifierFactory = Mock()
     private DependencySubstitutionRules globalSubstitutionRules = Mock()
@@ -69,16 +68,19 @@
     private ComponentSelectorConverter componentSelectorConverter = Mock()
     private DependencyLockingProvider dependencyLockingProvider = Mock()
     private ProjectStateRegistry projectStateRegistry = Mock()
-    private DocumentationRegistry documentationRegistry = Mock()
     private UserCodeApplicationContext userCodeApplicationContext = Mock()
     private CalculatedValueContainerFactory calculatedValueContainerFactory = Mock()
 
-    private CollectionCallbackActionDecorator domainObjectCollectionCallbackActionDecorator = Mock() {
+    private CollectionCallbackActionDecorator domainObjectCollectionCallbackActionDecorator = Mock(CollectionCallbackActionDecorator) {
         decorateSpec(_) >> { Spec spec -> spec }
+        decorate(_ as Action) >> { it[0] }
     }
     def immutableAttributesFactory = AttributeTestUtil.attributesFactory()
+    def metadataBuilder = Mock(DefaultRootComponentMetadataBuilder) {
+        getValidator() >> Mock(MutationValidator)
+    }
     private DefaultRootComponentMetadataBuilder.Factory rootComponentMetadataBuilderFactory = Mock(DefaultRootComponentMetadataBuilder.Factory) {
-        create(_) >> Mock(DefaultRootComponentMetadataBuilder)
+        create(_) >> metadataBuilder
     }
     private DefaultConfigurationFactory configurationFactory = new DefaultConfigurationFactory(
         instantiator,
@@ -92,7 +94,7 @@
         buildOperationExecutor,
         Stub(PublishArtifactNotationParserFactory),
         immutableAttributesFactory,
-        documentationRegistry,
+        Stub(ResolveExceptionContextualizer),
         userCodeApplicationContext,
         projectStateRegistry,
         Mock(WorkerThreadRegistry),
@@ -126,7 +128,7 @@
 
         then:
         compile.name == "compile"
-        compile.path == ":compile"
+        compile.incoming.path == ":compile"
         compile instanceof DefaultConfiguration
 
         and:
@@ -160,7 +162,7 @@
         then:
         configurationContainer.getByName("compile") == compile
         compile.description == "I compile!"
-        compile.path == ":compile"
+        compile.incoming.path == ":compile"
     }
 
     def "creates detached"() {
@@ -180,6 +182,6 @@
         detached.getHierarchy() == [detached] as Set
         [dependency1, dependency2].each { detached.getDependencies().contains(it) }
         detached.getDependencies().size() == 2
-        detached.path == ":detachedConfiguration1"
+        detached.incoming.path == ":detachedConfiguration1"
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
index 60b61e7..a82ec38 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationContainerTest.groovy
@@ -17,15 +17,16 @@
 package org.gradle.api.internal.artifacts.configurations
 
 import groovy.test.NotYetImplemented
+import org.gradle.api.Action
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.UnknownConfigurationException
 import org.gradle.api.internal.CollectionCallbackActionDecorator
-import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.internal.DomainObjectContext
 import org.gradle.api.internal.artifacts.ComponentSelectorConverter
 import org.gradle.api.internal.artifacts.ConfigurationResolver
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory
+import org.gradle.api.internal.artifacts.ResolveExceptionContextualizer
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactNotationParserFactory
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyLockingProvider
@@ -60,8 +61,9 @@
     private BuildOperationExecutor buildOperationExecutor = Mock(BuildOperationExecutor)
     private DependencyLockingProvider lockingProvider = Mock(DependencyLockingProvider)
     private ProjectStateRegistry projectStateRegistry = Mock(ProjectStateRegistry)
-    private DocumentationRegistry documentationRegistry = Mock(DocumentationRegistry)
-    private CollectionCallbackActionDecorator callbackActionDecorator = Mock()
+    private CollectionCallbackActionDecorator callbackActionDecorator = Mock(CollectionCallbackActionDecorator) {
+        decorate(_ as Action) >> { it[0] }
+    }
     private UserCodeApplicationContext userCodeApplicationContext = Mock()
     private CalculatedValueContainerFactory calculatedValueContainerFactory = Mock()
     private Instantiator instantiator = TestUtil.instantiatorFactory().decorateLenient()
@@ -73,8 +75,11 @@
     }
     private ComponentSelectorConverter componentSelectorConverter = Mock()
     private DomainObjectContext domainObjectContext = new RootScriptDomainObjectContext()
+    private DefaultRootComponentMetadataBuilder metadataBuilder = Mock(DefaultRootComponentMetadataBuilder) {
+        getValidator() >> Mock(MutationValidator)
+    }
     private DefaultRootComponentMetadataBuilder.Factory rootComponentMetadataBuilderFactory = Mock(DefaultRootComponentMetadataBuilder.Factory) {
-        create(_) >> Mock(DefaultRootComponentMetadataBuilder)
+        create(_) >> metadataBuilder
     }
     private DefaultConfigurationFactory configurationFactory = new DefaultConfigurationFactory(
         instantiator,
@@ -93,7 +98,7 @@
                 TestFiles.taskDependencyFactory(),
         ),
         immutableAttributesFactory,
-        documentationRegistry,
+        Stub(ResolveExceptionContextualizer),
         userCodeApplicationContext,
         projectStateRegistry,
         Mock(WorkerThreadRegistry),
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy
index d317b4f..915a49b 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/DefaultConfigurationSpec.groovy
@@ -45,11 +45,14 @@
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.DefaultResolverResults
 import org.gradle.api.internal.artifacts.DependencyResolutionServices
+import org.gradle.api.internal.artifacts.ResolveExceptionContextualizer
 import org.gradle.api.internal.artifacts.ResolverResults
+import org.gradle.api.internal.artifacts.ResolveContext
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
 import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
 import org.gradle.api.internal.artifacts.dsl.PublishArtifactNotationParserFactory
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyLockingProvider
+import org.gradle.api.internal.artifacts.ivyservice.ArtifactResolveState
 import org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration
 import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.RootComponentMetadataBuilder
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedFileVisitor
@@ -90,7 +93,7 @@
 import static org.hamcrest.CoreMatchers.equalTo
 import static org.hamcrest.MatcherAssert.assertThat
 
-class DefaultConfigurationSpec extends Specification {
+class DefaultConfigurationSpec extends Specification implements InspectableConfigurationFixture {
     Instantiator instantiator = TestUtil.instantiatorFactory().decorateLenient()
 
     def configurationsProvider = Mock(ConfigurationsProvider)
@@ -365,9 +368,7 @@
         def failure = new ResolveException("bad", new RuntimeException())
 
         and:
-        _ * resolver.resolveGraph(_, _) >> { ConfigurationInternal config, DefaultResolverResults resolverResults ->
-            resolverResults.failed(failure)
-        }
+        _ * resolver.resolveGraph(_) >> DefaultResolverResults.failed(failure, failure)
         _ * resolutionStrategy.resolveGraphToDetermineTaskDependencies() >> true
 
         when:
@@ -485,11 +486,11 @@
         }
 
         _ * localComponentsResult.resolvedProjectConfigurations >> Collections.emptySet()
-        _ * resolver.resolveGraph(_, _) >> { ConfigurationInternal config, DefaultResolverResults resolverResults ->
-            resolverResults.graphResolved(resolutionResults, localComponentsResult, visitedArtifactSet)
-            resolverResults.artifactsResolved(Stub(ResolvedConfiguration), visitedArtifactSet)
-        }
         _ * resolver.getRepositories() >> []
+
+        def graphResults = DefaultResolverResults.graphResolved(resolutionResults, localComponentsResult, visitedArtifactSet, Mock(ArtifactResolveState))
+        _ * resolver.resolveGraph(_) >> graphResults
+        _ * resolver.resolveArtifacts(_, _) >> DefaultResolverResults.artifactsResolved(resolutionResults, localComponentsResult, Stub(ResolvedConfiguration), visitedArtifactSet)
     }
 
     private ResolutionResult stubResolutionResults() {
@@ -516,10 +517,9 @@
         _ * resolvedConfiguration.hasError() >> true
 
         _ * localComponentsResult.resolvedProjectConfigurations >> Collections.emptySet()
-        _ * resolver.resolveGraph(_, _) >> { ConfigurationInternal config, DefaultResolverResults resolverResults ->
-            resolverResults.graphResolved(resolutionResults, localComponentsResult, visitedArtifactSet)
-            resolverResults.artifactsResolved(resolvedConfiguration, visitedArtifactSet)
-        }
+        def graphResults = DefaultResolverResults.graphResolved(resolutionResults, localComponentsResult, visitedArtifactSet, Mock(ArtifactResolveState))
+        _ * resolver.resolveGraph(_) >> graphResults
+        _ * resolver.resolveArtifacts(_, _) >> DefaultResolverResults.artifactsResolved(resolutionResults, localComponentsResult, resolvedConfiguration, visitedArtifactSet)
     }
 
     def "artifacts have correct build dependencies"() {
@@ -582,9 +582,7 @@
         _ * artifactTaskDependencies.getDependencies(_) >> requiredTasks
 
         and:
-        _ * resolver.resolveBuildDependencies(_, _) >> { ConfigurationInternal config, ResolverResults resolverResults ->
-            resolverResults.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifactSet)
-        }
+        _ * resolver.resolveBuildDependencies(_) >> DefaultResolverResults.buildDependenciesResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifactSet)
 
         expect:
         configuration.buildDependencies.getDependencies(targetTask) == requiredTasks
@@ -765,31 +763,26 @@
     }
 
     void "deprecations are passed to copies when corresponding role is #state"() {
-        def configuration = prepareConfigurationForCopyTest()
+        ConfigurationRole role = new DefaultConfigurationRole("test", enabled, enabled, enabled, true, true, true)
+        def configuration = prepareConfigurationForCopyTest(role)
         def resolutionStrategyCopy = Mock(ResolutionStrategyInternal)
         1 * resolutionStrategy.copy() >> resolutionStrategyCopy
+        configuration.addDeclarationAlternatives("declaration")
+        configuration.addResolutionAlternatives("resolution")
 
         when:
-        configuration.deprecateForConsumption(x -> x.willBecomeAnErrorInGradle9().undocumented())
-        configuration.deprecateForDeclarationAgainst("declaration")
-        configuration.deprecateForResolution("resolution")
-        configuration.canBeConsumed = enabled
-        configuration.canBeResolved = enabled
-        configuration.canBeDeclaredAgainst = enabled
-
         def copy = configuration.copy()
 
         then:
         // This is not desired behavior. Roles should be copied without modification.
-        copy.canBeDeclaredAgainst
+        copy.canBeDeclared
         copy.canBeResolved
         copy.canBeConsumed
-        copy.consumptionDeprecation != null
         copy.declarationAlternatives == ["declaration"]
         copy.resolutionAlternatives == ["resolution"]
-        copy.roleAtCreation.consumptionDeprecated
-        copy.roleAtCreation.resolutionDeprecated
-        copy.roleAtCreation.declarationAgainstDeprecated
+        copy.deprecatedForConsumption
+        copy.deprecatedForResolution
+        copy.deprecatedForDeclarationAgainst
 
         where:
         state | enabled
@@ -805,17 +798,16 @@
         when:
         configuration.canBeConsumed = false
         configuration.canBeResolved = false
-        configuration.canBeDeclaredAgainst = false
+        configuration.canBeDeclared = false
 
 
         def copy = configuration.copy()
 
         then:
         // This is not desired behavior. Roles and deprecations should be copied without modification.
-        copy.canBeDeclaredAgainst
+        copy.canBeDeclared
         copy.canBeResolved
         copy.canBeConsumed
-        copy.consumptionDeprecation != null
         copy.declarationAlternatives == []
         copy.resolutionAlternatives == []
         copy.roleAtCreation.consumptionDeprecated
@@ -905,8 +897,8 @@
         copy.dependencyResolutionListeners.size() == 1
     }
 
-    private prepareConfigurationForCopyTest() {
-        def configuration = conf()
+    private prepareConfigurationForCopyTest(ConfigurationRole role = ConfigurationRoles.LEGACY) {
+        def configuration = conf("conf", ":", ":", role)
         configuration.visible = false
         configuration.transitive = false
         configuration.description = "descript"
@@ -914,8 +906,12 @@
         configuration.exclude([group: "value2"])
         configuration.artifacts.add(artifact("name1", "ext1", "type1", "classifier1"))
         configuration.artifacts.add(artifact("name2", "ext2", "type2", "classifier2"))
-        configuration.dependencies.add(dependency("group1", "name1", "version1"))
-        configuration.dependencies.add(dependency("group2", "name2", "version2"))
+
+        if (role.declarable) {
+            configuration.dependencies.add(dependency("group1", "name1", "version1"))
+            configuration.dependencies.add(dependency("group2", "name2", "version2"))
+        }
+
         configuration.getAttributes().attribute(Attribute.of('key', String.class), 'value')
         configuration.resolutionStrategy
 
@@ -1114,11 +1110,9 @@
             collectFiles(_) >> { return it[0] }
         }
 
-        resolver.resolveGraph(config, _) >> { ConfigurationInternal conf, DefaultResolverResults res ->
-            res.graphResolved(resolutionResult, localComponentsResult, visitedArtifactSet)
-        }
-        resolver.resolveArtifacts(config, _) >> { ConfigurationInternal conf, DefaultResolverResults res ->
-            res.artifactsResolved(resolvedConfiguration, visitedArtifactSet)
+        resolver.resolveGraph(config) >> DefaultResolverResults.graphResolved(resolutionResult, localComponentsResult, visitedArtifactSet, Mock(ArtifactResolveState))
+        resolver.resolveArtifacts(config, _ as ResolverResults) >> { ResolveContext conf, ResolverResults res ->
+            DefaultResolverResults.artifactsResolved(res.resolutionResult, res.resolvedLocalComponents, resolvedConfiguration, visitedArtifactSet)
         }
     }
 
@@ -1170,9 +1164,7 @@
         config.state == RESOLVED
 
         and:
-        1 * resolver.resolveGraph(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts())
-        }
+        1 * resolver.resolveGraph(config) >> DefaultResolverResults.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts(), Mock(ArtifactResolveState))
         1 * resolver.getRepositories() >> []
         0 * resolver._
     }
@@ -1191,14 +1183,13 @@
         config.state == UNRESOLVED
 
         and:
-        1 * resolver.resolveBuildDependencies(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts())
-        }
+        1 * resolver.resolveBuildDependencies(config) >> DefaultResolverResults.buildDependenciesResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts())
         0 * resolver._
     }
 
     def "resolving graph for task dependencies, and then resolving it for results does not re-resolve graph"() {
         def config = conf("conf")
+        def graphResults = DefaultResolverResults.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts(), Mock(ArtifactResolveState))
 
         given:
         _ * resolutionStrategy.resolveGraphToDetermineTaskDependencies() >> true
@@ -1211,9 +1202,7 @@
         config.state == RESOLVED
 
         and:
-        1 * resolver.resolveGraph(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts())
-        }
+        1 * resolver.resolveGraph(config) >> graphResults
         1 * resolver.getRepositories() >> []
         0 * resolver._
 
@@ -1225,14 +1214,13 @@
         config.state == RESOLVED
 
         and:
-        1 * resolver.resolveArtifacts(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.artifactsResolved(Stub(ResolvedConfiguration), visitedArtifacts())
-        }
+        1 * resolver.resolveArtifacts(config, _) >> DefaultResolverResults.artifactsResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), Stub(ResolvedConfiguration), visitedArtifacts())
         0 * resolver._
     }
 
     def "resolves graph when result requested after resolving task dependencies"() {
         def config = conf("conf")
+        def graphResults = DefaultResolverResults.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts(), Mock(ArtifactResolveState))
 
         given:
         _ * resolutionStrategy.resolveGraphToDetermineTaskDependencies() >> false
@@ -1245,9 +1233,7 @@
         config.state == UNRESOLVED
 
         and:
-        1 * resolver.resolveBuildDependencies(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts())
-        }
+        1 * resolver.resolveBuildDependencies(config) >> graphResults
         0 * resolver._
 
         when:
@@ -1258,18 +1244,15 @@
         config.state == RESOLVED
 
         and:
-        1 * resolver.resolveGraph(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts())
-        }
-        1 * resolver.resolveArtifacts(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.artifactsResolved(Stub(ResolvedConfiguration), visitedArtifacts())
-        }
+        1 * resolver.resolveGraph(config) >> DefaultResolverResults.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts(), Mock(ArtifactResolveState))
+        1 * resolver.resolveArtifacts(config, _) >> DefaultResolverResults.artifactsResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), Stub(ResolvedConfiguration), visitedArtifacts())
         1 * resolver.getRepositories() >> []
         0 * resolver._
     }
 
     def "resolving configuration for results, and then resolving task dependencies required does not re-resolve graph"() {
         def config = conf("conf")
+        def graphResults = DefaultResolverResults.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts(), Mock(ArtifactResolveState))
 
         given:
         _ * resolutionStrategy.resolveGraphToDetermineTaskDependencies() >> graphResolveRequired
@@ -1282,12 +1265,8 @@
         config.state == RESOLVED
 
         and:
-        1 * resolver.resolveGraph(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.graphResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), visitedArtifacts())
-        }
-        1 * resolver.resolveArtifacts(config, _) >> { ConfigurationInternal c, ResolverResults r ->
-            r.artifactsResolved(Stub(ResolvedConfiguration), visitedArtifacts())
-        }
+        1 * resolver.resolveGraph(config) >> graphResults
+        1 * resolver.resolveArtifacts(config, _) >> DefaultResolverResults.artifactsResolved(Stub(ResolutionResult), Stub(ResolvedLocalComponentsResult), Stub(ResolvedConfiguration), visitedArtifacts())
         1 * resolver.getRepositories() >> []
         0 * resolver._
 
@@ -1674,8 +1653,12 @@
         configuration.getDependencies().add(configurationDependency)
 
         then:
-        configuration.dump() == """
-Configuration:  class='class org.gradle.api.internal.artifacts.configurations.DefaultConfiguration'  name='conf'  hashcode='${configuration.hashCode()}'
+        dump(configuration) == """
+Configuration:  class='class org.gradle.api.internal.artifacts.configurations.DefaultConfiguration'  name='conf'  hashcode='${configuration.hashCode()}'  role='Legacy'
+Current Usage:
+\tConsumable - this configuration can be selected by another project as a dependency
+\tResolvable - this configuration can be resolved by this project to a set of files
+\tDeclarable - this configuration can have dependencies added to it
 Local Dependencies:
    DefaultExternalModuleDependency{group='dumpgroup1', name='dumpname1', version='dumpversion1', configuration='default'}
 Local Artifacts:
@@ -1785,7 +1768,7 @@
         usageName               | changeUsage
         'consumable'            | { it.setCanBeConsumed(!it.isCanBeConsumed()) }
         'resolvable'            | { it.setCanBeResolved(!it.isCanBeResolved()) }
-        'declarable against'    | { it.setCanBeDeclaredAgainst(!it.isCanBeDeclaredAgainst()) }
+        'declarable'            | { it.setCanBeDeclared(!it.isCanBeDeclared()) }
     }
 
     def "locking all changes prevents #usageName usage changes"() {
@@ -1804,7 +1787,7 @@
         usageName               | changeUsage
         'consumable'            | { it.setCanBeConsumed(!it.isCanBeConsumed()) }
         'resolvable'            | { it.setCanBeResolved(!it.isCanBeResolved()) }
-        'declarable against'    | { it.setCanBeDeclaredAgainst(!it.isCanBeDeclaredAgainst()) }
+        'declarable'            | { it.setCanBeDeclared(!it.isCanBeDeclared()) }
     }
 
     def 'locking constraints are attached to a configuration and not its children'() {
@@ -1888,7 +1871,7 @@
         new DefaultExternalModuleDependency(group, name, version)
     }
 
-    private DefaultConfiguration conf(String confName = "conf", String projectPath = ":", String buildPath = ":") {
+    private DefaultConfiguration conf(String confName = "conf", String projectPath = ":", String buildPath = ":", ConfigurationRole role = ConfigurationRoles.LEGACY) {
         def domainObjectContext = Stub(DomainObjectContext)
         def build = Path.path(buildPath)
         _ * domainObjectContext.identityPath(_) >> { String p -> build.append(Path.path(projectPath)).child(p) }
@@ -1914,7 +1897,7 @@
             new TestBuildOperationExecutor(),
             publishArtifactNotationParser,
             immutableAttributesFactory,
-            Stub(DocumentationRegistry),
+            new ResolveExceptionContextualizer(Mock(DomainObjectContext), Mock(DocumentationRegistry)),
             userCodeApplicationContext,
             projectStateRegistry,
             Stub(WorkerThreadRegistry),
@@ -1922,7 +1905,7 @@
             calculatedValueContainerFactory,
             TestFiles.taskDependencyFactory()
         )
-        defaultConfigurationFactory.create(confName, configurationsProvider, Factories.constant(resolutionStrategy), rootComponentMetadataBuilder, ConfigurationRoles.LEGACY, false)
+        defaultConfigurationFactory.create(confName, configurationsProvider, Factories.constant(resolutionStrategy), rootComponentMetadataBuilder, role, false)
     }
 
     private DefaultPublishArtifact artifact(String name) {
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/InspectableConfigurationFixture.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/InspectableConfigurationFixture.groovy
new file mode 100644
index 0000000..330edc1
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/InspectableConfigurationFixture.groovy
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2023 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.api.internal.artifacts.configurations
+
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.PublishArtifact
+
+/**
+ * A fixture containing methods useful for inspecting {@link Configuration}s and the {@link DefaultConfigurationContainer}s that create them.
+ */
+trait InspectableConfigurationFixture {
+    /**
+     * Builds a formatted representation of a {@link Configuration}.
+     *
+     * @param the Configuration to be inspected
+     * @return a formatted representation of the Configuration
+     */
+    String dump(Configuration configuration) {
+        StringBuilder reply = new StringBuilder()
+
+        reply.append("\nConfiguration:")
+        reply.append("  class='").append(configuration.getClass()).append("'")
+        reply.append("  name='").append(configuration.getName()).append("'")
+        reply.append("  hashcode='").append(configuration.hashCode()).append("'")
+
+        if (configuration instanceof DefaultConfiguration) {
+            reply.append("  role='").append(configuration.roleAtCreation).append("'")
+            String roleDesc = UsageDescriber.describeUsage(configuration.isCanBeConsumed(), configuration.isCanBeResolved(), configuration.isCanBeDeclared(),
+                    configuration.isDeprecatedForConsumption(), configuration.isDeprecatedForResolution(), configuration.isDeprecatedForDeclarationAgainst())
+            reply.append("\nCurrent Usage:\n").append(roleDesc)
+        }
+
+        reply.append("\nLocal Dependencies:")
+        if (configuration.getDependencies().size() > 0) {
+            configuration.getDependencies().each { Dependency d ->
+                reply.append("\n   ").append(d)
+            }
+        } else {
+            reply.append("\n   none")
+        }
+
+        reply.append("\nLocal Artifacts:")
+        if (configuration.getArtifacts().size() > 0) {
+            configuration.getArtifacts().each { PublishArtifact a ->
+                reply.append("\n   ").append(a)
+            }
+        } else {
+            reply.append("\n   none")
+        }
+
+        reply.append("\nAll Dependencies:")
+        if (configuration.getAllDependencies().size() > 0) {
+            configuration.getAllDependencies().each { Dependency d ->
+                reply.append("\n   ").append(d)
+            }
+        } else {
+            reply.append("\n   none")
+        }
+
+
+        reply.append("\nAll Artifacts:")
+        if (configuration.getAllArtifacts().size() > 0) {
+            configuration.getAllArtifacts().each { PublishArtifact a ->
+                reply.append("\n   ").append(a)
+            }
+        } else {
+            reply.append("\n   none")
+        }
+
+        return reply.toString()
+    }
+
+    /**
+     * Build a formatted representation of all {@link Configuration}s in a {@link DefaultConfigurationContainer}.
+     * <p>
+     * Configuration(s) being toStringed are likely derivations of {@link DefaultConfiguration}.
+     *
+     * @param the Configuration to be inspected
+     * @return a formatted representation of all Configurations in the container
+     */
+    String dump(DefaultConfigurationContainer container) {
+        StringBuilder reply = new StringBuilder()
+
+        reply.append("Configuration of type: ").append(container.getTypeDisplayName())
+        Collection<? extends Configuration> configs = container.getAll()
+        configs.each { Configuration c ->
+            reply.append("\n  ").append(c.toString())
+        }
+
+        return reply.toString()
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/RoleCheckerSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/RoleCheckerSpec.groovy
index bad7df3..1a4374c 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/RoleCheckerSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/RoleCheckerSpec.groovy
@@ -27,24 +27,22 @@
         def configuration = Mock(ConfigurationInternal)
         configuration.isCanBeConsumed() >> consumable
         configuration.isCanBeResolved() >> resolvable
-        configuration.isCanBeDeclaredAgainst() >> declarableAgainst
+        configuration.isCanBeDeclared() >> declarable
         configuration.isDeprecatedForConsumption() >> consumptionDeprecated
         configuration.isDeprecatedForResolution() >> resolutionDeprecated
         configuration.isDeprecatedForDeclarationAgainst() >> declarationAgainstDeprecated
 
-        and:
-        def role = ConfigurationRole.forUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated)
-
         expect:
         RoleChecker.isUsageConsistentWithRole(configuration, role)
 
-        where: // These are just a sample, not all possible combinations
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
-        true        | true          | true              | false                 | false                 | false
-        true        | false         | false             | false                 | false                 | false
-        false       | true          | false             | false                 | false                 | false
-        false       | false         | true              | false                 | false                 | false
-        true        | true          | true              | true                  | true                  | true
+        where: // These are just a sample, not all possibilities
+        role                                                                                || consumable  | resolvable    | declarable | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
+        ConfigurationRoles.LEGACY                                                           || true        | true          | true       | false                 | false                 | false
+        ConfigurationRoles.CONSUMABLE                                                       || true        | false         | false      | false                 | false                 | false
+        ConfigurationRoles.RESOLVABLE_BUCKET                                                || false       | true          | true       | false                 | false                 | false
+        ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET                          || true        | true          | true       | true                  | false                 | false
+        ConfigurationRolesForMigration.LEGACY_TO_CONSUMABLE                                 || true        | true          | true       | false                 | true                  | true
+        ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE                      || false       | true          | true       | false                 | false                 | true
     }
 
     def "can detect if usage is not consistent with role"() {
@@ -52,23 +50,22 @@
         def configuration = Mock(ConfigurationInternal)
         configuration.isCanBeConsumed() >> true
         configuration.isCanBeResolved() >> false
-        configuration.isCanBeDeclaredAgainst() >> false
+        configuration.isCanBeDeclared() >> false
         configuration.isDeprecatedForConsumption() >> true
         configuration.isDeprecatedForResolution() >> false
         configuration.isDeprecatedForDeclarationAgainst() >> false
 
-        and:
-        def role = ConfigurationRole.forUsage(consumable, resolvable, declarableAgainst, consumptionDeprecated, resolutionDeprecated, declarationAgainstDeprecated)
-
         expect:
         !RoleChecker.isUsageConsistentWithRole(configuration, role)
 
-        where: // These are just a sample, not all possible combinations
-        consumable  | resolvable    | declarableAgainst | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
-        true        | true          | true              | false                 | false                 | false
-        true        | false         | false             | false                 | false                 | false
-        false       | true          | false             | false                 | false                 | false
-        true        | false         | true              | false                 | false                 | false
+        where: // These are just a sample, not all possibilities
+        role                                                                                || consumable  | resolvable    | declarable | consumptionDeprecated | resolutionDeprecated  | declarationAgainstDeprecated
+        ConfigurationRoles.LEGACY                                                           || false       | true          | true              | false                 | false                 | false
+        ConfigurationRoles.CONSUMABLE                                              || true        | true          | false             | false                 | false                 | false
+        ConfigurationRoles.RESOLVABLE_BUCKET                                       || false       | true          | false             | false                 | false                 | false
+        ConfigurationRolesForMigration.LEGACY_TO_RESOLVABLE_BUCKET                 || true        | true          | true              | false                 | false                 | false
+        ConfigurationRolesForMigration.LEGACY_TO_CONSUMABLE                        || true        | true          | true              | false                 | false                 | true
+        ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE    || false       | true          | true              | false                 | false                 | false
     }
 
     def "can assert if usage is consistent with role"() {
@@ -77,16 +74,16 @@
         configuration.getName() >> 'custom'
         configuration.isCanBeConsumed() >> true
         configuration.isCanBeResolved() >> false
-        configuration.isCanBeDeclaredAgainst() >> false
+        configuration.isCanBeDeclared() >> false
         configuration.isDeprecatedForConsumption() >> false
         configuration.isDeprecatedForResolution() >> false
         configuration.isDeprecatedForDeclarationAgainst() >> false
 
         when:
-        RoleChecker.assertIsInRole(configuration, ConfigurationRoles.INTENDED_RESOLVABLE)
+        RoleChecker.assertIsInRole(configuration, ConfigurationRoles.RESOLVABLE)
 
         then:
         GradleException e = thrown()
-        e.message.contains("Usage for configuration: custom is not consistent with the role: Intended Resolvable.")
+        e.message.contains("Usage for configuration: custom is not consistent with the role: Resolvable.")
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/RoleDescriberSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/RoleDescriberSpec.groovy
deleted file mode 100644
index db51466..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/RoleDescriberSpec.groovy
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2022 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.api.internal.artifacts.configurations
-
-import spock.lang.Specification
-
-import static org.gradle.api.internal.artifacts.configurations.ConfigurationRole.RoleDescriber
-
-class RoleDescriberSpec extends Specification {
-    def "can describe usage for role"() {
-        given:
-        def role = ConfigurationRole.forUsage(false, true, true, false, true, false)
-
-        expect:
-        RoleDescriber.describeRole(role) == "\tResolvable - this configuration can be resolved by this project to a set of files (but this behavior is marked deprecated)\n" +
-                "\tDeclarable Against - this configuration can have dependencies added to it"
-    }
-
-    def "can describe if usage for role which allows nothing"() {
-        given:
-        def role = ConfigurationRole.forUsage(false, false, false, false, false, false)
-
-        expect:
-        RoleDescriber.describeRole(role) == "\tThis configuration does not allow any usage"
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/UsageDescriberSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/UsageDescriberSpec.groovy
new file mode 100644
index 0000000..22b6622
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/configurations/UsageDescriberSpec.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 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.api.internal.artifacts.configurations
+
+import spock.lang.Specification
+
+class UsageDescriberSpec extends Specification {
+    def "can describe usage for role"() {
+        given:
+        def role = ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE
+
+        expect:
+        UsageDescriber.describeRole(role) == "\tResolvable - this configuration can be resolved by this project to a set of files\n" +
+                "\tDeclarable - this configuration can have dependencies added to it (but this behavior is marked deprecated)"
+    }
+
+    def "can describe usage for role which allows nothing"() {
+        given:
+        def role = new DefaultConfigurationRole("test", false, false, false, false, false, false)
+
+        expect:
+        UsageDescriber.describeRole(role) == "\tThis configuration does not allow any usage"
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsersTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsersTest.groovy
index 6597821..06e84c7 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsersTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/ComponentSelectorParsersTest.groovy
@@ -17,9 +17,9 @@
 package org.gradle.api.internal.artifacts.dsl
 
 import org.gradle.api.InvalidUserDataException
-import org.gradle.api.artifacts.component.BuildIdentifier
 import org.gradle.api.artifacts.component.ModuleComponentSelector
 import org.gradle.api.artifacts.component.ProjectComponentSelector
+import org.gradle.api.internal.artifacts.DefaultBuildIdentifier
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
 import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint
@@ -113,8 +113,7 @@
 
     def "understands project input"() {
         when:
-        def buildId = Stub(BuildIdentifier)
-        buildId.name >> "build"
+        def buildId = new DefaultBuildIdentifier(Path.path(":build"))
         def projectState = Mock(ProjectState) {
             getComponentIdentifier() >> new DefaultProjectComponentIdentifier(buildId, Path.path(":id:bar"), Path.path(":bar"), "name")
         }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
index 79b1d1c5..f34a961 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/DefaultArtifactHandlerTest.groovy
@@ -39,7 +39,7 @@
     private DefaultArtifactHandler artifactHandler = TestUtil.instantiatorFactory().decorateLenient().newInstance(DefaultArtifactHandler, configurationContainerStub, artifactFactoryStub)
 
     void setup() {
-        configurationMock.isDeclarableAgainstByExtension() >> true
+        configurationMock.isDeclarableByExtension() >> true
         configurationContainerStub.findByName(TEST_CONF_NAME) >> configurationMock
         configurationContainerStub.getByName(TEST_CONF_NAME) >> configurationMock
         configurationMock.artifacts >> artifactsMock
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/GradlePluginVariantsSupportTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/GradlePluginVariantsSupportTest.groovy
index 6d6132b..066201e 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/GradlePluginVariantsSupportTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/dsl/dependencies/GradlePluginVariantsSupportTest.groovy
@@ -45,7 +45,7 @@
         def producer = versionAttribute('7.0')
 
         then:
-        accepts == (schema.matcher().matches([producer], consumer, null, ep) == [producer])
+        accepts == (schema.matcher().matches([producer], consumer, ep) == [producer])
         accepts == schema.matcher().isMatching(producer, consumer)
 
         where:
@@ -74,7 +74,7 @@
         ]
 
         then:
-        schema.matcher().matches(producer, consumer, null, ep) == [versionAttribute('7.0')]
+        schema.matcher().matches(producer, consumer, ep) == [versionAttribute('7.0')]
 
     }
 
@@ -92,7 +92,7 @@
         ]
 
         then:
-        schema.matcher().matches(producer, consumer, null, ep) == [versionAttribute('7.1')]
+        schema.matcher().matches(producer, consumer, ep) == [versionAttribute('7.1')]
     }
 
     def "fails to select one candidate if there is no clear preference"() {
@@ -107,7 +107,7 @@
         ]
 
         then:
-        schema.matcher().matches(producer, consumer, null, ep) == [versionAttribute('7.1'), versionAttribute('7.1')]
+        schema.matcher().matches(producer, consumer, ep) == [versionAttribute('7.1'), versionAttribute('7.1')]
     }
 
     private AttributeContainerInternal versionAttribute(String version) {
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy
index 90b430a..d12df9d 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/CacheLayoutTest.groovy
@@ -53,7 +53,8 @@
         CacheLayout cacheLayout = CacheLayout.META_DATA
 
         then:
-        def expectedVersion = 100
+        // If you change the value here, update the docs in dependency_resolution.adoc#sub:cache_copy
+        def expectedVersion = 106
         cacheLayout.name == 'metadata'
         cacheLayout.key == "metadata-2.${expectedVersion}"
         cacheLayout.version == CacheVersion.parse("2.${expectedVersion}")
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfigurationTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfigurationTest.groovy
index 024f19d..5f6c8b6 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfigurationTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfigurationTest.groovy
@@ -28,7 +28,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedFileDependencyResults
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResults
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResultsLoader
-import org.gradle.api.internal.artifacts.transform.ArtifactTransforms
+import org.gradle.api.internal.artifacts.transform.VariantSelectorFactory
 import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.api.specs.Spec
 import org.gradle.internal.operations.BuildOperationExecutor
@@ -36,7 +36,7 @@
 import spock.lang.Specification
 
 class DefaultLenientConfigurationTest extends Specification {
-    def transforms = Stub(ArtifactTransforms)
+    def transforms = Stub(VariantSelectorFactory)
     def transientConfigurationResults = Mock(TransientConfigurationResults)
     def resultsLoader = Mock(TransientConfigurationResultsLoader)
     def artifactsResults = Stub(VisitedArtifactsResults)
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingConfigurationResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingConfigurationResolverTest.groovy
index a5c1ad8..5d4df01 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingConfigurationResolverTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ErrorHandlingConfigurationResolverTest.groovy
@@ -18,12 +18,16 @@
 import org.gradle.api.artifacts.LenientConfiguration
 import org.gradle.api.artifacts.ResolveException
 import org.gradle.api.artifacts.ResolvedConfiguration
-import org.gradle.api.artifacts.result.ResolutionResult
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.DomainObjectContext
 import org.gradle.api.internal.artifacts.ConfigurationResolver
 import org.gradle.api.internal.artifacts.DefaultResolverResults
+import org.gradle.api.internal.artifacts.ResolveExceptionContextualizer
+import org.gradle.api.internal.artifacts.ResolverResults
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.VisitedArtifactSet
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.projectresult.ResolvedLocalComponentsResult
+import org.gradle.api.internal.artifacts.result.ResolutionResultInternal
 import org.gradle.api.specs.Specs
 import spock.lang.Specification
 
@@ -32,14 +36,17 @@
 class ErrorHandlingConfigurationResolverTest extends Specification {
     private delegate = Mock(ConfigurationResolver)
     private resolvedConfiguration = Mock(ResolvedConfiguration)
-    private resolutionResult = Mock(ResolutionResult)
+    private resolutionResult = Mock(ResolutionResultInternal)
     private projectConfigResult = Mock(ResolvedLocalComponentsResult)
     private visitedArtifactSet = Mock(VisitedArtifactSet)
-    private context = Mock(ConfigurationInternal) {
-        maybeAddContext(_) >> { args -> args[0] }
+    private artifactResolveState = Mock(ArtifactResolveState)
+    private context = Mock(ConfigurationInternal)
+    private contextualizer =  new ResolveExceptionContextualizer(Mock(DomainObjectContext), Mock(DocumentationRegistry))
+    private resolver = new ErrorHandlingConfigurationResolver(delegate, contextualizer);
+
+    def delegateResults = Mock(ResolverResults) {
+        getResolvedConfiguration() >> resolvedConfiguration
     }
-    private results = new DefaultResolverResults()
-    private resolver = new ErrorHandlingConfigurationResolver(delegate);
 
     def setup() {
         context.displayName >> "resolve context 'foo'"
@@ -47,41 +54,37 @@
 
     void "delegates to backing service to resolve build dependencies"() {
         when:
-        resolver.resolveBuildDependencies(context, results)
+        def results = resolver.resolveBuildDependencies(context)
 
         then:
-        1 * delegate.resolveBuildDependencies(context, results) >> {
-            results.graphResolved(resolutionResult, projectConfigResult, visitedArtifactSet)
-        }
+        1 * delegate.resolveBuildDependencies(context) >> delegateResults
+        results == delegateResults
     }
 
     void "delegates to backing service to resolve graph"() {
         when:
-        resolver.resolveGraph(context, results)
+        resolver.resolveGraph(context)
 
         then:
-        1 * delegate.resolveGraph(context, results) >> {
-            results.graphResolved(resolutionResult, projectConfigResult, visitedArtifactSet)
-        }
+        1 * delegate.resolveGraph(context) >> delegateResults
     }
 
     void "delegates to backing service to resolve artifacts"() {
         when:
-        resolver.resolveArtifacts(context, results)
+        ResolverResults graphResults = Mock()
+        resolver.resolveArtifacts(context, graphResults)
 
         then:
-        1 * delegate.resolveArtifacts(context, results) >> {
-            results.artifactsResolved(Stub(ResolvedConfiguration), Stub(VisitedArtifactSet))
-        }
+        1 * delegate.resolveArtifacts(context, _) >> delegateResults
     }
 
     void "wraps build dependency resolve failures"() {
         given:
         def failure = new RuntimeException()
-        delegate.resolveBuildDependencies(context, results) >> { throw failure }
+        delegate.resolveBuildDependencies(context) >> { throw failure }
 
         when:
-        resolver.resolveBuildDependencies(context, results)
+        def results = resolver.resolveBuildDependencies(context)
 
         then:
         results.resolvedConfiguration.hasError()
@@ -96,10 +99,10 @@
     void "wraps graph resolve failures"() {
         given:
         def failure = new RuntimeException()
-        delegate.resolveGraph(context, results) >> { throw failure }
+        delegate.resolveGraph(context) >> { throw failure }
 
         when:
-        resolver.resolveGraph(context, results)
+        def results = resolver.resolveGraph(context)
 
         then:
         results.resolvedConfiguration.hasError()
@@ -113,11 +116,12 @@
 
     void "wraps artifact resolve failures"() {
         given:
+        ResolverResults graphResults = Mock()
         def failure = new RuntimeException()
-        delegate.resolveArtifacts(context, results) >> { throw failure }
+        delegate.resolveArtifacts(context, graphResults) >> { throw failure }
 
         when:
-        resolver.resolveArtifacts(context, results)
+        def results = resolver.resolveArtifacts(context, graphResults)
 
         then:
         results.resolvedConfiguration.hasError()
@@ -131,6 +135,7 @@
 
     void "wraps exceptions thrown by resolved configuration"() {
         given:
+        ResolverResults graphResults = Mock()
         def failure = new RuntimeException()
 
         resolvedConfiguration.rethrowFailure() >> { throw failure }
@@ -140,12 +145,10 @@
         resolvedConfiguration.getResolvedArtifacts() >> { throw failure }
         resolvedConfiguration.getLenientConfiguration() >> { throw failure }
 
-        delegate.resolveGraph(context, results) >> { results.graphResolved(resolutionResult, projectConfigResult, visitedArtifactSet) }
-        delegate.resolveArtifacts(context, results) >> { results.artifactsResolved(resolvedConfiguration, visitedArtifactSet) }
+        delegate.resolveArtifacts(context, graphResults) >> delegateResults
 
         when:
-        resolver.resolveGraph(context, results)
-        resolver.resolveArtifacts(context, results)
+        def results = resolver.resolveArtifacts(context, graphResults)
 
         then:
         def result = results.resolvedConfiguration
@@ -160,6 +163,7 @@
 
     void "wraps exceptions thrown by resolved lenient configuration"() {
         given:
+        ResolverResults graphResults = Mock()
         def failure = new RuntimeException()
         def lenientConfiguration = Stub(LenientConfiguration)
 
@@ -169,12 +173,10 @@
         lenientConfiguration.getArtifacts(_) >> { throw failure }
         lenientConfiguration.getUnresolvedModuleDependencies() >> { throw failure }
 
-        delegate.resolveGraph(context, results) >> { results.graphResolved(resolutionResult, projectConfigResult, visitedArtifactSet) }
-        delegate.resolveArtifacts(context, results) >> { results.artifactsResolved(resolvedConfiguration, visitedArtifactSet) }
+        delegate.resolveArtifacts(context, _) >> DefaultResolverResults.artifactsResolved(resolutionResult, projectConfigResult, resolvedConfiguration, visitedArtifactSet)
 
         when:
-        resolver.resolveGraph(context, results)
-        resolver.resolveArtifacts(context, results)
+        def results = resolver.resolveArtifacts(context, graphResults)
 
         then:
         def result = results.resolvedConfiguration.lenientConfiguration
@@ -187,18 +189,19 @@
 
     void "wraps exceptions thrown by resolution result"() {
         given:
+        def graphResult = DefaultResolverResults.graphResolved(resolutionResult, projectConfigResult, visitedArtifactSet, artifactResolveState)
         def failure = new RuntimeException()
 
         resolutionResult.root >> {
             throw failure
         }
 
-        delegate.resolveGraph(context, results) >> { results.graphResolved(resolutionResult, projectConfigResult, visitedArtifactSet) }
-        delegate.resolveArtifacts(context, results) >> { results.artifactsResolved(resolvedConfiguration, visitedArtifactSet) }
+        delegate.resolveGraph(context) >> graphResult
+        delegate.resolveArtifacts(context, _) >> delegateResults
 
         when:
-        resolver.resolveGraph(context, results)
-        resolver.resolveArtifacts(context, results)
+        def results = resolver.resolveGraph(context)
+        results = resolver.resolveArtifacts(context, results)
 
         then:
         def result = results.resolutionResult
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortCircuitEmptyConfigurationResolverSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortCircuitEmptyConfigurationResolverSpec.groovy
index 2e45c3f..2461f0a 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortCircuitEmptyConfigurationResolverSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ShortCircuitEmptyConfigurationResolverSpec.groovy
@@ -16,15 +16,13 @@
 package org.gradle.api.internal.artifacts.ivyservice
 
 import org.gradle.api.artifacts.Dependency
-import org.gradle.api.artifacts.DependencyConstraintSet
-import org.gradle.api.artifacts.DependencySet
 import org.gradle.api.artifacts.component.BuildIdentifier
 import org.gradle.api.internal.artifacts.ConfigurationResolver
 import org.gradle.api.internal.artifacts.DefaultImmutableModuleIdentifierFactory
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
-import org.gradle.api.internal.artifacts.DefaultResolverResults
+import org.gradle.api.internal.artifacts.ResolverResults
+import org.gradle.api.internal.artifacts.ResolveContext
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
 import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyLockingProvider
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyLockingState
@@ -37,11 +35,8 @@
 class ShortCircuitEmptyConfigurationResolverSpec extends Specification {
 
     def delegate = Mock(ConfigurationResolver)
-    def configuration = Stub(ConfigurationInternal)
-    def dependencies = Stub(DependencySet)
-    def dependencyConstraints = Stub(DependencyConstraintSet)
+    def resolveContext = Stub(ResolveContext)
     def componentIdentifierFactory = Mock(ComponentIdentifierFactory)
-    def results = new DefaultResolverResults()
     def moduleIdentifierFactory = new DefaultImmutableModuleIdentifierFactory()
 
     def dependencyResolver = new ShortCircuitEmptyConfigurationResolver(delegate, componentIdentifierFactory, moduleIdentifierFactory, Stub(BuildIdentifier))
@@ -51,13 +46,10 @@
         def artifactVisitor = Mock(ArtifactVisitor)
 
         given:
-        dependencies.isEmpty() >> true
-        dependencyConstraints.isEmpty() >> true
-        configuration.getAllDependencies() >> dependencies
-        configuration.getAllDependencyConstraints() >> dependencyConstraints
+        resolveContext.hasDependencies() >> false
 
         when:
-        dependencyResolver.resolveBuildDependencies(configuration, results)
+        def results = dependencyResolver.resolveBuildDependencies(resolveContext)
 
         then:
         def localComponentsResult = results.resolvedLocalComponents
@@ -79,13 +71,10 @@
         def artifactVisitor = Mock(ArtifactVisitor)
 
         given:
-        dependencies.isEmpty() >> true
-        dependencyConstraints.isEmpty() >> true
-        configuration.getAllDependencies() >> dependencies
-        configuration.getAllDependencyConstraints() >> dependencyConstraints
+        resolveContext.hasDependencies() >> false
 
         when:
-        dependencyResolver.resolveGraph(configuration, results)
+        def results = dependencyResolver.resolveGraph(resolveContext)
 
         then:
         def result = results.resolutionResult
@@ -109,14 +98,11 @@
 
     def "returns empty result when no dependencies"() {
         given:
-        dependencies.isEmpty() >> true
-        dependencyConstraints.isEmpty() >> true
-        configuration.getAllDependencies() >> dependencies
-        configuration.getAllDependencyConstraints() >> dependencyConstraints
+        resolveContext.hasDependencies() >> false
 
         when:
-        dependencyResolver.resolveGraph(configuration, results)
-        dependencyResolver.resolveArtifacts(configuration, results)
+        def results = dependencyResolver.resolveGraph(resolveContext)
+        results = dependencyResolver.resolveArtifacts(resolveContext, results)
 
         then:
         def resolvedConfig = results.resolvedConfiguration
@@ -135,15 +121,12 @@
         given:
         ResolutionStrategyInternal resolutionStrategy = Mock()
 
-        configuration.name >> 'lockedConf'
-        configuration.resolutionStrategy >> resolutionStrategy
-        dependencies.isEmpty() >> true
-        dependencyConstraints.isEmpty() >> true
-        configuration.getAllDependencies() >> dependencies
-        configuration.getAllDependencyConstraints() >> dependencyConstraints
+        resolveContext.name >> 'lockedConf'
+        resolveContext.resolutionStrategy >> resolutionStrategy
+        resolveContext.hasDependencies() >> false
 
         when:
-        dependencyResolver.resolveBuildDependencies(configuration, results)
+        dependencyResolver.resolveBuildDependencies(resolveContext)
 
         then:
 
@@ -156,15 +139,12 @@
         DependencyLockingProvider lockingProvider = Mock()
         DependencyLockingState lockingState = Mock()
 
-        configuration.name >> 'lockedConf'
-        configuration.resolutionStrategy >> resolutionStrategy
-        dependencies.isEmpty() >> true
-        dependencyConstraints.isEmpty() >> true
-        configuration.getAllDependencies() >> dependencies
-        configuration.getAllDependencyConstraints() >> dependencyConstraints
+        resolveContext.name >> 'lockedConf'
+        resolveContext.resolutionStrategy >> resolutionStrategy
+        resolveContext.hasDependencies() >> false
 
         when:
-        dependencyResolver.resolveGraph(configuration, results)
+        dependencyResolver.resolveGraph(resolveContext)
 
         then:
 
@@ -180,16 +160,14 @@
         ResolutionStrategyInternal resolutionStrategy = Mock()
         DependencyLockingProvider lockingProvider = Mock()
         DependencyLockingState lockingState = Mock()
+        ResolverResults delegateResults = Mock()
 
-        configuration.name >> 'lockedConf'
-        configuration.resolutionStrategy >> resolutionStrategy
-        dependencies.isEmpty() >> true
-        dependencyConstraints.isEmpty() >> true
-        configuration.getAllDependencies() >> dependencies
-        configuration.getAllDependencyConstraints() >> dependencyConstraints
+        resolveContext.name >> 'lockedConf'
+        resolveContext.resolutionStrategy >> resolutionStrategy
+        resolveContext.hasDependencies() >> false
 
         when:
-        dependencyResolver.resolveGraph(configuration, results)
+        def results = dependencyResolver.resolveGraph(resolveContext)
 
         then:
         1 * resolutionStrategy.dependencyLockingEnabled >> true
@@ -197,42 +175,49 @@
         1 * lockingProvider.loadLockState('lockedConf') >> lockingState
         1 * lockingState.mustValidateLockState() >> true
         1 * lockingState.lockedDependencies >> [DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId('org', 'foo'), '1.0')]
-        1 * delegate.resolveGraph(configuration, results)
+        1 * delegate.resolveGraph(resolveContext) >> delegateResults
+        results == delegateResults
     }
 
     def "delegates to backing service to resolve build dependencies when there are one or more dependencies"() {
         given:
-        dependencies.isEmpty() >> false
-        configuration.getAllDependencies() >> dependencies
+        ResolverResults delegateResults = Mock()
+        resolveContext.hasDependencies() >> true
 
         when:
-        dependencyResolver.resolveBuildDependencies(configuration, results)
+        def results = dependencyResolver.resolveBuildDependencies(resolveContext)
 
         then:
-        1 * delegate.resolveBuildDependencies(configuration, results)
+        1 * delegate.resolveBuildDependencies(resolveContext) >> delegateResults
+        results == delegateResults
     }
 
     def "delegates to backing service to resolve graph when there are one or more dependencies"() {
         given:
-        dependencies.isEmpty() >> false
-        configuration.getAllDependencies() >> dependencies
+        ResolverResults delegateResults = Mock()
+        resolveContext.hasDependencies() >> true
 
         when:
-        dependencyResolver.resolveGraph(configuration, results)
+        def results = dependencyResolver.resolveGraph(resolveContext)
 
         then:
-        1 * delegate.resolveGraph(configuration, results)
+        1 * delegate.resolveGraph(resolveContext) >> delegateResults
+        results == delegateResults
     }
 
     def "delegates to backing service to resolve artifacts when there are one or more dependencies"() {
         given:
-        dependencies.isEmpty() >> false
-        configuration.getAllDependencies() >> dependencies
+        def graphResults = Mock(ResolverResults) {
+            getArtifactResolveState() >> Mock(ArtifactResolveState)
+        }
+        ResolverResults delegateResults = Mock()
+        resolveContext.hasDependencies() >> true
 
         when:
-        dependencyResolver.resolveArtifacts(configuration, results)
+        def results = dependencyResolver.resolveArtifacts(resolveContext, graphResults)
 
         then:
-        1 * delegate.resolveArtifacts(configuration, results)
+        1 * delegate.resolveArtifacts(resolveContext, graphResults) >> delegateResults
+        results == delegateResults
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachedMetadataProviderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachedMetadataProviderTest.groovy
index 086d8e5..36eeac6 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachedMetadataProviderTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachedMetadataProviderTest.groovy
@@ -16,25 +16,25 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.ivyresolve
 
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState
 import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata
 import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult
 import spock.lang.Specification
 import spock.lang.Subject
 
 class CachedMetadataProviderTest extends Specification {
 
-    BuildableModuleComponentMetaDataResolveResult cachedResult = Mock()
+    def cachedResult = Stub(BuildableModuleComponentMetaDataResolveResult)
+    def componentState = Stub(ModuleComponentGraphResolveState)
     @Subject
     CachedMetadataProvider provider
 
-
     def 'verifies that metadata was provided when state is Resolved'() {
         given:
         cachedResult.state >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
+        cachedResult.metaData >> componentState
         provider = new CachedMetadataProvider(cachedResult)
 
-
         expect:
         provider.usable
         provider.componentMetadata
@@ -56,10 +56,10 @@
     def 'returns IvyModuleDescriptor when available'() {
         given:
         cachedResult.state >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
-        cachedResult.metaData >> Mock(IvyModuleResolveMetadata)
+        cachedResult.metaData >> componentState
+        componentState.moduleResolveMetadata >> Mock(IvyModuleResolveMetadata)
         provider = new CachedMetadataProvider(cachedResult)
 
-
         expect:
         provider.ivyModuleDescriptor
     }
@@ -67,10 +67,9 @@
     def 'returns null for IvyModuleDescriptor when not available'() {
         given:
         cachedResult.state >> BuildableModuleComponentMetaDataResolveResult.State.Resolved
-        cachedResult.metaData >> Mock(ModuleComponentResolveMetadata)
+        cachedResult.metaData >> componentState
         provider = new CachedMetadataProvider(cachedResult)
 
-
         expect:
         !provider.ivyModuleDescriptor
     }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepositoryTest.groovy
index 1f8ae7d..789585e 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepositoryTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/CachingModuleComponentRepositoryTest.groovy
@@ -18,6 +18,7 @@
 
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier
 import org.gradle.api.internal.artifacts.ComponentMetadataProcessor
+import org.gradle.api.internal.artifacts.DependencyManagementTestUtil
 import org.gradle.api.internal.artifacts.configurations.dynamicversion.CachePolicy
 import org.gradle.api.internal.artifacts.configurations.dynamicversion.Expiry
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.AbstractModuleMetadataCache
@@ -31,6 +32,7 @@
 import org.gradle.api.internal.component.ArtifactType
 import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata
 import org.gradle.internal.component.external.model.ModuleDependencyMetadata
 import org.gradle.internal.component.model.ComponentArtifactMetadata
@@ -63,7 +65,8 @@
     def metadataProcessor = Stub(ComponentMetadataProcessor)
     def listener = Stub(ChangingValueDependencyResolutionListener)
     def caches = new ModuleRepositoryCaches(moduleResolutionCache, moduleDescriptorCache, moduleArtifactsCache, artifactAtRepositoryCache)
-    def repo = new CachingModuleComponentRepository(realRepo, caches, cachePolicy, Stub(BuildCommencedTimeProvider), metadataProcessor, listener)
+    def resolveStateFactory = DependencyManagementTestUtil.modelGraphResolveFactory()
+    def repo = new CachingModuleComponentRepository(realRepo, caches, resolveStateFactory, cachePolicy, Stub(BuildCommencedTimeProvider), metadataProcessor, listener)
 
     def "artifact last modified date is cached - lastModified = #lastModified"() {
         given:
@@ -119,8 +122,8 @@
         repo.localAccess.resolveComponentMetaData(componentId, prescribedMetaData, result)
 
         then:
-        realLocalAccess.resolveComponentMetaData(componentId, prescribedMetaData, result) >> {
-            result.resolved(Mock(ModuleComponentResolveMetadata))
+        1 * realLocalAccess.resolveComponentMetaData(componentId, prescribedMetaData, _) >> { id, m, r ->
+            r.resolved(Stub(ModuleComponentResolveMetadata))
         }
         0 * _
     }
@@ -191,8 +194,10 @@
             isMustCheck() >> mustRefreshChangingModule
         }
         moduleDescriptorCache.getCachedModuleDescriptor(_, module) >> Stub(ModuleMetadataCache.CachedMetadata) {
-            getProcessedMetadata(_) >> Stub(ModuleComponentResolveMetadata) {
-                isChanging() >> true
+            getProcessedMetadata(_) >> Stub(ModuleComponentGraphResolveState) {
+                getMetadata() >> Stub(ModuleComponentResolveMetadata) {
+                    isChanging() >> true
+                }
             }
             getAge() >> Duration.ofMillis(100)
         }
@@ -221,7 +226,7 @@
             isMustCheck() >> mustRefreshModule
         }
         moduleDescriptorCache.getCachedModuleDescriptor(_, module) >> Stub(ModuleMetadataCache.CachedMetadata) {
-            getProcessedMetadata(_) >> Stub(ModuleComponentResolveMetadata)
+            getProcessedMetadata(_) >> Stub(ModuleComponentGraphResolveState)
             getAge() >> Duration.ofMillis(100)
         }
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProviderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProviderTest.groovy
index b99021f..283e3ed 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProviderTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultMetadataProviderTest.groovy
@@ -32,6 +32,7 @@
 import org.gradle.internal.action.DefaultConfigurableRule
 import org.gradle.internal.action.DefaultConfigurableRules
 import org.gradle.internal.action.InstantiatingAction
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata
 import org.gradle.internal.component.external.model.ivy.IvyModuleResolveMetadata
 import org.gradle.internal.resolve.caching.ComponentMetadataSupplierRuleExecutor
@@ -52,6 +53,9 @@
         getVersion() >> "1.2"
     }
     def metaData = Stub(ModuleComponentResolveMetadata)
+    def componentState = Stub(ModuleComponentGraphResolveState) {
+        getModuleResolveMetadata() >> metaData
+    }
     def resolveState = Mock(ModuleComponentResolveState)
     def metadataProvider = new DefaultMetadataProvider(resolveState)
     def cachePolicy = new DefaultCachePolicy()
@@ -71,7 +75,7 @@
         then:
         1 * resolveState.resolve() >> {
             def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
-            result.resolved(metaData)
+            result.resolved(componentState)
             return result
         }
         0 * resolveState.resolve()
@@ -81,7 +85,7 @@
         given:
         resolveState.resolve() >> {
             def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
-            result.resolved(metaData)
+            result.resolved(componentState)
             return result
         }
 
@@ -108,7 +112,7 @@
         resolveState.id >> Stub(ModuleComponentIdentifier)
         resolveState.resolve() >> {
             def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
-            result.resolved(metaData)
+            result.resolved(componentState)
             return result
         }
 
@@ -129,9 +133,12 @@
         metaData.branch >> "branchValue"
         metaData.extraAttributes >> ImmutableMap.copyOf(extraInfo)
 
+        def componentState = Stub(ModuleComponentGraphResolveState)
+        componentState.moduleResolveMetadata >> metaData
+
         resolveState.resolve() >> {
             def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
-            result.resolved(metaData)
+            result.resolved(componentState)
             return result
         }
 
@@ -148,7 +155,7 @@
         given:
         resolveState.resolve() >> {
             def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
-            result.resolved(metaData)
+            result.resolved(componentState)
             return result
         }
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooserTest.groovy
index fd6a51d..2c514f3 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooserTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DefaultVersionedComponentChooserTest.groovy
@@ -28,9 +28,10 @@
 import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.api.specs.Specs
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveMetadata
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata
 import org.gradle.internal.component.model.AttributeMatcher
-import org.gradle.internal.component.model.ComponentResolveMetadata
 import org.gradle.internal.resolve.ModuleVersionResolveException
 import org.gradle.internal.resolve.caching.ComponentMetadataSupplierRuleExecutor
 import org.gradle.internal.resolve.result.ComponentSelectionContext
@@ -54,13 +55,13 @@
     def chooser = new DefaultVersionedComponentChooser(versionComparator, versionParser, componentSelectionRules, attributesSchema)
 
     def "chooses latest version for component meta data"() {
-        def one = Stub(ComponentResolveMetadata) {
+        def one = Stub(ModuleComponentGraphResolveMetadata) {
             getModuleVersionId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.0")
         }
-        def two = Stub(ComponentResolveMetadata) {
+        def two = Stub(ModuleComponentGraphResolveMetadata) {
             getModuleVersionId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.1")
         }
-        def three = Stub(ComponentResolveMetadata) {
+        def three = Stub(ModuleComponentGraphResolveMetadata) {
             getModuleVersionId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.2")
         }
 
@@ -78,10 +79,10 @@
     }
 
     def "chooses non-generated descriptor over generated"() {
-        def one = Mock(ComponentResolveMetadata) {
+        def one = Mock(ModuleComponentGraphResolveMetadata) {
             getModuleVersionId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.0")
         }
-        def two = Mock(ComponentResolveMetadata) {
+        def two = Mock(ModuleComponentGraphResolveMetadata) {
             getModuleVersionId() >> DefaultModuleVersionIdentifier.newId("group", "name", "1.0")
         }
 
@@ -364,8 +365,11 @@
             getStatus() >> status
             getAttributes() >> AttributeTestUtil.attributes(attributes)
         }
+        def state = Stub(ModuleComponentGraphResolveState) {
+            getModuleResolveMetadata() >> meta
+        }
         def result = new DefaultBuildableModuleComponentMetaDataResolveResult()
-        result.resolved(meta)
+        result.resolved(state)
         return result
     }
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy
deleted file mode 100644
index 3e6ebeb..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/DependencyResolverIdentifierTest.groovy
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2011 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.api.internal.artifacts.ivyservice.ivyresolve
-
-
-import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources
-import org.gradle.api.internal.artifacts.repositories.metadata.MetadataArtifactProvider
-import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver
-import org.gradle.api.internal.artifacts.repositories.resolver.ResourcePattern
-import org.gradle.api.internal.component.ArtifactType
-import org.gradle.internal.hash.Hasher
-import org.gradle.internal.resource.ExternalResourceRepository
-import org.gradle.internal.resource.local.FileStore
-import org.gradle.internal.resource.local.LocallyAvailableResourceFinder
-import org.gradle.internal.resource.transfer.CacheAwareExternalResourceAccessor
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-
-import java.lang.reflect.Field
-
-class DependencyResolverIdentifierTest extends Specification {
-    private final static Field IVY = ExternalResourceResolver.getDeclaredField('ivyPatterns')
-    private final static Field ARTIFACT = ExternalResourceResolver.getDeclaredField('artifactPatterns')
-
-    def "dependency resolvers of type ExternalResourceResolver are differentiated by their patterns"() {
-        given:
-        ExternalResourceResolver resolver1 = resolver()
-        ExternalResourceResolver resolver1a = resolver()
-        ExternalResourceResolver resolver2 = resolver()
-        ExternalResourceResolver resolver2a = resolver()
-
-        patterns(resolver1, IVY, ['ivy1', 'ivy2'])
-        patterns(resolver1, ARTIFACT, ['artifact1', 'artifact2'])
-        patterns(resolver1a, IVY, ['ivy1', 'ivy2'])
-        patterns(resolver1a, ARTIFACT, ['artifact1', 'artifact2'])
-        patterns(resolver2, IVY, ['ivy1', 'different'])
-        patterns(resolver2, ARTIFACT, ['artifact1', 'artifact2'])
-        patterns(resolver2a, IVY, ['ivy1', 'ivy2'])
-        patterns(resolver2a, ARTIFACT, ['artifact1', 'different'])
-
-        expect:
-        id(resolver1) == id(resolver1a)
-        id(resolver1) != id(resolver2)
-        id(resolver1) != id(resolver2a)
-        id(resolver2) != id(resolver2a)
-    }
-
-    def patterns(ExternalResourceResolver resolver, Field field, List<String> patterns) {
-        field.accessible = true
-        field.set(resolver, patterns.collect { p ->
-            Mock(ResourcePattern) {
-                getPattern() >> p
-            }
-        })
-    }
-
-    def id(ExternalResourceResolver resolver) {
-        resolver.getId()
-    }
-
-    def resolver() {
-        return new TestResolver("repo", false, Stub(ExternalResourceRepository), Stub(CacheAwareExternalResourceAccessor), Stub(LocallyAvailableResourceFinder), Stub(FileStore), Stub(ImmutableMetadataSources), Stub(MetadataArtifactProvider))
-    }
-
-    static class TestResolver extends ExternalResourceResolver {
-
-        protected TestResolver(String name, boolean local, ExternalResourceRepository repository, CacheAwareExternalResourceAccessor cachingResourceAccessor, LocallyAvailableResourceFinder locallyAvailableResourceFinder, FileStore artifactFileStore, ImmutableMetadataSources metadataSources, MetadataArtifactProvider metadataArtifactProvider) {
-            super(name, local, repository, cachingResourceAccessor, locallyAvailableResourceFinder, artifactFileStore, metadataSources, metadataArtifactProvider, null, null, null, TestUtil.checksumService)
-        }
-
-        @Override
-        protected void appendId(Hasher hasher) {
-            super.appendId(hasher)
-        }
-
-        @Override
-        protected Class getSupportedMetadataType() {
-            throw new UnsupportedOperationException()
-        }
-
-        @Override
-        protected boolean isMetaDataArtifact(ArtifactType artifactType) {
-            throw new UnsupportedOperationException()
-        }
-
-        @Override
-        ModuleComponentRepositoryAccess getLocalAccess() {
-            throw new UnsupportedOperationException()
-        }
-
-        @Override
-        ModuleComponentRepositoryAccess getRemoteAccess() {
-            throw new UnsupportedOperationException()
-        }
-
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.groovy
index 4b72120..4742e64 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ErrorHandlingArtifactResolverTest.groovy
@@ -18,12 +18,9 @@
 
 import org.gradle.api.artifacts.component.ComponentArtifactIdentifier
 import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.component.ArtifactType
 import org.gradle.internal.component.model.ComponentArtifactMetadata
-import org.gradle.internal.component.model.ComponentResolveMetadata
-import org.gradle.internal.component.model.ImmutableModuleSources
-import org.gradle.internal.component.model.ModuleSource
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata
 import org.gradle.internal.resolve.ArtifactResolveException
 import org.gradle.internal.resolve.resolver.ArtifactResolver
 import org.gradle.internal.resolve.result.BuildableArtifactResolveResult
@@ -35,22 +32,21 @@
     def artifactResolver = new ErrorHandlingArtifactResolver(delegate)
 
     def "wraps resolveArtifact exception as failure"() {
-        def moduleVersionId = DefaultModuleVersionIdentifier.newId("org", "name", "1.0")
         def componentArtifactId = Stub(ComponentArtifactIdentifier) {
             getDisplayName() >> "<component-artifact>"
         }
         def componentArtifact = Stub(ComponentArtifactMetadata) {
             getId() >> componentArtifactId
         }
-        def moduleSources = ImmutableModuleSources.of(Mock(ModuleSource))
+        def component = Stub(ComponentArtifactResolveMetadata)
         def artifactResolveResult = Mock(BuildableArtifactResolveResult)
         def failure = new RuntimeException("foo")
 
         when:
-        delegate.resolveArtifact(moduleVersionId, componentArtifact, moduleSources, artifactResolveResult) >> { throw failure }
+        delegate.resolveArtifact(component, componentArtifact, artifactResolveResult) >> { throw failure }
 
         and:
-        artifactResolver.resolveArtifact(moduleVersionId, componentArtifact, moduleSources, artifactResolveResult)
+        artifactResolver.resolveArtifact(component, componentArtifact, artifactResolveResult)
 
         then:
         1 * artifactResolveResult.failed(_ as ArtifactResolveException) >> { ArtifactResolveException e ->
@@ -63,7 +59,7 @@
         def componentId = Stub(ComponentIdentifier) {
             getDisplayName() >> "<component>"
         }
-        def component = Stub(ComponentResolveMetadata) {
+        def component = Stub(ComponentArtifactResolveMetadata) {
             getId() >> componentId
         }
         def result = Mock(BuildableArtifactSetResolveResult)
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy
index 9bcef2e..5b6460d 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainArtifactResolverTest.groovy
@@ -20,6 +20,7 @@
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactMetadata
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata
 import org.gradle.internal.component.model.DefaultIvyArtifactName
 import org.gradle.internal.component.model.ImmutableModuleSources
 import org.gradle.internal.model.CalculatedValueContainerFactory
@@ -52,6 +53,9 @@
     final repo2Source = new RepositoryChainModuleSource(repo2)
 
     final moduleSources = ImmutableModuleSources.of(repo2Source)
+    final component = Stub(ComponentArtifactResolveMetadata) {
+        getSources() >> moduleSources
+    }
 
     final resolver = new RepositoryChainArtifactResolver(calculatedValueContainerFactory)
 
@@ -62,7 +66,7 @@
 
     def "locates artifact with local access in repository defined by module source"() {
         when:
-        resolver.resolveArtifact(moduleVersionId, artifact, moduleSources, result)
+        resolver.resolveArtifact(component, artifact, result)
         then:
         result.hasResult()
         cache.size() == 1
@@ -81,7 +85,7 @@
         result.result.file == artifactFile
 
         when:
-        resolver.resolveArtifact(moduleVersionId, artifact, moduleSources, result)
+        resolver.resolveArtifact(component, artifact, result)
         then:
         result.hasResult()
         result.result.file == artifactFile
@@ -91,7 +95,7 @@
 
     def "locates artifact with remote access in repository defined by module source"() {
         when:
-        resolver.resolveArtifact(moduleVersionId, artifact, moduleSources, result)
+        resolver.resolveArtifact(component, artifact, result)
         then:
         result.hasResult()
         cache.size() == 1
@@ -113,7 +117,7 @@
         result.result.file == artifactFile
 
         when:
-        resolver.resolveArtifact(moduleVersionId, artifact, moduleSources, result)
+        resolver.resolveArtifact(component, artifact, result)
         then:
         result.hasResult()
         result.result.file == artifactFile
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainComponentMetaDataResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainComponentMetaDataResolverTest.groovy
new file mode 100644
index 0000000..7b47629
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/RepositoryChainComponentMetaDataResolverTest.groovy
@@ -0,0 +1,506 @@
+/*
+ * Copyright 2012 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.api.internal.artifacts.ivyservice.ivyresolve
+
+
+import org.gradle.api.artifacts.ModuleVersionSelector
+import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
+import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveState
+import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata
+import org.gradle.internal.component.model.ComponentGraphResolveState
+import org.gradle.internal.component.model.ComponentOverrideMetadata
+import org.gradle.internal.resolve.ModuleVersionResolveException
+import org.gradle.internal.resolve.result.BuildableComponentResolveResult
+import spock.lang.Specification
+
+class RepositoryChainComponentMetaDataResolverTest extends Specification {
+    final org.gradle.internal.Factory<String> broken = { "broken" }
+    final metaData = metaData("1.2")
+    final componentState = Stub(ModuleComponentGraphResolveState) {
+        getModuleResolveMetadata() >> metaData
+    }
+    final moduleComponentId = DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("group", "project"), "1.0")
+    final componentRequestMetaData = Mock(ComponentOverrideMetadata)
+
+    final result = Mock(BuildableComponentResolveResult)
+    def localAccess = Mock(ModuleComponentRepositoryAccess)
+    def remoteAccess = Mock(ModuleComponentRepositoryAccess)
+    def localAccess2 = Mock(ModuleComponentRepositoryAccess)
+    def remoteAccess2 = Mock(ModuleComponentRepositoryAccess)
+
+    final VersionedComponentChooser componentSelectionStrategy = Mock(VersionedComponentChooser)
+    final RepositoryChainComponentMetaDataResolver resolver = new RepositoryChainComponentMetaDataResolver(componentSelectionStrategy)
+
+    def addRepo1() {
+        addModuleComponentRepository("repo1", localAccess, remoteAccess)
+    }
+
+    def addRepo2() {
+        addModuleComponentRepository("repo2", localAccess2, remoteAccess2)
+    }
+
+    def addModuleComponentRepository(def name, def repoLocalAccess, def repoRemoteAccess) {
+        def repo = Stub(ModuleComponentRepository) {
+            getLocalAccess() >> repoLocalAccess
+            getRemoteAccess() >> repoRemoteAccess
+            getName() >> name
+        }
+        resolver.add(repo)
+        repo
+    }
+
+    def "uses local dependency when available"() {
+        given:
+        def repo = addRepo1()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "attempts to find remote dependency when local dependency is unknown"() {
+        given:
+        def repo = addRepo1()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
+        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "attempts to find remote dependency when local dependency is probably missing"() {
+        given:
+        def repo = addRepo1()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "fails with not found when local static dependency is marked as missing"() {
+        given:
+        def repo = addRepo1()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.attempted("scheme:thing")
+            result.missing()
+        }
+        1 * result.attempted("scheme:thing")
+        1 * result.notFound(moduleComponentId)
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "fails with not found when local and remote static dependency marked as missing"() {
+        given:
+        addRepo1()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+        }
+        1 * result.notFound(moduleComponentId)
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "stops on first available local dependency for static version"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = Mock(ModuleComponentRepository)
+        resolver.add(repo2)
+        def repo3 = Mock(ModuleComponentRepository)
+        resolver.add(repo3)
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * repo2._
+        0 * repo3._
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * result._
+    }
+
+    def "uses local dependency when available in one repository and missing from all other repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "uses local dependency when available in one repository and probably missing in all other repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "uses remote dependency when local dependency is unknown for a given repository and probably missing in other repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
+        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "attempts to find remote dependency when local dependency is probably missing in all repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+        }
+        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "does not attempt to resolve remote dependency when local dependency is missing"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "attempts to find remote dependency when local dependency is missing or unknown in all repositories"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+            result.authoritative = false
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
+        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+        }
+        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "ignores failure to resolve local dependency when available in another repository"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.failed(new ModuleVersionResolveException(id, broken))
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "ignores failure to resolve remote dependency when available in another repository"() {
+        given:
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
+        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.failed(new ModuleVersionResolveException(id, broken))
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
+        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.resolved(componentState)
+        }
+        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
+            assert state == componentState
+        }
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "rethrows failure to resolve local dependency when not available in any repository"() {
+        given:
+        def failure = new ModuleVersionResolveException(Stub(ModuleVersionSelector), broken)
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.failed(failure)
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
+        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+        }
+        1 * result.failed({ it.cause == failure })
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def "rethrows failure to resolve remote dependency when not available in any repository"() {
+        given:
+        def failure = new ModuleVersionResolveException(Stub(ModuleVersionSelector), broken)
+        def repo1 = addRepo1()
+        def repo2 = addRepo2()
+
+        when:
+        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
+
+        then:
+        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
+        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.failed(failure)
+        }
+        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
+        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
+            result.missing()
+        }
+        1 * result.failed({ it.cause == failure })
+
+        and:
+        0 * localAccess._
+        0 * remoteAccess._
+        0 * localAccess2._
+        0 * remoteAccess2._
+        0 * result._
+    }
+
+    def metaData(String version) {
+        return Stub(ModuleComponentResolveMetadata) {
+            toString() >> version
+            getId() >> DefaultModuleVersionIdentifier.newId("org", "module", version)
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactoryTest.groovy
index ea71fea..362db61 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactoryTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolveIvyFactoryTest.groovy
@@ -22,6 +22,7 @@
 import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.internal.artifacts.ComponentMetadataProcessorFactory
 import org.gradle.api.internal.artifacts.ComponentSelectionRulesInternal
+import org.gradle.api.internal.artifacts.DependencyManagementTestUtil
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory
 import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator
@@ -37,6 +38,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.AbstractModuleVersionsCache
 import org.gradle.api.internal.artifacts.ivyservice.modulecache.dynamicversions.ModuleVersionsCache
 import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository
+import org.gradle.api.internal.artifacts.repositories.descriptor.UrlRepositoryDescriptor
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources
 import org.gradle.api.internal.artifacts.repositories.metadata.MetadataArtifactProvider
 import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver
@@ -100,8 +102,9 @@
         versionParser = new VersionParser()
         buildOperationExecutor = Mock()
         listener = Mock()
+        def resolveStateFactory = DependencyManagementTestUtil.modelGraphResolveFactory()
 
-        resolveIvyFactory = new ResolveIvyFactory(cacheProvider, startParameterResolutionOverride, startParameterResolutionOverride.dependencyVerificationOverride(buildOperationExecutor, TestUtil.checksumService, Mock(SignatureVerificationServiceFactory), new DocumentationRegistry(), buildCommencedTimeProvider, (Factory<GradleProperties>) Mock(Factory), Stub(FileResourceListener)), buildCommencedTimeProvider, versionComparator, moduleIdentifierFactory, repositoryBlacklister, versionParser, listener, Stub(CalculatedValueContainerFactory))
+        resolveIvyFactory = new ResolveIvyFactory(cacheProvider, startParameterResolutionOverride, startParameterResolutionOverride.dependencyVerificationOverride(buildOperationExecutor, TestUtil.checksumService, Mock(SignatureVerificationServiceFactory), new DocumentationRegistry(), buildCommencedTimeProvider, (Factory<GradleProperties>) Mock(Factory), Stub(FileResourceListener)), buildCommencedTimeProvider, versionComparator, moduleIdentifierFactory, repositoryBlacklister, versionParser, listener, resolveStateFactory, Stub(CalculatedValueContainerFactory))
     }
 
     def "returns an empty resolver when no repositories are configured"() {
@@ -153,7 +156,7 @@
 
         return Spy(ExternalResourceResolver,
             constructorArgs: [
-                "Spy Resolver",
+                Stub(UrlRepositoryDescriptor),
                 false,
                 externalResourceRepository,
                 cacheAwareExternalResourceAccessor,
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolverProviderComponentMetaDataResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolverProviderComponentMetaDataResolverTest.groovy
deleted file mode 100644
index fea357d..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/ResolverProviderComponentMetaDataResolverTest.groovy
+++ /dev/null
@@ -1,578 +0,0 @@
-/*
- * Copyright 2012 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.api.internal.artifacts.ivyservice.ivyresolve
-
-import org.apache.ivy.core.module.descriptor.ModuleDescriptor
-import org.gradle.api.Transformer
-import org.gradle.api.artifacts.ModuleVersionIdentifier
-import org.gradle.api.artifacts.ModuleVersionSelector
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.IvyUtil
-import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
-import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata
-import org.gradle.internal.component.model.ComponentGraphResolveState
-import org.gradle.internal.component.model.ComponentOverrideMetadata
-import org.gradle.internal.resolve.ModuleVersionResolveException
-import org.gradle.internal.resolve.result.BuildableComponentResolveResult
-import spock.lang.Specification
-
-class ResolverProviderComponentMetaDataResolverTest extends Specification {
-    final org.gradle.internal.Factory<String> broken = { "broken" }
-    final metaData = metaData("1.2")
-    final moduleComponentId = DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("group", "project"), "1.0")
-    final componentRequestMetaData = Mock(ComponentOverrideMetadata)
-
-    final Transformer<ModuleComponentResolveMetadata, RepositoryChainModuleResolution> transformer = Mock(Transformer)
-    final result = Mock(BuildableComponentResolveResult)
-    def localAccess = Mock(ModuleComponentRepositoryAccess)
-    def remoteAccess = Mock(ModuleComponentRepositoryAccess)
-    def localAccess2 = Mock(ModuleComponentRepositoryAccess)
-    def remoteAccess2 = Mock(ModuleComponentRepositoryAccess)
-
-    final VersionedComponentChooser componentSelectionStrategy = Mock(VersionedComponentChooser)
-    final RepositoryChainComponentMetaDataResolver resolver = new RepositoryChainComponentMetaDataResolver(componentSelectionStrategy, transformer)
-
-    ModuleVersionIdentifier moduleVersionIdentifier(ModuleDescriptor moduleDescriptor) {
-        def moduleRevId = moduleDescriptor.moduleRevisionId
-        DefaultModuleVersionIdentifier.newId(DefaultModuleIdentifier.newId(moduleRevId.organisation, moduleRevId.name), moduleRevId.revision)
-    }
-
-    def addRepo1() {
-        addModuleComponentRepository("repo1", localAccess, remoteAccess)
-    }
-
-    def addRepo2() {
-        addModuleComponentRepository("repo2", localAccess2, remoteAccess2)
-    }
-
-    def addModuleComponentRepository(def name, def repoLocalAccess, def repoRemoteAccess) {
-        def repo = Stub(ModuleComponentRepository) {
-            getLocalAccess() >> repoLocalAccess
-            getRemoteAccess() >> repoRemoteAccess
-            getName() >> name
-        }
-        resolver.add(repo)
-        repo
-    }
-
-    def "uses local dependency when available"() {
-        given:
-        def repo = addRepo1()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * result._
-    }
-
-    def "attempts to find remote dependency when local dependency is unknown"() {
-        given:
-        def repo = addRepo1()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
-        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * result._
-    }
-
-    def "attempts to find remote dependency when local dependency is probably missing"() {
-        given:
-        def repo = addRepo1()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-            result.authoritative = false
-        }
-        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * result._
-    }
-
-    def "fails with not found when local static dependency is marked as missing"() {
-        given:
-        def repo = addRepo1()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.attempted("scheme:thing")
-            result.missing()
-        }
-        1 * result.attempted("scheme:thing")
-        1 * result.notFound(moduleComponentId)
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * result._
-    }
-
-    def "fails with not found when local and remote static dependency marked as missing"() {
-        given:
-        addRepo1()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-            result.authoritative = false
-        }
-        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-        }
-        1 * result.notFound(moduleComponentId)
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * result._
-    }
-
-    def "stops on first available local dependency for static version"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = Mock(ModuleComponentRepository)
-        resolver.add(repo2)
-        def repo3 = Mock(ModuleComponentRepository)
-        resolver.add(repo3)
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo1
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * repo2._
-        0 * repo3._
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * result._
-    }
-
-    def "uses local dependency when available in one repository and missing from all other repositories"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "uses local dependency when available in one repository and probably missing in all other repositories"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-            result.authoritative = false
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "uses remote dependency when local dependency is unknown for a given repository and probably missing in other repositories"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-            result.authoritative = false
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
-        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "attempts to find remote dependency when local dependency is probably missing in all repositories"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-            result.authoritative = false
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-            result.authoritative = false
-        }
-        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-        }
-        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "does not attempt to resolve remote dependency when local dependency is missing"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-            result.authoritative = false
-        }
-        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "attempts to find remote dependency when local dependency is missing or unknown in all repositories"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-            result.authoritative = false
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
-        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-        }
-        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo1
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "ignores failure to resolve local dependency when available in another repository"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.failed(new ModuleVersionResolveException(id, broken))
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "ignores failure to resolve remote dependency when available in another repository"() {
-        given:
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
-        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.failed(new ModuleVersionResolveException(id, broken))
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
-        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.resolved(metaData)
-        }
-        1 * transformer.transform(_) >> { RepositoryChainModuleResolution it ->
-            assert it.module == metaData
-            assert it.repository == repo2
-            metaData
-        }
-        1 * result.resolved(_) >> { ComponentGraphResolveState state ->
-            assert state.metadata == this.metaData
-        }
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "rethrows failure to resolve local dependency when not available in any repository"() {
-        given:
-        def failure = new ModuleVersionResolveException(Stub(ModuleVersionSelector), broken)
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.failed(failure)
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
-        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-        }
-        1 * result.failed({ it.cause == failure })
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def "rethrows failure to resolve remote dependency when not available in any repository"() {
-        given:
-        def failure = new ModuleVersionResolveException(Stub(ModuleVersionSelector), broken)
-        def repo1 = addRepo1()
-        def repo2 = addRepo2()
-
-        when:
-        resolver.resolve(moduleComponentId, componentRequestMetaData, result)
-
-        then:
-        1 * localAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
-        1 * remoteAccess.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.failed(failure)
-        }
-        1 * localAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _)
-        1 * remoteAccess2.resolveComponentMetaData(moduleComponentId, componentRequestMetaData, _) >> { id, meta, result ->
-            result.missing()
-        }
-        1 * result.failed({ it.cause == failure })
-
-        and:
-        0 * localAccess._
-        0 * remoteAccess._
-        0 * localAccess2._
-        0 * remoteAccess2._
-        0 * result._
-    }
-
-    def descriptor(String version) {
-        def descriptor = Stub(ModuleDescriptor)
-        descriptor.resolvedModuleRevisionId >> IvyUtil.createModuleRevisionId("org", "module", version)
-        return descriptor
-    }
-
-    def metaData(String version) {
-        return Stub(ModuleComponentResolveMetadata) {
-            toString() >> version
-            getId() >> DefaultModuleVersionIdentifier.newId("org", "module", version)
-            getDescriptor() >> descriptor(version)
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradleModuleMetadataParserTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradleModuleMetadataParserTest.groovy
index 965c7f6..ec3d011 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradleModuleMetadataParserTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/ivyresolve/parser/GradleModuleMetadataParserTest.groovy
@@ -33,6 +33,8 @@
 import org.junit.Rule
 import spock.lang.Specification
 
+import java.util.stream.Collectors
+
 import static org.gradle.util.AttributeTestUtil.attributes
 
 class GradleModuleMetadataParserTest extends Specification {
@@ -869,6 +871,50 @@
         json.replace('"formatVersion": "1.1"', '"formatVersion": "' + metadataVersion + '"')
     }
 
+    def "new hierarchy in ModuleDependency is added to equals and hashcode"() {
+        when:
+        // If this test fails, you added a type hierarchy to GradleModuleMetadataParser.ModuleDependency, update this test _after_ making sure it is considered by hashcode and equals
+        def modDepClass = GradleModuleMetadataParser.ModuleDependency.class
+
+        then:
+        modDepClass.getSuperclass() == Object.class
+    }
+
+    def "new hierarchy in ModuleDependencyConstraint is added to equals and hashcode"() {
+        when:
+        // If this test fails, you added a type hierarchy to GradleModuleMetadataParser.ModuleDependency, update this test _after_ making sure it is considered by hashcode and equals
+        def modDepClass = GradleModuleMetadataParser.ModuleDependencyConstraint.class
+
+        then:
+        modDepClass.getSuperclass() == Object.class
+    }
+
+    def "new fields in ModuleDependency are added to equals and hashcode"() {
+        when:
+        // If this test fails, you added a field to GradleModuleMetadataParser.ModuleDependency, add it here _after_ making sure it is considered by hashcode and equals
+        def knownFields = ["group", "module", "versionConstraint", "excludes", "reason", "attributes", "requestedCapabilities", "endorsing", "artifact"]
+        def modDepClass = GradleModuleMetadataParser.ModuleDependency.class
+        def newFields = Arrays.stream(modDepClass.getDeclaredFields()).filter { !knownFields.contains(it.getName()) }
+            .map { it.getName() }
+            .collect(Collectors.toList())
+
+        then:
+        newFields == []
+    }
+
+    def "new fields in ModuleDependencyConstraint are added to equals and hashcode"() {
+        when:
+        // If this test fails, you added a field to GradleModuleMetadataParser.ModuleDependencyConstraint, add it here _after_ making sure it is considered by hashcode and equals
+        def knownFields = ["group", "module", "versionConstraint", "reason", "attributes"]
+        def modDepClass = GradleModuleMetadataParser.ModuleDependencyConstraint.class
+        def newFields = Arrays.stream(modDepClass.getDeclaredFields()).filter { !knownFields.contains(it.getName()) }
+            .map { it.getName() }
+            .collect(Collectors.toList())
+
+        then:
+        newFields == []
+    }
+
     def resource(String content) {
         def resource = Stub(LocallyAvailableExternalResource)
         _ * resource.displayName >> "<resource>"
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultLocalComponentMetadataBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultLocalComponentMetadataBuilderTest.groovy
deleted file mode 100644
index a3adfe2..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultLocalComponentMetadataBuilderTest.groovy
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2013 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.api.internal.artifacts.ivyservice.moduleconverter
-
-import org.gradle.api.artifacts.PublishArtifact
-import org.gradle.api.artifacts.PublishArtifactSet
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
-import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder
-import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
-import org.gradle.internal.component.external.model.ImmutableCapabilities
-import org.gradle.internal.component.local.model.BuildableLocalComponentMetadata
-import org.gradle.internal.component.local.model.BuildableLocalConfigurationMetadata
-import spock.lang.Specification
-
-class DefaultLocalComponentMetadataBuilderTest extends Specification {
-    def configurationMetadataBuilder = Mock(LocalConfigurationMetadataBuilder)
-    def converter = new DefaultLocalComponentMetadataBuilder(configurationMetadataBuilder)
-
-    def componentId = DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org", "name"), "rev")
-
-    def "adds artifacts from each configuration"() {
-        def emptySet = new HashSet<String>()
-        def metaData = Mock(BuildableLocalComponentMetadata)
-        def sourceConfig1 = Stub(ConfigurationInternal)
-        def sourceConfig2 = Stub(ConfigurationInternal)
-        def targetConfig1 = Mock(BuildableLocalConfigurationMetadata)
-        def targetConfig2 = Mock(BuildableLocalConfigurationMetadata)
-        def artifacts1 = [Stub(PublishArtifact)] as Set
-        def artifacts2 = [Stub(PublishArtifactSet)] as Set
-
-        given:
-        sourceConfig1.name >> "config1"
-        sourceConfig1.collectVariants(_) >> { ConfigurationInternal.VariantVisitor visitor ->
-            visitor.visitArtifacts(artifacts1)
-            visitor.visitChildVariant("child1", null, null, null, null)
-            visitor.visitChildVariant("child2", null, null, null, null)
-        }
-        sourceConfig2.name >> "config2"
-        sourceConfig2.collectVariants(_) >> { ConfigurationInternal.VariantVisitor visitor ->
-            visitor.visitArtifacts(artifacts2)
-        }
-
-        when:
-        converter.addConfiguration(metaData, sourceConfig1)
-        converter.addConfiguration(metaData, sourceConfig2)
-
-        then:
-        _ * metaData.id >> Stub(ComponentIdentifier)
-        1 * metaData.addConfiguration("config1", '', emptySet, emptySet, false, false, _, false, _, false, ImmutableCapabilities.EMPTY) >> targetConfig1
-        1 * metaData.addDependenciesAndExcludesForConfiguration(sourceConfig1, configurationMetadataBuilder)
-        1 * metaData.addConfiguration("config2", '', emptySet, emptySet, false, false, _, false, _, false, ImmutableCapabilities.EMPTY) >> targetConfig2
-        1 * metaData.addDependenciesAndExcludesForConfiguration(sourceConfig2, configurationMetadataBuilder)
-        1 * targetConfig1.addArtifacts(artifacts1)
-        1 * targetConfig1.addVariant("config1-child1", _, _, _, _, _)
-        1 * targetConfig1.addVariant("config1-child2", _, _, _, _, _)
-        1 * targetConfig2.addArtifacts(artifacts2)
-        0 * metaData._
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultRootComponentMetadataBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultRootComponentMetadataBuilderTest.groovy
index caaf165..6e054d1 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultRootComponentMetadataBuilderTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/DefaultRootComponentMetadataBuilderTest.groovy
@@ -16,22 +16,18 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter
 
-import com.google.common.collect.ImmutableSet
+
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory
 import org.gradle.api.internal.artifacts.Module
 import org.gradle.api.internal.artifacts.component.ComponentIdentifierFactory
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
-import org.gradle.api.internal.artifacts.configurations.DefaultConfigurationContainer
+import org.gradle.api.internal.artifacts.configurations.ConfigurationsProvider
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider
 import org.gradle.api.internal.artifacts.configurations.MutationValidator
-import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal
-import org.gradle.api.internal.attributes.ImmutableAttributes
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.LocalConfigurationMetadataBuilder
 import org.gradle.api.internal.project.ProjectStateRegistry
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
-import org.gradle.internal.component.external.model.ImmutableCapabilities
-import org.gradle.internal.component.local.model.BuildableLocalComponentMetadata
-import org.gradle.internal.component.local.model.DefaultLocalConfigurationMetadata
+import org.gradle.internal.component.local.model.LocalConfigurationMetadata
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 
@@ -42,15 +38,12 @@
     }
     ComponentIdentifierFactory componentIdentifierFactory = Mock()
     ImmutableModuleIdentifierFactory moduleIdentifierFactory = Mock()
-    LocalComponentMetadataBuilder configurationComponentMetaDataBuilder = Mock()
-    ConfigurationInternal configuration = Mock()
-    def configurationsProvider = Stub(DefaultConfigurationContainer) {
-        getAll() >> ([configuration] as Set)
-        size() >> 1
+    LocalConfigurationMetadataBuilder configurationMetadataBuilder = Mock(LocalConfigurationMetadataBuilder) {
+        create(_, _, _, _, _, _) >> Mock(LocalConfigurationMetadata)
     }
-    ProjectStateRegistry projectStateRegistry = Mock()
 
-    DefaultLocalConfigurationMetadata configurationMetadata = Mock()
+    def configurationsProvider = Stub(ConfigurationsProvider)
+    ProjectStateRegistry projectStateRegistry = Mock()
 
     def mid = DefaultModuleIdentifier.newId('foo', 'bar')
 
@@ -58,26 +51,13 @@
         metaDataProvider,
         componentIdentifierFactory,
         moduleIdentifierFactory,
-        configurationComponentMetaDataBuilder,
+        configurationMetadataBuilder,
         projectStateRegistry,
         TestUtil.calculatedValueContainerFactory()
     )
 
     def builder = builderFactory.create(configurationsProvider)
 
-    def setup() {
-        ResolutionStrategyInternal resolutionStrategy = Mock()
-        resolutionStrategy.isDependencyLockingEnabled() >> false
-        configuration.getResolutionStrategy() >> resolutionStrategy
-        configuration.getName() >> "conf"
-
-        configurationComponentMetaDataBuilder.addConfiguration(_, _) >> { args ->
-            BuildableLocalComponentMetadata componentMetadata = args[0]
-            componentMetadata.addConfiguration("conf", "", ImmutableSet.of(), ImmutableSet.of("conf"), true, true, ImmutableAttributes.EMPTY, true, null, false, ImmutableCapabilities.EMPTY)
-            configurationMetadata
-        }
-    }
-
     def "caches root component metadata"() {
         componentIdentifierFactory.createComponentIdentifier(_) >> {
             new DefaultModuleComponentIdentifier(mid, '1.0')
@@ -114,10 +94,9 @@
         }
         def root = builder.toRootComponentMetaData()
 
-        def conf = root.getConfiguration("conf")
-        assert conf.needsReevaluate()
-        conf.realizeDependencies()
-        assert !conf.needsReevaluate()
+        assert !root.isConfigurationRealized("conf")
+        root.getConfiguration("conf")
+        assert root.isConfigurationRealized("conf")
 
         when:
         builder.validator.validateMutation(mutationType)
@@ -125,12 +104,15 @@
 
         then:
         root == otherRoot
-        conf.needsReevaluate()
+        !root.isConfigurationRealized("conf")
 
         where:
         mutationType << [
             MutationValidator.MutationType.DEPENDENCIES,
+            MutationValidator.MutationType.DEPENDENCY_ATTRIBUTES,
             MutationValidator.MutationType.ARTIFACTS,
+            MutationValidator.MutationType.USAGE,
+            MutationValidator.MutationType.HIERARCHY
         ]
     }
 
@@ -140,10 +122,9 @@
         }
         def root = builder.toRootComponentMetaData()
 
-        def conf = root.getConfiguration("conf")
-        assert conf.needsReevaluate()
-        conf.realizeDependencies()
-        assert !conf.needsReevaluate()
+        assert !root.isConfigurationRealized("conf")
+        root.getConfiguration("conf")
+        assert root.isConfigurationRealized("conf")
 
         when:
         builder.validator.validateMutation(mutationType)
@@ -151,12 +132,9 @@
 
         then:
         root == otherRoot
-        !conf.needsReevaluate()
+        root.isConfigurationRealized("conf")
 
         where:
-        mutationType << [
-                MutationValidator.MutationType.USAGE,
-                MutationValidator.MutationType.STRATEGY,
-        ]
+        mutationType << [MutationValidator.MutationType.STRATEGY]
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy
deleted file mode 100644
index 5f44a0e..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyDescriptorFactoryTest.groovy
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2009 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies
-
-import org.gradle.api.InvalidUserDataException
-import org.gradle.api.artifacts.ProjectDependency
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.artifacts.component.ModuleComponentSelector
-import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyConstraint
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata
-import spock.lang.Specification
-
-class DefaultDependencyDescriptorFactoryTest extends Specification {
-    def configurationName = "conf"
-    def projectDependency = Stub(ProjectDependency)
-    def componentId = new ComponentIdentifier() {
-        @Override
-        String getDisplayName() {
-            return "example"
-        }
-    }
-
-    def "delegates to internal factory"() {
-        given:
-        def ivyDependencyDescriptorFactory1 = Mock(IvyDependencyDescriptorFactory)
-        def ivyDependencyDescriptorFactory2 = Mock(IvyDependencyDescriptorFactory)
-        def result = Stub(LocalOriginDependencyMetadata)
-
-        when:
-        def dependencyDescriptorFactory = new DefaultDependencyDescriptorFactory(
-                ivyDependencyDescriptorFactory1, ivyDependencyDescriptorFactory2
-        )
-        def created = dependencyDescriptorFactory.createDependencyDescriptor(componentId, configurationName, null, projectDependency)
-
-        then:
-        created == result
-
-        and:
-        1 * ivyDependencyDescriptorFactory1.canConvert(projectDependency) >> false
-        1 * ivyDependencyDescriptorFactory2.canConvert(projectDependency) >> true
-        1 * ivyDependencyDescriptorFactory2.createDependencyDescriptor(componentId, configurationName, null, projectDependency) >> result
-    }
-
-    def "fails where no internal factory can handle dependency type"() {
-        def ivyDependencyDescriptorFactory1 = Mock(IvyDependencyDescriptorFactory);
-
-        when:
-        ivyDependencyDescriptorFactory1.canConvert(projectDependency) >> false
-
-        and:
-        def dependencyDescriptorFactory = new DefaultDependencyDescriptorFactory(
-                ivyDependencyDescriptorFactory1
-        )
-        dependencyDescriptorFactory.createDependencyDescriptor(componentId, configurationName, null, projectDependency)
-
-        then:
-        thrown InvalidUserDataException
-    }
-
-    def "creates descriptor for dependency constraints"() {
-        given:
-        def dependencyConstraint = new DefaultDependencyConstraint("g", "m", "1")
-
-        when:
-        def dependencyDescriptorFactory = new DefaultDependencyDescriptorFactory()
-        def created = dependencyDescriptorFactory.createDependencyConstraintDescriptor(componentId, configurationName, null, dependencyConstraint)
-        def selector = created.selector as ModuleComponentSelector
-
-        then:
-        created.constraint
-        selector.group == "g"
-        selector.module == "m"
-        selector.version == "1"
-
-        and:
-        created.moduleConfiguration == configurationName
-        created.artifacts.empty
-        created.excludes.empty
-        !created.force
-        !created.transitive
-        !created.changing
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyMetadataFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyMetadataFactoryTest.groovy
new file mode 100644
index 0000000..88578df
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultDependencyMetadataFactoryTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2009 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies
+
+import org.gradle.api.InvalidUserDataException
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.api.internal.artifacts.dependencies.DefaultDependencyConstraint
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata
+import spock.lang.Specification
+
+class DefaultDependencyMetadataFactoryTest extends Specification {
+    def configurationName = "conf"
+    def projectDependency = Stub(ProjectDependency)
+    def componentId = new ComponentIdentifier() {
+        @Override
+        String getDisplayName() {
+            return "example"
+        }
+    }
+
+    def "delegates to internal factory"() {
+        given:
+        def converter1 = Mock(DependencyMetadataConverter)
+        def converter2 = Mock(DependencyMetadataConverter)
+        def result = Stub(LocalOriginDependencyMetadata)
+
+        when:
+        def dependencyDescriptorFactory = new DefaultDependencyMetadataFactory(converter1, converter2)
+        def created = dependencyDescriptorFactory.createDependencyMetadata(componentId, configurationName, null, projectDependency)
+
+        then:
+        created == result
+
+        and:
+        1 * converter1.canConvert(projectDependency) >> false
+        1 * converter2.canConvert(projectDependency) >> true
+        1 * converter2.createDependencyMetadata(componentId, configurationName, null, projectDependency) >> result
+    }
+
+    def "fails where no internal factory can handle dependency type"() {
+        def converter = Mock(DependencyMetadataConverter);
+
+        when:
+        converter.canConvert(projectDependency) >> false
+
+        and:
+        def dependencyDescriptorFactory = new DefaultDependencyMetadataFactory(converter)
+        dependencyDescriptorFactory.createDependencyMetadata(componentId, configurationName, null, projectDependency)
+
+        then:
+        thrown InvalidUserDataException
+    }
+
+    def "creates descriptor for dependency constraints"() {
+        given:
+        def dependencyConstraint = new DefaultDependencyConstraint("g", "m", "1")
+
+        when:
+        def dependencyDescriptorFactory = new DefaultDependencyMetadataFactory()
+        def created = dependencyDescriptorFactory.createDependencyConstraintMetadata(componentId, configurationName, null, dependencyConstraint)
+        def selector = created.selector as ModuleComponentSelector
+
+        then:
+        created.constraint
+        selector.group == "g"
+        selector.module == "m"
+        selector.version == "1"
+
+        and:
+        created.moduleConfiguration == configurationName
+        created.artifacts.empty
+        created.excludes.empty
+        !created.force
+        !created.transitive
+        !created.changing
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultLocalConfigurationMetadataBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultLocalConfigurationMetadataBuilderTest.groovy
index 21be60b..04d5233 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultLocalConfigurationMetadataBuilderTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/DefaultLocalConfigurationMetadataBuilderTest.groovy
@@ -15,7 +15,8 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies
 
-import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
+import com.google.common.collect.ImmutableList
+import org.gradle.api.artifacts.ConfigurationPublications
 import org.gradle.api.artifacts.DependencyConstraint
 import org.gradle.api.artifacts.DependencyConstraintSet
 import org.gradle.api.artifacts.DependencySet
@@ -24,90 +25,100 @@
 import org.gradle.api.artifacts.ModuleDependency
 import org.gradle.api.artifacts.component.ComponentIdentifier
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.configurations.DetachedConfigurationsProvider
 import org.gradle.api.internal.attributes.AttributeContainerInternal
-import org.gradle.internal.component.local.model.BuildableLocalConfigurationMetadata
+import org.gradle.api.internal.initialization.RootScriptDomainObjectContext
+import org.gradle.internal.component.local.model.LocalComponentMetadata
 import org.gradle.internal.component.model.Exclude
+import org.gradle.internal.component.model.ExcludeMetadata
 import org.gradle.internal.component.model.LocalOriginDependencyMetadata
+import org.gradle.util.TestUtil
 import spock.lang.Specification
 
+/**
+ * Tests {@link DefaultLocalConfigurationMetadataBuilder}
+ */
 class DefaultLocalConfigurationMetadataBuilderTest extends Specification {
-    def dependencyDescriptorFactory = Mock(DependencyDescriptorFactory)
+    def dependencyMetadataFactory = Mock(DependencyMetadataFactory)
     def excludeRuleConverter = Mock(ExcludeRuleConverter)
-    def converter = new DefaultLocalConfigurationMetadataBuilder(dependencyDescriptorFactory, excludeRuleConverter)
+    def converter = new DefaultLocalConfigurationMetadataBuilder(dependencyMetadataFactory, excludeRuleConverter)
 
-    def descriptor = Mock(DefaultModuleDescriptor)
-    def metaData = Mock(BuildableLocalConfigurationMetadata)
     def configuration = Mock(ConfigurationInternal)
     def dependencySet = Mock(DependencySet)
     def dependencyConstraintSet = Mock(DependencyConstraintSet)
 
-    def "ignores configuration with no dependencies or exclude rules"() {
+    def cache = new LocalConfigurationMetadataBuilder.DependencyCache();
+    def component = Mock(LocalComponentMetadata)
+    def configurationsProvider = new DetachedConfigurationsProvider()
+    def componentId = Mock(ComponentIdentifier)
+
+    def setup() {
+        ConfigurationPublications outgoing = Mock(ConfigurationPublications)
+        outgoing.getCapabilities() >> Collections.emptySet()
+
+        component.getId() >> componentId
+        component.getConfigurationNames() >> ["config"]
+        configuration.isCanBeDeclared() >> true
+        configuration.name >> "config"
+        configuration.extendsFrom >> []
+        configuration.hierarchy >> [configuration]
+        configuration.outgoing >> outgoing
+        configuration.dependencies >> dependencySet
+        configuration.dependencyConstraints >> dependencyConstraintSet
+        configuration.attributes >> Stub(AttributeContainerInternal)
+        configuration.excludeRules >> ([] as Set)
+        dependencySet.iterator() >> [].iterator()
+        dependencyConstraintSet.iterator() >> [].iterator()
+
+        configurationsProvider.setTheOnlyConfiguration(configuration)
+    }
+
+    def "builds configuration with no dependencies or exclude rules"() {
         when:
-        converter.addDependenciesAndExcludes(metaData, configuration)
+        def metaData = create()
 
         then:
         1 * configuration.runDependencyActions()
-        1 * configuration.dependencies >> dependencySet
-        1 * configuration.dependencyConstraints >> dependencyConstraintSet
-        1 * dependencySet.iterator() >> [].iterator()
-        1 * dependencyConstraintSet.iterator() >> [].iterator()
-        1 * configuration.excludeRules >> ([] as Set)
-        2 * configuration.attributes >> Stub(AttributeContainerInternal)
-        0 * _
+
+        metaData.dependencies.size() == 0
+        metaData.files.size() == 0
+        metaData.excludes.size() == 0
     }
 
     def "adds ModuleDependency instances from configuration"() {
-        def componentId = Mock(ComponentIdentifier)
         def dependencyDescriptor1 = Mock(LocalOriginDependencyMetadata)
         def dependencyDescriptor2 = Mock(LocalOriginDependencyMetadata)
         def dependency1 = Mock(ModuleDependency)
         def dependency2 = Mock(ModuleDependency)
 
         when:
-        converter.addDependenciesAndExcludes(metaData, configuration)
+        def metaData = create()
 
         then:
-        _ * metaData.getComponentId() >> componentId
         1 * configuration.runDependencyActions()
-        1 * configuration.dependencies >> dependencySet
-        1 * configuration.dependencyConstraints >> dependencyConstraintSet
         1 * dependencySet.iterator() >> [dependency1, dependency2].iterator()
-        1 * dependencyConstraintSet.iterator() >> [].iterator()
-        _ * configuration.name >> "config"
-        _ * configuration.attributes >> Stub(AttributeContainerInternal)
-        1 * dependencyDescriptorFactory.createDependencyDescriptor(componentId, "config", _, dependency1) >> dependencyDescriptor1
-        1 * dependencyDescriptorFactory.createDependencyDescriptor(componentId, "config", _, dependency2) >> dependencyDescriptor2
-        1 * metaData.addDependency(dependencyDescriptor1)
-        1 * metaData.addDependency(dependencyDescriptor2)
-        1 * configuration.excludeRules >> ([] as Set)
-        0 * _
+        1 * dependencyMetadataFactory.createDependencyMetadata(componentId, "config", _, dependency1) >> dependencyDescriptor1
+        1 * dependencyMetadataFactory.createDependencyMetadata(componentId, "config", _, dependency2) >> dependencyDescriptor2
+
+        metaData.dependencies == [dependencyDescriptor1, dependencyDescriptor2]
     }
 
     def "adds DependencyConstraint instances from configuration"() {
-        def componentId = Mock(ComponentIdentifier)
         def dependencyDescriptor1 = Mock(LocalOriginDependencyMetadata)
         def dependencyDescriptor2 = Mock(LocalOriginDependencyMetadata)
         def dependencyConstraint1 = Mock(DependencyConstraint)
         def dependencyConstraint2 = Mock(DependencyConstraint)
 
         when:
-        converter.addDependenciesAndExcludes(metaData, configuration)
+        def metaData = create()
 
         then:
-        _ * metaData.getComponentId() >> componentId
         1 * configuration.runDependencyActions()
-        1 * configuration.dependencies >> dependencySet
-        1 * configuration.dependencyConstraints >> dependencyConstraintSet
-        1 * dependencySet.iterator() >> [].iterator()
         1 * dependencyConstraintSet.iterator() >> [dependencyConstraint1, dependencyConstraint2].iterator()
-        _ * configuration.name >> "config"
-        _ * configuration.attributes >> Stub(AttributeContainerInternal)
-        1 * dependencyDescriptorFactory.createDependencyConstraintDescriptor(componentId, "config", _, dependencyConstraint1) >> dependencyDescriptor1
-        1 * dependencyDescriptorFactory.createDependencyConstraintDescriptor(componentId, "config", _, dependencyConstraint2) >> dependencyDescriptor2
-        1 * metaData.addDependency(dependencyDescriptor1)
-        1 * metaData.addDependency(dependencyDescriptor2)
-        1 * configuration.excludeRules >> ([] as Set)
-        0 * _
+        1 * dependencyMetadataFactory.createDependencyConstraintMetadata(componentId, "config", _, dependencyConstraint1) >> dependencyDescriptor1
+        1 * dependencyMetadataFactory.createDependencyConstraintMetadata(componentId, "config", _, dependencyConstraint2) >> dependencyDescriptor2
+
+        metaData.dependencies == [dependencyDescriptor1, dependencyDescriptor2]
     }
 
     def "adds FileCollectionDependency instances from configuration"() {
@@ -115,41 +126,31 @@
         def dependency2 = Mock(FileCollectionDependency)
 
         when:
-        converter.addDependenciesAndExcludes(metaData, configuration)
+        def metaData = create()
 
         then:
         1 * configuration.runDependencyActions()
-        1 * configuration.dependencies >> dependencySet
-        1 * configuration.dependencyConstraints >> dependencyConstraintSet
         1 * dependencySet.iterator() >> [dependency1, dependency2].iterator()
-        1 * dependencyConstraintSet.iterator() >> [].iterator()
-        _ * configuration.name >> "config"
-        _ * configuration.attributes >> Stub(AttributeContainerInternal)
-        1 * metaData.addFiles({it.source == dependency1})
-        1 * metaData.addFiles({it.source == dependency2})
-        1 * configuration.excludeRules >> ([] as Set)
-        0 * _
+
+        metaData.files*.source == [dependency1, dependency2]
     }
 
     def "adds exclude rule from configuration"() {
         def excludeRule = Mock(ExcludeRule)
-        def ivyExcludeRule = Mock(Exclude)
+        ExcludeMetadata ivyExcludeRule = Mock(Exclude)
 
         when:
-        converter.addDependenciesAndExcludes(metaData, configuration)
+        def metaData = create()
 
         then:
         1 * configuration.runDependencyActions()
-        1 * configuration.dependencies >> dependencySet
-        1 * configuration.dependencyConstraints >> dependencyConstraintSet
-        1 * dependencySet.iterator() >> [].iterator()
-        1 * dependencyConstraintSet.iterator() >> [].iterator()
-
         1 * configuration.excludeRules >> ([excludeRule] as Set)
-        _ * configuration.getName() >> "config"
-        _ * configuration.attributes >> Stub(AttributeContainerInternal)
         1 * excludeRuleConverter.convertExcludeRule(excludeRule) >> ivyExcludeRule
-        1 * metaData.addExclude(ivyExcludeRule)
-        0 * _
+
+        metaData.getExcludes() == ImmutableList.of(ivyExcludeRule)
+    }
+
+    def create() {
+        return converter.create(configuration, configurationsProvider, component, cache, RootScriptDomainObjectContext.INSTANCE, TestUtil.calculatedValueContainerFactory())
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.groovy
deleted file mode 100644
index 1b2ebd2..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyDescriptorFactoryTest.groovy
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2010 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies
-
-import org.gradle.api.artifacts.ExternalModuleDependency
-import org.gradle.api.artifacts.ModuleDependency
-import org.gradle.api.artifacts.ProjectDependency
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.artifacts.component.ModuleComponentSelector
-import org.gradle.api.internal.artifacts.VersionConstraintInternal
-import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata
-
-class ExternalModuleDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalSpec {
-
-    ExternalModuleIvyDependencyDescriptorFactory externalModuleDependencyDescriptorFactory =
-            new ExternalModuleIvyDependencyDescriptorFactory(excludeRuleConverterStub)
-    private final ComponentIdentifier componentId = new ComponentIdentifier() {
-        @Override
-        String getDisplayName() {
-            return "example"
-        }
-    }
-
-    def canConvert() {
-        expect:
-        !externalModuleDependencyDescriptorFactory.canConvert(Mock(ProjectDependency))
-        externalModuleDependencyDescriptorFactory.canConvert(Mock(ExternalModuleDependency))
-    }
-
-    def testAddWithNullGroupAndNullVersionShouldHaveEmptyStringModuleRevisionValues() {
-        when:
-        ModuleDependency dependency = new DefaultExternalModuleDependency(null, "gradle-core", null, TEST_DEP_CONF)
-        LocalOriginDependencyMetadata dependencyMetaData = externalModuleDependencyDescriptorFactory.createDependencyDescriptor(componentId, TEST_CONF, null, dependency)
-        ModuleComponentSelector selector = (ModuleComponentSelector) dependencyMetaData.getSelector()
-
-        then:
-        selector.group == ""
-        selector.module == "gradle-core"
-        selector.version == ""
-        selector.versionConstraint.preferredVersion == ""
-    }
-
-    def "test create from module dependency"() {
-        when:
-        def configuration = withArtifacts ? null : TEST_DEP_CONF
-        DefaultExternalModuleDependency moduleDependency = new DefaultExternalModuleDependency("org.gradle", "gradle-core", "1.0", configuration)
-        setUpDependency(moduleDependency, withArtifacts)
-
-        LocalOriginDependencyMetadata dependencyMetaData = externalModuleDependencyDescriptorFactory.createDependencyDescriptor(componentId, TEST_CONF, null, moduleDependency)
-
-        then:
-        moduleDependency.changing == dependencyMetaData.changing
-        moduleDependency.force == dependencyMetaData.force
-        moduleDependency.group == dependencyMetaData.selector.group
-        moduleDependency.name == dependencyMetaData.selector.module
-        moduleDependency.version == dependencyMetaData.selector.version
-        ((VersionConstraintInternal) moduleDependency.getVersionConstraint()).asImmutable() == dependencyMetaData.selector.versionConstraint
-        assertDependencyDescriptorHasCommonFixtureValues(dependencyMetaData, withArtifacts)
-
-        where:
-        withArtifacts << [true, false]
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyMetadataConverterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyMetadataConverterTest.groovy
new file mode 100644
index 0000000..f19431b
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ExternalModuleDependencyMetadataConverterTest.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2010 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies
+
+import org.gradle.api.artifacts.ExternalModuleDependency
+import org.gradle.api.artifacts.ModuleDependency
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.artifacts.component.ModuleComponentSelector
+import org.gradle.api.internal.artifacts.VersionConstraintInternal
+import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata
+
+class ExternalModuleDependencyMetadataConverterTest extends AbstractDependencyDescriptorFactoryInternalSpec {
+
+    ExternalModuleDependencyMetadataConverter converter = new ExternalModuleDependencyMetadataConverter(excludeRuleConverterStub)
+    private final ComponentIdentifier componentId = new ComponentIdentifier() {
+        @Override
+        String getDisplayName() {
+            return "example"
+        }
+    }
+
+    def canConvert() {
+        expect:
+        !converter.canConvert(Mock(ProjectDependency))
+        converter.canConvert(Mock(ExternalModuleDependency))
+    }
+
+    def testAddWithNullGroupAndNullVersionShouldHaveEmptyStringModuleRevisionValues() {
+        when:
+        ModuleDependency dependency = new DefaultExternalModuleDependency(null, "gradle-core", null, TEST_DEP_CONF)
+        LocalOriginDependencyMetadata dependencyMetaData = converter.createDependencyMetadata(componentId, TEST_CONF, null, dependency)
+        ModuleComponentSelector selector = (ModuleComponentSelector) dependencyMetaData.getSelector()
+
+        then:
+        selector.group == ""
+        selector.module == "gradle-core"
+        selector.version == ""
+        selector.versionConstraint.preferredVersion == ""
+    }
+
+    def "test create from module dependency"() {
+        when:
+        def configuration = withArtifacts ? null : TEST_DEP_CONF
+        DefaultExternalModuleDependency moduleDependency = new DefaultExternalModuleDependency("org.gradle", "gradle-core", "1.0", configuration)
+        setUpDependency(moduleDependency, withArtifacts)
+
+        LocalOriginDependencyMetadata dependencyMetaData = converter.createDependencyMetadata(componentId, TEST_CONF, null, moduleDependency)
+
+        then:
+        moduleDependency.changing == dependencyMetaData.changing
+        moduleDependency.force == dependencyMetaData.force
+        moduleDependency.group == dependencyMetaData.selector.group
+        moduleDependency.name == dependencyMetaData.selector.module
+        moduleDependency.version == dependencyMetaData.selector.version
+        ((VersionConstraintInternal) moduleDependency.getVersionConstraint()).asImmutable() == dependencyMetaData.selector.versionConstraint
+        assertDependencyDescriptorHasCommonFixtureValues(dependencyMetaData, withArtifacts)
+
+        where:
+        withArtifacts << [true, false]
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy
deleted file mode 100644
index f41a5bc..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyDescriptorFactoryTest.groovy
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2010 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies
-
-import org.gradle.api.Project
-import org.gradle.api.artifacts.ExternalModuleDependency
-import org.gradle.api.artifacts.ProjectDependency
-import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.internal.artifacts.DefaultBuildIdentifier
-import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
-import org.gradle.api.internal.attributes.ImmutableAttributes
-import org.gradle.api.internal.tasks.DefaultTaskDependencyFactory
-import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
-import org.gradle.internal.component.model.LocalOriginDependencyMetadata
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Path
-import org.gradle.util.TestUtil
-import org.junit.Rule
-
-class ProjectDependencyDescriptorFactoryTest extends AbstractDependencyDescriptorFactoryInternalSpec {
-
-    private ProjectIvyDependencyDescriptorFactory projectDependencyDescriptorFactory = new ProjectIvyDependencyDescriptorFactory(excludeRuleConverterStub)
-    private final ComponentIdentifier componentId = new ComponentIdentifier() {
-        @Override
-        String getDisplayName() {
-            return "example"
-        }
-    }
-
-    @Rule
-    TestNameTestDirectoryProvider temporaryFolder = TestNameTestDirectoryProvider.newInstance(getClass())
-
-    def canConvert() {
-        expect:
-        projectDependencyDescriptorFactory.canConvert(Mock(ProjectDependency))
-        !projectDependencyDescriptorFactory.canConvert(Mock(ExternalModuleDependency))
-    }
-
-    def "test create from project dependency"() {
-        when:
-        def configuration = withArtifacts ? null : TEST_DEP_CONF
-        ProjectDependency projectDependency = createProjectDependency(configuration)
-        setUpDependency(projectDependency, withArtifacts)
-        LocalOriginDependencyMetadata dependencyMetaData = projectDependencyDescriptorFactory.createDependencyDescriptor(componentId, TEST_CONF, null, projectDependency)
-
-        then:
-        assertDependencyDescriptorHasCommonFixtureValues(dependencyMetaData, withArtifacts)
-        !dependencyMetaData.changing
-        !dependencyMetaData.force
-        dependencyMetaData.selector == new DefaultProjectComponentSelector(DefaultBuildIdentifier.ROOT, Path.ROOT, Path.ROOT, "root", ImmutableAttributes.EMPTY, [])
-        projectDependency == dependencyMetaData.source
-
-        where:
-        withArtifacts << [true, false]
-    }
-
-    private ProjectDependency createProjectDependency(String dependencyConfiguration) {
-        Project dependencyProject = TestUtil.create(temporaryFolder).rootProject()
-        dependencyProject.setGroup("someGroup")
-        dependencyProject.setVersion("someVersion")
-        if (dependencyConfiguration != null) {
-            dependencyProject.configurations.create(dependencyConfiguration)
-        }
-        return new DefaultProjectDependency(dependencyProject, dependencyConfiguration, true, DefaultTaskDependencyFactory.withNoAssociatedProject())
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyMetadataConverterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyMetadataConverterTest.groovy
new file mode 100644
index 0000000..2b260e9
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/moduleconverter/dependencies/ProjectDependencyMetadataConverterTest.groovy
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 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.api.internal.artifacts.ivyservice.moduleconverter.dependencies
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.ExternalModuleDependency
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.internal.artifacts.DefaultBuildIdentifier
+import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
+import org.gradle.api.internal.attributes.ImmutableAttributes
+import org.gradle.api.internal.tasks.DefaultTaskDependencyFactory
+import org.gradle.internal.component.local.model.DefaultProjectComponentSelector
+import org.gradle.internal.component.model.LocalOriginDependencyMetadata
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.util.Path
+import org.gradle.util.TestUtil
+import org.junit.Rule
+
+class ProjectDependencyMetadataConverterTest extends AbstractDependencyDescriptorFactoryInternalSpec {
+
+    private ProjectDependencyMetadataConverter converter = new ProjectDependencyMetadataConverter(excludeRuleConverterStub)
+    private final ComponentIdentifier componentId = new ComponentIdentifier() {
+        @Override
+        String getDisplayName() {
+            return "example"
+        }
+    }
+
+    @Rule
+    TestNameTestDirectoryProvider temporaryFolder = TestNameTestDirectoryProvider.newInstance(getClass())
+
+    def canConvert() {
+        expect:
+        converter.canConvert(Mock(ProjectDependency))
+        !converter.canConvert(Mock(ExternalModuleDependency))
+    }
+
+    def "test create from project dependency"() {
+        when:
+        def configuration = withArtifacts ? null : TEST_DEP_CONF
+        ProjectDependency projectDependency = createProjectDependency(configuration)
+        setUpDependency(projectDependency, withArtifacts)
+        LocalOriginDependencyMetadata dependencyMetaData = converter.createDependencyMetadata(componentId, TEST_CONF, null, projectDependency)
+
+        then:
+        assertDependencyDescriptorHasCommonFixtureValues(dependencyMetaData, withArtifacts)
+        !dependencyMetaData.changing
+        !dependencyMetaData.force
+        dependencyMetaData.selector == new DefaultProjectComponentSelector(DefaultBuildIdentifier.ROOT, Path.ROOT, Path.ROOT, "root", ImmutableAttributes.EMPTY, [])
+        projectDependency == dependencyMetaData.source
+
+        where:
+        withArtifacts << [true, false]
+    }
+
+    private ProjectDependency createProjectDependency(String dependencyConfiguration) {
+        Project dependencyProject = TestUtil.create(temporaryFolder).rootProject()
+        dependencyProject.setGroup("someGroup")
+        dependencyProject.setVersion("someVersion")
+        if (dependencyConfiguration != null) {
+            dependencyProject.configurations.create(dependencyConfiguration)
+        }
+        return new DefaultProjectDependency(dependencyProject, dependencyConfiguration, true, DefaultTaskDependencyFactory.withNoAssociatedProject())
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
index d8f0afa..2d3e496 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/projectmodule/ProjectDependencyResolverTest.groovy
@@ -39,7 +39,7 @@
     final ComponentIdentifierFactory componentIdentifierFactory = Mock()
     final ProjectStateRegistry projectRegistry = Stub()
     final ProjectArtifactResolver projectArtifactResolver = Stub()
-    final ProjectDependencyResolver resolver = new ProjectDependencyResolver(registry, componentIdentifierFactory, projectArtifactResolver)
+    final ProjectDependencyResolver resolver = new ProjectDependencyResolver(registry, projectArtifactResolver)
 
     def setup() {
         def projectState = Stub(ProjectState)
@@ -61,7 +61,6 @@
         resolver.resolve(dependencyMetaData, null, null, result)
 
         then:
-        1 * componentIdentifierFactory.createProjectComponentIdentifier(selector) >> id
         1 * registry.getComponent(id) >> componentState
         1 * result.resolved(componentState)
         0 * result._
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
index 0692dd8..dc71af5 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/DependencyGraphBuilderTest.groovy
@@ -15,10 +15,8 @@
  */
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine
 
-import com.google.common.collect.ImmutableSet
 import org.apache.ivy.core.module.id.ModuleRevisionId
 import org.gradle.api.Action
-import org.gradle.api.artifacts.DependencySet
 import org.gradle.api.artifacts.ModuleDependency
 import org.gradle.api.artifacts.ModuleVersionIdentifier
 import org.gradle.api.artifacts.ResolveException
@@ -28,7 +26,7 @@
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.DependencyManagementTestUtil
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.ResolveContext
 import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal
 import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint
 import org.gradle.api.internal.artifacts.dsl.ModuleReplacementsData
@@ -47,16 +45,19 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.DefaultCapabilitiesConflictHandler
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.DefaultConflictHandler
 import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
+import org.gradle.api.internal.attributes.AttributeDesugaring
 import org.gradle.api.internal.attributes.AttributesSchemaInternal
 import org.gradle.api.internal.attributes.ImmutableAttributes
-import org.gradle.api.internal.initialization.RootScriptDomainObjectContext
 import org.gradle.api.specs.Specs
 import org.gradle.internal.component.external.descriptor.DefaultExclude
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
 import org.gradle.internal.component.external.model.ImmutableCapabilities
-import org.gradle.internal.component.local.model.DefaultLocalComponentGraphResolveState
 import org.gradle.internal.component.local.model.DefaultLocalComponentMetadata
+import org.gradle.internal.component.local.model.DefaultLocalConfigurationMetadata
 import org.gradle.internal.component.local.model.DslOriginDependencyMetadataWrapper
+import org.gradle.internal.component.local.model.LocalComponentGraphResolveStateFactory
+import org.gradle.internal.component.local.model.PublishArtifactLocalArtifactMetadata
+import org.gradle.internal.component.model.ComponentIdGenerator
 import org.gradle.internal.component.model.ComponentOverrideMetadata
 import org.gradle.internal.component.model.ComponentResolveMetadata
 import org.gradle.internal.component.model.DependencyMetadata
@@ -80,7 +81,7 @@
 import static org.gradle.internal.component.local.model.TestComponentIdentifiers.newProjectId
 
 class DependencyGraphBuilderTest extends Specification {
-    def configuration = Mock(ConfigurationInternal) {
+    def resolveContext = Mock(ResolveContext) {
         getResolutionStrategy() >> Stub(ResolutionStrategyInternal)
     }
     def conflictResolver = Mock(ModuleConflictResolver)
@@ -88,7 +89,7 @@
     def metaDataResolver = Mock(ComponentMetaDataResolver)
     def attributesSchema = Mock(AttributesSchemaInternal)
     def attributes = ImmutableAttributes.EMPTY
-    def root = rootProject('root', '1.0', ['root'])
+    def root = rootProject()
     def moduleReplacements = Mock(ModuleReplacementsData)
     def moduleIdentifierFactory = Mock(ImmutableModuleIdentifierFactory) {
         module(_, _) >> { args ->
@@ -117,21 +118,21 @@
     def capabilitiesConflictHandler = new DefaultCapabilitiesConflictHandler()
     def versionComparator = new DefaultVersionComparator()
     def versionSelectorScheme = new DefaultVersionSelectorScheme(versionComparator, new VersionParser())
+    def desugaring = new AttributeDesugaring(AttributeTestUtil.attributesFactory())
+    def resolveStateFactory = new LocalComponentGraphResolveStateFactory(desugaring, new ComponentIdGenerator())
 
     DependencyGraphBuilder builder
 
     def setup() {
-        _ * configuration.name >> 'root'
-        _ * configuration.path >> 'root'
-        _ * configuration.allDependencies >> Stub(DependencySet)
-        _ * configuration.toRootComponentMetaData() >> root
+        _ * resolveContext.name >> 'root'
+        _ * resolveContext.toRootComponentMetaData() >> root
 
-        builder = new DependencyGraphBuilder(idResolver, metaDataResolver, moduleConflictHandler, capabilitiesConflictHandler, Specs.satisfyAll(), attributesSchema, moduleExclusions, buildOperationProcessor, dependencySubstitutionApplicator, componentSelectorConverter, AttributeTestUtil.attributesFactory(), versionSelectorScheme, versionComparator.asVersionComparator(), new VersionParser())
+        builder = new DependencyGraphBuilder(idResolver, metaDataResolver, moduleConflictHandler, capabilitiesConflictHandler, Specs.satisfyAll(), attributesSchema, moduleExclusions, buildOperationProcessor, dependencySubstitutionApplicator, componentSelectorConverter, AttributeTestUtil.attributesFactory(), desugaring, versionSelectorScheme, versionComparator.asVersionComparator(), resolveStateFactory, new ComponentIdGenerator(), new VersionParser())
     }
 
     private TestGraphVisitor resolve(DependencyGraphBuilder builder = this.builder) {
         def graphVisitor = new TestGraphVisitor()
-        builder.resolve(configuration, graphVisitor, false)
+        builder.resolve(resolveContext, graphVisitor, false)
         return graphVisitor
     }
 
@@ -577,7 +578,7 @@
     def "does not include filtered dependencies"() {
         given:
         def spec = { DependencyMetadata dep -> dep.selector.module != 'c' }
-        builder = new DependencyGraphBuilder(idResolver, metaDataResolver, moduleConflictHandler, capabilitiesConflictHandler, spec, attributesSchema, moduleExclusions, buildOperationProcessor, dependencySubstitutionApplicator, componentSelectorConverter, AttributeTestUtil.attributesFactory(), versionSelectorScheme, Stub(Comparator), new VersionParser())
+        builder = new DependencyGraphBuilder(idResolver, metaDataResolver, moduleConflictHandler, capabilitiesConflictHandler, spec, attributesSchema, moduleExclusions, buildOperationProcessor, dependencySubstitutionApplicator, componentSelectorConverter, AttributeTestUtil.attributesFactory(), desugaring, versionSelectorScheme, Stub(Comparator), resolveStateFactory, new ComponentIdGenerator(), new VersionParser())
 
         def a = revision('a')
         def b = revision('b')
@@ -1023,20 +1024,38 @@
     def revision(String name, String revision = '1.0') {
         // TODO Shouldn't really be using the local component implementation here
         def id = newId("group", name, revision)
-        def metaData = new DefaultLocalComponentMetadata(id, DefaultModuleComponentIdentifier.newId(id), "release", attributesSchema, RootScriptDomainObjectContext.INSTANCE, TestUtil.calculatedValueContainerFactory())
-        def defaultConfiguration = metaData.addConfiguration("default", "defaultConfig", [] as Set<String>, ImmutableSet.of("default"), true, true, attributes, true, null, true, ImmutableCapabilities.EMPTY)
-        defaultConfiguration.addArtifacts([new DefaultPublishArtifact("art1", "zip", "art", null, new Date(), new File("art1.zip"))])
-        return metaData
+        def componentId = DefaultModuleComponentIdentifier.newId(id)
+
+        def artifacts = [new PublishArtifactLocalArtifactMetadata(componentId, new DefaultPublishArtifact("art1", "zip", "art", null, new Date(), new File("art1.zip")))]
+        def defaultConfiguration = new DefaultLocalConfigurationMetadata(
+            "default", "defaultConfig", componentId, true, true, ["default"] as Set, attributes, ImmutableCapabilities.EMPTY,
+            true, false, true, [], [] as Set, [],
+            [] as Set, TestUtil.calculatedValueContainerFactory(), artifacts
+        )
+
+        def configurations = new DefaultLocalComponentMetadata.ConfigurationsMapMetadataFactory(["default": defaultConfiguration])
+        return new DefaultLocalComponentMetadata(id, componentId, "release", attributesSchema, configurations, null)
     }
 
-    def rootProject(String name, String revision = '1.0', List<String> extraConfigs = []) {
-        def metaData = new DefaultLocalComponentMetadata(newId("group", name, revision), newProjectId(":${name}"), "release", attributesSchema, RootScriptDomainObjectContext.INSTANCE, TestUtil.calculatedValueContainerFactory())
-        def defaultConfiguration = metaData.addConfiguration("default", "defaultConfig", [] as Set<String>, ImmutableSet.of("default"), true, true, attributes, true, null, true, ImmutableCapabilities.EMPTY)
-        extraConfigs.each { String config ->
-            metaData.addConfiguration(config, "${config}Config", ["default"] as Set<String>, ImmutableSet.of("default", config), true, true, attributes, true, null, true, ImmutableCapabilities.EMPTY)
-        }
-        defaultConfiguration.addArtifacts([new DefaultPublishArtifact("art1", "zip", "art", null, new Date(), new File("art1.zip"))])
-        return metaData
+    def rootProject() {
+        // TODO Shouldn't really be using the local component implementation here
+        def componentId = newProjectId(":root")
+
+        def artifacts = [new PublishArtifactLocalArtifactMetadata(componentId, new DefaultPublishArtifact("art1", "zip", "art", null, new Date(), new File("art1.zip")))]
+        def defaultConfiguration = new DefaultLocalConfigurationMetadata(
+            "default", "defaultConfig", componentId, true, true, ["default"] as Set, attributes, ImmutableCapabilities.EMPTY,
+            true, false, true, [], [] as Set, [],
+            [] as Set, TestUtil.calculatedValueContainerFactory(), artifacts
+        )
+
+        def rootConfiguration = new DefaultLocalConfigurationMetadata(
+            "root", "rootConfig", componentId, true, true, ["default", "root"] as Set, attributes, ImmutableCapabilities.EMPTY,
+            true, false, true, defaultConfiguration.getDependencies(), [] as Set, [],
+            [] as Set, TestUtil.calculatedValueContainerFactory(), []
+        )
+
+        def configurations = new DefaultLocalComponentMetadata.ConfigurationsMapMetadataFactory(["default": defaultConfiguration, "root": rootConfiguration])
+        return new DefaultLocalComponentMetadata(newId("group", "root", "1.0"), componentId, "release", attributesSchema, configurations, null)
     }
 
     def traverses(Map<String, ?> args = [:], def from, ComponentResolveMetadata to) {
@@ -1045,7 +1064,7 @@
         println "Traverse $from to ${to.id}"
         1 * metaDataResolver.resolve(to.id, _, _) >> { ComponentIdentifier id, ComponentOverrideMetadata requestMetaData, BuildableComponentResolveResult result ->
             println "Called ${to.id}"
-            result.resolved(new DefaultLocalComponentGraphResolveState(to))
+            result.resolved(resolveStateFactory.stateFor(to))
         }
     }
 
@@ -1111,7 +1130,7 @@
         dependencyMetaData = new DslOriginDependencyMetadataWrapper(dependencyMetaData, Stub(ModuleDependency) {
             getAttributes() >> ImmutableAttributes.EMPTY
         })
-        from.getConfiguration("default").addDependency(dependencyMetaData)
+        from.getConfiguration("default").getDependencies().add(dependencyMetaData)
         return dependencyMetaData
     }
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSetTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSetTest.groovy
index 367a0fc..5296fe0 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSetTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/DefaultArtifactSetTest.groovy
@@ -26,10 +26,11 @@
 import org.gradle.api.internal.attributes.AttributesSchemaInternal
 import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.internal.Describables
+import org.gradle.internal.component.external.model.DefaultImmutableCapability
 import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactIdentifier
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
 import org.gradle.internal.component.external.model.ImmutableCapabilities
-import org.gradle.internal.component.external.model.DefaultImmutableCapability
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata
 import org.gradle.internal.component.model.DefaultIvyArtifactName
 import spock.lang.Specification
 
@@ -37,6 +38,7 @@
     def componentId = Stub(ComponentIdentifier)
     def schema = Stub(AttributesSchemaInternal)
     def artifactTypeRegistry = Stub(ArtifactTypeRegistry)
+    def component = Stub(ComponentArtifactResolveMetadata)
 
     def setup() {
         artifactTypeRegistry.mapAttributesFor(_) >> ImmutableAttributes.EMPTY
@@ -50,7 +52,7 @@
         given:
         def artifacts1 = ArtifactSetFactory.createFromVariantMetadata(componentId, () -> ([variant1, variant2] as Set), [variant1, variant2] as Set, schema, ImmutableAttributes.EMPTY)
         def artifacts2 = ArtifactSetFactory.createFromVariantMetadata(componentId, () -> ([variant1] as Set), [variant1] as Set, schema, ImmutableAttributes.EMPTY)
-        def artifacts3 = ArtifactSetFactory.adHocVariant(componentId, ownerId, [] as Set, null, schema, null, ImmutableAttributes.EMPTY, ImmutableAttributes.EMPTY)
+        def artifacts3 = ArtifactSetFactory.adHocVariant(component, [] as Set, schema, null, ImmutableAttributes.EMPTY, ImmutableAttributes.EMPTY)
 
         ownerId.group >> "group"
         ownerId.name >> "name"
@@ -107,7 +109,7 @@
         given:
         def artifacts1 = ArtifactSetFactory.createFromVariantMetadata(componentId, () -> ([variant1, variant2] as Set), [variant1, variant2] as Set, schema, ImmutableAttributes.EMPTY)
         def artifacts2 = ArtifactSetFactory.createFromVariantMetadata(componentId, () -> ([variant1] as Set), [variant1] as Set, schema, ImmutableAttributes.EMPTY)
-        def artifacts3 = ArtifactSetFactory.adHocVariant(componentId, ownerId, [] as Set, null, schema, null, ImmutableAttributes.EMPTY, ImmutableAttributes.EMPTY)
+        def artifacts3 = ArtifactSetFactory.adHocVariant(component, [] as Set, schema, null, ImmutableAttributes.EMPTY, ImmutableAttributes.EMPTY)
 
         selector.select(_, _) >> resolvedVariant1
         ownerId.group >> "group"
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/IntersectionsTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/IntersectionsTest.groovy
index c189036..4addde8 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/IntersectionsTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/excludes/factories/IntersectionsTest.groovy
@@ -83,6 +83,7 @@
         moduleId("com.google.collections", "google-collections")                                                       | moduleIdSet(["com.sun.jmx", "jmxri"], ["javax.jms", "jms"], ["com.sun.jdmk", "jmxtools"])                             | nothing()
         moduleId("com.google.collections", "google-collections")                                                       | moduleIdSet(["com.sun.jmx", "jmxri"], ["com.google.collections", "google-collections"], ["com.sun.jdmk", "jmxtools"]) | moduleId("com.google.collections", "google-collections")
         moduleId("org", "foo")                                                                                         | moduleId("org2", "baz")                                                                                               | nothing()
+        moduleId("org", "foo")                                                                                         | moduleSet("foo", "baz")                                                                                               | moduleId("org", "foo")
 
         anyOf(group("org.slf4j"), module("py4j"))                                                                      | anyOf(group("org.slf4j"), module("py4j"), moduleIdSet(["org.jboss.netty", "netty"], ["jline", "jline"]))              | anyOf(group("org.slf4j"), module("py4j"))
         anyOf(group("G1"), module("M1"), group("G2"))                                                                  | anyOf(group("G1"), module("M2"))                                                                                      | anyOf(group("G1"), allOf(anyOf(module("M1"), group("G2")), module("M2")))
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeStateTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeStateTest.groovy
index a55fe6d..8258697 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeStateTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/builder/NodeStateTest.groovy
@@ -25,6 +25,7 @@
 import org.gradle.api.specs.Spec
 import org.gradle.internal.component.model.DependencyMetadata
 import org.gradle.internal.component.model.VariantGraphResolveMetadata
+import org.gradle.internal.component.model.VariantGraphResolveState
 import spock.lang.Specification
 
 class NodeStateTest extends Specification {
@@ -218,9 +219,12 @@
     }
 
     private NodeState nextNode(int outgoingEndorsing = 0) {
+        def state = Stub(VariantGraphResolveState)
         def metadata = Stub(VariantGraphResolveMetadata)
+        state.metadata >> metadata
         def resolveState = Stub(ResolveState)
-        def newState = new NodeState(idIdx++, null, Mock(ComponentState), resolveState, metadata, true)
+
+        def newState = new NodeState(idIdx++, null, Mock(ComponentState), resolveState, state, true)
         // if there are outgoing endorsing edges, also include a normal edge to make sure that it is filtered out
         metadata.dependencies >> ((0..<outgoingEndorsing).collect { edge(newState).dependencyMetadata } + (outgoingEndorsing > 0 ? [edge(newState, false).dependencyMetadata] : []))
         resolveState.moduleExclusions >> Mock(ModuleExclusions)
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultCapabilitiesConflictHandlerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultCapabilitiesConflictHandlerTest.groovy
index 041ad02..ac4643f 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultCapabilitiesConflictHandlerTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/conflicts/DefaultCapabilitiesConflictHandlerTest.groovy
@@ -27,6 +27,7 @@
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
 import org.gradle.internal.component.external.model.ImmutableCapabilities
 import org.gradle.internal.component.model.VariantGraphResolveMetadata
+import org.gradle.internal.component.model.VariantGraphResolveState
 import spock.lang.Issue
 import spock.lang.Specification
 import spock.lang.Subject
@@ -108,10 +109,14 @@
     }
 
     NodeState node(ComponentState cs) {
-        return new NodeState(id++, Mock(ResolvedConfigurationIdentifier) { getId() >> Mock(ModuleVersionIdentifier) }, cs, Mock(VariantGraphResolveMetadata) {
+        def metadata = Mock(VariantGraphResolveMetadata) {
             getDependencies() >> []
             getCapabilities() >> ImmutableCapabilities.of([])
-        }, true) {
+        }
+        def state = Mock(VariantGraphResolveState) {
+            getMetadata() >> metadata
+        }
+        return new NodeState(id++, Mock(ResolvedConfigurationIdentifier) { getId() >> Mock(ModuleVersionIdentifier) }, cs, state, true) {
             @Override
             boolean isSelected() {
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/SelectorStateResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/SelectorStateResolverTest.groovy
index 6358c88..3a3358f 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/SelectorStateResolverTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/graph/selectors/SelectorStateResolverTest.groovy
@@ -57,10 +57,12 @@
 import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_DEPENDENCY_WITH_REJECT
 import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_EMPTY
 import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_FOUR_DEPENDENCIES
-import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_PREFER
+import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_PREFER_BATCH1
+import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_PREFER_BATCH2
 import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_SINGLE
 import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_THREE_DEPENDENCIES
-import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_TWO_DEPENDENCIES
+import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_TWO_DEPENDENCIES_BATCH1
+import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_TWO_DEPENDENCIES_BATCH2
 import static org.gradle.resolve.scenarios.VersionRangeResolveTestScenarios.SCENARIOS_WITH_REJECT
 
 /**
@@ -90,7 +92,7 @@
         permutation << SCENARIOS_SINGLE
     }
 
-    def "resolve pair #permutation"() {
+    def "resolve pair #permutation (batch 1)"() {
         given:
         def candidates = permutation.candidates
         def expected = permutation.expectedSingle
@@ -99,7 +101,19 @@
         resolver(permutation.conflicts).resolve(candidates) == expected
 
         where:
-        permutation << SCENARIOS_TWO_DEPENDENCIES
+        permutation << SCENARIOS_TWO_DEPENDENCIES_BATCH1
+    }
+
+    def "resolve pair #permutation (batch 2)"() {
+        given:
+        def candidates = permutation.candidates
+        def expected = permutation.expectedSingle
+
+        expect:
+        resolver(permutation.conflicts).resolve(candidates) == expected
+
+        where:
+        permutation << SCENARIOS_TWO_DEPENDENCIES_BATCH2
     }
 
     def "resolve empty pair #permutation"() {
@@ -114,7 +128,7 @@
         permutation << SCENARIOS_EMPTY
     }
 
-    def "resolve prefer pair #permutation"() {
+    def "resolve prefer pair #permutation (batch 1)"() {
         given:
         def candidates = permutation.candidates
         def expected = permutation.expectedSingle
@@ -123,7 +137,19 @@
         resolver(permutation.conflicts).resolve(candidates) == expected
 
         where:
-        permutation << SCENARIOS_PREFER
+        permutation << SCENARIOS_PREFER_BATCH1
+    }
+
+    def "resolve prefer pair #permutation (batch 2)"() {
+        given:
+        def candidates = permutation.candidates
+        def expected = permutation.expectedSingle
+
+        expect:
+        resolver(permutation.conflicts).resolve(candidates) == expected
+
+        where:
+        permutation << SCENARIOS_PREFER_BATCH2
     }
 
     def "resolve reject pair #permutation"() {
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy
index e6d5e04..989ace3 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentIdentifierSerializerTest.groovy
@@ -68,7 +68,7 @@
 
     def "serializes root ProjectComponentIdentifier"() {
         given:
-        def identifier = new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier("build"), Path.ROOT, Path.ROOT, "someProject")
+        def identifier = new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier(Path.path(":build")), Path.ROOT, Path.ROOT, "someProject")
 
         when:
         def result = serialize(identifier, serializer)
@@ -82,7 +82,7 @@
 
     def "serializes root build ProjectComponentIdentifier"() {
         given:
-        def identifier = new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier("build"), Path.path(":a:b"), Path.path(":a:b"), "b")
+        def identifier = new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier(Path.path(":build")), Path.path(":a:b"), Path.path(":a:b"), "b")
 
         when:
         def result = serialize(identifier, serializer)
@@ -96,7 +96,7 @@
 
     def "serializes other build root ProjectComponentIdentifier"() {
         given:
-        def identifier = new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier("build"), Path.path(":prefix:someProject"), Path.ROOT, "someProject")
+        def identifier = new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier(Path.path(":build")), Path.path(":prefix:someProject"), Path.ROOT, "someProject")
 
         when:
         def result = serialize(identifier, serializer)
@@ -110,7 +110,7 @@
 
     def "serializes other build ProjectComponentIdentifier"() {
         given:
-        def identifier = new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier("build"), Path.path(":prefix:a:b"), Path.path(":a:b"), "b")
+        def identifier = new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier(Path.path(":build")), Path.path(":prefix:a:b"), Path.path(":a:b"), "b")
 
         when:
         def result = serialize(identifier, serializer)
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentResultSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentResultSerializerTest.groovy
index 54601cc..c5eb12d 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentResultSerializerTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentResultSerializerTest.groovy
@@ -16,32 +16,30 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result
 
-
+import org.gradle.api.artifacts.result.ResolvedComponentResult
 import org.gradle.api.artifacts.result.ResolvedVariantResult
 import org.gradle.api.attributes.Attribute
 import org.gradle.api.capabilities.Capability
-import org.gradle.api.internal.artifacts.DefaultImmutableModuleIdentifierFactory
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.DependencyManagementTestUtil
-import org.gradle.api.internal.attributes.ImmutableAttributes
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphComponent
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphVariant
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
-import org.gradle.internal.serialize.SerializerSpec
+import org.gradle.internal.component.model.ComponentGraphResolveMetadata
+import org.gradle.internal.component.model.ComponentGraphResolveState
+import org.gradle.internal.component.model.VariantGraphResolveState
+import org.gradle.internal.serialize.kryo.KryoBackedDecoder
+import org.gradle.internal.serialize.kryo.KryoBackedEncoder
 import org.gradle.util.AttributeTestUtil
-import org.gradle.util.TestUtil
+import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.newId
 
-class ComponentResultSerializerTest extends SerializerSpec {
-
-    private ComponentIdentifierSerializer componentIdentifierSerializer = new ComponentIdentifierSerializer()
+class ComponentResultSerializerTest extends Specification {
     def serializer = new ComponentResultSerializer(
-        new DefaultImmutableModuleIdentifierFactory(),
-        new ResolvedVariantResultSerializer(
-            componentIdentifierSerializer,
-            new DesugaredAttributeContainerSerializer(AttributeTestUtil.attributesFactory(), TestUtil.objectInstantiator())
-        ),
+        new ThisBuildOnlyComponentDetailsSerializer(),
+        new ThisBuildOnlySelectedVariantSerializer(),
         DependencyManagementTestUtil.componentSelectionDescriptorFactory(),
-        componentIdentifierSerializer,
         false
     )
 
@@ -51,74 +49,81 @@
         def attributes = AttributeTestUtil.attributesFactory().mutable()
         attributes.attribute(Attribute.of('type', String), 'custom')
         attributes.attribute(Attribute.of('format', String), 'jar')
-        def v1 = Mock(ResolvedVariantResult) {
-            getOwner() >> componentIdentifier
-            getDisplayName() >> "v1"
-            getAttributes() >> ImmutableAttributes.EMPTY
-            getCapabilities() >> [capability('foo')]
-            getExternalVariant() >> Optional.empty()
+        def v1Result = Mock(ResolvedVariantResult)
+        def v1State = Mock(VariantGraphResolveState) {
+            getInstanceId() >> 1
+            getVariantResult(null) >> v1Result
         }
-        def v3 = Mock(ResolvedVariantResult) {
+        def v1 = Mock(ResolvedGraphVariant) {
+            getNodeId() >> 1
+            getOwner() >> componentIdentifier
+            getResolveState() >> v1State
+            getExternalVariant() >> null
+        }
+        def v3Result = Mock(ResolvedVariantResult)
+        def v3State = Mock(VariantGraphResolveState) {
+            getInstanceId() >> 3
+            getVariantResult(null) >> v3Result
+        }
+        def v3 = Mock(ResolvedGraphVariant) {
+            getNodeId() >> 3
             getOwner() >> extId
-            getDisplayName() >> "v3"
-            getAttributes() >> attributes
-            getCapabilities() >> [capability('bar'), capability('baz')]
-            getExternalVariant() >> Optional.empty()
+            getResolveState() >> v3State
+            getExternalVariant() >> null
         }
-        def v2 = Mock(ResolvedVariantResult) {
+        def v2Result = Mock(ResolvedVariantResult)
+        def v2State = Mock(VariantGraphResolveState) {
+            getInstanceId() >> 2
+            getVariantResult(v3Result) >> v2Result
+        }
+        def v2 = Mock(ResolvedGraphVariant) {
+            getNodeId() >> 2
             getOwner() >> componentIdentifier
-            getDisplayName() >> "v2"
-            getAttributes() >> attributes
-            getCapabilities() >> [capability('bar'), capability('baz')]
-            getExternalVariant() >> Optional.of(v3)
+            getResolveState() >> v2State
+            getExternalVariant() >> v3
         }
-        def selection = new DetachedComponentResult(12L,
-            newId('org', 'foo', '2.0'),
-            ComponentSelectionReasons.requested(),
-            componentIdentifier, [v1, v2], [v1, v2],
-            'repoName')
+        def componentMetadata = Stub(ComponentGraphResolveMetadata) {
+            moduleVersionId >> newId('org', 'foo', '2.0')
+        }
+        def componentState = Stub(ComponentGraphResolveState) {
+            id >> componentIdentifier
+            metadata >> componentMetadata
+            repositoryId >> 'repoName'
+        }
+        def component = Stub(ResolvedGraphComponent) {
+            resolveState >> componentState
+            selectionReason >> ComponentSelectionReasons.requested()
+            selectedVariants >> [v1, v2]
+        }
 
         when:
-        def result = serialize(selection, serializer)
+        def serialized = serialize(component)
+        def result = deserialize(serialized)
 
         then:
-        result.resultId == 12L
+        result.id == componentIdentifier
         result.selectionReason == ComponentSelectionReasons.requested()
         result.moduleVersion == newId('org', 'foo', '2.0')
-        result.componentId == componentIdentifier
-        for (def variants : [result.resolvedVariants, result.allVariants]) {
+        for (def variants : [result.selectedVariants, result.availableVariants]) {
             variants.size() == 2
-            variants[0].displayName == 'v1'
-            variants[0].attributes == ImmutableAttributes.EMPTY
-            variants[0].capabilities.size() == 1
-            variants[0].capabilities[0].group == 'org'
-            variants[0].capabilities[0].name == 'foo'
-            variants[0].capabilities[0].version == '1.0'
-            variants[0].owner == componentIdentifier
-            variants[1].displayName == 'v2'
-            variants[1].attributes == attributes.asImmutable()
-            variants[1].capabilities.size() == 2
-            variants[1].capabilities[0].group == 'org'
-            variants[1].capabilities[0].name == 'bar'
-            variants[1].capabilities[0].version == '1.0'
-            variants[1].capabilities[1].group == 'org'
-            variants[1].capabilities[1].name == 'baz'
-            variants[1].capabilities[1].version == '1.0'
-            variants[1].owner == componentIdentifier
-            variants[1].externalVariant.present
-            def external = variants[1].externalVariant.get()
-            external.displayName == 'v3'
-            external.attributes == attributes.asImmutable()
-            external.capabilities.size() == 2
-            external.capabilities[0].group == 'org'
-            external.capabilities[0].name == 'bar'
-            external.capabilities[0].version == '1.0'
-            external.capabilities[1].group == 'org'
-            external.capabilities[1].name == 'baz'
-            external.capabilities[1].version == '1.0'
-            external.owner == extId
+            variants[0] == v1Result
+            variants[1] == v2Result
         }
-        result.repositoryName == 'repoName'
+        result.repositoryId == 'repoName'
+    }
+
+    private byte[] serialize(ResolvedGraphComponent component) {
+        def outstr = new ByteArrayOutputStream()
+        def encoder = new KryoBackedEncoder(outstr)
+        serializer.write(encoder, component)
+        encoder.flush()
+        return outstr.toByteArray()
+    }
+
+    private ResolvedComponentResult deserialize(byte[] serialized) {
+        def builder = new DefaultResolutionResultBuilder()
+        serializer.readInto(new KryoBackedDecoder(new ByteArrayInputStream(serialized)), builder)
+        return builder.complete(0).root
     }
 
     private Capability capability(String name) {
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy
index ac9483b..bac2f7b 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/ComponentSelectorSerializerTest.groovy
@@ -62,7 +62,7 @@
 
     def "serializes root project ProjectComponentSelector"() {
         given:
-        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier("build"), Path.ROOT, Path.ROOT, "rootProject", ImmutableAttributes.EMPTY, capabilities())
+        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier(path(":build")), Path.ROOT, Path.ROOT, "rootProject", ImmutableAttributes.EMPTY, capabilities())
 
         when:
         def result = serialize(selector, serializer)
@@ -77,7 +77,7 @@
 
     def "serializes root build ProjectComponentSelector"() {
         given:
-        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier("build"), path(":a:b"), path(":a:b"), "b", ImmutableAttributes.EMPTY, capabilities())
+        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier(path(":build")), path(":a:b"), path(":a:b"), "b", ImmutableAttributes.EMPTY, capabilities())
 
         when:
         def result = serialize(selector, serializer)
@@ -92,7 +92,7 @@
 
     def "serializes other build root ProjectComponentSelector"() {
         given:
-        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier("build"), path(":prefix"), Path.ROOT, "someProject", ImmutableAttributes.EMPTY, capabilities())
+        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier(path(":build")), path(":prefix"), Path.ROOT, "someProject", ImmutableAttributes.EMPTY, capabilities())
 
         when:
         def result = serialize(selector, serializer)
@@ -107,7 +107,7 @@
 
     def "serializes other build ProjectComponentSelector"() {
         given:
-        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier("build"), path(":prefix:a:b"), path(":a:b"), "b", ImmutableAttributes.EMPTY, capabilities())
+        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier(path(":build")), path(":prefix:a:b"), path(":a:b"), "b", ImmutableAttributes.EMPTY, capabilities())
 
         when:
         def result = serialize(selector, serializer)
@@ -122,7 +122,7 @@
 
     def "serializes ProjectComponentSelector with attributes"() {
         given:
-        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier(buildId), identityPath, projectPath, projectName, AttributeTestUtil.attributes(foo: 'x', bar: 'y'), capabilities())
+        def selector = new DefaultProjectComponentSelector(new DefaultBuildIdentifier(path(":build")), identityPath, projectPath, projectName, AttributeTestUtil.attributes(foo: 'x', bar: 'y'), capabilities())
 
         when:
         def result = serialize(selector, serializer)
@@ -137,11 +137,10 @@
         result.requestedCapabilities == selector.requestedCapabilities
 
         where:
-        buildId | identityPath                       | projectPath       | projectName
-        'build' | path(":prefix:a:b")           | path(":a:b") | 'b'
-        'build' | path(":prefix:a:someProject") | Path.ROOT         | "someProject"
-        'build' | Path.ROOT                          | Path.ROOT         | "rootProject"
-
+        identityPath                  | projectPath  | projectName
+        path(":prefix:a:b")           | path(":a:b") | 'b'
+        path(":prefix:a:someProject") | Path.ROOT    | "someProject"
+        Path.ROOT                     | Path.ROOT    | "rootProject"
     }
 
     def "serializes ModuleComponentSelector"() {
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy
index 200bfc0..e70a512 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DefaultResolutionResultBuilderSpec.groovy
@@ -18,7 +18,6 @@
 
 import org.gradle.api.artifacts.ModuleVersionIdentifier
 import org.gradle.api.artifacts.component.ComponentIdentifier
-import org.gradle.api.artifacts.component.ComponentSelector
 import org.gradle.api.artifacts.result.ComponentSelectionReason
 import org.gradle.api.artifacts.result.ResolvedVariantResult
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
@@ -27,6 +26,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphDependency
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
 import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
+import org.gradle.internal.component.model.ComponentGraphResolveState
 import org.gradle.internal.resolve.ModuleVersionResolveException
 import spock.lang.Specification
 
@@ -49,10 +49,10 @@
         node("leaf3")
         node("leaf4")
 
-        resolvedConf("root", [dep("mid1"), dep("mid2")])
+        resolvedConf("root", [dep("root", "mid1"), dep("root", "mid2")])
 
-        resolvedConf("mid1", [dep("leaf1"), dep("leaf2")])
-        resolvedConf("mid2", [dep("leaf3"), dep("leaf4")])
+        resolvedConf("mid1", [dep("mid1", "leaf1"), dep("mid1", "leaf2")])
+        resolvedConf("mid2", [dep("mid2", "leaf3"), dep("mid2", "leaf4")])
 
         resolvedConf("leaf1", [])
         resolvedConf("leaf2", [])
@@ -80,10 +80,10 @@
         node("b2")
         node("b3")
 
-        resolvedConf("a", [dep("b1"), dep("b2"), dep("b3")])
+        resolvedConf("a", [dep("a", "b1"), dep("a", "b2"), dep("a", "b3")])
 
-        resolvedConf("b1", [dep("b2"), dep("b3")])
-        resolvedConf("b2", [dep("b3")])
+        resolvedConf("b1", [dep("b1", "b2"), dep("b1", "b3")])
+        resolvedConf("b2", [dep("b2", "b3")])
         resolvedConf("b3", [])
 
         when:
@@ -105,9 +105,9 @@
         node("a")
         node("b")
         node("c")
-        resolvedConf("a", [dep("b")])
-        resolvedConf("b", [dep("c")])
-        resolvedConf("c", [dep("a")])
+        resolvedConf("a", [dep("a", "b")])
+        resolvedConf("b", [dep("b", "c")])
+        resolvedConf("c", [dep("c", "a")])
 
         when:
         def result = builder.complete(id("a"))
@@ -126,7 +126,7 @@
         node("b", ComponentSelectionReasons.of(ComponentSelectionReasons.FORCED))
         node("c", ComponentSelectionReasons.of(ComponentSelectionReasons.CONFLICT_RESOLUTION))
         node("d")
-        resolvedConf("a", [dep("b"), dep("c"), dep("d", new RuntimeException("Boo!"))])
+        resolvedConf("a", [dep("a", "b"), dep("a", "c"), dep("a", "d", new RuntimeException("Boo!"))])
         resolvedConf("b", [])
         resolvedConf("c", [])
         resolvedConf("d", [])
@@ -147,9 +147,9 @@
         node("a")
         node("b")
         node("c")
-        resolvedConf("a", [dep("b")])
-        resolvedConf("b", [dep("c")])
-        resolvedConf("c", [dep("a")])
+        resolvedConf("a", [dep("a", "b")])
+        resolvedConf("b", [dep("b", "c")])
+        resolvedConf("c", [dep("c", "a")])
 
         when:
         def a = builder.complete(id("a")).root
@@ -177,11 +177,11 @@
         node("leaf1")
         node("leaf2")
 
-        resolvedConf("root", [dep("mid1")])
+        resolvedConf("root", [dep("root", "mid1")])
 
-        resolvedConf("mid1", [dep("leaf1")])
-        resolvedConf("mid1", [dep("leaf1")]) //dupe
-        resolvedConf("mid1", [dep("leaf2")])
+        resolvedConf("mid1", [dep("mid1", "leaf1")])
+        resolvedConf("mid1", [dep("mid1", "leaf1")]) //dupe
+        resolvedConf("mid1", [dep("mid1", "leaf2")])
 
         resolvedConf("leaf1", [])
         resolvedConf("leaf2", [])
@@ -203,11 +203,11 @@
         node("mid1")
         node("leaf1")
         node("leaf2")
-        resolvedConf("root", [dep("mid1")])
+        resolvedConf("root", [dep("root", "mid1")])
 
-        resolvedConf("mid1", [dep("leaf1", new RuntimeException("foo!"))])
-        resolvedConf("mid1", [dep("leaf1", new RuntimeException("bar!"))]) //dupe
-        resolvedConf("mid1", [dep("leaf2", new RuntimeException("baz!"))])
+        resolvedConf("mid1", [dep("mid1", "leaf1", new RuntimeException("foo!"))])
+        resolvedConf("mid1", [dep("mid1", "leaf1", new RuntimeException("bar!"))]) //dupe
+        resolvedConf("mid1", [dep("mid1", "leaf2", new RuntimeException("baz!"))])
 
         when:
         def result = builder.complete(id("root"))
@@ -223,7 +223,7 @@
         node("a")
         node("b")
         node("c")
-        resolvedConf("a", [dep("b"), dep("c"), dep("U", new RuntimeException("unresolved!"))])
+        resolvedConf("a", [dep("a", "b"), dep("a", "c"), dep("a", "U", new RuntimeException("unresolved!"))])
         resolvedConf("b", [])
         resolvedConf("c", [])
 
@@ -240,7 +240,11 @@
 
     private void node(String module, ComponentSelectionReason reason = ComponentSelectionReasons.requested()) {
         DummyModuleVersionSelection moduleVersion = comp(module, reason)
-        builder.visitComponent(moduleVersion)
+        builder.startVisitComponent(moduleVersion.resultId, moduleVersion.selectionReason)
+        builder.visitComponentDetails(moduleVersion.componentId, moduleVersion.moduleVersion, "repo")
+        builder.visitSelectedVariant(moduleVersion.resultId, Stub(ResolvedVariantResult))
+        builder.visitComponentVariants([])
+        builder.endVisitComponent()
     }
 
     private DummyModuleVersionSelection comp(String module, ComponentSelectionReason reason = ComponentSelectionReasons.requested()) {
@@ -252,11 +256,17 @@
         builder.visitOutgoingEdges(id(module), deps)
     }
 
-    private ResolvedGraphDependency dep(String requested, Exception failure = null, String selected = requested) {
+    private ResolvedGraphDependency dep(String from, String requested, Exception failure = null, String selected = requested) {
         def selector = DefaultModuleComponentSelector.newSelector(DefaultModuleIdentifier.newId("x", requested), DefaultImmutableVersionConstraint.of("1"))
         def moduleVersionSelector = newSelector(DefaultModuleIdentifier.newId("x", requested), "1")
         failure = failure == null ? null : new ModuleVersionResolveException(moduleVersionSelector, failure)
-        new DummyInternalDependencyResult(requested: selector, selected: id(selected), failure: failure)
+        return Stub(ResolvedGraphDependency) {
+            getRequested() >> selector
+            getFromVariant() >> id(from)
+            getSelected() >> id(selected)
+            getSelectedVariant() >> id(selected)
+            getFailure() >> failure
+        }
     }
 
     private Long id(String module) {
@@ -268,18 +278,11 @@
         ModuleVersionIdentifier moduleVersion
         ComponentSelectionReason selectionReason
         ComponentIdentifier componentId
-        String repositoryName
-        List<ResolvedVariantResult> resolvedVariants = []
-        List<ResolvedVariantResult> allVariants = []
-    }
+        List<ResolvedVariantResult> selectedVariants = []
 
-    class DummyInternalDependencyResult implements ResolvedGraphDependency {
-        ComponentSelector requested
-        Long selected
-        ResolvedVariantResult fromVariant
-        ResolvedVariantResult selectedVariant
-        ModuleVersionResolveException failure
-        ComponentSelectionReason reason
-        boolean constraint
+        @Override
+        ComponentGraphResolveState getResolveState() {
+            throw new UnsupportedOperationException()
+        }
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DependencyResultSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DependencyResultSerializerTest.groovy
index 7e337f9..5691a25 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DependencyResultSerializerTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DependencyResultSerializerTest.groovy
@@ -26,17 +26,13 @@
 import org.gradle.internal.resolve.ModuleVersionResolveException
 import org.gradle.internal.serialize.InputStreamBackedDecoder
 import org.gradle.internal.serialize.OutputStreamBackedEncoder
-import org.gradle.util.AttributeTestUtil
-import org.gradle.util.TestUtil
 import spock.lang.Specification
 
 import static org.gradle.api.internal.artifacts.DefaultModuleVersionSelector.newSelector
 
 class DependencyResultSerializerTest extends Specification {
 
-    def serializer = new DependencyResultSerializer(new ResolvedVariantResultSerializer(
-        new ComponentIdentifierSerializer(),
-        new DesugaredAttributeContainerSerializer(AttributeTestUtil.attributesFactory(), TestUtil.objectInstantiator())), DependencyManagementTestUtil.componentSelectionDescriptorFactory())
+    def serializer = new DependencyResultSerializer(DependencyManagementTestUtil.componentSelectionDescriptorFactory())
 
     def "serializes successful dependency result"() {
         def requested = DefaultModuleComponentSelector.newSelector(DefaultModuleIdentifier.newId("org", "foo"), new DefaultMutableVersionConstraint("1.0"))
@@ -45,8 +41,10 @@
                 getResultId() >> 4L
                 getRequested() >> requested
             }
+            getFromVariant() >> 55L
             getFailure() >> null
             getSelected() >> 12L
+            getSelectedVariant() >> 123L
             getReason() >> ComponentSelectionReasons.requested()
         }
 
@@ -60,7 +58,9 @@
         then:
         out.requested == requested
         out.failure == null
+        out.fromVariant == 55L
         out.selected == 12L
+        out.selectedVariant == 123L
     }
 
     def "serializes failed dependency result"() {
@@ -73,8 +73,8 @@
                 getResultId() >> 4L
                 getRequested() >> requested
             }
+            getFromVariant() >> 55L
             getFailure() >> failure
-            getSelected() >> null
             getReason() >> ComponentSelectionReasons.of(ComponentSelectionReasons.CONFLICT_RESOLUTION)
         }
 
@@ -90,7 +90,9 @@
         then:
         out.requested == requested
         out.failure.cause.message == "Boo!"
+        out.fromVariant == 55L
         out.selected == null
+        out.selectedVariant == null
         out.reason.conflictResolution
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy
index b7c51ae..1497362 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/DummyBinaryStore.groovy
@@ -21,19 +21,19 @@
 import org.gradle.internal.serialize.InputStreamBackedDecoder
 import org.gradle.internal.serialize.OutputStreamBackedEncoder
 
-public class DummyBinaryStore implements BinaryStore {
+class DummyBinaryStore implements BinaryStore {
 
     private final ByteArrayOutputStream bytes = new ByteArrayOutputStream()
     private Encoder output = new OutputStreamBackedEncoder(bytes)
 
-    void write(BinaryStore.WriteAction write) {
+    void write(WriteAction write) {
         write.write(output)
     }
 
-    BinaryStore.BinaryData done() {
-        new BinaryStore.BinaryData() {
+    BinaryData done() {
+        new BinaryData() {
             Decoder decoder
-            def <T> T read(BinaryStore.ReadAction<T> readAction) {
+            def <T> T read(ReadAction<T> readAction) {
                 if (decoder == null) {
                     decoder = new InputStreamBackedDecoder(new ByteArrayInputStream(bytes.toByteArray()))
                 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy
index 0cc9a63..3cce0f1 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/ivyservice/resolveengine/result/StreamingResolutionResultBuilderTest.groovy
@@ -17,22 +17,22 @@
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.result
 
 import org.gradle.api.artifacts.result.ComponentSelectionReason
-import org.gradle.api.artifacts.result.ResolvedVariantResult
-import org.gradle.api.internal.artifacts.DefaultImmutableModuleIdentifierFactory
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.artifacts.DependencyManagementTestUtil
-import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory
 import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphComponent
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphEdge
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphNode
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphSelector
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.ResolvedGraphVariant
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.RootGraphNode
 import org.gradle.api.internal.attributes.AttributeDesugaring
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
 import org.gradle.internal.component.external.model.DefaultModuleComponentSelector
 import org.gradle.internal.component.local.model.LocalConfigurationGraphResolveMetadata
+import org.gradle.internal.component.model.ComponentGraphResolveMetadata
+import org.gradle.internal.component.model.ComponentGraphResolveState
 import org.gradle.internal.resolve.ModuleVersionResolveException
 import org.gradle.util.AttributeTestUtil
 import org.gradle.util.TestUtil
@@ -46,13 +46,12 @@
 import static org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ResolutionResultPrinter.printGraph
 
 class StreamingResolutionResultBuilderTest extends Specification {
-
-    final ImmutableModuleIdentifierFactory moduleIdentifierFactory = new DefaultImmutableModuleIdentifierFactory()
-    StreamingResolutionResultBuilder builder = new StreamingResolutionResultBuilder(
+    def builder = new StreamingResolutionResultBuilder(
         new DummyBinaryStore(),
         new DummyStore(),
-        moduleIdentifierFactory,
         new DesugaredAttributeContainerSerializer(AttributeTestUtil.attributesFactory(), TestUtil.objectInstantiator()),
+        new ThisBuildOnlyComponentDetailsSerializer(),
+        new ThisBuildOnlySelectedVariantSerializer(),
         new AttributeDesugaring(AttributeTestUtil.attributesFactory()),
         DependencyManagementTestUtil.componentSelectionDescriptorFactory(),
         false
@@ -82,8 +81,8 @@
         def selector2 = selector(2, "org", "dep2", "3.0")
         def dep1 = node(2, "org", "dep1", "2.0", of(CONFLICT_RESOLUTION))
         root.outgoingEdges >> [
-                dep(selector1, 2),
-                dep(selector2, new RuntimeException("Boo!"))
+                dep(selector1, 1, 2),
+                dep(selector2, 1, new RuntimeException("Boo!"))
         ]
 
         builder.start(root)
@@ -109,7 +108,7 @@
     def "visiting resolved module version again has no effect"() {
         def root = rootNode(1, "org", "root", "1.0")
         def selector = selector(7, "org", "dep1", "2.0")
-        root.outgoingEdges >> [dep(selector, 2)]
+        root.outgoingEdges >> [dep(selector, 1, 2)]
 
         builder.start(root)
 
@@ -137,15 +136,15 @@
     def "accumulates dependencies for all configurations of same component"() {
         def root = rootNode(1, "org", "root", "1.0")
         def selector1 = selector(10, "org", "dep1", "1.0")
-        root.outgoingEdges >> [dep(selector1, 2)]
+        root.outgoingEdges >> [dep(selector1, 1, 2)]
 
         def conf1 = node(2, "org", "dep1", "1.0")
         def selector2 = selector(11, "org", "dep2", "1.0")
-        conf1.outgoingEdges >> [dep(selector2, 3)]
+        conf1.outgoingEdges >> [dep(selector2, 2, 3)]
 
         def conf2 = node(2, "org", "dep1", "1.0")
         def selector3 = selector(12, "org", "dep3", "1.0")
-        conf2.outgoingEdges >> [dep(selector3, 4)]
+        conf2.outgoingEdges >> [dep(selector3, 2, 4)]
 
         builder.start(root)
 
@@ -181,12 +180,12 @@
         def selector1 = selector(10, "org", "dep1", "1.0")
         def selector2 = selector(11, "org", "dep2", "2.0")
         root.outgoingEdges >> [
-            dep(selector1, new RuntimeException()),
-            dep(selector2, 3)
+            dep(selector1, 1, new RuntimeException()),
+            dep(selector2, 1, 3)
         ]
         def dep2 = node(3, "org", "dep2", "2.0")
         def selector3 = selector(12, "org", "dep1", "5.0")
-        dep2.outgoingEdges >> [dep(selector3, new RuntimeException())]
+        dep2.outgoingEdges >> [dep(selector3, 3, new RuntimeException())]
 
         builder.start(root)
 
@@ -215,57 +214,70 @@
 """
     }
 
-    private DependencyGraphEdge dep(DependencyGraphSelector selector, Long selectedId) {
+    private DependencyGraphEdge dep(DependencyGraphSelector selector, Long fromVariant, Long selectedId) {
         def edge = Stub(DependencyGraphEdge)
         _ * edge.selector >> selector
         _ * edge.selected >> selectedId
         _ * edge.failure >> null
-        _ * edge.fromVariant >> Stub(ResolvedVariantResult) {
-            getOwner() >> DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org", "from"), "1.0")
-        }
-        _ * edge.selectedVariant >> Stub(ResolvedVariantResult) {
-            getOwner() >> DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org", "module"), "1.0")
-        }
+        _ * edge.fromVariant >> fromVariant
+        _ * edge.selectedVariant >> selectedId
         return edge
     }
 
-    private DependencyGraphEdge dep(DependencyGraphSelector selector, Throwable failure) {
+    private DependencyGraphEdge dep(DependencyGraphSelector selector, Long fromVariant, Throwable failure) {
         def edge = Stub(DependencyGraphEdge)
         _ * edge.selector >> selector
         _ * edge.requested >> selector.requested
         _ * edge.reason >> requested()
         _ * edge.failure >> new ModuleVersionResolveException(selector.requested, failure)
-        _ * edge.fromVariant >> Stub(ResolvedVariantResult) {
-            getOwner() >> DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org", "from"), "1.0")
-        }
-        _ * edge.selectedVariant >> Stub(ResolvedVariantResult) {
-            getOwner() >> DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org", "module"), "1.0")
-        }
+        _ * edge.fromVariant >> fromVariant
+        _ * edge.selectedVariant >> null
         return edge
     }
 
-    private DependencyGraphNode node(Long resultId, String org, String name, String ver, ComponentSelectionReason reason = requested()) {
+    private DependencyGraphNode node(Long componentId, String org, String name, String ver, ComponentSelectionReason reason = requested()) {
+        def componentMetadata = Stub(ComponentGraphResolveMetadata)
+        _ * componentMetadata.moduleVersionId >> DefaultModuleVersionIdentifier.newId(DefaultModuleIdentifier.newId(org, name), ver)
+
+        def componentState = Stub(ComponentGraphResolveState)
+        _ * componentState.instanceId >> componentId
+        _ * componentState.id >> DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId(org, name), ver)
+        _ * componentState.metadata >> componentMetadata
+
+        def variant = Stub(ResolvedGraphVariant)
+        variant.nodeId >> componentId
+
         def component = Stub(DependencyGraphComponent)
-        _ * component.resultId >> resultId
-        _ * component.moduleVersion >> DefaultModuleVersionIdentifier.newId(DefaultModuleIdentifier.newId(org, name), ver)
-        _ * component.componentId >> DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId(org, name), ver)
+        _ * component.resultId >> componentId
         _ * component.selectionReason >> reason
+        _ * component.resolveState >> componentState
+        _ * component.selectedVariants >> [variant]
 
         def node = Stub(DependencyGraphNode)
         _ * node.owner >> component
         return node
     }
 
-    private RootGraphNode rootNode(Long resultId, String org, String name, String ver) {
+    private RootGraphNode rootNode(Long componentId, String org, String name, String ver) {
+        def componentMetadata = Stub(ComponentGraphResolveMetadata)
+        _ * componentMetadata.moduleVersionId >> DefaultModuleVersionIdentifier.newId(DefaultModuleIdentifier.newId(org, name), ver)
+
+        def componentState = Stub(ComponentGraphResolveState)
+        _ * componentState.id >> DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId(org, name), ver)
+        _ * componentState.metadata >> componentMetadata
+
+        def variant = Stub(ResolvedGraphVariant)
+        variant.nodeId >> componentId
+
         def component = Stub(DependencyGraphComponent)
-        _ * component.resultId >> resultId
-        _ * component.moduleVersion >> DefaultModuleVersionIdentifier.newId(DefaultModuleIdentifier.newId(org, name), ver)
-        _ * component.componentId >> DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId(org, name), ver)
+        _ * component.resultId >> componentId
         _ * component.selectionReason >> root()
+        _ * component.resolveState >> componentState
+        _ * component.selectedVariants >> [variant]
 
         def node = Stub(RootGraphNode)
         _ * node.owner >> component
-        _ * node.getMetadata() >> Mock(LocalConfigurationGraphResolveMetadata) {
+        _ * node.metadata >> Mock(LocalConfigurationGraphResolveMetadata) {
             getAttributes() >> AttributeTestUtil.attributes(["org.foo": "v1", "org.bar": 2, "org.baz": true])
         }
         return node
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
index dd2e1b6..c2c1d6f 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/mvnsettings/DefaultLocalMavenRepositoryLocatorTest.groovy
@@ -18,8 +18,8 @@
 import org.apache.maven.settings.io.SettingsParseException
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Issue
 import spock.lang.Specification
@@ -208,7 +208,7 @@
         'env.unknown.ENV_VAR' |   "environment variable"
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     @Issue('https://github.com/gradle/gradle/issues/2843')
     def "handle the case of absolute path on Windows"() {
         when:
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy
index 656813e..6c8a0c9 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultFlatDirArtifactRepositoryTest.groovy
@@ -42,7 +42,7 @@
     final DefaultArtifactIdentifierFileStore artifactIdentifierFileStore = Stub()
     final IvyMutableModuleMetadataFactory metadataFactory = DependencyManagementTestUtil.ivyMetadataFactory()
 
-    final DefaultFlatDirArtifactRepository repository = new DefaultFlatDirArtifactRepository(fileCollectionFactory, transportFactory, locallyAvailableResourceFinder, artifactIdentifierFileStore, metadataFactory, Mock(InstantiatorFactory), Mock(ObjectFactory), TestUtil.checksumService, new VersionParser())
+    final DefaultFlatDirArtifactRepository repository = newRepo()
 
     def "creates a repository with multiple root directories"() {
         given:
@@ -52,7 +52,6 @@
         _ * repositoryTransport.repository >> resourceRepository
 
         and:
-        repository.name = 'repo-name'
         repository.dirs('a', 'b')
 
         when:
@@ -84,4 +83,35 @@
         InvalidUserDataException e = thrown()
         e.message == 'You must specify at least one directory for a flat directory repository.'
     }
+
+    def "repositories have the same id when base dirs and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.dirs('a', 'b')
+        same.dirs('a', 'b')
+        different.dirs('a')
+
+        and:
+        _ * fileCollectionFactory.resolving(['a', 'b']) >> TestFiles.fixed(new File('a'), new File('b'))
+        _ * fileCollectionFactory.resolving(['a']) >> TestFiles.fixed(new File('a'))
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        different.dirs('b')
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+    }
+
+    private DefaultFlatDirArtifactRepository newRepo() {
+        def repo = new DefaultFlatDirArtifactRepository(fileCollectionFactory, transportFactory, locallyAvailableResourceFinder, artifactIdentifierFileStore, metadataFactory, Mock(InstantiatorFactory), Mock(ObjectFactory), TestUtil.checksumService, new VersionParser())
+        repo.name = 'repo-name'
+        return repo
+    }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy
index 0f6e60d..dbf479b 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultIvyArtifactRepositoryTest.groovy
@@ -23,6 +23,7 @@
 import org.gradle.api.artifacts.ComponentMetadataVersionLister
 import org.gradle.api.artifacts.repositories.AuthenticationContainer
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository
+import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout
 import org.gradle.api.internal.artifacts.DependencyManagementTestUtil
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory
 import org.gradle.api.internal.artifacts.ivyservice.IvyContextManager
@@ -63,12 +64,7 @@
     final DefaultUrlArtifactRepository.Factory urlArtifactRepositoryFactory = new DefaultUrlArtifactRepository.Factory(fileResolver)
     final ProviderFactory providerFactory = Mock()
 
-    final DefaultIvyArtifactRepository repository = instantiator.newInstance(DefaultIvyArtifactRepository.class, fileResolver, transportFactory, locallyAvailableResourceFinder,
-        artifactIdentifierFileStore, externalResourceFileStore, authenticationContainer, ivyContextManager,
-        moduleIdentifierFactory, TestUtil.instantiatorFactory(), Mock(FileResourceRepository), moduleMetadataParser,
-        metadataFactory, SnapshotTestUtil.isolatableFactory(), TestUtil.objectFactory(), urlArtifactRepositoryFactory,
-        TestUtil.checksumService, providerFactory, new VersionParser()
-    )
+    final DefaultIvyArtifactRepository repository = newRepo()
 
     def "default values"() {
         expect:
@@ -296,7 +292,7 @@
 
         then:
         InvalidUserDataException e = thrown()
-        e.message == "You must specify a base url or at least one artifact pattern for the Ivy repository 'null'."
+        e.message == "You must specify a base url or at least one artifact pattern for the Ivy repository 'repo'."
     }
 
     def "can set a custom metadata rule"() {
@@ -331,7 +327,7 @@
 
         then:
         supplier.rules.configurableRules[0].ruleClass == CustomMetadataSupplierWithParams
-        supplier.rules.configurableRules[0].ruleParams.isolate() == ["a", 12, [1,2,3]] as Object[]
+        supplier.rules.configurableRules[0].ruleParams.isolate() == ["a", 12, [1, 2, 3]] as Object[]
     }
 
     def "can set a custom version lister"() {
@@ -365,7 +361,7 @@
 
         then:
         lister.rules.configurableRules[0].ruleClass == CustomVersionListerWithParams
-        lister.rules.configurableRules[0].ruleParams.isolate() == ["a", 12, [1,2,3]] as Object[]
+        lister.rules.configurableRules[0].ruleParams.isolate() == ["a", 12, [1, 2, 3]] as Object[]
     }
 
     def "can retrieve metadataSources"() {
@@ -395,6 +391,223 @@
         metadataSources.isIgnoreGradleMetadataRedirectionEnabled()
     }
 
+    def "repositories have the same id when base url and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        same.url = new URI("http://localhost")
+        different.url = new URI("http://localhost/repo")
+
+        and:
+        _ * fileResolver.resolveUri(_) >> { URI uri -> uri }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        different.url = new URI("http://localhost")
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+    }
+
+    def "repositories have the same id when layout and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        repo.layout(layout)
+        same.url = new URI("http://localhost")
+        same.layout(layout)
+        different.url = new URI("http://localhost")
+
+        and:
+        _ * fileResolver.resolveUri({ it instanceof URI }) >> { URI uri -> uri }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        different.layout(layout)
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+
+        where:
+        layout << ["ivy", "maven", "pattern"]
+    }
+
+    def "repositories have the same id when m2 compatible and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        repo.patternLayout { it.m2compatible = true }
+        same.url = new URI("http://localhost")
+        same.patternLayout { it.m2compatible = true }
+        different.url = new URI("http://localhost")
+        different.patternLayout { it.m2compatible = false }
+
+        and:
+        _ * fileResolver.resolveUri({ it instanceof URI }) >> { URI uri -> uri }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+    }
+
+    def "repositories have the same id when layout patterns and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        repo.patternLayout {
+            patterns(it)
+        }
+        same.url = new URI("http://localhost")
+        same.patternLayout {
+            patterns(it)
+        }
+        different.url = new URI("http://localhost")
+        different.patternLayout {}
+
+        and:
+        _ * fileResolver.resolveUri({ it instanceof URI }) >> { URI uri -> uri }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        different.patternLayout {
+            patterns(it)
+        }
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+
+        where:
+        patterns << [
+            { IvyPatternRepositoryLayout layout -> layout.ivy("[thing]") },
+            { IvyPatternRepositoryLayout layout -> layout.artifact("[thing].jar") },
+            { IvyPatternRepositoryLayout layout ->
+                layout.ivy("[thing]")
+                layout.artifact("[thing].jar")
+            },
+        ]
+    }
+
+    def "repositories have the same id when additional ivy patterns and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        repo.ivyPattern("http://localhost/[thing]")
+        same.url = new URI("http://localhost")
+        same.ivyPattern("http://localhost/[thing]")
+        different.url = new URI("http://localhost")
+
+        and:
+        _ * fileResolver.resolveUri({ it instanceof URI }) >> { URI uri -> uri }
+        _ * fileResolver.resolveUri({ it instanceof String }) >> { String uri -> new URI(uri) }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        different.ivyPattern("http://localhost/[thing]")
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+    }
+
+    def "repositories have the same id when additional artifact patterns and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        repo.artifactPattern("http://localhost/[thing]")
+        same.url = new URI("http://localhost")
+        same.artifactPattern("http://localhost/[thing]")
+        different.url = new URI("http://localhost")
+
+        and:
+        _ * fileResolver.resolveUri({ it instanceof URI }) >> { URI uri -> uri }
+        _ * fileResolver.resolveUri({ it instanceof String }) >> { String uri -> new URI(uri) }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        different.artifactPattern("http://localhost/[thing]")
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+    }
+
+    def "repositories have the same id when metadata sources and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        source(repo)
+        same.url = new URI("http://localhost")
+        source(same)
+        different.url = new URI("http://localhost")
+
+        and:
+        _ * fileResolver.resolveUri(_) >> { URI uri -> uri }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        source(different)
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+
+        where:
+        source << [
+            { IvyArtifactRepository repository -> repository.metadataSources.gradleMetadata() },
+            { IvyArtifactRepository repository ->
+                repository.metadataSources.ignoreGradleMetadataRedirection()
+            },
+            { IvyArtifactRepository repository -> repository.metadataSources.artifact() }
+        ]
+    }
+
+    private DefaultIvyArtifactRepository newRepo() {
+        def repo = instantiator.newInstance(DefaultIvyArtifactRepository.class, fileResolver, transportFactory, locallyAvailableResourceFinder,
+            artifactIdentifierFileStore, externalResourceFileStore, authenticationContainer, ivyContextManager,
+            moduleIdentifierFactory, TestUtil.instantiatorFactory(), Mock(FileResourceRepository), moduleMetadataParser,
+            metadataFactory, SnapshotTestUtil.isolatableFactory(), TestUtil.objectFactory(), urlArtifactRepositoryFactory,
+            TestUtil.checksumService, providerFactory, new VersionParser()
+        )
+        repo.name = 'repo'
+        return repo
+    }
+
     private void standardMockFileTransport() {
         transportFactory.createTransport({ it == ['file'] as Set }, 'name', _, _) >> transport()
     }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
index f63e411..2c2cd32 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/DefaultMavenArtifactRepositoryTest.groovy
@@ -58,11 +58,7 @@
     final DefaultUrlArtifactRepository.Factory urlArtifactRepositoryFactory = new DefaultUrlArtifactRepository.Factory(resolver)
     final ProviderFactory providerFactory = Mock()
 
-    final DefaultMavenArtifactRepository repository = new DefaultMavenArtifactRepository(
-            resolver, transportFactory, locallyAvailableResourceFinder, TestUtil.instantiatorFactory(),
-            artifactIdentifierFileStore, pomParser, metadataParser, authenticationContainer, externalResourceFileStore,
-            Mock(FileResourceRepository), mavenMetadataFactory, SnapshotTestUtil.isolatableFactory(),
-            TestUtil.objectFactory(), urlArtifactRepositoryFactory, TestUtil.checksumService, providerFactory, new VersionParser())
+    final DefaultMavenArtifactRepository repository = newRepo()
 
     def "creates local repository"() {
         given:
@@ -271,6 +267,102 @@
         metadataSources.isIgnoreGradleMetadataRedirectionEnabled()
     }
 
+    def "repositories have the same id when base url and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        same.url = new URI("http://localhost")
+        different.url = new URI("http://localhost/repo")
+
+        and:
+        _ * resolver.resolveUri(_) >> { URI uri -> uri }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        different.url = new URI("http://localhost")
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+    }
+
+    def "repositories have the same id when artifact urls and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        repo.artifactUrls(new URI("http://localhost/dir"))
+        same.url = new URI("http://localhost")
+        same.artifactUrls(new URI("http://localhost/dir"))
+        different.url = new URI("http://localhost")
+
+        and:
+        _ * resolver.resolveUri(_) >> { URI uri -> uri }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        different.artifactUrls(new URI("http://localhost/dir"))
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+    }
+
+    def "repositories have the same id when metadata sources and other configuration is the same"() {
+        def repo = newRepo()
+        def same = newRepo()
+        def different = newRepo()
+
+        given:
+        repo.url = new URI("http://localhost")
+        source(repo)
+        same.url = new URI("http://localhost")
+        source(same)
+        different.url = new URI("http://localhost")
+
+        and:
+        _ * resolver.resolveUri(_) >> { URI uri -> uri }
+
+        expect:
+        same.descriptor.id == repo.descriptor.id
+        different.descriptor.id != repo.descriptor.id
+
+        when:
+        source(different)
+
+        then:
+        different.descriptor.id == repo.descriptor.id
+
+        where:
+        source << [
+            { MavenArtifactRepository repository -> repository.metadataSources.gradleMetadata() },
+            { MavenArtifactRepository repository ->
+                repository.metadataSources.mavenPom()
+                repository.metadataSources.ignoreGradleMetadataRedirection()
+            },
+            { MavenArtifactRepository repository -> repository.metadataSources.artifact() }
+        ]
+    }
+
+    private DefaultMavenArtifactRepository newRepo() {
+        def repo = new DefaultMavenArtifactRepository(
+            resolver, transportFactory, locallyAvailableResourceFinder, TestUtil.instantiatorFactory(),
+            artifactIdentifierFileStore, pomParser, metadataParser, authenticationContainer, externalResourceFileStore,
+            Mock(FileResourceRepository), mavenMetadataFactory, SnapshotTestUtil.isolatableFactory(),
+            TestUtil.objectFactory(), urlArtifactRepositoryFactory, TestUtil.checksumService, providerFactory, new VersionParser())
+        repo.name = 'repo'
+        return repo
+    }
+
     static class CustomVersionLister implements ComponentMetadataVersionLister {
 
         @Override
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapterTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapterTest.groovy
index 25802db..74091f4 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapterTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ComponentMetadataDetailsAdapterTest.groovy
@@ -31,7 +31,6 @@
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata
 import org.gradle.internal.component.external.model.maven.MutableMavenModuleResolveMetadata
-import org.gradle.internal.component.model.ComponentGraphResolveState
 import org.gradle.internal.component.model.LocalComponentDependencyMetadata
 import org.gradle.util.AttributeTestUtil
 import org.gradle.util.SnapshotTestUtil
@@ -157,11 +156,9 @@
         def consumerIdentifier = DefaultModuleVersionIdentifier.newId(componentIdentifier)
         def componentSelector = newSelector(DefaultModuleIdentifier.newId(consumerIdentifier.group, consumerIdentifier.name), new DefaultMutableVersionConstraint(consumerIdentifier.version))
         def consumer = new LocalComponentDependencyMetadata(componentIdentifier, componentSelector, "default", attributes, ImmutableAttributes.EMPTY, null, [] as List, [], false, false, true, false, false, null)
-        def state = Stub(ComponentGraphResolveState) {
-            metadata >> immutable
-        }
+        def state = DependencyManagementTestUtil.modelGraphResolveFactory().stateFor(immutable)
 
-        def configuration = consumer.selectVariants(attributes, state, schema, [] as Set).variants[0]
-        configuration.dependencies
+        def variant = consumer.selectVariants(attributes, state, schema, [] as Set).variants[0]
+        variant.metadata.dependencies
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
index 49482ae..0ad974e 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/ExternalResourceResolverTest.groovy
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.ImmutableList
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.internal.artifacts.repositories.descriptor.UrlRepositoryDescriptor
 import org.gradle.api.internal.artifacts.repositories.metadata.DefaultArtifactMetadataSource
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources
 import org.gradle.api.internal.artifacts.repositories.metadata.MetadataArtifactProvider
@@ -59,7 +60,7 @@
     ExternalResourceResolver resolver
 
     def setup() {
-        resolver = new TestResolver(name, true, repository, resourceAccessor, locallyAvailableResourceFinder, fileStore, metadataSources, Stub(MetadataArtifactProvider))
+        resolver = new TestResolver(Stub(UrlRepositoryDescriptor), true, repository, resourceAccessor, locallyAvailableResourceFinder, fileStore, metadataSources, Stub(MetadataArtifactProvider))
         resolver.artifactResolver = artifactResolver
     }
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy
index c4b2286..5584e1e 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/IvyResolverTest.groovy
@@ -22,6 +22,7 @@
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor
 import org.gradle.api.internal.artifacts.repositories.metadata.DefaultIvyDescriptorMetadataSource
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources
 import org.gradle.api.internal.artifacts.repositories.metadata.IvyMetadataArtifactProvider
@@ -63,21 +64,6 @@
         resolver.toString() == "Ivy repository 'repo'"
     }
 
-    def "resolvers are differentiated by m2compatible flag"() {
-        given:
-        def resolver1 = resolver()
-        def resolver2 = resolver()
-
-        resolver1.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver1.addArtifactPattern(new IvyResourcePattern("artifact1"))
-        resolver2.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver2.addArtifactPattern(new IvyResourcePattern("artifact1"))
-        resolver2.m2compatible = true
-
-        expect:
-        resolver1.id != resolver2.id
-    }
-
     def "remote access fails directly for module id #moduleId with layout #layoutPattern"() {
         given:
         def overrideMetadata = DefaultComponentOverrideMetadata.EMPTY
@@ -134,41 +120,12 @@
         newId(mid("group", "name"), "")  | "[organization]/[module]-([revision])"
     }
 
-
-    def "resolvers are differentiated by useGradleMetadata flag"() {
-        given:
-        def resolver1 = resolver()
-        def resolver2 = resolver(null, true)
-
-        resolver1.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver1.addArtifactPattern(new IvyResourcePattern("artifact1"))
-        resolver2.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver2.addArtifactPattern(new IvyResourcePattern("artifact1"))
-
-        expect:
-        resolver1.id != resolver2.id
-    }
-
-    def "resolvers are differentiated by alwaysProvidesMetadataForModules flag"() {
-        given:
-        def resolver1 = resolver(null, false, false)
-        def resolver2 = resolver(null, false, true)
-
-        resolver1.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver1.addArtifactPattern(new IvyResourcePattern("artifact1"))
-        resolver2.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver2.addArtifactPattern(new IvyResourcePattern("artifact1"))
-
-        expect:
-        resolver1.id != resolver2.id
-    }
-
     def "correctly sets caching of component metadata rules depending on ivy repository transport"() {
         given:
         transport.isLocal() >> isLocal
         ModuleComponentIdentifier moduleComponentIdentifier = DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org", "foo"), "1.0")
         ImmutableMetadataSources metadataSources = mockMetadataSourcesForComponentMetadataRulesCachingTest()
-        def resolver = resolver(null, false, false, metadataSources)
+        def resolver = resolver(null, metadataSources)
 
         when:
         BuildableModuleComponentMetaDataResolveResult result = new DefaultBuildableModuleComponentMetaDataResolveResult()
@@ -203,7 +160,7 @@
         return metadataSources
     }
 
-    private IvyResolver resolver(String ivyPattern = null, boolean useGradleMetadata = false, boolean alwaysProvidesMetadataForModules = false , ImmutableMetadataSources metadataSources = null) {
+    private IvyResolver resolver(String ivyPattern = null, ImmutableMetadataSources metadataSources = null) {
         transport.resourceAccessor >> externalResourceAccessor
 
         MetadataArtifactProvider metadataArtifactProvider = new IvyMetadataArtifactProvider()
@@ -218,31 +175,33 @@
                         TestUtil.checksumService
                     ))
                 }
-                appendId(_) >> { args ->
-                    args[0].putBoolean(useGradleMetadata)
-                    args[0].putBoolean(alwaysProvidesMetadataForModules)
-                }
             }
         }
 
         def supplier = new InstantiatingAction<ComponentMetadataSupplierDetails>(DefaultConfigurableRules.of(Stub(ConfigurableRule)), TestUtil.instantiatorFactory().inject(), Stub(InstantiatingAction.ExceptionHandler))
         def lister = new InstantiatingAction<ComponentMetadataListerDetails>(DefaultConfigurableRules.of(Stub(ConfigurableRule)), TestUtil.instantiatorFactory().inject(), Stub(InstantiatingAction.ExceptionHandler))
 
-        new IvyResolver(
-                "repo",
-                transport,
-                Stub(LocallyAvailableResourceFinder),
-                false,
-                Stub(FileStore),
-                supplier,
-                lister,
-                metadataSources,
-                metadataArtifactProvider, Mock(Instantiator),
-                TestUtil.checksumService).with {
-            if (ivyPattern) {
-                it.addDescriptorLocation(URI.create(""), ivyPattern)
-            }
-            it
+        def builder = new IvyRepositoryDescriptor.Builder("repo", new URI("http://localhost"))
+        builder.m2Compatible = false
+        builder.metadataSources = []
+        builder.authenticated = false
+        builder.authenticationSchemes = []
+        builder.layoutType = "test"
+        if (ivyPattern != null) {
+            builder.addIvyResource(new URI("http://localhost"), ivyPattern)
         }
+        def descriptor = builder.create()
+
+        new IvyResolver(
+            descriptor,
+            transport,
+            Stub(LocallyAvailableResourceFinder),
+            false,
+            Stub(FileStore),
+            supplier,
+            lister,
+            metadataSources,
+            metadataArtifactProvider, Mock(Instantiator),
+            TestUtil.checksumService)
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
index 4a28860..35521f2 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/MavenResolverTest.groovy
@@ -21,6 +21,7 @@
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory
+import org.gradle.api.internal.artifacts.repositories.descriptor.MavenRepositoryDescriptor
 import org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader
 import org.gradle.api.internal.artifacts.repositories.metadata.DefaultMavenPomMetadataSource
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources
@@ -48,47 +49,20 @@
 class MavenResolverTest extends Specification {
     def module = Mock(MavenModuleResolveMetadata)
     def transport = Stub(RepositoryTransport)
-    def resolver = resolver()
 
     def "has useful string representation"() {
+        def resolver = resolver()
+
         expect:
         resolver.toString() == "Maven repository 'repo'"
     }
 
-    def "resolvers are differentiated by useGradleMetadata flag"() {
-        given:
-        def resolver1 = resolver()
-        def resolver2 = resolver(true)
-
-        resolver1.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver1.addArtifactPattern(new IvyResourcePattern("artifact1"))
-        resolver2.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver2.addArtifactPattern(new IvyResourcePattern("artifact1"))
-
-        expect:
-        resolver1.id != resolver2.id
-    }
-
-    def "resolvers are differentiated by alwaysProvidesMetadataForModules flag"() {
-        given:
-        def resolver1 = resolver(false, false)
-        def resolver2 = resolver(false, true)
-
-        resolver1.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver1.addArtifactPattern(new IvyResourcePattern("artifact1"))
-        resolver2.addIvyPattern(new IvyResourcePattern("ivy1"))
-        resolver2.addArtifactPattern(new IvyResourcePattern("artifact1"))
-
-        expect:
-        resolver1.id != resolver2.id
-    }
-
     def "correctly sets caching of component metadata rules depending on maven repository transport"() {
         given:
         transport.isLocal() >> isLocal
         ModuleComponentIdentifier moduleComponentIdentifier = DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org", "foo"), "1.0")
         ImmutableMetadataSources metadataSources = mockMetadataSourcesForComponentMetadataRulesCachingTest()
-        def resolver = resolver(false, false, metadataSources)
+        def resolver = resolver(metadataSources)
 
         when:
         BuildableModuleComponentMetaDataResolveResult result = new DefaultBuildableModuleComponentMetaDataResolveResult()
@@ -123,7 +97,7 @@
         return metadataSources
     }
 
-    private MavenResolver resolver(boolean useGradleMetadata = false, boolean alwaysProvidesMetadataForModules = false, ImmutableMetadataSources metadataSources = null) {
+    private MavenResolver resolver(ImmutableMetadataSources metadataSources = null) {
         MetadataArtifactProvider metadataArtifactProvider = new MavenMetadataArtifactProvider()
         def fileResourceRepository = Stub(FileResourceRepository)
         def moduleIdentifierFactory = Stub(ImmutableModuleIdentifierFactory)
@@ -137,16 +111,18 @@
                         moduleIdentifierFactory, validator
                     ))
                 }
-                appendId(_) >> { args ->
-                    args[0].putBoolean(useGradleMetadata)
-                    args[0].putBoolean(alwaysProvidesMetadataForModules)
-                }
             }
         }
 
         def supplier = new InstantiatingAction<ComponentMetadataSupplierDetails>(DefaultConfigurableRules.of(Stub(ConfigurableRule)), TestUtil.instantiatorFactory().inject(), Stub(InstantiatingAction.ExceptionHandler))
         def lister = new InstantiatingAction<ComponentMetadataListerDetails>(DefaultConfigurableRules.of(Stub(ConfigurableRule)), TestUtil.instantiatorFactory().inject(), Stub(InstantiatingAction.ExceptionHandler))
 
-        new MavenResolver("repo", new URI("http://localhost"), transport, Stub(LocallyAvailableResourceFinder), Stub(FileStore), metadataSources, metadataArtifactProvider, Stub(MavenMetadataLoader), supplier, lister, Mock(Instantiator), TestUtil.checksumService)
+        def builder = new MavenRepositoryDescriptor.Builder("repo", new URI("http://localhost"))
+        builder.metadataSources = []
+        builder.authenticated = false
+        builder.authenticationSchemes = []
+        builder.artifactUrls = []
+        def descriptor = builder.create()
+        new MavenResolver(descriptor, new URI("http://localhost"), transport, Stub(LocallyAvailableResourceFinder), Stub(FileStore), metadataSources, metadataArtifactProvider, Stub(MavenMetadataLoader), supplier, lister, Mock(Instantiator), TestUtil.checksumService)
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/TestResolver.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/TestResolver.java
index 2aa19d4..b93cf0e 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/TestResolver.java
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/repositories/resolver/TestResolver.java
@@ -17,6 +17,7 @@
 package org.gradle.api.internal.artifacts.repositories.resolver;
 
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess;
+import org.gradle.api.internal.artifacts.repositories.descriptor.UrlRepositoryDescriptor;
 import org.gradle.api.internal.artifacts.repositories.metadata.ImmutableMetadataSources;
 import org.gradle.api.internal.artifacts.repositories.metadata.MetadataArtifactProvider;
 import org.gradle.api.internal.component.ArtifactType;
@@ -24,7 +25,6 @@
 import org.gradle.internal.component.external.model.ModuleComponentArtifactMetadata;
 import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata;
 import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata;
-import org.gradle.internal.hash.Hasher;
 import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult;
 import org.gradle.internal.resource.ExternalResourceRepository;
 import org.gradle.internal.resource.local.FileStore;
@@ -35,8 +35,8 @@
 public class TestResolver extends ExternalResourceResolver<ModuleComponentResolveMetadata> {
     ExternalResourceArtifactResolver artifactResolver;
 
-    protected TestResolver(String name, boolean local, ExternalResourceRepository repository, CacheAwareExternalResourceAccessor cachingResourceAccessor, LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder, FileStore<ModuleComponentArtifactIdentifier> artifactFileStore, ImmutableMetadataSources metadataSources, MetadataArtifactProvider metadataArtifactProvider) {
-        super(name, local, repository, cachingResourceAccessor, locallyAvailableResourceFinder, artifactFileStore, metadataSources, metadataArtifactProvider, null, null, null, TestUtil.getChecksumService());
+    protected TestResolver(UrlRepositoryDescriptor descriptor, boolean local, ExternalResourceRepository repository, CacheAwareExternalResourceAccessor cachingResourceAccessor, LocallyAvailableResourceFinder<ModuleComponentArtifactMetadata> locallyAvailableResourceFinder, FileStore<ModuleComponentArtifactIdentifier> artifactFileStore, ImmutableMetadataSources metadataSources, MetadataArtifactProvider metadataArtifactProvider) {
+        super(descriptor, local, repository, cachingResourceAccessor, locallyAvailableResourceFinder, artifactFileStore, metadataSources, metadataArtifactProvider, null, null, null, TestUtil.getChecksumService());
     }
 
     @Override
@@ -50,12 +50,7 @@ protected boolean isMetaDataArtifact(ArtifactType artifactType) {
     }
 
     @Override
-    protected void appendId(Hasher hasher) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public ModuleComponentRepositoryAccess getLocalAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> getLocalAccess() {
         throw new UnsupportedOperationException();
     }
 
@@ -65,7 +60,7 @@ protected ExternalResourceArtifactResolver createArtifactResolver() {
     }
 
     @Override
-    public ModuleComponentRepositoryAccess getRemoteAccess() {
+    public ModuleComponentRepositoryAccess<ModuleComponentResolveMetadata> getRemoteAccess() {
         return new RemoteRepositoryAccess() {
 
             @Override
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
index 249e2d7..f432b62 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/result/DefaultResolutionResultTest.groovy
@@ -139,8 +139,8 @@
         def dep = new DefaultUnresolvedDependencyResult(
             Stub(ComponentSelector), false,
             Stub(ComponentSelectionReason),
-            new DefaultResolvedComponentResult(mid, Stub(ComponentSelectionReason), projectId, [Stub(ResolvedVariantResult)], [Stub(ResolvedVariantResult)], null),
-            new ModuleVersionNotFoundException(Stub(ModuleComponentSelector), broken)
+            new DefaultResolvedComponentResult(mid, Stub(ComponentSelectionReason), projectId, [1: Stub(ResolvedVariantResult)], [Stub(ResolvedVariantResult)], null),
+            new ModuleVersionNotFoundException(Stub(ModuleComponentSelector), broken, [])
         )
         def edge = new UnresolvedDependencyEdge(dep)
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelectorSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelectorSpec.groovy
index f821a00..96915b2 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelectorSpec.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/AttributeMatchingVariantSelectorSpec.groovy
@@ -33,7 +33,7 @@
 
     def consumerProvidedVariantFinder = Mock(ConsumerProvidedVariantFinder)
     def transformedVariantFactory = Mock(TransformedVariantFactory)
-    def dependenciesResolverFactory = Mock(ExtraExecutionGraphDependenciesResolverFactory)
+    def dependenciesResolverFactory = Mock(TransformUpstreamDependenciesResolverFactory)
     def attributeMatcher = Mock(AttributeMatcher)
     def attributesSchema = Mock(AttributesSchemaInternal) {
         withProducer(_) >> attributeMatcher
@@ -107,7 +107,7 @@
 
         1 * attributeMatcher.matches(_, _, _) >> Collections.emptyList()
         1 * consumerProvidedVariantFinder.findTransformedVariants(variants, requestedAttributes) >> transformedVariants
-        1 * factory.asTransformed(variant, transformedVariants[0].getVariantChain(), dependenciesResolverFactory, transformedVariantFactory) >> transformed
+        1 * factory.asTransformed(variant, transformedVariants[0].getTransformedVariantDefinition(), dependenciesResolverFactory, transformedVariantFactory) >> transformed
         0 * attributeMatcher._
     }
 
@@ -127,7 +127,7 @@
         1 * attributeMatcher.matches(_, _, _) >> Collections.emptyList()
         1 * consumerProvidedVariantFinder.findTransformedVariants(variants, requestedAttributes) >> transformedVariants
         1 * attributeMatcher.matches(_, _, _) >> [transformedVariants[resultNum]]
-        1 * factory.asTransformed(variants[resultNum], transformedVariants[resultNum].getVariantChain(), dependenciesResolverFactory, transformedVariantFactory) >> transformed
+        1 * factory.asTransformed(variants[resultNum], transformedVariants[resultNum].getTransformedVariantDefinition(), dependenciesResolverFactory, transformedVariantFactory) >> transformed
 
         where:
         resultNum << [0, 1, 2]
@@ -162,7 +162,7 @@
     private List<TransformedVariant> transformedVariants(List<ResolvedVariant> variants) {
         variants.collect {
             new TransformedVariant(it, Mock(VariantDefinition) {
-                transformation >> Mock(Transformation)
+                transformChain >> Mock(TransformChain)
                 targetAttributes >> ImmutableAttributes.EMPTY
             })
         }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinderTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinderTest.groovy
index 4c92de7..fe8dd13 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinderTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/ConsumerProvidedVariantFinderTest.groovy
@@ -16,9 +16,9 @@
 
 package org.gradle.api.internal.artifacts.transform
 
-import org.gradle.api.Action
+
 import org.gradle.api.attributes.AttributeContainer
-import org.gradle.api.internal.artifacts.ArtifactTransformRegistration
+import org.gradle.api.internal.artifacts.TransformRegistration
 import org.gradle.api.internal.artifacts.VariantTransformRegistry
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant
 import org.gradle.api.internal.attributes.AttributeContainerInternal
@@ -56,7 +56,7 @@
         def variants = [ sourceVariant, otherVariant ]
 
         given:
-        transformRegistry.transforms >> [transform1, transform2, transform3]
+        transformRegistry.registrations >> [transform1, transform2, transform3]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -96,7 +96,7 @@
         def variants = [ sourceVariant, otherVariant ]
 
         given:
-        transformRegistry.transforms >> [transform1, transform2, transform3, transform4]
+        transformRegistry.registrations >> [transform1, transform2, transform3, transform4]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -138,7 +138,7 @@
         def variants = [ sourceVariant, otherVariant ]
 
         given:
-        transformRegistry.transforms >> [transform1, transform2]
+        transformRegistry.registrations >> [transform1, transform2]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -203,7 +203,7 @@
         def transform6 = registration(fromIntermediate2, incompatible2)
 
         given:
-        transformRegistry.transforms >> [transform1, transform2, transform3, transform4, transform5, transform6]
+        transformRegistry.registrations >> [transform1, transform2, transform3, transform4, transform5, transform6]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -256,7 +256,7 @@
         def transform4 = registration(fromOther, compatible2)
 
         given:
-        transformRegistry.transforms >> [transform1, transform2, transform3, transform4]
+        transformRegistry.registrations >> [transform1, transform2, transform3, transform4]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -310,7 +310,7 @@
         def fromIntermediate = AttributeTestUtil.attributesFactory().concat(requested, intermediate)
 
         given:
-        transformRegistry.transforms >> [registrations[registrationsIndex[0]], registrations[registrationsIndex[1]], registrations[registrationsIndex[2]], registrations[registrationsIndex[3]]]
+        transformRegistry.registrations >> [registrations[registrationsIndex[0]], registrations[registrationsIndex[1]], registrations[registrationsIndex[2]], registrations[registrationsIndex[3]]]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -361,7 +361,7 @@
         def transform3 = registration(fromIntermediate, compatible)
 
         given:
-        transformRegistry.transforms >> [transform1, transform2, transform3]
+        transformRegistry.registrations >> [transform1, transform2, transform3]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -401,7 +401,7 @@
         def variants = [ sourceVariant ]
 
         given:
-        transformRegistry.transforms >> [transform1, transform2]
+        transformRegistry.registrations >> [transform1, transform2]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -430,7 +430,7 @@
         def variants = [ sourceVariant ]
 
         given:
-        transformRegistry.transforms >> [transform1, transform2]
+        transformRegistry.registrations >> [transform1, transform2]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -468,7 +468,7 @@
         def finalAttributes = AttributeTestUtil.attributesFactory().concat(sourceVariant.getAttributes().asImmutable(), compatible)
 
         given:
-        transformRegistry.transforms >> [transform1]
+        transformRegistry.registrations >> [transform1]
 
         when:
         def result = transformations.findTransformedVariants(variants, requested)
@@ -488,27 +488,22 @@
         0 * attributeMatcher._
     }
 
-    private void assertTransformChain(TransformedVariant chain, ResolvedVariant source, AttributeContainer finalAttributes, ArtifactTransformRegistration... steps) {
+    private void assertTransformChain(TransformedVariant chain, ResolvedVariant source, AttributeContainer finalAttributes, TransformRegistration... registrations) {
         assert chain.root == source
         assert chain.attributes == finalAttributes
-        assert chain.transformation.stepsCount() == steps.length
         def actualSteps = []
-        chain.transformation.visitTransformationSteps {
+        chain.transformChain.visitTransformSteps {
             actualSteps << it
         }
-        def expectedSteps = steps*.transformationStep
+        def expectedSteps = registrations*.transformStep
         assert actualSteps == expectedSteps
     }
 
-    private ArtifactTransformRegistration registration(AttributeContainer from, AttributeContainer to) {
-        def transformationStep = Stub(TransformationStep)
-        _ * transformationStep.visitTransformationSteps(_) >> { Action action -> action.execute(transformationStep) }
-        _ * transformationStep.stepsCount() >> 1
-
-        return Mock(ArtifactTransformRegistration) {
+    private TransformRegistration registration(AttributeContainer from, AttributeContainer to) {
+        return Mock(TransformRegistration) {
             getFrom() >> from
             getTo() >> to
-            getTransformationStep() >> transformationStep
+            getTransformStep() >> Stub(TransformStep)
         }
     }
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformsTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformsTest.groovy
deleted file mode 100644
index 2330080..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultArtifactTransformsTest.groovy
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright 2016 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.api.internal.artifacts.transform
-
-import com.google.common.collect.ImmutableList
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariantSet
-import org.gradle.api.internal.attributes.AttributeContainerInternal
-import org.gradle.api.internal.attributes.AttributesSchemaInternal
-import org.gradle.api.internal.attributes.DefaultMutableAttributeContainer
-import org.gradle.api.internal.attributes.ImmutableAttributes
-import org.gradle.internal.Describables
-import org.gradle.internal.component.AmbiguousVariantSelectionException
-import org.gradle.internal.component.NoMatchingVariantSelectionException
-import org.gradle.internal.component.model.AttributeMatcher
-import org.gradle.internal.component.model.AttributeMatchingExplanationBuilder
-import org.gradle.util.AttributeTestUtil
-import spock.lang.Specification
-
-import static org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE
-import static org.gradle.util.internal.TextUtil.toPlatformLineSeparators
-
-class DefaultArtifactTransformsTest extends Specification {
-    def matchingCache = Mock(ConsumerProvidedVariantFinder)
-    def producerSchema = Mock(AttributesSchemaInternal)
-    def consumerSchema = Mock(AttributesSchemaInternal) {
-        getConsumerDescribers() >> []
-    }
-    def attributeMatcher = Mock(AttributeMatcher)
-    def factory = Mock(VariantSelector.Factory)
-    def dependenciesResolver = Stub(ExtraExecutionGraphDependenciesResolverFactory)
-    def transformedVariantFactory = Mock(TransformedVariantFactory)
-    def transforms = new DefaultArtifactTransforms(matchingCache, consumerSchema, AttributeTestUtil.attributesFactory(), transformedVariantFactory)
-
-    def "selects producer variant with requested attributes"() {
-        def variant1 = resolvedVariant()
-        def variant2 = resolvedVariant()
-        def variant1Artifacts = Stub(ResolvedArtifactSet)
-        def set = resolvedVariantSet()
-        def variants = [variant1, variant2] as Set
-
-        given:
-        set.schema >> producerSchema
-        set.variants >> variants
-        variant1.attributes >> typeAttributes("classes")
-        variant1.artifacts >> variant1Artifacts
-        variant2.attributes >> typeAttributes("jar")
-
-        consumerSchema.withProducer(producerSchema) >> attributeMatcher
-        attributeMatcher.matches(_ as Collection, typeAttributes("classes"), _ as AttributeMatchingExplanationBuilder) >> [variant1]
-
-        expect:
-        def result = transforms.variantSelector(typeAttributes("classes"), true, false, dependenciesResolver).select(set, factory)
-        result == variant1Artifacts
-    }
-
-    def "fails when multiple producer variants match"() {
-        def variant1 = resolvedVariant()
-        def variant2 = resolvedVariant()
-        def set = resolvedVariantSet()
-        def variants = [variant1, variant2] as Set
-
-        given:
-        set.asDescribable() >> Describables.of('<component>')
-        set.schema >> producerSchema
-        set.variants >> variants
-        variant1.asDescribable() >> Describables.of('<variant1>')
-        variant1.attributes >> typeAttributes("classes")
-        variant2.asDescribable() >> Describables.of('<variant2>')
-        variant2.attributes >> typeAttributes("jar")
-
-        consumerSchema.withProducer(producerSchema) >> attributeMatcher
-        attributeMatcher.matches(_ as Collection, typeAttributes("classes"), _ as AttributeMatchingExplanationBuilder) >> [variant1, variant2]
-        attributeMatcher.isMatching(_, _, _) >> true
-
-        when:
-        def result = transforms.variantSelector(typeAttributes("classes"), true, false, dependenciesResolver).select(set, factory)
-        visit(result)
-
-        then:
-        def e = thrown(AmbiguousVariantSelectionException)
-        e.message == toPlatformLineSeparators("""The consumer was configured to find attribute 'artifactType' with value 'classes'. However we cannot choose between the following variants of <component>:
-  - <variant1> declares attribute 'artifactType' with value 'classes'
-  - <variant2> declares attribute 'artifactType' with value 'jar'""")
-    }
-
-    def "fails when multiple transforms match"() {
-        def requested = typeAttributes("dll")
-        def variant1 = resolvedVariant()
-        def variant2 = resolvedVariant()
-        def set = resolvedVariantSet()
-        def variants = [variant1, variant2] as Set
-        def transformedVariants = variants.collect { transformedVariant(it, requested)}
-
-        given:
-        set.schema >> producerSchema
-        set.variants >> variants
-        set.asDescribable() >> Describables.of('<component>')
-        variant1.attributes >> typeAttributes("jar")
-        variant1.asDescribable() >> Describables.of('<variant1>')
-        variant2.attributes >> typeAttributes("classes")
-        variant2.asDescribable() >> Describables.of('<variant2>')
-
-        consumerSchema.withProducer(producerSchema) >> attributeMatcher
-        attributeMatcher.matches(ImmutableList.copyOf(variants), _, _) >> []
-        attributeMatcher.matches(transformedVariants, _, _) >> transformedVariants
-        matchingCache.findTransformedVariants(_, _) >> transformedVariants
-
-        def selector = transforms.variantSelector(requested, true, false, dependenciesResolver)
-
-        when:
-        def result = selector.select(set, factory)
-        visit(result)
-
-        then:
-        def e = thrown(AmbiguousTransformException)
-        e.message == toPlatformLineSeparators("""Found multiple transforms that can produce a variant of <component> with requested attributes:
-  - artifactType 'dll'
-Found the following transforms:
-  - From '<variant1>':
-      - With source attributes: artifactType 'jar'
-      - Candidate transform(s):
-          - Transform '' producing attributes: artifactType 'dll'
-  - From '<variant2>':
-      - With source attributes: artifactType 'classes'
-      - Candidate transform(s):
-          - Transform '' producing attributes: artifactType 'dll'""")
-    }
-
-    def "returns empty variant when no variants match and ignore no matching enabled"() {
-        def variant1 = resolvedVariant()
-        def variant2 = resolvedVariant()
-        def set = resolvedVariantSet()
-        def variants = [variant1, variant2] as Set
-
-        given:
-        set.schema >> producerSchema
-        set.variants >> variants
-        variant1.attributes >> typeAttributes("jar")
-        variant2.attributes >> typeAttributes("classes")
-
-        consumerSchema.withProducer(producerSchema) >> attributeMatcher
-        attributeMatcher.matches(_, _, _) >> []
-
-        matchingCache.findTransformedVariants(_, _) >> []
-
-        expect:
-        def result = transforms.variantSelector(typeAttributes("dll"), true, false, dependenciesResolver).select(set, factory)
-        result == ResolvedArtifactSet.EMPTY
-    }
-
-    def "fails when no variants match and ignore no matching disabled"() {
-        def variant1 = resolvedVariant()
-        def variant2 = resolvedVariant()
-        def set = resolvedVariantSet()
-        def variants = [variant1, variant2] as Set
-
-        given:
-        set.schema >> producerSchema
-        set.variants >> variants
-        set.asDescribable() >> Describables.of('<component>')
-        variant1.attributes >> typeAttributes("jar")
-        variant1.asDescribable() >> Describables.of('<variant1>')
-        variant2.attributes >> typeAttributes("classes")
-        variant2.asDescribable() >> Describables.of('<variant2>')
-
-        consumerSchema.withProducer(producerSchema) >> attributeMatcher
-        attributeMatcher.matches(_, _, _) >> []
-
-        matchingCache.findTransformedVariants(_, _) >> []
-
-        when:
-        def result = transforms.variantSelector(typeAttributes("dll"), false, false, dependenciesResolver).select(set, factory)
-        visit(result)
-
-        then:
-        def e = thrown(NoMatchingVariantSelectionException)
-        e.message == toPlatformLineSeparators("""No variants of  match the consumer attributes:
-  - <variant1>:
-      - Incompatible because this component declares attribute 'artifactType' with value 'jar' and the consumer needed attribute 'artifactType' with value 'dll'
-  - <variant2>:
-      - Incompatible because this component declares attribute 'artifactType' with value 'classes' and the consumer needed attribute 'artifactType' with value 'dll'""")
-    }
-
-    private ResolvedVariant resolvedVariant() {
-        Stub(ResolvedVariant)
-    }
-
-    private ResolvedVariantSet resolvedVariantSet() {
-        Stub(ResolvedVariantSet) {
-            getOverriddenAttributes() >> ImmutableAttributes.EMPTY
-        }
-    }
-
-    def visit(ResolvedArtifactSet set) {
-        def artifactVisitor = Stub(ArtifactVisitor)
-        _ * artifactVisitor.visitFailure(_) >> { Throwable t -> throw t }
-        def visitor = Stub(ResolvedArtifactSet.Visitor)
-        _ * visitor.visitArtifacts(_) >> { ResolvedArtifactSet.Artifacts artifacts -> artifacts.visit(artifactVisitor) }
-        set.visit(visitor)
-    }
-
-    private static AttributeContainerInternal typeAttributes(String artifactType) {
-        def attributeContainer = new DefaultMutableAttributeContainer(AttributeTestUtil.attributesFactory())
-        attributeContainer.attribute(ARTIFACT_TYPE_ATTRIBUTE, artifactType)
-        attributeContainer.asImmutable()
-    }
-
-    TransformedVariant transformedVariant(ResolvedVariant root, AttributeContainerInternal attributes) {
-        ImmutableAttributes attrs = attributes.asImmutable()
-        TransformationStep step = Mock(TransformationStep) {
-            getDisplayName() >> ""
-        }
-        VariantDefinition definition = Mock(VariantDefinition) {
-            getTransformation() >> step
-            getTargetAttributes() >> attrs
-        }
-        return new TransformedVariant(root, definition)
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultTransformInvocationFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultTransformInvocationFactoryTest.groovy
new file mode 100644
index 0000000..a1c38e4
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultTransformInvocationFactoryTest.groovy
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2017 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.api.internal.artifacts.transform
+
+import com.google.common.collect.ImmutableList
+import org.gradle.api.artifacts.component.ComponentArtifactIdentifier
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier
+import org.gradle.api.artifacts.transform.TransformAction
+import org.gradle.api.file.FileSystemLocation
+import org.gradle.api.internal.DocumentationRegistry
+import org.gradle.api.internal.artifacts.DefaultBuildIdentifier
+import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
+import org.gradle.api.internal.attributes.ImmutableAttributes
+import org.gradle.api.internal.file.TestFiles
+import org.gradle.api.internal.project.ProjectInternal
+import org.gradle.api.internal.project.ProjectState
+import org.gradle.api.internal.project.ProjectStateRegistry
+import org.gradle.api.internal.tasks.TaskDependencyResolveContext
+import org.gradle.api.provider.Provider
+import org.gradle.caching.internal.controller.BuildCacheController
+import org.gradle.initialization.DefaultBuildCancellationToken
+import org.gradle.internal.Try
+import org.gradle.internal.component.local.model.ComponentFileArtifactIdentifier
+import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager
+import org.gradle.internal.execution.BuildOutputCleanupRegistry
+import org.gradle.internal.execution.InputFingerprinter
+import org.gradle.internal.execution.OutputChangeListener
+import org.gradle.internal.execution.TestExecutionHistoryStore
+import org.gradle.internal.execution.WorkInputListeners
+import org.gradle.internal.execution.history.OutputFilesRepository
+import org.gradle.internal.execution.history.changes.DefaultExecutionStateChangeDetector
+import org.gradle.internal.execution.history.impl.DefaultOverlappingOutputDetector
+import org.gradle.internal.execution.impl.DefaultFileCollectionFingerprinterRegistry
+import org.gradle.internal.execution.impl.DefaultInputFingerprinter
+import org.gradle.internal.execution.impl.DefaultOutputSnapshotter
+import org.gradle.internal.execution.impl.FingerprinterRegistration
+import org.gradle.internal.execution.model.InputNormalizer
+import org.gradle.internal.execution.steps.ValidateStep
+import org.gradle.internal.execution.timeout.TimeoutHandler
+import org.gradle.internal.fingerprint.DirectorySensitivity
+import org.gradle.internal.fingerprint.FileNormalizer
+import org.gradle.internal.fingerprint.LineEndingSensitivity
+import org.gradle.internal.fingerprint.hashing.FileSystemLocationSnapshotHasher
+import org.gradle.internal.fingerprint.impl.AbsolutePathFileCollectionFingerprinter
+import org.gradle.internal.fingerprint.impl.DefaultFileCollectionSnapshotter
+import org.gradle.internal.hash.ClassLoaderHierarchyHasher
+import org.gradle.internal.hash.HashCode
+import org.gradle.internal.hash.TestHashCodes
+import org.gradle.internal.id.UniqueId
+import org.gradle.internal.operations.CurrentBuildOperationRef
+import org.gradle.internal.operations.TestBuildOperationExecutor
+import org.gradle.internal.scopeids.id.BuildInvocationScopeId
+import org.gradle.internal.service.ServiceRegistry
+import org.gradle.internal.service.scopes.ExecutionGradleServices
+import org.gradle.internal.snapshot.impl.DefaultValueSnapshotter
+import org.gradle.test.fixtures.AbstractProjectBuilderSpec
+import org.gradle.util.Path
+import org.gradle.work.InputChanges
+
+import java.util.function.BiFunction
+
+class DefaultTransformInvocationFactoryTest extends AbstractProjectBuilderSpec {
+    private DocumentationRegistry documentationRegistry = new DocumentationRegistry()
+
+    def immutableTransformsStoreDirectory = temporaryFolder.file("output")
+    def mutableTransformsStoreDirectory = temporaryFolder.file("child/build/transforms")
+
+    def classloaderHasher = Stub(ClassLoaderHierarchyHasher) {
+        getClassLoaderHash(_ as ClassLoader) >> TestHashCodes.hashCodeFrom(1234)
+    }
+    def valueSnapshotter = new DefaultValueSnapshotter([], classloaderHasher)
+
+    def executionHistoryStore = new TestExecutionHistoryStore()
+    def virtualFileSystem = TestFiles.virtualFileSystem()
+    def fileSystemAccess = TestFiles.fileSystemAccess(virtualFileSystem)
+    def fileCollectionSnapshotter = new DefaultFileCollectionSnapshotter(fileSystemAccess, TestFiles.fileSystem())
+
+    def transformWorkspaceServices = new TestTransformWorkspaceServices(immutableTransformsStoreDirectory, executionHistoryStore)
+
+    def fileCollectionFactory = TestFiles.fileCollectionFactory()
+    def artifactTransformListener = Mock(TransformExecutionListener)
+
+    def dependencyFingerprinter = new AbsolutePathFileCollectionFingerprinter(DirectorySensitivity.DEFAULT, fileCollectionSnapshotter, FileSystemLocationSnapshotHasher.DEFAULT)
+    def fileCollectionFingerprinterRegistry = new DefaultFileCollectionFingerprinterRegistry([FingerprinterRegistration.registration(DirectorySensitivity.DEFAULT, LineEndingSensitivity.DEFAULT, dependencyFingerprinter)])
+    def inputFingerprinter = new DefaultInputFingerprinter(fileCollectionSnapshotter, fileCollectionFingerprinterRegistry, valueSnapshotter)
+
+    def projectServiceRegistry = Stub(ServiceRegistry) {
+        get(TransformWorkspaceServices) >> new TestTransformWorkspaceServices(mutableTransformsStoreDirectory, executionHistoryStore)
+    }
+
+    def childProject = Stub(ProjectInternal) {
+        getServices() >> projectServiceRegistry
+    }
+
+    def projectStateRegistry = Stub(ProjectStateRegistry) {
+        stateFor(_ as ProjectComponentIdentifier) >> Stub(ProjectState) {
+            getMutableModel() >> childProject
+        }
+    }
+
+    def dependencies = Stub(TransformDependencies) {
+        getFiles() >> Optional.empty()
+    }
+
+    def buildOperationExecutor = new TestBuildOperationExecutor()
+
+    def buildCacheController = Stub(BuildCacheController)
+    def buildInvocationScopeId = new BuildInvocationScopeId(UniqueId.generate())
+    def cancellationToken = new DefaultBuildCancellationToken()
+    def outputChangeListener = { affectedOutputPaths -> fileSystemAccess.write(affectedOutputPaths, {}) } as OutputChangeListener
+    def outputFilesRepository = Stub(OutputFilesRepository) {
+        isGeneratedByGradle(_ as File) >> true
+    }
+    def workInputListeners = Stub(WorkInputListeners)
+    def buildOutputCleanupRegistry = Mock(BuildOutputCleanupRegistry)
+    def outputSnapshotter = new DefaultOutputSnapshotter(fileCollectionSnapshotter)
+    def deleter = TestFiles.deleter()
+    def validationWarningRecorder = Mock(ValidateStep.ValidationWarningRecorder)
+    def executionEngine = new ExecutionGradleServices().createExecutionEngine(
+        buildCacheController,
+        cancellationToken,
+        buildInvocationScopeId,
+        buildOperationExecutor,
+        buildOutputCleanupRegistry,
+        new GradleEnterprisePluginManager(),
+        classloaderHasher,
+        new CurrentBuildOperationRef(),
+        deleter,
+        new DefaultExecutionStateChangeDetector(),
+        outputChangeListener,
+        workInputListeners,
+        outputFilesRepository,
+        outputSnapshotter,
+        new DefaultOverlappingOutputDetector(),
+        Mock(TimeoutHandler),
+        validationWarningRecorder,
+        virtualFileSystem,
+        documentationRegistry
+    )
+
+    def invoker = new DefaultTransformInvocationFactory(
+        executionEngine,
+        fileSystemAccess,
+        artifactTransformListener,
+        transformWorkspaceServices,
+        fileCollectionFactory,
+        projectStateRegistry,
+        buildOperationExecutor
+    )
+
+    private static class TestTransform implements Transform {
+        private final HashCode secondaryInputsHash
+        private final BiFunction<File, File, List<File>> transformationAction
+
+        static TestTransform create(HashCode secondaryInputsHash = TestHashCodes.hashCodeFrom(1234), BiFunction<File, File, List<File>> transformationAction) {
+            return new TestTransform(secondaryInputsHash, transformationAction)
+        }
+
+        TestTransform(HashCode secondaryInputsHash, BiFunction<File, File, List<File>> transformationAction) {
+            this.transformationAction = transformationAction
+            this.secondaryInputsHash = secondaryInputsHash
+        }
+
+        @Override
+        Class<?> getImplementationClass() {
+            return TransformAction.class
+        }
+
+        @Override
+        ImmutableAttributes getFromAttributes() {
+            return ImmutableAttributes.EMPTY
+        }
+
+        @Override
+        ImmutableAttributes getToAttributes() {
+            return ImmutableAttributes.EMPTY
+        }
+
+        @Override
+        boolean requiresDependencies() {
+            return false
+        }
+
+        @Override
+        boolean requiresInputChanges() {
+            return false
+        }
+
+        @Override
+        boolean isCacheable() {
+            return false
+        }
+
+        @Override
+        TransformExecutionResult transform(Provider<FileSystemLocation> inputArtifactProvider, File outputDir, TransformDependencies dependencies, InputChanges inputChanges) {
+            def builder = TransformExecutionResult.builderFor(inputArtifactProvider.get().asFile, outputDir)
+            transformationAction.apply(inputArtifactProvider.get().asFile, outputDir).each {
+                builder.addOutput(it) {}
+            }
+            return builder.build()
+        }
+
+        @Override
+        HashCode getSecondaryInputHash() {
+            return secondaryInputsHash
+        }
+
+        @Override
+        FileNormalizer getInputArtifactNormalizer() {
+            return InputNormalizer.ABSOLUTE_PATH
+        }
+
+        @Override
+        FileNormalizer getInputArtifactDependenciesNormalizer() {
+            return InputNormalizer.ABSOLUTE_PATH
+        }
+
+        @Override
+        boolean isIsolated() {
+            return true
+        }
+
+        @Override
+        void isolateParametersIfNotAlready() {
+        }
+
+        @Override
+        String getDisplayName() {
+            return "Test transform"
+        }
+
+        @Override
+        void visitDependencies(TaskDependencyResolveContext context) {
+        }
+
+        @Override
+        DirectorySensitivity getInputArtifactDirectorySensitivity() {
+            return DirectorySensitivity.DEFAULT
+        }
+
+        @Override
+        DirectorySensitivity getInputArtifactDependenciesDirectorySensitivity() {
+            return DirectorySensitivity.DEFAULT
+        }
+
+        @Override
+        LineEndingSensitivity getInputArtifactLineEndingNormalization() {
+            return LineEndingSensitivity.DEFAULT
+        }
+
+        @Override
+        LineEndingSensitivity getInputArtifactDependenciesLineEndingNormalization() {
+            return LineEndingSensitivity.DEFAULT
+        }
+    }
+
+    def "executes transformations in workspace (#transformationType)"(TransformationType transformationType) {
+        def inputArtifact = temporaryFolder.file("input")
+        inputArtifact.text = "my input"
+        def transform = TestTransform.create { input, outputDir ->
+            def outputFile = new File(outputDir, input.name)
+            outputFile.text = input.text + "transformed"
+            return [outputFile]
+        }
+
+        when:
+        def result = invoke(transform, inputArtifact, dependencies, dependency(transformationType, inputArtifact), inputFingerprinter)
+
+        then:
+        result.get().size() == 1
+        def transformedFile = result.get()[0]
+        transformedFile.parentFile.parentFile.parentFile == workspaceDirectory(transformationType)
+
+        where:
+        transformationType << TransformationType.values()
+    }
+
+    def "up-to-date on second run"() {
+        def inputArtifact = temporaryFolder.file("input")
+        inputArtifact.text = "my input"
+        int transformInvocations = 0
+        def transform = TestTransform.create { input, outputDir ->
+            transformInvocations++
+            def outputFile = new File(outputDir, input.name)
+            outputFile.text = input.text + "transformed"
+            return [outputFile]
+        }
+
+        when:
+        invoke(transform, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
+
+        then:
+        transformInvocations == 1
+        1 * artifactTransformListener.beforeTransformExecution(_, _)
+        1 * artifactTransformListener.afterTransformExecution(_, _)
+
+        when:
+        invoke(transform, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
+        then:
+        transformInvocations == 1
+        0 * _
+    }
+
+    def "re-runs transform when previous execution failed"() {
+        def inputArtifact = temporaryFolder.file("input")
+        inputArtifact.text = "my input"
+        def failure = new RuntimeException("broken")
+        int transformInvocations = 0
+        def transform = TestTransform.create { input, outputDir ->
+            transformInvocations++
+            def outputFile = new File(outputDir, input.name)
+            assert !outputFile.exists()
+            outputFile.text = input.text + "transformed"
+            if (transformInvocations == 1) {
+                throw failure
+            }
+            return [outputFile]
+        }
+
+        when:
+        def result = invoke(transform, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
+
+        then:
+        transformInvocations == 1
+        1 * artifactTransformListener.beforeTransformExecution(_, _)
+        1 * artifactTransformListener.afterTransformExecution(_, _)
+        def wrappedFailure = result.failure.get()
+        wrappedFailure.cause == failure
+
+        when:
+        invoke(transform, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
+        then:
+        transformInvocations == 2
+        1 * artifactTransformListener.beforeTransformExecution(_, _)
+        1 * artifactTransformListener.afterTransformExecution(_, _)
+    }
+
+    def "re-runs transform when output has been modified"() {
+        def inputArtifact = temporaryFolder.file("input")
+        inputArtifact.text = "my input"
+        File outputFile = null
+        int transformInvocations = 0
+        def transform = TestTransform.create { input, outputDir ->
+            transformInvocations++
+            outputFile = new File(outputDir, input.name)
+            assert !outputFile.exists()
+            outputFile.text = input.text + " transformed"
+            return [outputFile]
+        }
+
+        when:
+        invoke(transform, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
+        then:
+        transformInvocations == 1
+        outputFile?.isFile()
+
+        when:
+        fileSystemAccess.write([outputFile.absolutePath], { -> outputFile.text = "changed" })
+
+        invoke(transform, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
+        then:
+        transformInvocations == 2
+    }
+
+    def "different workspace for different secondary inputs (#transformationType)"(TransformationType transformationType) {
+        def inputArtifact = temporaryFolder.file("input")
+        inputArtifact.text = "my input"
+        def workspaces = new HashSet<File>()
+        def transformationAction = { File input, File workspace ->
+            workspaces.add(workspace)
+            def outputFile = new File(workspace, input.name)
+            outputFile.text = input.text + " transformed"
+            return ImmutableList.of(outputFile)
+        }
+        def transform1 = TestTransform.create(TestHashCodes.hashCodeFrom(1234), transformationAction)
+        def transform2 = TestTransform.create(TestHashCodes.hashCodeFrom(4321), transformationAction)
+
+        def subject = dependency(transformationType, inputArtifact)
+        when:
+        invoke(transform1, inputArtifact, dependencies, subject, inputFingerprinter)
+        invoke(transform2, inputArtifact, dependencies, subject, inputFingerprinter)
+
+        then:
+        workspaces.size() == 2
+
+        where:
+        transformationType << TransformationType.values()
+    }
+
+    def "different workspace for different input artifact paths (#transformationType)"(TransformationType transformationType) {
+        def inputArtifact1 = temporaryFolder.file("input1")
+        inputArtifact1.text = "my input"
+        def inputArtifact2 = temporaryFolder.file("input2")
+        inputArtifact1.text = "my input"
+        def workspaces = new HashSet<File>()
+        def transformationAction = { File input, File workspace ->
+            workspaces.add(workspace)
+            def outputFile = new File(workspace, input.name)
+            outputFile.text = input.text + " transformed"
+            return ImmutableList.of(outputFile)
+        }
+        def transform = TestTransform.create(TestHashCodes.hashCodeFrom(1234), transformationAction)
+        when:
+        invoke(transform, inputArtifact1, dependencies, dependency(transformationType, inputArtifact1), inputFingerprinter)
+        then:
+        workspaces.size() == 1
+
+        when:
+        fileSystemAccess.write([inputArtifact1.absolutePath], { -> inputArtifact1.text = "changed" })
+        invoke(transform, inputArtifact2, dependencies, dependency(transformationType, inputArtifact2), inputFingerprinter)
+
+        then:
+        workspaces.size() == 2
+
+        where:
+        transformationType << TransformationType.values()
+    }
+
+    def "different workspace for different immutable input artifacts"() {
+        def inputArtifact = temporaryFolder.file("input")
+        inputArtifact.text = "my input"
+        def workspaces = new HashSet<File>()
+        def transformationAction = { File input, File workspace ->
+            workspaces.add(workspace)
+            def outputFile = new File(workspace, input.name)
+            outputFile.text = input.text + " transformed"
+            return ImmutableList.of(outputFile)
+        }
+        def transform = TestTransform.create(TestHashCodes.hashCodeFrom(1234), transformationAction)
+        def subject = immutableDependency(inputArtifact)
+
+        when:
+        invoke(transform, inputArtifact, dependencies, subject, inputFingerprinter)
+        then:
+        workspaces.size() == 1
+
+        when:
+        fileSystemAccess.write([inputArtifact.absolutePath], { -> inputArtifact.text = "changed" })
+        invoke(transform, inputArtifact, dependencies, subject, inputFingerprinter)
+
+        then:
+        workspaces.size() == 2
+    }
+
+    def "same workspace for different mutable input artifacts"() {
+        def inputArtifact = temporaryFolder.file("input")
+        inputArtifact.text = "my input"
+        def workspaces = new HashSet<File>()
+        def transformationAction = { File input, File workspace ->
+            workspaces.add(workspace)
+            def outputFile = new File(workspace, input.name)
+            outputFile.text = input.text + " transformed"
+            return ImmutableList.of(outputFile)
+        }
+        def transform = TestTransform.create(TestHashCodes.hashCodeFrom(1234), transformationAction)
+        def subject = mutableDependency(inputArtifact)
+
+        when:
+        invoke(transform, inputArtifact, dependencies, subject, inputFingerprinter)
+        then:
+        workspaces.size() == 1
+
+        when:
+        fileSystemAccess.write([inputArtifact.absolutePath], { -> inputArtifact.text = "changed" })
+        invoke(transform, inputArtifact, dependencies, subject, inputFingerprinter)
+
+        then:
+        workspaces.size() == 1
+    }
+
+    enum TransformationType {
+        MUTABLE, IMMUTABLE
+    }
+
+    private dependency(TransformationType type, File file) {
+        return type == TransformationType.MUTABLE ? mutableDependency(file) : immutableDependency(file)
+    }
+
+    private workspaceDirectory(TransformationType type) {
+        return type == TransformationType.MUTABLE ? mutableTransformsStoreDirectory : immutableTransformsStoreDirectory
+    }
+
+    private TransformStepSubject immutableDependency(File file) {
+        return TransformStepSubject.initial(artifact(Stub(ComponentArtifactIdentifier), file))
+    }
+
+    private TransformStepSubject mutableDependency(File file) {
+        def artifactIdentifier = new ComponentFileArtifactIdentifier(
+            new DefaultProjectComponentIdentifier(
+                DefaultBuildIdentifier.ROOT,
+                Path.path(":child"),
+                Path.path(":child"),
+                "child"
+            ), file.getName())
+        return TransformStepSubject.initial(artifact(artifactIdentifier, file))
+    }
+
+    private ResolvableArtifact artifact(ComponentArtifactIdentifier id, File file) {
+        return Stub(ResolvableArtifact) {
+            getId() >> id
+        }
+    }
+
+    private Try<ImmutableList<File>> invoke(
+        Transform transform,
+        File inputArtifact,
+        TransformDependencies dependencies,
+        TransformStepSubject subject,
+        InputFingerprinter inputFingerprinter
+    ) {
+        return invoker.createInvocation(transform, inputArtifact, dependencies, subject, inputFingerprinter).completeAndGet()
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultTransformerInvocationFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultTransformerInvocationFactoryTest.groovy
deleted file mode 100644
index 3cde670..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultTransformerInvocationFactoryTest.groovy
+++ /dev/null
@@ -1,528 +0,0 @@
-/*
- * Copyright 2017 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.api.internal.artifacts.transform
-
-import com.google.common.collect.ImmutableList
-import org.gradle.api.artifacts.component.ComponentArtifactIdentifier
-import org.gradle.api.artifacts.component.ProjectComponentIdentifier
-import org.gradle.api.artifacts.transform.TransformAction
-import org.gradle.api.file.FileSystemLocation
-import org.gradle.api.internal.DocumentationRegistry
-import org.gradle.api.internal.artifacts.DefaultBuildIdentifier
-import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier
-import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
-import org.gradle.api.internal.attributes.ImmutableAttributes
-import org.gradle.api.internal.file.TestFiles
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.internal.project.ProjectState
-import org.gradle.api.internal.project.ProjectStateRegistry
-import org.gradle.api.internal.tasks.TaskDependencyResolveContext
-import org.gradle.api.provider.Provider
-import org.gradle.caching.internal.controller.BuildCacheController
-import org.gradle.initialization.DefaultBuildCancellationToken
-import org.gradle.internal.Try
-import org.gradle.internal.component.local.model.ComponentFileArtifactIdentifier
-import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager
-import org.gradle.internal.execution.BuildOutputCleanupRegistry
-import org.gradle.internal.execution.InputFingerprinter
-import org.gradle.internal.execution.OutputChangeListener
-import org.gradle.internal.execution.TestExecutionHistoryStore
-import org.gradle.internal.execution.WorkInputListeners
-import org.gradle.internal.execution.history.OutputFilesRepository
-import org.gradle.internal.execution.history.changes.DefaultExecutionStateChangeDetector
-import org.gradle.internal.execution.history.impl.DefaultOverlappingOutputDetector
-import org.gradle.internal.execution.impl.DefaultFileCollectionFingerprinterRegistry
-import org.gradle.internal.execution.impl.DefaultInputFingerprinter
-import org.gradle.internal.execution.impl.DefaultOutputSnapshotter
-import org.gradle.internal.execution.impl.FingerprinterRegistration
-import org.gradle.internal.execution.model.InputNormalizer
-import org.gradle.internal.execution.steps.ValidateStep
-import org.gradle.internal.execution.timeout.TimeoutHandler
-import org.gradle.internal.fingerprint.DirectorySensitivity
-import org.gradle.internal.fingerprint.FileNormalizer
-import org.gradle.internal.fingerprint.LineEndingSensitivity
-import org.gradle.internal.fingerprint.hashing.FileSystemLocationSnapshotHasher
-import org.gradle.internal.fingerprint.impl.AbsolutePathFileCollectionFingerprinter
-import org.gradle.internal.fingerprint.impl.DefaultFileCollectionSnapshotter
-import org.gradle.internal.hash.ClassLoaderHierarchyHasher
-import org.gradle.internal.hash.HashCode
-import org.gradle.internal.hash.TestHashCodes
-import org.gradle.internal.id.UniqueId
-import org.gradle.internal.operations.CurrentBuildOperationRef
-import org.gradle.internal.operations.TestBuildOperationExecutor
-import org.gradle.internal.scopeids.id.BuildInvocationScopeId
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.internal.service.scopes.ExecutionGradleServices
-import org.gradle.internal.snapshot.impl.DefaultValueSnapshotter
-import org.gradle.test.fixtures.AbstractProjectBuilderSpec
-import org.gradle.util.Path
-import org.gradle.work.InputChanges
-
-import java.util.function.BiFunction
-
-class DefaultTransformerInvocationFactoryTest extends AbstractProjectBuilderSpec {
-    private DocumentationRegistry documentationRegistry = new DocumentationRegistry()
-
-    def immutableTransformsStoreDirectory = temporaryFolder.file("output")
-    def mutableTransformsStoreDirectory = temporaryFolder.file("child/build/transforms")
-
-    def classloaderHasher = Stub(ClassLoaderHierarchyHasher) {
-        getClassLoaderHash(_ as ClassLoader) >> TestHashCodes.hashCodeFrom(1234)
-    }
-    def valueSnapshotter = new DefaultValueSnapshotter([], classloaderHasher)
-
-    def executionHistoryStore = new TestExecutionHistoryStore()
-    def virtualFileSystem = TestFiles.virtualFileSystem()
-    def fileSystemAccess = TestFiles.fileSystemAccess(virtualFileSystem)
-    def fileCollectionSnapshotter = new DefaultFileCollectionSnapshotter(fileSystemAccess, TestFiles.fileSystem())
-
-    def transformationWorkspaceServices = new TestTransformationWorkspaceServices(immutableTransformsStoreDirectory, executionHistoryStore)
-
-    def fileCollectionFactory = TestFiles.fileCollectionFactory()
-    def artifactTransformListener = Mock(ArtifactTransformListener)
-
-    def dependencyFingerprinter = new AbsolutePathFileCollectionFingerprinter(DirectorySensitivity.DEFAULT, fileCollectionSnapshotter, FileSystemLocationSnapshotHasher.DEFAULT)
-    def fileCollectionFingerprinterRegistry = new DefaultFileCollectionFingerprinterRegistry([FingerprinterRegistration.registration(DirectorySensitivity.DEFAULT, LineEndingSensitivity.DEFAULT, dependencyFingerprinter)])
-    def inputFingerprinter = new DefaultInputFingerprinter(fileCollectionSnapshotter, fileCollectionFingerprinterRegistry, valueSnapshotter)
-
-    def projectServiceRegistry = Stub(ServiceRegistry) {
-        get(TransformationWorkspaceServices) >> new TestTransformationWorkspaceServices(mutableTransformsStoreDirectory, executionHistoryStore)
-    }
-
-    def childProject = Stub(ProjectInternal) {
-        getServices() >> projectServiceRegistry
-    }
-
-    def projectStateRegistry = Stub(ProjectStateRegistry) {
-        stateFor(_ as ProjectComponentIdentifier) >> Stub(ProjectState) {
-            getMutableModel() >> childProject
-        }
-    }
-
-    def dependencies = Stub(ArtifactTransformDependencies) {
-        getFiles() >> Optional.empty()
-    }
-
-    def buildOperationExecutor = new TestBuildOperationExecutor()
-
-    def buildCacheController = Stub(BuildCacheController)
-    def buildInvocationScopeId = new BuildInvocationScopeId(UniqueId.generate())
-    def cancellationToken = new DefaultBuildCancellationToken()
-    def outputChangeListener = { affectedOutputPaths -> fileSystemAccess.write(affectedOutputPaths, {}) } as OutputChangeListener
-    def outputFilesRepository = Stub(OutputFilesRepository) {
-        isGeneratedByGradle(_ as File) >> true
-    }
-    def workInputListeners = Stub(WorkInputListeners)
-    def buildOutputCleanupRegistry = Mock(BuildOutputCleanupRegistry)
-    def outputSnapshotter = new DefaultOutputSnapshotter(fileCollectionSnapshotter)
-    def deleter = TestFiles.deleter()
-    def validationWarningRecorder = Mock(ValidateStep.ValidationWarningRecorder)
-    def executionEngine = new ExecutionGradleServices().createExecutionEngine(
-        buildCacheController,
-        cancellationToken,
-        buildInvocationScopeId,
-        buildOperationExecutor,
-        buildOutputCleanupRegistry,
-        new GradleEnterprisePluginManager(),
-        classloaderHasher,
-        new CurrentBuildOperationRef(),
-        deleter,
-        new DefaultExecutionStateChangeDetector(),
-        outputChangeListener,
-        workInputListeners,
-        outputFilesRepository,
-        outputSnapshotter,
-        new DefaultOverlappingOutputDetector(),
-        Mock(TimeoutHandler),
-        validationWarningRecorder,
-        virtualFileSystem,
-        documentationRegistry
-    )
-
-    def invoker = new DefaultTransformerInvocationFactory(
-        executionEngine,
-        fileSystemAccess,
-        artifactTransformListener,
-        transformationWorkspaceServices,
-        fileCollectionFactory,
-        projectStateRegistry,
-        buildOperationExecutor
-    )
-
-    private static class TestTransformer implements Transformer {
-        private final HashCode secondaryInputsHash
-        private final BiFunction<File, File, List<File>> transformationAction
-
-        static TestTransformer create(HashCode secondaryInputsHash = TestHashCodes.hashCodeFrom(1234), BiFunction<File, File, List<File>> transformationAction) {
-            return new TestTransformer(secondaryInputsHash, transformationAction)
-        }
-
-        TestTransformer(HashCode secondaryInputsHash, BiFunction<File, File, List<File>> transformationAction) {
-            this.transformationAction = transformationAction
-            this.secondaryInputsHash = secondaryInputsHash
-        }
-
-        @Override
-        Class<?> getImplementationClass() {
-            return TransformAction.class
-        }
-
-        @Override
-        ImmutableAttributes getFromAttributes() {
-            return ImmutableAttributes.EMPTY
-        }
-
-        @Override
-        ImmutableAttributes getToAttributes() {
-            return ImmutableAttributes.EMPTY
-        }
-
-        @Override
-        boolean requiresDependencies() {
-            return false
-        }
-
-        @Override
-        boolean requiresInputChanges() {
-            return false
-        }
-
-        @Override
-        boolean isCacheable() {
-            return false
-        }
-
-        @Override
-        TransformationResult transform(Provider<FileSystemLocation> inputArtifactProvider, File outputDir, ArtifactTransformDependencies dependencies, InputChanges inputChanges) {
-            def builder = TransformationResult.builderFor(inputArtifactProvider.get().asFile, outputDir)
-            transformationAction.apply(inputArtifactProvider.get().asFile, outputDir).each {
-                builder.addOutput(it) {}
-            }
-            return builder.build()
-        }
-
-        @Override
-        HashCode getSecondaryInputHash() {
-            return secondaryInputsHash
-        }
-
-        @Override
-        FileNormalizer getInputArtifactNormalizer() {
-            return InputNormalizer.ABSOLUTE_PATH
-        }
-
-        @Override
-        FileNormalizer getInputArtifactDependenciesNormalizer() {
-            return InputNormalizer.ABSOLUTE_PATH
-        }
-
-        @Override
-        boolean isIsolated() {
-            return true
-        }
-
-        @Override
-        void isolateParametersIfNotAlready() {
-        }
-
-        @Override
-        String getDisplayName() {
-            return "Test transformer"
-        }
-
-        @Override
-        void visitDependencies(TaskDependencyResolveContext context) {
-        }
-
-        @Override
-        DirectorySensitivity getInputArtifactDirectorySensitivity() {
-            return DirectorySensitivity.DEFAULT
-        }
-
-        @Override
-        DirectorySensitivity getInputArtifactDependenciesDirectorySensitivity() {
-            return DirectorySensitivity.DEFAULT
-        }
-
-        @Override
-        LineEndingSensitivity getInputArtifactLineEndingNormalization() {
-            return LineEndingSensitivity.DEFAULT
-        }
-
-        @Override
-        LineEndingSensitivity getInputArtifactDependenciesLineEndingNormalization() {
-            return LineEndingSensitivity.DEFAULT
-        }
-    }
-
-    def "executes transformations in workspace (#transformationType)"(TransformationType transformationType) {
-        def inputArtifact = temporaryFolder.file("input")
-        inputArtifact.text = "my input"
-        def transformer = TestTransformer.create { input, outputDir ->
-            def outputFile = new File(outputDir, input.name)
-            outputFile.text = input.text + "transformed"
-            return [outputFile]
-        }
-
-        when:
-        def result = invoke(transformer, inputArtifact, dependencies, dependency(transformationType, inputArtifact), inputFingerprinter)
-
-        then:
-        result.get().size() == 1
-        def transformedFile = result.get()[0]
-        transformedFile.parentFile.parentFile.parentFile == workspaceDirectory(transformationType)
-
-        where:
-        transformationType << TransformationType.values()
-    }
-
-    def "up-to-date on second run"() {
-        def inputArtifact = temporaryFolder.file("input")
-        inputArtifact.text = "my input"
-        int transformerInvocations = 0
-        def transformer = TestTransformer.create { input, outputDir ->
-            transformerInvocations++
-            def outputFile = new File(outputDir, input.name)
-            outputFile.text = input.text + "transformed"
-            return [outputFile]
-        }
-
-        when:
-        invoke(transformer, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
-
-        then:
-        transformerInvocations == 1
-        1 * artifactTransformListener.beforeTransformerInvocation(_, _)
-        1 * artifactTransformListener.afterTransformerInvocation(_, _)
-
-        when:
-        invoke(transformer, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
-        then:
-        transformerInvocations == 1
-        0 * _
-    }
-
-    def "re-runs transform when previous execution failed"() {
-        def inputArtifact = temporaryFolder.file("input")
-        inputArtifact.text = "my input"
-        def failure = new RuntimeException("broken")
-        int transformerInvocations = 0
-        def transformer = TestTransformer.create { input, outputDir ->
-            transformerInvocations++
-            def outputFile = new File(outputDir, input.name)
-            assert !outputFile.exists()
-            outputFile.text = input.text + "transformed"
-            if (transformerInvocations == 1) {
-                throw failure
-            }
-            return [outputFile]
-        }
-
-        when:
-        def result = invoke(transformer, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
-
-        then:
-        transformerInvocations == 1
-        1 * artifactTransformListener.beforeTransformerInvocation(_, _)
-        1 * artifactTransformListener.afterTransformerInvocation(_, _)
-        def wrappedFailure = result.failure.get()
-        wrappedFailure.cause == failure
-
-        when:
-        invoke(transformer, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
-        then:
-        transformerInvocations == 2
-        1 * artifactTransformListener.beforeTransformerInvocation(_, _)
-        1 * artifactTransformListener.afterTransformerInvocation(_, _)
-    }
-
-    def "re-runs transform when output has been modified"() {
-        def inputArtifact = temporaryFolder.file("input")
-        inputArtifact.text = "my input"
-        File outputFile = null
-        int transformerInvocations = 0
-        def transformer = TestTransformer.create { input, outputDir ->
-            transformerInvocations++
-            outputFile = new File(outputDir, input.name)
-            assert !outputFile.exists()
-            outputFile.text = input.text + " transformed"
-            return [outputFile]
-        }
-
-        when:
-        invoke(transformer, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
-        then:
-        transformerInvocations == 1
-        outputFile?.isFile()
-
-        when:
-        fileSystemAccess.write([outputFile.absolutePath], { -> outputFile.text = "changed" })
-
-        invoke(transformer, inputArtifact, dependencies, immutableDependency(inputArtifact), inputFingerprinter)
-        then:
-        transformerInvocations == 2
-    }
-
-    def "different workspace for different secondary inputs (#transformationType)"(TransformationType transformationType) {
-        def inputArtifact = temporaryFolder.file("input")
-        inputArtifact.text = "my input"
-        def workspaces = new HashSet<File>()
-        def transformationAction = { File input, File workspace ->
-            workspaces.add(workspace)
-            def outputFile = new File(workspace, input.name)
-            outputFile.text = input.text + " transformed"
-            return ImmutableList.of(outputFile)
-        }
-        def transformer1 = TestTransformer.create(TestHashCodes.hashCodeFrom(1234), transformationAction)
-        def transformer2 = TestTransformer.create(TestHashCodes.hashCodeFrom(4321), transformationAction)
-
-        def subject = dependency(transformationType, inputArtifact)
-        when:
-        invoke(transformer1, inputArtifact, dependencies, subject, inputFingerprinter)
-        invoke(transformer2, inputArtifact, dependencies, subject, inputFingerprinter)
-
-        then:
-        workspaces.size() == 2
-
-        where:
-        transformationType << TransformationType.values()
-    }
-
-    def "different workspace for different input artifact paths (#transformationType)"(TransformationType transformationType) {
-        def inputArtifact1 = temporaryFolder.file("input1")
-        inputArtifact1.text = "my input"
-        def inputArtifact2 = temporaryFolder.file("input2")
-        inputArtifact1.text = "my input"
-        def workspaces = new HashSet<File>()
-        def transformationAction = { File input, File workspace ->
-            workspaces.add(workspace)
-            def outputFile = new File(workspace, input.name)
-            outputFile.text = input.text + " transformed"
-            return ImmutableList.of(outputFile)
-        }
-        def transformer = TestTransformer.create(TestHashCodes.hashCodeFrom(1234), transformationAction)
-        when:
-        invoke(transformer, inputArtifact1, dependencies, dependency(transformationType, inputArtifact1), inputFingerprinter)
-        then:
-        workspaces.size() == 1
-
-        when:
-        fileSystemAccess.write([inputArtifact1.absolutePath], { -> inputArtifact1.text = "changed" })
-        invoke(transformer, inputArtifact2, dependencies, dependency(transformationType, inputArtifact2), inputFingerprinter)
-
-        then:
-        workspaces.size() == 2
-
-        where:
-        transformationType << TransformationType.values()
-    }
-
-    def "different workspace for different immutable input artifacts"() {
-        def inputArtifact = temporaryFolder.file("input")
-        inputArtifact.text = "my input"
-        def workspaces = new HashSet<File>()
-        def transformationAction = { File input, File workspace ->
-            workspaces.add(workspace)
-            def outputFile = new File(workspace, input.name)
-            outputFile.text = input.text + " transformed"
-            return ImmutableList.of(outputFile)
-        }
-        def transformer = TestTransformer.create(TestHashCodes.hashCodeFrom(1234), transformationAction)
-        def subject = immutableDependency(inputArtifact)
-
-        when:
-        invoke(transformer, inputArtifact, dependencies, subject, inputFingerprinter)
-        then:
-        workspaces.size() == 1
-
-        when:
-        fileSystemAccess.write([inputArtifact.absolutePath], { -> inputArtifact.text = "changed" })
-        invoke(transformer, inputArtifact, dependencies, subject, inputFingerprinter)
-
-        then:
-        workspaces.size() == 2
-    }
-
-    def "same workspace for different mutable input artifacts"() {
-        def inputArtifact = temporaryFolder.file("input")
-        inputArtifact.text = "my input"
-        def workspaces = new HashSet<File>()
-        def transformationAction = { File input, File workspace ->
-            workspaces.add(workspace)
-            def outputFile = new File(workspace, input.name)
-            outputFile.text = input.text + " transformed"
-            return ImmutableList.of(outputFile)
-        }
-        def transformer = TestTransformer.create(TestHashCodes.hashCodeFrom(1234), transformationAction)
-        def subject = mutableDependency(inputArtifact)
-
-        when:
-        invoke(transformer, inputArtifact, dependencies, subject, inputFingerprinter)
-        then:
-        workspaces.size() == 1
-
-        when:
-        fileSystemAccess.write([inputArtifact.absolutePath], { -> inputArtifact.text = "changed" })
-        invoke(transformer, inputArtifact, dependencies, subject, inputFingerprinter)
-
-        then:
-        workspaces.size() == 1
-    }
-
-    enum TransformationType {
-        MUTABLE, IMMUTABLE
-    }
-
-    private dependency(TransformationType type, File file) {
-        return type == TransformationType.MUTABLE ? mutableDependency(file) : immutableDependency(file)
-    }
-
-    private workspaceDirectory(TransformationType type) {
-        return type == TransformationType.MUTABLE ? mutableTransformsStoreDirectory : immutableTransformsStoreDirectory
-    }
-
-    private TransformationSubject immutableDependency(File file) {
-        return TransformationSubject.initial(artifact(Stub(ComponentArtifactIdentifier), file))
-    }
-
-    private TransformationSubject mutableDependency(File file) {
-        def artifactIdentifier = new ComponentFileArtifactIdentifier(
-            new DefaultProjectComponentIdentifier(
-                DefaultBuildIdentifier.ROOT,
-                Path.path(":child"),
-                Path.path(":child"),
-                "child"
-            ), file.getName())
-        return TransformationSubject.initial(artifact(artifactIdentifier, file))
-    }
-
-    private ResolvableArtifact artifact(ComponentArtifactIdentifier id, File file) {
-        return Stub(ResolvableArtifact) {
-            getId() >> id
-        }
-    }
-
-    private Try<ImmutableList<File>> invoke(
-        Transformer transformer,
-        File inputArtifact,
-        ArtifactTransformDependencies dependencies,
-        TransformationSubject subject,
-        InputFingerprinter inputFingerprinter
-    ) {
-        return invoker.createInvocation(transformer, inputArtifact, dependencies, subject, inputFingerprinter).completeAndGet()
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultVariantSelectorFactoryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultVariantSelectorFactoryTest.groovy
new file mode 100644
index 0000000..091014e
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultVariantSelectorFactoryTest.groovy
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2016 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.api.internal.artifacts.transform
+
+import com.google.common.collect.ImmutableList
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariant
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedVariantSet
+import org.gradle.api.internal.attributes.AttributeContainerInternal
+import org.gradle.api.internal.attributes.AttributesSchemaInternal
+import org.gradle.api.internal.attributes.DefaultMutableAttributeContainer
+import org.gradle.api.internal.attributes.ImmutableAttributes
+import org.gradle.internal.Describables
+import org.gradle.internal.component.AmbiguousVariantSelectionException
+import org.gradle.internal.component.NoMatchingVariantSelectionException
+import org.gradle.internal.component.model.AttributeMatcher
+import org.gradle.internal.component.model.AttributeMatchingExplanationBuilder
+import org.gradle.util.AttributeTestUtil
+import spock.lang.Specification
+
+import static org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE
+import static org.gradle.util.internal.TextUtil.toPlatformLineSeparators
+
+class DefaultVariantSelectorFactoryTest extends Specification {
+    def matchingCache = Mock(ConsumerProvidedVariantFinder)
+    def producerSchema = Mock(AttributesSchemaInternal)
+    def consumerSchema = Mock(AttributesSchemaInternal) {
+        getConsumerDescribers() >> []
+    }
+    def attributeMatcher = Mock(AttributeMatcher)
+    def factory = Mock(VariantSelector.Factory)
+    def dependenciesResolverFactory = Stub(TransformUpstreamDependenciesResolverFactory)
+    def transformedVariantFactory = Mock(TransformedVariantFactory)
+    def variantSelectorFactory = new DefaultVariantSelectorFactory(matchingCache, consumerSchema, AttributeTestUtil.attributesFactory(), transformedVariantFactory)
+
+    def "selects producer variant with requested attributes"() {
+        def variant1 = resolvedVariant()
+        def variant2 = resolvedVariant()
+        def variant1Artifacts = Stub(ResolvedArtifactSet)
+        def set = resolvedVariantSet()
+        def variants = [variant1, variant2] as Set
+
+        given:
+        set.schema >> producerSchema
+        set.variants >> variants
+        variant1.attributes >> typeAttributes("classes")
+        variant1.artifacts >> variant1Artifacts
+        variant2.attributes >> typeAttributes("jar")
+
+        consumerSchema.withProducer(producerSchema) >> attributeMatcher
+        attributeMatcher.matches(_ as Collection, typeAttributes("classes"), _ as AttributeMatchingExplanationBuilder) >> [variant1]
+
+        expect:
+        def result = variantSelectorFactory.create(typeAttributes("classes"), true, false, dependenciesResolverFactory).select(set, factory)
+        result == variant1Artifacts
+    }
+
+    def "fails when multiple producer variants match"() {
+        def variant1 = resolvedVariant()
+        def variant2 = resolvedVariant()
+        def set = resolvedVariantSet()
+        def variants = [variant1, variant2] as Set
+
+        given:
+        set.asDescribable() >> Describables.of('<component>')
+        set.schema >> producerSchema
+        set.variants >> variants
+        variant1.asDescribable() >> Describables.of('<variant1>')
+        variant1.attributes >> typeAttributes("classes")
+        variant2.asDescribable() >> Describables.of('<variant2>')
+        variant2.attributes >> typeAttributes("jar")
+
+        consumerSchema.withProducer(producerSchema) >> attributeMatcher
+        attributeMatcher.matches(_ as Collection, typeAttributes("classes"), _ as AttributeMatchingExplanationBuilder) >> [variant1, variant2]
+        attributeMatcher.isMatching(_, _, _) >> true
+
+        when:
+        def result = variantSelectorFactory.create(typeAttributes("classes"), true, false, dependenciesResolverFactory).select(set, factory)
+        visit(result)
+
+        then:
+        def e = thrown(AmbiguousVariantSelectionException)
+        e.message == toPlatformLineSeparators("""The consumer was configured to find attribute 'artifactType' with value 'classes'. However we cannot choose between the following variants of <component>:
+  - <variant1> declares attribute 'artifactType' with value 'classes'
+  - <variant2> declares attribute 'artifactType' with value 'jar'""")
+    }
+
+    def "fails when multiple transforms match"() {
+        def requested = typeAttributes("dll")
+        def variant1 = resolvedVariant()
+        def variant2 = resolvedVariant()
+        def set = resolvedVariantSet()
+        def variants = [variant1, variant2] as Set
+        def transformedVariants = variants.collect { transformedVariant(it, requested)}
+
+        given:
+        set.schema >> producerSchema
+        set.variants >> variants
+        set.asDescribable() >> Describables.of('<component>')
+        variant1.attributes >> typeAttributes("jar")
+        variant1.asDescribable() >> Describables.of('<variant1>')
+        variant2.attributes >> typeAttributes("classes")
+        variant2.asDescribable() >> Describables.of('<variant2>')
+
+        consumerSchema.withProducer(producerSchema) >> attributeMatcher
+        attributeMatcher.matches(ImmutableList.copyOf(variants), _, _) >> []
+        attributeMatcher.matches(transformedVariants, _, _) >> transformedVariants
+        matchingCache.findTransformedVariants(_, _) >> transformedVariants
+
+        def selector = variantSelectorFactory.create(requested, true, false, dependenciesResolverFactory)
+
+        when:
+        def result = selector.select(set, factory)
+        visit(result)
+
+        then:
+        def e = thrown(AmbiguousTransformException)
+        e.message == toPlatformLineSeparators("""Found multiple transforms that can produce a variant of <component> with requested attributes:
+  - artifactType 'dll'
+Found the following transforms:
+  - From '<variant1>':
+      - With source attributes: artifactType 'jar'
+      - Candidate transform(s):
+          - Transform '' producing attributes: artifactType 'dll'
+  - From '<variant2>':
+      - With source attributes: artifactType 'classes'
+      - Candidate transform(s):
+          - Transform '' producing attributes: artifactType 'dll'""")
+    }
+
+    def "returns empty variant when no variants match and ignore no matching enabled"() {
+        def variant1 = resolvedVariant()
+        def variant2 = resolvedVariant()
+        def set = resolvedVariantSet()
+        def variants = [variant1, variant2] as Set
+
+        given:
+        set.schema >> producerSchema
+        set.variants >> variants
+        variant1.attributes >> typeAttributes("jar")
+        variant2.attributes >> typeAttributes("classes")
+
+        consumerSchema.withProducer(producerSchema) >> attributeMatcher
+        attributeMatcher.matches(_, _, _) >> []
+
+        matchingCache.findTransformedVariants(_, _) >> []
+
+        expect:
+        def result = variantSelectorFactory.create(typeAttributes("dll"), true, false, dependenciesResolverFactory).select(set, factory)
+        result == ResolvedArtifactSet.EMPTY
+    }
+
+    def "fails when no variants match and ignore no matching disabled"() {
+        def variant1 = resolvedVariant()
+        def variant2 = resolvedVariant()
+        def set = resolvedVariantSet()
+        def variants = [variant1, variant2] as Set
+
+        given:
+        set.schema >> producerSchema
+        set.variants >> variants
+        set.asDescribable() >> Describables.of('<component>')
+        variant1.attributes >> typeAttributes("jar")
+        variant1.asDescribable() >> Describables.of('<variant1>')
+        variant2.attributes >> typeAttributes("classes")
+        variant2.asDescribable() >> Describables.of('<variant2>')
+
+        consumerSchema.withProducer(producerSchema) >> attributeMatcher
+        attributeMatcher.matches(_, _, _) >> []
+
+        matchingCache.findTransformedVariants(_, _) >> []
+
+        when:
+        def result = variantSelectorFactory.create(typeAttributes("dll"), false, false, dependenciesResolverFactory).select(set, factory)
+        visit(result)
+
+        then:
+        def e = thrown(NoMatchingVariantSelectionException)
+        e.message == toPlatformLineSeparators("""No variants of  match the consumer attributes:
+  - <variant1>:
+      - Incompatible because this component declares attribute 'artifactType' with value 'jar' and the consumer needed attribute 'artifactType' with value 'dll'
+  - <variant2>:
+      - Incompatible because this component declares attribute 'artifactType' with value 'classes' and the consumer needed attribute 'artifactType' with value 'dll'""")
+    }
+
+    private ResolvedVariant resolvedVariant() {
+        Stub(ResolvedVariant)
+    }
+
+    private ResolvedVariantSet resolvedVariantSet() {
+        Stub(ResolvedVariantSet) {
+            getOverriddenAttributes() >> ImmutableAttributes.EMPTY
+        }
+    }
+
+    def visit(ResolvedArtifactSet set) {
+        def artifactVisitor = Stub(ArtifactVisitor)
+        _ * artifactVisitor.visitFailure(_) >> { Throwable t -> throw t }
+        def visitor = Stub(ResolvedArtifactSet.Visitor)
+        _ * visitor.visitArtifacts(_) >> { ResolvedArtifactSet.Artifacts artifacts -> artifacts.visit(artifactVisitor) }
+        set.visit(visitor)
+    }
+
+    private static AttributeContainerInternal typeAttributes(String artifactType) {
+        def attributeContainer = new DefaultMutableAttributeContainer(AttributeTestUtil.attributesFactory())
+        attributeContainer.attribute(ARTIFACT_TYPE_ATTRIBUTE, artifactType)
+        attributeContainer.asImmutable()
+    }
+
+    TransformedVariant transformedVariant(ResolvedVariant root, AttributeContainerInternal attributes) {
+        ImmutableAttributes attrs = attributes.asImmutable()
+        TransformStep step = Mock(TransformStep) {
+            getDisplayName() >> ""
+        }
+        VariantDefinition definition = Mock(VariantDefinition) {
+            getTransformChain() >> new TransformChain(null, step)
+            getTargetAttributes() >> attrs
+        }
+        return new TransformedVariant(root, definition)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistryTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistryTest.groovy
index a5721f8..8ce18e3 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistryTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/DefaultVariantTransformRegistryTest.groovy
@@ -55,7 +55,7 @@
     final TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass())
 
     def instantiatorFactory = TestUtil.instantiatorFactory()
-    def transformerInvocationFactory = Mock(TransformerInvocationFactory)
+    def transformInvocationFactory = Mock(TransformInvocationFactory)
     def inputFingerprinter = Mock(InputFingerprinter)
     def fileCollectionFactory = Mock(FileCollectionFactory)
     def propertyWalker = Mock(PropertyWalker)
@@ -70,21 +70,21 @@
     def classLoaderHierarchyHasher = Mock(ClassLoaderHierarchyHasher)
     def calculatedValueContainerFactory = TestUtil.calculatedValueContainerFactory()
     def attributesFactory = AttributeTestUtil.attributesFactory()
-    def registryFactory = new DefaultTransformationRegistrationFactory(
+    def registryFactory = new DefaultTransformRegistrationFactory(
         new TestBuildOperationExecutor(),
         isolatableFactory,
         classLoaderHierarchyHasher,
-        transformerInvocationFactory,
+        transformInvocationFactory,
         fileCollectionFactory,
         Mock(FileLookup),
         inputFingerprinter,
         calculatedValueContainerFactory,
         domainObjectContext,
-        new ArtifactTransformParameterScheme(
+        new TransformParameterScheme(
             instantiatorFactory.injectScheme(),
             inspectionScheme
         ),
-        new ArtifactTransformActionScheme(
+        new TransformActionScheme(
             instantiatorFactory.injectScheme(
                 ImmutableSet.of(InputArtifact, InputArtifactDependencies)
             ),
@@ -107,12 +107,12 @@
         }
 
         then:
-        registry.transforms.size() == 1
-        def registration = registry.transforms[0]
+        registry.registrations.size() == 1
+        def registration = registry.registrations[0]
         registration.from.getAttribute(TEST_ATTRIBUTE) == "FROM"
         registration.to.getAttribute(TEST_ATTRIBUTE) == "TO"
-        registration.transformationStep.transformer.implementationClass == TestTransform
-        registration.transformationStep.transformer.isolatedParameters.supplier.parameterObject instanceof TestTransform.Parameters
+        registration.transformStep.transform.implementationClass == TestTransform
+        registration.transformStep.transform.isolatedParameters.supplier.parameterObject instanceof TestTransform.Parameters
     }
 
     def "creates registration for parameterless action"() {
@@ -123,12 +123,12 @@
         }
 
         then:
-        registry.transforms.size() == 1
-        def registration = registry.transforms[0]
+        registry.registrations.size() == 1
+        def registration = registry.registrations[0]
         registration.from.getAttribute(TEST_ATTRIBUTE) == "FROM"
         registration.to.getAttribute(TEST_ATTRIBUTE) == "TO"
-        registration.transformationStep.transformer.implementationClass == ParameterlessTestTransform
-        registration.transformationStep.transformer.isolatedParameters.supplier.parameterObject == null
+        registration.transformStep.transform.implementationClass == ParameterlessTestTransform
+        registration.transformStep.transform.isolatedParameters.supplier.parameterObject == null
     }
 
     def "cannot use TransformParameters as parameter type"() {
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TestTransformWorkspaceServices.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TestTransformWorkspaceServices.java
new file mode 100644
index 0000000..f1f434a
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TestTransformWorkspaceServices.java
@@ -0,0 +1,52 @@
+/*
+ * 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.api.internal.artifacts.transform;
+
+import org.gradle.cache.Cache;
+import org.gradle.cache.ManualEvictionInMemoryCache;
+import org.gradle.internal.Try;
+import org.gradle.internal.execution.UnitOfWork;
+import org.gradle.internal.execution.history.ExecutionHistoryStore;
+import org.gradle.internal.execution.workspace.WorkspaceProvider;
+
+import java.io.File;
+
+public class TestTransformWorkspaceServices implements TransformWorkspaceServices {
+    private final File transformationsStoreDirectory;
+    private final ExecutionHistoryStore executionHistoryStore;
+
+    public TestTransformWorkspaceServices(File transformationsStoreDirectory, ExecutionHistoryStore executionHistoryStore) {
+        this.transformationsStoreDirectory = transformationsStoreDirectory;
+        this.executionHistoryStore = executionHistoryStore;
+    }
+
+    @Override
+    public WorkspaceProvider getWorkspaceProvider() {
+        return new WorkspaceProvider() {
+            @Override
+            public <T> T withWorkspace(String path, WorkspaceAction<T> action) {
+                File workspace = new File(transformationsStoreDirectory, path);
+                return action.executeInWorkspace(workspace, executionHistoryStore);
+            }
+        };
+    }
+
+    @Override
+    public Cache<UnitOfWork.Identity, Try<TransformExecutionResult>> getIdentityCache() {
+        return new ManualEvictionInMemoryCache<>();
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TestTransformationWorkspaceServices.java b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TestTransformationWorkspaceServices.java
deleted file mode 100644
index fdbc0b4..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TestTransformationWorkspaceServices.java
+++ /dev/null
@@ -1,52 +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.api.internal.artifacts.transform;
-
-import org.gradle.cache.Cache;
-import org.gradle.cache.ManualEvictionInMemoryCache;
-import org.gradle.internal.Try;
-import org.gradle.internal.execution.UnitOfWork;
-import org.gradle.internal.execution.history.ExecutionHistoryStore;
-import org.gradle.internal.execution.workspace.WorkspaceProvider;
-
-import java.io.File;
-
-public class TestTransformationWorkspaceServices implements TransformationWorkspaceServices {
-    private final File transformationsStoreDirectory;
-    private final ExecutionHistoryStore executionHistoryStore;
-
-    public TestTransformationWorkspaceServices(File transformationsStoreDirectory, ExecutionHistoryStore executionHistoryStore) {
-        this.transformationsStoreDirectory = transformationsStoreDirectory;
-        this.executionHistoryStore = executionHistoryStore;
-    }
-
-    @Override
-    public WorkspaceProvider getWorkspaceProvider() {
-        return new WorkspaceProvider() {
-            @Override
-            public <T> T withWorkspace(String path, WorkspaceAction<T> action) {
-                File workspace = new File(transformationsStoreDirectory, path);
-                return action.executeInWorkspace(workspace, executionHistoryStore);
-            }
-        };
-    }
-
-    @Override
-    public Cache<UnitOfWork.Identity, Try<TransformationResult>> getIdentityCache() {
-        return new ManualEvictionInMemoryCache<>();
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformExecutionResultSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformExecutionResultSerializerTest.groovy
new file mode 100644
index 0000000..41b118d
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformExecutionResultSerializerTest.groovy
@@ -0,0 +1,145 @@
+/*
+ * 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.api.internal.artifacts.transform
+
+import com.google.common.collect.ImmutableList
+import org.gradle.test.fixtures.file.CleanupTestDirectory
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.junit.Rule
+import spock.lang.Specification
+
+@CleanupTestDirectory
+class TransformExecutionResultSerializerTest extends Specification {
+    @Rule
+    final TestNameTestDirectoryProvider temporaryFolder = TestNameTestDirectoryProvider.newInstance(getClass())
+
+    def inputArtifact = file("inputArtifact").createDir()
+    def outputDir = file("outputDir")
+    def resultFile = file("results.txt")
+    def serializer = new TransformExecutionResultSerializer(outputDir)
+
+    def "reads and writes transformation results"() {
+        expect:
+        assertCanWriteAndReadResult(
+            inputArtifact.file("inside"),
+            inputArtifact,
+            outputDir.file("first"),
+            outputDir.file("second"),
+            outputDir
+        )
+    }
+
+    def "reads and writes output only transformation results"() {
+        expect:
+        assertCanWriteAndReadResult(
+            outputDir.file("first"),
+            outputDir.file("second"),
+            outputDir
+        )
+    }
+
+    def "reads and writes input only transformation results"() {
+        expect:
+        assertCanWriteAndReadResult(
+            inputArtifact.file("inside"),
+            inputArtifact,
+            inputArtifact
+        )
+    }
+
+    def "resolves files in input artifact relative to input artifact"() {
+        def newInputArtifact = file("newInputArtifact").createDir()
+
+        ImmutableList<File> resultFiles = ImmutableList.of(
+            inputArtifact.file("inside"),
+            inputArtifact,
+            outputDir,
+            inputArtifact
+        )
+        ImmutableList<File> resultResolvedForNewInputArtifact = ImmutableList.of(
+            newInputArtifact.file("inside"),
+            newInputArtifact,
+            outputDir,
+            newInputArtifact
+        )
+
+        when:
+        def initialResults = buildTransformExecutionResult(resultFiles)
+        serializer.writeToFile(resultFile, initialResults)
+        then:
+        resultFile.exists()
+        initialResults.resolveOutputsForInputArtifact(inputArtifact) == resultFiles
+        initialResults.resolveOutputsForInputArtifact(newInputArtifact) == resultResolvedForNewInputArtifact
+
+        when:
+        def loadedResults = serializer.readResultsFile(resultFile)
+        then:
+        loadedResults.resolveOutputsForInputArtifact(inputArtifact) == resultFiles
+        loadedResults.resolveOutputsForInputArtifact(newInputArtifact) == resultResolvedForNewInputArtifact
+    }
+
+    def "loads files in output directory relative to output directory"() {
+        def newOutputDir = file("newOutputDir").createDir()
+
+        ImmutableList<File> resultFiles = ImmutableList.of(
+            inputArtifact,
+            outputDir,
+            outputDir.file("output.txt")
+        )
+        ImmutableList<File> resultInNewOutputDir = ImmutableList.of(
+            inputArtifact,
+            newOutputDir,
+            newOutputDir.file("output.txt")
+        )
+
+        when:
+        def initialResults = buildTransformExecutionResult(resultFiles)
+        serializer.writeToFile(resultFile, initialResults)
+        then:
+        resultFile.exists()
+        initialResults.resolveOutputsForInputArtifact(inputArtifact) == resultFiles
+
+        when:
+        def serializerWithNewOutputDir = new TransformExecutionResultSerializer(newOutputDir)
+        def loadedResults = serializerWithNewOutputDir.readResultsFile(resultFile)
+        then:
+        loadedResults.resolveOutputsForInputArtifact(inputArtifact) == resultInNewOutputDir
+    }
+
+    private void assertCanWriteAndReadResult(File... files) {
+        ImmutableList<File> resultFiles = ImmutableList.<File>builder().add(files).build()
+        def initialResults = buildTransformExecutionResult(resultFiles)
+        assert initialResults.resolveOutputsForInputArtifact(inputArtifact) == resultFiles
+
+        serializer.writeToFile(resultFile, initialResults)
+        assert resultFile.exists()
+        assert serializer.readResultsFile(resultFile).resolveOutputsForInputArtifact(inputArtifact) == resultFiles
+    }
+
+    private TransformExecutionResult buildTransformExecutionResult(Collection<File> files) {
+        def builder = TransformExecutionResult.builderFor(inputArtifact, outputDir)
+        for (File file in files) {
+            builder.addOutput(file) {}
+        }
+        return builder.build()
+    }
+
+    TestFile file(String path) {
+        temporaryFolder.file(path)
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformationMatchingSpec.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformationMatchingSpec.groovy
deleted file mode 100644
index 4c094e6..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformationMatchingSpec.groovy
+++ /dev/null
@@ -1,101 +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.api.internal.artifacts.transform
-
-import org.gradle.api.internal.DomainObjectContext
-import org.gradle.internal.execution.InputFingerprinter
-import spock.lang.Specification
-
-class TransformationMatchingSpec extends Specification {
-
-    def "different TransformationStep does not contain each other"() {
-        given:
-        def step1 = step()
-        def step2 = step()
-
-        expect:
-        !step1.endsWith(step2)
-        !step2.endsWith(step1)
-    }
-
-    def "TransformationStep contains itself"() {
-        given:
-        def step = step()
-
-        expect:
-        step.endsWith(step)
-    }
-
-    def "chain contains its final step"() {
-        given:
-        def step1 = step()
-        def step2 = step()
-        def chain = new TransformationChain(step1, step2)
-
-        expect:
-        chain.endsWith(step2)
-        !chain.endsWith(step1)
-        !step1.endsWith(chain)
-        !step2.endsWith(chain)
-
-    }
-
-    def "chain contains itself"() {
-        given:
-        def step1 = step()
-        def step2 = step()
-        def chain = new TransformationChain(step1, step2)
-
-        expect:
-        chain.endsWith(chain)
-    }
-
-    def "longer chain contains shorter chain"() {
-        given:
-        def step1 = step()
-        def step2 = step()
-        def step3 = step()
-        def subChain = new TransformationChain(step2, step3)
-        def longChain = new TransformationChain(new TransformationChain(step1, step2), step3)
-
-        expect:
-        longChain.endsWith(subChain)
-        !subChain.endsWith(longChain)
-    }
-
-    def "different chains do not contain each other"() {
-        given:
-        def step1 = step()
-        def step2 = step()
-        def step3 = step()
-        def chain1 = new TransformationChain(step2, step3)
-        def chain2 = new TransformationChain(step1, step2)
-        def chain3 = new TransformationChain(step1, step3)
-
-        expect:
-        !chain1.endsWith(chain2)
-        !chain2.endsWith(chain1)
-        !chain3.endsWith(chain2)
-        !chain3.endsWith(chain1)
-        !chain2.endsWith(chain3)
-        !chain1.endsWith(chain3)
-    }
-
-    private TransformationStep step() {
-        new TransformationStep(Mock(Transformer), Mock(TransformerInvocationFactory), Mock(DomainObjectContext), Mock(InputFingerprinter))
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformationResultSerializerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformationResultSerializerTest.groovy
deleted file mode 100644
index e130526..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformationResultSerializerTest.groovy
+++ /dev/null
@@ -1,145 +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.api.internal.artifacts.transform
-
-import com.google.common.collect.ImmutableList
-import org.gradle.test.fixtures.file.CleanupTestDirectory
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-@CleanupTestDirectory
-class TransformationResultSerializerTest extends Specification {
-    @Rule
-    final TestNameTestDirectoryProvider temporaryFolder = TestNameTestDirectoryProvider.newInstance(getClass())
-
-    def inputArtifact = file("inputArtifact").createDir()
-    def outputDir = file("outputDir")
-    def resultFile = file("results.txt")
-    def serializer = new TransformationResultSerializer(outputDir)
-
-    def "reads and writes transformation results"() {
-        expect:
-        assertCanWriteAndReadResult(
-            inputArtifact.file("inside"),
-            inputArtifact,
-            outputDir.file("first"),
-            outputDir.file("second"),
-            outputDir
-        )
-    }
-
-    def "reads and writes output only transformation results"() {
-        expect:
-        assertCanWriteAndReadResult(
-            outputDir.file("first"),
-            outputDir.file("second"),
-            outputDir
-        )
-    }
-
-    def "reads and writes input only transformation results"() {
-        expect:
-        assertCanWriteAndReadResult(
-            inputArtifact.file("inside"),
-            inputArtifact,
-            inputArtifact
-        )
-    }
-
-    def "resolves files in input artifact relative to input artifact"() {
-        def newInputArtifact = file("newInputArtifact").createDir()
-
-        ImmutableList<File> resultFiles = ImmutableList.of(
-            inputArtifact.file("inside"),
-            inputArtifact,
-            outputDir,
-            inputArtifact
-        )
-        ImmutableList<File> resultResolvedForNewInputArtifact = ImmutableList.of(
-            newInputArtifact.file("inside"),
-            newInputArtifact,
-            outputDir,
-            newInputArtifact
-        )
-
-        when:
-        def initialResults = buildTransformationResult(resultFiles)
-        serializer.writeToFile(resultFile, initialResults)
-        then:
-        resultFile.exists()
-        initialResults.resolveOutputsForInputArtifact(inputArtifact) == resultFiles
-        initialResults.resolveOutputsForInputArtifact(newInputArtifact) == resultResolvedForNewInputArtifact
-
-        when:
-        def loadedResults = serializer.readResultsFile(resultFile)
-        then:
-        loadedResults.resolveOutputsForInputArtifact(inputArtifact) == resultFiles
-        loadedResults.resolveOutputsForInputArtifact(newInputArtifact) == resultResolvedForNewInputArtifact
-    }
-
-    def "loads files in output directory relative to output directory"() {
-        def newOutputDir = file("newOutputDir").createDir()
-
-        ImmutableList<File> resultFiles = ImmutableList.of(
-            inputArtifact,
-            outputDir,
-            outputDir.file("output.txt")
-        )
-        ImmutableList<File> resultInNewOutputDir = ImmutableList.of(
-            inputArtifact,
-            newOutputDir,
-            newOutputDir.file("output.txt")
-        )
-
-        when:
-        def initialResults = buildTransformationResult(resultFiles)
-        serializer.writeToFile(resultFile, initialResults)
-        then:
-        resultFile.exists()
-        initialResults.resolveOutputsForInputArtifact(inputArtifact) == resultFiles
-
-        when:
-        def serializerWithNewOutputDir = new TransformationResultSerializer(newOutputDir)
-        def loadedResults = serializerWithNewOutputDir.readResultsFile(resultFile)
-        then:
-        loadedResults.resolveOutputsForInputArtifact(inputArtifact) == resultInNewOutputDir
-    }
-
-    private void assertCanWriteAndReadResult(File... files) {
-        ImmutableList<File> resultFiles = ImmutableList.<File>builder().add(files).build()
-        def initialResults = buildTransformationResult(resultFiles)
-        assert initialResults.resolveOutputsForInputArtifact(inputArtifact) == resultFiles
-
-        serializer.writeToFile(resultFile, initialResults)
-        assert resultFile.exists()
-        assert serializer.readResultsFile(resultFile).resolveOutputsForInputArtifact(inputArtifact) == resultFiles
-    }
-
-    private TransformationResult buildTransformationResult(Collection<File> files) {
-        def builder = TransformationResult.builderFor(inputArtifact, outputDir)
-        for (File file in files) {
-            builder.addOutput(file) {}
-        }
-        return builder.build()
-    }
-
-    TestFile file(String path) {
-        temporaryFolder.file(path)
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListenerTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListenerTest.groovy
index 0fc2089..89321dc 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListenerTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListenerTest.groovy
@@ -30,12 +30,12 @@
 import spock.lang.Specification
 
 class TransformingAsyncArtifactListenerTest extends Specification {
-    def transformation = Mock(TransformationStep)
+    def transformStep = Mock(TransformStep)
     def targetAttributes = Mock(ImmutableAttributes)
     def result = ImmutableList.<ResolvedArtifactSet.Artifacts>builder()
-    def invocation = Mock(Deferrable<TransformationSubject>)
+    def invocation = Mock(Deferrable<TransformStepSubject>)
     def operationQueue = Mock(BuildOperationQueue)
-    def listener = new TransformingAsyncArtifactListener([new BoundTransformationStep(transformation, Stub(TransformUpstreamDependencies))], targetAttributes, [], result)
+    def listener = new TransformingAsyncArtifactListener([new BoundTransformStep(transformStep, Stub(TransformUpstreamDependencies))], targetAttributes, [], result)
     def file = new File("foo")
     def artifactFile = new File("foo-artifact")
     def artifactId = Stub(ComponentArtifactIdentifier)
@@ -64,7 +64,7 @@
         artifacts[0].startFinalization(operationQueue, true)
 
         then:
-        1 * transformation.createInvocation(_, _, _) >> invocation
+        1 * transformStep.createInvocation(_, _, _) >> invocation
         1 * invocation.getCompleted() >> Optional.empty()
         1 * operationQueue.add(_ as BuildOperation)
     }
@@ -83,8 +83,8 @@
         artifacts[0].startFinalization(operationQueue, true)
 
         then:
-        1 * transformation.createInvocation({ it.files == [this.artifactFile] }, _ as TransformUpstreamDependencies, _) >> invocation
-        2 * invocation.getCompleted() >> Optional.of(Try.successful(TransformationSubject.initial(artifact)))
+        1 * transformStep.createInvocation({ it.files == [this.artifactFile] }, _ as TransformUpstreamDependencies, _) >> invocation
+        2 * invocation.getCompleted() >> Optional.of(Try.successful(TransformStepSubject.initial(artifact)))
         0 * operationQueue._
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformInvocationTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformInvocationTest.groovy
index 70f8582..01cb5ac 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformInvocationTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/integtests/resolve/transform/ArtifactTransformInvocationTest.groovy
@@ -26,9 +26,9 @@
 import org.gradle.api.internal.artifacts.VariantTransformRegistry
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
 import org.gradle.api.internal.artifacts.transform.DefaultTransformUpstreamDependenciesResolver
-import org.gradle.api.internal.artifacts.transform.TransformationStep
-import org.gradle.api.internal.artifacts.transform.TransformationSubject
-import org.gradle.api.internal.artifacts.transform.TransformerInvocationFactory
+import org.gradle.api.internal.artifacts.transform.TransformInvocationFactory
+import org.gradle.api.internal.artifacts.transform.TransformStep
+import org.gradle.api.internal.artifacts.transform.TransformStepSubject
 import org.gradle.api.provider.Provider
 import org.gradle.api.tasks.Classpath
 import org.gradle.internal.Try
@@ -96,14 +96,14 @@
         selectedFile2.text = "Hello"
 
         when:
-        def transformationResult1 = invokeTransform(transform, inputArtifact1).get()
-        def transformationResult2 = invokeTransform(transform, inputArtifact2).get()
+        def transformResult1 = invokeTransform(transform, inputArtifact1).get()
+        def transformResult2 = invokeTransform(transform, inputArtifact2).get()
         then:
-        transformationResult1.size() == 4
-        transformationResult2.size() == 4
-        transformationResult1[0, 2] == [selectedFile1, inputArtifact1]
-        transformationResult2[0, 2] == [selectedFile2, inputArtifact2]
-        transformationResult1[1, 3] == transformationResult2[1, 3]
+        transformResult1.size() == 4
+        transformResult2.size() == 4
+        transformResult1[0, 2] == [selectedFile1, inputArtifact1]
+        transformResult2[0, 2] == [selectedFile2, inputArtifact2]
+        transformResult1[1, 3] == transformResult2[1, 3]
         INVOCATION_COUNT.get() == 1
     }
 
@@ -111,28 +111,28 @@
         new TestFile(project.file(path))
     }
 
-    private <T extends TransformParameters> TransformationStep registerTransform(Class<? extends TransformAction<T>> actionType) {
+    private <T extends TransformParameters> TransformStep registerTransform(Class<? extends TransformAction<T>> actionType) {
         def variantTransformRegistry = project.services.get(VariantTransformRegistry)
-        int currentRegisteredTransforms = variantTransformRegistry.transforms.size()
+        int currentRegisteredTransforms = variantTransformRegistry.registrations.size()
         project.dependencies.registerTransform(actionType) {
             it.from.attribute(artifactType, 'jar')
             it.to.attribute(artifactType, 'transformed')
         }
-        return variantTransformRegistry.transforms[currentRegisteredTransforms].transformationStep
+        return variantTransformRegistry.registrations[currentRegisteredTransforms].transformStep
     }
 
-    private Try<ImmutableList<File>> invokeTransform(TransformationStep transform, File inputArtifact) {
+    private Try<ImmutableList<File>> invokeTransform(TransformStep transform, File inputArtifact) {
         transform.isolateParametersIfNotAlready()
-        def invocationFactory = project.services.get(TransformerInvocationFactory)
+        def invocationFactory = project.services.get(TransformInvocationFactory)
         def inputFingerprinter = project.services.get(InputFingerprinter)
         def artifact = Stub(ResolvableArtifact) {
             getId() >> new OpaqueComponentArtifactIdentifier(inputArtifact)
         }
         def invocation = invocationFactory.createInvocation(
-            transform.getTransformer(),
+            transform.getTransform(),
             inputArtifact,
             DefaultTransformUpstreamDependenciesResolver.NO_RESULT,
-            TransformationSubject.initial(artifact),
+            TransformStepSubject.initial(artifact),
             inputFingerprinter
         )
         invocation.completeAndGet()
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractDependencyMetadataRulesTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractDependencyMetadataRulesTest.groovy
index a87404e..f29e4d8 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractDependencyMetadataRulesTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractDependencyMetadataRulesTest.groovy
@@ -35,8 +35,8 @@
 import org.gradle.internal.component.external.model.ivy.IvyDependencyDescriptor
 import org.gradle.internal.component.external.model.maven.MavenDependencyDescriptor
 import org.gradle.internal.component.external.model.maven.MavenDependencyType
-import org.gradle.internal.component.model.ComponentGraphResolveState
 import org.gradle.internal.component.model.LocalComponentDependencyMetadata
+import org.gradle.internal.component.model.VariantGraphResolveMetadata
 import org.gradle.util.AttributeTestUtil
 import org.gradle.util.SnapshotTestUtil
 import org.gradle.util.TestUtil
@@ -90,6 +90,7 @@
         }
         ivyMetadataFactory.create(componentIdentifier, dependencies)
     }
+
     private mavenComponentMetadata(String[] deps) {
         def dependencies = deps.collect { name ->
             MavenDependencyType type = addAllDependenciesAsConstraints() ? MavenDependencyType.OPTIONAL_DEPENDENCY : MavenDependencyType.DEPENDENCY
@@ -97,6 +98,7 @@
         }
         mavenMetadataFactory.create(componentIdentifier, dependencies)
     }
+
     private gradleComponentMetadata(String[] deps) {
         def metadata = mavenMetadataFactory.create(componentIdentifier, [])
         //gradle metadata is distinguished from maven POM metadata by explicitly defining variants
@@ -269,18 +271,17 @@
         "gradle"     | gradleComponentMetadata("toRemove")
     }
 
-    def selectTargetConfigurationMetadata(MutableModuleComponentResolveMetadata targetComponent) {
-        selectTargetConfigurationMetadata(targetComponent.asImmutable())
+    VariantGraphResolveMetadata selectTargetConfigurationMetadata(MutableModuleComponentResolveMetadata targetComponent) {
+        return selectTargetConfigurationMetadata(targetComponent.asImmutable())
     }
 
-    def selectTargetConfigurationMetadata(ModuleComponentResolveMetadata immutable) {
+    VariantGraphResolveMetadata selectTargetConfigurationMetadata(ModuleComponentResolveMetadata immutable) {
         def componentIdentifier = DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org.test", "consumer"), "1.0")
         def consumerIdentifier = DefaultModuleVersionIdentifier.newId(componentIdentifier)
         def componentSelector = newSelector(consumerIdentifier.module, new DefaultMutableVersionConstraint(consumerIdentifier.version))
         def consumer = new LocalComponentDependencyMetadata(componentIdentifier, componentSelector, "default", attributes, ImmutableAttributes.EMPTY, null, [] as List, [], false, false, true, false, false, null)
-        def state = Stub(ComponentGraphResolveState)
-        state.metadata >> immutable
+        def state = DependencyManagementTestUtil.modelGraphResolveFactory().stateFor(immutable)
 
-        consumer.selectVariants(attributes, state, schema, [] as Set).variants[0]
+        return consumer.selectVariants(attributes, state, schema, [] as Set).variants[0].metadata
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractMutableModuleComponentResolveMetadataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractMutableModuleComponentResolveMetadataTest.groovy
index 691ef9b..793636d 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractMutableModuleComponentResolveMetadataTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/AbstractMutableModuleComponentResolveMetadataTest.groovy
@@ -282,7 +282,8 @@
 
         expect:
         def immutable = metadata.asImmutable()
-        def variantsForTraversal = immutable.getVariantsForGraphTraversal().orNull()
+        def variantsForTraversal = immutable.getVariantsForGraphTraversal().orElse(null)
+        variantsForTraversal != null
         variantsForTraversal.size() == 2
         variantsForTraversal[0].name == 'api'
         variantsForTraversal[0].dependencies.size() == 1
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetadataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetadataTest.groovy
index c332412..d397203 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetadataTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/DefaultMavenModuleResolveMetadataTest.groovy
@@ -128,7 +128,7 @@
 
         when:
         def immutableMetadata = metadata.asImmutable().withDerivationStrategy(JavaEcosystemVariantDerivationStrategy.getInstance())
-        def variantsForGraphTraversal = immutableMetadata.getVariantsForGraphTraversal().orNull()
+        def variantsForGraphTraversal = immutableMetadata.getVariantsForGraphTraversal().orElse(null)
         def compileConf = immutableMetadata.getConfiguration("compile")
         def runtimeConf = immutableMetadata.getConfiguration("runtime")
 
@@ -136,6 +136,7 @@
         assertHasOnlyStatusAttribute(compileConf.attributes)
         assertHasOnlyStatusAttribute(runtimeConf.attributes)
 
+        variantsForGraphTraversal != null
         variantsForGraphTraversal.size() == 8
         variantsForGraphTraversal[0].name == "compile"
         variantsForGraphTraversal[0].attributes.getAttribute(stringUsageAttribute) == "java-api"
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/ExternalDependencyDescriptorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/ExternalDependencyDescriptorTest.groovy
index 39714a6..6abdb0d 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/ExternalDependencyDescriptorTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/ExternalDependencyDescriptorTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.internal.component.external.model
 
+import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableSet
 import org.gradle.api.artifacts.VersionConstraint
 import org.gradle.api.artifacts.component.ComponentSelector
@@ -23,15 +24,18 @@
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint
-import org.gradle.api.internal.attributes.AttributesSchemaInternal
+import org.gradle.internal.component.model.ComponentArtifactMetadata
+import org.gradle.internal.component.model.ComponentGraphResolveState
+import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata
+import org.gradle.internal.component.model.ConfigurationGraphResolveState
 import org.gradle.internal.component.model.ConfigurationMetadata
+import org.gradle.internal.component.model.VariantArtifactGraphResolveMetadata
+import org.gradle.internal.component.model.VariantGraphResolveState
 import spock.lang.Specification
 
 import static org.gradle.internal.component.external.model.DefaultModuleComponentSelector.newSelector
 
 abstract class ExternalDependencyDescriptorTest extends Specification {
-    def attributesSchema = Stub(AttributesSchemaInternal)
-
     def requested = newSelector(DefaultModuleIdentifier.newId("org", "module"), v("1.2+"))
     def id = DefaultModuleVersionIdentifier.newId("org", "module", "1.2+")
 
@@ -69,9 +73,39 @@
         componentSelector.version == '1.2+'
     }
 
-    def configuration(String name, String... parents) {
+    ConfigurationMetadata configuration(String name, String... parents) {
         def config = Stub(ConfigurationMetadata)
         config.hierarchy >> ImmutableSet.copyOf(([name] as Set) + (parents as Set))
         return config
     }
+
+    VariantGraphResolveState configuration(ComponentGraphResolveState component, String name) {
+        return configurationWithHierarchy(component, name, ImmutableSet.of(name))
+    }
+
+    VariantGraphResolveState configurationWithArtifacts(ComponentGraphResolveState component, String name) {
+        def artifacts = Stub(VariantArtifactGraphResolveMetadata)
+        artifacts.artifacts >> ImmutableList.of(Stub(ComponentArtifactMetadata))
+        def variant = configuration(component, name)
+        variant.resolveArtifacts() >> artifacts
+        return variant
+    }
+
+    VariantGraphResolveState configurationWithHierarchy(ComponentGraphResolveState component, String name, Set<String> hierarchy) {
+        def metadata = Stub(ConfigurationGraphResolveMetadata)
+        metadata.visible >> true
+        metadata.hierarchy >> hierarchy
+
+        def variant = Stub(VariantGraphResolveState)
+        variant.toString() >> name
+        variant.name >> name
+
+        def configuration = Stub(ConfigurationGraphResolveState)
+        component.getConfiguration(name) >> configuration
+        configuration.name >> name
+        configuration.toString() >> name
+        configuration.asVariant() >> variant
+        configuration.metadata >> metadata
+        return variant
+    }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/IvyDependencyDescriptorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/IvyDependencyDescriptorTest.groovy
index c6bfece..c0ee412 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/IvyDependencyDescriptorTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/IvyDependencyDescriptorTest.groovy
@@ -32,11 +32,13 @@
 import org.gradle.internal.component.model.ComponentGraphResolveMetadata
 import org.gradle.internal.component.model.ComponentGraphResolveState
 import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata
+import org.gradle.internal.component.model.ConfigurationGraphResolveState
 import org.gradle.internal.component.model.ConfigurationMetadata
 import org.gradle.internal.component.model.ConfigurationNotFoundException
 import org.gradle.internal.component.model.DefaultIvyArtifactName
 import org.gradle.internal.component.model.Exclude
 import org.gradle.internal.component.model.ModuleConfigurationMetadata
+import org.gradle.internal.component.model.VariantGraphResolveState
 
 import static com.google.common.collect.ImmutableList.copyOf
 
@@ -157,26 +159,22 @@
 
     def "selects no configurations when no configuration mappings provided"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         fromConfig.name >> "from"
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, ImmutableListMultimap.of(), [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants.empty
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants.empty
     }
 
     def "selects configurations from target component that match configuration mappings"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
-        def toConfig2 = Stub(ConfigurationGraphResolveMetadata)
+        def toConfig1 = configuration(toComponent, "to-1")
+        def toConfig2 = configuration(toComponent, "to-2")
         fromConfig.hierarchy >> ImmutableSet.of("from")
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("to-1") >> toConfig1
-        toComponent.getConfiguration("to-2") >> toConfig2
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put("from", "to-1")
@@ -185,20 +183,16 @@
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1, toConfig2] // verify order as well
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1, toConfig2] // verify order as well
     }
 
     def "selects matching configurations for super-configurations"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ModuleConfigurationMetadata)
-        def toConfig2 = Stub(ModuleConfigurationMetadata)
+        def toConfig1 = configuration(toComponent, "to-1")
+        def toConfig2 = configuration(toComponent, "to-2")
         fromConfig.hierarchy >> ImmutableSet.of("from", "super")
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("to-1") >> toConfig1
-        toComponent.getConfiguration("to-2") >> toConfig2
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put("from", "to-1")
@@ -207,22 +201,18 @@
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1, toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1, toConfig2]
     }
 
     def "configuration mapping can use wildcard on LHS"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         def fromConfig2 = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
-        def toConfig2 = Stub(ConfigurationGraphResolveMetadata)
+        def toConfig1 = configuration(toComponent, "to-1")
+        def toConfig2 = configuration(toComponent, "to-2")
         fromConfig.hierarchy >> ImmutableSet.of("from")
         fromConfig2.hierarchy >> ImmutableSet.of("other")
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("to-1") >> toConfig1
-        toComponent.getConfiguration("to-2") >> toConfig2
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put("from", "to-1")
@@ -230,23 +220,21 @@
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1, toConfig2]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toState).variants == [toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1, toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toComponent).variants == [toConfig2]
     }
 
     def "configuration mapping can use wildcard on RHS to select all public configurations"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
+        def toComponentMetadata = Stub(ComponentGraphResolveMetadata)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         fromConfig.hierarchy >> ImmutableSet.of("from")
-        def toConfig1 = config('to-1', true)
-        def toConfig2 = config('to-2', true)
+        def toConfig1 = configuration(toComponent, "to-1")
+        def toConfig2 = configuration(toComponent, "to-2")
         def toConfig3 = config('to-3', false)
-        toState.metadata >> toComponent
-        toComponent.getConfigurationNames() >> ["to-1", "to-2", "to-3"]
-        toComponent.getConfiguration("to-1") >> toConfig1
-        toComponent.getConfiguration("to-2") >> toConfig2
+        toComponent.metadata >> toComponentMetadata
+        toComponentMetadata.getConfigurationNames() >> ["to-1", "to-2", "to-3"]
         toComponent.getConfiguration("to-3") >> toConfig3
 
         def configMapping = LinkedHashMultimap.create()
@@ -256,32 +244,32 @@
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1, toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1, toConfig2]
     }
 
-    private ConfigurationGraphResolveMetadata config(name, visible) {
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
-        toConfig1.visible >> visible
-        toConfig1.name >> name
-        toConfig1.getHierarchy() >> ImmutableSet.of(name)
-        toConfig1
+    private ConfigurationGraphResolveState config(name, visible) {
+        def toConfig = Stub(ConfigurationGraphResolveMetadata)
+        toConfig.isVisible() >> visible
+        toConfig.name >> name
+        toConfig.getHierarchy() >> ImmutableSet.of(name)
+        def variant = Stub(VariantGraphResolveState)
+        def toState = Stub(ConfigurationGraphResolveState)
+        toState.metadata >> toConfig
+        toState.asVariant() >> { throw new RuntimeException() }
+        return toState
     }
 
     def "configuration mapping can use all-except-wildcard on LHS"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         def fromConfig2 = Stub(ModuleConfigurationMetadata)
         def fromConfig3 = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
-        def toConfig2 = Stub(ConfigurationGraphResolveMetadata)
+        def toConfig1 = configuration(toComponent, "to-1")
+        def toConfig2 = configuration(toComponent, "to-2")
         fromConfig.hierarchy >> ImmutableSet.of("from")
         fromConfig2.hierarchy >> ImmutableSet.of("child", "from")
         fromConfig3.hierarchy >> ImmutableSet.of("other")
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("to-1") >> toConfig1
-        toComponent.getConfiguration("to-2") >> toConfig2
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put("*", "to-2")
@@ -290,28 +278,23 @@
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toState).variants == [toConfig1]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig3, toState).variants == [toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toComponent).variants == [toConfig1]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig3, toComponent).variants == [toConfig2]
     }
 
     def "configuration mapping can include fallback on LHS"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         def fromConfig2 = Stub(ModuleConfigurationMetadata)
         def fromConfig3 = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
-        def toConfig2 = Stub(ConfigurationGraphResolveMetadata)
-        def toConfig3 = Stub(ConfigurationGraphResolveMetadata)
+        def toConfig1 = configuration(toComponent, "to-1")
+        def toConfig2 = configuration(toComponent, "to-2")
+        def toConfig3 = configuration(toComponent, "to-3")
         fromConfig.hierarchy >> ImmutableSet.of("from")
         fromConfig2.hierarchy >> ImmutableSet.of("child", "from")
         fromConfig3.hierarchy >> ImmutableSet.of("other")
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("to-1") >> toConfig1
-        toComponent.getConfiguration("to-2") >> toConfig2
-        toComponent.getConfiguration("to-3") >> toConfig3
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put("from", "to-1")
@@ -320,30 +303,28 @@
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1, toConfig3]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toState).variants == [toConfig1, toConfig3]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig3, toState).variants == [toConfig2, toConfig3]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1, toConfig3]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toComponent).variants == [toConfig1, toConfig3]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig3, toComponent).variants == [toConfig2, toConfig3]
     }
 
     def "configuration mapping can include fallback on RHS"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
+        def toComponentMetadata = Stub(ComponentGraphResolveMetadata)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         def fromConfig2 = Stub(ModuleConfigurationMetadata)
         def fromConfig3 = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
-        def toConfig2 = Stub(ConfigurationGraphResolveMetadata)
+        def toConfig1 = configuration(toComponent, "to-1")
+        def toConfig2 = configuration(toComponent, "to-2")
         fromConfig.hierarchy >> ImmutableSet.of("from")
         fromConfig2.hierarchy >> ImmutableSet.of("other")
         fromConfig3.hierarchy >> ImmutableSet.of("other2")
         toConfig1.visible >> true
         toConfig2.visible >> true
-        toState.metadata >> toComponent
-        toComponent.getConfigurationNames() >> ["to-1", "to-2"]
-        toComponent.getConfiguration("unknown") >> null
-        toComponent.getConfiguration("to-1") >> toConfig1
-        toComponent.getConfiguration("to-2") >> toConfig2
+        toComponent.metadata >> toComponentMetadata
+        toComponentMetadata.getConfigurationNames() >> ["to-1", "to-2"]
+        toComponent.getConfiguration(_) >> null
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put("from", "unknown(*)")
@@ -352,80 +333,69 @@
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1, toConfig2]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toState).variants == [toConfig1]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig3, toState).variants == [toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1, toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toComponent).variants == [toConfig1]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig3, toComponent).variants == [toConfig2]
     }
 
     def "configuration mapping can include self placeholder on RHS"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         def fromConfig2 = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
+        def toConfig1 = configuration(toComponent, "a")
         fromConfig.hierarchy >> ImmutableSet.of("a")
         fromConfig2.hierarchy >> ImmutableSet.of("other", "a")
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("a") >> toConfig1
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put("a", "@")
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toState).variants == [toConfig1]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toComponent).variants == [toConfig1]
     }
 
     def "configuration mapping can include this placeholder on RHS"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         def fromConfig2 = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
-        def toConfig2 = Stub(ConfigurationGraphResolveMetadata)
+        def toConfig1 = configuration(toComponent, "a")
+        def toConfig2 = configuration(toComponent, "b")
         fromConfig.name >> "a"
         fromConfig2.name >> "b"
         fromConfig.hierarchy >> ImmutableSet.of("a")
         fromConfig2.hierarchy >> ImmutableSet.of("b", "a")
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("a") >> toConfig1
-        toComponent.getConfiguration("b") >> toConfig2
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put("a", "#")
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toState).variants == [toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toComponent).variants == [toConfig2]
     }
 
     def "configuration mapping can include wildcard on LHS and placeholder on RHS"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromConfig = Stub(ModuleConfigurationMetadata)
         def fromConfig2 = Stub(ModuleConfigurationMetadata)
-        def toConfig1 = Stub(ConfigurationGraphResolveMetadata)
-        def toConfig2 = Stub(ConfigurationGraphResolveMetadata)
+        def toConfig1 = configuration(toComponent, "a")
+        def toConfig2 = configuration(toComponent, "b")
         fromConfig.name >> "a"
         fromConfig2.name >> "b"
         fromConfig.hierarchy >> ImmutableSet.of("a")
         fromConfig2.hierarchy >> ImmutableSet.of("b", "a")
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("a") >> toConfig1
-        toComponent.getConfiguration("b") >> toConfig2
 
         def configMapping = LinkedHashMultimap.create()
         configMapping.put(lhs, rhs)
 
         expect:
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState).variants == [toConfig1]
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toState).variants == [toConfig2]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent).variants == [toConfig1]
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig2, toComponent).variants == [toConfig2]
 
         where:
         // these all map to the same thing
@@ -443,9 +413,7 @@
         def toId = Stub(ComponentIdentifier) {
             getDisplayName() >> "thing b"
         }
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
-        toState.metadata >> toComponent
+        def toComponent = Stub(ComponentGraphResolveState)
         toComponent.id >> toId
         def fromConfig = Stub(ModuleConfigurationMetadata)
         fromConfig.hierarchy >> ImmutableSet.of("from")
@@ -458,7 +426,7 @@
         def metadata = new IvyDependencyDescriptor(requested, "12", true, true, false, configMapping, [], [])
 
         when:
-        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toState)
+        metadata.selectLegacyConfigurations(fromComponent, fromConfig, toComponent)
 
         then:
         ConfigurationNotFoundException e = thrown()
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/MavenDependencyDescriptorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/MavenDependencyDescriptorTest.groovy
index daf7db4..5d0d3d7 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/MavenDependencyDescriptorTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/MavenDependencyDescriptorTest.groovy
@@ -43,10 +43,7 @@
 import org.gradle.internal.component.external.descriptor.MavenScope
 import org.gradle.internal.component.external.model.maven.MavenDependencyDescriptor
 import org.gradle.internal.component.external.model.maven.MavenDependencyType
-import org.gradle.internal.component.model.ComponentArtifactMetadata
-import org.gradle.internal.component.model.ComponentGraphResolveMetadata
 import org.gradle.internal.component.model.ComponentGraphResolveState
-import org.gradle.internal.component.model.ConfigurationGraphResolveMetadata
 import org.gradle.internal.component.model.ConfigurationMetadata
 import org.gradle.internal.component.model.ConfigurationNotFoundException
 import org.gradle.internal.component.model.Exclude
@@ -88,170 +85,121 @@
 
     def "selects compile and master configurations from target when traversing from compile configuration"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromCompile = Stub(ModuleConfigurationMetadata)
-        def toCompile = Stub(ConfigurationGraphResolveMetadata)
-        def toMaster = Stub(ModuleConfigurationMetadata)
+        def toCompile = configuration(toComponent, "compile")
+        def toMaster = configurationWithArtifacts(toComponent, "master")
         fromCompile.name >> "compile"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("compile") >> toCompile
-        toComponent.getConfiguration("master") >> toMaster
-        toState.resolveArtifactsFor(toMaster) >> toMaster
-        toMaster.artifacts >> ImmutableList.of(ComponentArtifactMetadata)
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         expect:
-        dep.selectLegacyConfigurations(fromComponent, fromCompile, toState).variants == [toCompile, toMaster]
+        dep.selectLegacyConfigurations(fromComponent, fromCompile, toComponent).variants == [toCompile, toMaster]
     }
 
     def "selects compile, runtime and master configurations from target when traversing from other configuration"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromRuntime = Stub(ModuleConfigurationMetadata)
         def fromRuntime2 = Stub(ModuleConfigurationMetadata)
-        def toRuntime = Stub(ConfigurationGraphResolveMetadata)
-        def toCompile = Stub(ConfigurationGraphResolveMetadata)
-        def toMaster = Stub(ModuleConfigurationMetadata)
+        def toRuntime = configuration(toComponent, "runtime")
+        def toCompile = configuration(toComponent, "compile")
+        def toMaster = configurationWithArtifacts(toComponent, "master")
         fromRuntime.name >> "runtime"
         fromRuntime2.name >> "provided"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("runtime") >> toRuntime
-        toComponent.getConfiguration("compile") >> toCompile
-        toComponent.getConfiguration("master") >> toMaster
-        toState.resolveArtifactsFor(toMaster) >> toMaster
-        toMaster.artifacts >> ImmutableList.of(ComponentArtifactMetadata)
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         expect:
-        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toState).variants == [toRuntime, toCompile, toMaster]
-        dep.selectLegacyConfigurations(fromComponent, fromRuntime2, toState).variants == [toRuntime, toCompile, toMaster]
+        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toComponent).variants == [toRuntime, toCompile, toMaster]
+        dep.selectLegacyConfigurations(fromComponent, fromRuntime2, toComponent).variants == [toRuntime, toCompile, toMaster]
     }
 
     def "selects runtime and master configurations from target when traversing from other configuration and target's runtime extends compile"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromRuntime = Stub(ModuleConfigurationMetadata)
         def fromRuntime2 = Stub(ModuleConfigurationMetadata)
-        def toRuntime = Stub(ConfigurationGraphResolveMetadata)
-        def toMaster = Stub(ModuleConfigurationMetadata)
+        def toRuntime = configurationWithHierarchy(toComponent, "runtime", ImmutableSet.of("runtime", "compile"))
+        def toMaster = configurationWithArtifacts(toComponent, "master")
         fromRuntime.name >> "runtime"
         fromRuntime2.name >> "provided"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("runtime") >> toRuntime
-        toComponent.getConfiguration("master") >> toMaster
-        toRuntime.hierarchy >> ImmutableSet.of("runtime", "compile")
-        toState.resolveArtifactsFor(toMaster) >> toMaster
-        toMaster.artifacts >> ImmutableList.of(ComponentArtifactMetadata)
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         expect:
-        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toState).variants == [toRuntime, toMaster]
-        dep.selectLegacyConfigurations(fromComponent, fromRuntime2, toState).variants == [toRuntime, toMaster]
+        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toComponent).variants == [toRuntime, toMaster]
+        dep.selectLegacyConfigurations(fromComponent, fromRuntime2, toComponent).variants == [toRuntime, toMaster]
     }
 
     def "ignores missing master configuration"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromRuntime = Stub(ModuleConfigurationMetadata)
-        def toRuntime = Stub(ConfigurationGraphResolveMetadata)
+        def toRuntime = configurationWithHierarchy(toComponent, "runtime", ImmutableSet.of("compile", "runtime"))
         fromRuntime.name >> "runtime"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("runtime") >> toRuntime
-        toComponent.getConfiguration("master") >> null
-        toRuntime.hierarchy >> ImmutableSet.of("compile", "runtime")
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         expect:
-        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toState).variants == [toRuntime]
+        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toComponent).variants == [toRuntime]
     }
 
     def "ignores empty master configuration"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromRuntime = Stub(ModuleConfigurationMetadata)
-        def toRuntime = Stub(ConfigurationGraphResolveMetadata)
-        def toMaster = Stub(ConfigurationGraphResolveMetadata)
-        toRuntime.artifacts >> ImmutableList.of()
-        toMaster.artifacts >> ImmutableList.of()
+        def toRuntime = configurationWithHierarchy(toComponent, "runtime", ImmutableSet.of("compile", "runtime"))
+        configuration(toComponent, "master")
         fromRuntime.name >> "runtime"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("runtime") >> toRuntime
-        toComponent.getConfiguration("master") >> toMaster
-        toRuntime.hierarchy >> ImmutableSet.of("compile", "runtime")
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         expect:
-        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toState).variants == [toRuntime]
+        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toComponent).variants == [toRuntime]
     }
 
     def "falls back to default configuration when compile is not defined in target component"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromCompile = Stub(ModuleConfigurationMetadata)
-        def toDefault = Stub(ConfigurationGraphResolveMetadata)
-        def toMaster = Stub(ModuleConfigurationMetadata)
+        def toDefault = configuration(toComponent, "default")
+        def toMaster = configurationWithArtifacts(toComponent, "master")
         fromCompile.name >> "compile"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("compile") >> null
-        toComponent.getConfiguration("default") >> toDefault
-        toComponent.getConfiguration("master") >> toMaster
-        toState.resolveArtifactsFor(toMaster) >> toMaster
-        toMaster.artifacts >> ImmutableList.of(ComponentArtifactMetadata)
+        toComponent.getConfiguration(_) >> null
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         expect:
-        dep.selectLegacyConfigurations(fromComponent, fromCompile, toState).variants == [toDefault, toMaster]
+        dep.selectLegacyConfigurations(fromComponent, fromCompile, toComponent).variants == [toDefault, toMaster]
     }
 
     def "falls back to default configuration when runtime is not defined in target component"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromRuntime = Stub(ModuleConfigurationMetadata)
-        def toDefault = Stub(ConfigurationGraphResolveMetadata)
-        def toMaster = Stub(ModuleConfigurationMetadata)
+        def toDefault = configurationWithHierarchy(toComponent, "default", ImmutableSet.of("compile", "default"))
+        def toMaster = configurationWithArtifacts(toComponent, "master")
         fromRuntime.name >> "runtime"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("runtime") >> null
-        toComponent.getConfiguration("default") >> toDefault
-        toComponent.getConfiguration("master") >> toMaster
-        toState.resolveArtifactsFor(toMaster) >> toMaster
-        toDefault.hierarchy >> ImmutableSet.of("compile", "default")
-        toMaster.artifacts >> ImmutableList.of(ComponentArtifactMetadata)
+        toComponent.getConfiguration(_) >> null
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         expect:
-        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toState).variants == [toDefault, toMaster]
+        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toComponent).variants == [toDefault, toMaster]
     }
 
     def "fails when compile configuration is not defined in target component"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromCompile = Stub(ConfigurationMetadata)
         fromCompile.name >> "compile"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("compile") >> null
-        toComponent.getConfiguration("default") >> null
-        toComponent.getConfiguration("master") >> null
+        toComponent.getConfiguration(_) >> null
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         when:
-        dep.selectLegacyConfigurations(fromComponent, fromCompile, toState)
+        dep.selectLegacyConfigurations(fromComponent, fromCompile, toComponent)
 
         then:
         thrown(ConfigurationNotFoundException)
@@ -259,19 +207,15 @@
 
     def "fails when runtime configuration is not defined in target component"() {
         def fromComponent = Stub(ComponentIdentifier)
-        def toState = Stub(ComponentGraphResolveState)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
+        def toComponent = Stub(ComponentGraphResolveState)
         def fromRuntime = Stub(ConfigurationMetadata)
         fromRuntime.name >> "runtime"
-        toState.metadata >> toComponent
-        toComponent.getConfiguration("runtime") >> null
-        toComponent.getConfiguration("default") >> null
-        toComponent.getConfiguration("master") >> null
+        toComponent.getConfiguration(_) >> null
 
         def dep = mavenDependencyMetadata(MavenScope.Compile, Stub(ModuleComponentSelector), [])
 
         when:
-        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toState)
+        dep.selectLegacyConfigurations(fromComponent, fromRuntime, toComponent)
 
         then:
         thrown(ConfigurationNotFoundException)
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/VariantFilesMetadataRulesTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/VariantFilesMetadataRulesTest.groovy
index 1a80603..01ce6f4 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/VariantFilesMetadataRulesTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/external/model/VariantFilesMetadataRulesTest.groovy
@@ -24,10 +24,7 @@
 import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.artifacts.DependencyManagementTestUtil
-import org.gradle.api.internal.artifacts.JavaEcosystemSupport
 import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint
-import org.gradle.api.internal.artifacts.dsl.dependencies.GradlePluginVariantsSupport
-import org.gradle.api.internal.attributes.DefaultAttributesSchema
 import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.internal.component.external.descriptor.Artifact
 import org.gradle.internal.component.external.descriptor.Configuration
@@ -35,12 +32,9 @@
 import org.gradle.internal.component.external.model.ivy.IvyDependencyDescriptor
 import org.gradle.internal.component.external.model.maven.MavenDependencyDescriptor
 import org.gradle.internal.component.external.model.maven.MavenDependencyType
-import org.gradle.internal.component.model.ComponentGraphResolveState
+import org.gradle.internal.component.model.ConfigurationMetadata
 import org.gradle.internal.component.model.DefaultIvyArtifactName
-import org.gradle.internal.component.model.LocalComponentDependencyMetadata
 import org.gradle.util.AttributeTestUtil
-import org.gradle.util.SnapshotTestUtil
-import org.gradle.util.TestUtil
 import spock.lang.Shared
 import spock.lang.Specification
 
@@ -50,19 +44,10 @@
     @Shared versionIdentifier = new DefaultModuleVersionIdentifier("org.test", "producer", "1.0")
     @Shared componentIdentifier = DefaultModuleComponentIdentifier.newId(versionIdentifier)
     @Shared attributes = AttributeTestUtil.attributesFactory().of(Attribute.of("someAttribute", String), "someValue")
-    @Shared schema = createSchema()
     @Shared mavenMetadataFactory = DependencyManagementTestUtil.mavenMetadataFactory()
     @Shared ivyMetadataFactory = DependencyManagementTestUtil.ivyMetadataFactory()
     @Shared defaultVariant
 
-    private DefaultAttributesSchema createSchema() {
-        def schema = new DefaultAttributesSchema(TestUtil.instantiatorFactory(), SnapshotTestUtil.isolatableFactory())
-        DependencyManagementTestUtil.platformSupport().configureSchema(schema)
-        GradlePluginVariantsSupport.configureSchema(schema)
-        JavaEcosystemSupport.configureSchema(schema, TestUtil.objectFactory())
-        schema
-    }
-
     private ivyComponentMetadata(String[] deps) {
         def dependencies = deps.collect { name ->
             new IvyDependencyDescriptor(newSelector(DefaultModuleIdentifier.newId('org.test', name), '1.0'), ImmutableListMultimap.of('runtime', 'runtime'))
@@ -284,7 +269,7 @@
         artifacts[1].relativeUrl == 'added1.zip'
         artifacts[2].relativeUrl == '../path.jar'
 
-        metadataType == "gradle" ? artifacts[0] instanceof UrlBackedArtifactMetadata : artifacts[0] instanceof DefaultModuleComponentArtifactMetadata
+        artifacts[0] instanceof DefaultModuleComponentArtifactMetadata
         artifacts[1] instanceof UrlBackedArtifactMetadata
         artifacts[2] instanceof UrlBackedArtifactMetadata
 
@@ -322,18 +307,11 @@
         "gradle"     | gradleComponentMetadata()
     }
 
-    def selectTargetConfigurationMetadata(MutableModuleComponentResolveMetadata targetComponent) {
+    ConfigurationMetadata selectTargetConfigurationMetadata(MutableModuleComponentResolveMetadata targetComponent) {
         selectTargetConfigurationMetadata(targetComponent.asImmutable())
     }
 
-    def selectTargetConfigurationMetadata(ModuleComponentResolveMetadata immutable) {
-        def componentIdentifier = DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId("org.test", "consumer"), "1.0")
-        def consumerIdentifier = DefaultModuleVersionIdentifier.newId(componentIdentifier)
-        def componentSelector = newSelector(consumerIdentifier.module, new DefaultMutableVersionConstraint(consumerIdentifier.version))
-        def consumer = new LocalComponentDependencyMetadata(componentIdentifier, componentSelector, "default", attributes, ImmutableAttributes.EMPTY, null, [] as List, [], false, false, true, false, false, null)
-        def state = Stub(ComponentGraphResolveState) {
-            metadata >> immutable
-        }
-        consumer.selectVariants(attributes, state, schema, [] as Set).variants[0]
+    ConfigurationMetadata selectTargetConfigurationMetadata(ModuleComponentResolveMetadata immutable) {
+        return immutable.getConfiguration("default")
     }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetadataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetadataTest.groovy
index f9b88cb..4cc24e7 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetadataTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/local/model/DefaultLocalComponentMetadataTest.groovy
@@ -16,76 +16,113 @@
 
 package org.gradle.internal.component.local.model
 
-import com.google.common.collect.ImmutableSet
+import org.gradle.api.NamedDomainObjectContainer
+import org.gradle.api.artifacts.ConfigurationPublications
+import org.gradle.api.artifacts.ConfigurationVariant
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.DependencyConstraint
+import org.gradle.api.artifacts.DependencyConstraintSet
+import org.gradle.api.artifacts.DependencySet
+import org.gradle.api.artifacts.ExcludeRule
+import org.gradle.api.artifacts.ExternalModuleDependency
+import org.gradle.api.artifacts.FileCollectionDependency
+import org.gradle.api.artifacts.ModuleDependency
 import org.gradle.api.artifacts.PublishArtifact
-import org.gradle.api.internal.artifacts.DefaultModuleIdentifier
+import org.gradle.api.artifacts.PublishArtifactSet
+import org.gradle.api.artifacts.component.ComponentIdentifier
+import org.gradle.api.attributes.AttributeContainer
+import org.gradle.api.internal.artifacts.DefaultDependencySet
+import org.gradle.api.internal.artifacts.DefaultExcludeRule
+import org.gradle.api.internal.artifacts.DefaultImmutableModuleIdentifierFactory
 import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier
 import org.gradle.api.internal.artifacts.DefaultPublishArtifactSet
+import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.configurations.ConfigurationsProvider
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DefaultExcludeRuleConverter
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DefaultLocalConfigurationMetadataBuilder
+import org.gradle.api.internal.artifacts.ivyservice.moduleconverter.dependencies.DependencyMetadataFactory
 import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
-import org.gradle.api.internal.attributes.AttributeContainerInternal
 import org.gradle.api.internal.attributes.AttributesSchemaInternal
 import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.api.internal.initialization.RootScriptDomainObjectContext
-import org.gradle.api.tasks.TaskDependency
-import org.gradle.internal.DisplayName
-import org.gradle.internal.component.external.descriptor.DefaultExclude
+import org.gradle.api.internal.tasks.TaskDependencyFactory
+import org.gradle.internal.Describables
 import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
-import org.gradle.internal.component.external.model.ImmutableCapabilities
 import org.gradle.internal.component.model.DefaultIvyArtifactName
 import org.gradle.internal.component.model.IvyArtifactName
 import org.gradle.internal.component.model.LocalOriginDependencyMetadata
-import org.gradle.internal.component.model.VariantResolveMetadata
 import org.gradle.util.TestUtil
-import org.gradle.util.internal.WrapUtil
 import spock.lang.Specification
 
+import java.util.function.Consumer
+
+/**
+ * Tests {@link DefaultLocalComponentMetadata}.
+ *
+ * TODO: This class currently tests a lot of the functionality of
+ * {@link DefaultLocalConfigurationMetadataBuilder}. That class should either be merged
+ * with {@link DefaultLocalComponentMetadata}, or the relevant tests should be moved
+ * to the builder's test class.
+ */
 class DefaultLocalComponentMetadataTest extends Specification {
     def id = DefaultModuleVersionIdentifier.newId("group", "module", "version")
     def componentIdentifier = DefaultModuleComponentIdentifier.newId(id)
-    def metadata = createMetadata()
 
-    protected DefaultLocalComponentMetadata createMetadata() {
-        new DefaultLocalComponentMetadata(id, componentIdentifier, "status", Mock(AttributesSchemaInternal), RootScriptDomainObjectContext.INSTANCE, TestUtil.calculatedValueContainerFactory())
+    def metadataBuilder = new DefaultLocalConfigurationMetadataBuilder(
+        new TestDependencyMetadataFactory(),
+        new DefaultExcludeRuleConverter(new DefaultImmutableModuleIdentifierFactory())
+    )
+
+    Map<String, ConfigurationInternal> configurations = [:]
+    def configurationsProvider = Mock(ConfigurationsProvider) {
+        size() >> { this.configurations.size() }
+        getAll() >> { this.configurations.values() }
+        visitAll(_) >> { Consumer<ConfigurationInternal> visitor -> this.configurations.values().forEach {visitor.accept(it) } }
+        findByName(_) >> { String name -> this.configurations.get(name) }
     }
 
+    def configurationsFactory = new DefaultLocalComponentMetadata.ConfigurationsProviderMetadataFactory(
+        configurationsProvider, metadataBuilder, RootScriptDomainObjectContext.INSTANCE, TestUtil.calculatedValueContainerFactory())
+
+    def metadata = new DefaultLocalComponentMetadata(
+        id, componentIdentifier, "status", Mock(AttributesSchemaInternal),
+        configurationsFactory, null
+    )
+
     def "can lookup configuration after it has been added"() {
         when:
-        metadata.addConfiguration("super", "description", [] as Set<String>, ImmutableSet.of("super"), false, false, null, true, null, true, ImmutableCapabilities.EMPTY)
-        metadata.addConfiguration("conf", "description", ["super"] as Set, ImmutableSet.of("super", "conf"), true, true, null, true, null, true, ImmutableCapabilities.EMPTY)
+        def parent = addConfiguration("parent")
+        addConfiguration("conf", [parent])
 
         then:
-        metadata.configurationNames == ['conf', 'super'] as Set
+        metadata.configurationNames == ['conf', 'parent'] as Set
 
-        def conf = metadata.getConfiguration('conf')
-        conf != null
-        conf.visible
-        conf.transitive
-        conf.hierarchy == ['conf', 'super'] as Set
+        def confMd = metadata.getConfiguration('conf')
+        confMd != null
+        confMd.hierarchy == ['conf', 'parent'] as Set
 
-        def superConf = metadata.getConfiguration('super')
-        superConf != null
-        !superConf.visible
-        !superConf.transitive
-        superConf.hierarchy == ['super'] as Set
+        def parentMd = metadata.getConfiguration('parent')
+        parentMd != null
+        parentMd.hierarchy == ['parent'] as Set
     }
 
     def "configuration has no dependencies or artifacts when none have been added"() {
         when:
-        metadata.addConfiguration("super", "description", [] as Set<String>, ImmutableSet.of("super"), false, false, ImmutableAttributes.EMPTY, true, null, true, ImmutableCapabilities.EMPTY)
-        metadata.addConfiguration("conf", "description", ["super"] as Set, ImmutableSet.of("super", "conf"), true, true, ImmutableAttributes.EMPTY, true, null, true, ImmutableCapabilities.EMPTY)
+        def parent = addConfiguration("parent")
+        addConfiguration("conf", [parent])
 
         then:
-        def conf = metadata.getConfiguration('conf')
-        conf.dependencies.empty
-        conf.excludes.empty
-        conf.files.empty
+        def confMd = metadata.getConfiguration('conf')
+        confMd.dependencies.empty
+        confMd.excludes.empty
+        confMd.files.empty
 
         when:
-        conf.prepareToResolveArtifacts()
+        confMd.prepareToResolveArtifacts()
 
         then:
-        conf.artifacts.empty
+        confMd.artifacts.empty
     }
 
     def "can lookup artifact in various ways after it has been added"() {
@@ -97,7 +134,7 @@
 
         when:
         addArtifact(conf, artifact, file)
-        conf.prepareToResolveArtifacts()
+        metadata.getConfiguration("conf").prepareToResolveArtifacts()
 
         then:
         metadata.getConfiguration("conf").artifacts.size() == 1
@@ -122,16 +159,16 @@
         given:
         def conf1 = addConfiguration("conf1")
         def conf2 = addConfiguration("conf2")
-        def child1 = addConfiguration("child1", ["conf1", "conf2"])
-        def child2 = addConfiguration("child2", ["conf1"])
+        def child1 = addConfiguration("child1", [conf1, conf2])
+        addConfiguration("child2", [conf1])
 
         when:
         addArtifact(conf1, artifact1, file1)
         addArtifact(conf2, artifact2, file2)
         addArtifact(child1, artifact3, file3)
-        conf1.prepareToResolveArtifacts()
-        child1.prepareToResolveArtifacts()
-        child2.prepareToResolveArtifacts()
+        metadata.getConfiguration("conf1").prepareToResolveArtifacts()
+        metadata.getConfiguration("child1").prepareToResolveArtifacts()
+        metadata.getConfiguration("child2").prepareToResolveArtifacts()
 
         then:
         metadata.getConfiguration("conf1").artifacts.size() == 1
@@ -139,24 +176,6 @@
         metadata.getConfiguration("child2").artifacts.size() == 1
     }
 
-    BuildableLocalConfigurationMetadata addConfiguration(String name, Collection<String> extendsFrom = [], AttributeContainerInternal attributes = ImmutableAttributes.EMPTY) {
-        return metadata.addConfiguration(name, "", extendsFrom as Set, ImmutableSet.copyOf(extendsFrom + [name]), true, true, attributes as ImmutableAttributes, true, null, true, ImmutableCapabilities.EMPTY)
-    }
-
-    void addArtifact(BuildableLocalConfigurationMetadata configuration, IvyArtifactName name, File file, TaskDependency buildDeps = null) {
-        PublishArtifact publishArtifact = new DefaultPublishArtifact(name.name, name.extension, name.type, name.classifier, new Date(), file)
-        if (buildDeps != null) {
-            publishArtifact.builtBy(buildDeps)
-        }
-        addArtifact(configuration, publishArtifact)
-    }
-
-    void addArtifact(BuildableLocalConfigurationMetadata configuration, PublishArtifact publishArtifact) {
-        configuration.addArtifacts(
-            new DefaultPublishArtifactSet("arts", WrapUtil.toDomainObjectSet(PublishArtifact, publishArtifact), TestFiles.fileCollectionFactory(), TestFiles.taskDependencyFactory())
-        )
-    }
-
     def "can add artifact to several configurations"() {
         def artifact = artifactName()
         def file = new File("artifact.zip")
@@ -167,10 +186,10 @@
 
         when:
         def publishArtifact = new DefaultPublishArtifact(artifact.name, artifact.extension, artifact.type, artifact.classifier, new Date(), file)
-        conf1.addArtifacts([publishArtifact])
-        conf2.addArtifacts([publishArtifact])
-        conf1.prepareToResolveArtifacts()
-        conf2.prepareToResolveArtifacts()
+        conf1.artifacts.add(publishArtifact)
+        conf2.artifacts.add(publishArtifact)
+        metadata.getConfiguration("conf1").prepareToResolveArtifacts()
+        metadata.getConfiguration("conf2").prepareToResolveArtifacts()
 
         then:
         metadata.getConfiguration("conf1").artifacts.size() == 1
@@ -186,7 +205,7 @@
 
         and:
         addArtifact(conf, artifact, file)
-        conf.prepareToResolveArtifacts()
+        metadata.getConfiguration("conf").prepareToResolveArtifacts()
 
         expect:
         def ivyArtifact = artifactName()
@@ -198,10 +217,12 @@
         def artifact = artifactName()
 
         given:
-        def conf = addConfiguration("conf")
-        conf.prepareToResolveArtifacts()
+        addConfiguration("conf")
 
-        expect:
+        when:
+        metadata.getConfiguration("conf").prepareToResolveArtifacts()
+
+        then:
         def resolveArtifact = metadata.getConfiguration("conf").artifact(artifact)
         resolveArtifact != null
         resolveArtifact.file == null
@@ -213,13 +234,15 @@
         def file1 = new File("artifact-1.zip")
         def file2 = new File("artifact-2.zip")
 
-        when:
+        given:
         def conf1 = addConfiguration("conf1")
         def conf2 = addConfiguration("conf2")
         addArtifact(conf1, artifact1, file1)
         addArtifact(conf2, artifact2, file2)
-        conf1.prepareToResolveArtifacts()
-        conf2.prepareToResolveArtifacts()
+
+        when:
+        metadata.getConfiguration("conf1").prepareToResolveArtifacts()
+        metadata.getConfiguration("conf2").prepareToResolveArtifacts()
 
         then:
         def conf1Artifacts = metadata.getConfiguration("conf1").artifacts as List
@@ -239,110 +262,195 @@
     }
 
     def "variants are attached to configuration but not its children"() {
-        def variant1Attrs = attributes()
-        def variant1Artifacts = ([Stub(PublishArtifact)] as Set)
-        def variant2Attrs = attributes()
-        def variant2Artifacts = ([Stub(PublishArtifact)] as Set)
+        def variant1Attrs = Stub(ImmutableAttributes)
+        def variant2Attrs = Stub(ImmutableAttributes)
+
+        given:
+        def conf1 = addConfiguration("conf1")
+        def conf2 = addConfiguration("conf2", [conf1])
+        ConfigurationVariant variant1 = conf1.outgoing.getVariants().create("variant1")
+        ConfigurationVariant variant2 = conf2.outgoing.getVariants().create("variant2")
+        variant1.attributes >> variant1Attrs
+        variant2.attributes >> variant2Attrs
+        variant1.artifacts.add(Stub(PublishArtifact))
+        variant2.artifacts.add(Stub(PublishArtifact))
+        variant2.artifacts.add(Stub(PublishArtifact))
 
         when:
-        def conf1 = addConfiguration("conf1")
-        def conf2 = addConfiguration("conf2", ["conf1"])
-        conf1.addVariant("variant1", Stub(VariantResolveMetadata.Identifier), Stub(DisplayName), variant1Attrs, ImmutableCapabilities.EMPTY, variant1Artifacts)
-        conf2.addVariant("variant2", Stub(VariantResolveMetadata.Identifier), Stub(DisplayName), variant2Attrs, ImmutableCapabilities.EMPTY, variant2Artifacts)
+        def config1 = metadata.getConfiguration("conf1")
+        def config2 = metadata.getConfiguration("conf2")
 
         then:
-        def config1 = metadata.getConfiguration("conf1")
-        config1.variants.size() == 1
-        config1.variants.first().name == "variant1"
-        config1.variants.first().attributes == variant1Attrs
+        config1.variants*.name as List == ["conf1", "conf1-variant1"]
+        config1.variants.find { it.name == "conf1-variant1" }.attributes == variant1Attrs
 
-        def config2 = metadata.getConfiguration("conf2")
-        config2.variants.size() == 1
-        config2.variants.first().name == "variant2"
-        config2.variants.first().attributes == variant2Attrs
+        config2.variants*.name as List == ["conf2", "conf2-variant2"]
+        config2.variants.find { it.name == "conf2-variant2" }.attributes == variant2Attrs
 
         when:
         config1.prepareToResolveArtifacts()
         config2.prepareToResolveArtifacts()
 
         then:
-        config1.variants.first().artifacts.size() == 1
-        config2.variants.first().artifacts.size() == 1
-    }
-
-    private ImmutableAttributes attributes() {
-        return Stub(ImmutableAttributes)
+        config1.variants.find { it.name == "conf1-variant1" }.artifacts.size() == 1
+        config2.variants.find { it.name == "conf2-variant2" }.artifacts.size() == 2
     }
 
     def "files attached to configuration and its children"() {
-        def files1 = Stub(LocalFileDependencyMetadata)
-        def files2 = Stub(LocalFileDependencyMetadata)
-        def files3 = Stub(LocalFileDependencyMetadata)
+        def files1 = Stub(FileCollectionDependency)
+        def files2 = Stub(FileCollectionDependency)
+        def files3 = Stub(FileCollectionDependency)
 
         given:
         def conf1 = addConfiguration("conf1")
         def conf2 = addConfiguration("conf2")
-        def child1 = addConfiguration("child1", ["conf1", "conf2"])
-        addConfiguration("child2", ["conf1"])
+        def child1 = addConfiguration("child1", [conf1, conf2])
+        addConfiguration("child2", [conf1])
 
         when:
-        conf1.addFiles(files1)
-        conf2.addFiles(files2)
-        child1.addFiles(files3)
+        conf1.getDependencies().add(files1)
+        conf2.getDependencies().add(files2)
+        child1.getDependencies().add(files3)
 
         then:
-        metadata.getConfiguration("conf1").files == [files1] as Set
-        metadata.getConfiguration("conf2").files == [files2] as Set
-        metadata.getConfiguration("child1").files == [files1, files2, files3] as Set
-        metadata.getConfiguration("child2").files == [files1] as Set
+        metadata.getConfiguration("conf1").files*.source == [files1]
+        metadata.getConfiguration("conf2").files*.source == [files2]
+        metadata.getConfiguration("child1").files*.source == [files1, files2, files3]
+        metadata.getConfiguration("child2").files*.source == [files1]
     }
 
     def "dependency is attached to configuration and its children"() {
-        def dependency1 = Mock(LocalOriginDependencyMetadata)
-        dependency1.moduleConfiguration >> "conf1"
-        def dependency2 = Mock(LocalOriginDependencyMetadata)
-        dependency2.moduleConfiguration >> "conf2"
-        def dependency3 = Mock(LocalOriginDependencyMetadata)
-        dependency3.moduleConfiguration >> "child1"
+        def dependency1 = Mock(ExternalModuleDependency)
+        def dependency2 = Mock(ExternalModuleDependency)
+        def dependency3 = Mock(ExternalModuleDependency)
 
         when:
         def conf1 = addConfiguration("conf1")
         def conf2 = addConfiguration("conf2")
-        def child1 = addConfiguration("child1", ["conf1", "conf2"])
-        addConfiguration("child2", ["conf1"])
+        def child1 = addConfiguration("child1", [conf1, conf2])
+        addConfiguration("child2", [conf1])
         addConfiguration("other")
 
-        conf1.addDependency(dependency1)
-        conf2.addDependency(dependency2)
-        child1.addDependency(dependency3)
+        conf1.getDependencies().add(dependency1)
+        conf2.getDependencies().add(dependency2)
+        child1.getDependencies().add(dependency3)
 
         then:
-        metadata.getConfiguration("conf1").dependencies == [dependency1]
-        metadata.getConfiguration("conf2").dependencies == [dependency2]
-        metadata.getConfiguration("child1").dependencies == [dependency1, dependency2, dependency3]
-        metadata.getConfiguration("child2").dependencies == [dependency1]
+        metadata.getConfiguration("conf1").dependencies*.source == [dependency1]
+        metadata.getConfiguration("conf2").dependencies*.source == [dependency2]
+        metadata.getConfiguration("child1").dependencies*.source == [dependency1, dependency2, dependency3]
+        metadata.getConfiguration("child2").dependencies*.source == [dependency1]
         metadata.getConfiguration("other").dependencies.isEmpty()
     }
 
     def "builds and caches exclude rules for a configuration"() {
         given:
-        def compile = metadata.addConfiguration("compile", null, [] as Set<String>, ImmutableSet.of("compile"), true, true, null, true, null, true, ImmutableCapabilities.EMPTY)
-        def runtime = metadata.addConfiguration("runtime", null, ["compile"] as Set, ImmutableSet.of("compile", "runtime"), true, true, null, true, null, true, ImmutableCapabilities.EMPTY)
+        def compile = addConfiguration("compile")
+        def runtime = addConfiguration("runtime", [compile])
 
-        def rule1 = new DefaultExclude(DefaultModuleIdentifier.newId("group1", "module1"))
-        def rule2 = new DefaultExclude(DefaultModuleIdentifier.newId("group1", "module1"))
-
-        compile.addExclude(rule1)
-        runtime.addExclude(rule2)
+        compile.getExcludeRules().add(new DefaultExcludeRule("group1", "module1"))
+        runtime.getExcludeRules().add(new DefaultExcludeRule("group2", "module2"))
 
         expect:
         def config = metadata.getConfiguration("runtime")
         def excludes = config.excludes
-        excludes == [rule1, rule2]
+        config.excludes*.moduleId.group == ["group1", "group2"]
+        config.excludes*.moduleId.name == ["module1", "module2"]
         config.excludes.is(excludes)
     }
 
     def artifactName() {
         return new DefaultIvyArtifactName("artifact", "type", "ext")
     }
+
+    ConfigurationInternal addConfiguration(String name, List<ConfigurationInternal> extendsFrom = []) {
+        def conf = configuration(name, extendsFrom)
+        configurations.put(name, conf)
+        return conf
+    }
+
+    /**
+     * Creates a minimal mocked {@link ConfigurationInternal} instance.
+     *
+     * TODO: We really should not need such a complex Configuration mock here. However, since some of
+     * the tests here are testing functionality of {@link DefaultLocalConfigurationMetadataBuilder}, this
+     * complex Configuration mock is required.
+     *
+     * TODO: And TBH if we're doing this much mocking, we really should be using real Configurations.
+     */
+    ConfigurationInternal configuration(String name, List<ConfigurationInternal> extendsFrom) {
+
+        DependencySet dependencies = new DefaultDependencySet(Describables.of("dependencies"), Mock(ConfigurationInternal) {
+            isCanBeDeclared() >> true
+        }, TestUtil.domainObjectCollectionFactory().newDomainObjectSet(Dependency))
+
+        DependencyConstraintSet dependencyConstraints = Mock() {
+            iterator() >> Collections.emptyIterator()
+        }
+
+        NamedDomainObjectContainer<ConfigurationVariant> variants = TestUtil.domainObjectCollectionFactory()
+            .newNamedDomainObjectContainer(ConfigurationVariant.class, variantName -> Mock(ConfigurationVariant) {
+                getName() >> variantName
+                getArtifacts() >> artifactSet()
+            })
+
+        PublishArtifactSet artifacts = artifactSet()
+        ConfigurationPublications outgoing = Mock(ConfigurationPublications) {
+            getCapabilities() >> Collections.emptySet()
+            getArtifacts() >> artifacts
+            getVariants() >> variants
+        }
+
+        def conf = Mock(ConfigurationInternal) {
+            getName() >> name
+            getAttributes() >> ImmutableAttributes.EMPTY
+            isCanBeDeclared() >> true
+            getDependencies() >> dependencies
+            getDependencyConstraints() >> dependencyConstraints
+            getExcludeRules() >> new LinkedHashSet<ExcludeRule>()
+            collectVariants(_ as ConfigurationInternal.VariantVisitor) >> { ConfigurationInternal.VariantVisitor visitor ->
+                visitor.visitArtifacts(artifacts)
+                visitor.visitOwnVariant(Describables.of(name), ImmutableAttributes.EMPTY, Collections.emptySet(), artifacts)
+                variants.each {
+                    visitor.visitChildVariant(it.name, Describables.of(it.name), it.attributes as ImmutableAttributes, Collections.emptySet(), it.artifacts)
+                }
+            }
+            getOutgoing() >> outgoing
+            getExtendsFrom() >> extendsFrom
+            getArtifacts() >> artifacts
+        }
+        conf.getHierarchy() >> [conf] + extendsFrom
+        return conf
+    }
+
+    void addArtifact(ConfigurationInternal configuration, IvyArtifactName name, File file) {
+        PublishArtifact publishArtifact = new DefaultPublishArtifact(name.name, name.extension, name.type, name.classifier, new Date(), file)
+
+        configuration.artifacts.add(publishArtifact)
+    }
+
+    PublishArtifactSet artifactSet() {
+        new DefaultPublishArtifactSet(
+            Describables.of("artifacts"),
+            TestUtil.domainObjectCollectionFactory().newDomainObjectSet(PublishArtifact),
+            TestFiles.fileCollectionFactory(),
+            Mock(TaskDependencyFactory)
+        )
+    }
+
+    LocalOriginDependencyMetadata dependencyMetadata(Dependency dependency) {
+        return new DslOriginDependencyMetadataWrapper(Mock(LocalOriginDependencyMetadata), dependency)
+    }
+
+    class TestDependencyMetadataFactory implements DependencyMetadataFactory {
+        @Override
+        LocalOriginDependencyMetadata createDependencyMetadata(ComponentIdentifier componentId, String clientConfiguration, AttributeContainer attributes, ModuleDependency dependency) {
+            return dependencyMetadata(dependency)
+        }
+
+        @Override
+        LocalOriginDependencyMetadata createDependencyConstraintMetadata(ComponentIdentifier componentId, String clientConfiguration, AttributeContainer attributes, DependencyConstraint dependencyConstraint) {
+            throw new UnsupportedOperationException()
+        }
+    }
 }
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/AttributeConfigurationSelectorTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/AttributeConfigurationSelectorTest.groovy
index 0a8eb0e..900fc4c 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/AttributeConfigurationSelectorTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/AttributeConfigurationSelectorTest.groovy
@@ -16,7 +16,6 @@
 
 package org.gradle.internal.component.model
 
-import com.google.common.base.Optional
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.Lists
 import org.gradle.api.artifacts.ArtifactIdentifier
@@ -46,16 +45,17 @@
 
     private ComponentGraphResolveState targetState
     private ComponentGraphResolveMetadata targetComponent
-    private ConfigurationMetadata selected
+    private VariantGraphResolveState selected
     private ImmutableAttributes consumerAttributes = ImmutableAttributes.EMPTY
     private List<Capability> requestedCapabilities = []
     private List<IvyArtifactName> artifacts = []
+    private ConfigurationGraphResolveState defaultConfiguration
 
     def "selects a variant when there's no ambiguity"() {
         given:
         component(
-                variant("api", attributes('org.gradle.usage': 'java-api')),
-                variant("runtime", attributes('org.gradle.usage': 'java-runtime'))
+            variant("api", attributes('org.gradle.usage': 'java-api')),
+            variant("runtime", attributes('org.gradle.usage': 'java-runtime'))
         )
 
         and:
@@ -76,8 +76,8 @@
     def "fails to select a variant when there are more than one candidate"() {
         given:
         component(
-                variant("api1", attributes('org.gradle.usage': 'java-api')),
-                variant("api2", attributes('org.gradle.usage': 'java-api'))
+            variant("api1", attributes('org.gradle.usage': 'java-api')),
+            variant("api2", attributes('org.gradle.usage': 'java-api'))
         )
 
         and:
@@ -99,8 +99,8 @@
     def "fails to select a variant when there no matching candidates"() {
         given:
         component(
-                variant("api", attributes('org.gradle.usage': 'java-api')),
-                variant("runtime", attributes('org.gradle.usage': 'java-runtime'))
+            variant("api", attributes('org.gradle.usage': 'java-api')),
+            variant("runtime", attributes('org.gradle.usage': 'java-runtime'))
         )
 
         and:
@@ -118,11 +118,42 @@
       - Incompatible because this component declares attribute 'org.gradle.usage' with value 'java-runtime' and the consumer needed attribute 'org.gradle.usage' with value 'cplusplus-headers\'''')
     }
 
+    def "falls back to the default configuration if variant aware resolution is not supported"() {
+        given:
+        component(Optional.empty())
+
+        and:
+        defaultConfiguration()
+
+        when:
+        performSelection()
+
+        then:
+        selected.name == "default"
+    }
+
+    def "fails to fall back to the default configuration if the attributes do not match"() {
+        given:
+        component(Optional.empty())
+
+        and:
+        defaultConfiguration(attributes('org.gradle.usage': 'java-api'))
+        consumerAttributes('org.gradle.usage': 'cplusplus-headers')
+
+        when:
+        performSelection()
+
+        then:
+        NoMatchingConfigurationSelectionException e = thrown()
+        failsWith(e, '''No matching configuration of org:lib:1.0 was found. The consumer was configured to find attribute 'org.gradle.usage' with value 'cplusplus-headers' but:
+  - None of the consumable configurations have attributes.''')
+    }
+
     def "can select a variant thanks to the capabilities"() {
         given:
         component(
-                variant("api1", attributes('org.gradle.usage': 'java-api'), capability('first')),
-                variant("api2", attributes('org.gradle.usage': 'java-api'), capability('second'))
+            variant("api1", attributes('org.gradle.usage': 'java-api'), capability('first')),
+            variant("api2", attributes('org.gradle.usage': 'java-api'), capability('second'))
         )
 
         and:
@@ -144,8 +175,8 @@
     def "can select a variant thanks to the implicit capability"() {
         given:
         component(
-                variant("api1", attributes('org.gradle.usage': 'java-api')),
-                variant("api2", attributes('org.gradle.usage': 'java-api'), capability('second'))
+            variant("api1", attributes('org.gradle.usage': 'java-api')),
+            variant("api2", attributes('org.gradle.usage': 'java-api'), capability('second'))
         )
 
         and:
@@ -172,9 +203,9 @@
     def "fails if more than one variant provides the implicit capability"() {
         given:
         component(
-                variant("api1", attributes('org.gradle.usage': 'java-api')),
-                variant("api2", attributes('org.gradle.usage': 'java-api')),
-                variant("api3", attributes('org.gradle.usage': 'java-api'), capability('lib'), capability('second'))
+            variant("api1", attributes('org.gradle.usage': 'java-api')),
+            variant("api2", attributes('org.gradle.usage': 'java-api')),
+            variant("api3", attributes('org.gradle.usage': 'java-api'), capability('lib'), capability('second'))
         )
 
         and:
@@ -292,8 +323,8 @@
     def "should select the variant which matches the most attributes"() {
         given:
         component(
-                variant("first", attributes('org.gradle.usage': 'java-api')),
-                variant("second", attributes('org.gradle.usage': 'java-api', 'other': true))
+            variant("first", attributes('org.gradle.usage': 'java-api')),
+            variant("second", attributes('org.gradle.usage': 'java-api', 'other': true))
         )
 
         and:
@@ -309,8 +340,8 @@
     def "should not select variant whenever 2 variants provide different extra attributes"() {
         given:
         component(
-                variant("first", attributes('org.gradle.usage': 'java-api', extra: 'v1')),
-                variant("second", attributes('org.gradle.usage': 'java-api', other: true))
+            variant("first", attributes('org.gradle.usage': 'java-api', extra: 'v1')),
+            variant("second", attributes('org.gradle.usage': 'java-api', other: true))
         )
 
         and:
@@ -337,8 +368,8 @@
     def "should select the variant matching most closely the requested attributes when they provide more than one extra attributes"() {
         given:
         component(
-                variant("first", attributes('org.gradle.usage': 'java-api', extra: 'v1', other: true)),
-                variant("second", attributes('org.gradle.usage': 'java-api', other: true))
+            variant("first", attributes('org.gradle.usage': 'java-api', extra: 'v1', other: true)),
+            variant("second", attributes('org.gradle.usage': 'java-api', other: true))
         )
 
         and:
@@ -356,8 +387,8 @@
     def "should select the variant which matches the most attributes and producer doesn't have requested value"() {
         given:
         component(
-                variant("first", attributes('org.gradle.usage': 'java-api')),
-                variant("second", attributes('org.gradle.usage': 'java-runtime', 'other': true))
+            variant("first", attributes('org.gradle.usage': 'java-api')),
+            variant("second", attributes('org.gradle.usage': 'java-runtime', 'other': true))
         )
         attributesSchema.attribute(Attribute.of("org.gradle.usage", String)) {
             it.compatibilityRules.add(UsageCompatibilityRule)
@@ -373,12 +404,10 @@
     }
 
     def "should select the variant which matches the requested classifier"() {
-        def variant1 = variant("first", ImmutableAttributes.EMPTY)
-        def variant2 = variant("second", ImmutableAttributes.EMPTY)
+        def variant1 = variantWithArtifacts("first", artifact('foo', null))
+        def variant2 = variantWithArtifacts("second", artifact('foo', 'classy'))
 
         given:
-        variant1.getArtifacts() >> ImmutableList.of(artifact('foo', null))
-        variant2.getArtifacts() >> ImmutableList.of(artifact('foo', 'classy'))
         component(variant1, variant2)
 
         and:
@@ -433,11 +462,11 @@
 
     private void performSelection() {
         selected = AttributeConfigurationSelector.selectVariantsUsingAttributeMatching(
-                consumerAttributes,
-                requestedCapabilities,
-                targetState,
-                attributesSchema,
-                artifacts
+            consumerAttributes,
+            requestedCapabilities,
+            targetState,
+            attributesSchema,
+            artifacts
         ).variants[0]
     }
 
@@ -465,7 +494,26 @@
         requestedCapabilities << c
     }
 
-    private void component(ConfigurationMetadata... variants) {
+    private void defaultConfiguration(ImmutableAttributes attrs = attributes([:])) {
+        def variant = Stub(VariantGraphResolveState) {
+            getName() >> 'default'
+        }
+        def metadata = Stub(ConfigurationGraphResolveMetadata) {
+            isCanBeConsumed() >> true
+        }
+        defaultConfiguration = Stub(ConfigurationGraphResolveState) {
+            getName() >> 'default'
+            getAttributes() >> attrs
+            getMetadata() >> metadata
+            asVariant() >> variant
+        }
+    }
+
+    private void component(VariantGraphResolveState... variants) {
+        component(Optional.of(ImmutableList.copyOf(variants)))
+    }
+
+    private void component(Optional<List<VariantGraphResolveState>> variants) {
         targetComponent = Stub(ComponentGraphResolveMetadata) {
             getModuleVersionId() >> Stub(ModuleVersionIdentifier) {
                 getGroup() >> 'org'
@@ -475,22 +523,48 @@
             getId() >> Stub(ComponentIdentifier) {
                 getDisplayName() >> 'org:lib:1.0'
             }
-            getVariantsForGraphTraversal() >> Optional.of(
-                    ImmutableList.copyOf(variants)
-            )
             getAttributesSchema() >> attributesSchema
         }
+        def candidates = Stub(GraphSelectionCandidates) {
+            isUseVariants() >> { variants.isPresent() }
+            getVariants() >> { variants.get() }
+            getLegacyConfiguration() >> { defaultConfiguration }
+        }
         targetState = Stub(ComponentGraphResolveState) {
             getMetadata() >> targetComponent
-            resolveArtifactsFor(_) >> { VariantGraphResolveMetadata variant -> variant }
+            getCandidatesForGraphVariantSelection() >> candidates
         }
     }
 
-    private ModuleConfigurationMetadata variant(String name, ImmutableAttributes attributes, Capability... capabilities) {
-        Stub(ModuleConfigurationMetadata) {
+    private VariantGraphResolveState variant(String name, ImmutableAttributes attributes, Capability... capabilities) {
+        def metadata = Stub(VariantGraphResolveMetadata) {
             getName() >> name
             getAttributes() >> attributes
-            getCapabilities() >> ImmutableCapabilities.of(Lists.newArrayList(capabilities));
+            getCapabilities() >> ImmutableCapabilities.of(Lists.newArrayList(capabilities))
+        }
+        return Stub(VariantGraphResolveState) {
+            getName() >> name
+            getAttributes() >> attributes
+            getCapabilities() >> ImmutableCapabilities.of(Lists.newArrayList(capabilities))
+            getMetadata() >> metadata
+        }
+    }
+
+    private VariantGraphResolveState variantWithArtifacts(String name, ModuleComponentArtifactMetadata artifact) {
+        def metadata = Stub(VariantGraphResolveMetadata) {
+            getName() >> name
+            getAttributes() >> ImmutableAttributes.EMPTY
+            getCapabilities() >> ImmutableCapabilities.EMPTY
+        }
+        def artifactMetadata = Stub(VariantArtifactGraphResolveMetadata) {
+            getArtifacts() >> ImmutableList.of(artifact)
+        }
+        return Stub(VariantGraphResolveState) {
+            getName() >> name
+            getAttributes() >> ImmutableAttributes.EMPTY
+            getCapabilities() >> ImmutableCapabilities.EMPTY
+            getMetadata() >> metadata
+            resolveArtifacts() >> artifactMetadata
         }
     }
 
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/AttributePrecedenceSchemaAttributeMatcherTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/AttributePrecedenceSchemaAttributeMatcherTest.groovy
index b7d83be..4bc2cc5 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/AttributePrecedenceSchemaAttributeMatcherTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/AttributePrecedenceSchemaAttributeMatcherTest.groovy
@@ -98,13 +98,13 @@
         def candidate6 = candidate("compatible", "compatible", "compatible")
         def requested = requested("requested", "requested","requested")
         expect:
-        schema.matcher().matches([candidate1], requested, null, explanationBuilder) == [candidate1]
-        schema.matcher().matches([candidate1, candidate2, candidate3, candidate4, candidate5, candidate6], requested, null, explanationBuilder) == [candidate1]
-        schema.matcher().matches([candidate2, candidate3, candidate4, candidate5, candidate6], requested, null, explanationBuilder) == [candidate2]
-        schema.matcher().matches([candidate3, candidate4, candidate5, candidate6], requested, null, explanationBuilder) == [candidate3]
-        schema.matcher().matches([candidate4, candidate5, candidate6], requested, null, explanationBuilder) == [candidate4]
-        schema.matcher().matches([candidate5, candidate6], requested, null, explanationBuilder) == [candidate5]
-        schema.matcher().matches([candidate6], requested, null, explanationBuilder) == [candidate6]
+        schema.matcher().matches([candidate1], requested, explanationBuilder) == [candidate1]
+        schema.matcher().matches([candidate1, candidate2, candidate3, candidate4, candidate5, candidate6], requested, explanationBuilder) == [candidate1]
+        schema.matcher().matches([candidate2, candidate3, candidate4, candidate5, candidate6], requested, explanationBuilder) == [candidate2]
+        schema.matcher().matches([candidate3, candidate4, candidate5, candidate6], requested, explanationBuilder) == [candidate3]
+        schema.matcher().matches([candidate4, candidate5, candidate6], requested, explanationBuilder) == [candidate4]
+        schema.matcher().matches([candidate5, candidate6], requested, explanationBuilder) == [candidate5]
+        schema.matcher().matches([candidate6], requested, explanationBuilder) == [candidate6]
     }
 
     def "disambiguates extra attributes in precedence order"() {
@@ -115,9 +115,9 @@
         def requested = AttributeTestUtil.attributes("unknown": "unknown")
 
         expect:
-        schema.matcher().matches([candidate1, candidate2, candidate3, candidate4], requested, null, explanationBuilder) == [candidate1]
-        schema.matcher().matches([candidate2, candidate3, candidate4], requested, null, explanationBuilder) == [candidate2]
-        schema.matcher().matches([candidate3, candidate4], requested, null, explanationBuilder) == [candidate3]
+        schema.matcher().matches([candidate1, candidate2, candidate3, candidate4], requested, explanationBuilder) == [candidate1]
+        schema.matcher().matches([candidate2, candidate3, candidate4], requested, explanationBuilder) == [candidate2]
+        schema.matcher().matches([candidate3, candidate4], requested, explanationBuilder) == [candidate3]
     }
 
     private static AttributeContainerInternal requested(String highestValue, String middleValue, String lowestValue) {
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/ComponentAttributeMatcherTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/ComponentAttributeMatcherTest.groovy
deleted file mode 100644
index 64c03d4..0000000
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/ComponentAttributeMatcherTest.groovy
+++ /dev/null
@@ -1,668 +0,0 @@
-/*
- * Copyright 2016 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.component.model
-
-import com.google.common.collect.LinkedListMultimap
-import com.google.common.collect.Multimap
-import org.gradle.api.Named
-import org.gradle.api.attributes.Attribute
-import org.gradle.api.attributes.AttributeDisambiguationRule
-import org.gradle.api.attributes.MultipleCandidatesDetails
-import org.gradle.api.internal.attributes.AttributeContainerInternal
-import org.gradle.api.internal.attributes.ImmutableAttributes
-import org.gradle.util.AttributeTestUtil
-import spock.lang.Specification
-
-import java.util.stream.Collectors
-import java.util.stream.IntStream
-
-import static org.gradle.util.AttributeTestUtil.attributes
-import static org.gradle.util.TestUtil.objectFactory
-
-class ComponentAttributeMatcherTest extends Specification {
-
-    def schema = new TestSchema()
-    def factory = AttributeTestUtil.attributesFactory()
-    def explanationBuilder = Stub(AttributeMatchingExplanationBuilder)
-
-    def "selects candidate with same set of attributes and whose values match"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of('usage', String)
-        schema.attribute(usage)
-
-        def candidate1 = attributes(usage: "match")
-        def candidate2 = attributes(usage: "no match")
-        def requested = attributes(usage: "match")
-
-        expect:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == [candidate1]
-        matcher.matches([candidate2], requested, null, explanationBuilder) == []
-
-        matcher.isMatching(candidate1, requested)
-        !matcher.isMatching(candidate2, requested)
-    }
-
-    def "selects candidate with subset of attributes and whose values match"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of('usage', String)
-        schema.attribute(usage)
-        def other = Attribute.of('other', String)
-        schema.attribute(other)
-
-        def candidate1 = attributes(usage: "match")
-        def candidate2 = attributes(usage: "no match")
-        def candidate3 = attributes(usage: "match", other: "no match")
-        def candidate4 = attributes()
-
-        def requested = attributes(usage: "match", other: "match")
-
-        expect:
-        matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, null, explanationBuilder) == [candidate1]
-        matcher.matches([candidate2, candidate3, candidate4], requested, null, explanationBuilder) == [candidate4]
-
-        matcher.isMatching(candidate1, requested)
-        !matcher.isMatching(candidate2, requested)
-        !matcher.isMatching(candidate3, requested)
-        matcher.isMatching(candidate4, requested)
-    }
-
-    def "selects candidate with additional attributes and whose values match"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of('usage', String)
-        schema.attribute(usage)
-        def other = Attribute.of('other', String)
-        schema.attribute(other)
-
-        def candidate1 = attributes(usage: "match", other: "dont care")
-        def candidate2 = attributes(usage: "no match")
-        def requested = attributes(usage: "match")
-
-        expect:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == [candidate1]
-        matcher.matches([candidate2], requested, null, explanationBuilder) == []
-
-        matcher.isMatching(candidate1, requested)
-        !matcher.isMatching(candidate2, requested)
-    }
-
-    def "selects multiple candidates with compatible values"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of('usage', String)
-        schema.attribute(usage)
-        def other = Attribute.of('other', String)
-        schema.attribute(other)
-
-        def candidate1 = attributes(usage: "match")
-        def candidate2 = attributes(usage: "no match")
-        def candidate3 = attributes(other: "match")
-        def candidate4 = attributes()
-        def candidate5 = attributes(usage: "match")
-
-        def requested = attributes(usage: "match", other: "match")
-
-        expect:
-        matcher.matches([candidate1, candidate2, candidate3, candidate4, candidate5], requested, null, explanationBuilder) == [candidate1, candidate3, candidate4, candidate5]
-    }
-
-    def "applies disambiguation rules and selects intersection of best matches for each attribute"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of('usage', String)
-        schema.attribute(usage)
-        schema.accept(usage, "requested", "compatible")
-        schema.accept(usage, "requested", "best")
-        schema.prefer(usage, "best")
-        def other = Attribute.of('other', String)
-        schema.attribute(other)
-        schema.accept(other, "requested", "compatible")
-        schema.accept(other, "requested", "best")
-        schema.prefer(other, "best")
-
-        def candidate1 = attributes(usage: "best", other: "compatible")
-        def candidate2 = attributes(usage: "no match", other: "no match")
-        def candidate3 = attributes(usage: "compatible", other: "best")
-        def candidate4 = attributes()
-        def candidate5 = attributes(usage: "best", other: "best")
-
-        def requested = attributes(usage: "requested", other: "requested")
-
-        expect:
-        matcher.matches([candidate1, candidate2, candidate3, candidate4, candidate5], requested, null, explanationBuilder) == [candidate5]
-        matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, null, explanationBuilder) == [candidate1, candidate3, candidate4]
-        matcher.matches([candidate1, candidate2, candidate4], requested, null, explanationBuilder) == [candidate1]
-        matcher.matches([candidate2, candidate4], requested, null, explanationBuilder) == [candidate4]
-    }
-
-    def "rule can disambiguate based on requested value"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of('usage', String)
-        def rule = new AttributeDisambiguationRule<String>() {
-            @Override
-            void execute(MultipleCandidatesDetails details) {
-                if (details.consumerValue == null) {
-                    details.closestMatch("compatible")
-                } else if (details.consumerValue == "requested") {
-                    details.closestMatch("best")
-                }
-            }
-        }
-        schema.attribute(usage)
-        schema.accept(usage, "requested", "compatible")
-        schema.accept(usage, "requested", "best")
-        schema.select(usage, rule)
-
-        def candidate1 = attributes(usage: "compatible")
-        def candidate2 = attributes(usage: "no match")
-        def candidate3 = attributes(usage: "best")
-        def candidate4 = attributes()
-        def requested1 = attributes(usage: "requested")
-        def requested2 = attributes()
-
-
-        expect:
-        matcher.matches([candidate1, candidate2, candidate3, candidate4], requested1, null, explanationBuilder) == [candidate3]
-        matcher.matches([candidate1, candidate2, candidate3, candidate4], requested2, null, explanationBuilder) == [candidate1]
-    }
-
-    def "disambiguation rule is presented with all non-null candidate values"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-        def usage = Attribute.of("usage", String)
-        def rule = new AttributeDisambiguationRule<String>() {
-            @Override
-            void execute(MultipleCandidatesDetails details) {
-                if (details.consumerValue == "requested") {
-                    assert details.candidateValues == ["best", "compatible"] as Set
-                    details.closestMatch("best")
-                }
-            }
-        }
-        schema.attribute(usage)
-        schema.accept(usage, "requested", "best")
-        schema.accept(usage, "requested", "compatible")
-        schema.select(usage, rule)
-
-        def candidate1 = attributes(usage: "best")
-        def candidate2 = attributes(usage: "compatible")
-        def candidate3 = attributes()
-        def requested = attributes(usage: "requested")
-
-        expect:
-        matcher.matches([candidate1, candidate2, candidate3], requested, null, explanationBuilder) == [candidate1]
-    }
-
-    def "prefers match with superset of matching attributes"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of('usage', String)
-        schema.attribute(usage)
-        def other = Attribute.of('other', String)
-        schema.attribute(other)
-
-        def candidate1 = attributes(usage: "match")
-        def candidate2 = attributes(usage: "no match")
-        def candidate3 = attributes(other: "match")
-        def candidate4 = attributes()
-        def candidate5 = attributes(usage: "match", other: "match")
-        def candidate6 = attributes(usage: "match")
-        def requested = attributes(usage: "match", other: "match")
-
-        expect:
-        matcher.matches([candidate1, candidate2, candidate3, candidate4, candidate5, candidate6], requested, null, explanationBuilder) == [candidate5]
-        matcher.matches([candidate1, candidate2, candidate3, candidate4, candidate6], requested, null, explanationBuilder) == [candidate1, candidate3, candidate4, candidate6]
-        matcher.matches([candidate1, candidate2, candidate4, candidate6], requested, null, explanationBuilder) == [candidate1, candidate4, candidate6]
-        matcher.matches([candidate2, candidate3, candidate4], requested, null, explanationBuilder) == [candidate3]
-    }
-
-    def "disambiguates multiple matches using extra attributes from producer"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-        def usage = Attribute.of("usage", String)
-        def other = Attribute.of("other", String)
-        schema.attribute(usage)
-        schema.attribute(other)
-        schema.prefer(other, "best")
-
-        def candidate1 = attributes(usage: "match", other: "ignored")
-        def candidate2 = attributes(usage: "match", other: "best")
-        def requested = attributes(usage: "match")
-
-        expect:
-        def matches = matcher.matches([candidate1, candidate2], requested, null, explanationBuilder)
-        matches == [candidate2]
-    }
-
-    def "ignores extra attributes if match is found after disambiguation requested attributes"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of("usage", String)
-        schema.attribute(usage)
-        schema.accept(usage, "foo", "compatible")
-        schema.accept(usage, "foo", "best")
-        schema.prefer(usage, "best")
-
-        def other = Attribute.of("other", String)
-        schema.attribute(other)
-        schema.prefer(other, "best")
-
-        def candidate1 = attributes(usage: "compatible", other: "ignored")
-        def candidate2 = attributes(usage: "best", other: "best")
-        def requested = attributes(usage: "foo")
-
-        expect:
-        def matches = matcher.matches([candidate1, candidate2], requested, null, explanationBuilder)
-        matches == [candidate2]
-    }
-
-    def "empty consumer attributes match any producer attributes"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of("usage", String)
-        schema.attribute(usage)
-
-        def candidate1 = attributes()
-        def candidate2 = attributes(usage: "ignored")
-        def requested = attributes()
-
-        expect:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == [candidate1, candidate2]
-
-        matcher.matches([candidate1], requested, null, explanationBuilder) == [candidate1]
-        matcher.isMatching(candidate1, requested)
-
-        matcher.matches([candidate2], requested, null, explanationBuilder) == [candidate2]
-        matcher.isMatching(candidate2, requested)
-    }
-
-    def "non-empty consumer attributes match empty producer attributes"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def candidate = attributes()
-        def requested = attributes(usage: "dont care", other: "dont care")
-
-        expect:
-        matcher.matches([candidate], requested, null, explanationBuilder) == [candidate]
-        matcher.isMatching(candidate, requested)
-    }
-
-    def "selects fallback when it matches requested and there are no candidates"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of("usage", String)
-        def other = Attribute.of("other", String)
-        def another = Attribute.of("another", String)
-        schema.attribute(usage)
-        schema.attribute(other)
-        schema.attribute(another)
-
-        def candidate1 = attributes(usage: "no match")
-        def candidate2 = attributes(other: "no match")
-        def fallback1 = attributes()
-        def fallback2 = attributes(usage: "match")
-        def fallback3 = attributes(another: "dont care")
-        def fallback4 = attributes(usage: "other")
-        def requested = attributes(usage: "match", other: "match")
-
-        expect:
-        // No candidates, fallback matches
-        matcher.matches([], requested, fallback1, explanationBuilder) == [fallback1]
-        matcher.matches([], requested, fallback2, explanationBuilder) == [fallback2]
-        matcher.matches([], requested, fallback3, explanationBuilder) == [fallback3]
-
-        // Fallback does not match
-        matcher.matches([], requested, fallback4, explanationBuilder) == []
-
-        // Candidates, fallback matches
-        matcher.matches([candidate1, candidate2], requested, fallback1, explanationBuilder) == []
-        matcher.matches([candidate1, candidate2], requested, fallback2, explanationBuilder) == []
-        matcher.matches([candidate1, candidate2], requested, fallback3, explanationBuilder) == []
-
-        // No fallback
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == []
-        matcher.matches([], requested, null, explanationBuilder) == []
-
-        // Fallback also a candidate
-        matcher.matches([candidate1, fallback4], requested, fallback4, explanationBuilder) == []
-        matcher.matches([candidate1, candidate2, fallback1], requested, fallback1, explanationBuilder) == [fallback1]
-    }
-
-    def "can match when consumer uses more general type for attribute"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def consumer = Attribute.of("a", Number)
-        def producer = Attribute.of("a", Integer)
-        schema.attribute(consumer)
-
-        def candidate1 = attributes().attribute(producer, 1)
-        def candidate2 = attributes().attribute(producer, 2)
-        def requested = attributes().attribute(consumer, 1)
-
-        expect:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == [candidate1]
-    }
-
-    def "can match when producer uses desugared attribute of type Named"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def consumer = Attribute.of("a", NamedTestAttribute)
-        def producer = Attribute.of("a", String)
-        schema.attribute(consumer)
-
-        def candidate1 = attributes().attribute(producer, "name1")
-        def candidate2 = attributes().attribute(producer, "name2")
-        def requested = attributes().attribute(consumer, objectFactory().named(NamedTestAttribute, "name1"))
-
-        expect:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == [candidate1]
-    }
-
-    def "can match when consumer uses desugared attribute of type Named"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def consumer = Attribute.of("a", String)
-        def producer = Attribute.of("a", NamedTestAttribute)
-        schema.attribute(consumer)
-
-        def candidate1 = attributes().attribute(producer, objectFactory().named(NamedTestAttribute, "name1"))
-        def candidate2 = attributes().attribute(producer, objectFactory().named(NamedTestAttribute, "name2"))
-        def requested = attributes().attribute(consumer, "name1")
-
-        expect:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == [candidate1]
-    }
-
-    def "can match when producer uses desugared attribute of type Enum"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def consumer = Attribute.of("a", EnumTestAttribute)
-        def producer = Attribute.of("a", String)
-        schema.attribute(consumer)
-
-        def candidate1 = attributes().attribute(producer, "NAME1")
-        def candidate2 = attributes().attribute(producer, "NAME2")
-        def requested = attributes().attribute(consumer, EnumTestAttribute.NAME1)
-
-        expect:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == [candidate1]
-    }
-
-    def "can match when consumer uses desugared attribute of type Enum"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def consumer = Attribute.of("a", String)
-        def producer = Attribute.of("a", EnumTestAttribute)
-        schema.attribute(consumer)
-
-        def candidate1 = attributes().attribute(producer, EnumTestAttribute.NAME1)
-        def candidate2 = attributes().attribute(producer, EnumTestAttribute.NAME2)
-        def requested = attributes().attribute(consumer, "NAME1")
-
-        expect:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder) == [candidate1]
-    }
-
-    def "cannot match when producer uses desugared attribute of unsupported type"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def consumer = Attribute.of("a", NotSerializableInGradleMetadataAttribute)
-        def producer = Attribute.of("a", String)
-        schema.attribute(consumer)
-
-        def candidate1 = attributes().attribute(producer, "name1")
-        def candidate2 = attributes().attribute(producer, "name2")
-        def requested = attributes().attribute(consumer, new NotSerializableInGradleMetadataAttribute("name1"))
-
-        when:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder)
-
-        then:
-        def e = thrown(IllegalArgumentException)
-        e.message == "Unexpected type for attribute 'a' provided. Expected a value of type org.gradle.internal.component.model.ComponentAttributeMatcherTest${'$'}NotSerializableInGradleMetadataAttribute but found a value of type java.lang.String."
-    }
-
-    def "cannot match when consumer uses desugared attribute of unsupported type"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def consumer = Attribute.of("a", String)
-        def producer = Attribute.of("a", NotSerializableInGradleMetadataAttribute)
-        schema.attribute(consumer)
-
-        def candidate1 = attributes().attribute(producer, new NotSerializableInGradleMetadataAttribute("name1"))
-        def candidate2 = attributes().attribute(producer, new NotSerializableInGradleMetadataAttribute("name2"))
-        def requested = attributes().attribute(consumer, "name1")
-
-        when:
-        matcher.matches([candidate1, candidate2], requested, null, explanationBuilder)
-
-        then:
-        def e = thrown(IllegalArgumentException)
-        e.message == "Unexpected type for attribute 'a' provided. Expected a value of type java.lang.String but found a value of type org.gradle.internal.component.model.ComponentAttributeMatcherTest${'$'}NotSerializableInGradleMetadataAttribute."
-    }
-
-    def "matching fails when attribute has incompatible types in consumer and producer"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def consumer = Attribute.of("a", String)
-        def producer = Attribute.of("a", Number)
-        schema.attribute(consumer)
-
-        def candidate = attributes().attribute(producer, 1)
-        def requested = attributes().attribute(consumer, "1")
-
-        when:
-        matcher.matches([candidate], requested, null, explanationBuilder)
-
-        then:
-        def e = thrown(IllegalArgumentException)
-        e.message == "Unexpected type for attribute 'a' provided. Expected a value of type java.lang.String but found a value of type java.lang.Integer."
-    }
-
-    def "prefers a strict match with requested values"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of("usage", String)
-        def other = Attribute.of("other", String)
-        schema.attribute(usage)
-        schema.attribute(other)
-
-        def candidate1 = attributes(usage: 'match')
-        def candidate2 = attributes(usage: 'match', other: 'foo')
-        def requested = attributes(usage: 'match')
-
-        expect:
-        def matches = matcher.matches([candidate1, candidate2], requested, null, explanationBuilder)
-        matches == [candidate1]
-    }
-
-    def "prefers a shorter match with compatible requested values and more than one extra attribute (type: #type)"() {
-        given:
-        def matcher = new ComponentAttributeMatcher(schema)
-
-        def usage = Attribute.of("usage", String)
-        def bundling = Attribute.of("bundling", type)
-        def status = Attribute.of("status", String)
-
-        schema.with {
-            attribute(usage)
-            attribute(bundling)
-            attribute(status)
-            accept(usage, 'java-api', 'java-api-extra')
-            accept(usage, 'java-api', 'java-runtime-extra')
-            prefer(usage, 'java-api-extra')
-        }
-
-        def candidate1 = attributes(usage: 'java-api-extra', status: 'integration')
-        def candidate2 = attributes(usage: 'java-runtime-extra', status: 'integration')
-        def candidate3 = attributes(usage: 'java-api-extra', status: 'integration', bundling: value1)
-        def candidate4 = attributes(usage: 'java-runtime-extra', status: 'integration', bundling: value2)
-        def requested = attributes(usage: 'java-api')
-
-        when:
-        def result = matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, null, explanationBuilder)
-        then:
-        result == [candidate1]
-
-        when: // check with a different attribute order
-        candidate1 = attributes(usage: 'java-api-extra', status: 'integration')
-        candidate2 = attributes(usage: 'java-runtime-extra', status: 'integration')
-        candidate3 = attributes(usage: 'java-api-extra', bundling: value2, status: 'integration')
-        candidate4 = attributes(usage: 'java-runtime-extra', bundling: value1, status: 'integration')
-
-        result = matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, null, explanationBuilder)
-
-        then:
-        result == [candidate1]
-
-        when: // yet another attribute order
-        candidate1 = attributes(status: 'integration', usage: 'java-api-extra')
-        candidate2 = attributes(usage: 'java-runtime-extra', status: 'integration')
-        candidate3 = attributes(bundling: value1, status: 'integration', usage: 'java-api-extra')
-        candidate4 = attributes(status: 'integration', usage: 'java-runtime-extra', bundling: value2)
-
-        result = matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, null, explanationBuilder)
-
-        then:
-        result == [candidate1]
-
-        where:
-        type                | value1        | value2
-        String              | "embedded"    | "embedded"
-        EnumTestAttribute   | "NAME1"       | "NAME2"
-        NamedTestAttribute  | "foo"         | "bar"
-    }
-
-    private AttributeContainerInternal attributes() {
-        factory.mutable()
-    }
-
-    interface NamedTestAttribute extends Named { }
-    enum EnumTestAttribute { NAME1, NAME2 }
-    static class NotSerializableInGradleMetadataAttribute implements Serializable {
-        String name
-
-        NotSerializableInGradleMetadataAttribute(String name) {
-            this.name = name
-        }
-    }
-
-    private static class TestSchema implements AttributeSelectionSchema {
-        Set<Attribute<?>> attributes = []
-        Map<String, Attribute<?>> attributesByName = [:]
-        Map<Attribute<?>, Object> preferredValue = [:]
-        Map<Attribute<?>, AttributeDisambiguationRule> rules = [:]
-        Map<Attribute<?>, Multimap<Object, Object>> compatibleValues = [:]
-
-        void attribute(Attribute<?> attribute) {
-            attributes.add(attribute)
-            attributesByName.put(attribute.getName(), attribute)
-        }
-
-        void accept(Attribute<?> attribute, Object consumer, Object producer) {
-            if (!compatibleValues.containsKey(attribute)) {
-                compatibleValues.put(attribute, LinkedListMultimap.create())
-            }
-            compatibleValues.get(attribute).put(consumer, producer)
-        }
-
-        void select(Attribute<?> attribute, AttributeDisambiguationRule rule) {
-            rules.put(attribute, rule)
-        }
-
-        void prefer(Attribute<?> attribute, Object value) {
-            preferredValue.put(attribute, value)
-        }
-
-        @Override
-        boolean hasAttribute(Attribute<?> attribute) {
-            return attributes.contains(attribute)
-        }
-
-        @Override
-        Attribute<?> getAttribute(String name) {
-            return attributesByName.get(name)
-        }
-
-        @Override
-        boolean matchValue(Attribute<?> attribute, Object requested, Object candidate) {
-            if (attributes.contains(attribute)) {
-                if (compatibleValues.containsKey(attribute)) {
-                    if (compatibleValues.get(attribute).get(requested).contains(candidate)) {
-                        return true
-                    }
-                }
-                if (requested == candidate) {
-                    return true
-                }
-            }
-
-            return false
-        }
-
-        @Override
-        Set<Object> disambiguate(Attribute<?> attribute, Object requested, Set<Object> candidates) {
-            def result = new DefaultMultipleCandidateResult(requested, candidates)
-
-            def rule = rules.get(attribute)
-            if (rule != null) {
-                rule.execute(result)
-                return result.matches
-            }
-
-            def preferred = preferredValue.get(attribute)
-            if (preferred != null && candidates.contains(preferred)) {
-                return [preferred]
-            }
-
-            null
-        }
-
-        @Override
-        Attribute<?>[] collectExtraAttributes(ImmutableAttributes[] candidates, ImmutableAttributes requested) {
-            AttributeSelectionUtils.collectExtraAttributes(this, candidates, requested)
-        }
-
-        @Override
-        PrecedenceResult orderByPrecedence(Collection<Attribute<?>> requested) {
-            return new PrecedenceResult(IntStream.range(0, requested.size()).boxed().collect(Collectors.toList()))
-        }
-    }
-}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultAttributeMatcherTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultAttributeMatcherTest.groovy
new file mode 100644
index 0000000..95593e5
--- /dev/null
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/DefaultAttributeMatcherTest.groovy
@@ -0,0 +1,626 @@
+/*
+ * Copyright 2016 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.component.model
+
+import com.google.common.collect.LinkedListMultimap
+import com.google.common.collect.Multimap
+import org.gradle.api.Named
+import org.gradle.api.attributes.Attribute
+import org.gradle.api.attributes.AttributeDisambiguationRule
+import org.gradle.api.attributes.MultipleCandidatesDetails
+import org.gradle.api.internal.attributes.AttributeContainerInternal
+import org.gradle.api.internal.attributes.ImmutableAttributes
+import org.gradle.util.AttributeTestUtil
+import spock.lang.Specification
+
+import java.util.stream.Collectors
+import java.util.stream.IntStream
+
+import static org.gradle.util.AttributeTestUtil.attributes
+import static org.gradle.util.TestUtil.objectFactory
+
+class DefaultAttributeMatcherTest extends Specification {
+
+    def schema = new TestSchema()
+    def factory = AttributeTestUtil.attributesFactory()
+    def explanationBuilder = Stub(AttributeMatchingExplanationBuilder)
+
+    def "selects candidate with same set of attributes and whose values match"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of('usage', String)
+        schema.attribute(usage)
+
+        def candidate1 = attributes(usage: "match")
+        def candidate2 = attributes(usage: "no match")
+        def requested = attributes(usage: "match")
+
+        expect:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder) == [candidate1]
+        matcher.matches([candidate2], requested, explanationBuilder) == []
+
+        matcher.isMatching(candidate1, requested)
+        !matcher.isMatching(candidate2, requested)
+    }
+
+    def "selects candidate with subset of attributes and whose values match"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of('usage', String)
+        schema.attribute(usage)
+        def other = Attribute.of('other', String)
+        schema.attribute(other)
+
+        def candidate1 = attributes(usage: "match")
+        def candidate2 = attributes(usage: "no match")
+        def candidate3 = attributes(usage: "match", other: "no match")
+        def candidate4 = attributes()
+
+        def requested = attributes(usage: "match", other: "match")
+
+        expect:
+        matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, explanationBuilder) == [candidate1]
+        matcher.matches([candidate2, candidate3, candidate4], requested, explanationBuilder) == [candidate4]
+
+        matcher.isMatching(candidate1, requested)
+        !matcher.isMatching(candidate2, requested)
+        !matcher.isMatching(candidate3, requested)
+        matcher.isMatching(candidate4, requested)
+    }
+
+    def "selects candidate with additional attributes and whose values match"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of('usage', String)
+        schema.attribute(usage)
+        def other = Attribute.of('other', String)
+        schema.attribute(other)
+
+        def candidate1 = attributes(usage: "match", other: "dont care")
+        def candidate2 = attributes(usage: "no match")
+        def requested = attributes(usage: "match")
+
+        expect:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder) == [candidate1]
+        matcher.matches([candidate2], requested, explanationBuilder) == []
+
+        matcher.isMatching(candidate1, requested)
+        !matcher.isMatching(candidate2, requested)
+    }
+
+    def "selects multiple candidates with compatible values"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of('usage', String)
+        schema.attribute(usage)
+        def other = Attribute.of('other', String)
+        schema.attribute(other)
+
+        def candidate1 = attributes(usage: "match")
+        def candidate2 = attributes(usage: "no match")
+        def candidate3 = attributes(other: "match")
+        def candidate4 = attributes()
+        def candidate5 = attributes(usage: "match")
+
+        def requested = attributes(usage: "match", other: "match")
+
+        expect:
+        matcher.matches([candidate1, candidate2, candidate3, candidate4, candidate5], requested, explanationBuilder) == [candidate1, candidate3, candidate4, candidate5]
+    }
+
+    def "applies disambiguation rules and selects intersection of best matches for each attribute"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of('usage', String)
+        schema.attribute(usage)
+        schema.accept(usage, "requested", "compatible")
+        schema.accept(usage, "requested", "best")
+        schema.prefer(usage, "best")
+        def other = Attribute.of('other', String)
+        schema.attribute(other)
+        schema.accept(other, "requested", "compatible")
+        schema.accept(other, "requested", "best")
+        schema.prefer(other, "best")
+
+        def candidate1 = attributes(usage: "best", other: "compatible")
+        def candidate2 = attributes(usage: "no match", other: "no match")
+        def candidate3 = attributes(usage: "compatible", other: "best")
+        def candidate4 = attributes()
+        def candidate5 = attributes(usage: "best", other: "best")
+
+        def requested = attributes(usage: "requested", other: "requested")
+
+        expect:
+        matcher.matches([candidate1, candidate2, candidate3, candidate4, candidate5], requested, explanationBuilder) == [candidate5]
+        matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, explanationBuilder) == [candidate1, candidate3, candidate4]
+        matcher.matches([candidate1, candidate2, candidate4], requested, explanationBuilder) == [candidate1]
+        matcher.matches([candidate2, candidate4], requested, explanationBuilder) == [candidate4]
+    }
+
+    def "rule can disambiguate based on requested value"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of('usage', String)
+        def rule = new AttributeDisambiguationRule<String>() {
+            @Override
+            void execute(MultipleCandidatesDetails details) {
+                if (details.consumerValue == null) {
+                    details.closestMatch("compatible")
+                } else if (details.consumerValue == "requested") {
+                    details.closestMatch("best")
+                }
+            }
+        }
+        schema.attribute(usage)
+        schema.accept(usage, "requested", "compatible")
+        schema.accept(usage, "requested", "best")
+        schema.select(usage, rule)
+
+        def candidate1 = attributes(usage: "compatible")
+        def candidate2 = attributes(usage: "no match")
+        def candidate3 = attributes(usage: "best")
+        def candidate4 = attributes()
+        def requested1 = attributes(usage: "requested")
+        def requested2 = attributes()
+
+
+        expect:
+        matcher.matches([candidate1, candidate2, candidate3, candidate4], requested1, explanationBuilder) == [candidate3]
+        matcher.matches([candidate1, candidate2, candidate3, candidate4], requested2, explanationBuilder) == [candidate1]
+    }
+
+    def "disambiguation rule is presented with all non-null candidate values"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+        def usage = Attribute.of("usage", String)
+        def rule = new AttributeDisambiguationRule<String>() {
+            @Override
+            void execute(MultipleCandidatesDetails details) {
+                if (details.consumerValue == "requested") {
+                    assert details.candidateValues == ["best", "compatible"] as Set
+                    details.closestMatch("best")
+                }
+            }
+        }
+        schema.attribute(usage)
+        schema.accept(usage, "requested", "best")
+        schema.accept(usage, "requested", "compatible")
+        schema.select(usage, rule)
+
+        def candidate1 = attributes(usage: "best")
+        def candidate2 = attributes(usage: "compatible")
+        def candidate3 = attributes()
+        def requested = attributes(usage: "requested")
+
+        expect:
+        matcher.matches([candidate1, candidate2, candidate3], requested, explanationBuilder) == [candidate1]
+    }
+
+    def "prefers match with superset of matching attributes"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of('usage', String)
+        schema.attribute(usage)
+        def other = Attribute.of('other', String)
+        schema.attribute(other)
+
+        def candidate1 = attributes(usage: "match")
+        def candidate2 = attributes(usage: "no match")
+        def candidate3 = attributes(other: "match")
+        def candidate4 = attributes()
+        def candidate5 = attributes(usage: "match", other: "match")
+        def candidate6 = attributes(usage: "match")
+        def requested = attributes(usage: "match", other: "match")
+
+        expect:
+        matcher.matches([candidate1, candidate2, candidate3, candidate4, candidate5, candidate6], requested, explanationBuilder) == [candidate5]
+        matcher.matches([candidate1, candidate2, candidate3, candidate4, candidate6], requested, explanationBuilder) == [candidate1, candidate3, candidate4, candidate6]
+        matcher.matches([candidate1, candidate2, candidate4, candidate6], requested, explanationBuilder) == [candidate1, candidate4, candidate6]
+        matcher.matches([candidate2, candidate3, candidate4], requested, explanationBuilder) == [candidate3]
+    }
+
+    def "disambiguates multiple matches using extra attributes from producer"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+        def usage = Attribute.of("usage", String)
+        def other = Attribute.of("other", String)
+        schema.attribute(usage)
+        schema.attribute(other)
+        schema.prefer(other, "best")
+
+        def candidate1 = attributes(usage: "match", other: "ignored")
+        def candidate2 = attributes(usage: "match", other: "best")
+        def requested = attributes(usage: "match")
+
+        expect:
+        def matches = matcher.matches([candidate1, candidate2], requested, explanationBuilder)
+        matches == [candidate2]
+    }
+
+    def "ignores extra attributes if match is found after disambiguation requested attributes"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of("usage", String)
+        schema.attribute(usage)
+        schema.accept(usage, "foo", "compatible")
+        schema.accept(usage, "foo", "best")
+        schema.prefer(usage, "best")
+
+        def other = Attribute.of("other", String)
+        schema.attribute(other)
+        schema.prefer(other, "best")
+
+        def candidate1 = attributes(usage: "compatible", other: "ignored")
+        def candidate2 = attributes(usage: "best", other: "best")
+        def requested = attributes(usage: "foo")
+
+        expect:
+        def matches = matcher.matches([candidate1, candidate2], requested, explanationBuilder)
+        matches == [candidate2]
+    }
+
+    def "empty consumer attributes match any producer attributes"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of("usage", String)
+        schema.attribute(usage)
+
+        def candidate1 = attributes()
+        def candidate2 = attributes(usage: "ignored")
+        def requested = attributes()
+
+        expect:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder) == [candidate1, candidate2]
+
+        matcher.matches([candidate1], requested, explanationBuilder) == [candidate1]
+        matcher.isMatching(candidate1, requested)
+
+        matcher.matches([candidate2], requested, explanationBuilder) == [candidate2]
+        matcher.isMatching(candidate2, requested)
+    }
+
+    def "non-empty consumer attributes match empty producer attributes"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def candidate = attributes()
+        def requested = attributes(usage: "dont care", other: "dont care")
+
+        expect:
+        matcher.matches([candidate], requested, explanationBuilder) == [candidate]
+        matcher.isMatching(candidate, requested)
+    }
+
+    def "can match when consumer uses more general type for attribute"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def consumer = Attribute.of("a", Number)
+        def producer = Attribute.of("a", Integer)
+        schema.attribute(consumer)
+
+        def candidate1 = attributes().attribute(producer, 1)
+        def candidate2 = attributes().attribute(producer, 2)
+        def requested = attributes().attribute(consumer, 1)
+
+        expect:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder) == [candidate1]
+    }
+
+    def "can match when producer uses desugared attribute of type Named"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def consumer = Attribute.of("a", NamedTestAttribute)
+        def producer = Attribute.of("a", String)
+        schema.attribute(consumer)
+
+        def candidate1 = attributes().attribute(producer, "name1")
+        def candidate2 = attributes().attribute(producer, "name2")
+        def requested = attributes().attribute(consumer, objectFactory().named(NamedTestAttribute, "name1"))
+
+        expect:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder) == [candidate1]
+    }
+
+    def "can match when consumer uses desugared attribute of type Named"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def consumer = Attribute.of("a", String)
+        def producer = Attribute.of("a", NamedTestAttribute)
+        schema.attribute(consumer)
+
+        def candidate1 = attributes().attribute(producer, objectFactory().named(NamedTestAttribute, "name1"))
+        def candidate2 = attributes().attribute(producer, objectFactory().named(NamedTestAttribute, "name2"))
+        def requested = attributes().attribute(consumer, "name1")
+
+        expect:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder) == [candidate1]
+    }
+
+    def "can match when producer uses desugared attribute of type Enum"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def consumer = Attribute.of("a", EnumTestAttribute)
+        def producer = Attribute.of("a", String)
+        schema.attribute(consumer)
+
+        def candidate1 = attributes().attribute(producer, "NAME1")
+        def candidate2 = attributes().attribute(producer, "NAME2")
+        def requested = attributes().attribute(consumer, EnumTestAttribute.NAME1)
+
+        expect:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder) == [candidate1]
+    }
+
+    def "can match when consumer uses desugared attribute of type Enum"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def consumer = Attribute.of("a", String)
+        def producer = Attribute.of("a", EnumTestAttribute)
+        schema.attribute(consumer)
+
+        def candidate1 = attributes().attribute(producer, EnumTestAttribute.NAME1)
+        def candidate2 = attributes().attribute(producer, EnumTestAttribute.NAME2)
+        def requested = attributes().attribute(consumer, "NAME1")
+
+        expect:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder) == [candidate1]
+    }
+
+    def "cannot match when producer uses desugared attribute of unsupported type"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def consumer = Attribute.of("a", NotSerializableInGradleMetadataAttribute)
+        def producer = Attribute.of("a", String)
+        schema.attribute(consumer)
+
+        def candidate1 = attributes().attribute(producer, "name1")
+        def candidate2 = attributes().attribute(producer, "name2")
+        def requested = attributes().attribute(consumer, new NotSerializableInGradleMetadataAttribute("name1"))
+
+        when:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder)
+
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message == "Unexpected type for attribute 'a' provided. Expected a value of type org.gradle.internal.component.model.DefaultAttributeMatcherTest\$NotSerializableInGradleMetadataAttribute but found a value of type java.lang.String."
+    }
+
+    def "cannot match when consumer uses desugared attribute of unsupported type"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def consumer = Attribute.of("a", String)
+        def producer = Attribute.of("a", NotSerializableInGradleMetadataAttribute)
+        schema.attribute(consumer)
+
+        def candidate1 = attributes().attribute(producer, new NotSerializableInGradleMetadataAttribute("name1"))
+        def candidate2 = attributes().attribute(producer, new NotSerializableInGradleMetadataAttribute("name2"))
+        def requested = attributes().attribute(consumer, "name1")
+
+        when:
+        matcher.matches([candidate1, candidate2], requested, explanationBuilder)
+
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message == "Unexpected type for attribute 'a' provided. Expected a value of type java.lang.String but found a value of type org.gradle.internal.component.model.DefaultAttributeMatcherTest\$NotSerializableInGradleMetadataAttribute."
+    }
+
+    def "matching fails when attribute has incompatible types in consumer and producer"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def consumer = Attribute.of("a", String)
+        def producer = Attribute.of("a", Number)
+        schema.attribute(consumer)
+
+        def candidate = attributes().attribute(producer, 1)
+        def requested = attributes().attribute(consumer, "1")
+
+        when:
+        matcher.matches([candidate], requested, explanationBuilder)
+
+        then:
+        def e = thrown(IllegalArgumentException)
+        e.message == "Unexpected type for attribute 'a' provided. Expected a value of type java.lang.String but found a value of type java.lang.Integer."
+    }
+
+    def "prefers a strict match with requested values"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of("usage", String)
+        def other = Attribute.of("other", String)
+        schema.attribute(usage)
+        schema.attribute(other)
+
+        def candidate1 = attributes(usage: 'match')
+        def candidate2 = attributes(usage: 'match', other: 'foo')
+        def requested = attributes(usage: 'match')
+
+        expect:
+        def matches = matcher.matches([candidate1, candidate2], requested, explanationBuilder)
+        matches == [candidate1]
+    }
+
+    def "prefers a shorter match with compatible requested values and more than one extra attribute (type: #type)"() {
+        given:
+        def matcher = new DefaultAttributeMatcher(schema)
+
+        def usage = Attribute.of("usage", String)
+        def bundling = Attribute.of("bundling", type)
+        def status = Attribute.of("status", String)
+
+        schema.with {
+            attribute(usage)
+            attribute(bundling)
+            attribute(status)
+            accept(usage, 'java-api', 'java-api-extra')
+            accept(usage, 'java-api', 'java-runtime-extra')
+            prefer(usage, 'java-api-extra')
+        }
+
+        def candidate1 = attributes(usage: 'java-api-extra', status: 'integration')
+        def candidate2 = attributes(usage: 'java-runtime-extra', status: 'integration')
+        def candidate3 = attributes(usage: 'java-api-extra', status: 'integration', bundling: value1)
+        def candidate4 = attributes(usage: 'java-runtime-extra', status: 'integration', bundling: value2)
+        def requested = attributes(usage: 'java-api')
+
+        when:
+        def result = matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, explanationBuilder)
+        then:
+        result == [candidate1]
+
+        when: // check with a different attribute order
+        candidate1 = attributes(usage: 'java-api-extra', status: 'integration')
+        candidate2 = attributes(usage: 'java-runtime-extra', status: 'integration')
+        candidate3 = attributes(usage: 'java-api-extra', bundling: value2, status: 'integration')
+        candidate4 = attributes(usage: 'java-runtime-extra', bundling: value1, status: 'integration')
+
+        result = matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, explanationBuilder)
+
+        then:
+        result == [candidate1]
+
+        when: // yet another attribute order
+        candidate1 = attributes(status: 'integration', usage: 'java-api-extra')
+        candidate2 = attributes(usage: 'java-runtime-extra', status: 'integration')
+        candidate3 = attributes(bundling: value1, status: 'integration', usage: 'java-api-extra')
+        candidate4 = attributes(status: 'integration', usage: 'java-runtime-extra', bundling: value2)
+
+        result = matcher.matches([candidate1, candidate2, candidate3, candidate4], requested, explanationBuilder)
+
+        then:
+        result == [candidate1]
+
+        where:
+        type                | value1        | value2
+        String              | "embedded"    | "embedded"
+        EnumTestAttribute   | "NAME1"       | "NAME2"
+        NamedTestAttribute  | "foo"         | "bar"
+    }
+
+    private AttributeContainerInternal attributes() {
+        factory.mutable()
+    }
+
+    interface NamedTestAttribute extends Named { }
+    enum EnumTestAttribute { NAME1, NAME2 }
+    static class NotSerializableInGradleMetadataAttribute implements Serializable {
+        String name
+
+        NotSerializableInGradleMetadataAttribute(String name) {
+            this.name = name
+        }
+    }
+
+    private static class TestSchema implements AttributeSelectionSchema {
+        Set<Attribute<?>> attributes = []
+        Map<String, Attribute<?>> attributesByName = [:]
+        Map<Attribute<?>, Object> preferredValue = [:]
+        Map<Attribute<?>, AttributeDisambiguationRule> rules = [:]
+        Map<Attribute<?>, Multimap<Object, Object>> compatibleValues = [:]
+
+        void attribute(Attribute<?> attribute) {
+            attributes.add(attribute)
+            attributesByName.put(attribute.getName(), attribute)
+        }
+
+        void accept(Attribute<?> attribute, Object consumer, Object producer) {
+            if (!compatibleValues.containsKey(attribute)) {
+                compatibleValues.put(attribute, LinkedListMultimap.create())
+            }
+            compatibleValues.get(attribute).put(consumer, producer)
+        }
+
+        void select(Attribute<?> attribute, AttributeDisambiguationRule rule) {
+            rules.put(attribute, rule)
+        }
+
+        void prefer(Attribute<?> attribute, Object value) {
+            preferredValue.put(attribute, value)
+        }
+
+        @Override
+        boolean hasAttribute(Attribute<?> attribute) {
+            return attributes.contains(attribute)
+        }
+
+        @Override
+        Attribute<?> getAttribute(String name) {
+            return attributesByName.get(name)
+        }
+
+        @Override
+        boolean matchValue(Attribute<?> attribute, Object requested, Object candidate) {
+            if (attributes.contains(attribute)) {
+                if (compatibleValues.containsKey(attribute)) {
+                    if (compatibleValues.get(attribute).get(requested).contains(candidate)) {
+                        return true
+                    }
+                }
+                if (requested == candidate) {
+                    return true
+                }
+            }
+
+            return false
+        }
+
+        @Override
+        Set<Object> disambiguate(Attribute<?> attribute, Object requested, Set<Object> candidates) {
+            def result = new DefaultMultipleCandidateResult(requested, candidates)
+
+            def rule = rules.get(attribute)
+            if (rule != null) {
+                rule.execute(result)
+                return result.matches
+            }
+
+            def preferred = preferredValue.get(attribute)
+            if (preferred != null && candidates.contains(preferred)) {
+                return [preferred]
+            }
+
+            null
+        }
+
+        @Override
+        Attribute<?>[] collectExtraAttributes(ImmutableAttributes[] candidates, ImmutableAttributes requested) {
+            AttributeSelectionUtils.collectExtraAttributes(this, candidates, requested)
+        }
+
+        @Override
+        PrecedenceResult orderByPrecedence(Collection<Attribute<?>> requested) {
+            return new PrecedenceResult(IntStream.range(0, requested.size()).boxed().collect(Collectors.toList()))
+        }
+    }
+}
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/JavaEcosystemAttributeMatcherTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/JavaEcosystemAttributeMatcherTest.groovy
index 2b013d7..5588273 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/JavaEcosystemAttributeMatcherTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/JavaEcosystemAttributeMatcherTest.groovy
@@ -320,7 +320,7 @@
     def matchConfigurations(List<List<AttributeContainerInternal>> candidates, AttributeContainerInternal requested) {
         // The first element in each configuration array is the implicit variant.
         def implicitVariants = candidates.collect { it.first() }
-        def configurationMatches = schema.matcher().matches(implicitVariants, requested, null, explanationBuilder)
+        def configurationMatches = schema.matcher().matches(implicitVariants, requested, explanationBuilder)
 
         // This test is checking only for successful (single) matches. If we matched multiple configurations
         // in the first round, something is wrong here. Fail before attempting the second round of variant matching.
@@ -328,7 +328,7 @@
 
         // Get all the variants for the configuration which was selected and apply variant matching on them.
         def configurationVariants = candidates.get(implicitVariants.indexOf(configurationMatches.get(0)))
-        def variantMatches = schema.matcher().matches(configurationVariants, requested, null, explanationBuilder)
+        def variantMatches = schema.matcher().matches(configurationVariants, requested, explanationBuilder)
 
         // Once again, the purpose of this test is for successful results. Something is wrong if we have
         // multiple matched variants.
diff --git a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/LocalComponentDependencyMetadataTest.groovy b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/LocalComponentDependencyMetadataTest.groovy
index 65c9b32..3f5dc36 100644
--- a/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/LocalComponentDependencyMetadataTest.groovy
+++ b/subprojects/dependency-management/src/test/groovy/org/gradle/internal/component/model/LocalComponentDependencyMetadataTest.groovy
@@ -16,7 +16,6 @@
 
 package org.gradle.internal.component.model
 
-import com.google.common.base.Optional
 import com.google.common.collect.ImmutableList
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.VersionConstraint
@@ -32,7 +31,6 @@
 import org.gradle.api.internal.attributes.AttributeContainerInternal
 import org.gradle.api.internal.attributes.AttributesSchemaInternal
 import org.gradle.api.internal.attributes.DefaultAttributesSchema
-import org.gradle.api.internal.attributes.EmptySchema
 import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory
 import org.gradle.api.internal.capabilities.CapabilitiesMetadataInternal
@@ -77,55 +75,30 @@
 
     def "selects the target configuration from target component"() {
         def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, "to", [] as List, [], false, false, true, false, false, null)
-        def toComponent = Stub(ComponentGraphResolveMetadata)
-        def toState = Stub(ComponentGraphResolveState) {
-            getMetadata() >> toComponent
-        }
-        def toConfig = Stub(ConfigurationGraphResolveMetadata) {
-            isCanBeConsumed() >> true
-            getAttributes() >> attributes([:])
-        }
-
-        given:
-        toComponent.getConfiguration("to") >> toConfig
+        def toComponent = Stub(ComponentGraphResolveState)
+        def toConfig = consumableConfiguration(toComponent, "to")
 
         expect:
-        dep.selectVariants(attributes([:]), toState, attributesSchema, [] as Set).variants == [toConfig]
+        dep.selectVariants(attributes([:]), toComponent, attributesSchema, [] as Set).variants == [toConfig]
     }
 
-    @Unroll("selects configuration '#expected' from target component (#scenario)")
-    def "selects the target configuration from target component which matches the attributes"() {
+    @Unroll("selects variant '#expected' from target component (#scenario)")
+    def "selects the variant from target component that matches the attributes"() {
         def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, null, [] as List, [], false, false, true, false, false, null)
-        def defaultConfig = defaultConfiguration()
-        def toFooConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'foo'
-            getAttributes() >> attributes(key: 'something')
-            isCanBeConsumed() >> true
-            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
+        def toFooVariant = variant('foo', attributes(key: 'something'))
+        def toBarVariant = variant('bar', attributes(key: 'something else'))
+        def toCandidates = Stub(GraphSelectionCandidates) {
+            useVariants >> true
+            variants >> ImmutableList.of(toFooVariant, toBarVariant)
         }
-        def toBarConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'bar'
-            getAttributes() >> attributes(key: 'something else')
-            isCanBeConsumed() >> true
-            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
-        }
-        def toComponent = Stub(ComponentGraphResolveMetadata) {
-            getVariantsForGraphTraversal() >> Optional.of(ImmutableList.of(toFooConfig, toBarConfig))
-            getAttributesSchema() >> EmptySchema.INSTANCE
-        }
-        def toState = Stub(ComponentGraphResolveState) {
-            getMetadata() >> toComponent
+        def toComponent = Stub(ComponentGraphResolveState) {
+            getCandidatesForGraphVariantSelection() >> toCandidates
         }
         attributesSchema.attribute(Attribute.of('key', String))
         attributesSchema.attribute(Attribute.of('extra', String))
 
-        given:
-        toComponent.getConfiguration("default") >> defaultConfig
-        toComponent.getConfiguration("foo") >> toFooConfig
-        toComponent.getConfiguration("bar") >> toBarConfig
-
         expect:
-        dep.selectVariants(attributes(queryAttributes), toState, attributesSchema, [] as Set).variants.name as Set == [expected] as Set
+        dep.selectVariants(attributes(queryAttributes), toComponent, attributesSchema, [] as Set).variants.name as Set == [expected] as Set
 
         where:
         scenario                                         | queryAttributes                 | expected
@@ -136,28 +109,20 @@
 
     def "revalidates default configuration if it has attributes"() {
         def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, Dependency.DEFAULT_CONFIGURATION, [] as List, [], false, false, true, false, false, null)
-        def defaultConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'default'
-            isCanBeConsumed() >> true
-            getAttributes() >> attributes(key: 'nothing')
-        }
-        def toComponent = Stub(ComponentGraphResolveMetadata) {
-            getAttributesSchema() >> attributesSchema
+        def toComponentMetadata = Stub(ComponentGraphResolveMetadata) {
             getId() >> Stub(ComponentIdentifier) {
                 getDisplayName() >> "[target]"
             }
         }
-        def toState = Stub(ComponentGraphResolveState) {
-            getMetadata() >> toComponent
+        def toComponent = Stub(ComponentGraphResolveState) {
+            getMetadata() >> toComponentMetadata
         }
+        defaultConfiguration(toComponent, attributes(key: 'nothing'))
         attributesSchema.attribute(Attribute.of('key', String))
         attributesSchema.attribute(Attribute.of('will', String))
 
-        given:
-        toComponent.getConfiguration("default") >> defaultConfig
-
         when:
-        dep.selectVariants(attributes(key: 'other'), toState, attributesSchema, [] as Set)*.name as Set
+        dep.selectVariants(attributes(key: 'other'), toComponent, attributesSchema, [] as Set)*.name as Set
 
         then:
         def e = thrown(IncompatibleConfigurationSelectionException)
@@ -168,36 +133,20 @@
 
     def "revalidates explicit configuration selection if it has attributes"() {
         def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, 'bar', [] as List, [], false, false, true, false, false, null)
-        def defaultConfig = defaultConfiguration()
-        def toFooConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'foo'
-            getAttributes() >> attributes(key: 'something')
-            isCanBeConsumed() >> true
-        }
-        def toBarConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'bar'
-            getAttributes() >> attributes(key: 'something else')
-            isCanBeConsumed() >> true
-        }
-        def toComponent = Stub(ComponentGraphResolveMetadata) {
+        def toComponentMetadata = Stub(ComponentGraphResolveMetadata) {
             getId() >> Stub(ComponentIdentifier) {
                 getDisplayName() >> "[target]"
             }
-            getAttributesSchema() >> EmptySchema.INSTANCE
         }
-        def toState = Stub(ComponentGraphResolveState) {
-            getMetadata() >> toComponent
+        def toComponent = Stub(ComponentGraphResolveState) {
+            getMetadata() >> toComponentMetadata
         }
+        consumableConfiguration(toComponent, 'bar', attributes(key: 'something else'))
 
         attributesSchema.attribute(Attribute.of('key', String))
 
-        given:
-        toComponent.getConfiguration("default") >> defaultConfig
-        toComponent.getConfiguration("foo") >> toFooConfig
-        toComponent.getConfiguration("bar") >> toBarConfig
-
         when:
-        dep.selectVariants(attributes(key: 'something'), toState, attributesSchema, [] as Set)*.name as Set
+        dep.selectVariants(attributes(key: 'something'), toComponent, attributesSchema, [] as Set)*.name as Set
 
         then:
         def e = thrown(IncompatibleConfigurationSelectionException)
@@ -206,31 +155,23 @@
   - Incompatible because this component declares attribute 'key' with value 'something else' and the consumer needed attribute 'key' with value 'something'""")
     }
 
-    @Unroll("selects configuration '#expected' from target component with Java proximity matching strategy (#scenario)")
-    def "selects the target configuration from target component with Java proximity matching strategy"() {
+    @Unroll("selects variant '#expected' from target component with Java proximity matching strategy (#scenario)")
+    def "selects the variant from target component with Java proximity matching strategy"() {
         def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, null, [] as List, [], false, false, true, false, false, null)
-        def defaultConfig = defaultConfiguration()
-        def toFooConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'foo'
-            getAttributes() >> attributes(fooAttributes)
-            isCanBeConsumed() >> true
-            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
-        }
-        def toBarConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'bar'
-            getAttributes() >> attributes(barAttributes)
-            isCanBeConsumed() >> true
-            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
-        }
-        def toComponent = Stub(ComponentGraphResolveMetadata) {
-            getVariantsForGraphTraversal() >> Optional.of(ImmutableList.of(toFooConfig, toBarConfig))
-            getAttributesSchema() >> attributesSchema
+        def toFooVariant = variant('foo', attributes(fooAttributes))
+        def toBarVariant = variant('bar', attributes(barAttributes))
+        def toComponentMetadata = Stub(ComponentGraphResolveMetadata) {
             getId() >> Stub(ComponentIdentifier) {
                 getDisplayName() >> "[target]"
             }
         }
-        def toState = Stub(ComponentGraphResolveState) {
-            getMetadata() >> toComponent
+        def toCandidates = Stub(GraphSelectionCandidates) {
+            useVariants >> true
+            variants >> ImmutableList.of(toFooVariant, toBarVariant)
+        }
+        def toComponent = Stub(ComponentGraphResolveState) {
+            getMetadata() >> toComponentMetadata
+            getCandidatesForGraphVariantSelection() >> toCandidates
         }
         attributesSchema.attribute(Attribute.of('platform', JavaVersion), {
             it.ordered { a, b -> a <=> b }
@@ -239,14 +180,9 @@
         attributesSchema.attribute(Attribute.of('flavor', String))
         attributesSchema.attribute(Attribute.of('extra', String))
 
-        given:
-        toComponent.getConfiguration("default") >> defaultConfig
-        toComponent.getConfiguration("foo") >> toFooConfig
-        toComponent.getConfiguration("bar") >> toBarConfig
-
         expect:
         try {
-            def result = dep.selectVariants(attributes(queryAttributes), toState, attributesSchema, [] as Set).variants.name as Set
+            def result = dep.selectVariants(attributes(queryAttributes), toComponent, attributesSchema, [] as Set).variants.name as Set
             if (expected == null && result) {
                 throw new AssertionError("Expected an ambiguous result, but got $result")
             }
@@ -279,31 +215,23 @@
 
     }
 
-    @Unroll("selects configuration '#expected' from target component with Java proximity matching strategy using short-hand notation (#scenario)")
-    def "selects the target configuration from target component with Java proximity matching strategy using short-hand notation"() {
+    @Unroll("selects variant '#expected' from target component with Java proximity matching strategy using short-hand notation (#scenario)")
+    def "selects variant from target component with Java proximity matching strategy using short-hand notation"() {
         def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, null, [] as List, [], false, false, true, false, false, null)
-        def defaultConfig = defaultConfiguration()
-        def toFooConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'foo'
-            getAttributes() >> attributes(fooAttributes)
-            isCanBeConsumed() >> true
-            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
-        }
-        def toBarConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'bar'
-            getAttributes() >> attributes(barAttributes)
-            isCanBeConsumed() >> true
-            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
-        }
-        def toComponent = Stub(ComponentGraphResolveMetadata) {
-            getVariantsForGraphTraversal() >> Optional.of(ImmutableList.of(toFooConfig, toBarConfig))
-            getAttributesSchema() >> attributesSchema
+        def toFooVariant = variant('foo', attributes(fooAttributes))
+        def toBarVariant = variant('bar', attributes(barAttributes))
+        def toComponentMetadata = Stub(ComponentGraphResolveMetadata) {
             getId() >> Stub(ComponentIdentifier) {
                 getDisplayName() >> "[target]"
             }
         }
-        def toState = Stub(ComponentGraphResolveState) {
-            getMetadata() >> toComponent
+        def toCandidates = Stub(GraphSelectionCandidates) {
+            useVariants >> true
+            variants >> ImmutableList.of(toFooVariant, toBarVariant)
+        }
+        def toComponent = Stub(ComponentGraphResolveState) {
+            getMetadata() >> toComponentMetadata
+            getCandidatesForGraphVariantSelection() >> toCandidates
         }
         attributesSchema.attribute(Attribute.of('platform', JavaVersion), {
             it.ordered { a, b -> a <=> b }
@@ -312,14 +240,9 @@
         attributesSchema.attribute(Attribute.of('flavor', String))
         attributesSchema.attribute(Attribute.of('extra', String))
 
-        given:
-        toComponent.getConfiguration("default") >> defaultConfig
-        toComponent.getConfiguration("foo") >> toFooConfig
-        toComponent.getConfiguration("bar") >> toBarConfig
-
         expect:
         try {
-            def result = dep.selectVariants(attributes(queryAttributes), toState, attributesSchema, [] as Set).variants.name as Set
+            def result = dep.selectVariants(attributes(queryAttributes), toComponent, attributesSchema, [] as Set).variants.name as Set
             if (expected == null && result) {
                 throw new AssertionError("Expected an ambiguous result, but got $result")
             }
@@ -362,7 +285,7 @@
         }
 
         given:
-        toComponent.getConfiguration("to") >> null
+        toState.getConfiguration("to") >> null
 
         when:
         dep.selectVariants(attributes([:]), toState, attributesSchema,[] as Set)
@@ -404,6 +327,7 @@
             }
         }
     }
+
     static class ValueCompatibleRule implements AttributeCompatibilityRule<String> {
         @Override
         void execute(CompatibilityCheckDetails<String> details) {
@@ -418,25 +342,14 @@
     @Unroll("can select a compatible attribute value (#scenario)")
     def "can select a compatible attribute value"() {
         def dep = new LocalComponentDependencyMetadata(componentId, Stub(ComponentSelector), "from", null, ImmutableAttributes.EMPTY, null, [] as List, [], false, false, true, false, false, null)
-        def defaultConfig = defaultConfiguration()
-        def toFooConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'foo'
-            getAttributes() >> attributes(key: 'something')
-            isCanBeConsumed() >> true
-            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
-        }
-        def toBarConfig = Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'bar'
-            getAttributes() >> attributes(key: 'something else')
-            isCanBeConsumed() >> true
-            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
-        }
-        def toComponent = Stub(ComponentGraphResolveMetadata) {
-            getVariantsForGraphTraversal() >> Optional.of(ImmutableList.of(toFooConfig, toBarConfig))
-            getAttributesSchema() >> EmptySchema.INSTANCE
+        def toFooVariant = variant('foo', attributes(key: 'something'))
+        def toBarVariant = variant('bar', attributes(key: 'something else'))
+        def toCandidates = Stub(GraphSelectionCandidates) {
+            useVariants >> true
+            variants >> ImmutableList.of(toFooVariant, toBarVariant)
         }
         def toState = Stub(ComponentGraphResolveState) {
-            getMetadata() >> toComponent
+            getCandidatesForGraphVariantSelection() >> toCandidates
         }
         def attributeSchemaWithCompatibility = new DefaultAttributesSchema(TestUtil.instantiatorFactory(), SnapshotTestUtil.isolatableFactory())
         attributeSchemaWithCompatibility.attribute(Attribute.of('key', String), {
@@ -445,11 +358,6 @@
         })
         attributeSchemaWithCompatibility.attribute(Attribute.of('extra', String))
 
-        given:
-        toComponent.getConfiguration("default") >> defaultConfig
-        toComponent.getConfiguration("foo") >> toFooConfig
-        toComponent.getConfiguration("bar") >> toBarConfig
-
         expect:
         dep.selectVariants(attributes(queryAttributes), toState, attributeSchemaWithCompatibility, [] as Set).variants.name as Set == [expected] as Set
 
@@ -474,14 +382,38 @@
         return attributes.asImmutable()
     }
 
-    private ConfigurationGraphResolveMetadata defaultConfiguration() {
-        Stub(ConfigurationGraphResolveMetadata) {
-            getName() >> 'default'
-            isCanBeConsumed() >> true
-            getAttributes() >> Mock(AttributeContainerInternal) {
-                isEmpty() >> true
-            }
+    private VariantGraphResolveState variant(String name, AttributeContainerInternal attributes) {
+        def variant = Stub(VariantGraphResolveState) {
+            getName() >> name
+            getAttributes() >> attributes
+            getCapabilities() >> Stub(CapabilitiesMetadataInternal)
         }
+        return variant
+    }
+
+    private VariantGraphResolveState consumableConfiguration(ComponentGraphResolveState component, String name, ImmutableAttributes attrs = attributes([:])) {
+        def variantMetadata = Stub(VariantGraphResolveMetadata) {
+            getName() >> name
+            getAttributes() >> attrs
+        }
+        def variant = Stub(VariantGraphResolveState) {
+            getMetadata() >> variantMetadata
+        }
+        def configurationMetadata = Stub(ConfigurationGraphResolveMetadata) {
+            isCanBeConsumed() >> true
+        }
+        def state = Stub(ConfigurationGraphResolveState) {
+            getName() >> name
+            getMetadata() >> configurationMetadata
+            asVariant() >> variant
+            getAttributes() >> attrs
+        }
+        component.getConfiguration(name) >> state
+        return variant
+    }
+
+    private VariantGraphResolveState defaultConfiguration(ComponentGraphResolveState component, ImmutableAttributes attrs = attributes([:])) {
+        return consumableConfiguration(component, 'default', attrs)
     }
 
     enum JavaVersion {
diff --git a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/DependencyManagementTestUtil.groovy b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/DependencyManagementTestUtil.groovy
index 239c796..7f5dcf6 100644
--- a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/DependencyManagementTestUtil.groovy
+++ b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/DependencyManagementTestUtil.groovy
@@ -20,11 +20,21 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionDescriptorFactory
 import org.gradle.api.internal.artifacts.repositories.metadata.IvyMutableModuleMetadataFactory
 import org.gradle.api.internal.artifacts.repositories.metadata.MavenMutableModuleMetadataFactory
+import org.gradle.api.internal.attributes.AttributeDesugaring
+import org.gradle.internal.component.external.model.ModuleComponentGraphResolveStateFactory
 import org.gradle.internal.component.external.model.PreferJavaRuntimeVariant
+import org.gradle.internal.component.model.ComponentIdGenerator
 import org.gradle.util.AttributeTestUtil
 import org.gradle.util.TestUtil
 
 class DependencyManagementTestUtil {
+    private static final ComponentIdGenerator ID_GENERATOR = new ComponentIdGenerator()
+    private static final AttributeDesugaring ATTRIBUTE_DESUGARING = new AttributeDesugaring(AttributeTestUtil.attributesFactory())
+
+    static ModuleComponentGraphResolveStateFactory modelGraphResolveFactory() {
+        return new ModuleComponentGraphResolveStateFactory(ID_GENERATOR, ATTRIBUTE_DESUGARING)
+    }
+
     static MavenMutableModuleMetadataFactory mavenMetadataFactory() {
         return new MavenMutableModuleMetadataFactory(new DefaultImmutableModuleIdentifierFactory(), AttributeTestUtil.attributesFactory(), TestUtil.objectInstantiator(), defaultSchema())
     }
diff --git a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
index bf4f8e7..68881b0 100644
--- a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
+++ b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/api/internal/artifacts/result/ResolutionResultDataBuilder.groovy
@@ -45,7 +45,7 @@
     }
 
     static DefaultResolvedComponentResult newModule(String group='a', String module='a', String version='1', ComponentSelectionReason selectionReason = ComponentSelectionReasons.requested(), ResolvedVariantResult variant = newVariant(), String repoId = null) {
-        new DefaultResolvedComponentResult(newId(group, module, version), selectionReason, new DefaultModuleComponentIdentifier(DefaultModuleIdentifier.newId(group, module), version), [variant], [variant], repoId)
+        new DefaultResolvedComponentResult(newId(group, module, version), selectionReason, new DefaultModuleComponentIdentifier(DefaultModuleIdentifier.newId(group, module), version), [1: variant], [variant], repoId)
     }
 
     static DefaultResolvedDependencyResult newDependency(ComponentSelector componentSelector, String group='a', String module='a', String selectedVersion='1') {
diff --git a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/resolve/VariantAwareDependencyResolutionTestFixture.groovy b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/resolve/VariantAwareDependencyResolutionTestFixture.groovy
index 752d6f2..35f53d3 100644
--- a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/resolve/VariantAwareDependencyResolutionTestFixture.groovy
+++ b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/integtests/resolve/VariantAwareDependencyResolutionTestFixture.groovy
@@ -35,14 +35,14 @@
                     resolver {
                         attributes.attribute(color, 'blue')
                         extendsFrom implementation
-                        canBeResolved = true
+                        assert canBeResolved
                         canBeConsumed = false
                     }
                     outgoing {
                         attributes.attribute(color, 'blue')
                         extendsFrom implementation
                         canBeResolved = false
-                        canBeConsumed = true
+                        assert canBeConsumed
                     }
                 }
             }
diff --git a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/internal/component/local/model/TestComponentIdentifiers.java b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/internal/component/local/model/TestComponentIdentifiers.java
index 1450980..bbdec0c 100644
--- a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/internal/component/local/model/TestComponentIdentifiers.java
+++ b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/internal/component/local/model/TestComponentIdentifiers.java
@@ -29,25 +29,25 @@ public static ProjectComponentIdentifier newProjectId(String projectPath) {
         return newProjectId(":", projectPath);
     }
 
-    public static ProjectComponentIdentifier newProjectId(String buildName, String projectPath) {
+    public static ProjectComponentIdentifier newProjectId(String buildPath, String projectPath) {
         Path path = Path.path(projectPath);
         String name = path.getName();
         if (name == null) {
             name = "root";
         }
-        return new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier(buildName), path, path, name);
+        return new DefaultProjectComponentIdentifier(new DefaultBuildIdentifier(Path.path(buildPath)), path, path, name);
     }
 
     public static ProjectComponentSelector newSelector(String projectPath) {
         return newSelector(":", projectPath);
     }
 
-    public static ProjectComponentSelector newSelector(String buildName, String projectPath) {
+    public static ProjectComponentSelector newSelector(String buildPath, String projectPath) {
         Path path = Path.path(projectPath);
         String name = path.getName();
         if (name == null) {
             name = "root";
         }
-        return new DefaultProjectComponentSelector(new DefaultBuildIdentifier(buildName), path, path, name, ImmutableAttributes.EMPTY, Collections.emptyList());
+        return new DefaultProjectComponentSelector(new DefaultBuildIdentifier(Path.path(buildPath)), path, path, name, ImmutableAttributes.EMPTY, Collections.emptyList());
     }
 }
diff --git a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/resolve/scenarios/VersionRangeResolveTestScenarios.groovy b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/resolve/scenarios/VersionRangeResolveTestScenarios.groovy
index fceb16c..1b7f3fb 100644
--- a/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/resolve/scenarios/VersionRangeResolveTestScenarios.groovy
+++ b/subprojects/dependency-management/src/testFixtures/groovy/org/gradle/resolve/scenarios/VersionRangeResolveTestScenarios.groovy
@@ -104,7 +104,7 @@
         expected: "12"
     )
 
-    public static final StrictPermutationsProvider SCENARIOS_PREFER = StrictPermutationsProvider.check(
+    public static final StrictPermutationsProvider SCENARIOS_PREFER_BATCH1 = StrictPermutationsProvider.check(
         versions: [PREFER_11, PREFER_12],
         expected: "12",
         conflicts: true
@@ -137,10 +137,18 @@
         expectedStrict: [IGNORE, "12"],
         conflicts: true // TODO Should not be any conflict resolution here
     ).and(
-        versions: [PREFER_12, RANGE_10_11],
+        versions: [FIXED_11, PREFER_12, RANGE_10_14],
         expected: "11",
-        expectedStrict: [IGNORE, "11"]
+        expectedStrictSingle: ["11", IGNORE, "11"],
+        expectedStrictMulti: ["11", IGNORE, IGNORE]
     ).and(
+        versions: [PREFER_11, PREFER_13, RANGE_10_12],
+        expected: "11",
+        expectedStrictSingle: [IGNORE, IGNORE, "11"],
+        expectedStrictMulti: [IGNORE, IGNORE, IGNORE],
+    )
+
+    public static final StrictPermutationsProvider SCENARIOS_PREFER_BATCH2 = StrictPermutationsProvider.check(
         versions: [PREFER_12, RANGE_10_12],
         expected: "12",
         expectedStrict: [IGNORE, "12"]
@@ -163,25 +171,19 @@
         versions: [PREFER_11, RANGE_10_11, PREFER_13, RANGE_10_14],
         expected: "11"
     ).and(
-        versions: [FIXED_11, PREFER_12, RANGE_10_14],
-        expected: "11",
-        expectedStrictSingle: ["11", IGNORE, "11"],
-        expectedStrictMulti: ["11", IGNORE, IGNORE]
-    ).and(
-        versions: [PREFER_11, PREFER_13, RANGE_10_12],
-        expected: "11",
-        expectedStrictSingle: [IGNORE, IGNORE, "11"],
-        expectedStrictMulti: [IGNORE, IGNORE, IGNORE],
-    ).and(
         versions: [PREFER_7_8, FIXED_12],  // No version satisfies the range [7,8]
         expected: FAILED
     ).and(
         versions: [PREFER_14_16, FIXED_12], // No version satisfies the range [14,16]
         expected: FAILED
+    ).and(
+        versions: [PREFER_12, RANGE_10_11],
+        expected: "11",
+        expectedStrict: [IGNORE, "11"]
     )
 
     // Keep in mind that only versions in 9..13 are published
-    public static final StrictPermutationsProvider SCENARIOS_TWO_DEPENDENCIES = StrictPermutationsProvider.check(
+    public static final StrictPermutationsProvider SCENARIOS_TWO_DEPENDENCIES_BATCH1 = StrictPermutationsProvider.check(
         versions: [FIXED_7, FIXED_13],
         expected: "13",
         expectedStrict: [REJECTED, "13"],
@@ -254,7 +256,9 @@
         versions: [RANGE_10_14, RANGE_10_OR_HIGHER],
         expected: "13",
         expectedStrict: ["13", IGNORE]
-    ).and(
+    )
+
+    public static final StrictPermutationsProvider SCENARIOS_TWO_DEPENDENCIES_BATCH2 = StrictPermutationsProvider.check(
         versions: [RANGE_10_OR_HIGHER, RANGE_11_OR_HIGHER],
         expected: "13"
     ).and(
diff --git a/subprojects/diagnostics/build.gradle.kts b/subprojects/diagnostics/build.gradle.kts
index 61c4022..5754db7 100644
--- a/subprojects/diagnostics/build.gradle.kts
+++ b/subprojects/diagnostics/build.gradle.kts
@@ -54,8 +54,3 @@
     excludePatterns.add("org/gradle/api/reporting/dependencies/internal/*")
     excludePatterns.add("org/gradle/api/plugins/internal/*")
 }
-
-// Remove as part of fixing https://github.com/gradle/configuration-cache/issues/585
-tasks.configCacheIntegTest {
-    systemProperties["org.gradle.configuration-cache.internal.test-disable-load-after-store"] = "true"
-}
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/plugins/ProjectReportsPluginIntegrationSpec.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/plugins/ProjectReportsPluginIntegrationSpec.groovy
index 9e8730f..a4be115 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/plugins/ProjectReportsPluginIntegrationSpec.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/plugins/ProjectReportsPluginIntegrationSpec.groovy
@@ -21,7 +21,9 @@
 class ProjectReportsPluginIntegrationSpec extends AbstractIntegrationSpec {
     def setup() {
         buildFile << """
-            apply plugin: 'project-report'
+        plugins {
+            id 'project-report'
+        }
         """
     }
 
@@ -39,7 +41,12 @@
     def "produces report files in custom directory"() {
         given:
         buildFile << """
-            projectReportDirName = "custom"
+            tasks.withType(ConventionReportTask) {
+                projectReportDirectory = project.layout.buildDirectory.dir('reports/custom')
+            }
+            tasks.withType(HtmlDependencyReportTask) {
+                projectReportDirectory = project.layout.buildDirectory.dir('reports/custom')
+            }
         """
 
         when:
@@ -95,4 +102,25 @@
         then:
         !result.getOutput().contains("See the report at:")
     }
+
+    def "nags users about deprecations"() {
+        given:
+        buildFile << """
+            projectReportDirName = "custom"
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning(
+            "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: " +
+                "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+        ).expectDocumentedDeprecationWarning(
+            "The org.gradle.api.plugins.ProjectReportsPluginConvention type has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: " +
+                "https://docs.gradle.org/current/userguide/upgrading_version_8.html#project_report_convention_deprecation"
+        )
+        succeeds("projectReport")
+    }
 }
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTaskIntegrationTest.groovy
index 1084743..c3fc6e0 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependencies/HtmlDependencyReportTaskIntegrationTest.groovy
@@ -563,10 +563,7 @@
                maven { url "${mavenRepo.uri}" }
             }
             configurations {
-                compileOnly.deprecateForResolution('compileClasspath')
-                compileOnly.deprecateForConsumption { builder ->
-                    builder.willBecomeAnErrorInGradle9().withUpgradeGuideSection(8, "foo")
-                }
+                createWithRole('compileOnly', org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_BUCKET)
             }
             dependencies {
                 compileOnly 'foo:foo:1.0'
@@ -599,7 +596,7 @@
             }
             configurations {
                 undeclarable {
-                    canBeDeclaredAgainst = false
+                    canBeDeclared = false
                 }
             }
         """
@@ -624,10 +621,10 @@
             }
             configurations {
                 declarable {
-                    canBeDeclaredAgainst = true
+                    canBeDeclared = true
                 }
                 undeclarable {
-                    canBeDeclaredAgainst = false
+                    canBeDeclared = false
                     extendsFrom(declarable)
                 }
             }
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependents/DependentComponentsReportIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependents/DependentComponentsReportIntegrationTest.groovy
index af8e558..fcf67f1 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependents/DependentComponentsReportIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/reporting/dependents/DependentComponentsReportIntegrationTest.groovy
@@ -17,7 +17,6 @@
 package org.gradle.api.reporting.dependents
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 
 class DependentComponentsReportIntegrationTest extends AbstractIntegrationSpec {
 
@@ -37,7 +36,6 @@
         output.contains("--component     Component to generate the report for (can be specified more than once).")
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "displays empty dependents report for an empty project"() {
         given:
         buildFile
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy
index f8d55f7..7b73f23 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyInsightReportTaskIntegrationTest.groovy
@@ -21,6 +21,9 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
 import org.gradle.integtests.resolve.locking.LockfileFixture
+import spock.lang.Issue
+
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
 
 class DependencyInsightReportTaskIntegrationTest extends AbstractIntegrationSpec {
     def jvmVersion = JavaVersion.current().majorVersion
@@ -35,7 +38,7 @@
 
     def "requires use of configuration flag if Java plugin isn't applied"() {
         given:
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -61,7 +64,7 @@
         mavenRepo.module("org", "middle").dependsOnModules("leaf1", "leaf2").publish()
         mavenRepo.module("org", "top").dependsOnModules("middle", "leaf2").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java'
 
             repositories {
@@ -91,7 +94,7 @@
         mavenRepo.module("org", "middle").dependsOnModules("leaf1", "leaf2").publish()
         mavenRepo.module("org", "top").dependsOnModules("middle", "leaf2").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java'
 
             repositories {
@@ -123,7 +126,7 @@
 
         mavenRepo.module("org", "top").dependsOnModules("middle", "leaf2").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -182,7 +185,7 @@
 
         mavenRepo.module("org", "toplevel4").dependsOnModules("middle3").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -239,7 +242,7 @@
             def c = module('org', 'c').dependsOn(a).publish()
             def d = module('org', 'd').dependsOn(leaf).publish()
         }
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -299,6 +302,7 @@
 """
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/24356")
     def "displays information about conflicting modules when failOnVersionConflict is used"() {
         given:
         mavenRepo.module("org", "leaf1").publish()
@@ -319,7 +323,7 @@
 
         mavenRepo.module("org", "toplevel4").dependsOnModules("middle3").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -338,7 +342,7 @@
         run "dependencyInsight", "--dependency", "leaf2", "--configuration", "conf"
 
         then:
-        outputContains """Dependency resolution failed because of conflict(s) on the following module(s):
+        outputContains """Dependency resolution failed because of conflict on the following module:
    - org:leaf2 between versions 2.5, 1.5 and 1.0
 
 org:leaf2:2.5
@@ -370,6 +374,7 @@
 """
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/24356")
     def "displays information about conflicting modules when failOnVersionConflict is used and afterResolve is used"() {
         given:
         mavenRepo.module("org", "leaf1").publish()
@@ -390,7 +395,7 @@
 
         mavenRepo.module("org", "toplevel4").dependsOnModules("middle3").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -413,7 +418,7 @@
         run "dependencyInsight", "--dependency", "leaf2", "--configuration", "conf"
 
         then:
-        outputContains """Dependency resolution failed because of conflict(s) on the following module(s):
+        outputContains """Dependency resolution failed because of conflict on the following module:
    - org:leaf2 between versions 2.5, 1.5 and 1.0
 
 org:leaf2:2.5
@@ -563,7 +568,7 @@
         mavenRepo.module("org", "foo", "1.0").dependsOn('org', 'leaf', '1.0').publish()
         mavenRepo.module("org", "bar", "1.0").dependsOn('org', 'leaf', '2.0').publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -646,7 +651,7 @@
             .dependsOn("org", "middle", "latest.integration")
             .publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 ivy { url "${ivyRepo.uri}" }
             }
@@ -697,7 +702,7 @@
         given:
         mavenRepo.module("org", "bar", "2.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -761,7 +766,7 @@
         mavenRepo.module("org.test", "bar", "2.0").publish()
         mavenRepo.module("org", "baz", "1.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -847,7 +852,7 @@
         mavenRepo.module("org", "bar", "1.0").publish()
         mavenRepo.module("org", "baz", "2.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -910,7 +915,7 @@
         mavenRepo.module("org", "foo", "2.0").dependsOn('org', 'leaf', '1.0').publish()
         mavenRepo.module("org", "bar", "1.0").dependsOn('org', 'leaf', '2.0').publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -963,7 +968,7 @@
         mavenRepo.module("org", "bar", "1.0").publish()
         mavenRepo.module("org", "bar", "2.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1028,7 +1033,7 @@
             .dependsOn("org", "leaf", "1.+")
             .publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 ivy { url "${ivyRepo.uri}" }
             }
@@ -1078,7 +1083,7 @@
         mavenRepo.module("org", "foo", "1.0").dependsOn('org', 'leaf', '1.0').publish()
         mavenRepo.module("org", "bar", "1.0").dependsOn('org', 'leaf', '2.0').publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1129,7 +1134,7 @@
         mavenRepo.module("org", "foo", "1.0").dependsOn('org', 'leaf', '1.0').publish()
         mavenRepo.module("org", "bar", "1.0").dependsOn('org', 'leaf', '2.0').publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1176,7 +1181,7 @@
         mavenRepo.module("org", "leaf", "2.0").publish()
         mavenRepo.module("org", "foo", "1.0").dependsOn('org', 'leaf', '1.0').publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1225,7 +1230,7 @@
 
     def "shows decent failure when inputs missing"() {
         given:
-        file("build.gradle") << """
+        buildFile << """
             task insight(type: DependencyInsightReportTask) {
                 showingAllVariants = false
                 setDependencySpec { it.requested.module == 'leaf2' }
@@ -1241,7 +1246,7 @@
 
     def "informs that there are no dependencies"() {
         given:
-        file("build.gradle") << """
+        buildFile << """
             configurations {
                 conf
             }
@@ -1263,7 +1268,7 @@
         given:
         mavenRepo.module("org", "top").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1291,7 +1296,7 @@
         given:
         mavenRepo.module("org", "top").dependsOnModules("middle").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1324,7 +1329,7 @@
         mavenRepo.module("org", "top").dependsOn("org", "middle", "1.0").publish()
         mavenRepo.module("org", "middle", "1.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1354,7 +1359,7 @@
       - Could not find org:middle:2.0.
         Searched in the following locations:
           - ${mavenRepoURL}/org/middle/2.0/middle-2.0.pom
-        If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
+        ${repositoryHint("Maven POM")}
 
 org:middle:1.0 -> 2.0 FAILED
 \\--- org:top:1.0
@@ -1367,7 +1372,7 @@
         mavenRepo.module("org", "top").dependsOn("org", "middle", "1.0").publish()
         mavenRepo.module("org", "middle", "1.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1404,7 +1409,7 @@
         mavenRepo.module("org", "top").dependsOn("org", "middle", "1.0").publish()
         mavenRepo.module("org", "middle", "1.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1451,7 +1456,7 @@
             .dependsOn("org", "leaf", "1.6+")
             .publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 ivy { url "${ivyRepo.uri}" }
             }
@@ -1478,7 +1483,7 @@
       - Could not find org:leaf:1.0.
         Searched in the following locations:
           - ${ivyRepoURL}/org/leaf/1.0/ivy-1.0.xml
-        If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
+        ${repositoryHint("ivy.xml")}
 
 org:leaf:1.0 FAILED
 \\--- org:top:1.0
@@ -1506,9 +1511,9 @@
 
     void "marks project dependencies that cannot be resolved as 'FAILED'"() {
         given:
-        file("settings.gradle") << "include 'A', 'B', 'C'; rootProject.name='root'"
+        settingsFile << "include 'A', 'B', 'C'; rootProject.name='root'"
 
-        file("build.gradle") << """
+        buildFile << """
             configurations.create('conf')
             dependencies {
               conf project(':A')
@@ -1557,7 +1562,7 @@
         mavenRepo.module("org", "leaf1").dependsOnModules("leaf2").publish()
         mavenRepo.module("org", "leaf2").dependsOnModules("leaf1").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -1597,9 +1602,9 @@
 
     def "deals with dependency cycle to root"() {
         given:
-        file("settings.gradle") << "include 'impl'; rootProject.name='root'"
+        settingsFile << "include 'impl'; rootProject.name='root'"
 
-        file("build.gradle") << """
+        buildFile << """
             allprojects {
                 apply plugin: 'java-library'
                 group = 'org.foo'
@@ -1660,9 +1665,9 @@
         mavenRepo.module("org", "leaf2").dependsOnModules("leaf3").publish()
         mavenRepo.module("org", "leaf3").publish()
 
-        file("settings.gradle") << "include 'impl'; rootProject.name='root'"
+        settingsFile << "include 'impl'; rootProject.name='root'"
 
-        file("build.gradle") << """
+        buildFile << """
             allprojects {
                 apply plugin: 'java-library'
                 group = 'org.foo'
@@ -1716,9 +1721,9 @@
         mavenRepo.module("org", "leaf2").dependsOnModules("leaf3").publish()
         mavenRepo.module("org", "leaf3").publish()
 
-        file("settings.gradle") << "include 'impl'; rootProject.name='root'"
+        settingsFile << "include 'impl'; rootProject.name='root'"
 
-        file("build.gradle") << """
+        buildFile << """
             allprojects {
                 apply plugin: 'java-library'
                 group = 'org.foo'
@@ -1770,9 +1775,9 @@
         mavenRepo.module("org", "leaf3").publish()
         mavenRepo.module("org", "leaf4").publish()
 
-        file("settings.gradle") << "include 'api', 'impl'; rootProject.name='root'"
+        settingsFile << "include 'api', 'impl'; rootProject.name='root'"
 
-        file("build.gradle") << """
+        buildFile << """
             allprojects {
                 apply plugin: 'java-library'
                 group = 'org.foo'
@@ -1825,7 +1830,7 @@
         mavenRepo.module("org", "leaf1").publish()
         mavenRepo.module("org", "leaf2").publish()
 
-        file("build.gradle") << """
+        buildFile << """
                 apply plugin: 'java-library'
                 repositories {
                     maven { url "${mavenRepo.uri}" }
@@ -1886,9 +1891,9 @@
         mavenRepo.module("org", "leaf3").publish()
         mavenRepo.module("org", "leaf4").publish()
 
-        file("settings.gradle") << "include 'api', 'impl', 'some:deeply:nested'; rootProject.name='root'"
+        settingsFile << "include 'api', 'impl', 'some:deeply:nested'; rootProject.name='root'"
 
-        file("build.gradle") << """
+        buildFile << """
             allprojects {
                 apply plugin: 'java-library'
                 group = 'org.foo'
@@ -1987,9 +1992,9 @@
         mavenRepo.module("org", "leaf2").dependsOnModules("leaf3").publish()
         mavenRepo.module("org", "leaf3").publish()
 
-        file("settings.gradle") << "include 'api', 'impl'; rootProject.name='root'"
+        settingsFile << "include 'api', 'impl'; rootProject.name='root'"
 
-        file("build.gradle") << """
+        buildFile << """
             allprojects {
                 apply plugin: 'java-library'
                 group = 'org.foo'
@@ -2046,7 +2051,7 @@
         mavenRepo.module("foo", "foo", '1.0').publish()
         mavenRepo.module("foo", "bar", '2.0').publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                maven { url "${mavenRepo.uri}" }
             }
@@ -2107,7 +2112,7 @@
         mavenRepo.module("org", "foo", "1.5").publish()
         mavenRepo.module("org", "foo", "2.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2162,7 +2167,7 @@
         mavenRepo.module("org", "foo", "1.5").publish()
         mavenRepo.module("org", "foo", "2.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2218,7 +2223,7 @@
         mavenRepo.module("org", "foo", "1.5").publish()
         mavenRepo.module("org", "foo", "2.0").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2271,7 +2276,7 @@
         mavenRepo.module("org", "foo", "1.5").publish()
         mavenRepo.module("org", "foo", "2.0").publish()
         mavenRepo.module('org', 'bar', '1.0').dependsOn('org', 'foo', '[1.1,1.3]').publish()
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2318,7 +2323,7 @@
         mavenRepo.module("org", "bar", "1.1").publish()
         mavenRepo.module("org", "bar", "1.2").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2390,7 +2395,7 @@
         mavenRepo.module("org", "bar", "1.1").publish()
         mavenRepo.module("org", "bar", "1.2").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2468,7 +2473,7 @@
         bom.dependencyConstraint(leaf)
         bom.publish()
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2522,7 +2527,7 @@
 
         }
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2568,7 +2573,7 @@
 
         mavenRepo.module("org", "top").dependsOnModules("middle", "leaf2").publish()
 
-        file("build.gradle") << """
+        buildFile << """
             repositories {
                 maven { url "${mavenRepo.uri}" }
             }
@@ -2621,7 +2626,7 @@
         mavenRepo.module("org", "bar", "1.2").publish()
 
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2697,7 +2702,7 @@
         mavenRepo.module("org", "foo", "1.2").publish()
 
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2764,7 +2769,7 @@
                 .publish()
         }
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
@@ -2816,7 +2821,7 @@
                     }
                 }"""
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             def COLOR = Attribute.of('color', String)
@@ -2910,7 +2915,7 @@
             module('planet', 'pluto', '1.0.0').publish()
         }
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: 'java-library'
 
             repositories {
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskIntegrationTest.groovy
index 2d1b83d..968a0d0 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/DependencyReportTaskIntegrationTest.groovy
@@ -237,8 +237,9 @@
             }
 
             task resolveConf {
+                def foo = configurations.foo
                 doLast {
-                    configurations.foo.each { println it }
+                    foo.each { println it }
                 }
             }
         """
@@ -1036,7 +1037,7 @@
         buildFile << """
             subprojects {
                 configurations {
-                    compile.deprecateForDeclarationAgainst('implementation')
+                    createWithRole('compile', org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE)
                     'default' { extendsFrom compile }
                 }
                 group = "group"
@@ -1047,7 +1048,7 @@
             }
         """
 
-        executer.expectDocumentedDeprecationWarning("The compile configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use the implementation configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+        executer.expectDocumentedDeprecationWarning("The compile configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
 
         expect:
         succeeds ':a:dependencies'
@@ -1060,7 +1061,7 @@
         buildFile << """
             subprojects {
                 configurations {
-                    compile.canBeDeclaredAgainst = false
+                    compile.canBeDeclared = false
                     'default' { extendsFrom compile }
                 }
                 group = "group"
@@ -1085,10 +1086,7 @@
                maven { url "${mavenRepo.uri}" }
             }
             configurations {
-                compileOnly.deprecateForResolution("compileClasspath")
-                compileOnly.deprecateForConsumption { builder ->
-                    builder.willBecomeAnErrorInGradle9().withUpgradeGuideSection(8, "foo")
-                }
+                createWithRole('compileOnly', org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_BUCKET)
                 implementation.extendsFrom compileOnly
             }
             dependencies {
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest.groovy
index f58f051..a91ac0f 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest.groovy
@@ -15,12 +15,19 @@
  */
 package org.gradle.api.tasks.diagnostics
 
+
 import org.gradle.cache.internal.BuildScopeCacheDir
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.util.GradleVersion
 import org.junit.Rule
 
+import static org.gradle.api.internal.DocumentationRegistry.BASE_URL
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+
 class HelpTaskIntegrationTest extends AbstractIntegrationSpec {
     @Rule
     public final TestResources resources = new TestResources(temporaryFolder)
@@ -46,13 +53,13 @@
 
 To create a new build in this directory, run gradle init
 
-For more detail on the 'init' task, see https://docs.gradle.org/$version/userguide/build_init_plugin.html
+For more detail on the 'init' task, see $BASE_URL/userguide/build_init_plugin.html
 
-For more detail on creating a Gradle build, see https://docs.gradle.org/$version/userguide/tutorial_using_tasks.html
+For more detail on creating a Gradle build, see $BASE_URL/userguide/tutorial_using_tasks.html
 
 To see a list of command-line options, run gradle --help
 
-For more detail on using Gradle, see https://docs.gradle.org/$version/userguide/command_line_interface.html
+For more detail on using Gradle, see $BASE_URL/userguide/command_line_interface.html
 
 For troubleshooting, visit https://help.gradle.org
 
@@ -205,7 +212,7 @@
 
 To see a list of command-line options, run gradle --help
 
-For more detail on using Gradle, see https://docs.gradle.org/$version/userguide/command_line_interface.html
+For more detail on using Gradle, see $BASE_URL/userguide/command_line_interface.html
 
 For troubleshooting, visit https://help.gradle.org
 
@@ -371,7 +378,7 @@
 ${builtInOptions}
 
 Description
-     Assembles a jar archive containing the main classes.
+     Assembles a jar archive containing the classes of the 'main' feature.
 
 Group
      build
@@ -394,7 +401,7 @@
 ${builtInOptions}
 
 Description
-     Assembles a jar archive containing the main classes.
+     Assembles a jar archive containing the classes of the 'main' feature.
 
 Group
      build
@@ -524,9 +531,10 @@
         failure.assertHasCause("Unknown command-line option '--tasssk'.")
         failure.assertHasResolutions(
             "Run gradle help --task :help to get task usage details.",
-            "Run with --stacktrace option to get the stack trace.",
-            "Run with --info or --debug option to get more log output.",
-            "Run with --scan to get full insights.",
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP,
         )
     }
 
@@ -553,6 +561,8 @@
                           DEF
                           GHIJKL
 
+     --no-booleanValue     Disables option --booleanValue
+
 ${builtInOptions}
 
 Description
@@ -602,6 +612,12 @@
         then:
         output.contains """
 Options
+     --no-valueA     Disables option --valueA
+
+     --no-valueB     Disables option --valueB
+
+     --no-valueC     Disables option --valueC
+
      --valueA     descA
 
      --valueB     descB
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/OutgoingVariantsReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/OutgoingVariantsReportTaskIntegrationTest.groovy
index 534e371..3783037 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/OutgoingVariantsReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/OutgoingVariantsReportTaskIntegrationTest.groovy
@@ -38,7 +38,7 @@
         buildFile << """
             configurations.create("custom") {
                 description = "My custom configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
             }
         """
@@ -53,8 +53,8 @@
         buildFile << """
             configurations.create("legacy") {
                 description = "My legacy configuration"
-                canBeResolved = true
-                canBeConsumed = true
+                assert canBeResolved
+                assert canBeConsumed
             }
         """
 
@@ -69,8 +69,8 @@
         buildFile << """
             configurations.create("legacy") {
                 description = "My custom legacy configuration"
-                canBeResolved = true
-                canBeConsumed = true
+                assert canBeResolved
+                assert canBeConsumed
             }
         """
 
@@ -96,7 +96,7 @@
             configurations.create("custom") {
                 description = "My custom configuration"
                 canBeResolved = false
-                canBeConsumed = true
+                assert canBeConsumed
             }
         """
 
@@ -121,7 +121,7 @@
             configurations.create("custom") {
                 description = "My custom configuration"
                 canBeResolved = false
-                canBeConsumed = true
+                assert canBeConsumed
 
                 attributes {
                     attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR))
@@ -159,7 +159,7 @@
             configurations.create("someConf") {
                 description = "My first custom configuration"
                 canBeResolved = false
-                canBeConsumed = true
+                assert canBeConsumed
 
                 attributes {
                     attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR))
@@ -171,7 +171,7 @@
             configurations.create("otherConf") {
                 description = "My second custom configuration"
                 canBeResolved = false
-                canBeConsumed = true
+                assert canBeConsumed
 
                 attributes {
                     attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category, Category.DOCUMENTATION));
@@ -231,7 +231,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains """--------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-API elements for main.
+API elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -298,7 +298,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -389,7 +389,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains("""--------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-API elements for main.
+API elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -471,7 +471,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -576,7 +576,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains("""--------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-API elements for main.
+API elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -658,7 +658,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -756,7 +756,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains("""--------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -826,7 +826,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains("""--------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-API elements for main.
+API elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -893,7 +893,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -978,7 +978,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains("""--------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-API elements for main.
+API elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -1045,7 +1045,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -1127,7 +1127,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains("""--------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org.test:extra:1.0
@@ -1159,7 +1159,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains("""--------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -1219,7 +1219,7 @@
             def sample = configurations.create("sample") {
                 visible = true
                 canBeResolved = false
-                canBeConsumed = true
+                assert canBeConsumed
 
                 attributes {
                     attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.VERIFICATION))
@@ -1240,7 +1240,7 @@
         result.groupedOutput.task(":outgoingVariants").assertOutputContains("""--------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-API elements for main.
+API elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -1307,7 +1307,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - org:myLib:1.0 (default capability)
@@ -1392,7 +1392,7 @@
             configurations.create("custom") {
                 description = "My custom configuration"
                 canBeResolved = false
-                canBeConsumed = true
+                assert canBeConsumed
             }
 
             task redJar(type: Jar) {
diff --git a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/ResolvableConfigurationsReportTaskIntegrationTest.groovy b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/ResolvableConfigurationsReportTaskIntegrationTest.groovy
index dba35fe..cb50e3b 100644
--- a/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/ResolvableConfigurationsReportTaskIntegrationTest.groovy
+++ b/subprojects/diagnostics/src/integTest/groovy/org/gradle/api/tasks/diagnostics/ResolvableConfigurationsReportTaskIntegrationTest.groovy
@@ -39,7 +39,7 @@
             configurations.create("custom") {
                 description = "My custom configuration"
                 canBeResolved = false
-                canBeConsumed = true
+                assert canBeConsumed
             }
         """
 
@@ -53,8 +53,8 @@
         buildFile << """
             configurations.create("legacy") {
                 description = "My legacy configuration"
-                canBeResolved = true
-                canBeConsumed = true
+                assert canBeResolved
+                assert canBeConsumed
             }
         """
 
@@ -69,8 +69,8 @@
         buildFile << """
             configurations.create("legacy") {
                 description = "My custom legacy configuration"
-                canBeResolved = true
-                canBeConsumed = true
+                assert canBeResolved
+                assert canBeConsumed
             }
         """
 
@@ -95,7 +95,7 @@
         buildFile << """
             configurations.create("custom") {
                 description = "My custom configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
             }
         """
@@ -121,7 +121,7 @@
         buildFile << """
             configurations.create("custom") {
                 description = "My custom configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
 
                 attributes {
@@ -158,7 +158,7 @@
         buildFile << """
             configurations.create("someConf") {
                 description = "My first custom configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
 
                 attributes {
@@ -170,7 +170,7 @@
 
             configurations.create("otherConf") {
                 description = "My second custom configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
 
                 attributes {
@@ -328,8 +328,8 @@
             configurations {
                 archiveLegacy {
                     description = 'Example legacy configuration.'
-                    canBeConsumed = true
-                    canBeResolved = true
+                    assert canBeConsumed
+                    assert canBeResolved
                 }
             }
 
@@ -453,7 +453,7 @@
         buildFile << """
             configurations.create("custom") {
                 description = "My custom configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
             }
         """
@@ -469,14 +469,14 @@
         buildFile << """
             configurations.create("custom") {
                 description = "My custom configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
             }
 
             configurations.create("legacy") {
                 description = "My custom configuration"
-                canBeResolved = true
-                canBeConsumed = true
+                assert canBeResolved
+                assert canBeConsumed
             }
         """
 
@@ -498,7 +498,7 @@
             configurations {
                 custom {
                     description = "My custom configuration"
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
 
                     attributes {
@@ -547,7 +547,7 @@
             configurations {
                 custom {
                     description = "My custom configuration"
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
 
                     attributes {
@@ -598,7 +598,7 @@
             configurations {
                 custom {
                     description = "My custom configuration"
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
 
                     attributes {
@@ -661,7 +661,7 @@
             configurations {
                 custom {
                     description = "My custom configuration"
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
 
                     attributes {
@@ -714,20 +714,20 @@
         buildFile << """
             def base = configurations.create("base") {
                 description = "Base configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
             }
 
             def mid = configurations.create("mid") {
                 description = "Mid configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
                 extendsFrom base
             }
 
             def leaf = configurations.create("leaf") {
                 description = "Leaf configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
                 extendsFrom mid
             }
@@ -766,20 +766,20 @@
         buildFile << """
             def base = configurations.create("base") {
                 description = "Base configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
             }
 
             def mid = configurations.create("mid") {
                 description = "Mid configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
                 extendsFrom base
             }
 
             def leaf = configurations.create("leaf") {
                 description = "Leaf configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
                 extendsFrom mid
             }
@@ -817,13 +817,13 @@
         buildFile << """
             def base = configurations.create("base") {
                 description = "Base configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
             }
 
             def mid = configurations.create("mid") {
                 description = "Mid configuration"
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
                 extendsFrom base
             }
diff --git a/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsCommonDynamicAvailableValues/settings.gradle b/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsCommonDynamicAvailableValues/settings.gradle
index 0e3b93f..deec2c1 100644
--- a/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsCommonDynamicAvailableValues/settings.gradle
+++ b/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsCommonDynamicAvailableValues/settings.gradle
@@ -15,4 +15,4 @@
  */
 
 include 'sub1'
-include 'sub2'
\ No newline at end of file
+include 'sub2'
diff --git a/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsEnumAndBooleanCmdOptionValues/build.gradle b/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsEnumAndBooleanCmdOptionValues/build.gradle
index 612cd10..f5c1ccc 100644
--- a/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsEnumAndBooleanCmdOptionValues/build.gradle
+++ b/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsEnumAndBooleanCmdOptionValues/build.gradle
@@ -20,4 +20,4 @@
 
 enum TestEnum {
     ABC, DEF, GHIJKL
-}
\ No newline at end of file
+}
diff --git a/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsEnumAndBooleanCmdOptionValues/settings.gradle b/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsEnumAndBooleanCmdOptionValues/settings.gradle
index c913b39..981e95b 100644
--- a/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsEnumAndBooleanCmdOptionValues/settings.gradle
+++ b/subprojects/diagnostics/src/integTest/resources/org/gradle/api/tasks/diagnostics/HelpTaskIntegrationTest/listsEnumAndBooleanCmdOptionValues/settings.gradle
@@ -1,2 +1,2 @@
 include ':proj1'
-include ':proj2'
\ No newline at end of file
+include ':proj2'
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/ProjectReportsPlugin.java b/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/ProjectReportsPlugin.java
index 7326653..38a0041 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/ProjectReportsPlugin.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/ProjectReportsPlugin.java
@@ -21,6 +21,7 @@
 import org.gradle.api.tasks.diagnostics.DependencyReportTask;
 import org.gradle.api.tasks.diagnostics.PropertyReportTask;
 import org.gradle.api.tasks.diagnostics.TaskReportTask;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.util.internal.WrapUtil;
 
 /**
@@ -40,33 +41,38 @@ public void apply(final Project project) {
         project.getPluginManager().apply(ReportingBasePlugin.class);
         @SuppressWarnings("deprecation")
         final ProjectReportsPluginConvention convention = project.getObjects().newInstance(org.gradle.api.plugins.internal.DefaultProjectReportsPluginConvention.class, project);
-        @SuppressWarnings("deprecation")
-        Convention projectConvention = project.getConvention();
-        projectConvention.getPlugins().put("projectReports", convention);
+
+        DeprecationLogger.whileDisabled(new Runnable() {
+            @Override
+            @SuppressWarnings("deprecation")
+            public void run() {
+                project.getConvention().getPlugins().put("projectReports", convention);
+            }
+        });
 
         project.getTasks().register(TASK_REPORT, TaskReportTask.class, taskReportTask -> {
-            taskReportTask.getProjectReportDirectory().convention(project.getLayout().dir(project.provider(() -> convention.getProjectReportDir())));
+            taskReportTask.getProjectReportDirectory().convention(project.getLayout().dir(project.provider(() -> DeprecationLogger.whileDisabled(() -> convention.getProjectReportDir()))));
             taskReportTask.setDescription("Generates a report about your tasks.");
             taskReportTask.conventionMapping("outputFile", () -> taskReportTask.getProjectReportDirectory().file("tasks.txt").get().getAsFile());
             taskReportTask.conventionMapping("projects", () -> WrapUtil.toSet(project));
         });
 
         project.getTasks().register(PROPERTY_REPORT, PropertyReportTask.class, propertyReportTask -> {
-            propertyReportTask.getProjectReportDirectory().convention(project.getLayout().dir(project.provider(() -> convention.getProjectReportDir())));
+            propertyReportTask.getProjectReportDirectory().convention(project.getLayout().dir(project.provider(() -> DeprecationLogger.whileDisabled(() ->convention.getProjectReportDir()))));
             propertyReportTask.setDescription("Generates a report about your properties.");
             propertyReportTask.conventionMapping("outputFile", () -> propertyReportTask.getProjectReportDirectory().file("properties.txt").get().getAsFile());
             propertyReportTask.conventionMapping("projects", () -> WrapUtil.toSet(project));
         });
 
         project.getTasks().register(DEPENDENCY_REPORT, DependencyReportTask.class, dependencyReportTask -> {
-            dependencyReportTask.getProjectReportDirectory().convention(project.getLayout().dir(project.provider(() -> convention.getProjectReportDir())));
+            dependencyReportTask.getProjectReportDirectory().convention(project.getLayout().dir(project.provider(() -> DeprecationLogger.whileDisabled(() ->convention.getProjectReportDir()))));
             dependencyReportTask.setDescription("Generates a report about your library dependencies.");
             dependencyReportTask.conventionMapping("outputFile", () -> dependencyReportTask.getProjectReportDirectory().file("dependencies.txt").get().getAsFile());
             dependencyReportTask.conventionMapping("projects", () -> WrapUtil.toSet(project));
         });
 
         project.getTasks().register(HTML_DEPENDENCY_REPORT, HtmlDependencyReportTask.class, htmlDependencyReportTask -> {
-            htmlDependencyReportTask.getProjectReportDirectory().convention(project.getLayout().dir(project.provider(() -> convention.getProjectReportDir())));
+            htmlDependencyReportTask.getProjectReportDirectory().convention(project.getLayout().dir(project.provider(() -> DeprecationLogger.whileDisabled(() ->convention.getProjectReportDir()))));
             htmlDependencyReportTask.setDescription("Generates an HTML report about your library dependencies.");
             htmlDependencyReportTask.getReports().getHtml().getOutputLocation().convention(htmlDependencyReportTask.getProjectReportDirectory().dir("dependencies"));
             htmlDependencyReportTask.conventionMapping("projects", () -> WrapUtil.toSet(project));
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/internal/DefaultProjectReportsPluginConvention.java b/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/internal/DefaultProjectReportsPluginConvention.java
index 3b6c820..28f66ed 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/internal/DefaultProjectReportsPluginConvention.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/plugins/internal/DefaultProjectReportsPluginConvention.java
@@ -21,6 +21,7 @@
 import org.gradle.api.reflect.HasPublicType;
 import org.gradle.api.reflect.TypeOf;
 import org.gradle.api.reporting.ReportingExtension;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.util.internal.WrapUtil;
 
 import javax.inject.Inject;
@@ -47,21 +48,32 @@ public TypeOf<?> getPublicType() {
 
     @Override
     public String getProjectReportDirName() {
+        logDeprecation();
         return projectReportDirName;
     }
 
     @Override
     public void setProjectReportDirName(String projectReportDirName) {
+        logDeprecation();
         this.projectReportDirName = projectReportDirName;
     }
 
     @Override
     public File getProjectReportDir() {
+        logDeprecation();
         return project.getExtensions().getByType(ReportingExtension.class).file(projectReportDirName);
     }
 
     @Override
     public Set<Project> getProjects() {
+        logDeprecation();
         return WrapUtil.toSet(project);
     }
+
+    private static void logDeprecation() {
+        DeprecationLogger.deprecateType(org.gradle.api.plugins.ProjectReportsPluginConvention.class)
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "project_report_convention_deprecation")
+            .nagUser();
+    }
 }
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/components/package-info.java b/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/components/package-info.java
index 5ca2aa6..03fe781 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/components/package-info.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/components/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Component reporting tasks.
  */
-package org.gradle.api.reporting.components;
\ No newline at end of file
+package org.gradle.api.reporting.components;
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/dependencies/HtmlDependencyReportTask.java b/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/dependencies/HtmlDependencyReportTask.java
index 25e8766..7989e9f 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/dependencies/HtmlDependencyReportTask.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/dependencies/HtmlDependencyReportTask.java
@@ -191,7 +191,7 @@ private ProjectsWithConfigurations<ProjectDetails.ProjectNameAndPath, Configurat
     private static Stream<? extends ConfigurationDetails> getConfigurationsWhichCouldHaveDependencyInfo(Project project) {
         return project.getConfigurations().stream()
             .map(ConfigurationInternal.class::cast)
-            .filter(c -> c.isDeclarableAgainstByExtension())
+            .filter(c -> c.isDeclarableByExtension())
             .map(ConfigurationDetails::of);
     }
 }
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/dependents/DependentComponentsReport.java b/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/dependents/DependentComponentsReport.java
index 4d16cb4..23761e0 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/dependents/DependentComponentsReport.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/dependents/DependentComponentsReport.java
@@ -53,6 +53,10 @@ public abstract class DependentComponentsReport extends DefaultTask {
     private boolean showTestSuites;
     private List<String> components;
 
+    {
+        notCompatibleWithConfigurationCache("Requires access to the component model at execution time.");
+    }
+
     /**
      * Should this include non-buildable components in the report?
      */
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/model/package-info.java b/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/model/package-info.java
index 6f4e3e4..b99793d 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/model/package-info.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/reporting/model/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Configuration model reporting tasks.
  */
-package org.gradle.api.reporting.model;
\ No newline at end of file
+package org.gradle.api.reporting.model;
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/AbstractDependencyReportTask.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/AbstractDependencyReportTask.java
index a0decd8..9e3c96c 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/AbstractDependencyReportTask.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/AbstractDependencyReportTask.java
@@ -133,7 +133,7 @@ public void setConfiguration(String configurationName) {
     private Set<Configuration> getNonDeprecatedTaskConfigurations() {
         Set<Configuration> filteredConfigurations = new HashSet<>();
         for (Configuration configuration : getTaskConfigurations()) {
-            if (((ConfigurationInternal)configuration).isDeclarableAgainstByExtension()) {
+            if (((ConfigurationInternal)configuration).isDeclarableByExtension()) {
                 filteredConfigurations.add(configuration);
             }
         }
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/DependencyInsightReportTask.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/DependencyInsightReportTask.java
index 73ada75..14b2fe2 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/DependencyInsightReportTask.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/DependencyInsightReportTask.java
@@ -25,7 +25,6 @@
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.result.DependencyResult;
-import org.gradle.api.artifacts.result.ResolutionResult;
 import org.gradle.api.artifacts.result.ResolvedComponentResult;
 import org.gradle.api.artifacts.result.ResolvedDependencyResult;
 import org.gradle.api.artifacts.result.ResolvedVariantResult;
@@ -37,9 +36,11 @@
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser;
 import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
+import org.gradle.api.internal.artifacts.result.ResolutionResultInternal;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.provider.Property;
+import org.gradle.api.provider.Provider;
 import org.gradle.api.specs.Spec;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.Internal;
@@ -121,10 +122,15 @@ public abstract class DependencyInsightReportTask extends DefaultTask {
     private final Property<Boolean> showingAllVariants = getProject().getObjects().property(Boolean.class);
     private transient Configuration configuration;
     private final Property<ResolvedComponentResult> rootComponentProperty = getProject().getObjects().property(ResolvedComponentResult.class);
+
+    // this field is named with a starting `z` to be serialized after `rootComponentProperty`
+    // because the serialization of `rootComponentProperty` can still trigger callback that can affect
+    // a value of `configuration.getAttributes()`.
+    // TODO:configuration-cache find a way to clean up this #23732
+    private Provider<AttributeContainer> zConfigurationAttributes;
     private ResolutionErrorRenderer errorHandler;
     private String configurationName;
     private String configurationDescription;
-    private AttributeContainer configurationAttributes;
 
     /**
      * The root component of the dependency graph to be inspected.
@@ -146,13 +152,15 @@ public Property<ResolvedComponentResult> getRootComponentProperty() {
                         "In order to use the '--all-variants' option, the configuration must not be resolved before this task is executed."
                     );
                 }
-                configurationInternal.setReturnAllVariants(true);
+                configurationInternal.getResolutionStrategy().setReturnAllVariants(true);
             }
             configurationName = configuration.getName();
             configurationDescription = configuration.toString();
-            configurationAttributes = configuration.getAttributes();
+            zConfigurationAttributes = getProject().provider(configuration::getAttributes);
+
             ResolvableDependenciesInternal incoming = (ResolvableDependenciesInternal) configuration.getIncoming();
-            ResolutionResult result = incoming.getResolutionResult(errorHandler);
+            ResolutionResultInternal result = (ResolutionResultInternal) incoming.getLenientResolutionResult();
+            errorHandler.addErrorProvider(result.getNonFatalFailure());
             rootComponentProperty.set(result.getRootComponent());
         }
         return rootComponentProperty;
@@ -320,7 +328,7 @@ private void renderSelectedDependencies(StyledTextOutput output, Set<DependencyR
         GraphRenderer renderer = new GraphRenderer(output);
         DependencyInsightReporter reporter = new DependencyInsightReporter(getVersionSelectorScheme(), getVersionComparator(), getVersionParser());
         Collection<RenderableDependency> itemsToRender = reporter.convertToRenderableItems(selectedDependencies, isShowSinglePathToDependency());
-        RootDependencyRenderer rootRenderer = new RootDependencyRenderer(this, configurationAttributes, getAttributesFactory());
+        RootDependencyRenderer rootRenderer = new RootDependencyRenderer(this, zConfigurationAttributes.get(), getAttributesFactory());
         ReplaceProjectWithConfigurationNameRenderer dependenciesRenderer = new ReplaceProjectWithConfigurationNameRenderer(configurationName);
         DependencyGraphsRenderer dependencyGraphRenderer = new DependencyGraphsRenderer(output, renderer, rootRenderer, dependenciesRenderer);
         dependencyGraphRenderer.setShowSinglePath(showSinglePathToDependency);
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/ResolutionErrorRenderer.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/ResolutionErrorRenderer.java
index d279ed0..9aaf912 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/ResolutionErrorRenderer.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/ResolutionErrorRenderer.java
@@ -15,35 +15,53 @@
  */
 package org.gradle.api.tasks.diagnostics;
 
-import com.google.common.collect.Lists;
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.ResolveException;
 import org.gradle.api.artifacts.component.ComponentSelector;
 import org.gradle.api.artifacts.result.DependencyResult;
 import org.gradle.api.artifacts.result.ResolvedComponentResult;
+import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.Conflict;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.conflicts.VersionConflictException;
+import org.gradle.api.provider.Provider;
 import org.gradle.api.specs.Spec;
-import org.gradle.internal.Pair;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.component.external.model.DefaultModuleComponentSelector;
 import org.gradle.internal.locking.LockOutOfDateException;
 import org.gradle.internal.logging.text.StyledTextOutput;
+import org.gradle.internal.logging.text.StyledTextOutput.Style;
 
 import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 
-class ResolutionErrorRenderer implements Action<Throwable> {
+import static org.gradle.util.internal.TextUtil.getPluralEnding;
+
+class ResolutionErrorRenderer {
     private final Spec<DependencyResult> dependencySpec;
-    private final List<Action<StyledTextOutput>> errorActions = Lists.newArrayListWithExpectedSize(1);
+    private final List<Action<StyledTextOutput>> errorActions = new ArrayList<>(1);
+    private final List<Provider<Throwable>> errorProviders = new ArrayList<>(1);
 
     public ResolutionErrorRenderer(@Nullable Spec<DependencyResult> dependencySpec) {
         this.dependencySpec = dependencySpec;
     }
 
-    @Override
-    public void execute(Throwable throwable) {
+    public void addErrorProvider(Provider<Throwable> errorProvider) {
+        errorProviders.add(errorProvider);
+    }
+
+    private void resolveErrorProviders() {
+        for (Provider<Throwable> errorProvider : errorProviders) {
+            Throwable error = errorProvider.getOrNull();
+            if (error != null) {
+                handleError(error);
+            }
+        }
+    }
+
+    public void handleError(Throwable throwable) {
         if (throwable instanceof ResolveException) {
             Throwable cause = throwable.getCause();
             handleResolutionError(cause);
@@ -79,23 +97,24 @@ private void handleOutOfDateLocks(final LockOutOfDateException cause) {
 
     private void handleConflict(final VersionConflictException conflict) {
         registerError(output -> {
-            output.text("Dependency resolution failed because of conflict(s) on the following module(s):");
+            Collection<Conflict> conflicts = conflict.getConflicts();
+            String plural = getPluralEnding(conflicts);
+            output.text("Dependency resolution failed because of conflict" + plural + " on the following module"+ plural + ":");
             output.println();
-            for (Pair<List<? extends ModuleVersionIdentifier>, String> identifierStringPair : conflict.getConflicts()) {
-                boolean matchesSpec = hasVersionConflictOnRequestedDependency(identifierStringPair.getLeft());
-                if (!matchesSpec) {
-                    continue;
-                }
-                output.text("   - ");
-                output.withStyle(StyledTextOutput.Style.Error).text(identifierStringPair.getRight());
-                output.println();
-            }
+            conflicts.stream()
+                .filter(idendifierStringPair -> hasVersionConflictOnRequestedDependency(idendifierStringPair.getVersions()))
+                .forEach(identifierStringPair -> {
+                    output.text("   - ");
+                    output.withStyle(Style.Error).text(identifierStringPair.getMessage());
+                    output.println();
+                });
             output.println();
         });
 
     }
 
     public void renderErrors(StyledTextOutput output) {
+        resolveErrorProviders();
         for (Action<StyledTextOutput> errorAction : errorActions) {
             errorAction.execute(output);
         }
@@ -105,7 +124,7 @@ private void registerError(Action<StyledTextOutput> errorAction) {
         errorActions.add(errorAction);
     }
 
-    private boolean hasVersionConflictOnRequestedDependency(final List<? extends ModuleVersionIdentifier> versionIdentifiers) {
+    private boolean hasVersionConflictOnRequestedDependency(final Collection<? extends ModuleVersionIdentifier> versionIdentifiers) {
         Objects.requireNonNull(dependencySpec, "Dependency spec must be specified");
         for (final ModuleVersionIdentifier versionIdentifier : versionIdentifiers) {
             if (dependencySpec.isSatisfiedBy(asDependencyResult(versionIdentifier))) {
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/ConfigurationDetails.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/ConfigurationDetails.java
index 36caf00..899c012 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/ConfigurationDetails.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/ConfigurationDetails.java
@@ -38,7 +38,7 @@ public static ConfigurationDetails of(Configuration configuration) {
     }
 
     private static boolean canBeResolved(Configuration configuration) {
-        boolean isDeprecatedForResolving = ((DeprecatableConfiguration) configuration).getResolutionAlternatives() != null;
+        boolean isDeprecatedForResolving = ((DeprecatableConfiguration) configuration).isDeprecatedForResolution();
         return configuration.isCanBeResolved() && !isDeprecatedForResolving;
     }
 
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/AbstractRenderableModuleResult.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/AbstractRenderableModuleResult.java
index 7dc406a..badb31b 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/AbstractRenderableModuleResult.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/AbstractRenderableModuleResult.java
@@ -50,7 +50,7 @@ public List<ResolvedVariantResult> getResolvedVariants() {
 
     @Override
     public List<ResolvedVariantResult> getAllVariants() {
-        return ((ResolvedComponentResultInternal) module).getAllVariants();
+        return ((ResolvedComponentResultInternal) module).getAvailableVariants();
     }
 
     @Override
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/ResolvedDependencyEdge.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/ResolvedDependencyEdge.java
index cee3a22..6897411 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/ResolvedDependencyEdge.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/ResolvedDependencyEdge.java
@@ -56,7 +56,7 @@ public List<ResolvedVariantResult> getSelectedVariants() {
 
     @Override
     public List<ResolvedVariantResult> getAllVariants() {
-        return ((ResolvedComponentResultInternal) dependency.getSelected()).getAllVariants();
+        return ((ResolvedComponentResultInternal) dependency.getSelected()).getAvailableVariants();
     }
 
     @Override
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/UnresolvableConfigurationResult.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/UnresolvableConfigurationResult.java
index e2768fe..ff4dbcb 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/UnresolvableConfigurationResult.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/graph/nodes/UnresolvableConfigurationResult.java
@@ -38,11 +38,13 @@ public static UnresolvableConfigurationResult of(Configuration configuration) {
         );
     }
 
+    @SuppressWarnings("deprecation")
     private static Set<? extends RenderableDependency> unresolvableChildren(Configuration configuration) {
         final DependencySet dependencies = configuration.getDependencies();
         if (dependencies.isEmpty()) {
             return Collections.emptySet();
         }
+
         Set<UnresolvableRenderableDependency> children = Sets.newLinkedHashSet();
         for (final Dependency dependency : dependencies) {
             children.add(new UnresolvableRenderableDependency(
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporter.java b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporter.java
index 3f81902..a0e9533 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporter.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporter.java
@@ -40,6 +40,7 @@
 import org.gradle.api.tasks.diagnostics.internal.graph.nodes.Section;
 import org.gradle.api.tasks.diagnostics.internal.graph.nodes.UnresolvedDependencyEdge;
 import org.gradle.internal.InternalTransformer;
+import org.gradle.internal.exceptions.ResolutionProvider;
 import org.gradle.internal.logging.text.TreeFormatter;
 import org.gradle.util.internal.CollectionUtils;
 
@@ -145,6 +146,12 @@ private static String collectErrorMessages(Throwable failure, Set<Throwable> alr
     private static void collectErrorMessages(Throwable failure, TreeFormatter formatter, Set<Throwable> alreadyReportedErrors) {
         if (alreadyReportedErrors.add(failure)) {
             formatter.node(failure.getMessage());
+            if(failure instanceof ResolutionProvider){
+                ResolutionProvider resolutionProvider = (ResolutionProvider) failure;
+                resolutionProvider.getResolutions().forEach(resolution -> {
+                    formatter.node(resolution);
+                });
+            }
             Throwable cause = failure.getCause();
             if (alreadyReportedErrors.contains(cause)) {
                 formatter.append(" (already reported)");
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/configuration/TaskDetailPrinter.java b/subprojects/diagnostics/src/main/java/org/gradle/configuration/TaskDetailPrinter.java
index 6dd1343..4f136cb 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/configuration/TaskDetailPrinter.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/configuration/TaskDetailPrinter.java
@@ -37,6 +37,7 @@
 import static org.gradle.internal.logging.text.StyledTextOutput.Style.UserInput;
 import static org.gradle.util.internal.CollectionUtils.collect;
 import static org.gradle.util.internal.CollectionUtils.sort;
+import static org.gradle.util.internal.TextUtil.getPluralEnding;
 
 @NonNullApi
 public class TaskDetailPrinter {
@@ -62,7 +63,7 @@ public void print(StyledTextOutput output) {
             final TaskDetails anyTask = tasksByType.iterator().next();
             String shortTypeName = anyTask.getShortTypeName();
             final LinePrefixingStyledTextOutput pathOutput = createIndentedOutput(output, INDENT);
-            pathOutput.println(tasksByType.size() > 1 ? "Paths" : "Path");
+            pathOutput.println("Path" + getPluralEnding(tasksByType));
             for (TaskDetails task : tasksByType) {
                 pathOutput.withStyle(UserInput).println(task.getPath());
             }
diff --git a/subprojects/diagnostics/src/main/java/org/gradle/configuration/TaskDetails.java b/subprojects/diagnostics/src/main/java/org/gradle/configuration/TaskDetails.java
index 75c5bb6..62fbefc 100644
--- a/subprojects/diagnostics/src/main/java/org/gradle/configuration/TaskDetails.java
+++ b/subprojects/diagnostics/src/main/java/org/gradle/configuration/TaskDetails.java
@@ -22,6 +22,7 @@
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.internal.plugins.DslObject;
+import org.gradle.api.internal.tasks.TaskOptionsGenerator;
 import org.gradle.api.internal.tasks.options.OptionDescriptor;
 import org.gradle.api.internal.tasks.options.OptionReader;
 
@@ -138,7 +139,7 @@ public List<OptionDetails> getOptions() {
     public static TaskDetails from(Task task, OptionReader optionReader) {
         String path = task.getPath();
         int projectDepth = StringUtils.countMatches(path, Project.PATH_SEPARATOR);
-        List<OptionDetails> options = optionReader.getOptions(task).stream().map(OptionDetails::from).collect(Collectors.toList());
+        List<OptionDetails> options = TaskOptionsGenerator.generate(task, optionReader).getAll().stream().map(OptionDetails::from).collect(Collectors.toList());
         Class<?> declaredTaskType = getDeclaredTaskType(task);
         String taskType = declaredTaskType.getName();
         String shortTypeName = declaredTaskType.getSimpleName();
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecTest.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecTest.groovy
index 1e87740..67cf32b 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecTest.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/dsl/DependencyResultSpecTest.groovy
@@ -29,7 +29,7 @@
         new DependencyResultSpec(notation).isSatisfiedBy(newDependency("org.foo", "foo-core", "1.0"))
 
         where:
-        notation   << ['org', 'org.foo', 'foo-core', '1.0', 'org.foo:foo-core', 'org.foo:foo-core:1.0']
+        notation << ['org', 'org.foo', 'foo-core', '1.0', 'org.foo:foo-core', 'org.foo:foo-core:1.0']
     }
 
     def "knows mismatching dependencies"() {
diff --git a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporterSpec.groovy b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporterSpec.groovy
index 0f50c6c..2986e6a 100644
--- a/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporterSpec.groovy
+++ b/subprojects/diagnostics/src/test/groovy/org/gradle/api/tasks/diagnostics/internal/insight/DependencyInsightReporterSpec.groovy
@@ -192,12 +192,12 @@
     }
 
     private static DefaultResolvedDependencyResult dep(String group, String name, String requested, String selected = requested, ComponentSelectionReason selectionReason = ComponentSelectionReasons.requested()) {
-        def selectedModule = new DefaultResolvedComponentResult(newId(group, name, selected), selectionReason, new DefaultModuleComponentIdentifier(DefaultModuleIdentifier.newId(group, name), selected), [defaultVariant()], [defaultVariant()], "repoId")
+        def selectedModule = new DefaultResolvedComponentResult(newId(group, name, selected), selectionReason, new DefaultModuleComponentIdentifier(DefaultModuleIdentifier.newId(group, name), selected), [1: defaultVariant()], [defaultVariant()], "repoId")
         new DefaultResolvedDependencyResult(newSelector(DefaultModuleIdentifier.newId(group, name), requested),
                 false,
                 selectedModule,
                 null,
-                new DefaultResolvedComponentResult(newId("a", "root", "1"), ComponentSelectionReasons.requested(), new DefaultModuleComponentIdentifier(DefaultModuleIdentifier.newId(group, name), selected), [defaultVariant()], [defaultVariant()], "repoId"))
+                new DefaultResolvedComponentResult(newId("a", "root", "1"), ComponentSelectionReasons.requested(), new DefaultModuleComponentIdentifier(DefaultModuleIdentifier.newId(group, name), selected), [1: defaultVariant()], [defaultVariant()], "repoId"))
     }
 
     private static DefaultResolvedVariantResult defaultVariant(String ownerGroup = 'com', String ownerModule = 'foo', String ownerVersion = '1.0') {
@@ -208,10 +208,10 @@
     }
 
     private static DefaultResolvedDependencyResult path(String path) {
-        DefaultResolvedComponentResult from = new DefaultResolvedComponentResult(newId("group", "root", "1"), ComponentSelectionReasons.requested(), new DefaultModuleComponentIdentifier(DefaultModuleIdentifier.newId("group", "root"), "1"), [defaultVariant()], [defaultVariant()], "repoId")
+        DefaultResolvedComponentResult from = new DefaultResolvedComponentResult(newId("group", "root", "1"), ComponentSelectionReasons.requested(), new DefaultModuleComponentIdentifier(DefaultModuleIdentifier.newId("group", "root"), "1"), [1: defaultVariant()], [defaultVariant()], "repoId")
         List<DefaultResolvedDependencyResult> pathElements = (path.split(' -> ') as List).reverse().collect {
             def (name, version) = it.split(':')
-            def componentResult = new DefaultResolvedComponentResult(newId('group', name, version), ComponentSelectionReasons.requested(), DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId('group', name), version), [defaultVariant()], [defaultVariant()], "repoId")
+            def componentResult = new DefaultResolvedComponentResult(newId('group', name, version), ComponentSelectionReasons.requested(), DefaultModuleComponentIdentifier.newId(DefaultModuleIdentifier.newId('group', name), version), [1: defaultVariant()], [defaultVariant()], "repoId")
             def result = new DefaultResolvedDependencyResult(newSelector(DefaultModuleIdentifier.newId("group", name), version), false, componentResult, null, from)
             from = componentResult
             result
diff --git a/subprojects/distributions-dependencies/build.gradle.kts b/subprojects/distributions-dependencies/build.gradle.kts
index 03d9e2a..a670354 100644
--- a/subprojects/distributions-dependencies/build.gradle.kts
+++ b/subprojects/distributions-dependencies/build.gradle.kts
@@ -15,8 +15,8 @@
 
 description = "Provides a platform dependency to align all distribution versions"
 
-val antVersion = "1.10.11"
-val asmVersion = "9.3"
+val antVersion = "1.10.13"
+val asmVersion = "9.4"
 val awsS3Version = "1.12.365"
 val bouncycastleVersion = "1.68"
 val jacksonVersion = "2.14.1"
@@ -67,7 +67,7 @@
         api(libs.commonsLang3)          { version { strictly("3.12.0") }}
         api(libs.commonsMath)           { version { strictly("3.6.1") }}
         api(libs.fastutil)              { version { strictly("8.5.2") }}
-        api(libs.gradleProfiler)        { version { strictly("0.19.0") }}
+        api(libs.gradleProfiler)        { version { strictly("0.20.0-alpha01") }}
         api(libs.gradleEnterpriseTestAnnotation) { version { strictly("1.0") }}
         api(libs.gcs)                   { version { strictly("v1-rev20220705-1.32.1") }}
         api(libs.googleApiClient)       { version { strictly("1.34.0"); because("our GCS version requires 1.34.0") }}
@@ -90,8 +90,9 @@
         api(libs.groovyTest)            { version { strictly(libs.groovyVersion) }}
         api(libs.groovyXml)             { version { strictly(libs.groovyVersion) }}
         api(libs.gson)                  { version { strictly("2.8.9") }}
+        api(libs.h2Database)            { version { strictly("2.1.214") }}
         api(libs.hamcrest)              { version { strictly("1.3"); because("2.x changes the API") }}
-        api(libs.hikariCP)              { version { strictly("4.0.2") }}
+        api(libs.hikariCP)              { version { strictly("4.0.3"); because("5.x requires Java 11+") }}
         api(libs.httpcore)              { version { strictly("4.4.14") }}
         api(libs.inject)                { version { strictly("1") }}
         api(libs.ivy)                   { version { strictly("2.3.0"); because("2.4.0 contains a breaking change in DefaultModuleDescriptor.getExtraInfo(), cf. https://issues.apache.org/jira/browse/IVY-1457") }}
@@ -103,6 +104,7 @@
         api(libs.jakartaXmlBind)        { version { strictly("3.0.0") }}
         api(libs.jansi)                 { version { strictly("1.18"); because("2.x changes the API") }}
         api(libs.jatl)                  { version { strictly("0.2.3") }}
+        api(libs.javaPoet)              { version { strictly("1.13.0") } }
         api(libs.jaxbCore)              { version { strictly(jaxbVersion) }}
         api(libs.jaxbImpl)              { version { strictly(jaxbVersion) }}
         api(libs.jcifs)                 { version { strictly("1.3.17") }}
@@ -139,7 +141,7 @@
         api(libs.plist)                 { version { strictly("1.21") }}
         api(libs.servletApi)            { version { strictly("3.1.0") }}
         api(libs.slf4jApi)              { version { strictly(slf4jVersion) }}
-        api(libs.snakeyaml)             { version { strictly("1.32") }}
+        api(libs.snakeyaml)             { version { strictly("2.0") }}
         api(libs.testng)                { version { strictly("6.3.1"); because("later versions break test cross-version test filtering") }}
         api(libs.tomlj)                 { version { strictly(tomljVersion) }}
         api(libs.trove4j)               { version { strictly("1.0.20200330") }}
@@ -161,6 +163,7 @@
         api(libs.bytebuddy)             { version { strictly(bytebuddyVersion) }}
         api(libs.bytebuddyAgent)        { version { strictly(bytebuddyVersion) }}
         api(libs.cglib)                 { version { strictly("3.2.6") }}
+        api(libs.compileTesting)        { version { strictly("0.21.0")}}
         api(libs.equalsverifier)        { version { strictly("2.1.6") }}
         api(libs.guice)                 { version { strictly("4.2.3") }}
         api(libs.httpmime)              { version { strictly("4.5.10") }}
diff --git a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/AllDistributionIntegrationSpec.groovy b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/AllDistributionIntegrationSpec.groovy
index 7127dea..aeba16c 100644
--- a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/AllDistributionIntegrationSpec.groovy
+++ b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/AllDistributionIntegrationSpec.groovy
@@ -17,8 +17,8 @@
 package org.gradle
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Shared
 
 class AllDistributionIntegrationSpec extends DistributionIntegrationSpec {
@@ -36,10 +36,10 @@
 
     @Override
     int getMaxDistributionSizeBytes() {
-        return 162 * 1024 * 1024
+        return 186 * 1024 * 1024
     }
 
-    @Requires(TestPrecondition.STABLE_GROOVY) // cannot link to public javadocs of Groovy snapshots like https://docs.groovy-lang.org/docs/groovy-4.0.5-SNAPSHOT/html/gapi/
+    @Requires(UnitTestPreconditions.StableGroovy) // cannot link to public javadocs of Groovy snapshots like https://docs.groovy-lang.org/docs/groovy-4.0.5-SNAPSHOT/html/gapi/
     def allZipContents() {
         given:
         TestFile contentsDir = unpackDistribution()
diff --git a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/BinDistributionIntegrationSpec.groovy b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/BinDistributionIntegrationSpec.groovy
index 3a942ae..190fc31 100644
--- a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/BinDistributionIntegrationSpec.groovy
+++ b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/BinDistributionIntegrationSpec.groovy
@@ -31,7 +31,7 @@
 
     @Override
     int getMaxDistributionSizeBytes() {
-        return 120 * 1024 * 1024
+        return 125 * 1024 * 1024
     }
 
     def binZipContents() {
diff --git a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DistributionIntegrationSpec.groovy b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DistributionIntegrationSpec.groovy
index c613dc5..a183400 100644
--- a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DistributionIntegrationSpec.groovy
+++ b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DistributionIntegrationSpec.groovy
@@ -20,8 +20,8 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.archive.JarTestFixture
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.PreconditionVerifier
 import org.gradle.util.GradleVersion
-import org.gradle.util.PreconditionVerifier
 import org.gradle.util.internal.GUtil
 import org.junit.Rule
 import spock.lang.Shared
@@ -36,7 +36,7 @@
 
 abstract class DistributionIntegrationSpec extends AbstractIntegrationSpec {
 
-    protected static final THIRD_PARTY_LIB_COUNT = 150
+    protected static final THIRD_PARTY_LIB_COUNT = 151
 
     @Rule public final PreconditionVerifier preconditionVerifier = new PreconditionVerifier()
 
@@ -50,7 +50,7 @@
      * Change this whenever you add or remove subprojects for distribution core modules (lib/).
      */
     int getCoreLibJarsCount() {
-        43
+        45
     }
 
     /**
diff --git a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DistributionIntegritySpec.groovy b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DistributionIntegritySpec.groovy
index 4d17fcd..e836df2 100644
--- a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DistributionIntegritySpec.groovy
+++ b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DistributionIntegritySpec.groovy
@@ -35,7 +35,7 @@
 
     @Override
     int getMaxDistributionSizeBytes() {
-        return 120 * 1024 * 1024
+        return 125 * 1024 * 1024
     }
 
     @Issue(['https://github.com/gradle/gradle/issues/9990', 'https://github.com/gradle/gradle/issues/10038'])
diff --git a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DocsDistributionIntegrationSpec.groovy b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DocsDistributionIntegrationSpec.groovy
index 82fc536..b5363e8 100644
--- a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DocsDistributionIntegrationSpec.groovy
+++ b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/DocsDistributionIntegrationSpec.groovy
@@ -18,8 +18,8 @@
 
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Shared
 
 class DocsDistributionIntegrationSpec extends DistributionIntegrationSpec {
@@ -32,7 +32,7 @@
 
     @Override
     int getMaxDistributionSizeBytes() {
-        return 45 * 1024 * 1024
+        return 60 * 1024 * 1024
     }
 
     @Override
@@ -40,7 +40,7 @@
         0
     }
 
-    @Requires([TestPrecondition.NOT_WINDOWS, TestPrecondition.STABLE_GROOVY]) // cannot link to public javadocs of Groovy snapshots like https://docs.groovy-lang.org/docs/groovy-4.0.5-SNAPSHOT/html/gapi/
+    @Requires([UnitTestPreconditions.NotWindows, UnitTestPreconditions.StableGroovy]) // cannot link to public javadocs of Groovy snapshots like https://docs.groovy-lang.org/docs/groovy-4.0.5-SNAPSHOT/html/gapi/
     def docsZipContents() {
         given:
         TestFile contentsDir = unpackDistribution()
diff --git a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/SrcDistributionIntegrationSpec.groovy b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/SrcDistributionIntegrationSpec.groovy
index cb84d6a..52f8b55 100644
--- a/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/SrcDistributionIntegrationSpec.groovy
+++ b/subprojects/distributions-integ-tests/src/integTest/groovy/org/gradle/SrcDistributionIntegrationSpec.groovy
@@ -21,8 +21,8 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.internal.jvm.Jvm
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.AntUtil
 import org.gradle.util.internal.ToBeImplemented
 
@@ -46,7 +46,7 @@
         0
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @ToBeFixedForConfigurationCache
     def sourceZipContents() {
         given:
@@ -84,7 +84,7 @@
     }
 
     @ToBeImplemented("https://github.com/gradle/gradle/issues/21114")
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "source distribution must contain generated sources"() {
         given:
         TestFile contentsDir = unpackDistribution()
diff --git a/subprojects/docs/README.md b/subprojects/docs/README.md
index dd6b3c5..e042dc8 100644
--- a/subprojects/docs/README.md
+++ b/subprojects/docs/README.md
@@ -145,11 +145,72 @@
 
 It is possible to embed sample sources, commands, and expected output directly in the Asciidoc (or a mixture of embedded and `include`d), but we don't use this for the user manual yet. See the [Exemplar documentation](https://github.com/gradle/exemplar/#configuring-embedded-samples) if you're interested in this.
 
-### Testing samples and snippets
+## Testing docs 
 
-#### Code samples
+Currently, `docs` is tested by `docs:docsTest`, which covers three kinds of code: 
 
-Samples and output belong under `src/samples` and are published beside the user manual. See the `org.gradle.samples` plugin.
+- The code generated by [Build Init Plugin](https://docs.gradle.org/current/userguide/build_init_plugin.html), i.e. `gradle init` task.
+  - `generate-samples.gradle.kts` registers multiple generator tasks that generate the same sample project code as you manually run `gradle init`.
+  - These sample projects will also be published beside the user manual.
+- The code samples under `subprojects/docs/src/samples`. They are published beside the user manual.
+- [The code snippets](#code-snippets) under `subprojects/docs/src/snippets`, which are typically included in the user manual.
+
+Note: the terminology `sample` could refer to different things depending on the context:
+
+- The code samples under `subprojects/docs/src/samples`. We'll call them "code samples."
+- The test unit used by [exemplar framework](https://github.com/gradle/exemplar/blob/9c4415f6237d7d86329fdbb32d80a2f7dd8ae0a9/samples-discovery/src/main/java/org/gradle/exemplar/model/Sample.java#L21). We'll call them "exemplar sample".
+
+The build script of the `docs` subproject (`supprojects/docs/build.gradle`) eventually assembles the three kinds of docs code above to exemplar samples and then tests them with exemplar.
+
+### `org.gradle.samples` plugin
+
+`subprojects/docs/build.gradle` applies `generate-samples.gradle.kts`, which further applies an opinionated plugin called `org.gradle.samples`.
+The source code of this plugin is [here](https://github.com/gradle/guides/blob/ba018cec535d90f75876bfcca29381d213a956cc/subprojects/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/LegacySamplesDocumentationPlugin.java#L9).
+This plugin adds a [`Samples`](https://github.com/gradle/guides/blob/fa335417efb5656e202e95759ebf8a4e60843f10/subprojects/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/Samples.java#L8) extension named `samples`.
+
+This `samples` extension is configured in both `subprojects/docs/build.gradle` and `generate-samples.gradle.kts`:
+all docs code to be tested will be assembled into [`samples.publishedSamples`](https://github.com/gradle/guides/blob/fa335417efb5656e202e95759ebf8a4e60843f10/subprojects/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/Samples.java#L41), as follows:
+
+```
+This graph is generated by [asciiflow.com](https://asciiflow.com/). You can copy-paste it to the website to modify it. 
+
+┌────────────────────────────────────┐
+│ subprojects/docs/build.gradle      │
+│ ┌────────────────────────────────┐ │    ┌─────────────────────────────────┐
+│ │ generate-samples.gradle.kts    ├─┼───►│ code generated by init tasks    ├───┐
+│ │                                │ │    │                                 │   │
+│ └────────────────────────────────┘ │    └─────────────────────────────────┘   │
+│                                    │                                          │
+│  samples {                         │    ┌─────────────────────────────────┐   │
+│    ...                             │    │ code snippets in src/snippets   ├───┤
+│    publishedSamples {  ────────────┼─┬─►│                                 │   │
+│      ...                           │ │  └─────────────────────────────────┘   │
+└────────────────────────────────────┘ │                                        │
+                                       │  ┌─────────────────────────────────┐   │
+                                       └─►│ code samples in src/samples     ├───┤
+                                          │                                 │   │
+                                          └─────────────────────────────────┘   │
+                                                                                │
+                                                                                │
+                                        ┌───────────────────────────────────┐   │
+                                        │ org.gradle.samples plugin         │   │
+                                        │ ┌─────────────────────────────┐   │   │
+┌─────────────┐   Install samples to    │ │ Samples.publishedSamples    │   │   │
+│  Exemplar   │   local directory and   │ │                             │   │   │
+│             │   test with exemplar    │ │                             │   │   │
+│             │◄────────────────────────┤ │                             ◄───┼───┘
+│             │                         │ │                             │   │
+└─────────────┘                         │ │                             │   │
+                                        │ └─────────────────────────────┘   │
+                                        │                                   │
+                                        └───────────────────────────────────┘
+```
+
+The elements in `samples.publishedSamples` container will later be installed into a local directory (by default [`docs/build/working/samples/install`](https://github.com/gradle/guides/blob/900650c6fd6c980ae7335d7aab6dea200a693aa0/subprojects/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/SamplesInternal.java#L46)) as exemplar samples.
+
+After the exemplar examples are installed, `docs:docsTest` will start testing them (see [`BaseSamplesTest`](https://github.com/gradle/gradle/blob/a503d4a36c53e43a8857da3115fa744612c6ad36/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/BaseSamplesTest.java#L66)).
+
+### Code samples
 
 To run the samples tests:
 ```
@@ -161,9 +222,9 @@
 ./gradlew :docs:docsTest --tests "org.gradle.docs.samples.DependencyManagementSnippetsTest.java-application*"
 ```
 
-#### Code snippets
+Note that the samples are also used in `samples` subproject, see [`@UsesSample`](https://github.com/gradle/gradle/blob/9ade1a05427aaf04c976a0e85814b44b3435f9f9/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/UsesSample.java#L25) and [`Sample`](https://github.com/gradle/gradle/blob/903c5f2cee88c9768077d46025eaafdf65862fc8/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/Sample.java#L37).
 
-Snippets live under `src/snippets`.
+### Code snippets
 
 As an example, you can run Kotlin and Groovy snippets tests from [`src/snippets/java/toolchain-task/`](src/snippets/java/toolchain-task) using:
 ```
@@ -175,7 +236,7 @@
 ./gradlew :docs:docsTest --tests "*.snippet-java-toolchain-task_kotlin_*"
 ```
 
-#### Testing with configuration cache
+### Testing with configuration cache
 
 It is possible to run samples and snippets with the configuration cache enabled to ensure compatibility.
 You can do that by setting the Gradle property `enableConfigurationCacheForDocsTests` in the command line or in the `gradle.properties` file.
diff --git a/subprojects/docs/build.gradle b/subprojects/docs/build.gradle
index 6f82c89..7c08c04 100644
--- a/subprojects/docs/build.gradle
+++ b/subprojects/docs/build.gradle
@@ -555,9 +555,11 @@
     }
 }
 
-// Use the version of Gradle being built, not the version of Gradle used to build
-tasks.named("generateWrapperForSamples") {
+// Use the version of Gradle being built, not the version of Gradle used to build,
+// also don't validate distribution url, since it is just a local distribution
+tasks.named("generateWrapperForSamples", Wrapper) {
     gradleVersion = project.version
+    validateDistributionUrl = false
 }
 
 // TODO: The rich console to plain text is flaky
@@ -722,6 +724,8 @@
                 "snippet-ide-idea-additional-test-sources_kotlin_ideaAdditionalTestSources.sample",
                 "snippet-ide-idea_groovy_projectWithXml.sample",
                 "snippet-ide-idea_kotlin_projectWithXml.sample",
+                "snippet-init-scripts-custom-logger_groovy_customLogger.groovy.sample",
+                "snippet-init-scripts-custom-logger_kotlin_customLogger.kotlin.sample",
                 "snippet-model-rules-basic-rule-source-plugin_groovy_basicRuleSourcePlugin-all.sample",
                 "snippet-model-rules-basic-rule-source-plugin_groovy_basicRuleSourcePlugin-model-task.sample",
                 "snippet-model-rules-configure-as-required_groovy_modelDslConfigureRuleRunWhenRequired.sample",
@@ -757,36 +761,6 @@
                 "snippet-dependency-management-working-with-dependencies-iterate-dependencies_groovy_iterating-dependencies.sample",
                 "snippet-dependency-management-working-with-dependencies-iterate-dependencies_kotlin_iterating-dependencies.sample",
 
-                // TODO(https://github.com/gradle/gradle/issues/19725) EAR plugin support
-                "snippet-ear-ear-customized_groovy_earCustomized.sample",
-                "snippet-ear-ear-customized_kotlin_earCustomized.sample",
-                "snippet-ear-ear-with-war_groovy_earWithWar.sample",
-                "snippet-ear-ear-with-war_kotlin_earWithWar.sample",
-
-                // TODO(https://github.com/gradle/gradle/issues/13469) Ivy Publish plugin support
-                "snippet-ivy-publish-conditional-publishing_groovy_publishingIvyConditionally.sample",
-                "snippet-ivy-publish-descriptor-customization_groovy_publishingIvyGenerateDescriptor.sample",
-                "snippet-ivy-publish-descriptor-customization_kotlin_publishingIvyGenerateDescriptor.sample",
-                "snippet-ivy-publish-java-multi-project_groovy_publish-multi-project.sample",
-                "snippet-ivy-publish-java-multi-project_kotlin_publish-multi-project.sample",
-                "snippet-ivy-publish-quickstart_groovy_publishingIvyPublishLifecycle.sample",
-                "snippet-ivy-publish-quickstart_groovy_publishingIvyPublishSingle.sample",
-                "snippet-ivy-publish-quickstart_kotlin_publishingIvyPublishLifecycle.sample",
-                "snippet-ivy-publish-quickstart_kotlin_publishingIvyPublishSingle.sample",
-                "snippet-plugins-publishing_groovy_publishToIvy.sample",
-                "snippet-plugins-publishing_kotlin_publishToIvy.sample",
-                "snippet-tooling-api-custom-model_groovy_sanityCheck.sample",
-
-                // TODO(https://github.com/gradle/gradle/issues/13470) Signing plugin support
-                "snippet-signing-configurations_groovy_signingArchivesOutput.sample",
-                "snippet-signing-configurations_kotlin_signingArchivesOutput.sample",
-                "snippet-signing-maven-publish_groovy_publishingMavenSignAndPublish.sample",
-                "snippet-signing-maven-publish_groovy_signingPluginSignPublication.sample",
-                "snippet-signing-maven-publish_kotlin_publishingMavenSignAndPublish.sample",
-                "snippet-signing-maven-publish_kotlin_signingPluginSignPublication.sample",
-                "snippet-signing-tasks_groovy_signingTaskOutput.sample",
-                "snippet-signing-tasks_kotlin_signingTaskOutput.sample",
-
                 // TODO(https://github.com/gradle/gradle/issues/22879) The snippet extracts build logic into a method and calls the method at execution time
                 "snippet-tutorial-ant-loadfile-with-method_groovy_antLoadfileWithMethod.sample",
                 "snippet-tutorial-ant-loadfile-with-method_kotlin_antLoadfileWithMethod.sample",
@@ -797,6 +771,12 @@
             def testsToBeFixedForConfigurationCache = [
                 "snippet-build-cache-configure-task_groovy_configureTask.sample",
                 "snippet-build-cache-configure-task_kotlin_configureTask.sample",
+                // TODO(mlopatkin) These snippets use bintray plugin which is not fully CC-compatible. Remove bintray plugin from samples.
+                "snippet-plugins-buildscript_groovy_sanityCheck.sample",
+                "snippet-plugins-buildscript_kotlin_sanityCheck.sample",
+                "snippet-plugins-dsl_groovy_sanityCheck.sample",
+                "snippet-plugins-dsl_kotlin_sanityCheck.sample",
+                "snippet-kotlin-dsl-interoperability-closure-of_kotlin_sanityCheck.sample",
             ]
 
             def brokenTests = testsForUnsupportedFeatures + testsWithThirdPartyFailures + testsForNotYetSupportedFeatures + testsToBeFixedForConfigurationCache
diff --git a/subprojects/docs/src/docs/css/manual.css b/subprojects/docs/src/docs/css/manual.css
index 961a266..7b526a7 100644
--- a/subprojects/docs/src/docs/css/manual.css
+++ b/subprojects/docs/src/docs/css/manual.css
@@ -1834,7 +1834,14 @@
     padding: 30px 12px;
     max-width: 62.5rem;
     margin: 0 auto;
-    padding-left: 5rem
+    padding-left: 5rem;
+}
+
+@media not screen and (min-width: 64rem) {
+    .site-footer__navigation {
+        /* same to nav.docs-navigation for mobiles */
+        padding: 20px 20px 20px 26px;
+    }
 }
 
 .site-footer__links {
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.JavaExec.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.JavaExec.xml
index ed741a7..261bf64 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.JavaExec.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.JavaExec.xml
@@ -17,6 +17,10 @@
                 <td><literal>[]</literal></td>
             </tr>
             <tr>
+                <td>jvmArguments</td>
+                <td><literal>null</literal></td>
+            </tr>
+            <tr>
                 <td>jvmArgumentProviders</td>
                 <td><literal>[]</literal></td>
             </tr>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.wrapper.Wrapper.xml b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.wrapper.Wrapper.xml
index 25b47a3..be9cbff 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.wrapper.Wrapper.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.api.tasks.wrapper.Wrapper.xml
@@ -56,6 +56,10 @@
                 <td>networkTimeout</td>
                 <td><literal>10000</literal>ms</td>
             </tr>
+            <tr>
+                <td>validateDistributionUrl</td>
+                <td><literal>true</literal></td>
+            </tr>
         </table>
     </section>
     <section>
diff --git a/subprojects/docs/src/docs/dsl/org.gradle.testing.jacoco.plugins.JacocoPluginExtension.xml b/subprojects/docs/src/docs/dsl/org.gradle.testing.jacoco.plugins.JacocoPluginExtension.xml
index d653182..f7fbe5e 100644
--- a/subprojects/docs/src/docs/dsl/org.gradle.testing.jacoco.plugins.JacocoPluginExtension.xml
+++ b/subprojects/docs/src/docs/dsl/org.gradle.testing.jacoco.plugins.JacocoPluginExtension.xml
@@ -26,7 +26,7 @@
             </thead>
             <tr>
                 <td>toolVersion</td>
-                <td><literal>0.8.8</literal></td>
+                <td><literal>0.8.9</literal></td>
             </tr>
             <tr>
                 <td>reportsDirectory</td>
diff --git a/subprojects/docs/src/docs/kotlin/Module.md b/subprojects/docs/src/docs/kotlin/Module.md
new file mode 100644
index 0000000..7252ccc
--- /dev/null
+++ b/subprojects/docs/src/docs/kotlin/Module.md
@@ -0,0 +1,19 @@
+# Module gradle
+
+# Gradle Kotlin DSL Reference
+
+Gradle’s Kotlin DSL provides an enhanced editing experience in supported IDEs, with superior content assist, refactoring, documentation, and more.
+For an introduction see the <a href="../userguide/kotlin_dsl.html">Kotlin DSL Primer</a>.
+
+The Kotlin DSL is implemented on top of Gradle’s Java API.
+Many of the objects, functions and properties you use in your build scripts come from the Gradle API and the APIs of the applied plugins.
+This reference covers both the Kotlin DSL and the Java API, but it doesn't include functionality provided by external plugins.
+
+The main package of the Kotlin DSL is <a href="./gradle/org.gradle.kotlin.dsl/index.html">org.gradle.kotlin.dsl</a>.
+All members of this package are implicitly imported and readily available in `.gradle.kts` scripts in addition to the Java API <a href="../userguide/writing_build_scripts.html#script-default-imports">default imports</a>.
+
+# Package org.gradle.kotlin.dsl
+
+The `org.gradle.kotlin.dsl` package contains the Gradle Kotlin DSL public API.
+
+All members of this package are implicitly imported and readily available in `.gradle.kts` scripts in addition to the Gradle Java API <a href="../../../userguide/writing_build_scripts.html#script-default-imports">default imports</a>.
diff --git a/subprojects/docs/src/docs/kotlin/images/gradle-logo.svg b/subprojects/docs/src/docs/kotlin/images/gradle-logo.svg
new file mode 100644
index 0000000..2c20d9a
--- /dev/null
+++ b/subprojects/docs/src/docs/kotlin/images/gradle-logo.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 66.06"><defs><style>.cls-1{fill:#fff;}</style></defs><title>gradle-elephant-icon-white-primary</title><path class="cls-1" d="M85.11,4.18a14.27,14.27,0,0,0-19.83-.34,1.38,1.38,0,0,0,0,2L67,7.6a1.36,1.36,0,0,0,1.78.12A8.18,8.18,0,0,1,79.5,20.06C68.17,31.38,53.05-.36,18.73,16a4.65,4.65,0,0,0-2,6.54l5.89,10.17a4.64,4.64,0,0,0,6.3,1.73l.14-.08-.11.08L31.53,33a60.29,60.29,0,0,0,8.22-6.13,1.44,1.44,0,0,1,1.87-.06h0a1.34,1.34,0,0,1,.06,2A61.61,61.61,0,0,1,33,35.34l-.09,0-2.61,1.46a7.34,7.34,0,0,1-3.61.94,7.45,7.45,0,0,1-6.47-3.71l-5.57-9.61C4,32-2.54,46.56,1,65a1.36,1.36,0,0,0,1.33,1.11H8.61A1.36,1.36,0,0,0,10,64.87a9.29,9.29,0,0,1,18.42,0,1.35,1.35,0,0,0,1.34,1.19H35.9a1.36,1.36,0,0,0,1.34-1.19,9.29,9.29,0,0,1,18.42,0A1.36,1.36,0,0,0,57,66.06H63.1a1.36,1.36,0,0,0,1.36-1.34c.14-8.6,2.46-18.48,9.07-23.43C96.43,24.16,90.41,9.48,85.11,4.18ZM61.76,30.05l-4.37-2.19h0a2.74,2.74,0,1,1,4.37,2.2Z"/></svg>
\ No newline at end of file
diff --git a/subprojects/docs/src/docs/kotlin/styles/gradle.css b/subprojects/docs/src/docs/kotlin/styles/gradle.css
new file mode 100644
index 0000000..61b28f4
--- /dev/null
+++ b/subprojects/docs/src/docs/kotlin/styles/gradle.css
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/* Adjust top left logo */
+
+.library-name a {
+    position: relative;
+    margin-left: 65px;
+}
+
+.library-name a::before {
+    content: '';
+    background: url("../images/gradle-logo.svg") center no-repeat;
+    background-size: contain;
+    position: absolute;
+    width: 65px;
+    height: 65px;
+    top: -27px;
+    left: -72px;
+}
+
+/* Hide the module name title on the front page */
+
+div.main-content:not([data-page-type]) h1.cover {
+    display: none;
+}
+
+/* Capitalize the module name */
+
+div.library-name,
+div.sideMenuPart div.overview:first-child a[href="index.html"],
+div.breadcrumbs a:first-child {
+    text-transform: capitalize;
+}
diff --git a/subprojects/docs/src/docs/release/notes-template.md b/subprojects/docs/src/docs/release/notes-template.md
index e5b4565..24012a2 100644
--- a/subprojects/docs/src/docs/release/notes-template.md
+++ b/subprojects/docs/src/docs/release/notes-template.md
@@ -16,7 +16,7 @@
 
 `./gradlew wrapper --gradle-version=@version@`
 
-See the [Gradle 7.x upgrade guide](userguide/upgrading_version_7.html#changes_@baseVersion@) to learn about deprecations, breaking changes and other considerations when upgrading to Gradle @version@.
+See the [Gradle 8.x upgrade guide](userguide/upgrading_version_8.html#changes_@baseVersion@) to learn about deprecations, breaking changes and other considerations when upgrading to Gradle @version@.
 
 For Java, Groovy, Kotlin and Android compatibility, see the [full compatibility notes](userguide/compatibility.html).   
 
@@ -70,10 +70,18 @@
 
 ## Fixed issues
 
+<!--
+This section will be populated automatically
+-->
+
 ## Known issues
 
 Known issues are problems that were discovered post release that are directly related to changes made in this release.
 
+<!--
+This section will be populated automatically
+-->
+
 ## External contributions
 
 We love getting contributions from the Gradle community. For information on contributing, please see [gradle.org/contribute](https://gradle.org/contribute).
diff --git a/subprojects/docs/src/docs/release/notes.md b/subprojects/docs/src/docs/release/notes.md
index c725fee..24012a2 100644
--- a/subprojects/docs/src/docs/release/notes.md
+++ b/subprojects/docs/src/docs/release/notes.md
@@ -2,27 +2,13 @@
 
 This release features [1](), [2](), ... [n](), and more.
 
-<!--
+<!-- 
 Include only their name, impactful features should be called out separately below.
  [Some person](https://github.com/some-person)
 
  THiS LIST SHOULD BE ALPHABETIZED BY [PERSON NAME] - the docs:updateContributorsInReleaseNotes task will enforce this ordering, which is case-insensitive.
 -->
-
 We would like to thank the following community members for their contributions to this release of Gradle:
-[Attila Király](https://github.com/akiraly),
-[Björn Kautler](https://github.com/Vampire),
-[DJtheRedstoner](https://github.com/DJtheRedstoner),
-[JayaKrishnan Nair K](https://github.com/jknair0),
-[kackey0-1](https://github.com/kackey0-1),
-[Martin Bonnin](https://github.com/martinbonnin),
-[Martin Kealey](https://github.com/kurahaupo),
-[modmuss50](https://github.com/modmuss50),
-[Sebastian Schuberth](https://github.com/sschuberth),
-[valery1707](https://github.com/valery1707),
-[Xin Wang](https://github.com/scaventz),
-[Yanshun Li](https://github.com/Chaoba),
-[Thrillpool](https://github.com/Thrillpool)
 
 ## Upgrade instructions
 
@@ -30,30 +16,14 @@
 
 `./gradlew wrapper --gradle-version=@version@`
 
-See the [Gradle 7.x upgrade guide](userguide/upgrading_version_7.html#changes_@baseVersion@) to learn about deprecations, breaking changes and other considerations when upgrading to Gradle @version@.
+See the [Gradle 8.x upgrade guide](userguide/upgrading_version_8.html#changes_@baseVersion@) to learn about deprecations, breaking changes and other considerations when upgrading to Gradle @version@.
 
-For Java, Groovy, Kotlin and Android compatibility, see the [full compatibility notes](userguide/compatibility.html).
+For Java, Groovy, Kotlin and Android compatibility, see the [full compatibility notes](userguide/compatibility.html).   
 
-## New features, performance and usability improvements
+## New features and usability improvements
 
 <!-- Do not add breaking changes or deprecations here! Add them to the upgrade guide instead. -->
 
-### Gradle Wrapper
-
-#### Introduced labels for selecting the version
-
-The [`--gradle-version`](userguide/gradle_wrapper.html#sec:adding_wrapper) parameter for the wrapper plugin
-now supports using predefined labels to select a version.
-
-The allowed labels are:
-
-- `latest`
-- `release-candidate`
-- `nightly`
-- `release-nightly`
-
-More details can be found in the [Gradle Wrapper](userguide/gradle_wrapper.html#sec:adding_wrapper) section.
-
 <!--
 
 ================== TEMPLATE ==============================
@@ -80,14 +50,15 @@
 ADD RELEASE FEATURES BELOW
 vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -->
 
-### Configuration cache improvements
 
-TODO - Java lambdas are supported, and unsupported captured values are reported.
-TODO - File collections queried at configuration time are treated as configuration inputs.
-TODO - File system repositories are fully supported including dynamic versions in Maven, Maven local, and Ivy repositories
+
+<!-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ADD RELEASE FEATURES ABOVE
+==========================================================
+
+-->
 
 ## Promoted features
-
 Promoted features are features that were incubating in previous versions of Gradle but are now supported and subject to backwards compatibility.
 See the User Manual section on the “[Feature Lifecycle](userguide/feature_lifecycle.html)” for more information.
 
@@ -99,10 +70,18 @@
 
 ## Fixed issues
 
+<!--
+This section will be populated automatically
+-->
+
 ## Known issues
 
 Known issues are problems that were discovered post release that are directly related to changes made in this release.
 
+<!--
+This section will be populated automatically
+-->
+
 ## External contributions
 
 We love getting contributions from the Gradle community. For information on contributing, please see [gradle.org/contribute](https://gradle.org/contribute).
@@ -110,6 +89,6 @@
 ## Reporting problems
 
 If you find a problem with this release, please file a bug on [GitHub Issues](https://github.com/gradle/gradle/issues) adhering to our issue guidelines.
-If you're not sure that you're encountering a bug, please use the [forum](https://discuss.gradle.org/c/help-discuss).
+If you're not sure you're encountering a bug, please use the [forum](https://discuss.gradle.org/c/help-discuss).
 
 We hope you will build happiness with Gradle, and we look forward to your feedback via [Twitter](https://twitter.com/gradle) or on [GitHub](https://github.com/gradle).
diff --git a/subprojects/docs/src/docs/userguide/about_manual.adoc b/subprojects/docs/src/docs/userguide/about_manual.adoc
new file mode 100644
index 0000000..5681411
--- /dev/null
+++ b/subprojects/docs/src/docs/userguide/about_manual.adoc
@@ -0,0 +1,35 @@
+// 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.
+
+[[about_manual]]
+= The User Manual
+
+== Content
+
+The Gradle User Manual is broken down into the following sections:
+
+<<getting_started.adoc#getting_started,Quick Start>> :: Get up and running by installing Gradle.
+<<command_line_interface.adoc#command_line_interface,Running Gradle Builds>> :: Use Gradle to build your project.
+<<build_lifecycle.adoc#build_lifecycle,Authoring Gradle Builds>> :: Develop tasks and plugins to customize your build.
+<<building_java_projects.adoc#building_java_projects,Authoring JVM Builds>> :: Use Gradle with your Java project.
+<<dependency_management_terminology.adoc#dependency_management_terminology,Working with Dependencies>> :: Add dependencies to your build.
+<<performance.adoc#performance_gradle,Optimizing Build Times>> :: Use a cache to optimize your build.
+<<jenkins.adoc#build_jenkins,Gradle on CI>> :: Gradle integration with popular continuous integration (CI) servers.
+
+== Reference
+
+. Gradle API http://gradle.org/docs/current/javadoc/[Javadocs]
+. Gradle's https://docs.gradle.org/current/dsl/index.html[Groovy DSL]
+. Gradle's https://gradle.github.io/kotlin-dsl-docs/api/[Kotlin DSL]
+. Gradle <<plugin_reference#plugin_reference,Core Plugins>>
diff --git a/subprojects/docs/src/docs/userguide/api/kotlin_dsl.adoc b/subprojects/docs/src/docs/userguide/api/kotlin_dsl.adoc
index 7960388..86c4b88 100644
--- a/subprojects/docs/src/docs/userguide/api/kotlin_dsl.adoc
+++ b/subprojects/docs/src/docs/userguide/api/kotlin_dsl.adoc
@@ -149,14 +149,15 @@
 Everything you can read in a Kotlin DSL script is Kotlin code compiled and executed by Gradle.
 Many of the objects, functions and properties you use in your build scripts come from the Gradle API and the APIs of the applied plugins.
 
+[TIP]
+====
+You can use the link:{kotlinDslPath}/[Kotlin DSL reference] search functionality to drill through the available members.
+====
+
 === Script file names
 
-[NOTE]
-====
-Groovy DSL script files use the `.gradle` file name extension.
-
-Kotlin DSL script files use the `.gradle.kts` file name extension.
-====
+* Groovy DSL script files use the `.gradle` file name extension.
+* Kotlin DSL script files use the `.gradle.kts` file name extension.
 
 To activate the Kotlin DSL, simply use the `.gradle.kts` extension for your build scripts in place of `.gradle`. That also applies to the <<organizing_gradle_projects#sec:settings_file,settings file>> — for example `settings.gradle.kts` — and <<init_scripts#init_scripts,initialization scripts>>.
 
@@ -175,15 +176,37 @@
 All Kotlin DSL build scripts have implicit imports consisting of:
 
 * The <<writing_build_scripts#script-default-imports,default Gradle API imports>>
-* The Kotlin DSL API, which is all types within the `org.gradle.kotlin.dsl` and `org.gradle.kotlin.dsl.plugins.dsl` packages currently
+* The Kotlin DSL API, which is all types within the following packages:
+** `org.gradle.kotlin.dsl`
+** `org.gradle.kotlin.dsl.plugins.dsl`
+** `org.gradle.kotlin.dsl.precompile`
 
 [CAUTION]
 .Avoid using internal Kotlin DSL APIs
 ====
 Use of internal Kotlin DSL APIs in plugins and build scripts has the potential to break builds when either Gradle or plugins change.
-The link:{kotlinDslPath}/[Kotlin DSL API] extends the <<authoring_maintainable_build_scripts#sec:avoiding_gradle_internal_apis,Gradle public API>> with the types listed in the https://gradle.github.io/kotlin-dsl-docs/api/[corresponding API docs] that are in the `org.gradle.kotlin.dsl` or `org.gradle.kotlin.dsl.plugins.dsl` packages (but not subpackages of those).
+The link:{kotlinDslPath}/[Kotlin DSL API] extends the <<authoring_maintainable_build_scripts#sec:avoiding_gradle_internal_apis,Gradle public API>> with the types listed in the https://gradle.github.io/kotlin-dsl-docs/api/[corresponding API docs] that are in the packages listed above (but not subpackages of those).
 ====
 
+[[sec:compilation_warnings]]
+=== Compilation warnings
+
+Gradle Kotlin DSL scripts are compiled by Gradle during the configuration phase of your build.
+Deprecation warnings found by the Kotlin compiler are reported on the console when compiling the scripts.
+
+[source,text]
+----
+> Configure project :
+w: build.gradle.kts:4:5: 'getter for uploadTaskName: String!' is deprecated. Deprecated in Java
+----
+
+It is possible to configure your build to fail on any warning emitted during script compilation by <<build_environment#sec:gradle_configuration_properties,setting>> the `org.gradle.kotlin.dsl.allWarningsAsErrors` Gradle property to `true`:
+
+[source,properties]
+----
+# gradle.properties
+org.gradle.kotlin.dsl.allWarningsAsErrors=true
+----
 
 [[sec:configuring_plugins]]
 [[type-safe-accessors]]
@@ -301,7 +324,7 @@
 ==== Project extensions and conventions
 
 Project extensions and <<#sec:kotlin_dsl_about_conventions,conventions>> have both a name and a unique type, but the Kotlin DSL only needs to know the type in order to configure them.
-As the following sample shows for the `sourceSets {}` and `java {}` blocks from the original example build script, you can use the link:{kotlinDslPath}/org.gradle.kotlin.dsl/org.gradle.api.-project/configure.html[`configure<T>()`] function with the corresponding type to do that:
+As the following sample shows for the `sourceSets {}` and `java {}` blocks from the original example build script, you can use the link:{kotlinDslPath}/gradle/org.gradle.kotlin.dsl/configure.html[`configure<T>()`] function with the corresponding type to do that:
 
 .Project extensions and conventions
 ====
@@ -314,7 +337,7 @@
 The report provides both names and types.
 As a last resort, you can also check a plugin's source code, but that shouldn't be necessary in the majority of cases.
 
-Note that you can also use the link:{kotlinDslPath}/org.gradle.kotlin.dsl/org.gradle.api.-project/the.html[`the<T>()`] function if you only need a reference to the extension or convention without configuring it, or if you want to perform a one-line configuration, like so:
+Note that you can also use the link:{kotlinDslPath}/gradle/org.gradle.kotlin.dsl/the.html[`the<T>()`] function if you only need a reference to the extension or convention without configuring it, or if you want to perform a one-line configuration, like so:
 
 [source,kotlin]
 ----
@@ -370,8 +393,8 @@
 
 Some of the Gradle core plugins expose configurability with the help of a so-called _convention_ object.
 These serve a similar purpose to — and have now been superseded by — _extensions_.
+Conventions are deprecated.
 Please avoid using convention objects when writing new plugins.
-The long term plan is to migrate all Gradle core plugins to use extensions and remove the convention objects altogether.
 
 As seen above, the Kotlin DSL provides accessors only for convention objects on `Project`.
 There are situations that require you to interact with a Gradle plugin that uses convention objects on other types.
@@ -382,7 +405,7 @@
 include::sample[dir="snippets/kotlinDsl/sourceSetConvention/kotlin",files="build.gradle.kts[tags=source-set-convention]"]
 ====
 
-This technique is most commonly required for source sets that are added by language plugins other than the Java Plugin, e.g. the Groovy Plugin and the Scala Plugin. You can see which plugins add which properties to source sets in the link:{groovyDslPath}/org.gradle.api.tasks.SourceSet.html[SourceSet] reference documentation.
+This technique is primarily necessary for source sets added by language plugins that have yet to be migrated to extensions.
 
 [[kotdsl:multi_project_builds]]
 [[sec:multi_project_builds]]
@@ -563,7 +586,7 @@
 
 [NOTE]
 ====
-The above rely on configuration avoidance APIs. If you need to eagerly configure or register container elements simply replace link:{kotlinDslPath}/org.gradle.kotlin.dsl/org.gradle.api.-named-domain-object-container/existing.html[`existing()`] with link:{kotlinDslPath}/org.gradle.kotlin.dsl/org.gradle.api.-named-domain-object-container/getting.html[`getting()`] and link:{kotlinDslPath}/org.gradle.kotlin.dsl/org.gradle.api.-named-domain-object-container/registering.html[`registering()`] with link:{kotlinDslPath}/org.gradle.kotlin.dsl/org.gradle.api.-named-domain-object-container/creating.html[`creating()`].
+The above rely on configuration avoidance APIs. If you need to eagerly configure or register container elements simply replace link:{kotlinDslPath}/gradle/org.gradle.kotlin.dsl/existing.html[`existing()`] with link:{kotlinDslPath}/gradle/org.gradle.kotlin.dsl/getting.html[`getting()`] and link:{kotlinDslPath}/gradle/org.gradle.kotlin.dsl/registering.html[`registering()`] with link:{kotlinDslPath}/gradle/org.gradle.kotlin.dsl/creating.html[`creating()`].
 ====
 
 === Configuring multiple container elements together
@@ -702,6 +725,31 @@
 // === `Property`, `Provider` and `NamedDomainObjectProvider`
 
 
+[[kotdsl:assignment]]
+== Kotlin lazy property assignment
+
+Gradle's Kotlin DSL supports lazy property assignment using the `=` operator as an incubating feature enabled by default. It can be disabled by adding `systemProp.org.gradle.unsafe.kotlin.assignment=false` to the `gradle.properties` file.
+Lazy property assignment reduces the verbosity for Kotlin DSL when <<lazy_configuration#lazy_properties,lazy properties>> are used.
+It works for properties that are publicly seen as `final` (without a setter) and have type `Property` or `ConfigurableFileCollection`.
+Since properties have to be `final`, our general recommendation is not to implement custom setters for properties with lazy types and, if possible, implement such properties via an abstract getter.
+
+Once the feature is promoted to stable, using the `=` operator will be the preferred way to call `set()` in the Kotlin DSL.
+
+.Kotlin lazy property assignment
+====
+include::sample[dir="snippets/kotlinDsl/assignment/kotlin",files="build.gradle.kts[tags=assignment]"]
+====
+<1> Set value with the `.set()` method
+<2> Set value with lazy property assignment using the `=` operator
+<3> The `=` operator can be used also for assigning lazy values
+
+=== IDE support
+
+Lazy property assignment is supported from IntelliJ 2022.3 and from Android Studio Giraffe.
+For IDE to detect the Kotlin assignment is disabled, `systemProp.org.gradle.unsafe.kotlin.assignment=false` has to be added to the root project `gradle.properties` file (also for all included builds).
+Additionally, the project has to be reopened after the property value is changed.
+
+
 [[sec:kotlin-dsl_plugin]]
 == The Kotlin DSL Plugin
 
@@ -958,7 +1006,8 @@
 Changing something in the _buildSrc_ directory also has an impact as it invalidates build-script caching.
 The main reason for this is the slower script compilation for Kotlin DSL.
 * In IntelliJ IDEA, you must link:https://www.jetbrains.com/help/idea/gradle.html#gradle_import[import your project from the Gradle model] in order to get content assist and refactoring support for your Kotlin DSL build scripts.
+* Kotlin DSL script compilation avoidance has known issues.
+  If you encounter problems, it can be disabled by <<build_environment#sec:gradle_system_properties, setting>> the `org.gradle.kotlin.dsl.scriptCompilationAvoidance` system property to `false`.
 * The Kotlin DSL will not support the `model {}` block, which is part of the link:https://blog.gradle.org/state-and-future-of-the-gradle-software-model[discontinued Gradle Software Model].
-* We recommend against enabling the incubating <<multi_project_configuration_and_execution#sec:configuration_on_demand,configuration on demand>> feature as it can lead to very hard-to-diagnose problems.
 
 If you run into trouble or discover a suspected bug, please report the issue in the link:{gradle-issues}[Gradle issue tracker].
diff --git a/subprojects/docs/src/docs/userguide/authoring-builds/build_lifecycle.adoc b/subprojects/docs/src/docs/userguide/authoring-builds/build_lifecycle.adoc
index c5d6877..57d73e9 100644
--- a/subprojects/docs/src/docs/userguide/authoring-builds/build_lifecycle.adoc
+++ b/subprojects/docs/src/docs/userguide/authoring-builds/build_lifecycle.adoc
@@ -93,7 +93,7 @@
 . If Gradle finds a `settings.gradle(.kts)` file, Gradle checks if the current project is part of the multi-project build. If so, Gradle builds as a multi-project.
 . If Gradle does not find a `settings.gradle(.kts)` file, Gradle builds as a single project.
 
-In the standard Gradle project layout, project paths match the physical subproject layout on disk.
+In the standard Gradle project layout, <<intro_multi_project_builds#sec:project_path,project paths>> match the physical subproject layout on disk.
 The automatic search for a settings file only works for multi-project builds with a standard project layout.
 To build projects that use a nonstandard layout, execute the build from the directory that contains `settings.gradle(.kts)`.
 
diff --git a/subprojects/docs/src/docs/userguide/authoring-builds/logging.adoc b/subprojects/docs/src/docs/userguide/authoring-builds/logging.adoc
index b3ec4ee..c2214b4 100644
--- a/subprojects/docs/src/docs/userguide/authoring-builds/logging.adoc
+++ b/subprojects/docs/src/docs/userguide/authoring-builds/logging.adoc
@@ -46,9 +46,9 @@
 [%header%autowidth,compact]
 |===
 | Option | Outputs Log Levels
-| no logging options | LIFECYCLE and higher
 | `-q` or `--quiet` | QUIET and higher
 | `-w` or `--warn` | WARN and higher
+| no logging options | LIFECYCLE and higher
 | `-i` or `--info` | INFO and higher
 | `-d` or `--debug` | DEBUG and higher (that is, all log messages)
 |===
@@ -154,6 +154,12 @@
 
 [[sec:changing_what_gradle_logs]]
 == Changing what Gradle logs
+[WARNING]
+====
+The <<configuration_cache.adoc#config_cache,configuration cache>> limits the ability to customize Gradle's logging UI.
+The custom logger can only implement <<configuration_cache.adoc#config_cache:requirements:build_listeners,supported listener interfaces>>.
+These interfaces do not receive events when the configuration cache entry is reused, because the whole configuration phase is skipped.
+====
 
 You can replace much of Gradle's logging UI with your own. You might do this, for example, if you want to customize the UI in some way - to log more or less information, or to change the formatting. You replace the logging using the link:{groovyDslPath}/org.gradle.api.invocation.Gradle.html#org.gradle.api.invocation.Gradle:useLogger(java.lang.Object)[Gradle.useLogger(java.lang.Object)] method. This is accessible from a build script, or an init script, or via the embedding API. Note that this completely disables Gradle's default output. Below is an example init script which changes how task execution and build completion is logged.
 
@@ -176,8 +182,8 @@
 
 Your logger can implement any of the listener interfaces listed below. When you register a logger, only the logging for the interfaces that it implements is replaced. Logging for the other interfaces is left untouched. You can find out more about the listener interfaces in <<build_lifecycle.adoc#build_lifecycle_events,Build lifecycle events>>.
 
-* link:{javadocPath}/org/gradle/BuildListener.html[BuildListener]
+* link:{javadocPath}/org/gradle/BuildListener.html[BuildListener]footnote:config_cache_incompat[Not compatible with the configuration cache.]
 * link:{javadocPath}/org/gradle/api/ProjectEvaluationListener.html[ProjectEvaluationListener]
 * link:{javadocPath}/org/gradle/api/execution/TaskExecutionGraphListener.html[TaskExecutionGraphListener]
-* link:{javadocPath}/org/gradle/api/execution/TaskExecutionListener.html[TaskExecutionListener]
-* link:{javadocPath}/org/gradle/api/execution/TaskActionListener.html[TaskActionListener]
+* link:{javadocPath}/org/gradle/api/execution/TaskExecutionListener.html[TaskExecutionListener]footnote:config_cache_incompat[]
+* link:{javadocPath}/org/gradle/api/execution/TaskActionListener.html[TaskActionListener]footnote:config_cache_incompat[]
diff --git a/subprojects/docs/src/docs/userguide/authoring-builds/multi-project/declaring_dependencies_between_subprojects.adoc b/subprojects/docs/src/docs/userguide/authoring-builds/multi-project/declaring_dependencies_between_subprojects.adoc
index 1680d47..8b40df7 100644
--- a/subprojects/docs/src/docs/userguide/authoring-builds/multi-project/declaring_dependencies_between_subprojects.adoc
+++ b/subprojects/docs/src/docs/userguide/authoring-builds/multi-project/declaring_dependencies_between_subprojects.adoc
@@ -77,7 +77,7 @@
 The `person-service` project has a dependency on the other two projects.
 The `api` project has a dependency on the `shared` project.
 It has no build script and gets nothing injected by another build script.
-We use the `:` separator to define a project path.
+We use the `:` separator to define a <<intro_multi_project_builds#sec:project_path,project path>>.
 Consult the DSL documentation of link:{groovyDslPath}++/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[])++[Settings.include(java.lang.String[\])] for more information about defining project paths.
 
 [[javadependencies_2]]
diff --git a/subprojects/docs/src/docs/userguide/authoring-builds/multi-project/fine_tuning_project_layout.adoc b/subprojects/docs/src/docs/userguide/authoring-builds/multi-project/fine_tuning_project_layout.adoc
index 20a8f02..4db603f 100644
--- a/subprojects/docs/src/docs/userguide/authoring-builds/multi-project/fine_tuning_project_layout.adoc
+++ b/subprojects/docs/src/docs/userguide/authoring-builds/multi-project/fine_tuning_project_layout.adoc
@@ -23,7 +23,7 @@
 
 Multi-project builds are always represented by a tree with a single root.
 Each element in the tree represents a project.
-A project has a path which denotes the position of the project in the multi-project build tree.
+A project has <<intro_multi_project_builds#sec:project_path,a path>> which denotes the position of the project in the multi-project build tree.
 In most cases the project path is consistent with the physical location of the project in the file system.
 However, this behavior is configurable. The project tree is created in the `settings.gradle` file.
 The location of the settings file is also the location of the root project.
@@ -39,7 +39,7 @@
 include::sample[dir="snippets/multiproject/standardLayouts/groovy",files="settings.gradle[tags=hierarchical-layout]"]
 ====
 
-The `include` method takes project paths as arguments.
+The `include` method takes <<intro_multi_project_builds#sec:project_path,project paths>> as arguments.
 The project path is assumed to be equal to the relative physical file system path.
 For example, a path 'services:api' is mapped by default to a folder 'services/api' (relative from the project root).
 You only need to specify the leaves of the tree.
diff --git a/subprojects/docs/src/docs/userguide/authoring-builds/organizing_gradle_projects.adoc b/subprojects/docs/src/docs/userguide/authoring-builds/organizing_gradle_projects.adoc
index ead0554..99fa3cd 100644
--- a/subprojects/docs/src/docs/userguide/authoring-builds/organizing_gradle_projects.adoc
+++ b/subprojects/docs/src/docs/userguide/authoring-builds/organizing_gradle_projects.adoc
@@ -203,10 +203,10 @@
 │                   └── enterprise
 │                       └── DeploymentPluginTest.java
 ├── settings.gradle
-├── subprojecto-one
-│   └── build.gradle.kts
+├── subproject-one
+│   └── build.gradle
 └── subproject-two
-    └── build.gradle.kts
+    └── build.gradle
 ----
 =====
 ====
diff --git a/subprojects/docs/src/docs/userguide/authoring-builds/software-product/composite_builds.adoc b/subprojects/docs/src/docs/userguide/authoring-builds/software-product/composite_builds.adoc
index 08771d3..27b5011 100644
--- a/subprojects/docs/src/docs/userguide/authoring-builds/software-product/composite_builds.adoc
+++ b/subprojects/docs/src/docs/userguide/authoring-builds/software-product/composite_builds.adoc
@@ -91,9 +91,11 @@
 include::sample[dir="snippets/developingPlugins/testingPlugins/groovy/include-plugin-build",files="settings.gradle[tags=include-build]"]
 ====
 
-NOTE: Including plugin builds via the plugin management block is an incubating feature.
-You may also use the stable `includeBuild` mechanism outside `pluginManagement` to include plugin builds.
-However, this does not support all use cases and including plugin builds like that will be deprecated once the new mechanism is stable.
+[NOTE]
+====
+You may also use the `includeBuild` mechanism outside `pluginManagement` to include plugin builds.
+However, this does not support all use cases and including plugin builds like that might be deprecated in a future Gradle version.
+====
 
 [[included_builds]]
 === Restrictions on included builds
@@ -107,6 +109,11 @@
 * must not have a `rootProject.name` the same as a top-level project of the composite build.
 * must not have a `rootProject.name` the same as the composite build `rootProject.name`.
 
+[NOTE]
+===
+When a composite build is included into another composite build, then both included builds would have the same parent.
+In other words, nested composite build structure is flattened.
+===
 
 [[interacting_with_composite_builds]]
 == Interacting with a composite build
diff --git a/subprojects/docs/src/docs/userguide/authoring-builds/test_kit.adoc b/subprojects/docs/src/docs/userguide/authoring-builds/test_kit.adoc
index 0225dd2..1aabf7b 100644
--- a/subprojects/docs/src/docs/userguide/authoring-builds/test_kit.adoc
+++ b/subprojects/docs/src/docs/userguide/authoring-builds/test_kit.adoc
@@ -133,7 +133,7 @@
 [[sec:controlling_the_build_environment]]
 == Controlling the build environment
 
-The runner executes the test builds in an isolated environment by specifying a dedicated "working directory" in a directory inside the JVM's temp directory (i.e. the location specified by the `java.io.tmpdir` system property, typically `/tmp`). Any configuration in the default Gradle user home directory (e.g. `~/.gradle/gradle.properties`) is not used for test execution. The TestKit does not expose a mechanism for fine grained control of all aspects of the environment (e.g., JDK). Future versions of the TestKit will provide improved configuration options.
+The runner executes the test builds in an isolated environment by specifying a dedicated "working directory" in a directory inside the JVM's temp directory (i.e. the location specified by the `java.io.tmpdir` system property, typically `/tmp`). Any configuration in the default Gradle User Home (e.g. `~/.gradle/gradle.properties`) is not used for test execution. The TestKit does not expose a mechanism for fine grained control of all aspects of the environment (e.g., JDK). Future versions of the TestKit will provide improved configuration options.
 
 The TestKit uses dedicated daemon processes that are automatically shut down after test execution.
 
@@ -227,7 +227,7 @@
 include::{snippetsPath}/testKit/testKitFunctionalTestSpockBuildCache/groovy/src/test/groovy/org/gradle/sample/BuildLogicFunctionalTest.groovy[tag=functional-test-build-cache]
 ----
 
-Note that TestKit re-uses a Gradle user home between tests (see link:{javadocPath}/org/gradle/testkit/runner/GradleRunner.html#withTestKitDir-java.io.File-[GradleRunner.withTestKitDir(java.io.File)]) which contains the default location for the local build cache.
+Note that TestKit re-uses a Gradle User Home between tests (see link:{javadocPath}/org/gradle/testkit/runner/GradleRunner.html#withTestKitDir-java.io.File-[GradleRunner.withTestKitDir(java.io.File)]) which contains the default location for the local build cache.
 For testing with the build cache, the build cache directory should be cleaned between tests.
 The easiest way to accomplish this is to configure the local build cache to use a temporary directory.
 
diff --git a/subprojects/docs/src/docs/userguide/authoring-builds/working_with_files.adoc b/subprojects/docs/src/docs/userguide/authoring-builds/working_with_files.adoc
index b4c0ab2..f3ad90d 100644
--- a/subprojects/docs/src/docs/userguide/authoring-builds/working_with_files.adoc
+++ b/subprojects/docs/src/docs/userguide/authoring-builds/working_with_files.adoc
@@ -35,7 +35,7 @@
 include::sample[dir="snippets/files/copy/groovy",files="build.gradle[tags=copy-single-file-example]"]
 ====
 
-The link:{groovyDslPath}/org.gradle.api.Project.html#org.gradle.api.Project:file(java.lang.Object)[Project.file(java.lang.Object)] method is used to create a file or directory path relative to the current project and is a common way to make build scripts work regardless of the project path. The file and directory paths are then used to specify what file to copy using link:{groovyDslPath}/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:from(java.lang.Object++[]++)[Copy.from(java.lang.Object...)] and which directory to copy it to using link:{groovyDslPath}/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:into(java.lang.Object)[Copy.into(java.lang.Object)].
+The link:{javadocPath}/org/gradle/api/file/ProjectLayout.html[ProjectLayout] class is used to find a file or directory path relative to the current project. This is a common way to make build scripts work regardless of the project path. The file and directory paths are then used to specify what file to copy using link:{groovyDslPath}/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:from(java.lang.Object++[]++)[Copy.from(java.lang.Object...)] and which directory to copy it to using link:{groovyDslPath}/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:into(java.lang.Object)[Copy.into(java.lang.Object)].
 
 You can even use the path directly without the `file()` method, as explained early in the section <<#sec:copying_files,File copying in depth>>:
 
diff --git a/subprojects/docs/src/docs/userguide/build-cache/build_cache.adoc b/subprojects/docs/src/docs/userguide/build-cache/build_cache.adoc
index 788172b..82789bb 100644
--- a/subprojects/docs/src/docs/userguide/build-cache/build_cache.adoc
+++ b/subprojects/docs/src/docs/userguide/build-cache/build_cache.adoc
@@ -43,7 +43,7 @@
 Put `org.gradle.caching=true` in your `gradle.properties`::
 Gradle will try to reuse outputs from previous builds for all builds, unless explicitly disabled with `--no-build-cache`.
 
-When the build cache is enabled, it will store build outputs in the Gradle user home.
+When the build cache is enabled, it will store build outputs in the Gradle User Home.
 For configuring this directory or different kinds of build caches see <<#sec:build_cache_configure,Configure the Build Cache>>.
 
 [[sec:task_output_caching]]
@@ -277,7 +277,7 @@
 === Built-in local build cache
 
 The built-in local build cache, link:{groovyDslPath}/org.gradle.caching.local.DirectoryBuildCache.html[DirectoryBuildCache], uses a directory to store build cache artifacts.
-By default, this directory resides in the Gradle user home directory, but its location is configurable.
+By default, this directory resides in the Gradle User Home, but its location is configurable.
 
 Gradle will periodically clean-up the local cache directory by removing entries that have not been used recently to conserve disk space.
 How often Gradle will perform this clean-up is configurable as shown in the example below.
@@ -396,7 +396,7 @@
 include::sample[dir="snippets/buildCache/developer-ci-setup/groovy",files="settings.gradle[tags=developer-ci-setup]"]
 ====
 
-It is also possible to configure the build cache from an <<init_scripts.adoc#sec:using_an_init_script,init script>>, which can be used from the command line, added to your Gradle user home or be a part of your custom Gradle distribution.
+It is also possible to configure the build cache from an <<init_scripts.adoc#sec:using_an_init_script,init script>>, which can be used from the command line, added to your Gradle User Home or be a part of your custom Gradle distribution.
 
 .Init script to configure the build cache
 ====
diff --git a/subprojects/docs/src/docs/userguide/compatibility.adoc b/subprojects/docs/src/docs/userguide/compatibility.adoc
index 2dc7338..3d022ef 100644
--- a/subprojects/docs/src/docs/userguide/compatibility.adoc
+++ b/subprojects/docs/src/docs/userguide/compatibility.adoc
@@ -15,7 +15,9 @@
 
 [[compatibility]]
 = Compatibility Matrix
-The sections below describe Gradle's compatibility with several integrations. Other versions not listed here may or may not work.
+
+The sections below describe Gradle's compatibility with several integrations.
+Versions not listed here may or may not work.
 
 == Java
 A Java version between 8 and 19 is required to execute Gradle.
@@ -24,36 +26,41 @@
 Java 6 and 7 can still be used for <<building_java_projects.adoc#sec:java_cross_compilation,compilation>>, but are deprecated
 for use with testing. Testing with Java 6 and 7 will not be supported in Gradle 9.0.
 
-Any supported version of Java can be used for compile or test.
+Any fully supported version of Java can be used for compile or test.
+The latest Java version may however only be supported for compile or test but not yet for running Gradle.
 
 For older Gradle versions, please see the table below which Java version is supported by which Gradle release.
 
-
 .Java Compatibility
 |===
-|Java version|First Gradle version to support it
-|8|2.0
-|9|4.3
-|10|4.7
-|11|5.0
-|12|5.4
-|13|6.0
-|14|6.3
-|15|6.7
-|16|7.0
-|17|7.3
-|18|7.5
-|19|7.6
+|Java version |First Gradle version to support it
+
+| 8 | 2.0
+| 9 | 4.3
+| 10| 4.7
+| 11| 5.0
+| 12| 5.4
+| 13| 6.0
+| 14| 6.3
+| 15| 6.7
+| 16| 7.0
+| 17| 7.3
+| 18| 7.5
+| 19| 7.6
+| 20| 8.1
 |===
 
+[.yellow]#⚠#: Indicates that the Java version can be used for compilation and tests, but not yet running Gradle itself.
+
 [[kotlin]]
 == Kotlin
-Gradle is tested with Kotlin 1.6.10 through 1.8.10.
+Gradle is tested with Kotlin 1.6.10 through 1.8.21.
 Beta and RC versions may or may not work.
 
 .Embedded Kotlin version
 |===
 | Gradle version | Embedded Kotlin version | Kotlin Language version
+
 | 5.0 | 1.3.10 | 1.3
 | 5.1 | 1.3.11 | 1.3
 | 5.2 | 1.3.20 | 1.3
@@ -72,6 +79,8 @@
 | 7.5 | 1.6.21 | 1.4
 | 7.6 | 1.7.10 | 1.4
 | 8.0 | 1.8.10 | 1.8
+| 8.2 | 1.8.20 | 1.8
+| 8.3 | 1.8.21 | 1.8
 |===
 
 == Groovy
@@ -80,5 +89,5 @@
 Gradle plugins written in Groovy must use Groovy 3.x for compatibility with Gradle and Groovy DSL build scripts.
 
 == Android
-Gradle is tested with Android Gradle Plugin 7.3, 7.4 and 8.0.
+Gradle is tested with Android Gradle Plugin 7.3 through 8.0.
 Alpha and beta versions may or may not work.
diff --git a/subprojects/docs/src/docs/userguide/core-plugins/base_plugin.adoc b/subprojects/docs/src/docs/userguide/core-plugins/base_plugin.adoc
index 223ab17..57e1143 100644
--- a/subprojects/docs/src/docs/userguide/core-plugins/base_plugin.adoc
+++ b/subprojects/docs/src/docs/userguide/core-plugins/base_plugin.adoc
@@ -45,7 +45,7 @@
 Intended to build everything, including running all tests, producing the production artifacts and generating documentation. You will probably rarely attach concrete tasks directly to `build` as `assemble` and `check` are typically more appropriate.
 
 `build__Configuration__` — task rule::
-Assembles those artifacts attached to the named configuration. For example, `buildArchives` will execute any task that is required to create any artifact attached to the `archives` configuration.
+Assembles those artifacts attached to the named configuration. For example, `buildRuntimeElements` will execute any task that is required to create any artifact attached to the `runtimeElements` configuration.
 
 `clean__Task__` — task rule::
 Removes the <<incremental_build.adoc#sec:task_inputs_outputs,defined outputs>> of a task, e.g. `cleanJar` will delete the JAR file produced by the `jar` task of the Java Plugin.
@@ -57,14 +57,18 @@
 The Base Plugin adds no <<declaring_dependencies.adoc#sec:what-are-dependency-configurations,configurations for dependencies>>, but it does add the following configurations:
 
 `default`::
-A fallback configuration used by consumer projects. Let's say you have project B with a <<declaring_dependencies.adoc#sub:project_dependencies,project dependency>> on project A. Gradle uses some internal logic to determine which of project A's artifacts and dependencies are added to the specified configuration of project B. If no other factors apply — you don't need to worry what these are — then Gradle falls back to using everything in project A's `default` configuration.
+A fallback configuration used when dependency resolution is performed without request attributes.
 +
-*New builds and plugins should not be using the `default` configuration!* It remains for the reason of backwards compatibility.
+*New builds and plugins should not be using the `default` configuration!*
+It remains solely for backwards compatibility.
+Dependency resolution should be performed with request attributes.
 
 `archives`::
-A standard configuration for the production artifacts of a project.
-
-Note that the `assemble` task generates all artifacts that are attached to the `archives` configuration.
+All artifacts defined on the `archives` configuration are automatically built by the `assemble` task.
++
+*New builds and plugins should not be using the `archives` configuration!*
+It remains solely for backwards compatibility.
+Instead, task dependencies should be declared directly on the `assemble` task.
 
 [[sec:base_plugin_extension]]
 == Contributed extensions
diff --git a/subprojects/docs/src/docs/userguide/core-plugins/build_init_plugin.adoc b/subprojects/docs/src/docs/userguide/core-plugins/build_init_plugin.adoc
index d39dcd9..6a9f9db 100644
--- a/subprojects/docs/src/docs/userguide/core-plugins/build_init_plugin.adoc
+++ b/subprojects/docs/src/docs/userguide/core-plugins/build_init_plugin.adoc
@@ -72,13 +72,13 @@
 
 If a `--type` option is not provided, Gradle will attempt to infer the type from the environment. For example, it will infer a type of “`pom`” if it finds a `pom.xml` file to convert to a Gradle build. If the type could not be inferred, the type “`basic`” will be used.
 
-The `init` task also supports generating build scripts using either the Gradle Groovy DSL or the Gradle Kotlin DSL. The build script DSL defaults to the Groovy DSL for most build types and to the Kotlin DSL for Kotlin build types. The DSL can be selected by using the `--dsl` command-line option. For example, to create a Java library project with Kotlin DSL build scripts  run: `gradle init --type java-library --dsl kotlin`.
+The `init` task also supports generating build scripts using either the Gradle Kotlin DSL or the Gradle Groovy DSL. The build script DSL defaults to the Kotlin DSL for most build types and to the Groovy DSL for Groovy build types. The DSL can be selected by using the `--dsl` command-line option. For example, to create a Java library project with Kotlin DSL build scripts  run: `gradle init --type java-library --dsl kotlin`.
 
 You can change the name of the generated project using the `--project-name` option. It defaults to the name of the directory where the `init` task is run.
 
 You can change the package used for generated source files using the `--package` option. It defaults to the project name.
 
-If the `--incubating` option is provided, Gradle will generate build scripts which may use the latest versions of APIs, which are marked `@Incubating` and remain <<feature_lifecycle.adoc#feature_lifecycle,subject to change>>.
+If the `--incubating` option is provided, Gradle will generate build scripts which may use the latest versions of APIs, which are marked `@Incubating` and remain <<feature_lifecycle.adoc#feature_lifecycle,subject to change>>. To disable this behavior, use `--no-incubating`.
 
 All build types also setup the Gradle wrapper.
 
diff --git a/subprojects/docs/src/docs/userguide/core-plugins/distribution_plugin.adoc b/subprojects/docs/src/docs/userguide/core-plugins/distribution_plugin.adoc
index 55d1214..054509d 100644
--- a/subprojects/docs/src/docs/userguide/core-plugins/distribution_plugin.adoc
+++ b/subprojects/docs/src/docs/userguide/core-plugins/distribution_plugin.adoc
@@ -113,8 +113,6 @@
 include::sample[dir="snippets/ivy-publish/distribution/groovy",files="build.gradle[tags=publishing]"]
 ====
 
-WARNING: The Ivy Publish Plugin is not yet compatible with the <<configuration_cache.adoc#config_cache:plugins:core,configuration cache>>.
-
 Similarly, to publish a distribution to a Maven repository using the <<publishing_maven.adoc#publishing_maven, Maven Publish Plugin>>, add one or both of its archive tasks to a link:{groovyDslPath}/org.gradle.api.publish.maven.MavenPublication.html[MavenPublication] as follows:
 
 .Adding distribution archives to a Maven publication
diff --git a/subprojects/docs/src/docs/userguide/core-plugins/java_gradle_plugin.adoc b/subprojects/docs/src/docs/userguide/core-plugins/java_gradle_plugin.adoc
index 1062b7f..273a834 100644
--- a/subprojects/docs/src/docs/userguide/core-plugins/java_gradle_plugin.adoc
+++ b/subprojects/docs/src/docs/userguide/core-plugins/java_gradle_plugin.adoc
@@ -89,4 +89,4 @@
 
 === Plugin Publish Plugin
 
-Starting from link:https://plugins.gradle.org/plugin/com.gradle.plugin-publish/1.0.0[version 1.0.0, the Plugin Publish Plugin] always auto-applies the Java Gradle Plugin (`java-gradle-plugin`) and the Maven Publish Plugin (`maven-publish`).
\ No newline at end of file
+Starting from link:https://plugins.gradle.org/plugin/com.gradle.plugin-publish/1.0.0[version 1.0.0, the Plugin Publish Plugin] always auto-applies the Java Gradle Plugin (`java-gradle-plugin`) and the Maven Publish Plugin (`maven-publish`).
diff --git a/subprojects/docs/src/docs/userguide/core-plugins/project_report_plugin.adoc b/subprojects/docs/src/docs/userguide/core-plugins/project_report_plugin.adoc
index 8649c36..2b131da 100644
--- a/subprojects/docs/src/docs/userguide/core-plugins/project_report_plugin.adoc
+++ b/subprojects/docs/src/docs/userguide/core-plugins/project_report_plugin.adoc
@@ -15,8 +15,6 @@
 [[project_report_plugin]]
 = The Project Report Plugin
 
-WARNING: The Project report plugin is not compatible with the <<configuration_cache.adoc#config_cache:plugins:core,configuration cache>>.
-
 The Project report plugin adds some tasks to your project which generate reports containing useful information about your build. These tasks generate the same content that you get by executing the `tasks`, `dependencies`, and `properties` tasks from the command line (see <<command_line_interface.adoc#sec:command_line_project_reporting,Command-line project reporting>>). In contrast to the command line reports, the report plugin generates the reports into a file. There is also an aggregating task that depends on all report tasks added by the plugin.
 
 We plan to add much more to the existing reports and create additional ones in future releases of Gradle.
diff --git a/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/core_dependency_management.adoc b/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/core_dependency_management.adoc
index 5e2ed13..d549dfb 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/core_dependency_management.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/core_dependency_management.adoc
@@ -4,7 +4,8 @@
 Software projects rarely work in isolation.
 Projects often rely on reusable functionality from libraries.
 Some projects organize unrelated functionality into separate parts of a modular system.
-Dependency management is an automated technique for declaring, resolving and using functionality required by a project.
+
+Dependency management is an automated technique for declaring, resolving, and using functionality required by a project.
 
 [TIP]
 ====
@@ -14,36 +15,49 @@
 [[sec:dependency-mgmt-in-gradle]]
 == Dependency Management in Gradle
 
-[.inset]
+.Dependencies management at a glance
 image::dependency-management-resolution.png[]
 
 Gradle has built-in support for dependency management.
 
-We’ll explore the main concepts with the help of the theoretical but common project to define terms:
+Let's explore the main concepts with the help of a theoretical but common project:
 
 * This project builds Java source code.
-* Some of the Java source files import classes from link:https://github.com/google/guava[Google Guava], a open-source library.
+* Some Java source files import classes from the link:https://github.com/google/guava[Google Guava] library.
 * This project uses link:http://junit.org/junit5/[JUnit] for testing.
 
-This project uses Guava and JUnit as _dependencies_.
-Gradle fetches dependencies from _repositories_.
-You can <<declaring_repositories.adoc#declaring-repositories,declare repositories>> to tell Gradle where to fetch dependencies.
+The Gradle build file might look as follows:
+
+.Gradle build file with dependencies
+====
+include::sample[dir="snippets/dependencyManagement/introductionCoreDependencies/kotlin",files="build.gradle.kts"]
+include::sample[dir="snippets/dependencyManagement/introductionCoreDependencies/groovy",files="build.gradle"]
+====
+
+<1> *Here we define repositories for the project.*
+
+<2> *Here we declare remote and local repositories for dependency locations.*
++
+You can <<declaring_repositories.adoc#declaring-repositories,declare _repositories_>> to tell Gradle where to fetch local or remote _dependencies_. +
+In this example, Gradle fetches _dependencies_ from the link:https://repo1.maven.org/maven2/[Maven Central] and link:https://maven.google.com/[Google] _repositories_. +
+During a build, Gradle locates and downloads the dependencies, a process called <<dependency_resolution.adoc#sec:how-gradle-downloads-deps,_dependency resolution_>>.
+Gradle then <<dependency_resolution.adoc#sec:dependency_cache,stores resolved dependencies in a local cache>> called the _dependency cache_.
+Subsequent builds use this cache to avoid unnecessary network calls and speed up the build process.
+
+<3> *Here we define dependencies used by the project.*
+
+<4> *Here we declare the specific dependency name and version within a scope.*
++
+You can add code to your Java project from an external library such as `com.google.common.base` (a Guava package) which becomes a _dependency_. +
+In this example, the theoretical project uses Guava version 31.1-jre and JUnit 4.13.2 as _dependencies_. +
+A build engineer can <<declaring_dependencies.adoc#declaring-dependencies,declare dependencies>> for different scopes. For example, you can declare dependencies that are only used at compile time.
+Gradle calls the <<declaring_dependencies.adoc#sec:what-are-dependency-configurations,scope of a dependency>> a _configuration_.
+
 Repositories offer dependencies in multiple formats.
 For information about the formats supported by Gradle, see <<declaring_dependencies.adoc#sec:dependency-types,dependency types>>.
 
-A build script developer can <<declaring_dependencies.adoc#declaring-dependencies,declare dependencies>> for different scopes.
-For example, Gradle could use one scope to compile source code and another to execute tests.
-Gradle calls the <<declaring_dependencies.adoc#sec:what-are-dependency-configurations,scope of a dependency>> a _configuration_.
-
-During a build, Gradle locates the dependencies needed for the requested tasks.
-The dependencies might need to be downloaded from a remote repository, retrieved from a local directory, or (in a multi-project setting) built from another project.
-This process is called _dependency resolution_.
-For more information, see <<dependency_resolution.adoc#sec:how-gradle-downloads-deps,How Gradle downloads dependencies>>.
-
-Gradle <<dependency_resolution.adoc#sec:dependency_cache,stores resolved dependencies in a local cache>> called the _dependency cache_.
-Subsequent builds use the cache to avoid unnecessary network calls.
-
-Metadata describes dependencies. Some examples of metadata include:
+Metadata describes dependencies.
+Some examples of metadata include:
 
 * coordinates for finding the dependency in a repository
 * information about the project that created the dependency
@@ -52,10 +66,9 @@
 
 You can <<dependency_constraints.adoc#dependency-constraints,customize Gradle's handling of transitive dependencies>> based on the requirements of a project.
 
-Projects with tens or hundreds of declared dependencies can be difficult to debug.
-Gradle provides tooling to visualize and analyze a project's dependency graph.
-You can use a link:https://scans.gradle.com/get-started[build scan] or built-in tasks.
-For more information, see <<viewing_debugging_dependencies.adoc#viewing-debugging-dependencies,Viewing and Debugging Dependencies>>.
+Projects with hundreds of declared dependencies can be difficult to debug.
+Gradle provides tools to visualize and analyze a project's dependency graph (i.e. dependency tree).
+You can use a link:https://scans.gradle.com/get-started[Build Scan(TM)] or <<viewing_debugging_dependencies.adoc#viewing-debugging-dependencies,built-in tasks>>.
 
 .Build scan dependencies report
 image::gradle-core-test-build-scan-dependencies.png[]
diff --git a/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/declaring_dependencies.adoc b/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/declaring_dependencies.adoc
index ea11b97..0547d52 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/declaring_dependencies.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/declaring_dependencies.adoc
@@ -307,7 +307,7 @@
 ====
 
 One issue with the `project(":some:path")` notation is that you have to remember the path to every project you want to depend on.
-In addition, changing a project path requires you to change all places where the project dependency is used, but it is easy to miss one or more occurrences (because you have to rely on search and replace).
+In addition, changing a <<intro_multi_project_builds#sec:project_path,project path>> requires you to change all places where the project dependency is used, but it is easy to miss one or more occurrences (because you have to rely on search and replace).
 
 Since Gradle 7, Gradle offers an experimental type-safe API for project dependencies.
 The same example as above can now be rewritten as:
diff --git a/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/dependency_resolution.adoc b/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/dependency_resolution.adoc
index e23422e..641ad90 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/dependency_resolution.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/dependency_resolution.adoc
@@ -132,6 +132,7 @@
 As you can see separators are any of the `.`, `-`, `_`, `+` characters, plus the empty string when a numeric and a non-numeric part of the version are next to each-other.
 
 When resolving the conflict between competing versions, the following logic applies:
+
 * first the versions with the highest base version are selected, the rest are discarded
 * if there are still multiple competing versions left, then one is picked with a preference for not having a qualifier or having release status.
 
@@ -317,7 +318,9 @@
 
 | `modules-2`           | `files-2.1`           | `metadata-2.97`           | Gradle 6.8 to Gradle 7.4
 | `modules-2`           | `files-2.1`           | `metadata-2.99`           | Gradle 7.5 to Gradle 7.6
-| `modules-2`           | `files-2.1`           | `metadata-2.100`          | Gradle 8.0 and above
+| `modules-2`           | `files-2.1`           | `metadata-2.100`          | Gradle 8.0
+| `modules-2`           | `files-2.1`           | `metadata-2.105`          | Gradle 8.1
+| `modules-2`           | `files-2.1`           | `metadata-2.106`          | Gradle 8.2 and above
 |===
 
 [[sub:shared-readonly-cache]]
@@ -327,7 +330,7 @@
 This cache, unlike the classical dependency cache, is accessed without locking, making it possible for multiple builds to read from the cache concurrently. It's important that the read-only cache
 is not written to when other builds may be reading from it.
 
-When using the shared read-only cache, Gradle looks for dependencies (artifacts or metadata) in both the writable cache in the local Gradle user home directory and the shared read-only cache.
+When using the shared read-only cache, Gradle looks for dependencies (artifacts or metadata) in both the writable cache in the local Gradle User Home directory and the shared read-only cache.
 If a dependency is present in the read-only cache, it will not be downloaded.
 If a dependency is missing from the read-only cache, it will be downloaded and added to the writable cache.
 In practice, this means that the writable cache will only contain dependencies that are unavailable in the read-only cache.
diff --git a/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/dependency_verification.adoc b/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/dependency_verification.adoc
index 04a8e44..e040c99 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/dependency_verification.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/01-core-dependency-management/dependency_verification.adoc
@@ -57,7 +57,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <verify-metadata>true</verify-metadata>
       <verify-signatures>false</verify-signatures>
@@ -196,7 +198,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <verify-metadata>false</verify-metadata>
       <verify-signatures>false</verify-signatures>
@@ -230,12 +234,14 @@
 - `pdfbox-2.0.17.jar` which is the main artifact
 - `pdfbox-2.0.17.pom` which is the metadata file associated with this artifact
 
-As as consequence, you need to declare the checksums for both of them (unless you <<sec:disabling-metadata-verification,disabled metadata verification>>):
+As a consequence, you need to declare the checksums for both of them (unless you <<sec:disabling-metadata-verification,disabled metadata verification>>):
 
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <verify-metadata>true</verify-metadata>
       <verify-signatures>false</verify-signatures>
@@ -344,7 +350,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <verify-signatures>true</verify-signatures>
    </configuration>
@@ -418,7 +426,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <verify-metadata>true</verify-metadata>
       <verify-signatures>true</verify-signatures>
@@ -465,7 +475,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <verify-metadata>true</verify-metadata>
       <verify-signatures>true</verify-signatures>
@@ -487,7 +499,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <verify-metadata>true</verify-metadata>
       <verify-signatures>true</verify-signatures>
@@ -506,11 +520,13 @@
 
 Gradle automatically downloads the required keys but this operation can be quite slow and requires everyone to download the keys.
 To avoid this, Gradle offers the ability to use a local keyring file containing the required public keys.
+Note that only public key packets and a single userId per key are stored and used.
+All other information (user attributes, signatures, etc.) is stripped from downloaded or exported keys.
 
 Gradle supports 2 different file formats for keyrings: a binary format (`.gpg` file) and a plain text format (`.keys`).
 
 There are pros and cons for each of the formats: the binary format is more compact and can be updated directly via GPG commands, but is completely opaque (binary).
-On the opposite, the plain text format is human readable, can be easily updated by hand and makes it easier to do code reviews thanks to readable diffs.
+On the opposite, the plain text format is human-readable, can be easily updated by hand and makes it easier to do code reviews thanks to readable diffs.
 
 If the `gradle/verification-keyring.gpg` or `gradle/verification-keyring.keys` file is present, Gradle will search for keys there in priority.
 
@@ -583,7 +599,7 @@
 
 [NOTE]
 ====
-This command disables signature verification, similar to `--write-verification-metadata`.
+This command will not report verification errors, only export keys.
 ====
 
 [[sec:bootstrapping-signature-verification]]
@@ -632,7 +648,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <key-servers enabled="false"/>
       ...
@@ -914,7 +932,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <trusted-artifacts>
          <trust group="com.mycompany" reason="We trust mycompany artifacts"/>
@@ -941,7 +961,9 @@
 [source,xml]
 ----
 <?xml version="1.0" encoding="UTF-8"?>
-<verification-metadata>
+<verification-metadata xmlns="https://schema.gradle.org/dependency-verification"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.2.xsd">
    <configuration>
       <trusted-artifacts>
          <trust group="^com[.]mycompany($|([.].*))" regex="true" reason="We trust all mycompany artifacts"/>
diff --git a/subprojects/docs/src/docs/userguide/dep-man/02-declaring-dependency-versions/dynamic_versions.adoc b/subprojects/docs/src/docs/userguide/dep-man/02-declaring-dependency-versions/dynamic_versions.adoc
index fd9fb87..6a40ad8 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/02-declaring-dependency-versions/dynamic_versions.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/02-declaring-dependency-versions/dynamic_versions.adoc
@@ -47,7 +47,7 @@
 A team might decide to implement a series of features before releasing a new version of the application or library. A common strategy to allow consumers to integrate an unfinished version of their artifacts early and often is to release a module with a so-called _changing version_.
 A changing version indicates that the feature set is still under active development and hasn't released a stable version for general availability yet.
 
-In Maven repositories, changing versions are commonly referred to as link:https://maven.apache.org/guides/getting-started/index.html#What_is_a_SNAPSHOT_version[snapshot versions].
+In Maven repositories, changing versions are commonly referred to as link:https://maven.apache.org/guides/getting-started/index.html#what-is-a-snapshot-version[snapshot versions].
 Snapshot versions contain the suffix `-SNAPSHOT`.
 The following example demonstrates how to declare a snapshot version on the Spring dependency.
 
diff --git a/subprojects/docs/src/docs/userguide/dep-man/04-modeling-features/variant_model.adoc b/subprojects/docs/src/docs/userguide/dep-man/04-modeling-features/variant_model.adoc
index cecca1c..6f0091d 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/04-modeling-features/variant_model.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/04-modeling-features/variant_model.adoc
@@ -24,12 +24,13 @@
 
 In addition to a component, Gradle has the concept of _variants_ of a component.
 Variants correspond to the different ways a component can be used, such as for Java compilation or native linking or documentation.
-Artifacts are attached to a _variant_ and each variant can have a different set of dependencies:
+Artifacts are attached to a _variant_ and each variant can have a different set of dependencies.
 
 How does Gradle know which variant to choose when there's more than one?
 Variants are matched by use of <<variant_attributes.adoc#variant_attributes,attributes>>, which provide semantics to the variants and help the engine to produce a _consistent_ resolution result.
 
 Gradle differentiates between two kind of components:
+
 - local components (like projects), built from sources
 - external components, published to repositories
 
@@ -88,16 +89,17 @@
 
 First, the consumer needs to explain how it's going to use the result of dependency resolution. This is done by setting _attributes_ on the resolvable configuration of the consumer.
 
-The consumer wants to resolve a variant that matches:
-- `org.gradle.usage=JAVA_API`
+The consumer wants to resolve a variant that matches: `org.gradle.usage=JAVA_API`
 
 Second, the producer needs to expose the different variants of the component.
 
 The producer component exposes 2 variants:
+
 - its API (named `apiElements`) with attribute `org.gradle.usage=JAVA_API`
 - its runtime (named `runtimeElements`) with attribute `org.gradle.usage=JAVA_RUNTIME`
 
 Finally, Gradle selects the appropriate variant by looking at the variant attributes:
+
 - the consumer wants a variant with attributes `org.gradle.usage=JAVA_API`
 - the producer has a matching variant (`apiElements`)
 - the producer has a non-matching variant (`runtimeElements`)
@@ -108,7 +110,7 @@
 
 In the real world, consumers and producers have more than one attribute.
 
-A Java Library project in Gradle will involve several different attributes
+A Java Library project in Gradle will involve several different attributes:
 
 - `org.gradle.usage` that describes how the variant is used
 - `org.gradle.dependency.bundling` that describes how the variant handles dependencies (shadow jar vs fat jar vs regular jar)
@@ -121,18 +123,21 @@
 First, the consumer needs to explain which version of the Java it needs.
 
 The consumer wants to resolve a variant that:
+
 - can be used at runtime (has `org.gradle.usage=JAVA_RUNTIME`)
 - can be run on _at least_ Java 8 (`org.gradle.jvm.version=8`)
 
 Second, the producer needs to expose the different variants of the component.
 
 Like in the simple example, there is both a API (compilation) and runtime variant. These exist for both the Java 8 and Java 11 version of the component.
+
 - its API for Java 8 consumers (named `apiJava8Elements`) with attribute `org.gradle.usage=JAVA_API` and `org.gradle.jvm.version=8`
 - its runtime for Java 8 consumers (named `runtime8Elements`) with attribute `org.gradle.usage=JAVA_RUNTIME` and `org.gradle.jvm.version=8`
 - its API for Java 11 consumers (named `apiJava11Elements`) with attribute `org.gradle.usage=JAVA_API` and `org.gradle.jvm.version=11`
 - its runtime for Java 11 consumers (named `runtime11Elements`) with attribute `org.gradle.usage=JAVA_RUNTIME` and `org.gradle.jvm.version=11`
 
 Finally, Gradle selects the best matching variant by looking at all of the attributes:
+
 - the consumer wants a variant with compatible attributes to `org.gradle.usage=JAVA_RUNTIME` and `org.gradle.jvm.version=8`
 - the variants `runtime8Elements` and `runtime11Elements` have `org.gradle.usage=JAVA_RUNTIME`
 - the variants `apiJava8Elements` and `apiJava11Elements` are incompatible
@@ -156,6 +161,7 @@
 == Variant selection errors
 
 When selecting the most compatible variant of a component, resolution may fail:
+
 * when more than one variant from the producer matches the consumer attributes (ambiguity error)
 * when no variants from the producer match the consumer attributes (incompatibility error)
 
@@ -232,7 +238,7 @@
 All _compatible_ candidate variants are displayed with their attributes.
 
 * Incompatible attributes are presented first, as they usually are the key in understanding why a variant could not be selected.
-* Other attributes are presented second, this includes _required_ and _compatible_ ones as well as all extra _producer_ attributes that are not requested by the consumer.
+* Other attributes are presented second, this includes _requested_ and _compatible_ ones as well as all extra _producer_ attributes that are not requested by the consumer.
 
 Similar to the ambiguous variant error, the goal is to understand which variant should be selected. In some cases, there may not be any compatible variants from the producer (e.g., trying to run on Java 8 with a library built for Java 11).
 
@@ -248,7 +254,7 @@
 
 By default, `outgoingVariants` prints information about all variants.
 It offers the optional parameter `--variant <variantName>` to select a single variant to display.
-It also accepts the `--all` flag to include information about legacy and deprecated configurations.
+It also accepts the `--all` flag to include information about legacy and deprecated configurations, or `--no-all` to exclude this information.
 
 Here is the output of the `outgoingVariants` task on a freshly generated `java-library` project:
 
@@ -258,7 +264,7 @@
 --------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-Description = API elements for main.
+API elements for the 'main' feature.
 
 Capabilities
     - new-java-library:lib:unspecified (default capability)
@@ -305,7 +311,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Description = Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Capabilities
     - new-java-library:lib:unspecified (default capability)
@@ -391,8 +397,9 @@
 These are configurations that are marked resolvable but *not* marked consumable.
 Though some resolvable configurations are also marked consumable, these are legacy configurations that should *not* have dependencies added in build scripts.
 This report offers the optional parameter `--configuration <configurationName>` to select a single configuration to display.
-It also accepts the `--all` flag to include information about legacy and deprecated configurations.
+It also accepts the `--all` flag to include information about legacy and deprecated configurations, or `--no-all` to exclude this information.
 Finally, it accepts the `--recursive` flag to list in the extended configurations section those configurations which are extended _transitively_ rather than directly.
+Alternatively, `--no-recursive` can be used to exclude this information.
 
 Here is the output of the `resolvableConfigurations` task on a freshly generated `java-library` project:
 
@@ -536,13 +543,14 @@
 Modules published on a Maven repository are automatically converted into variant-aware modules.
 
 There is no way for Gradle to know which kind of component was published:
+
 - a BOM that represents a Gradle platform
 - a BOM used as a super-POM
 - a POM that is both a platform _and_ a library
 
 The default strategy used by Java projects in Gradle is to derive 8 different variants:
 
-* 2 "library" variants (attribute `org.gradle.category` = `library`)
+* two "library" variants (attribute `org.gradle.category` = `library`)
 ** the `compile` variant maps the `<scope>compile</scope>` dependencies.
 This variant is equivalent to the `apiElements` variant of the <<java_library_plugin.adoc#java_library_plugin,Java Library plugin>>.
 All dependencies of this scope are considered _API dependencies_.
@@ -552,7 +560,7 @@
 - in both cases, the `<dependencyManagement>` dependencies are _not converted to constraints_
 * a "sources" variant that represents the sources jar for the component
 * a "javadoc" variant that represents the javadoc jar for the component
-* 4 "platform" variants derived from the `<dependencyManagement>` block (attribute `org.gradle.category` = `platform`):
+* four "platform" variants derived from the `<dependencyManagement>` block (attribute `org.gradle.category` = `platform`):
 ** the `platform-compile` variant maps the  `<scope>compile</scope>` dependency management dependencies as _dependency constraints_.
 ** the `platform-runtime` variant maps both the `<scope>compile</scope>` and `<scope>runtime</scope>` dependency management dependencies as _dependency constraints_.
 ** the `enforced-platform-compile` is similar to `platform-compile` but all the constraints are _forced_
diff --git a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_customization.adoc b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_customization.adoc
index beabf7f..3a1d0cf 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_customization.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_customization.adoc
@@ -102,7 +102,7 @@
 - Add artifacts to a publication based on a component with metadata (not recommended, instead <<sec:adding-variants-to-existing-components,adjust a component>> or use a <<#sec:publishing-custom-components,adhoc component publication>> which will both also produce metadata fitting your artifacts)
 
 To create a publication based on artifacts, start by defining a custom artifact and attaching it to a Gradle <<dependency_management_terminology.adoc#sub:terminology_configuration,configuration>> of your choice.
-The following sample defines an RPM artifact that is produced by an `rpm` task (not shown) and attaches that artifact to the `archives` configuration:
+The following sample defines an RPM artifact that is produced by an `rpm` task (not shown) and attaches that artifact to the `conf` configuration:
 
 .Defining a custom artifact for a configuration
 ====
diff --git a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_ivy.adoc b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_ivy.adoc
index b7b5b07..2b91565 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_ivy.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_ivy.adoc
@@ -15,8 +15,6 @@
 [[publishing_ivy]]
 = Ivy Publish Plugin
 
-WARNING: The Ivy Publish Plugin is not yet compatible with the <<configuration_cache.adoc#config_cache:plugins:core,configuration cache>>.
-
 The Ivy Publish Plugin provides the ability to publish build artifacts in the http://ant.apache.org/ivy/[Apache Ivy] format, usually to a repository for consumption by other builds or projects. What is published is one or more artifacts created by the build, and an Ivy _module descriptor_ (normally `ivy.xml`) that describes the artifacts and the dependencies of the artifacts, if any.
 
 A published Ivy module can be consumed by Gradle (see <<declaring_dependencies.adoc#declaring-dependencies,Declaring Dependencies>>) and other tools that understand the Ivy format. You can learn about the fundamentals of publishing in <<publishing_setup.adoc#publishing_overview,Publishing Overview>>.
diff --git a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_maven.adoc b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_maven.adoc
index f6299d6..fb3a44a 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_maven.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_maven.adoc
@@ -256,8 +256,6 @@
 
 The <<signing_plugin.adoc#signing_plugin, Signing Plugin>> is used to generate a signature file for each artifact. In addition, checksum files will be generated for all artifacts and signature files.
 
-WARNING: Note that the Signing plugin is not yet compatible with the <<configuration_cache.adoc#config_cache:plugins:core,configuration cache>>.
-
 [TIP]
 ====
 `publishToMavenLocal` does not create checksum files in `$USER_HOME/.m2/repository`. If you want to verify that the checksum files are created correctly, or use them for later publishing, consider configuring a <<declaring_repositories.adoc#sec:maven_repo,custom Maven repository>> with a `file://` URL and using that as the publishing target instead.
diff --git a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_signing.adoc b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_signing.adoc
index ddb9247..b430b6a 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_signing.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/publishing_signing.adoc
@@ -1,8 +1,6 @@
 [[publishing_maven:signing]]
 = Signing artifacts
 
-WARNING: The Signing Plugin is not yet compatible with the <<configuration_cache.adoc#config_cache:plugins:core,configuration cache>>.
-
 The <<signing_plugin.adoc#signing_plugin, Signing Plugin>> can be used to sign all artifacts and metadata files that make up a publication, including Maven POM files and Ivy module descriptors. In order to use it:
 
 1. Apply the Signing Plugin
diff --git a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/signing_plugin.adoc b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/signing_plugin.adoc
index 8e04a6b..9fefb2d 100644
--- a/subprojects/docs/src/docs/userguide/dep-man/06-publishing/signing_plugin.adoc
+++ b/subprojects/docs/src/docs/userguide/dep-man/06-publishing/signing_plugin.adoc
@@ -15,8 +15,6 @@
 [[signing_plugin]]
 = The Signing Plugin
 
-WARNING: The Signing Plugin is not yet compatible with the <<configuration_cache.adoc#config_cache:plugins:core,configuration cache>>.
-
 The Signing Plugin adds the ability to digitally sign built files and artifacts. These digital signatures can then be used to prove who built the artifact the signature is attached to as well as other information such as when the signature was generated.
 
 The Signing Plugin currently only provides support for generating https://en.wikipedia.org/wiki/Pretty_Good_Privacy#OpenPGP[OpenPGP signatures] (which is the signature format http://central.sonatype.org/pages/requirements.html#sign-files-with-gpgpgp[required for publication to the Maven Central Repository]).
@@ -164,22 +162,22 @@
 [[sec:signing_configurations]]
 === Signing Configurations
 
-It is common to want to sign the artifacts of a configuration. For example, the <<java_plugin.adoc#java_plugin,Java plugin>> configures a jar to build and this jar artifact is added to the `archives` configuration. Using the Signing DSL, you can specify that all of the artifacts of this configuration should be signed.
+It is common to want to sign the artifacts of a configuration. For example, the <<java_plugin.adoc#java_plugin,Java plugin>> configures a jar to build and this jar artifact is added to the `runtimeElements` configuration. Using the Signing DSL, you can specify that all of the artifacts of this configuration should be signed.
 
 .Signing a configuration
 ====
-include::sample[dir="snippets/signing/configurations/kotlin",files="build.gradle.kts[tags=sign-archives]"]
-include::sample[dir="snippets/signing/configurations/groovy",files="build.gradle[tags=sign-archives]"]
+include::sample[dir="snippets/signing/configurations/kotlin",files="build.gradle.kts[tags=sign-runtime-elements]"]
+include::sample[dir="snippets/signing/configurations/groovy",files="build.gradle[tags=sign-runtime-elements]"]
 ====
 
-This will create a task (of type link:{groovyDslPath}/org.gradle.plugins.signing.Sign.html[Sign]) in your project named `signArchives`, that will build any `archives` artifacts (if needed) and then generate signatures for them. The signature files will be placed alongside the artifacts being signed.
+This will create a task (of type link:{groovyDslPath}/org.gradle.plugins.signing.Sign.html[Sign]) in your project named `signRuntimeElements`, that will build any `runtimeElements` artifacts (if needed) and then generate signatures for them. The signature files will be placed alongside the artifacts being signed.
 
 === Example: Signing a configuration output
 
-.Output of **`gradle signArchives`**
+.Output of **`gradle signRuntimeElements`**
 ----
-> gradle signArchives
-include::{snippetsPath}/signing/configurations/tests/signingArchivesOutput.out[]
+> gradle signRuntimeElements
+include::{snippetsPath}/signing/configurations/tests/signingRuntimeElementsOutput.out[]
 ----
 
 [[sec:signing_tasks]]
@@ -241,4 +239,4 @@
 When signing <<#sec:signing_publications,publications>>, the resultant signature artifacts are automatically added to the corresponding publication.
 Thus, when publishing to a repository, e.g. by executing the `publish` task, your signatures will be distributed along with the other artifacts without any additional configuration.
 
-When signing <<#sec:signing_configurations,configurations>> and <<#sec:signing_tasks,tasks>>, the resultant signature artifacts are automatically added to the `signatures` and `archives` dependency configurations.
+When signing <<#sec:signing_configurations,configurations>> and <<#sec:signing_tasks,tasks>>, the resultant signature artifacts are automatically added to the `signatures` dependency configuration.
diff --git a/subprojects/docs/src/docs/userguide/developing-plugins/implementing_gradle_plugins.adoc b/subprojects/docs/src/docs/userguide/developing-plugins/implementing_gradle_plugins.adoc
index 31af48e..6ce4e5c 100644
--- a/subprojects/docs/src/docs/userguide/developing-plugins/implementing_gradle_plugins.adoc
+++ b/subprojects/docs/src/docs/userguide/developing-plugins/implementing_gradle_plugins.adoc
@@ -118,14 +118,6 @@
 include::{snippetsPath}/developingPlugins/pluginExtension/groovy/buildSrc/src/main/java/org/myorg/SiteExtension.java[]
 ----
 
-=== Extensions vs. Conventions
-
-Some of the Gradle core plugins expose configurability with the help of a so-called link:{javadocPath}/org/gradle/api/plugins/Convention.html[Convention].
-`Convention` is the preceding concept of an extension and serves a similar purpose.
-The main difference between both concepts is that `Convention` does not allow for defining a namespace to model a DSL-like API making it hard to distinguish from the Gradle core DSL.
-Please avoid using the `Convention` concept when writing new plugins.
-The long term plan is to migrate all Gradle core plugins to use extensions and remove the `Convention` concept altogether.
-
 [[capturing_user_input_to_configure_plugin_runtime_behavior]]
 == Capturing user input to configure plugin runtime behavior
 
diff --git a/subprojects/docs/src/docs/userguide/developing-plugins/publishing_gradle_plugins.adoc b/subprojects/docs/src/docs/userguide/developing-plugins/publishing_gradle_plugins.adoc
index 38a2b5f..79e4a36 100644
--- a/subprojects/docs/src/docs/userguide/developing-plugins/publishing_gradle_plugins.adoc
+++ b/subprojects/docs/src/docs/userguide/developing-plugins/publishing_gradle_plugins.adoc
@@ -155,6 +155,14 @@
 $ ./gradlew publishPlugins
 ----
 
+You can validate your plugins before actually publishing them using the `--validate-only` flag:
+
+[listing]
+----
+$ ./gradlew publishPlugins --validate-only
+----
+
+
 If you have not configured your {portal} key and secret values in your `gradle.properties` file, you can specify them on the command-line
 
 [listing]
diff --git a/subprojects/docs/src/docs/userguide/extending-gradle/build_services.adoc b/subprojects/docs/src/docs/userguide/extending-gradle/build_services.adoc
index 5f75e11..5cbcc7b 100644
--- a/subprojects/docs/src/docs/userguide/extending-gradle/build_services.adoc
+++ b/subprojects/docs/src/docs/userguide/extending-gradle/build_services.adoc
@@ -44,13 +44,26 @@
 [[using_a_build_service_from_a_task]]
 == Using a build service from a task
 
-To use a build service from a task, add a property to the task of type `Property<MyServiceType>` and mark the property as `@Internal`.
-Using a service with any other annotation is currently not supported. For example, it is currently not possible
-to mark a service as an input to a task. It is also important to declare the association between the task and the service via the link:{javadocPath}/org/gradle/api/Task.html#usesService-org.gradle.api.provider.Provider-[Task.usesService] method so Gradle can properly honor the build service lifecycle and its usage constraints.
+To use a build service from a task, you need to:
 
-Here is an example of a task that uses the previous service:
+1. Add a property to the task of type `Property<MyServiceType>`.
+2. Either annotate the property with `@Internal` or `@ServiceReference` (since 8.0).
+3. Assign a shared build service provider to the property (optional, when using `@ServiceReference(<serviceName>)`).
+4. Declare the association between the task and the service so Gradle can properly honor the build service lifecycle and its usage constraints (also optional, when using `@ServiceReference`).
 
-.Using a build service
+Note that using a service with any other annotation is currently not supported. For example, it is currently not possible
+to mark a service as an input to a task.
+
+=== Annotating a shared build service property with `@Internal`
+
+When you annotate a shared build service property with `@Internal`, you need to do two more things:
+
+1. Explicitly assign a build service provider obtained when registering the service with link:{javadocPath}/org/gradle/api/services/BuildServiceRegistry.html#registerIfAbsent-java.lang.String-java.lang.Class-org.gradle.api.Action-[BuildServiceRegistry.registerIfAbsent()] to the property.
+2. Explicitly declare the association between the task and the service via the link:{javadocPath}/org/gradle/api/Task.html#usesService-org.gradle.api.provider.Provider-[Task.usesService].
+
+Here is an example of a task that consumes the previous service via a property annotated with `@Internal`:
+
+.Using a build service with explicit assignment
 ====
 [source.multi-language-sample,java]
 .Download.java
@@ -59,7 +72,28 @@
 ----
 ====
 
-== Registering a build service
+[[sec:service_references]]
+=== Annotating a shared build service property with `@ServiceReference`
+
+NOTE: The `@ServiceReference` annotation is an <<feature_lifecycle.adoc#feature_lifecycle,incubating>> API and is subject to changing in a future release.
+
+Otherwise, when you annotate a shared build service property with `@ServiceReference`,
+there is no need to explicitly declare the association between the task and the service;
+also, if you provide a service name to the annotation, and a shared build service is registered with that name,
+it will be automatically assigned to the property when the task is created.
+
+Here is an example of a task that consumes the previous service via a property annotated with `@ServiceReference`:
+
+.Using a build service with automatic assignment
+====
+[source.multi-language-sample,java]
+.Download.java
+----
+include::{snippetsPath}/plugins/buildServiceUsingServiceReference/groovy/buildSrc/src/main/java/Download.java[]
+----
+====
+
+== Registering a build service and connecting it to tasks
 
 To create a build service, you register the service instance using the link:{javadocPath}/org/gradle/api/services/BuildServiceRegistry.html#registerIfAbsent-java.lang.String-java.lang.Class-org.gradle.api.Action-[BuildServiceRegistry.registerIfAbsent()] method.
 Registering the service does not create the service instance. This happens on demand when a task first uses the service. If no task uses the service during a build, the service instance will not be created.
@@ -67,9 +101,9 @@
 Currently, build services are scoped to a build, rather than to a project, and these services are available to be shared by the tasks of all projects.
 You can access the registry of shared build services via `Project.getGradle().getSharedServices()`.
 
-Here is an example of a plugin that registers the previous service:
+Here is an example of a plugin that registers the previous service when the task property consuming the service is annotated with `@Internal`:
 
-.Build service registration
+.Build service registration when task property is annotated with `@Internal`
 ====
 [source.multi-language-sample,java]
 .DownloadPlugin.java
@@ -79,6 +113,23 @@
 ====
 
 The plugin registers the service and receives a `Provider<WebService>` back. This provider can be connected to task properties to pass the service to the task.
+Note that for a task property annotated with `@Internal`, the task property needs to (1) be explicitly assigned with the provider obtained during registation,
+and (2) you must tell Gradle the task uses the service via link:{javadocPath}/org/gradle/api/Task.html#usesService-org.gradle.api.provider.Provider-[Task.usesService].
+
+Compare that to when the task property consuming the service is annotated with `@ServiceReference`:
+
+.Build service registration when task property is annotated with `@ServiceReference`
+====
+[source.multi-language-sample,java]
+.DownloadPlugin.java
+----
+include::{snippetsPath}/plugins/buildServiceUsingServiceReference/groovy/buildSrc/src/main/java/DownloadPlugin.java[]
+----
+====
+
+As you can see, there is no need to assign the build service provider to the task, nor to declare explicitly that the task uses the service.
+
+=== Using shared build services from configuration actions
 
 Generally, build services are intended to be used by tasks, as they usually represent some state that is potentially expensive to create, and you should avoid using
 them at configuration time. However, sometimes it can make sense to use the service at configuration time. This is possible, simply call `get()` on the provider.
@@ -96,8 +147,10 @@
 
 You can constrain concurrent execution when you register the service, by using the `Property` object returned from link:{javadocPath}/org/gradle/api/services/BuildServiceSpec.html#getMaxParallelUsages--[BuildServiceSpec.getMaxParallelUsages()].
 When this property has no value, which is the default, Gradle does not constrain access to the service. When this property has a value > 0, Gradle will allow no more than the specified number of tasks to use the service concurrently.
-*IMPORTANT*: For the constraint to take effect the build service must be registered with the using tasks via
-link:{groovyDslPath}/org.gradle.api.Task.html#org.gradle.api.Task:usesService(org.gradle.api.provider.Provider)[Task.usesService(Provider<? extends BuildService<?>>)]
+
+*IMPORTANT*: When the consuming task property is annotated with `@Internal`, for the constraint to take effect, the build service *must* be registered with the consuming task via
+link:{groovyDslPath}/org.gradle.api.Task.html#org.gradle.api.Task:usesService(org.gradle.api.provider.Provider)[Task.usesService(Provider<? extends BuildService<?>>)].
+This is not necessary if, instead, the consuming property is annotated with `@ServiceReference`.
 
 [[operation_listener]]
 == Receiving information about task execution
diff --git a/subprojects/docs/src/docs/userguide/extending-gradle/custom_gradle_types.adoc b/subprojects/docs/src/docs/userguide/extending-gradle/custom_gradle_types.adoc
index 821f4f3..c8ff1b9 100644
--- a/subprojects/docs/src/docs/userguide/extending-gradle/custom_gradle_types.adoc
+++ b/subprojects/docs/src/docs/userguide/extending-gradle/custom_gradle_types.adoc
@@ -61,6 +61,7 @@
 - `ConfigurableFileTree`
 - `DomainObjectSet<T>`
 - `NamedDomainObjectContainer<T>`
+- `ExtensiblePolymorphicDomainObjectContainer<T>`
 
 Gradle creates values for managed properties in the same way as link:{javadocPath}/org/gradle/api/model/ObjectFactory.html[ObjectFactory].
 
diff --git a/subprojects/docs/src/docs/userguide/extending-gradle/custom_tasks.adoc b/subprojects/docs/src/docs/userguide/extending-gradle/custom_tasks.adoc
index 0b4e069..77c1b4c 100644
--- a/subprojects/docs/src/docs/userguide/extending-gradle/custom_tasks.adoc
+++ b/subprojects/docs/src/docs/userguide/extending-gradle/custom_tasks.adoc
@@ -378,7 +378,7 @@
 Gradle limits the set of data types that can be used for declaring command line options. The use on the command line differ per type.
 
 `boolean`, `Boolean`, `Property<Boolean>`::
-Describes an option with the value `true` or `false`. Passing the option on the command line treats the value as `true`. For example `--enabled` equates to `true`. The absence of the option uses the default value of the property.
+Describes an option with the value `true` or `false`. Passing the option on the command line treats the value as `true`, for example `--foo` equates to `true`. The absence of the option uses the default value of the property. For each boolean option, an opposite option is created automatically. For example, `--no-foo` is created for the provided option `--foo` and `--bar` is created for `--no-bar`. Options whose name starts with `--no` are disable options and set the option value to `false`. An opposite option is only created if no option with the same name already exists for the task.
 
 `String`, `Property<String>`::
 Describes an option with an arbitrary String value. Passing the option on the command line also requires a value e.g. `--container-id=2x94held` or `--container-id 2x94held`.
diff --git a/subprojects/docs/src/docs/userguide/extending-gradle/dataflow_actions.adoc b/subprojects/docs/src/docs/userguide/extending-gradle/dataflow_actions.adoc
new file mode 100644
index 0000000..b6d9a48
--- /dev/null
+++ b/subprojects/docs/src/docs/userguide/extending-gradle/dataflow_actions.adoc
@@ -0,0 +1,73 @@
+[[dataflow_action]]
+= Dataflow Actions
+
+[NOTE]
+====
+The dataflow actions support is an <<feature_lifecycle.adoc#feature_lifecycle,incubating>> feature, and the details described here may change.
+====
+
+A preferred way of executing work in a Gradle build is using a task.
+However, there are some kinds of work that do not fit tasks well, for example, custom handling of the build failure.
+What if you want to play a cheerful sound when the build succeeds and a sad one when it fails?
+This work piece has to process the task execution result, so it cannot be a task itself.
+
+The dataflow actions API provides a way to schedule such kind of work.
+A dataflow action is a parameterized isolated piece of work that becomes eligible for execution as soon as all of its input parameters become available.
+
+== Implementing a dataflow action
+
+The first step is to implement the action itself.
+To do that you create a class implementing link:{javadocPath}/org/gradle/api/flow/FlowAction.html[FlowAction] interface.
+The `execute` method must be implemented because this is where the work happens.
+An action implementation is treated as a <<custom_gradle_types.adoc#custom_gradle_types,custom Gradle type>> and can use any of the features available to custom Gradle types.
+In particular, some Gradle services can be injected into the implementation.
+
+A dataflow action may accept parameters.
+To provide parameters, you define an abstract class (or interface) to hold the parameters.
+The parameters type must implement (or extend) link:{javadocPath}/org/gradle/api/flow/FlowParameters.html[FlowParameters].
+The action implementation gets the parameters as an argument of the `execute` method.
+The parameters type is also a <<custom_gradle_types.adoc#custom_gradle_types,custom Gradle type>>.
+
+When the action requires no parameters, you can use link:{javadocPath}/org/gradle/api/flow/FlowParameters.None.html[FlowParameters.None] as the parameters type.
+
+Here is an example of a dataflow action that takes a shared build service and a file path as parameters.
+====
+[source.multi-language-sample,java]
+.SoundPlay.java
+----
+include::{snippetsPath}/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundPlay.java[]
+----
+====
+<1> Parameters in the parameter type must be annotated. If a parameter is annotated with link:{javadocPath}/org/gradle/api/services/ServiceReference.html[`@ServiceReference`] then a suitable shared build service implementation is automatically assigned to the parameter when the action is created, according to the <<build_services.adoc#sec:service_references,usual rules>>.
+<2> All other parameters must be annotated with link:{javadocPath}/org/gradle/api/tasks/Input.html[`@Input`].
+
+== Lifecycle event providers
+
+Besides the usual value providers, Gradle provides dedicated providers for build lifecycle events, like build completion.
+These providers are intended for dataflow actions and provide additional ordering guarantees when used as inputs.
+The ordering also applies if you derive a provider from the event provider by, for example, calling `map` or `flatMap`.
+You can obtain these providers from the link:{javadocPath}/org/gradle/api/flow/FlowProviders.html[FlowProviders] class.
+
+WARNING: If you're not using a lifecycle event provider as an input to the dataflow action, then the exact timing when the action is executed is not defined and may change in the next version of Gradle.
+
+
+== Supplying the action for execution
+
+You should not create `FlowAction` objects manually.
+Instead, you request to execute them in the appropriate scope of link:{javadocPath}/org/gradle/api/flow/FlowScope.html[`FlowScope`].
+When doing that, you can configure the parameters for task to use.
+
+====
+[source.multi-language-sample,java]
+.SoundFeedbackPlugin.java
+----
+include::{snippetsPath}/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundFeedbackPlugin.java[tag=flow-action]
+----
+====
+<1> Use service injection to obtain `FlowScope` and `FlowProviders` instances. They are available for project and settings plugins.
+<2> Use an appropriate scope to run your actions. As the name suggests, actions in the `always` scope are executed every time the build runs.
+<3> Specify the class that implements the action.
+<4> Use the spec argument to configure the action parameters.
+<5> A lifecycle event provider can be mapped into something else while preserving the action ordering.
+
+As a result, when you run the build, and it completes successfully, the action will play the "tada" sound. If the build fails at configuration or execution time, then you'll hear "sad-trombone" -- assuming, of course, that build configuration proceeds far enough for the action to be registered.
diff --git a/subprojects/docs/src/docs/userguide/extending-gradle/lazy_configuration.adoc b/subprojects/docs/src/docs/userguide/extending-gradle/lazy_configuration.adoc
index b930be7..2f744a2 100644
--- a/subprojects/docs/src/docs/userguide/extending-gradle/lazy_configuration.adoc
+++ b/subprojects/docs/src/docs/userguide/extending-gradle/lazy_configuration.adoc
@@ -50,6 +50,11 @@
 include::sample[dir="snippets/providers/propertyAndProvider/kotlin",files="build.gradle.kts[]"]
 include::sample[dir="snippets/providers/propertyAndProvider/groovy",files="build.gradle[]"]
 ====
+<1> A task that displays a greeting
+<2> A configurable greeting
+<3> Read-only property calculated from the greeting
+<4> Configure the greeting
+<5> Alternative notation to calling Property.set() (incubating for Kotlin, see <<kotlin_dsl.adoc#kotdsl:assignment,Kotlin DSL Primer>>)
 
 .Output of **`gradle greeting`**
 ----
diff --git a/subprojects/docs/src/docs/userguide/img/ant-task-migration.png b/subprojects/docs/src/docs/userguide/img/ant-task-migration.png
index e63507b..973af28 100644
--- a/subprojects/docs/src/docs/userguide/img/ant-task-migration.png
+++ b/subprojects/docs/src/docs/userguide/img/ant-task-migration.png
Binary files differ
diff --git a/subprojects/docs/src/docs/userguide/img/dependency-management-resolution.png b/subprojects/docs/src/docs/userguide/img/dependency-management-resolution.png
index 9339b6b..0a38541 100644
--- a/subprojects/docs/src/docs/userguide/img/dependency-management-resolution.png
+++ b/subprojects/docs/src/docs/userguide/img/dependency-management-resolution.png
Binary files differ
diff --git a/subprojects/docs/src/docs/userguide/img/gradle-core-test-build-scan-dependencies.png b/subprojects/docs/src/docs/userguide/img/gradle-core-test-build-scan-dependencies.png
index b580b2a..42c998f 100644
--- a/subprojects/docs/src/docs/userguide/img/gradle-core-test-build-scan-dependencies.png
+++ b/subprojects/docs/src/docs/userguide/img/gradle-core-test-build-scan-dependencies.png
Binary files differ
diff --git a/subprojects/docs/src/docs/userguide/jvm/building_java_projects.adoc b/subprojects/docs/src/docs/userguide/jvm/building_java_projects.adoc
index df06198..f66e63a 100644
--- a/subprojects/docs/src/docs/userguide/jvm/building_java_projects.adoc
+++ b/subprojects/docs/src/docs/userguide/jvm/building_java_projects.adoc
@@ -66,7 +66,7 @@
 
 The Java Library Plugin also integrates the above tasks into the standard <<base_plugin.adoc#sec:base_tasks,Base Plugin lifecycle tasks>>:
 
- * `jar` is attached to `assemble` footnote:[In fact, any artifact added to the `archives` configuration will be built by `assemble`]
+ * `jar` is attached to `assemble`
  * `test` is attached to `check`
 
 The rest of the chapter explains the different avenues for customizing the build to your requirements. You will also see later how to adjust the build for libraries, applications, web apps and enterprise apps.
diff --git a/subprojects/docs/src/docs/userguide/jvm/java_library_plugin.adoc b/subprojects/docs/src/docs/userguide/jvm/java_library_plugin.adoc
index 0de4b7e..9ffd0f5 100644
--- a/subprojects/docs/src/docs/userguide/jvm/java_library_plugin.adoc
+++ b/subprojects/docs/src/docs/userguide/jvm/java_library_plugin.adoc
@@ -200,7 +200,7 @@
 | For compiling against this library
 | yes
 | no
-| This configuration is meant to be used by consumers, to retrieve all the elements necessary to compile against this library. Unlike the `default` configuration, this doesn't leak implementation or runtime dependencies.
+| This configuration is meant to be used by consumers, to retrieve all the elements necessary to compile against this library.
 
 | `runtimeElements`
 | For executing this library
diff --git a/subprojects/docs/src/docs/userguide/jvm/java_plugin.adoc b/subprojects/docs/src/docs/userguide/jvm/java_plugin.adoc
index 46bf4f1..0eac22c 100644
--- a/subprojects/docs/src/docs/userguide/jvm/java_plugin.adoc
+++ b/subprojects/docs/src/docs/userguide/jvm/java_plugin.adoc
@@ -113,7 +113,7 @@
 The Java plugin attaches some of its tasks to the lifecycle tasks defined by the <<base_plugin.adoc#sec:base_tasks,Base Plugin>> — which the Java Plugin applies automatically — and it also adds a few other lifecycle tasks:
 
 `assemble`::
-_Depends on_: `jar`, and all other tasks that create artifacts attached to the `archives` configuration
+_Depends on_: `jar`
 +
 Aggregate task that assembles all the archives in the project. This task is added by the Base Plugin.
 
@@ -294,7 +294,12 @@
 [NOTE]
 ====
 
-To find information on the `api` configuration, please consult the <<java_library_plugin.adoc#sec:java_library_separation, Java Library Plugin>> reference documentation and <<dependency_management_for_java_projects.adoc#dependency_management_for_java_projects, Dependency Management for Java Projects>>.
+For information on the `default` and `archives` configurations, please consult the
+<<base_plugin.adoc#sec:base_plugin_configurations,Base Plugin>> reference documentation.
+
+For information on the `api` or `compileOnlyApi` configurations, please consult the
+<<java_library_plugin.adoc#sec:java_library_separation, Java Library Plugin>> reference documentation and
+<<dependency_management_for_java_projects.adoc#dependency_management_for_java_projects, Dependency Management for Java Projects>>.
 
 ====
 
@@ -331,15 +336,8 @@
 `testRuntimeClasspath` extends `testRuntimeOnly, testImplementation`::
 Runtime classpath for running tests. Used by task `test`.
 
-`archives`::
-Artifacts (e.g. jars) produced by this project. Used by Gradle to determine "default" tasks to execute when building.
-
-`default` extends `runtimeElements`::
-The default configuration used by a project dependency on this project. Contains the artifacts and dependencies required by this project at runtime.
-
 The following diagrams show the dependency configurations for the _main_ and _test_ source sets respectively. You can use this legend to interpret the colors:
 
- * Gray text — the configuration is _deprecated_.
  * Green background — you can declare dependencies against the configuration.
  * Blue-gray background — the configuration is for consumption by tasks, not for you to declare dependencies.
  * Light blue background with monospace text — a task.
diff --git a/subprojects/docs/src/docs/userguide/jvm/java_testing.adoc b/subprojects/docs/src/docs/userguide/jvm/java_testing.adoc
index a7ed656..f239ee6 100644
--- a/subprojects/docs/src/docs/userguide/jvm/java_testing.adoc
+++ b/subprojects/docs/src/docs/userguide/jvm/java_testing.adoc
@@ -88,7 +88,7 @@
 `failFast` —  (since Gradle 4.6) default: false::
 Set this to `true` if you want the build to fail and finish as soon as one of your tests fails. This can save a lot of time when you have a long-running test suite and is particularly useful when running the build on continuous integration servers. When a build fails before all tests have run, the test reports only include the results of the tests that have completed, successfully or not.
 +
-You can also enable this behavior by using the `--fail-fast` command line option.
+You can also enable this behavior by using the `--fail-fast` command line option, or disable it respectively with `--no-fail-fast`.
 
 `testLogging` — default: _not set_::
 This property represents a set of options that control which test events are logged and at what level. You can also configure other logging behavior via this property. See link:{javadocPath}/org/gradle/api/tasks/testing/logging/TestLoggingContainer.html[TestLoggingContainer] for more detail.
@@ -641,7 +641,7 @@
 [[sec:debugging_java_tests]]
 == Debugging when running tests
 
-On the few occasions that you want to debug your code while the tests are running, it can be helpful if you can attach a debugger at that point. You can either set the link:{groovyDslPath}/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:debug[Test.getDebug()] property to `true` or use the `--debug-jvm` command line option.
+On the few occasions that you want to debug your code while the tests are running, it can be helpful if you can attach a debugger at that point. You can either set the link:{groovyDslPath}/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:debug[Test.getDebug()] property to `true` or use the `--debug-jvm` command line option, or use `--no-debug-jvm` to set it to false.
 
 When debugging for tests is enabled, Gradle will start the test process suspended and listening on port 5005.
 
diff --git a/subprojects/docs/src/docs/userguide/jvm/toolchains.adoc b/subprojects/docs/src/docs/userguide/jvm/toolchains.adoc
index 7764337..1fadd97 100644
--- a/subprojects/docs/src/docs/userguide/jvm/toolchains.adoc
+++ b/subprojects/docs/src/docs/userguide/jvm/toolchains.adoc
@@ -216,7 +216,7 @@
 == Auto-provisioning
 
 If Gradle can't find a locally available toolchain that matches the requirements of the build, it can automatically download one (as long as a toolchain download repository has been configured; for detail, see <<#sub:download_repositories,relevant section>>).
-Gradle installs the downloaded JDKs in the <<directory_layout.adoc#dir:gradle_user_home,Gradle User Home directory>>.
+Gradle installs the downloaded JDKs in the <<directory_layout.adoc#dir:gradle_user_home,Gradle User Home>>.
 
 [NOTE]
 ====
@@ -224,7 +224,7 @@
 There is no support for downloading early access versions.
 ====
 
-Once installed in the <<directory_layout.adoc#dir:gradle_user_home,Gradle User Home directory>>, a provisioned JDK becomes one of the JDKs visible to <<#sec:auto_detection,auto-detection>> and can be used by any subsequent builds, just like any other JDK installed on the system.
+Once installed in the <<directory_layout.adoc#dir:gradle_user_home,Gradle User Home>>, a provisioned JDK becomes one of the JDKs visible to <<#sec:auto_detection,auto-detection>> and can be used by any subsequent builds, just like any other JDK installed on the system.
 
 Since auto-provisioning only kicks in when auto-detection fails to find a matching JDK, auto-provisioning can only download new JDKs and is in no way involved in updating any of the already installed ones.
 None of the auto-provisioned JDKs will ever be revisited and automatically updated by auto-provisioning, even if there is a newer minor version available for them.
@@ -293,7 +293,7 @@
      | Vendor:             AdoptOpenJDK
      | Architecture:       x86_64
      | Is JDK:             false
-     | Detected by:        system property 'org.gradle.java.installations.paths'
+     | Detected by:        Gradle property 'org.gradle.java.installations.paths'
 
  + Microsoft JDK 16.0.2+7
      | Location:           /Users/username/.sdkman/candidates/java/16.0.2.7.1-ms
@@ -317,7 +317,7 @@
      | Vendor:             Oracle
      | Architecture:       x86_64
      | Is JDK:             false
-     | Detected by:        macOS java_home
+     | Detected by:        MacOS java_home
 ----
 
 This can help to debug which toolchains are available to the build, how they are detected and what kind of metadata Gradle knows about those toolchains.
@@ -449,3 +449,14 @@
 This can be achieved by passing an empty lambda when requesting a tool: `javaToolchainService.launcherFor({})`.
 
 You can find more details on defining custom tasks in the <<more_about_tasks.adoc#more_about_tasks, Authoring tasks>> documentation.
+
+[[sec:limitations]]
+== Toolchains limitations
+
+Gradle may detect toolchains incorrectly when it's running in a JVM compiled against `musl`, an https://musl.libc.org/[alternative implementation] of the C standard library.
+JVMs compiled against `musl` can sometimes override the `LD_LIBRARY_PATH` environment variable to control dynamic library resolution.
+This can influence forked java processes launched by Gradle, resulting in unexpected behavior.
+
+As a consequence, using multiple java toolchains is discouraged in environments with the `musl` library.
+This is the case in most Alpine distributions — consider using another distribution, like Ubuntu, instead.
+If you are using a single toolchain, the JVM running Gradle, to build and run your application, you can safely ignore this limitation.
diff --git a/subprojects/docs/src/docs/userguide/migration/feature_lifecycle.adoc b/subprojects/docs/src/docs/userguide/migration/feature_lifecycle.adoc
index 2180163..8d9adb5c 100644
--- a/subprojects/docs/src/docs/userguide/migration/feature_lifecycle.adoc
+++ b/subprojects/docs/src/docs/userguide/migration/feature_lifecycle.adoc
@@ -15,71 +15,123 @@
 [[feature_lifecycle]]
 = The Feature Lifecycle
 
-Gradle is under constant development and improvement. New versions are delivered on a regular and frequent basis (approximately every 6 weeks) as described in <<#eol_support,the section on end-of-life support>>. Continuous improvement combined with frequent delivery allows new features to be made available to users early and for invaluable real world feedback to be incorporated into the development process. Getting new functionality into the hands of users regularly is a core value of the Gradle platform. At the same time, API and feature stability is taken very seriously and is also considered a core value of the Gradle platform. This is something that is engineered into the development process by design choices and automated testing, and is formalised by <<#backwards_compatibility,the section on backwards compatibility>>.
+Gradle is under constant development. New versions are delivered on a regular and frequent basis (approximately every 6 weeks) as described in <<#eol_support,the section on end-of-life support>>.
 
-The Gradle _feature lifecycle_ has been designed to meet these goals. It also serves to clearly communicate to users of Gradle what the state of a feature is. The term _feature_ typically means an API or DSL method or property in this context, but it is not restricted to this definition. Command line arguments and modes of execution (e.g. the Build Daemon) are two examples of other kinds of features.
+Continuous improvement combined with frequent delivery allows new features to be made available to users early.
+Early users provide invaluable feedback which is incorporated into the development process.
+
+Getting new functionality into the hands of users regularly is a core value of the Gradle platform.
+
+At the same time, API and feature stability is taken very seriously and is also considered a core value of the Gradle platform.
+This is something that is engineered into the development process by design choices and automated testing and formalized by <<#backwards_compatibility,the section on backward compatibility>>.
+
+The Gradle _feature lifecycle_ has been designed to meet these goals. It also serves to communicate to users of Gradle what the state of a feature is.
+The term _feature_ typically means an API or DSL method or property in this context, but it is not restricted to this definition.
+Command line arguments and modes of execution (e.g. the Build Daemon) are two examples of other kinds of features.
 
 
 [[sec:states]]
 == States
 
-Features can be in one of 4 states:
+Features can be in one of four states:
 
-* Internal
-* Incubating
-* Public
-* Deprecated
+1. Internal
+2. Incubating
+3. Public
+4. Deprecated
 
 
 [[sec:internal]]
 === Internal
 
-Internal features are not designed for public use and are only intended to be used by Gradle itself. They can change in any way at any point in time without any notice. Therefore, we recommend avoiding the use of such features. Internal features are not documented. If it appears in this User Manual, the DSL Reference or the API Reference documentation then the feature is not internal.
+_Internal_ features are not designed for public use and are only intended to be used by Gradle itself. They can change in any way at any point in time without any notice.
+Therefore, we recommend avoiding the use of such features.
+_Internal_ features are not documented.
+If it appears in this User Manual, the DSL Reference, or the API Reference, then the feature is not _internal_.
 
-Internal features may evolve into public features.
+_Internal_ features may evolve into public features.
 
 [[sec:incubating_state]]
 === Incubating
 
-Features are introduced in the _incubating_ state to allow real world feedback to be incorporated into the feature before it is made public and locked down to provide backwards compatibility. It also gives users who are willing to accept potential future changes early access to the feature so they can put it into use immediately.
+Features are introduced in the _incubating_ state to allow real-world feedback to be incorporated into the feature before it is made public.
+It also gives users who are willing to test potential future changes early access.
 
-A feature in an incubating state may change in future Gradle versions until it is no longer incubating. Changes to incubating features for a Gradle release will be highlighted in the release notes for that release. The incubation period for new features varies depending on the scope, complexity and nature of the feature.
+A feature in an _incubating_ state may change in future Gradle versions until it is no longer _incubating_.
+Changes to _incubating_ features for a Gradle release will be highlighted in the release notes for that release.
+The _incubation_ period for new features varies depending on the scope, complexity, and nature of the feature.
 
-Features in incubation are clearly indicated to be so. In the source code, all methods/properties/classes that are incubating are annotated with link:{javadocPath}/org/gradle/api/Incubating.html[Incubating], which is also used to specially mark them in the DSL and API references. If an incubating feature is discussed in this User Manual, it will be explicitly said to be in the incubating state.
+Features in _incubation_ are indicated.
+In the source code, all methods/properties/classes that are _incubating_ are annotated with link:{javadocPath}/org/gradle/api/Incubating.html[incubating].
+This results in a special mark for them in the DSL and API references.
+
+If an _incubating_ feature is discussed in this User Manual, it will be explicitly said to be in the _incubating_ state.
 
 [[feature_preview]]
-==== Feature Preview API
+===== Feature Preview API
 
-The feature preview API allows certain incubating features to be activated by adding `enableFeaturePreview('FEATURE')` in your _settings_ file. Individual preview features will be announced in release notes.
+The feature preview API allows certain _incubating_ features to be activated by adding `enableFeaturePreview('FEATURE')` in your _settings_ file.
+Individual preview features will be announced in release notes.
 
-When _incubating_ features are either promoted to _public_ or removed, the feature preview flags for them become obsolete, have no effect and should be removed from the settings file.
+When _incubating_ features are either promoted to _public_ or removed, the feature preview flags for them become obsolete, have no effect, and should be removed from the settings file.
 
 [[sec:public]]
 === Public
 
-The default state for a non-internal feature is _public_. Anything that is documented in the User Manual, DSL Reference or API references that is not explicitly said to be incubating or deprecated is considered public. Features are said to be _promoted_ from an incubating state to public. The release notes for each release indicate which previously incubating features are being promoted by the release.
+The default state for a non-internal feature is _public_. Anything that is documented in the User Manual, DSL Reference, or API reference that is not explicitly said to be _incubating_ or _deprecated_ is considered _public_.
+Features are said to be *promoted* from an _incubating_ state to _public_.
+The release notes for each release indicate which previously _incubating_ features are being promoted by the release.
 
-A public feature will _never_ be removed or intentionally changed without undergoing deprecation. All public features are subject to the backwards compatibility policy.
+A _public_ feature will *never* be removed or intentionally changed without undergoing _deprecation_.
+All public features are subject to the backward compatibility policy.
 
 [[sec:deprecated]]
 === Deprecated
 
-Some features will become superseded or irrelevant due to the natural evolution of Gradle. Such features will eventually be removed from Gradle after being _deprecated_. A deprecated feature will _never_ be changed, until it is finally removed according to the backwards compatibility policy.
+Some features may be replaced or become irrelevant due to the natural evolution of Gradle.
+Such features will eventually be removed from Gradle after being _deprecated_.
+A _deprecated_ feature may become stale until it is finally removed according to the backward compatibility policy.
 
-Deprecated features are clearly indicated to be so. In the source code, all methods/properties/classes that are deprecated are annotated with “`@java.lang.Deprecated`” which is reflected in the DSL and API references. In most cases, there is a replacement for the deprecated element, and this will be described in the documentation. Using a deprecated feature will also result in a runtime warning in Gradle's output.
+_Deprecated_ features are indicated to be so.
+In the source code, all methods/properties/classes that are _deprecated_ are annotated with “`@java.lang.Deprecated`” which is reflected in the DSL and API References.
+In most cases, there is a replacement for the deprecated element, and this will be described in the documentation.
+Using a _deprecated_ feature will result in a runtime warning in Gradle's output.
 
-Use of deprecated features should be avoided. The release notes for each release indicate any features that are being deprecated by the release.
+The use of _deprecated_ features should be avoided.
+The release notes for each release indicate any features that are being _deprecated_ by the release.
 
 [[backwards_compatibility]]
-== Backwards Compatibility Policy
+== Backward Compatibility Policy
 
-Gradle provides backwards compatibility across major versions (e.g. `1.x`, `2.x`, etc.). Once a public feature is introduced or promoted in a Gradle release it will remain indefinitely or until it is deprecated. Once deprecated, it may be removed in the next major release. Deprecated features may be supported across major releases, but this is not guaranteed.
+Gradle provides backward compatibility across major versions (e.g. `1.x`, `2.x`, etc.).
+Once a public feature is introduced in a Gradle release, it will remain indefinitely unless it is deprecated.
+Once deprecated, it may be removed in the next major release.
+Deprecated features may be supported across major releases, but this is not guaranteed.
 
 [[eol_support]]
 == Release end-of-life Policy
 
-Every day, a new nightly build of Gradle is created.  This contains all of the changes that have made it through Gradle's extensive continuous integration tests during that day.  Not every nightly may contain new changes and not every nightly may be stable.
+Every day, a new nightly build of Gradle is created.
 
-For each minor or major release, the Gradle team creates a pre-release distribution called a release candidate (RC). When no problems are found after a short period of time (usually a week), the release candidate is promoted to a final/general availability (GA) release. If a regression is found in the release candidate, a new RC distribution is created and the process repeats.  Release candidates are supported for as long as the release window is open, but they are not intended to be used long term for production use. Bug reports are greatly appreciated during the RC phase.
+This contains all of the changes that have made it through Gradle's extensive continuous integration tests during that day.
+Nightly builds may contain new changes that may or may not be stable.
 
-After a final release, the Gradle team may create additional patch releases that are intended to replace the previous final release due to an important bugfix or regression.  For instance, Gradle 5.2.1 replaces the Gradle 5.2 release.  Once a release candidate has been made, all feature development moves on to the next release.  As such, each Gradle release causes the previous release to become end-of-line (EOL) and that release will not receive any new bugfixes or feature backports.
+For each minor or major release, the Gradle team creates a pre-release distribution called a release candidate (RC).
+When no problems are found after a short time (usually a week), the release candidate is promoted to a general availability (GA) release.
+If a regression is found in the release candidate, a new RC distribution is created and the process repeats.
+Release candidates are supported for as long as the release window is open, but they are not intended to be used for production.
+Bug reports are greatly appreciated during the RC phase.
+
+The Gradle team may create additional patch releases that are intended to replace the final release due to important bug fixes or regressions.
+For instance, Gradle 5.2.1 replaces the Gradle 5.2 release.
+
+Once a release candidate has been made, all feature development moves on to the next release for the latest major version.
+As such, each minor Gradle release causes the previous minor releases in the same major version to become end-of-life (EOL). EOL releases do not receive bug fixes or feature backports.
+
+For major versions, Gradle will backport critical fixes and security fixes to the last minor in the previous major version.
+For example, when Gradle 7 was the latest major version, several releases in the 6.x line, including Gradle 6.9 (and subsequent releases) were made.
+
+As such, each major Gradle release causes:
+
+* The previous major version becomes maintenance only and it will only receive critical bug fixes and security fixes.
+* The major version before the previous one to become end-of-life (EOL), and that release line will not receive any new fixes.
diff --git a/subprojects/docs/src/docs/userguide/migration/migrating_from_ant.adoc b/subprojects/docs/src/docs/userguide/migration/migrating_from_ant.adoc
index 89b872d..6336e99 100644
--- a/subprojects/docs/src/docs/userguide/migration/migrating_from_ant.adoc
+++ b/subprojects/docs/src/docs/userguide/migration/migrating_from_ant.adoc
@@ -15,65 +15,97 @@
 [[migrating_from_ant]]
 = Migrating Builds From Apache Ant
 
-https://ant.apache.org/[Apache Ant] is a build tool with a long history in the Java world that is still widely used, albeit by a decreasing number of teams. While flexible, it lacks conventions and many of the powerful features that Gradle can provide. Migrating to Gradle is worthwhile so that your builds can become slimmer, simpler and faster, while still retaining the flexibility you enjoy with Ant. You'll also benefit from robust support for multi-project builds and easy-to-use, flexible dependency management.
+https://ant.apache.org/[Apache Ant] is a build tool with a long history in the Java world that is still widely used, albeit by a decreasing number of teams.
+While flexible, it lacks conventions and many of the powerful features that Gradle provides.
+Migrating to Gradle is worthwhile so that your builds can become slimmer, simpler, and faster, while still retaining the flexibility you enjoy with Ant.
+You will also benefit from robust support for multi-project builds and easy-to-use, flexible dependency management.
 
-The biggest challenge in migrating from Ant to Gradle is that there is no such thing as a standard Ant build. That makes it difficult to provide specific instructions. Fortunately, Gradle has some great integration features with Ant that can make the process relatively smooth. And even migrating from https://ant.apache.org/ivy/[Ivy]-based dependency management isn't particularly hard because Gradle has a similar model based on <<dependency_management_terminology#sub:terminology_configuration,dependency configurations>> that works with Ivy-compatible repositories.
+The biggest challenge in migrating from Ant to Gradle is that there is no such thing as a standard Ant build.
+That makes it difficult to provide specific instructions.
+Fortunately, Gradle has some great integration features with Ant that can make the process relatively smooth.
+Migrating from https://ant.apache.org/ivy/[Ivy]-based dependency management isn't difficult because Gradle has a similar model based on <<dependency_management_terminology#sub:terminology_configuration,dependency configurations>> that works with Ivy-compatible repositories.
 
-We will start by outlining the things you should consider at the outset of migrating a build from Ant to Gradle and offer some general guidelines on how to proceed.
+We will start by outlining the things you should consider when migrating a build from Ant to Gradle and offer some general guidelines on how to proceed.
 
 [[migant:general_guidelines]]
 == General guidelines
 
-When you undertake to migrate a build from Ant to Gradle, you should keep in mind the nature of both what you already have and where you would like to end up. Do you want a Gradle build that mirrors the structure of the existing Ant build? Or do you want to move to something that is more idiomatic to Gradle? What are the main benefits you are looking for?
+When you migrate a build from Ant to Gradle, you should keep in mind the nature of what you already have and where you would like to end up.
+Do you want a Gradle build that mirrors the structure of the existing Ant build?
+Or do you want to move to something that is more idiomatic to Gradle?
+What are the main benefits you are looking for?
 
-To understand the implications, consider the two extreme endpoints that you could aim for:
+To better understand, consider the following opposing scenarios:
 
  * An imported build via <<ant#sec:import_ant_build,`ant.importBuild()`>>
 +
-This approach is quick, simple and works for many Ant-based builds. You end up with a build that's effectively identical to the original Ant build, except your Ant targets become Gradle tasks. Even the dependencies between targets are retained.
+This approach is quick, simple, and works for many Ant-based builds.
+You end up with a build that is effectively identical to the original Ant build, except your Ant targets become Gradle tasks.
+Even the dependencies between targets are retained.
 +
-The downside is that you're still using the Ant build, which you must continue to maintain. You also lose the advantages of Gradle's conventions, many of its plugins, its dependency management, and so on. You can still enhance the build with <<incremental_build.adoc#incremental_build,incremental build information>>, but it's more effort than would be the case for a normal Gradle build.
+The downside is that you're still using the Ant build, which you must continue to maintain.
+You also lose the advantages of Gradle's conventions, many of its plugins, its dependency management, and so on.
+You can still enhance the build with <<incremental_build.adoc#incremental_build,incremental build information>>, but it's more effort than would be the case for a normal Gradle build.
+
  * An idiomatic Gradle build
 +
-If you want to future proof your build, this is where you want to end up. Making use of Gradle's conventions and plugins will result in a smaller, easier-to-maintain build, with a structure that is familiar to many Java developers. You will also find it easier to take advantage of Gradle's power features to improve build performance.
+If you want to future proof your build, this is where you want to end up.
+Making use of Gradle's conventions and plugins will result in a smaller, easier-to-maintain build, with a structure that is familiar to many Java developers.
+You will also find it easier to take advantage of Gradle's power features to improve build performance.
 +
-The main downside is the extra work required to perform the migration, particularly if the existing build is complex and has many inter-project dependencies. But such builds often benefit the most from a switch to idomatic Gradle. In addition, Gradle provides many features that can ease the migration, such as the ability to <<ant#sec:using_ant_tasks,use core and custom Ant tasks>> directly from a Gradle build.
+The main downside is the extra work required to perform the migration, particularly if the existing build is complex and has many inter-project dependencies.
+However, these builds often benefit the most from a switch to idiomatic Gradle.
+In addition, Gradle provides many features that can ease the migration, such as the ability to <<ant#sec:using_ant_tasks,use core and custom Ant tasks>> directly from a Gradle build.
 
 You ideally want to end up somewhere close to the second option in the long term, but you don't have to get there in one fell swoop.
 
 What follows is a series of steps to help you decide the approach you want to take and how to go about it:
 
- 1. Keep the old Ant build and new Gradle build side by side
+ 1. Keep the old Ant build and new Gradle build side by side.
 +
-You know the Ant build works, so you should keep it until you are confident that the Gradle build produces all the same artifacts and otherwise does what you need. This also means that users can try the Gradle build without getting a new copy of the source tree.
+You know the Ant build works, so you should keep it until you are confident that the Gradle build produces all the same artifacts and otherwise does what you need.
+This also means that users can try the Gradle build without creating a new copy of the source tree.
 +
 Don't try to change the directory and file structure of the build until after you're ready to make the switch.
- 2. Develop a mechanism to verify that the two builds produce the same artifacts
+ 2. Develop a mechanism to verify that the two builds produce the same artifacts.
 +
-This is a vitally important step to ensure that your deployments and tests don't break. Even small changes, such as the contents of a manifest file in a JAR, can cause problems. If your Gradle build produces the same output as the Ant build, this will give you and others confidence in switching over and make it easier to implement the big changes that will provide the greatest benefits.
- 3. Decide whether you have a multi-project build or not
+This is a vitally important step to ensure that your deployments and tests don't break.
+Even small changes, such as the contents of a manifest file in a JAR, can cause problems.
+If your Gradle build produces the same output as the Ant build, this will give you and others confidence in switching over and make it easier to implement the big changes that will provide the greatest benefits.
+ 3. Decide whether you have a multi-project build or not.
 +
 Multi-project builds are generally harder to migrate and require more work than single-project ones. We have provided some dedicated advice to help with the process in the <<migant:multi_project_builds,Migrating multi-project builds>> section.
- 4. Work out what plugins to use for each project
+ 4. Work out what plugins to use for each project.
 +
-We expect that the vast majority of Ant builds are for <<building_java_projects#building_java_projects,JVM-based projects>>, for which there are a wealth of plugins that provide a lot of the functionality you need. Not only are there the <<plugin_reference#plugin_reference,core plugins>> that come packaged with Gradle, but you can also find many useful plugins on the https://plugins.gradle.org/[Plugin Portal].
+We expect that the vast majority of Ant builds are for <<building_java_projects#building_java_projects,JVM-based projects>>, for which there are a wealth of plugins that provide a lot of the functionality you need.
+Gradle plugins include <<plugin_reference#plugin_reference,core plugins>> that come packaged with Gradle and useful community plugins on the https://plugins.gradle.org/[Plugin Portal].
 +
 Even if the <<java_plugin#java_plugin,Java Plugin>> or one of its derivatives (such as the <<java_library_plugin#java_library_plugin,Java Library Plugin>>) aren't a good match for your build, you should at least consider the <<base_plugin#base_plugin,Base Plugin>> for its lifecycle tasks.
- 5. Import the Ant build or create a Gradle build from scratch
+ 5. Import the Ant build or create a Gradle build from scratch.
 +
-This step very much depends on the requirements of your build. If a selection of Gradle plugins can do the vast majority of the work your Ant build does, then it probably makes sense to create a fresh Gradle build script that doesn't depend on the Ant build and either implements the missing pieces itself or <<ant#sec:using_ant_tasks,utilizes existing Ant tasks>>.
+This step very much depends on the requirements of your build.
+If a selection of Gradle plugins can do the vast majority of the work your Ant build does, then it probably makes sense to create a fresh Gradle build script that doesn't depend on the Ant build.
+You can either implement the missing pieces yourself or <<ant#sec:using_ant_tasks,use existing Ant tasks>>.
 +
-The alternative approach is to <<ant#sec:import_ant_build,import the Ant build>> into the Gradle build script and gradually replace the Ant build functionality. This allows you to have a working Gradle build at each stage, but it requires a bit of work to get the Gradle tasks working properly with the Ant ones. You can learn more about this approach in <<migant:imported_builds,Working with an imported build>>.
+The alternative approach is to <<ant#sec:import_ant_build,import the Ant build>> into the Gradle build script and gradually replace the Ant build functionality.
+This allows you to have a working Gradle build at each stage, but it requires a bit of work to get the Gradle tasks working properly with the Ant ones.
+You can learn more about this in <<migant:imported_builds,Working with an imported build>>.
  6. Configure your build for the existing directory and file structure
 +
-Gradle makes use of conventions to eliminate much of the boilerplate associated with older builds and to make it easier for users to work with new builds once they are familiar with those conventions. But that doesn't mean you have to follow them.
+Gradle makes use of conventions to eliminate much of the boilerplate associated with older builds and to make it easier for users to work with new builds once they are familiar with those conventions.
+But that doesn't mean you have to follow them.
 +
-Gradle provides many configuration options that allow for a good degree of customization. Those options are typically made available through the plugins that provide the conventions. For example, the standard source directory structure for production Java code — `src/main/java` — is provided by the Java Plugin, which allows you to <<building_java_projects#sec:custom_java_source_set_paths,configure a different source path>>. Many paths can be modified via properties on the link:{groovyDslPath}/org.gradle.api.Project.html[Project] object.
+Gradle provides many configuration options that allow for a good degree of customization.
+Those options are typically made available through the plugins that provide the conventions.
+For example, the standard source directory structure for production Java code — `src/main/java` — is provided by the Java Plugin, which allows you to <<building_java_projects#sec:custom_java_source_set_paths,configure a different source path>>.
+Many paths can be modified via properties on the link:{groovyDslPath}/org.gradle.api.Project.html[Project] object.
  7. Migrate to standard Gradle conventions if you wish
 +
-Once you're confident that the Gradle build is producing the same artifacts and other resources as the Ant build, you can consider migrating to the standard conventions, such as for source directory paths. Doing so will allow you to remove the extra configuration that was required to override those conventions. New team members will also find it easier to work with the build after the change.
+Once you're confident that the Gradle build is producing the same artifacts and other resources as the Ant build, you can consider migrating to the standard conventions, such as for source directory paths.
+Doing so will allow you to remove the extra configuration that was required to override those conventions.
+New team members will also find it easier to work with the build after the change.
 +
-It's up to you to decide whether this step is worth the time, energy and potential disruption that it might incur, which in turn depends on your specific build and team.
+It's up to you to decide whether this step is worth the effort and potential disruption, which in turn depends on your specific build and team.
 
 The rest of the chapter covers some common scenarios you will likely deal with during the migration, such as dependency management and working with Ant tasks.
 
@@ -83,32 +115,38 @@
 WARNING: Importing an Ant build is not supported with the <<configuration_cache.adoc#config_cache,configuration cache>>.
 You need to complete the conversion to Gradle to get the benefits of caching.
 
-The first step of many migrations will involve <<ant#sec:import_ant_build,importing an Ant build>> using `ant.importBuild()`. If you do that, how do you then move towards a standard Gradle build without replacing everything at once?
+The first step of many migrations will involve <<ant#sec:import_ant_build,importing an Ant build>> using `ant.importBuild()`.
+Then how do you then move towards a standard Gradle build without replacing everything at once?
 
-The important thing to remember is that the Ant targets become real Gradle tasks, meaning you can do things like modify their task dependencies, attach extra task actions, and so on. This allows you to substitute native Gradle tasks for the equivalent Ant ones, maintaining any links to other existing tasks.
+The important thing to remember is that the Ant targets become real Gradle tasks, meaning you can do things like modify their task dependencies, attach extra task actions, and so on.
+This allows you to substitute native Gradle tasks for the equivalent Ant ones, maintaining any links to other existing tasks.
 
-As an example, imagine that you have a Java library project that you want to migrate from Ant to Gradle. The Gradle build script has the line that imports the Ant build and now want to use the standard Gradle mechanism for compiling the Java source files. However, you want to keep using the existing `package` task that creates the library's JAR file.
+As an example, imagine that you have a Java library project that you want to migrate from Ant to Gradle.
+The Gradle build script has the line that imports the Ant build and now want to use the standard Gradle mechanism for compiling the Java source files.
+However, you want to keep using the existing `package` task that creates the library's JAR file.
 
-In diagrammatic form, the scenario looks like the following, where each box represents a target/task:
+In diagram form, the scenario looks like the following, where each box represents a target/task:
 
 image::ant-task-migration.png[]
 
-The idea is to substitute the standard Gradle `compileJava` task for the Ant `build` task. There are several steps involved in this substitution:
+The idea is to substitute the standard Gradle `compileJava` task for the Ant `build` task.
+There are several steps involved in this substitution:
 
- 1. Applying the <<java_library_plugin#java_library_plugin,Java Library Plugin>>
+ 1. Applying the <<java_library_plugin#java_library_plugin,Java Library Plugin>>.
 +
 This provides the `compileJava` task shown in the diagram.
- 2. Renaming the old `build` task
+ 2. Renaming the old `build` task.
 +
 The name `build` conflicts with the standard `build` task provided by the <<base_plugin#base_plugin,Base Plugin>> (via the Java Library Plugin).
- 3. Configuring the compilation to use the existing directory structure
+ 3. Configuring the compilation to use the existing directory structure.
 +
 There's a good chance the Ant build does not conform to the standard Gradle directory structure, so you need to tell Gradle where to find the source files and where to place the compiled classes so `package` can find them.
- 4. Updating task dependencies
+ 4. Updating task dependencies.
 +
 `compileJava` must depend on `prepare`, `package` must depend on `compileJava` rather than `ant_build`, and `assemble` must depend on `package` rather than the standard Gradle `jar` task.
 
-Applying the plugin is as simple as inserting a  `plugins {}` block at the beginning of the Gradle build script, i.e. before `ant.importBuild()`. Here's how to apply the Java Library Plugin:
+Applying the plugin is as simple as inserting a  `plugins {}` block at the beginning of the Gradle build script, i.e. before `ant.importBuild()`.
+Here's how to apply the Java Library Plugin:
 
 .Applying the Java Library Plugin
 ====
@@ -125,9 +163,11 @@
 ====
 <1> Renames the `build` target to `ant_build` and leaves all other targets unchanged
 
-Configuring a different path for the sources is described in the <<building_java_projects#sec:custom_java_source_set_paths,Building Java & JVM projects>> chapter, while you can change the output directory for the compiled classes in a similar way.
+Configuring a different path for the sources is described in <<building_java_projects#sec:custom_java_source_set_paths,Building Java & JVM projects>>.
+You can change the output directory for the compiled classes in a similar way.
 
-Let's say the original Ant build stores these paths in Ant properties, `src.dir` for the Java source files and `classes.dir` for the output. Here's how you would configure Gradle to use those paths:
+If, for example, the original Ant build stores these paths in Ant properties; `src.dir` for the Java source files and `classes.dir` for the output.
+Here's how you would configure Gradle to use those paths:
 
 .Configuring the source sets
 ====
@@ -135,11 +175,12 @@
 include::sample[dir="snippets/antMigration/importBuild/groovy",files="build.gradle[tags=source-sets]"]
 ====
 
-You should eventually aim to switch the standard directory structure for your type of project if possible and then you'll be able to remove this customization.
+You should eventually switch to the standard directory structure for your type of project so that you will be able to remove this customization.
 
-The last step is also straightforward and involves using the link:{groovyDslPath}/org.gradle.api.Task.html#org.gradle.api.Task:dependsOn[Task.dependsOn] property and link:{groovyDslPath}/org.gradle.api.Task.html#org.gradle.api.Task:dependsOn(java.lang.Object++[]++)[Task.dependsOn()] method to detach and link tasks. The property is appropriate for _replacing_ dependencies, while the method is the preferred way to add to the existing dependencies.
+The last step is straightforward and involves using the link:{groovyDslPath}/org.gradle.api.Task.html#org.gradle.api.Task:dependsOn[Task.dependsOn] property and link:{groovyDslPath}/org.gradle.api.Task.html#org.gradle.api.Task:dependsOn(java.lang.Object++[]++)[Task.dependsOn()] method to detach and link tasks.
+The property is appropriate for _replacing_ dependencies, while the method is the preferred way to add to the existing dependencies.
 
-Here is the required task dependency configuration required by the example scenario, which should come after the Ant build import:
+Here is the required task dependency configuration for the example scenario, which should come after the Ant build import:
 
 .Configuring the task dependencies
 ====
@@ -150,16 +191,19 @@
 <2> Detaches `package` from the `ant_build` task and makes it depend on `compileJava`
 <3> Detaches `assemble` from the standard Gradle `jar` task and makes it depend on `package` instead
 
-That's it! These four steps will successfully replace the old Ant compilation with the Gradle implementation. Even this small migration will be a big help because you'll be able to take advantage of Gradle's <<java_plugin#sec:incremental_compile,incremental Java compilation>> for faster builds.
+These four steps will successfully replace the old Ant compilation with the Gradle implementation.
+Even this small migration will give you the advantage of Gradle's <<java_plugin#sec:incremental_compile,incremental Java compilation>> for faster builds.
 
 [TIP]
 ====
-This is just a demonstration of how to go about performing a migration in stages. It may make more sense to include resource processing — like with properties files — and packaging with the compilation in this stage, since all three aspects are well integrated in Gradle.
+This is one example of a staged migration. It may make more sense to include resource processing — such as properties files — and packaging with the compilation in this stage.
 ====
 
-One important question you will have to ask yourself is how many tasks to migrate in each stage. The larger the chunks you can migrate in one go the better, but this must be offset against how many custom steps within the Ant build will be affected by the changes.
+One important question you will have to ask yourself is how many tasks to migrate in each stage.
+The more you can migrate in one go the better, but risk comes with the number of custom steps within the Ant build that will be affected by the changes.
 
-For example, if the Ant build follows a fairly standard approach for compilation, static resources, packaging and unit tests, then it is probably worth migrating all those together. But if the build performs some extra processing on the compiled classes, or does something unique when processing the static resources, it is probably worth splitting those tasks into separate stages.
+For example, if the Ant build follows a fairly standard approach for compilation, static resources, packaging and unit tests, then it is probably worth migrating all of those together.
+But if the build performs some extra processing on the compiled classes, or does something unique when processing the static resources, it is probably worth splitting those tasks into separate stages.
 
 [[migant:managing_dependencies]]
 == Managing dependencies
@@ -169,21 +213,23 @@
  * Storing them with the project in a local "lib" directory
  * Using https://ant.apache.org/ivy/[Apache Ivy] to manage them
 
-They each require a different technique for the migration to Gradle, but you will find the process straightforward in either case. We look at the details of each scenario in the following sections.
+They each require a different technique for the migration to Gradle, but you will find the process straightforward in either case.
+Let's look at each case, in detail, in the following sections.
 
 [[migant:filesystem_deps]]
 === Serving dependencies from a directory
 
-When you are attempting to migrate a build that stores its dependencies on the filesystem, either locally or on the network, you should consider whether you want to eventually move to managed dependencies using remote repositories. That's because you can incorporate filesystem dependencies into a Gradle build in one of two ways:
+When you are attempting to migrate a build that stores its dependencies on the filesystem, either locally or on the network, you should consider whether you want to eventually move to managed dependencies using remote repositories.
+That's because you can incorporate filesystem dependencies into a Gradle build in one of two ways:
 
  * Define a <<declaring_repositories.adoc#sub:flat_dir_resolver,flat-directory repository>> and use standard dependency declarations
  * Attach the files directly to the appropriate dependency configurations (<<declaring_dependencies#sub:file_dependencies,file dependencies>>)
 
-It's easier to migrate to managed dependencies served from Maven- or Ivy-compatible repositories if you take the first approach, but doing so requires all your files to conform to the naming convention "<moduleName>-<version>.<extension>".
+It's easier to migrate to managed dependencies served from Maven, or Ivy-compatible repositories, if you take the first approach, but doing so requires all your files to conform to the naming convention "<moduleName>-<version>.<extension>".
 
 [NOTE]
 ====
-If you store your dependencies in the standard Maven repository layout — `<repoDir>/<group>/<module>/<version>` — then you can define a <<declaring_repositories.adoc#sec:declaring_custom_repository,custom Maven repository>> with a "file://" URL.
+If you store your dependencies in the standard Maven repository layout — `<repoDir>/<group>/<module>/<version>` — then you can define a <<declaring_repositories.adoc#sec:declaring_custom_repository,custom Maven repository>> with a `file://` URL.
 ====
 
 To demonstrate the two techniques, consider a project that has the following library JARs in its `libs` directory:
@@ -194,8 +240,8 @@
 ├── awesome-framework-2.0.jar
 └── utility-library-1.0.jar
 
-The file `our-custom.jar` lacks a version number, so it has to be added as a file dependency.
-But the other two JARs match the required naming convention and so can be declared as normal <<declaring_dependencies#sub:module_dependencies,module dependencies>> that are retrieved from a flat-directory repository.
+The file `our-custom.jar` has no version number, so it has to be added as a file dependency.
+The other two JARs match the required naming convention and can be declared as normal <<declaring_dependencies#sub:module_dependencies,module dependencies>> that are retrieved from a flat-directory repository.
 
 The following sample build script demonstrates how you can incorporate all of these libraries into a build:
 
@@ -205,22 +251,24 @@
 include::sample[dir="snippets/antMigration/fileDeps/groovy",files="build.gradle[tags=file-deps]"]
 ====
 <1> Specifies the path to the directory containing the JAR files
-<2> Declares a _file dependency_ for the unversioned JAR
+<2> Declares a _file dependency_ for the un-versioned JAR
 <3> Declares dependencies using standard dependency coordinates — note that no group is specified, but each identifier has a leading `:`, implying an empty group
 
 The above sample will add `our-custom.jar`, `awesome-framework-2.0.jar` and `utility-library-1.0.jar` to the `implementation` configuration, which is used to compile the project's code.
 
 [NOTE]
 ====
-You can also specify a group in these module dependencies, even though they don't actually have a group. That's because the flat-directory repository simply ignores the information.
+You can also specify a group in these module dependencies, even though they don't actually have a group.
+That's because the flat-directory repository simply ignores this information.
 
-If you then add a normal Maven- or Ivy-compatible repository at a later date, Gradle will preferentially download the module dependencies that are declared with a group from that repository rather than the flat-directory one.
+Then, if you add a normal Maven or Ivy-compatible repository at a later date, Gradle will download the module dependencies that are declared with a group from that repository rather than the flat-directory one.
 ====
 
 [[migant:ivy_deps]]
 === Migrating Ivy dependencies
 
-Apache Ivy is a standalone dependency management tool that is widely used with Ant. It works in a similar fashion to Gradle. In fact, they both allow you to
+Apache Ivy is a standalone dependency management tool that is widely used with Ant. It works similarly to Gradle.
+In fact, they both allow you to:
 
  * Define your own <<dependency_management_terminology#sub:terminology_configuration,configurations>>
  * Extend configurations from one another
@@ -230,9 +278,9 @@
 
 The most notable difference is that Gradle has standard configurations for specific types of projects.
 For example, the <<java_plugin#tab:configurations,Java Plugin>> defines configurations like `implementation`, `testImplementation` and `runtimeOnly`.
-You can still <<declaring_dependencies.adoc#sec:defining-custom-configurations,define your own dependency configurations>>, though.
+You are able to <<declaring_dependencies.adoc#sec:defining-custom-configurations,define your own dependency configurations>> if needed.
 
-This similarity means that it's usually quite straightforward to migrate from Ivy to Gradle:
+As such, it's typically straightforward to migrate from Ivy to Gradle:
 
  * Transcribe the dependency declarations from your module descriptors into the link:{groovyDslPath}/org.gradle.api.Project.html#org.gradle.api.Project:dependencies(groovy.lang.Closure)[dependencies {}] block of your Gradle build script, ideally using the standard configurations provided by any plugins you apply.
  * Transcribe any configuration declarations from your module descriptors into the link:{groovyDslPath}/org.gradle.api.Project.html#org.gradle.api.Project:configurations(groovy.lang.Closure)[configurations {}] block of the build script for any custom configurations that can't be replaced by Gradle's standard ones.
@@ -240,33 +288,45 @@
 
 See the chapters on <<declaring_dependencies.adoc#sec:what-are-dependency-configurations,Managing Dependency Configurations>>, <<declaring_dependencies.adoc#sec:dependency-types,Declaring Dependencies>> and <<declaring_repositories.adoc#declaring-repositories,Declaring Repositories>> for more information.
 
-Ivy provides several Ant tasks that handle Ivy's process for fetching dependencies. The basic steps of that process consist of:
+Ivy provides several Ant tasks that handle Ivy's process for fetching dependencies.
+The basic steps of that process consist of:
 
  1. _Configure_ — applies the configuration defined in the Ivy settings file
  2. _Resolve_ — locates the declared dependencies and downloads them to the cache if necessary
  3. _Retrieve_ — copies the cached dependencies to another directory
 
-Gradle's process is similar, but you don't have to explicitly invoke the first two steps as it performs them automatically. The third step doesn't happen at all — unless you create a task to do it — because Gradle typically uses the files in the dependency cache directly in classpaths and as the source for assembling application packages.
+Gradle's process is similar, but you don't have to explicitly invoke the first two steps as it performs them automatically.
+The third step doesn't happen at all — unless you create a task to do it — because Gradle typically uses the files in the dependency cache directly in classpaths and as the source for assembling application packages.
 
 Let's look in more detail at how Ivy's steps map to Gradle:
 
 Configuration::
-Most of Gradle's dependency-related configuration is baked into the build script, as you've seen with elements like the `dependencies {}` block. Another particularly important configuration element is link:{groovyDslPath}/org.gradle.api.artifacts.ResolutionStrategy.html[resolutionStrategy], which can be accessed from dependency configurations. This provides many of the features you might get from Ivy's conflict managers and is a powerful way to control transitive dependencies and caching.
+Most of Gradle's dependency-related configuration is baked into the build script, as you've seen with elements like the `dependencies {}` block.
+Another particularly important configuration element is link:{groovyDslPath}/org.gradle.api.artifacts.ResolutionStrategy.html[resolutionStrategy], which can be accessed from dependency configurations.
+This provides many of the features you might get from Ivy's conflict managers and is a powerful way to control transitive dependencies and caching.
 +
-Some Ivy configuration options have no equivalent in Gradle. For example, there are no lock strategies because Gradle ensures that its dependency cache is concurrency safe, period. Nor are there "latest strategies" because it's simpler to have a reliable, single strategy for conflict resolution. If the "wrong" version is picked, you can easily override it using forced versions or other resolution strategy options.
+Some Ivy configuration options have no equivalent in Gradle.
+For example, there are no lock strategies because Gradle guarantees that its dependency cache is concurrency safe.
+There are no "latest strategies" methodology because it's simpler to have a reliable, single strategy for conflict resolution.
+If the "wrong" version is picked, you can override it using forced versions or other resolution options.
 +
-See the chapter on <<dependency_constraints.adoc#dependency-constraints,controlling transitive dependencies>> for more information on this aspect of Gradle.
+See the chapter on <<dependency_constraints.adoc#dependency-constraints,controlling transitive dependencies>> for more information.
 
 Resolution::
 At the beginning of the build, Gradle will automatically resolve any dependencies that you have declared and download them to its cache.
-It searches the repositories for those dependencies, with the search order defined by <<declaring_repositories.adoc#sec:declaring_multiple_repositories,the order in which the repositories are declared>>.
+Gradle searches the repositories for those dependencies, with the search order defined by <<declaring_repositories.adoc#sec:declaring_multiple_repositories,the order in which the repositories are declared>>.
 +
-It's worth noting that Gradle supports the same dynamic version syntax as Ivy, so you can still use versions like `1.0.+`. You can also use the special `latest.integration` and `latest.release` labels if you wish. If you decide to use such <<dynamic_versions.adoc#sub:declaring_dependency_with_dynamic_version,dynamic>> and <<dynamic_versions.adoc#sub:declaring_dependency_with_changing_version,changing>> dependencies, you can configure the caching behavior for them via link:{groovyDslPath}/org.gradle.api.artifacts.ResolutionStrategy.html[resolutionStrategy].
+It's worth noting that Gradle supports the same dynamic version syntax as Ivy, so you can still use conventions like `1.0.+`.
+You can also use the special `latest.integration` and `latest.release` labels.
+If you decide to use such <<dynamic_versions.adoc#sub:declaring_dependency_with_dynamic_version,dynamic>> and <<dynamic_versions.adoc#sub:declaring_dependency_with_changing_version,changing>> dependencies, you can configure the caching behavior for them via link:{groovyDslPath}/org.gradle.api.artifacts.ResolutionStrategy.html[resolutionStrategy].
 +
-You might also want to consider <<dependency_locking#dependency-locking,dependency locking>> if you're using dynamic and/or changing dependencies. It's a way to make the build more reliable and allows for https://reproducible-builds.org/[reproducible builds].
+You might also want to consider <<dependency_locking#dependency-locking,dependency locking>> if you're using dynamic and/or changing dependencies.
+It's a way to make the build more reliable and ensures https://reproducible-builds.org/[reproducibility].
 
 Retrieval::
-As mentioned, Gradle does not automatically copy files from the dependency cache. Its standard tasks typically use the files directly. If you want to copy the dependencies to a local directory, you can use a link:{groovyDslPath}/org.gradle.api.tasks.Copy.html[Copy] task like this in your build script:
+As mentioned, Gradle does not automatically copy files from the dependency cache.
+Its standard tasks typically use the files directly.
+If you want to copy the dependencies to a local directory, you can use a link:{groovyDslPath}/org.gradle.api.tasks.Copy.html[Copy] task like this in your build script:
 +
 .Copying dependencies to a local directory
 ====
@@ -274,15 +334,18 @@
 include::sample[dir="snippets/antMigration/fileDeps/groovy",files="build.gradle[tags=retrieve-deps]"]
 ====
 +
-A configuration is also a file collection, hence why it can be used in the `from()` configuration. You can use a similar technique to attach a configuration to a compilation task or one that produces documentation. See the chapter on <<working_with_files#working_with_files,Working with Files>> for more examples and information on Gradle's file API.
+A configuration is also a file collection, hence why it can be used in the `from()` configuration.
+You can use a similar technique to attach a configuration to a compilation task or one that produces documentation.
+See the chapter on <<working_with_files#working_with_files,Working with Files>> for more examples and information on Gradle's file API.
 
 [[migant:ivy_publishing]]
 == Publishing artifacts
 
-Projects that use Ivy to manage dependencies often also use it for publishing JARs and other artifacts to repositories. If you're migrating such a build, then you'll be glad to know that Gradle has built-in support for publishing artifacts to Ivy-compatible repositories.
+Projects that use Ivy to manage dependencies often also use it for publishing JARs and other artifacts to repositories.
+If you're migrating such a build, then you'll be glad to know that Gradle has built-in support for publishing artifacts to Ivy-compatible repositories.
 
 Before you attempt to migrate this particular aspect of your build, read the <<publishing_setup.adoc#publishing_overview,Publishing>> chapter to learn about Gradle's publishing model.
-That chapter's examples are based on Maven repositories, but the same model is used for Ivy repositories as well.
+The chapter examples are based on Maven repositories but the same model is used for Ivy repositories.
 
 The basic migration process looks like this:
 
@@ -290,46 +353,58 @@
  * <<publishing_ivy#publishing_ivy:publications,Configure at least one publication>>, representing what will be published (including additional artifacts if desired)
  * <<publishing_ivy#publishing_ivy:repositories,Configure one or more repositories to publish artifacts to>>
 
-Once that's all done, you'll be able to generate an Ivy module descriptor for each publication and publish them to one or more repositories.
+Once that's all done, you will be able to generate an Ivy module descriptor for each publication and publish them to one or more repositories.
 
-Let's say you have defined a publication named "myLibrary" and a repository named "myRepo". Ivy's Ant tasks would then map to the Gradle tasks like this:
+Let's say you have defined a publication named "myLibrary" and a repository named "myRepo".
+Ivy's Ant tasks would then map to the Gradle tasks like this:
 
  * `<deliver>` -> `generateDescriptorFileForMyLibraryPublication`
  * `<publish>` -> `publishMyLibraryPublicationToMyRepoRepository`
 
 There is also a convenient `publish` task that publishes _all_ publications to _all_ repositories.
-If you'd prefer to limit which publications go to which repositories, check out the <<publishing_customization.adoc#sec:publishing_maven:conditional_publishing,relevant section of the Publishing chapter>>.
+If you want to limit publications to specific repositories, check out the <<publishing_customization.adoc#sec:publishing_maven:conditional_publishing,relevant section of the Publishing chapter>>.
 
 .On dependency versions
 [NOTE]
 ====
-Ivy will, by default, automatically replace dynamic versions of dependencies with the resolved "static" versions when it generates the module descriptor. Gradle does _not_ mimic this behavior: declared dependency versions are left unchanged.
+Ivy will, by default, automatically replace dynamic versions of dependencies with the resolved "static" versions when it generates the module descriptor.
+Gradle does _not_ mimic this behavior, declared dependency versions are left unchanged.
 
-You can replicate the default Ivy behavior by using the https://plugins.gradle.org/plugin/nebula.ivy-resolved-dependencies[Nebula Ivy Resolved Plugin]. Alternatively, you can link:{groovyDslPath}/org.gradle.api.publish.ivy.IvyModuleDescriptorSpec.html#org.gradle.api.publish.ivy.IvyModuleDescriptorSpec:withXml(org.gradle.api.Action)[customize the descriptor file] so that it contains the versions you want.
+You can replicate the default Ivy behavior by using the https://plugins.gradle.org/plugin/nebula.ivy-resolved-dependencies[Nebula Ivy Resolved Plugin].
+Alternatively, you can link:{groovyDslPath}/org.gradle.api.publish.ivy.IvyModuleDescriptorSpec.html#org.gradle.api.publish.ivy.IvyModuleDescriptorSpec:withXml(org.gradle.api.Action)[customize the descriptor file] so that it contains the versions you want.
 ====
 
 [[migant:custom_tasks]]
 == Dealing with custom Ant tasks
 
-One of the advantages of Ant is that it's fairly easy to create a custom task and incorporate it into a build. If you have such tasks, then there are two main options for migrating them to a Gradle build:
+One of the advantages of Ant is that it's fairly easy to create a custom task and incorporate it into a build.
+If you have such tasks, then there are two main options for migrating them to a Gradle build:
 
  * <<ant#sec:using_custom_ant_tasks,Using the custom Ant task>> from the Gradle build
  * Rewriting the task as a <<custom_tasks#custom_tasks,custom Gradle task type>>
 
-The first option is usually quick and easy, but not always. And if you want to integrate the task into incremental build, you must use the <<incremental_build#sec:task_input_output_runtime_api,incremental build runtime API>>. You also often have to work with Ant paths and filesets, which are clunky.
+The first option is typically quick and easy.
+If you want to integrate the task into incremental build, you must use the <<incremental_build#sec:task_input_output_runtime_api,incremental build runtime API>>.
+You also often have to work with Ant paths and filesets, which can be inconvenient.
 
-The second option is preferable in the long term, if you have the time. Gradle task types tend to be simpler than Ant tasks because they don't have to work with an XML-based interface. You also gain access to Gradle's rich APIs. Lastly, this approach can make use of the <<incremental_build#sec:task_input_output_annotations,type-safe incremental build API>> based on typed properties.
+The second option is preferable long term.
+Gradle task types tend to be simpler than Ant tasks because they don't have to work with an XML-based interface.
+You also gain the benefits of Gradle's rich APIs.
+This approach enables the <<incremental_build#sec:task_input_output_annotations,type-safe incremental build API>> based on typed properties.
 
 [[migant:working_with_files]]
 == Working with files
 
-Ant has many tasks for working with files, most of which have Gradle equivalents. As with other areas of Ant to Gradle migration, you can <<ant#sec:using_ant_tasks,use those Ant tasks>> from within your Gradle build. However, we strongly recommend migrating to native Gradle constructs where possible so that the build benefits from:
+Ant has many tasks for working with files, most of which have Gradle equivalents.
+As with other areas of the Ant to Gradle migration, you can <<ant#sec:using_ant_tasks,use those Ant tasks>> from within your Gradle build.
+However, we strongly recommend migrating to native Gradle constructs where possible so that the build benefits from:
 
  * <<incremental_build#incremental_build,Incremental build>>
  * Easier integration with other parts of the build, such as dependency configurations
  * More idiomatic build scripts
 
-That said, it can be convenient to use those Ant tasks that have no direct equivalents, such as `<checksum>` and `<chown>`. Even then, in the long run it may be better to convert these to native Gradle task types that make use of standard Java APIs or third-party libraries to achieve the same thing.
+It can be convenient to use Ant tasks that have no direct equivalents, such as `<checksum>` and `<chown>`.
+However, in the long term, it may be better to convert these to native Gradle task types that make use of standard Java APIs or third-party libraries.
 
 Here are the most common file-related elements used by Ant builds, along with the Gradle equivalents:
 
@@ -342,30 +417,40 @@
 .On paths and filesets
 [NOTE]
 ====
-Ant makes use of the concepts of path-like structures and filesets to enable users to work with collections of files and directories. Gradle has a simpler, more powerful model based on link:{javadocPath}/org/gradle/api/file/FileCollection.html[FileCollection]s and link:{javadocPath}/org/gradle/api/file/FileTree.html[FileTree]s that can be treated as objects from within the build. Both types allow filtering based on Ant's glob syntax, e.g. `++**/books_*++`. Learn more about these types and other aspects of Gradle's file API in the <<working_with_files#working_with_files,Working with Files>> chapter.
+Ant makes use of the concepts of path-like structures and filesets to enable users to work with collections of files and directories.
+Gradle has a simpler, more powerful model based on link:{javadocPath}/org/gradle/api/file/FileCollection.html[FileCollection]s and link:{javadocPath}/org/gradle/api/file/FileTree.html[FileTree]s that can be treated as objects from within the build.
+Both types allow filtering based on Ant's glob syntax, e.g. `++**/books_*++`.
+You can learn more about these types and other aspects of Gradle's file API in the <<working_with_files#working_with_files,Working with Files>> chapter.
 
-You can still construct Ant paths and filesets from within your build via the `ant` object if you need to interact with an Ant task that requires them. The chapter on <<ant#ant,Ant integration>> has examples that use both `<path>` and `<fileset>`. There is even link:{javadocPath}/org/gradle/api/file/FileCollection.html#addToAntBuilder-java.lang.Object-java.lang.String-org.gradle.api.file.FileCollection.AntType-[a method on `FileCollection`] that will convert a file collection to a fileset or similar Ant type.
+You can construct Ant paths and filesets from within your build via the `ant` object if you need to interact with an Ant task that requires them.
+The chapter on <<ant#ant,Ant integration>> has examples that use both `<path>` and `<fileset>`.
+There is also link:{javadocPath}/org/gradle/api/file/FileCollection.html#addToAntBuilder-java.lang.Object-java.lang.String-org.gradle.api.file.FileCollection.AntType-[a method on `FileCollection`] that will convert a file collection to a fileset or similar Ant type.
 ====
 
 [[migant:properties]]
 == Migrating Ant properties
 
-Ant makes use of a properties map to store values that can be reused throughout the build. The big downsides to this approach are that property values are all strings and the properties themselves behave like global variables.
+Ant makes use of a properties map to store values that can be reused throughout the build.
+The big downsides to this approach are that property values are all strings and the properties themselves behave like global variables.
 
 .Interacting with Ant properties in Gradle
 [TIP]
 ====
-Sometimes you will want to make use of an Ant task directly from your Gradle build and that task requires one or more Ant properties to be set. If that's the case, you can easily set those properties via the `ant` object, as described in the <<ant#sec:ant_properties,Using Ant from Gradle>> chapter.
+Sometimes you will want to make use of an Ant task directly from your Gradle build and that task requires one or more Ant properties to be set.
+If that's the case, you can easily set those properties via the `ant` object, as described in the <<ant#sec:ant_properties,Using Ant from Gradle>> chapter.
 ====
 
-Gradle does use something similar in the form of <<build_environment#sec:project_properties,project properties>>, which are a reasonable way to parameterize a build. These can be set from the command line, in a <<build_environment#sec:gradle_configuration_properties,`gradle.properties` file>>, or even via specially named system properties and environment variables.
+Gradle does use something similar in the form of <<build_environment#sec:project_properties,project properties>>, which are a reasonable way to parameterize a build.
+These can be set from the command line, in the <<build_environment#sec:gradle_configuration_properties,`gradle.properties` file>>, or via specially named system properties and environment variables.
 
-If you have existing Ant properties files, you can copy their contents into the project's `gradle.properties` file. Just be aware of two important points:
+If you have existing Ant properties files, you can copy their contents into the project's `gradle.properties` file.
+Just be aware that:
 
  * Properties set in `gradle.properties` **do not** override <<writing_build_scripts#sec:extra_properties,extra project properties>> defined in the build script with the same name
  * Imported Ant tasks will not automatically "see" the Gradle project properties — you must copy them into the Ant properties map for that to happen
 
-Another important factor to understand is that a Gradle build script works with an object-oriented API and it's often best to use the properties of tasks, source sets and other objects where possible. For example, this build script fragment creates tasks for packaging Javadoc documentation as a JAR and unpacking it, linking tasks via their properties:
+Another important factor to understand is that a Gradle build script works with an object-oriented API and it's often best to use the properties of tasks, source sets, and other objects where possible.
+For example, this build script fragment creates tasks for packaging Javadoc documentation as a JAR and unpacking it, linking tasks via their properties:
 
 .Using task properties in place of project properties
 ====
@@ -376,16 +461,17 @@
 <2> Uses the location of the Javadoc JAR held by the `javadocJar` task
 <3> Uses an project property called `tmpDistDir` to define the location of the 'dist' directory
 
-As you can see from the example with `tmpDistDir`, there is often still a need to define paths and the like through properties, which is why Gradle also provides <<writing_build_scripts#sec:extra_properties,extra properties>> that can be attached to the project, tasks and some other types of objects.
+As you can see from the example with `tmpDistDir`, there is often a need to define paths through properties, which is why Gradle also provides <<writing_build_scripts#sec:extra_properties,extra properties>> that can be attached to the project, tasks, and some other types of objects.
 
 [[migant:multi_project_builds]]
 == Migrating multi-project builds
 
-Multi-project builds are a particular challenge to migrate because there is no standard approach in Ant for either structuring them or handling inter-project dependencies. Most of them likely use the `<ant>` task in some way, but that's about all that one can say.
+Multi-project builds are a particular challenge to migrate because there is no standard approach in Ant for structuring them or handling inter-project dependencies.
 
-Fortunately, Gradle's multi-project support can handle fairly diverse project structures and it provides much more robust and helpful support than Ant for constructing and maintaining multi-project builds. The `ant.importBuild()` method also handles `<ant>` and `<antcall>` tasks transparently, which allows for a phased migration.
+Fortunately, Gradle's multi-project support can handle fairly diverse project structures and it provides much more robust and helpful support than Ant for constructing and maintaining multi-project builds.
+The `ant.importBuild()` method also handles `<ant>` and `<antcall>` tasks transparently, which allows for a phased migration.
 
-We will suggest one process for migration here and hope that it either works for your case or at least gives you some ideas. It breaks down like this:
+The following steps highlight a suggested method for migrating a multi-project build:
 
  1. Start by learning <<multi_project_builds#multi_project_builds,how Gradle configures multi-project builds>>.
  2. Create a Gradle build script in each project of the build, setting their contents to this line:
@@ -400,47 +486,57 @@
 ant.importBuild("build.xml")
 ----
 +
-Replace `build.xml` with the path to the actual Ant build file that corresponds to the project. If there is no corresponding Ant build file, leave the Gradle build script empty. Your build may not be suitable in that case for this migration approach, but continue with these steps to see whether there is still a way to do a phased migration.
+Replace `build.xml` with the path to the actual Ant build file that corresponds to the project.
+If there is no corresponding Ant build file, leave the Gradle build script empty.
+Even if your build is not be suitable for this migration approach, continue with these steps to see if there is still a way to do a phased migration.
  3. Create a settings file that link:{groovyDslPath}/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String++[]++)[includes all the projects] that now have a Gradle build script.
  4. Implement inter-project dependencies.
 +
-Some projects in your multi-project build will depend on artifacts produced by one or more other projects in that build. Such projects need to ensure that those projects they depend on have produced their artifacts and that they know the paths to those artifacts.
+Some projects in your multi-project build will depend on artifacts produced by one or more other projects in that build.
+Such projects need to ensure that the projects they depend on have produced their artifacts and that the paths to those artifacts are known.
 +
-Ensuring the production of the required artifacts typically means calling into other projects' builds via the `<ant>` task. This unfortunately bypasses the Gradle build, negating any changes you make to the Gradle build scripts. You will need to replace targets that use `<ant>` tasks with Gradle <<more_about_tasks#sec:adding_dependencies_to_tasks,task dependencies>>.
+Ensuring the production of the required artifacts typically means calling into other projects' builds via the `<ant>` task.
+This unfortunately bypasses the Gradle build, negating any changes you make to the Gradle build scripts.
+You will need to replace targets that use `<ant>` tasks with Gradle <<more_about_tasks#sec:adding_dependencies_to_tasks,task dependencies>>.
 +
-For example, imagine you have a web project that depends on a "util" library that's part of the same build. The Ant build file for "web" might have a target like this:
+For example, your web project depends on a "util" library that's part of the same build. The Ant build file for "web" might have a target like this:
 +
 ====
 include::sample[dir="snippets/antMigration/multiProject/groovy",files="web/build.xml[tags=build-required]"]
 ====
 <1> `root.dir` would have to be defined by the build
 +
-This can be replaced by an inter-project task dependency in the corresponding Gradle build script, as demonstrated in the following example that assumes the "web" project's "compile" task is the thing that requires "util" to be built beforehand:
+This can be replaced by an inter-project task dependency in the corresponding Gradle build script, as demonstrated in the following example that assumes the "web" project's "compile" task is requires "util" to be built beforehand:
 +
 ====
 include::sample[dir="snippets/antMigration/multiProject/kotlin",files="web/build.gradle.kts[]"]
 include::sample[dir="snippets/antMigration/multiProject/groovy",files="web/build.gradle[]"]
 ====
 +
-This is not as robust or powerful as Gradle's <<declaring_dependencies_between_subprojects#sec:project_jar_dependencies,project dependencies>>, but it solves the immediate problem without big changes to the build. Just be careful to remove or override any dependencies on tasks that delegate to other subprojects, like the `buildRequiredProjects` task.
+This is not as robust or powerful as Gradle's <<declaring_dependencies_between_subprojects#sec:project_jar_dependencies,project dependencies>>, but it solves the immediate problem without big changes to the build.
+Just be careful to remove or override any dependencies on tasks that delegate to other subprojects, like the `buildRequiredProjects` task.
  5. Identify the projects that have no dependencies on other projects and migrate them to idiomatic Gradle builds scripts.
 +
-Just follow the advice in the rest of this guide to migrate individual project builds. As mentioned elsewhere, you should ideally use Gradle standard plugins where possible. This may mean that you need to add an extra copy task to each build that copies the generated artifacts to the location expected by the rest of the Ant builds.
- 6. Migrate projects as and when they depend solely on projects with fully migrated Gradle builds.
+Follow the advice in the rest of this guide to migrate individual project builds.
+As mentioned, you should use Gradle standard plugins where possible.
+This may mean that you need to add an extra copy task to each build that copies the generated artifacts to the location expected by the rest of the Ant builds.
+ 6. Migrate projects when they depend solely on projects with fully migrated Gradle builds.
 +
 At this point, you should be able to switch to using proper project dependencies attached to the appropriate dependency configurations.
  7. Clean up projects once no part of the Ant build depends on them.
 +
-We mentioned in step 5 that you might need to add copy tasks to satisfy the requirements of dependent Ant builds. Once those builds have been migrated, such build logic will no longer be needed and should be removed.
+We mentioned in step 5 that you might need to add copy tasks to satisfy the requirements of dependent Ant builds.
+Once those builds have been migrated, such build logic will no longer be needed and should be removed.
 
 At the end of the process you should have a Gradle build that you are confident works as it should, with much less build logic than before.
 
 == Further reading
 
-This chapter has covered the major topics that are specific to migrating Ant builds to Gradle. All that remain are a few other areas that may be useful during or after a migration:
+This chapter has covered the major topics that are specific to migrating Ant builds to Gradle.
+All that remain are a few other areas that may be useful following a migration:
 
  * Learn how to configure Gradle's <<build_environment#build_environment,build environment>>, including the JVM settings used to run it
  * Learn how to <<organizing_gradle_projects#organizing_gradle_projects,structure your builds effectively>>
  * <<logging#logging,Configure Gradle's logging>> and use it from your builds
 
-As a final note, this guide has only touched on a few of Gradle's features and we encourage you to learn about the rest from the other chapters of the user manual and from our link:../samples/index.html[step-by-step samples].
+As a final note, this guide has only touched on a few of Gradle's features and we encourage you to learn about the rest from the other chapters of the User Manual.
diff --git a/subprojects/docs/src/docs/userguide/migration/migrating_from_maven.adoc b/subprojects/docs/src/docs/userguide/migration/migrating_from_maven.adoc
index 3177f11..df20ccf 100644
--- a/subprojects/docs/src/docs/userguide/migration/migrating_from_maven.adoc
+++ b/subprojects/docs/src/docs/userguide/migration/migrating_from_maven.adoc
@@ -15,18 +15,18 @@
 [[migrating_from_maven]]
 = Migrating Builds From Apache Maven
 
-TIP: Suffering from slow Maven builds? https://gradle.org/training/#build-cache-deep-dive[Register here] for our Build Cache training session to learn how Gradle Enterprise can speed up Maven builds by up to 90%.
+https://maven.apache.org[Apache Maven] is a build tool for Java and other JVM-based projects. It is typical to migrate an existing Maven build to Gradle.
 
-https://maven.apache.org[Apache Maven] is a build tool for Java and other JVM-based projects that's in widespread use, and so people that want to use Gradle often have to migrate an existing Maven build.
-This guide will help with such a migration by explaining the differences and similarities between the two tools' models and providing steps that you can follow to ease the process.
+This guide will help with such a migration by explaining the differences and similarities between the two tools and providing steps that you can follow to ease the process.
 
 Converting a build can be scary, but you don't have to do it alone.
-You can search docs, forums, and StackOverflow from link:https://gradle.org/help[help.gradle.org] or reach out to the link:https://discuss.gradle.org/c/help-discuss[Gradle community on the forums] if you get stuck.
+You can search our https://docs.gradle.org/[documentation], post on our https://discuss.gradle.org/[community forums], or reach out on our https://gradle.org/slack-invite[Slack channel] if you get stuck.
 
 [[migmvn:making_a_case]]
 == Making a case for migration
 
 The primary differences between Gradle and Maven are flexibility, performance, user experience, and dependency management.
+
 A visual overview of these aspects is available in the link:https://gradle.org/maven-vs-gradle[Maven vs Gradle feature comparison].
 
 Since Gradle 3.0, Gradle has invested heavily in making Gradle builds much faster, with features such as link:https://blog.gradle.org/introducing-gradle-build-cache[build caching], link:https://blog.gradle.org/incremental-compiler-avoidance[compile avoidance], and an improved incremental Java compiler.
@@ -44,62 +44,68 @@
 Here we lay out a series of steps for you to follow that will help facilitate the migration of any Maven build to Gradle:
 
 TIP: Keep the old Maven build and new Gradle build side by side.
-You know the Maven build works, so you should keep it until you are confident that the Gradle build produces all the same artifacts and otherwise does what you need.
-This also means that users can try the Gradle build without getting a new copy of the source tree.
+You know the Maven build works, so you should keep it until you are confident that the Gradle build produces all the same artifacts.
+This also means that users can try the Gradle build without creating a new copy of the source tree.
 
- . link:https://scans.gradle.com#maven[Create a build scan for the Maven build].
+ . link:https://scans.gradle.com[Create a build scan for the Maven build].
 +
 A build scan will make it easier to visualize what's happening in your existing Maven build.
-For Maven builds, you'll be able to see the project structure, what plugins are being used, a timeline of the build steps, and more.
-Keep this handy so you can compare it to the Gradle build scans you get while converting the project.
+For Maven builds, you will be able to see the project structure, what plugins are being used, a timeline of the build steps, and more.
+Keep this handy so you can compare it to the Gradle build scans while converting the project.
 +
-. Develop a mechanism to verify that the two builds produce the same artifacts
+. Develop a mechanism to verify that the two builds produce the same artifacts.
 +
 This is a vitally important step to ensure that your deployments and tests don't break.
 Even small changes, such as the contents of a manifest file in a JAR, can cause problems.
-If your Gradle build produces the same output as the Maven build, this will give you and others confidence in switching over and make it easier to implement the big changes that will provide the greatest benefits.
+If your Gradle build produces the same output as the Maven build, this will give you confidence in switching over and make it easier to implement the changes that will provide the greatest benefits.
 +
 This doesn't mean that you need to verify every artifact at every stage, although doing so can help you quickly identify the source of a problem.
-You can just focus on the critical output such as final reports and the artifacts that are published or deployed.
+You should focus on the critical output such as final reports and the artifacts that are published or deployed.
 +
 You will need to factor in some inherent differences in the build output that Gradle produces compared to Maven.
 Generated POMs will contain only the information needed for consumption and they will use `<compile>` and `<runtime>` scopes correctly for that scenario.
 You might also see differences in the order of files in archives and of files on classpaths.
-Most differences will be benign, but it's worth identifying them and verifying that they are OK.
- . <<migmvn:automatic_conversion,Run an automatic conversion>>
+Most differences will be minor, but it's worth identifying them and verifying that they are acceptable.
++
+ . <<migmvn:automatic_conversion,Run an automatic conversion>>.
 +
 This will create all the Gradle build files you need, even for <<migmvn:multimodule_builds,multi-module builds>>.
 For simpler Maven projects, the Gradle build will be ready to run!
++
  . link:https://scans.gradle.com[Create a build scan for the Gradle build].
 +
 A build scan will make it easier to visualize what's happening in the build.
 For Gradle builds, you'll be able to see the project structure, the dependencies (regular and inter-project ones), what plugins are being used and the console output of the build.
 +
-Your build may fail at this point, but that's ok; the scan will still run. Compare the build scan for the Gradle build to the one for the Maven build and continue down this list to troubleshoot the failures.
+Your build may fail at this point, but that's ok; the scan will still run.
+Compare the build scan for the Gradle build to the one for the Maven build and continue down this list to troubleshoot the failures.
 +
 We recommend that you regularly generate build scans during the migration to help you identify and troubleshoot problems.
-If you want, you can also use a Gradle build scan to identify opportunities to <<performance.adoc#performance_gradle,improve the performance of the build>>, after all performance is a big reason for switching to Gradle in the first place.
- . <<migmvn:migrating_deps,Verify your dependencies and fix any problems>>
- . <<migmvn:integration_tests,Configure integration and functional tests>>
+If you want, you can also use a Gradle build scan to identify opportunities to <<performance.adoc#performance_gradle,improve the performance of the build>>.
++
+ . <<migmvn:migrating_deps,Verify your dependencies and fix any problems>>.
++
+ . <<migmvn:integration_tests,Configure integration and functional tests>>.
 +
 Many tests can simply be migrated by configuring an extra source set.
 If you are using a third-party library, such as http://docs.fitnesse.org/FrontPage[FitNesse], look to see whether  there is a suitable community plugin available on the https://plugins.gradle.org/[Gradle Plugin Portal].
- . Replace Maven plugins with Gradle equivalents
++
+ . Replace Maven plugins with Gradle equivalents.
 +
 In the case of <<migmvn:common_plugins,popular plugins>>, Gradle often has an equivalent plugin that you can use.
 You might also find that you can <<migmvn:unnecessary_plugins,replace a plugin with built-in Gradle functionality>>.
 As a last resort, you may need to reimplement a Maven plugin <<migmvn:custom_plugins,via your own custom plugins and task types>>.
-
++
 The rest of this chapter looks in more detail at specific aspects of migrating a build from Maven to Gradle.
 
 [[migmvn:build_lifecycle]]
 == Understanding the build lifecycle
 
 Maven builds are based around the concept of https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html[_build lifecycles_] that consist of a set of fixed phases.
-This can prove an impediment for users migrating to Gradle because its build lifecycle is <<build_lifecycle#build_lifecycle,something different>>, although it's important to understand how Gradle builds fit into the structure of initialization, configuration, and execution phases.
-Fortunately, Gradle has a feature that can mimic Maven's phases: <<more_about_tasks#sec:lifecycle_tasks,_lifecycle tasks_>>.
+This can be a challenge for users migrating to Gradle because the build lifecycle is <<build_lifecycle#build_lifecycle,a new concept>>.
+Although it's important to understand how Gradle builds fit into the structure of *initialization*, *configuration*, and *execution* phases, Gradle provides a helper feature that can mimic Maven's phases: <<more_about_tasks#sec:lifecycle_tasks,_lifecycle tasks_>>.
 
-These allow you to define your own "lifecycles" by creating no-action tasks that simply depend on the tasks you're interested in.
+This feature allow you to define your own "lifecycles" by creating no-action tasks that simply depend on the tasks you're interested in.
 And to make the transition to Gradle easier for Maven users, the <<base_plugin#sec:base_tasks,Base Plugin>> -- applied by all the JVM language plugins like the <<java_library_plugin#java_library_plugin,Java Library Plugin>> -- provides a set of lifecycle tasks that correspond to the main Maven phases.
 
 Here is a list of some of the main Maven phases and the Gradle tasks that they map to:
@@ -113,16 +119,16 @@
 
 `test`::
 Use the `test` task provided by the Java Plugin.
-It runs just the unit tests, or more specifically, the tests that make up the <<java_plugin.adoc#source_sets,`test` source set>>.
+It runs the unit tests, and more specifically, the tests that make up the <<java_plugin.adoc#source_sets,`test` source set>>.
 
 `package`::
 Use the `assemble` task provided by the Base Plugin.
-This builds whatever is the appropriate package for the project, for example a JAR for Java libraries or a WAR for traditional Java webapps.
+This builds whatever is the appropriate package for the project; for example, a JAR for Java libraries or a WAR for traditional Java webapps.
 
 `verify`::
 Use the `check` task provided by the Base Plugin.
 This runs all verification tasks that are attached to it, which typically includes the unit tests, any static analysis tasks -- such as <<checkstyle_plugin#checkstyle_plugin,Checkstyle>> -- and others.
-If you want to include integration tests, you will have to <<migmvn:integration_tests,configure these manually>>, which is a simple process.
+If you want to include integration tests, you will have to <<migmvn:integration_tests,configure these manually>>.
 
 `install`::
 Use the `publishToMavenLocal` task provided by the <<publishing_maven#publishing_maven:tasks,Maven Publish Plugin>>.
@@ -135,7 +141,7 @@
 `deploy`::
 Use the `publish` task provided by the <<publishing_maven#publishing_maven:tasks,Maven Publish Plugin>> -- making sure you switch from the older Maven Plugin (ID: `maven`) if your build is using that one.
 This will publish your package to all configured publication repositories.
-There are also other tasks that allow you to publish to a single repository even when multiple ones are defined.
+There are also tasks that allow you to publish to a single repository even when multiple ones are defined.
 +
 Note that the Maven Publish Plugin does not publish *source and Javadoc JARs* _by default_, but this can easily be activated as explained in <<building_java_projects.adoc#sec:java_packaging,the guide for building java projects>>.
 
@@ -150,8 +156,8 @@
 > gradle init
 ----
 
-from the root project directory and let Gradle do its thing.
-That basically consists of parsing the existing POMs and generating the corresponding Gradle build scripts.
+from the root project directory.
+This consists of parsing the existing POMs and generating the corresponding Gradle build scripts.
 Gradle will also create a settings script if you're migrating a <<multi_project_builds#multi_project_builds,multi-project build>>.
 
 You'll find that the new Gradle build includes the following:
@@ -162,8 +168,8 @@
 
 See the <<build_init_plugin#sec:pom_maven_conversion,Build Init Plugin chapter>> for a complete list of the automatic conversion features.
 
-One thing to bear in mind is that assemblies are not automatically converted.
-They aren't necessarily problematic to convert, but you will need to do some manual work.
+One thing to keep in mind is that assemblies are not automatically converted.
+This additional conversion will required some manual work.
 Options include:
 
  * Using the <<distribution_plugin#distribution_plugin,Distribution Plugin>>
@@ -172,7 +178,7 @@
  * <<working_with_files#sec:creating_archives_example,Creating custom archive tasks>>
  * Using a suitable community plugin from the https://plugins.gradle.org/[Gradle Plugin Portal]
 
-If your Maven build does not have many plugins or much in the way of customisation, you can simply run
+If your Maven build does not have many plugins or custom steps, you can simply run
 
 [listing.terminal]
 ----
@@ -180,13 +186,13 @@
 ----
 
 once the migration has completed.
-This will run the tests and produce the required artifacts without any extra intervention on your part.
+This will run the tests and produce the required artifacts automatically.
 
 [[migmvn:migrating_deps]]
 == Migrating dependencies
 
 Gradle's dependency management system is more flexible than Maven's, but it still supports the same concepts of repositories, declared dependencies, scopes (<<declaring_dependencies.adoc#sec:what-are-dependency-configurations,dependency configurations>> in Gradle), and transitive dependencies.
-In fact, Gradle works perfectly with Maven-compatible repositories, which makes it easy to migrate your dependencies.
+In fact, Gradle works with Maven-compatible repositories which makes it easy to migrate your dependencies.
 
 NOTE: One notable difference between the two tools is in how they manage version conflicts. Maven uses a "closest" match algorithm, whereas Gradle picks the newest.
 Don't worry though, you have a lot of control over which versions are selected, as documented in <<dependency_constraints.adoc#dependency-constraints,Managing Transitive Dependencies>>.
@@ -198,7 +204,7 @@
 
 Gradle uses the same dependency identifier components as Maven: group ID, artifact ID and version.
 It also supports classifiers.
-So all you need to do is substitute the identifier information for a dependency into Gradle's syntax, which is described in the <<declaring_dependencies#declaring-dependencies,Declaring Dependencies>> chapter.
+All you need to do is substitute the identifier information for a dependency into Gradle's syntax, which is described in the <<declaring_dependencies#declaring-dependencies,Declaring Dependencies>> chapter.
 
 For example, consider this Maven-style dependency on Log4J:
 
@@ -433,9 +439,9 @@
 Always.
 Fortunately, you can mimic the concept of profiles to provide overridable default values.
 
-Which brings us on to Maven profiles.
+Which brings us to Maven profiles.
 These are a way to enable and disable different configurations based on environment, target platform, or any other similar factor.
-Logically, they are nothing more than limited ‘if' statements.
+Logically, they are nothing more than limited `if` statements.
 And since Gradle has much more powerful ways to declare conditions, it does not need to have formal support for profiles (except in the POMs of dependencies).
 You can easily get the same behavior by combining conditions with secondary build scripts, as you'll see.
 
@@ -513,7 +519,7 @@
 
 Source sets also give you a lot of flexibility on where you place the source files for your integration tests.
 You can easily keep them in the same directory as the unit tests or, more preferably, in a separate source directory like `src/integTest/java`.
-To support other types of tests, you just add more source sets and link:{groovyDslPath}/org.gradle.api.tasks.testing.Test.html[Test] tasks!
+To support other types of tests, simple add more source sets and link:{groovyDslPath}/org.gradle.api.tasks.testing.Test.html[Test] tasks.
 
 [[migmvn:common_plugins]]
 == Migrating common plugins
@@ -527,7 +533,7 @@
 * JaCoCo
 * AntRun (see further down)
 
-Why does this matter? Because many plugins rely on standard Java conventions, so migration is just a matter of replicating the configuration of the Maven plugin in Gradle.
+Why does this matter? Because many plugins rely on standard Java conventions, migration is just a matter of replicating the configuration of the Maven plugin in Gradle.
 As an example, here's a simple Maven Checkstyle plugin configuration:
 
 [source,xml]
@@ -558,7 +564,7 @@
 ----
 
 Everything outside of the configuration block can safely be ignored when migrating to Gradle.
-In this case, the corresponding Gradle configuration looks like the following:
+In this case, the corresponding Gradle configuration is as follows:
 
 .Configuring the Gradle Checkstyle Plugin
 ====
@@ -567,7 +573,8 @@
 ====
 
 The Checkstyle tasks are automatically added as dependencies of the `check` task, which also includes `test`.
-If you want to ensure that Checkstyle runs before the tests, then just specify an ordering with the mustRunAfter() method:
+If you want to ensure that Checkstyle runs before the tests, then just specify an ordering with the
+link:{groovyDslPath}/org.gradle.api.Task.html#org.gradle.api.Task:mustRunAfter(java.lang.Object++[]++)[mustRunAfter(...)] method:
 
 .Controlling when the `checkstyle` task runs
 ====
@@ -599,7 +606,8 @@
 
 [TIP]
 ====
-It may be simpler and cleaner to just <<custom_tasks#custom_tasks,create custom task types>> to replace the work that Ant is doing for you. You can then more readily benefit from <<incremental_build.adoc#incremental_build,incremental build>> and other useful Gradle features.
+It may be simpler and cleaner to just <<custom_tasks#custom_tasks,create custom task types>> to replace the work that Ant is doing for you.
+You can then more readily benefit from <<incremental_build.adoc#incremental_build,incremental build>> and other useful Gradle features.
 ====
 
 
@@ -628,7 +636,7 @@
 If a plugin depends on the Maven project, then you will have to rewrite it.
 Don't start by considering how the Maven plugin works, but look at what problem it is trying to solve.
 Then try to work out how to solve that problem in Gradle.
-You'll probably find that the two build models are different enough that "transcribing" Maven plugin code into a Gradle plugin just won't be effective.
+You will probably find that the two build models are different enough that "transcribing" Maven plugin code into a Gradle plugin just won't be effective.
 On the plus side, the plugin is likely to be much easier to write than the original Maven one because Gradle has a much richer build model and API.
 
 If you do need to implement custom logic, either via build scripts or plugins, check out the https://gradle.org/guides/?q=Plugin%20Development[Guides related to plugin development].
diff --git a/subprojects/docs/src/docs/userguide/migration/upgrading_version_4.adoc b/subprojects/docs/src/docs/userguide/migration/upgrading_version_4.adoc
index 0d02b58..051430f 100644
--- a/subprojects/docs/src/docs/userguide/migration/upgrading_version_4.adoc
+++ b/subprojects/docs/src/docs/userguide/migration/upgrading_version_4.adoc
@@ -34,7 +34,7 @@
 This is so that you can see any deprecation warnings that apply to your build.
 Gradle 5.x will generate (potentially less obvious) errors if you try to upgrade directly to it.
 +
-Alternatively, you could run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
+Alternatively, you can run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
 . Update your plugins.
 +
 Some plugins will break with this new version of Gradle, for example because they use internal APIs that have been removed or changed.
@@ -219,7 +219,8 @@
  * <<#rel4.10:aws_s3_permissions,Publishing to AWS S3 requires new permissions>>.
  * Both link:{javadocPath}/org/gradle/plugin/devel/tasks/PluginUnderTestMetadata.html[PluginUnderTestMetadata] and link:{javadocPath}/org/gradle/plugin/devel/tasks/GeneratePluginDescriptors.html[GeneratePluginDescriptors] — classes used by the <<java_gradle_plugin#java_gradle_plugin,Java Gradle Plugin Development Plugin>> — have been updated to use the Provider API.
 +
-Use the link:{javadocPath}/org/gradle/api/provider/Property.html#set-T-[Property.set()] method to modify their values rather than using standard property assignment syntax, unless you are doing so in a Groovy build script. Standard property assignment still works in that one case.
+Use the link:{javadocPath}/org/gradle/api/provider/Property.html#set-T-[Property.set()] method to modify their values rather than using standard property assignment syntax, unless you are doing so in a Groovy build script.
+Standard property assignment still works in that one case.
 
 [[changes_4.9]]
 == Upgrading from 4.8 and earlier
@@ -230,7 +231,8 @@
 
  * You can no longer use GPath syntax with link:{javadocPath}/org/gradle/api/tasks/TaskCollection.html#withType-java.lang.Class-[tasks.withType()].
 +
-Use https://docs.groovy-lang.org/latest/html/documentation/#_spread_operator[Groovy's spread operator] instead. For example, you would replace `tasks.withType(JavaCompile).name` with `tasks.withType(JavaCompile)*.name`.
+Use https://docs.groovy-lang.org/latest/html/documentation/#_spread_operator[Groovy's spread operator] instead.
+For example, you would replace `tasks.withType(JavaCompile).name` with `tasks.withType(JavaCompile)*.name`.
 
 [[changes_4.8]]
 == Upgrading from 4.7 and earlier
@@ -248,7 +250,8 @@
  * <<#rel4.8:pom_wildcard_exclusions,Gradle now honors implicit wildcards in Maven POM exclusions>>.
  * The Kotlin DSL now respects JSR-305 package annotations.
 +
-This will lead to some types annotated according to JSR-305 being treated as nullable where they were treated as non-nullable before. This may lead to compilation errors in the build script. See https://github.com/gradle/kotlin-dsl/releases/tag/v0.17.4[the relevant Kotlin DSL release notes] for details.
+This will lead to some types annotated according to JSR-305 being treated as nullable where they were treated as non-nullable before.
+This may lead to compilation errors in the build script. See https://github.com/gradle/kotlin-dsl/releases/tag/v0.17.4[the relevant Kotlin DSL release notes] for details.
  * Error messages will be directed to standard error rather than standard output now, unless a console is attached to both standard output and standard error. This may affect tools that scrape a build's plain console output. Ignore this change if you're upgrading from an earlier version of Gradle.
 
 [[deprecations_4.8]]
@@ -283,7 +286,8 @@
 [[rel4.6:annotation_processor_configuration]]
  * You should not put annotation processors on the compile classpath or declare them with the `-processorpath` compiler argument.
 +
-They should be added to the `annotationProcessor` configuration instead. If you don't want any processing, but your compile classpath contains a processor unintentionally (e.g. as part of a library you depend on), use the `-proc:none` compiler argument to ignore it.
+They should be added to the `annotationProcessor` configuration instead.
+If you don't want any processing, but your compile classpath contains a processor unintentionally (e.g. as part of a library you depend on), use the `-proc:none` compiler argument to ignore it.
  * Use link:{javadocPath}/org/gradle/process/CommandLineArgumentProvider.html[CommandLineArgumentProvider] in place of link:https://docs.gradle.org/4.10.3/javadoc/org/gradle/api/tasks/compile/CompilerArgumentProvider.html[CompilerArgumentProvider].
 
 === Potential breaking changes
@@ -379,7 +383,8 @@
 
  * Non-Java projects that have a <<declaring_dependencies.adoc#sub:project_dependencies,project dependency>> on a Java project now consume the `runtimeElements` configuration by default instead of the `default` configuration.
 +
-To override this behavior, you can explicitly declare the configuration to use in the project dependency. For example: `project(path: ':myJavaProject', configuration: 'default')`.
+To override this behavior, you can explicitly declare the configuration to use in the project dependency.
+For example: `project(path: ':myJavaProject', configuration: 'default')`.
  * Default Zinc compiler upgraded from 0.3.13 to 0.3.15.
  * [Kotlin DSL] Base package renamed from `org.gradle.script.lang.kotlin` to `org.gradle.kotlin.dsl`.
 
@@ -461,16 +466,19 @@
  * The compilation classpath is much larger than it needs to be, slowing down compilation.
  * The compilation classpath includes runtime-scoped files that do not impact compilation, resulting in unnecessary re-compilation when those files change.
 
-With this new behavior, the Java and Java Library plugins both honor the <<java_library_plugin.adoc#sec:java_library_separation,separation of compile and runtime scopes>>. This means that the compilation classpath only includes compile-scoped dependencies, while the runtime classpath adds the runtime-scoped dependencies as well. This is particularly useful if you develop and publish Java libraries with Gradle where the separation between `api` and `implementation` dependencies is reflected in the published scopes.
+With this new behavior, the Java and Java Library plugins both honor the <<java_library_plugin.adoc#sec:java_library_separation,separation of compile and runtime scopes>>. This means that the compilation classpath only includes compile-scoped dependencies, while the runtime classpath adds the runtime-scoped dependencies as well.
+This is particularly useful if you develop and publish Java libraries with Gradle where the separation between `api` and `implementation` dependencies is reflected in the published scopes.
 
 [[rel5.0:changes_to_default_task]]
 === [5.0] Changes to property factory methods on `DefaultTask`
 
 ==== Property factory methods on `DefaultTask` are now final
-The property factory methods such as `newInputFile()` are intended to be called from the constructor of a type that extends `DefaultTask`. These methods are now final to avoid subclasses overriding these methods and using state that is not initialized.
+The property factory methods such as `newInputFile()` are intended to be called from the constructor of a type that extends `DefaultTask`.
+These methods are now final to avoid subclasses overriding these methods and using state that is not initialized.
 
 ==== Inputs and outputs are not automatically registered
-The Property instances that are returned by these methods are no longer automatically registered as inputs or outputs of the task. The Property instances need to be declared as inputs or outputs in the usual ways, such as attaching annotations such as `@OutputFile` or using the runtime API to register the property.
+The Property instances that are returned by these methods are no longer automatically registered as inputs or outputs of the task.
+The Property instances need to be declared as inputs or outputs in the usual ways, such as attaching annotations such as `@OutputFile` or using the runtime API to register the property.
 
 For example, you could previously use the following syntax and have both outputFile instances registered as declared outputs:
 
@@ -562,7 +570,8 @@
 
 With this new behavior, if a plugin or a transitive dependency of a plugin found in the `gradlePluginPortal()` repository has no Maven POM it will fail to resolve.
 
-Artifacts published to a Maven repository without a POM should be fixed. If you encounter such artifacts, please ask the plugin or library author to publish a new version with proper metadata.
+Artifacts published to a Maven repository without a POM should be fixed.
+If you encounter such artifacts, please ask the plugin or library author to publish a new version with proper metadata.
 
 If you are stuck with a bad plugin, you can work around by re-enabling JARs as metadata source for the `gradlePluginPortal()` repository:
 
@@ -642,7 +651,8 @@
 =====
 ====
 
-When converting this to use the new API, something surprising happens: `bar` doesn't exist.  The new API only executes configuration actions when necessary,
+When converting this to use the new API, something surprising happens: `bar` doesn't exist.
+The new API only executes configuration actions when necessary,
 so the `register()` for task `bar` only executes when `foo` is configured.
 
 ====
@@ -682,7 +692,9 @@
 [[rel4.10:aws_s3_permissions]]
 === [4.10] Publishing to AWS S3 requires new permissions
 
-The S3 repository transport protocol allows Gradle to publish artifacts to AWS S3 buckets. Starting with this release, every artifact uploaded to an S3 bucket will be equipped with the `bucket-owner-full-control` canned ACL. Make sure that the AWS account used to publish artifacts has the `s3:PutObjectAcl` and `s3:PutObjectVersionAcl` permissions, otherwise the upload will fail.
+The S3 repository transport protocol allows Gradle to publish artifacts to AWS S3 buckets.
+Starting with this release, every artifact uploaded to an S3 bucket will be equipped with the `bucket-owner-full-control` canned ACL.
+Make sure that the AWS account used to publish artifacts has the `s3:PutObjectAcl` and `s3:PutObjectVersionAcl` permissions, otherwise the upload will fail.
 
 [source,json]
 ----
@@ -708,14 +720,16 @@
 [[rel4.9:lazy_task_creation]]
 === [4.9] Consider trying the lazy API for task creation and configuration
 
-Gradle 4.9 introduced a new way to create and configure tasks that works lazily. When you use this approach for tasks that are expensive to configure, or when you have many, many tasks, your build configuration time can drop significantly when those tasks don't run.
+Gradle 4.9 introduced a new way to create and configure tasks that works lazily.
+When you use this approach for tasks that are expensive to configure, or when you have many, many tasks, your build configuration time can drop significantly when those tasks don't run.
 
 You can learn more about lazily creating tasks in the <<task_configuration_avoidance#task_configuration_avoidance,Task Configuration Avoidance>> chapter. You can also read about the background to this new feature in https://blog.gradle.org/preview-avoiding-task-configuration-time[this blog post].
 
 [[rel4.8:switch_to_publishing_plugins]]
 === [4.8] Switch to the Maven Publish and Ivy Publish Plugins
 
-Now that the publishing plugins are stable, we recommend that you migrate from the legacy publishing mechanism for standard Java projects, i.e. those based on the <<java_plugin#java_plugin,Java Plugin>>. That includes projects that use any one of: <<java_library_plugin#java_library_plugin,Java Library Plugin>>, <<application_plugin#application_plugin,Application Plugin>> or <<war_plugin#war_plugin,War Plugin>>.
+Now that the publishing plugins are stable, we recommend that you migrate from the legacy publishing mechanism for standard Java projects, i.e. those based on the <<java_plugin#java_plugin,Java Plugin>>.
+That includes projects that use any one of: <<java_library_plugin#java_library_plugin,Java Library Plugin>>, <<application_plugin#application_plugin,Application Plugin>> or <<war_plugin#war_plugin,War Plugin>>.
 
 To use the new approach, simply replace any `upload<Conf>` configuration with a `publishing {}` block. See the <<publishing_setup.adoc#publishing_overview,publishing overview chapter>> for more information.
 
@@ -747,7 +761,9 @@
 =====
 ====
 
-We recommend doing a test run with a local repository to see whether all artifacts still have the expected coordinates. In most cases everything should work as before and you are done. However, your publishing block may rely on the implicit deferred configuration, particularly if it relies on values that may change during the configuration phase of the build.
+We recommend doing a test run with a local repository to see whether all artifacts still have the expected coordinates.
+In most cases everything should work as before and you are done.
+However, your publishing block may rely on the implicit deferred configuration, particularly if it relies on values that may change during the configuration phase of the build.
 
 For example, under the new behavior, the following logic assumes that `jar.archiveBaseName` doesn't change after `artifactId` is set:
 
@@ -887,14 +903,16 @@
 [[rel4.8:pom_wildcard_exclusions]]
 === [4.8] Gradle now honors implicit wildcards in Maven POM exclusions
 
-If an exclusion in a Maven POM was missing either a `groupId` or `artifactId`, Gradle used to ignore the exclusion. Now the missing elements are treated as implicit wildcards — e.g. `<groupId>*</groupId>` — which means that some of your dependencies may now be excluded where they weren't before.
+If an exclusion in a Maven POM was missing either a `groupId` or `artifactId`, Gradle used to ignore the exclusion.
+Now the missing elements are treated as implicit wildcards — e.g. `<groupId>*</groupId>` — which means that some of your dependencies may now be excluded where they weren't before.
 
 You will need to explicitly declare any missing dependencies that you need.
 
 [[rel4.7:plain_console_output]]
 === [4.7] Changes to the structure of Gradle's plain console output
 
-The plain console mode now formats output consistently with the rich console, which means that the output format has changed. For example:
+The plain console mode now formats output consistently with the rich console, which means that the output format has changed.
+For example:
 
  * The output produced by a given task is now grouped together, even when other tasks execute in parallel with it.
  * Task execution headers are printed with a "> Task" prefix.
@@ -905,7 +923,8 @@
 [[rel:4.6:native_task_api_changes]]
 === [4.6] Changes to the APIs of native tasks related to compilation, linking and installation
 
-Many tasks related to compiling, linking and installing native libraries and applications have been converted to the Provider API so that they support <<lazy_configuration.adoc#lazy_configuration,lazy configuration>>. This conversion has introduced some breaking changes to the APIs of the tasks so that they match the conventions of the Provider API.
+Many tasks related to compiling, linking and installing native libraries and applications have been converted to the Provider API so that they support <<lazy_configuration.adoc#lazy_configuration,lazy configuration>>.
+This conversion has introduced some breaking changes to the APIs of the tasks so that they match the conventions of the Provider API.
 
 The following tasks have been changed:
 
@@ -949,7 +968,8 @@
 [[rel4.6:visual_studio_single_solution]]
 === [4.6] Visual Studio integration only supports a single solution file for all components of a build
 
-link:{groovyDslPath}/org.gradle.ide.visualstudio.VisualStudioExtension.html[VisualStudioExtension] no longer has a `solutions` property. Instead, you configure a single solution via link:{groovyDslPath}/org.gradle.ide.visualstudio.VisualStudioRootExtension.html[VisualStudioRootExtension] in the root project, like so:
+link:{groovyDslPath}/org.gradle.ide.visualstudio.VisualStudioExtension.html[VisualStudioExtension] no longer has a `solutions` property.
+Instead, you configure a single solution via link:{groovyDslPath}/org.gradle.ide.visualstudio.VisualStudioRootExtension.html[VisualStudioRootExtension] in the root project, like so:
 
 ====
 [.multi-language-sample]
@@ -973,7 +993,8 @@
 [[rel4.5:http_build_cache_no_follow_redirects]]
 === [4.5] `HttpBuildCache` no longer follows redirects
 
-When connecting to an HTTP build cache backend via `HttpBuildCache`, Gradle does not follow redirects any more, treating them as errors instead. Getting a redirect from the build cache backend is mostly a configuration error — using an "http" URL instead of "https" for example — and has negative effects on performance.
+When connecting to an HTTP build cache backend via `HttpBuildCache`, Gradle does not follow redirects any more, treating them as errors instead.
+Getting a redirect from the build cache backend is mostly a configuration error — using an "http" URL instead of "https" for example — and has negative effects on performance.
 
 [[rel4.4:security_library_upgrades]]
 === [4.4] Third-party dependency upgrades
diff --git a/subprojects/docs/src/docs/userguide/migration/upgrading_version_5.adoc b/subprojects/docs/src/docs/userguide/migration/upgrading_version_5.adoc
index 31ab8d2..2c391a9 100644
--- a/subprojects/docs/src/docs/userguide/migration/upgrading_version_5.adoc
+++ b/subprojects/docs/src/docs/userguide/migration/upgrading_version_5.adoc
@@ -15,7 +15,8 @@
 [[upgrading_version_5]]
 = Upgrading your build from Gradle 5.x to 6.0
 
-This chapter provides the information you need to migrate your Gradle 5.x builds to Gradle 6.0. For migrating from Gradle 4.x, complete the <<upgrading_version_4.adoc#upgrading_version_4, 4.x to 5.0 guide>> first.
+This chapter provides the information you need to migrate your Gradle 5.x builds to Gradle 6.0.
+For migrating from Gradle 4.x, complete the <<upgrading_version_4.adoc#upgrading_version_4, 4.x to 5.0 guide>> first.
 
 We recommend the following steps for all users:
 
@@ -25,10 +26,11 @@
 +
 This is so that you can see any deprecation warnings that apply to your build.
 +
-Alternatively, you could run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
+Alternatively, you can run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
 . Update your plugins.
 +
-Some plugins will break with this new version of Gradle, for example because they use internal APIs that have been removed or changed. The previous step will help you identify potential problems by issuing deprecation warnings when a plugin does try to use a deprecated part of the API.
+Some plugins will break with this new version of Gradle, for example because they use internal APIs that have been removed or changed.
+The previous step will help you identify potential problems by issuing deprecation warnings when a plugin does try to use a deprecated part of the API.
 +
 . Run `gradle wrapper --gradle-version 6.0` to update the project to 6.0.
 . Try to run the project and debug any errors using the <<troubleshooting.adoc#troubleshooting, Troubleshooting Guide>>.
@@ -76,7 +78,8 @@
 Property 'outputFile' is not annotated with an input or output annotation. This behavior is scheduled to be removed in Gradle 7.0.
 ```
 
-If you own the code of the tasks in question, you can fix them by <<incremental_build.adoc#sec:task_input_validation, following the suggestions>>. You can also use `--stacktrace` to see where in the code each warning originates from.
+If you own the code of the tasks in question, you can fix them by <<incremental_build.adoc#sec:task_input_validation, following the suggestions>>.
+You can also use `--stacktrace` to see where in the code each warning originates from.
 
 Otherwise, you'll need to report the problems to the maintainer of the relevant task or plugin.
 
@@ -129,7 +132,8 @@
 
 ==== Calling `Project.afterEvaluate` on an evaluated project has been deprecated
 
-Once a project is evaluated, Gradle ignores all configuration passed to `Project#afterEvaluate` and emits a deprecation warning. This scenario will become an error in Gradle 7.0.
+Once a project is evaluated, Gradle ignores all configuration passed to `Project#afterEvaluate` and emits a deprecation warning.
+This scenario will become an error in Gradle 7.0.
 
 ==== Deprecated plugins
 
@@ -211,9 +215,11 @@
 
 ==== Local build cache is always a directory cache
 
-In the past, it was possible to use any build cache implementation as the `local` cache.  This is no longer allowed as the local cache must always be a `DirectoryBuildCache`.
+In the past, it was possible to use any build cache implementation as the `local` cache.
+This is no longer allowed as the local cache must always be a `DirectoryBuildCache`.
 
-Calls to `BuildCacheConfiguration.local(Class)` with anything other than `DirectoryBuildCache` as the type will fail the build. Calling these methods with the `DirectoryBuildCache` type will produce a deprecation warning.
+Calls to `BuildCacheConfiguration.local(Class)` with anything other than `DirectoryBuildCache` as the type will fail the build.
+Calling these methods with the `DirectoryBuildCache` type will produce a deprecation warning.
 
 Use `getLocal()` and `local(Action)` instead.
 
@@ -278,7 +284,8 @@
 
 A number of fixes were made to produce more correct `ivy.xml` metadata in the `ivy-publish` plugin.
 
-As a consequence, the internal structure of the `ivy.xml` file has changed. The `runtime` configuration now contains more information, which corresponds to the _runtimeElements_ variant of a Java library. The `default` configuration should yield the same result as before.
+As a consequence, the internal structure of the `ivy.xml` file has changed. The `runtime` configuration now contains more information, which corresponds to the _runtimeElements_ variant of a Java library.
+The `default` configuration should yield the same result as before.
 
 In general, users are advised to migrate from `ivy.xml` to the new Gradle Module Metadata format.
 
@@ -296,7 +303,8 @@
 
 Previously, any `pluginManagement {}` blocks inside a settings script were executed during the normal execution of the script.
 
-Now, they are executed earlier in a similar manner to `buildscript {}` or `plugins {}`. This means that code inside such a block cannot reference anything declared elsewhere in the script.
+Now, they are executed earlier in a similar manner to `buildscript {}` or `plugins {}`.
+This means that code inside such a block cannot reference anything declared elsewhere in the script.
 
 This change has been made so that `pluginManagement` configuration can also be applied when resolving plugins for the settings script itself.
 
@@ -364,7 +372,9 @@
 
 ==== New Tooling API progress event
 
-In Gradle 6.0, we introduced a new progress event (link:{javadocPath}/org/gradle/tooling/events/test/TestOutputEvent.html[org.gradle.tooling.events.test.TestOutputEvent]) to expose the output of test execution. This new event breaks the convention of having a `StartEvent`-`FinisEvent` pair to express progress. `TaskOutputEvent` is a simple `ProgressEvent`.
+In Gradle 6.0, we introduced a new progress event (link:{javadocPath}/org/gradle/tooling/events/test/TestOutputEvent.html[org.gradle.tooling.events.test.TestOutputEvent]) to expose the output of test execution.
+This new event breaks the convention of having a `StartEvent`-`FinishEvent` pair to express progress.
+`TaskOutputEvent` is a simple `ProgressEvent`.
 
 ==== Changes to the task container behavior
 
@@ -485,9 +495,10 @@
 
 ==== Changing the contents of `ConfigurableFileCollection` task properties after task starts execution
 
-When a task property has type `ConfigurableFileCollection`, then the file collection referenced by the property will ignore changes made to the contents of the collection once the task
-starts execution. This has two benefits. Firstly, this prevents accidental changes to the property value during task execution which can cause Gradle up-to-date checks and build cache lookup
-using different values to those used by the task action. Secondly, this improves performance as Gradle can calculate the value once and cache the result.
+When a task property has type `ConfigurableFileCollection`, then the file collection referenced by the property will ignore changes made to the contents of the collection once the task starts execution.
+This has two benefits.
+Firstly, this prevents accidental changes to the property value during task execution which can cause Gradle up-to-date checks and build cache lookup using different values to those used by the task action.
+Secondly, this improves performance as Gradle can calculate the value once and cache the result.
 
 This will become an error in Gradle 6.0.
 
@@ -516,13 +527,15 @@
 
 ==== Task dependencies are honored for task `@Input` properties whose value is a `Property`
 
-Previously, task dependencies would be ignored for task `@Input` properties of type `Property<T>`. These are now honored, so that it is possible to attach a task output property to a task `@Input` property.
+Previously, task dependencies would be ignored for task `@Input` properties of type `Property<T>`.
+These are now honored, so that it is possible to attach a task output property to a task `@Input` property.
 
 This may introduce unexpected cycles in the task dependency graph, where the value of an output property is mapped to produce a value for an input property.
 
 ==== Declaring task dependencies using a file `Provider` that does not represent a task output
 
-Previously, it was possible to pass `Task.dependsOn()` a `Provider<File>`, `Provider<RegularFile>` or `Provider<Directory>` instance that did not represent a task output. These providers would be silently ignored.
+Previously, it was possible to pass `Task.dependsOn()` a `Provider<File>`, `Provider<RegularFile>` or `Provider<Directory>` instance that did not represent a task output.
+These providers would be silently ignored.
 
 This is now an error because Gradle does not know how to build files that are not task outputs.
 
@@ -530,18 +543,21 @@
 
 ==== Setting `Property` value to `null` uses the property convention
 
-Previously, calling `Property.set(null)` would always reset the value of the property to 'not defined'. Now, the convention that is associated with the property using the `convention()` method
-will be used to determine the value of the property.
+Previously, calling `Property.set(null)` would always reset the value of the property to 'not defined'.
+Now, the convention that is associated with the property using the `convention()` method will be used to determine the value of the property.
 
 ==== Enhanced validation of names for `publishing.publications` and `publishing.repositories`
 
-The repository and publication names are used to construct task names for publishing. It was possible to supply a name that would result in an invalid task name. Names for publications and repositories are now restricted to `[A-Za-z0-9_\\-.]+`.
+The repository and publication names are used to construct task names for publishing. It was possible to supply a name that would result in an invalid task name.
+Names for publications and repositories are now restricted to `[A-Za-z0-9_\\-.]+`.
 
 ==== Restricted Worker API classloader and process classpath
 
-Gradle now prevents internal dependencies (like Guava) from leaking into the classpath used by Worker API actions. This fixes link:https://github.com/gradle/gradle/issues/3698[an issue] where a worker needs to use a dependency that is also used by Gradle internally.
+Gradle now prevents internal dependencies (like Guava) from leaking into the classpath used by Worker API actions.
+This fixes link:https://github.com/gradle/gradle/issues/3698[an issue] where a worker needs to use a dependency that is also used by Gradle internally.
 
-In previous releases, it was possible to rely on these leaked classes. Plugins relying on this behavior will now fail.  To fix the plugin, the worker should explicitly include all required dependencies in its classpath.
+In previous releases, it was possible to rely on these leaked classes. Plugins relying on this behavior will now fail.
+To fix the plugin, the worker should explicitly include all required dependencies in its classpath.
 
 ==== Default PMD version upgraded to 6.15.0
 
@@ -551,11 +567,14 @@
 
 ==== Configuration copies have unique names
 
-Previously, all copies of a configuration always had the name `<OriginConfigurationName>Copy`. Now when creating multiple copies, each will have a unique name by adding an index starting from the second copy. (e.g. `CompileOnlyCopy2`)
+Previously, all copies of a configuration always had the name `<OriginConfigurationName>Copy`.
+Now when creating multiple copies, each will have a unique name by adding an index starting from the second copy. (e.g. `CompileOnlyCopy2`)
 
 ==== Changed classpath filtering for Eclipse
 
-Gradle 5.6 no longer supplies custom classpath attributes in the Eclipse model. Instead, it provides the attributes for link:https://www.eclipse.org/eclipse/news/4.8/jdt.php#jdt-test-sources[Eclipse test sources]. This change requires Buildship version 3.1.1 or later.
+Gradle 5.6 no longer supplies custom classpath attributes in the Eclipse model.
+Instead, it provides the attributes for link:https://www.eclipse.org/eclipse/news/4.8/jdt.php#jdt-test-sources[Eclipse test sources].
+This change requires Buildship version 3.1.1 or later.
 
 ==== Embedded Kotlin upgraded to 1.3.41
 
@@ -585,7 +604,8 @@
 
 === Disabled debug argument parsing in JavaExec
 
-Gradle 5.6 introduced a new DSL element (`JavaForkOptions.debugOptions(Action<JavaDebugOptions>)`) to configure debug properties for forked Java processes. Due to this change, Gradle no longer parses debug-related JVM arguments. Consequently, `JavaForkOptions.getDebu()` no longer returns `true` if the `-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005` or the `-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005` argument is specified to the process.
+Gradle 5.6 introduced a new DSL element (`JavaForkOptions.debugOptions(Action<JavaDebugOptions>)`) to configure debug properties for forked Java processes.
+Due to this change, Gradle no longer parses debug-related JVM arguments. Consequently, `JavaForkOptions.getDebu()` no longer returns `true` if the `-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005` or the `-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005` argument is specified to the process.
 
 === Scala 2.9 and Zinc compiler
 
@@ -610,10 +630,10 @@
 
 ==== User supplied Eclipse project names may be ignored on conflict
 
-Project names configured via link:{javadocPath}/org/gradle/plugins/ide/eclipse/model/EclipseProject.html[`EclipseProject.setName(...)`] were honored by Gradle and Buildship in all cases, even
-when the names caused conflicts and import/synchronization errors.
+Project names configured via link:{javadocPath}/org/gradle/plugins/ide/eclipse/model/EclipseProject.html[`EclipseProject.setName(...)`] were honored by Gradle and Buildship in all cases, even when the names caused conflicts and import/synchronization errors.
 
-Gradle can now deduplicate these names if they conflict with other project names in an Eclipse workspace. This may lead to different Eclipse project names for projects with user-specified names.
+Gradle can now deduplicate these names if they conflict with other project names in an Eclipse workspace.
+This may lead to different Eclipse project names for projects with user-specified names.
 
 The upcoming 3.1.1 version of Buildship is required to take advantage of this behavior.
 
@@ -631,7 +651,10 @@
 
 ==== Type `DependencyHandler` now statically exposes `ExtensionAware`
 
-This affects Kotlin DSL build scripts that make use of `ExtensionAware` extension members such as the `extra` properties accessor inside the `dependencies {}` block. The receiver for those members will no longer be the enclosing `Project` instance but the `dependencies` object itself, the innermost `ExtensionAware` conforming receiver. In order to address `Project` extra properties inside `dependencies {}` the receiver must be explicitly qualified i.e. `project.extra` instead of just `extra`. Affected extensions also include `the<T>()` and `configure<T>(T.() -> Unit)`.
+This affects Kotlin DSL build scripts that make use of `ExtensionAware` extension members such as the `extra` properties accessor inside the `dependencies {}` block.
+The receiver for those members will no longer be the enclosing `Project` instance but the `dependencies` object itself, the innermost `ExtensionAware` conforming receiver.
+In order to address `Project` extra properties inside `dependencies {}` the receiver must be explicitly qualified i.e. `project.extra` instead of just `extra`.
+Affected extensions also include `the<T>()` and `configure<T>(T.() -> Unit)`.
 
 ==== Improved processing of dependency excludes
 
@@ -641,7 +664,10 @@
 
 ==== Improved classpath separation for worker processes
 
-The system classpath for worker daemons started by the <<custom_tasks.adoc#worker_api, Worker API>> when using `PROCESS` isolation has been reduced to a minimum set of Gradle infrastructure. User code is still segregated into a separate classloader to isolate it from the Gradle runtime. This should be a transparent change for tasks using the worker API, but previous versions of Gradle mixed user code and Gradle internals in the worker process. Worker actions that rely on things like the `java.class.path` system property may be affected, since `java.class.path` now represents only the classpath of the Gradle internals.
+The system classpath for worker daemons started by the <<custom_tasks.adoc#worker_api, Worker API>> when using `PROCESS` isolation has been reduced to a minimum set of Gradle infrastructure.
+User code is still segregated into a separate classloader to isolate it from the Gradle runtime.
+This should be a transparent change for tasks using the worker API, but previous versions of Gradle mixed user code and Gradle internals in the worker process.
+Worker actions that rely on things like the `java.class.path` system property may be affected, since `java.class.path` now represents only the classpath of the Gradle internals.
 
 [[changes_5.4]]
 == Upgrading from 5.3 and earlier
diff --git a/subprojects/docs/src/docs/userguide/migration/upgrading_version_6.adoc b/subprojects/docs/src/docs/userguide/migration/upgrading_version_6.adoc
index 625d04d..e901d0f 100644
--- a/subprojects/docs/src/docs/userguide/migration/upgrading_version_6.adoc
+++ b/subprojects/docs/src/docs/userguide/migration/upgrading_version_6.adoc
@@ -26,10 +26,11 @@
 +
 This is so that you can see any deprecation warnings that apply to your build.
 +
-Alternatively, you could run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
+Alternatively, you can run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
 . Update your plugins.
 +
-Some plugins will break with this new version of Gradle, for example because they use internal APIs that have been removed or changed. The previous step will help you identify potential problems by issuing deprecation warnings when a plugin does try to use a deprecated part of the API.
+Some plugins will break with this new version of Gradle, for example because they use internal APIs that have been removed or changed.
+The previous step will help you identify potential problems by issuing deprecation warnings when a plugin does try to use a deprecated part of the API.
 +
 . Run `gradle wrapper --gradle-version 7.0` to update the project to 7.0.
 . Try to run the project and debug any errors using the <<troubleshooting.adoc#troubleshooting, Troubleshooting Guide>>.
@@ -216,7 +217,8 @@
 }
 ```
 
-The result of dependency resolution for your custom configuration may not be the same as Gradle 6.x or before. You may notice missing dependencies or artifacts.
+The result of dependency resolution for your custom configuration may not be the same as Gradle 6.x or before.
+You may notice missing dependencies or artifacts.
 ====
 
 ==== Location of temporary project files for `ProjectBuilder`
@@ -233,7 +235,8 @@
 Tests that use the <<test_kit#test_kit, TestKit>> API used to create temporary files under the system temporary directory as defined by `java.io.tmpdir`.
 These files were used to store copies of Gradle distributions or another test-only Gradle User Home.
 
-TestKit tests will now create temporary files under the `Test` task's temporary directory. This path is usually under the project build directory.
+TestKit tests will now create temporary files under the `Test` task's temporary directory.
+This path is usually under the project build directory.
 This may cause test failures when the test expects particular file paths.
 
 If the test uses `GradleRunner.withTestKitDir(...)`, it is unaffected.
@@ -483,7 +486,8 @@
 
 ==== Projects imported into Eclipse now include custom source set classpaths
 
-Previously, projects imported by Eclipse only included dependencies for the main and test source sets. The compile and runtime classpaths of custom source sets were ignored.
+Previously, projects imported by Eclipse only included dependencies for the main and test source sets.
+The compile and runtime classpaths of custom source sets were ignored.
 
 Since Gradle 6.8, projects imported into Eclipse include the compile and runtime classpath for every source set defined by the build.
 
@@ -788,7 +792,8 @@
 
 ==== Querying a mapped output property of a task before the task has completed
 
-Querying the value of a mapped output property before the task has completed can cause strange build failures because it indicates stale or non-existent outputs may be used by mistake. This behavior is deprecated and will emit a deprecation warning. This will become an error in Gradle 7.0.
+Querying the value of a mapped output property before the task has completed can cause strange build failures because it indicates stale or non-existent outputs may be used by mistake.
+This behavior is deprecated and will emit a deprecation warning. This will become an error in Gradle 7.0.
 
 The following example demonstrates this problem where the Producer's output file is parsed before the Producer executes:
 ```
@@ -830,7 +835,8 @@
 
 ==== `ProjectLayout` is no longer available to worker actions as a service
 
-In Gradle 6.0, the `ProjectLayout` service was made available to worker actions via service injection. This service allowed for mutable state to leak into a worker action and introduced a way for dependencies to go undeclared in the worker action.
+In Gradle 6.0, the `ProjectLayout` service was made available to worker actions via service injection.
+This service allowed for mutable state to leak into a worker action and introduced a way for dependencies to go undeclared in the worker action.
 
 `ProjectLayout` has been removed from the available services.  Worker actions that were using `ProjectLayout` should switch to injecting the `projectDirectory` or `buildDirectory` as a parameter instead.
 
diff --git a/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc b/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc
index 47bacf9..d3f4847 100644
--- a/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc
+++ b/subprojects/docs/src/docs/userguide/migration/upgrading_version_7.adoc
@@ -26,7 +26,7 @@
 +
 This is so that you can see any deprecation warnings that apply to your build.
 +
-Alternatively, you could run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
+Alternatively, you can run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
 . Update your plugins.
 +
 Some plugins will break with this new version of Gradle, for example because they use internal APIs that have been removed or changed.
@@ -93,6 +93,24 @@
 }
 ```
 
+Additionally, setting the test framework multiple times to the _same_ framework now accumulates any options that might be set on the framework.
+Previously, each time the framework was set, it would cause the framework options to be overwritten.
+
+The following code now results in both the "foo" and "bar" tags to be included for the `test` task:
+```
+test {
+   useJUnitPlatform {
+        includeTags("foo")
+   }
+}
+tasks.withType(Test).configureEach {
+   // previously, this would overwrite the included tags to only include "bar"
+   useJUnitPlatform {
+        includeTags("bar")
+   }
+}
+```
+
 === Removed APIs
 
 ==== Legacy ArtifactTransform API
@@ -197,7 +215,7 @@
 
 ==== AbstractArchiveTask API Cleanup
 
-The deprecated `appendix`, `archiveName`, `archivePath`, `baseName`, `classifier`, `desintationDir`, `extension` and `version` properties of the `AbstractArchiveTask` task type have been removed.
+The deprecated `appendix`, `archiveName`, `archivePath`, `baseName`, `classifier`, `destinationDir`, `extension` and `version` properties of the `AbstractArchiveTask` task type have been removed.
 Use the `archiveAppendix`, `archiveFileName` , `archiveFile`, `archiveBaseName`, `archiveClassifier`, `destinationDirectory`, `archiveExtension` and `archiveVersion` properties instead.
 
 ==== IdeaModule API Cleanup
@@ -395,7 +413,7 @@
 Gradle 7.x supports Kotlin Gradle Plugin 1.3.72 and above.
 Kotlin Gradle Plugin versions above 1.6.21 are not tested with Gradle 7.x.
 Gradle 8.x supports Kotlin Gradle Plugin 1.6.10 and above.
-You can use a lower Kotlin language version by modifying the language version and api version setting in the Kotlin `compile` task.
+You can use a lower Kotlin language version by modifying the language version and api version setting in the Kotlin compilation tasks.
 
 ==== Minimal supported Android Gradle Plugin version changed
 Gradle 7.x supports Android Gradle Plugin (AGP) 4.1 and above.
@@ -634,6 +652,12 @@
 * Kotlin 1.7 link:https://kotlinlang.org/docs/whatsnew17.html#language[language] / link:https://kotlinlang.org/docs/whatsnew17.html#standard-library[standard library]
 * Kotlin 1.8 link:https://kotlinlang.org/docs/whatsnew18.html#language[language] / link:https://kotlinlang.org/docs/whatsnew18.html#standard-library[standard library]
 
+Note that the Kotlin Gradle Plugin 1.8.0 started using Java toolchains.
+It is recommended you configure a toolchain instead of defining Java `sourceCompatibility`/`targetCompatibility` in Kotlin projects.
+
+Also note that the Kotlin Gradle Plugin 1.8.0 introduced `compilerOptions` with lazy configuration properties as a replacement for `kotlinOptions` which did not support lazy configuration.
+It is recommended you configure Kotlin compilation using `compilerOptions` instead of `kotlinOptions`.
+
 [[kotlin_dsl_plugin_toolchains]]
 ==== `kotlinDslPluginOptions.jvmTarget` is deprecated
 
@@ -700,6 +724,13 @@
 
 See the <<scala_plugin#sec:scala_target,Scala plugin documentation>> for details.
 
+==== `pluginBundle` dropped in Plugin Publish plugin
+
+Gradle 8 no longer supports the `pluginBundle` extension.
+Its functionality has been merged into the `gradlePlugin` block.
+These changes require recent versions of the Plugin Publish plugin (link:https://plugins.gradle.org/plugin/com.gradle.plugin-publish/1.1.0[1.0.+]).
+Documentation on configuring plugin publication can be found both link:https://plugins.gradle.org/docs/publish-plugin[on the Portal] and <<publishing_gradle_plugins#configure_the_plugin_publishing_plugin,in the user manual>>.
+
 [[changes_7.6]]
 == Upgrading from 7.5 and earlier
 
@@ -1297,10 +1328,8 @@
 [[task_execution_events]]
 ==== Task execution listeners and events
 
-The Gradle configuration cache does not support listeners and events that have direct access to `Task` and `Project` instances,
-which allows Gradle to execute tasks in parallel and to store the minimal amount of data in the configuration cache.
-In order to move towards an API that is consistent whether the configuration cache is enabled or not,
-the following APIs are deprecated and will be removed or be made an error in Gradle 8.0:
+The Gradle configuration cache does not support listeners and events that have direct access to `Task` and `Project` instances, which allows Gradle to execute tasks in parallel and to store the minimal amount of data in the configuration cache.
+In order to move towards an API that is consistent whether the configuration cache is enabled or not, the following APIs are deprecated and will be removed or be made an error in Gradle 8.0:
 
 - Interface link:{javadocPath}/org/gradle/api/execution/TaskExecutionListener.html[TaskExecutionListener]
 - Interface link:{javadocPath}/org/gradle/api/execution/TaskActionListener.html[TaskActionListener]
@@ -1310,8 +1339,7 @@
 - Method link:{javadocPath}/org/gradle/api/execution/TaskExecutionGraph.html#afterTask-org.gradle.api.Action-[TaskExecutionGraph.afterTask()]
 - Registering TaskExecutionListener, TaskActionListener, TestListener, TestOutputListener via link:{javadocPath}/org/gradle/api/invocation/Gradle.html#addListener-java.lang.Object-[Gradle.addListener()]
 
-See the <<configuration_cache#config_cache:requirements:build_listeners,configuration cache chapter>> for details on how to migrate
-these usages to APIs that are supported by the configuration cache.
+See the <<configuration_cache#config_cache:requirements:build_listeners,configuration cache chapter>> for details on how to migrate these usages to APIs that are supported by the configuration cache.
 
 [[build_finished_events]]
 ==== Build finished events
@@ -1322,28 +1350,23 @@
 - Method link:{javadocPath}/org/gradle/api/invocation/Gradle.html#buildFinished-org.gradle.api.Action-[Gradle.buildFinished()]
 - Method link:{javadocPath}/org/gradle/BuildListener.html#buildFinished-org.gradle.BuildResult-[BuildListener.buildFinished()]
 
-See the <<configuration_cache#config_cache:requirements:build_listeners,configuration cache chapter>> for details on how to migrate
-these usages to APIs that are supported by the configuration cache.
+See the <<configuration_cache#config_cache:requirements:build_listeners,configuration cache chapter>> for details on how to migrate these usages to APIs that are supported by the configuration cache.
 
 [[task_project]]
 ==== Calling `Task.getProject()` from a task action
 
-Calling link:{javadocPath}/org/gradle/api/Task.html#getProject--[Task.getProject()] from a task action at execution time is
-now deprecated and will be made an error in Gradle 8.0.
+Calling link:{javadocPath}/org/gradle/api/Task.html#getProject--[Task.getProject()] from a task action at execution time is now deprecated and will be made an error in Gradle 8.0.
 This method can be used during configuration time, but it is recommended to avoid doing this.
 
-See the <<configuration_cache#config_cache:requirements:use_project_during_execution,configuration cache chapter>> for details on
-how to migrate these usages to APIs that are supported by the configuration cache.
+See the <<configuration_cache#config_cache:requirements:use_project_during_execution,configuration cache chapter>> for details on how to migrate these usages to APIs that are supported by the configuration cache.
 
 [[task_dependencies]]
 ==== Calling `Task.getTaskDependencies()` from a task action
 
-Calling link:{javadocPath}/org/gradle/api/Task.html#getTaskDependencies--[Task.getTaskDependencies()] from a task action at
-execution time is now deprecated and will be made an error in Gradle 8.0.
+Calling link:{javadocPath}/org/gradle/api/Task.html#getTaskDependencies--[Task.getTaskDependencies()] from a task action at execution time is now deprecated and will be made an error in Gradle 8.0.
 This method can be used during configuration time, but it is recommended to avoid doing this.
 
-See the <<configuration_cache#config_cache:requirements:use_project_during_execution,configuration cache chapter>> for details on
-how to migrate these usages to APIs that are supported by the configuration cache.
+See the <<configuration_cache#config_cache:requirements:use_project_during_execution,configuration cache chapter>> for details on how to migrate these usages to APIs that are supported by the configuration cache.
 
 [[undeclared_build_service_usage]]
 ==== Using a build service from a task without the corresponding `Task.usesService` declaration
@@ -1548,8 +1571,7 @@
 
 ==== Start scripts require bash shell
 
-The command used to start Gradle, the Gradle wrapper as well as the scripts generated by the `application` plugin
-now require `bash` shell.
+The command used to start Gradle, the Gradle wrapper as well as the scripts generated by the `application` plugin now require `bash` shell.
 
 === Deprecations
 
@@ -1599,8 +1621,7 @@
 * settings file in the root of the build
 * build file in the root of each subproject
 
-For the use case where custom settings or build files are used to model different behavior (similar to Maven profiles),
-consider using <<build_environment#sec:gradle_system_properties, system properties>> with conditional logic.
+For the use case where custom settings or build files are used to model different behavior (similar to Maven profiles), consider using <<build_environment#sec:gradle_system_properties, system properties>> with conditional logic.
 For example, given a piece of code in either settings or build file:
 ```
 if (System.getProperty("profile") == "custom") {
@@ -1614,12 +1635,10 @@
 [[dependency_substitutions_with]]
 ==== Substitution.with replaced with Substitution.using
 
-<<resolution_rules#sec:dependency_substitution_rules, Dependency substitutions>> using `with` method have been deprecated
-and are replaced with `using` method that also allows chaining.
+<<resolution_rules#sec:dependency_substitution_rules, Dependency substitutions>> using `with` method have been deprecated and are replaced with `using` method that also allows chaining.
 For example, a dependency substitution rule `substitute(project(':a')).with(project(':b'))` should be replaced with
 `substitute(project(':a')).using(project(':b'))`.
-With chaining you can, for example, add a reason for a substitution like this:
-`substitute(project(':a')).using(project(':b')).because("a reason")`.
+With chaining you can, for example, add a reason for a substitution like this: `substitute(project(':a')).using(project(':b')).because("a reason")`.
 
 [[java_exec_properties]]
 ==== Properties deprecated in JavaExec task
@@ -1664,7 +1683,7 @@
 The concept of conventions is outdated and superseded by extensions.
 To reflect this in the Gradle API, the following elements are now deprecated:
 
-- link:{javadocPath}/org/gradle/api/Project.html#getConvention--[Project.html#getConvention()]
+- link:{javadocPath}/org/gradle/api/Project.html#getConvention--[org.gradle.api.Project.getConvention()]
 - `org.gradle.api.internal.HasConvention` (deprecated)
 
 The internal usages of conventions have been also cleaned up (see the deprecated items below).
@@ -1675,39 +1694,10 @@
 - Migrate plugin configuration: link:https://github.com/gradle/gradle/pull/16900/files#diff-ac53d4f39698b83e30b93855fe6a725ffd96d5ed9df156d4f9dfd32bdc7946e7[gradle/gradle#16900].
 - Migrate custom source sets: link:https://github.com/gradle/gradle/pull/17149/files#diff-e159587e2f9aec398fa795b1d8b344f1593cb631e15e04893d31cdc9465f9781[gradle/gradle#17149].
 
-[[base_convention_deprecation]]
-==== Deprecated `base` plugin conventions
-
-The convention properties contributed by the `base` plugin have been deprecated and scheduled for removal in Gradle 8.0.
-The conventions are replaced by the `base { }` configuration block backed by link:{groovyDslPath}/org.gradle.api.plugins.BasePluginExtension.html[BasePluginExtension].
-
-The old convention object defines the `distsDirName`, `libsDirName` and `archivesBaseName` properties with simple getter and setter methods.
-Those methods are available in the extension only to maintain backwards compatibility.
-Build scripts should solely use the properties of type `Property`:
-```
-base {
-    archivesName = 'customBase'
-    distsDirectory = layout.buildDirectory.dir('custom-dist')
-    libsDirectory = layout.buildDirectory.dir('custom-libs')
-}
-```
-
-[[application_convention_deprecation]]
-==== Deprecated `ApplicationPluginConvention`
-
-link:{javadocPath}/org/gradle/api/plugins/ApplicationPluginConvention.html[ApplicationPluginConvention] was already listed as deprecated in the <<application_plugin.adoc#sec:application_convention_properties, documentation>>.
-Now, it is officially annotated as deprecated and scheduled for removal in Gradle 8.0.
-
-[[java_convention_deprecation]]
-==== Deprecated `java` plugin conventions
-
-The convention properties contributed by the `java` plugin have been deprecated and scheduled for removal in Gradle 8.0.
-They are replaced by the properties of link:{groovyDslPath}/org.gradle.api.plugins.JavaPluginExtension.html[JavaPluginExtension] which can be configured in the `java {}` block.
-
 [[plugin_configuration_consumption]]
 ==== Deprecated consumption of internal plugin configurations
 
-Some of the core Gradle plugins declare configurations that are used by the plugin itself and are not meant to be
+Some core Gradle plugins declare configurations that are used by the plugin itself and are not meant to be
 published or consumed by another subproject directly.
 Gradle did not explicitly prohibit this.
 Gradle 7.1 deprecates consumption of those configurations and this will become an error in Gradle 8.0.
@@ -1768,27 +1758,6 @@
 }
 ```
 
-[[project_report_convention_deprecation]]
-==== Deprecated `project-report` plugin conventions
-
-link:{groovyDslPath}/org.gradle.api.plugins.ProjectReportsPluginConvention.html[ProjectReportsPluginConvention] is now deprecated and scheduled for removal in Gradle 8.0.
-Clients should configure the project report tasks directly.
-Also, link:{javadocPath}/org/gradle/api/DomainObjectCollection.html#withType-java.lang.Class-[tasks.withType(...).configureEach(...)] can be used to configure each task of the same type (`HtmlDependencyReportTask` for example).
-
-[[war_convention_deprecation]]
-==== Deprecated `war` plugin conventions
-
-link:{javadocPath}/org/gradle/api/plugins/WarPluginConvention.html[WarPluginConvention] is now deprecated and scheduled for removal in Gradle 8.0.
-Clients should configure the `war` task  directly.
-Also, link:{javadocPath}/org/gradle/api/DomainObjectCollection.html#withType-java.lang.Class-[tasks.withType(War.class).configureEach(...)] can be used to configure each task of type `War`.
-
-[[ear_convention_deprecation]]
-==== Deprecated `ear` plugin conventions
-
-link:{javadocPath}/org/gradle/plugins/ear/EarPluginConvention.html[EarPluginConvention] is now deprecated and scheduled for removal in Gradle 8.0.
-Clients should configure the `ear` task directly.
-Also, link:{javadocPath}/org/gradle/api/DomainObjectCollection.html#withType-java.lang.Class-[tasks.withType(Ear.class).configureEach(...)] can be used to configure each task of type `Ear`.
-
 [[custom_source_set_deprecation]]
 ==== Deprecated custom source set interfaces
 The following source set interfaces are now deprecated and scheduled for removal in Gradle 8.0:
diff --git a/subprojects/docs/src/docs/userguide/migration/upgrading_version_8.adoc b/subprojects/docs/src/docs/userguide/migration/upgrading_version_8.adoc
index b2e6cb4..dfb1512 100644
--- a/subprojects/docs/src/docs/userguide/migration/upgrading_version_8.adoc
+++ b/subprojects/docs/src/docs/userguide/migration/upgrading_version_8.adoc
@@ -26,7 +26,7 @@
 +
 This is so you can see any deprecation warnings that apply to your build.
 +
-Alternatively, you could run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
+Alternatively, you can run `gradle help --warning-mode=all` to see the deprecations in the console, though it may not report as much detailed information.
 . Update your plugins.
 +
 Some plugins will break with this new version of Gradle, for example because they use internal APIs that have been removed or changed.
@@ -35,9 +35,703 @@
 . Run `gradle wrapper --gradle-version {gradleVersion}` to update the project to {gradleVersion}.
 . Try to run the project and debug any errors using the <<troubleshooting.adoc#troubleshooting, Troubleshooting Guide>>.
 
+[[changes_8.3]]
+== Upgrading from 8.2 and earlier
+
+=== Potential breaking changes
+
+[[kotlin_1_8.20]]
+==== Upgrade to Kotlin 1.8.21
+
+The embedded Kotlin has been updated to link:https://github.com/JetBrains/kotlin/releases/tag/v1.8.21[Kotlin 1.8.21].
+
+[[changes_8.2]]
+== Upgrading from 8.1 and earlier
+
+=== Potential breaking changes
+
+[[kotlin_1_8.20]]
+==== Upgrade to Kotlin 1.8.20
+
+The embedded Kotlin has been updated to link:https://github.com/JetBrains/kotlin/releases/tag/v1.8.20[Kotlin 1.8.20].
+For more information, see https://kotlinlang.org/docs/whatsnew1820.html[What's new in Kotlin 1.8.20].
+
+Note that there is a known issue with Kotlin compilation avoidance that can cause `OutOfMemory` exceptions in `compileKotlin` tasks if the compilation classpath contains very large JAR files.
+This applies to builds applying the Kotlin plugin v1.8.20 or the `kotlin-dsl` plugin.
+
+You can work around it by disabling Kotlin compilation avoidance in your `gradle.properties` file:
+
+[source,properties]
+----
+kotlin.incremental.useClasspathSnapshot=false
+----
+
+See link:https://youtrack.jetbrains.com/issue/KT-57757/[KT-57757] for more information.
+
+==== Upgrade to Groovy 3.0.17
+
+Groovy has been updated to https://groovy-lang.org/changelogs/changelog-3.0.17.html[Groovy 3.0.17].
+
+Since the previous version was 3.0.15, the https://groovy-lang.org/changelogs/changelog-3.0.16.html[3.0.16] changes are also included.
+
+==== Upgrade to Ant 1.10.13
+
+Ant has been updated to https://github.com/apache/ant/blob/rel/1.10.13/WHATSNEW[Ant 1.10.13].
+
+Since the previous version was 1.10.11, the https://github.com/apache/ant/blob/rel/1.10.12/WHATSNEW[1.10.12] changes are also included.
+
+==== Upgrade to CodeNarc 3.2.0
+
+The default version of CodeNarc has been updated to https://github.com/CodeNarc/CodeNarc/blob/v3.2.0/CHANGELOG.md#version-320----jan-2023[CodeNarc 3.2.0].
+
+==== Upgrade to PMD 6.55.0
+
+PMD has been updated to https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_release_notes.html[PMD 6.55.0].
+
+Since the previous version was 6.48.0, all changes since then are included.
+
+==== Upgrade to JaCoCo 0.8.9
+
+JaCoCo has been updated to https://www.jacoco.org/jacoco/trunk/doc/changes.html[0.8.9].
+
+=== Deprecations
+
+[[compile_options_generated_sources_directory]]
+==== `link:{javadocPath}/org/gradle/api/tasks/compile/CompileOptions.html[CompileOptions]` method deprecations
+
+The following methods on `CompileOptions` are deprecated:
+
+- `getAnnotationProcessorGeneratedSourcesDirectory()`
+- `setAnnotationProcessorGeneratedSourcesDirectory(File)`
+- `setAnnotationProcessorGeneratedSourcesDirectory(Provider<File>)`
+
+Current usages of these methods should migrate to `link:{javadocPath}/org/gradle/api/tasks/compile/CompileOptions.html#getGeneratedSourceOutputDirectory--[DirectoryProperty getGeneratedSourceOutputDirectory()]`
+
+[[deprecated_configuration_usage]]
+==== Using configurations incorrectly
+
+Gradle will now warn at runtime when methods of link:{javadocPath}/org/gradle/api/artifacts/Configuration.html--[Configuration] are called inconsistently with the configuration's intended usage.
+
+This change is part of a larger ongoing effort to make the intended behavior of configurations more consistent and predictable, and to unlock further speed and memory improvements.
+
+Currently, the following methods should only be called with these listed allowed usages:
+
+- `resolve()` - RESOLVABLE configurations only
+- `files(Closure)`, `files(Spec)`, `files(Dependency…)`, `fileCollection(Spec)`, `fileCollection(Closure)`, `fileCollection(Dependency…)` - RESOLVABLE configurations only
+- `getResolvedConfigurations()` - RESOLVABLE configurations only
+- `defaultDependencies(Action)` - DECLARABLE configurations only
+- `shouldResolveConsistentlyWith(Configuration)` - RESOLVABLE configurations only
+- `disableConsistentResolution()` - RESOLVABLE configurations only
+- `getDependencyConstraints()` - DECLARABLE configurations only
+- `copy()`, `copy(Spec)`, `copy(Closure)`, `copyRecursive()`, `copyRecursive(Spec)`, `copyRecursive(Closure)` - RESOLVABLE configurations only
+
+Intended usage is noted in the `Configuration` interface's Javadoc.
+This list is likely to grow in future releases.
+
+Starting in Gradle 9.0, using a configuration inconsistently with its intended usage will be prohibited.
+
+Also note that although it is not currently restricted, the `getDependencies()` method is really only intended for use with DECLARABLE configurations.
+The `getAllDependencies()` method, which retrieves all declared dependencies on a configuration and any superconfigurations, will not be restricted to any particular usage.
+
+[[deprecated_access_to_conventions]]
+==== Deprecated access to plugin conventions
+
+The concept of conventions is outdated and superseded by <<implementing_gradle_plugins.adoc#modeling_dsl_like_apis, extensions>> to provide custom DSLs.
+
+To reflect this in the Gradle API, the following elements are deprecated:
+
+- link:{javadocPath}/org/gradle/api/Project.html#getConvention--[org.gradle.api.Project.getConvention()]
+- link:{javadocPath}/org/gradle/api/plugins/Convention.html[org.gradle.api.plugins.Convention]
+- `org.gradle.api.internal.HasConvention`
+
+Gradle Core plugins still register their conventions in addition to their extensions for backwards compatibility.
+
+It is deprecated to access any of these conventions and their properties.
+Doing so will now emit a deprecation warning.
+This will become an error in Gradle 9.0.
+You should prefer accessing the extensions and their properties instead.
+
+For specific examples see the next sections.
+
+Prominent community plugins already migrated to using extensions to provide custom DSLs.
+Some of them still registers conventions for backwards compatibility.
+Registering conventions does not emit a deprecation warning yet to provide a migration window.
+Future Gradle versions will do.
+
+[[base_convention_deprecation]]
+==== Deprecated `base` plugin conventions
+
+The convention properties contributed by the `base` plugin have been deprecated and scheduled for removal in Gradle 9.0.
+For the wider context see the <<deprecated_access_to_conventions, section about plugin convention deprecation>>.
+
+The conventions are replaced by the `base { }` configuration block backed by link:{groovyDslPath}/org.gradle.api.plugins.BasePluginExtension.html[BasePluginExtension].
+The old convention object defines the `distsDirName`, `libsDirName` and `archivesBaseName` properties with simple getter and setter methods.
+Those methods are available in the extension only to maintain backwards compatibility.
+Build scripts should solely use the properties of type `Property`:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    base
+}
+
+base {
+    archivesName.set("gradle")
+    distsDirectory.set(layout.buildDirectory.dir("custom-dist"))
+    libsDirectory.set(layout.buildDirectory.dir("custom-libs"))
+}
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'base'
+}
+
+base {
+    archivesName = "gradle"
+    distsDirectory = layout.buildDirectory.dir('custom-dist')
+    libsDirectory = layout.buildDirectory.dir('custom-libs')
+}
+----
+=====
+====
+
+[[application_convention_deprecation]]
+==== Deprecated `application` plugin conventions
+
+The convention properties contributed by the `application` plugin have been deprecated and scheduled for removal in Gradle 9.0.
+For the wider context see the <<deprecated_access_to_conventions, section about plugin convention deprecation>>.
+
+The following code will now emit deprecation warnings:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    application
+}
+
+applicationDefaultJvmArgs = listOf("-Dgreeting.language=en") // Accessing a convention
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'application'
+}
+
+applicationDefaultJvmArgs = ['-Dgreeting.language=en'] // Accessing a convention
+----
+=====
+====
+
+This should be changed to use the `application { }` configuration block, backed by link:{groovyDslPath}/org.gradle.api.plugins.JavaApplication.html[JavaApplication], instead:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    application
+}
+
+application {
+    applicationDefaultJvmArgs = listOf("-Dgreeting.language=en")
+}
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'application'
+}
+
+application {
+    applicationDefaultJvmArgs = ['-Dgreeting.language=en']
+}
+----
+=====
+====
+
+[[java_convention_deprecation]]
+==== Deprecated `java` plugin conventions
+
+The convention properties contributed by the `java` plugin have been deprecated and scheduled for removal in Gradle 9.0.
+For the wider context see the <<deprecated_access_to_conventions, section about plugin convention deprecation>>.
+
+The following code will now emit deprecation warnings:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    id("java")
+}
+
+configure<JavaPluginConvention> { // Accessing a convention
+    sourceCompatibility = JavaVersion.VERSION_18
+}
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'java'
+}
+
+sourceCompatibility = 18 // Accessing a convention
+----
+=====
+====
+
+This should be changed to use the `java { }` configuration block, backed by link:{groovyDslPath}/org.gradle.api.plugins.JavaPluginExtension.html[JavaPluginExtension], instead:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    id("java")
+}
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_18
+}
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'java'
+}
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_18
+}
+----
+=====
+====
+
+[[war_convention_deprecation]]
+==== Deprecated `war` plugin conventions
+
+The convention properties contributed by the `war` plugin have been deprecated and scheduled for removal in Gradle 9.0.
+For the wider context see the <<deprecated_access_to_conventions, section about plugin convention deprecation>>.
+
+The following code will now emit deprecation warnings:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    id("war")
+}
+
+configure<WarPluginConvention> { // Accessing a convention
+    webAppDirName = "src/main/webapp"
+}
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'war'
+}
+
+webAppDirName = 'src/main/webapp' // Accessing a convention
+----
+=====
+====
+
+Clients should configure the `war` task  directly.
+Also, link:{javadocPath}/org/gradle/api/DomainObjectCollection.html#withType-java.lang.Class-[tasks.withType(War.class).configureEach(...)] can be used to configure each task of type `War`.
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    id("war")
+}
+
+tasks.war {
+    webAppDirectory.set(file("src/main/webapp"))
+}
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'war'
+}
+
+war {
+    webAppDirectory = file('src/main/webapp')
+}
+----
+=====
+====
+
+[[ear_convention_deprecation]]
+==== Deprecated `ear` plugin conventions
+
+The convention properties contributed by the `ear` plugin have been deprecated and scheduled for removal in Gradle 9.0.
+For the wider context see the <<deprecated_access_to_conventions, section about plugin convention deprecation>>.
+
+The following code will now emit deprecation warnings:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    id("ear")
+}
+
+configure<EarPluginConvention> { // Accessing a convention
+    appDirName = "src/main/app"
+}
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'ear'
+}
+
+appDirName = 'src/main/app' // Accessing a convention
+----
+=====
+====
+
+Clients should configure the `ear` task directly.
+Also, link:{javadocPath}/org/gradle/api/DomainObjectCollection.html#withType-java.lang.Class-[tasks.withType(Ear.class).configureEach(...)] can be used to configure each task of type `Ear`.
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    id("ear")
+}
+
+tasks.ear {
+    appDirectory.set(file("src/main/app"))
+}
+----
+=====
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'ear'
+}
+
+ear {
+    appDirectory = file('src/main/app')  // use application metadata found in this folder
+}
+----
+=====
+====
+
+[[project_report_convention_deprecation]]
+==== Deprecated `project-report` plugin conventions
+
+The convention properties contributed by the `project-reports` plugin have been deprecated and scheduled for removal in Gradle 9.0.
+For the wider context see the <<deprecated_access_to_conventions, section about plugin convention deprecation>>.
+
+The following code will now emit deprecation warnings:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    `project-report`
+}
+
+configure<ProjectReportsPluginConvention> {
+    projectReportDirName = "custom" // Accessing a convention
+}
+----
+=====
+
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'project-report'
+}
+
+projectReportDirName = "custom" // Accessing a convention
+----
+=====
+====
+
+Configure your report task instead:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    `project-report`
+}
+
+tasks.withType<HtmlDependencyReportTask>() {
+    projectReportDirectory.set(project.layout.buildDirectory.dir("reports/custom"))
+}
+----
+=====
+
+[.multi-language-sample]
+=====
+.build.gradle
+[source,groovy]
+----
+plugins {
+    id 'project-report'
+}
+
+tasks.withType(HtmlDependencyReportTask) {
+    projectReportDirectory = project.layout.buildDirectory.dir("reports/custom")
+}
+----
+=====
+====
+
+[[redundant_configuration_usage_activation]]
+==== Redundant configuration usage activation
+
+Calling `setCanBeConsumed(boolean)` or `setCanBeResolved(boolean)` on a configuration that already allows that usage is deprecated.
+
+This deprecation is intended to help users identify unnecessary configuration usage modifications.
+
+[[deprecated_configuration_get_all]]
+==== `link:{javadocPath}/org/gradle/api/artifacts/Configuration.html[Configuration]` method deprecations
+
+The following method on `Configuration` is deprecated for removal:
+
+- `getAll()`
+
+Obtain the set of all configurations from the project's `configurations` container instead.
+
+[[test_framework_implementation_dependencies]]
+==== Relying on automatic test framework implementation dependencies
+
+In some cases, Gradle will load JVM test framework dependencies from the Gradle distribution in order to
+execute tests.
+This existing behavior can lead to test framework dependency version conflicts on the test
+classpath.
+To avoid these conflicts, this behavior is deprecated and will be removed in Gradle 9.0. Tests
+using TestNG are unaffected.
+
+In order to prepare for this change in behavior, either declare the required dependencies explicitly,
+or migrate to link:jvm_test_suite_plugin.html[Test Suites], where these dependencies are managed automatically.
+
+===== Test Suites
+
+Builds that use test suites will not be affected by this change.
+Test suites manage the test
+framework dependencies automatically and do not require dependencies to be explicitly declared.
+See link:jvm_test_suite_plugin.html[the user manual] for further information on migrating to test suites.
+
+===== Manually declaring dependencies
+
+In the absence of test suites, dependencies must be manually declared on the test runtime classpath:
+
+  * If using JUnit 5, an explicit `runtimeOnly` dependency on `junit-platform-launcher` is required
+  in addition to the existing `implementation` dependency on the test engine.
+  * If using JUnit 4, only the existing `implementation` dependency on `junit` 4 is required.
+  * If using JUnit 3, a test `runtimeOnly` dependency on `junit` 4 is required in addition to a
+  `compileOnly` dependency on `junit` 3.
+
+=====
+[.multi-language-sample]
+======
+.build.gradle.kts
+[source,kotlin]
+----
+dependencies {
+    // If using JUnit Jupiter
+    testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+
+    // If using JUnit Vintage
+    testCompileOnly("junit:junit:4.13.2")
+    testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.9.2")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+
+    // If using JUnit 4
+    testImplementation("junit:junit:4.13.2")
+
+    // If using JUnit 3
+    testCompileOnly("junit:junit:3.8.2")
+    testRuntimeOnly("junit:junit:4.13.2")
+}
+----
+======
+[.multi-language-sample]
+======
+.build.gradle
+[source,groovy]
+----
+dependencies {
+    // If using JUnit Jupiter
+    testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+
+    // If using JUnit Vintage
+    testCompileOnly("junit:junit:4.13.2")
+    testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.9.2")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+
+    // If using JUnit 4
+    testImplementation("junit:junit:4.13.2")
+
+    // If using JUnit 3
+    testCompileOnly("junit:junit:3.8.2")
+    testRuntimeOnly("junit:junit:4.13.2")
+}
+----
+======
+=====
+
+[[build_identifier_name_and_current_deprecation]]
+==== `link:{javadocPath}/org/gradle/api/artifacts/component/BuildIdentifier.html[BuildIdentifier]` and `link:{javadocPath}/org/gradle/api/artifacts/component/ProjectComponentSelector.html[ProjectComponentSelector]` method deprecations
+
+The following methods on `BuildIdentifier` are deprecated:
+
+- `getName()`
+- `isCurrentBuild()`
+
+You could use these methods to distinguish between different project components with the same name but from different builds.
+However, for certain composite build setups, these methods do not provide enough information to guarantee uniqueness.
+
+Current usages of these methods should migrate to `link:{javadocPath}/org/gradle/api/artifacts/component/BuildIdentifier.html#getBuildPath--[BuildIdentifier.getBuildPath()]`.
+
+Similarly, the method `ProjectComponentSelector.getBuildName()` is deprecated.
+Use `link:{javadocPath}/org/gradle/api/artifacts/component/ProjectComponentSelector.html#getBuildPath--[ProjectComponentSelector.getBuildPath()]` instead.
+
 [[changes_8.1]]
 == Upgrading from 8.0 and earlier
 
+[[cache_marking]]
+=== CACHEDIR.TAG files are created in global cache directories
+Gradle now emits a `CACHEDIR.TAG` file in some global cache directories, as specified in <<directory_layout#dir:gradle_user_home:cache_marking>>.
+
+This may cause these directories to no longer be searched or backed up by some tools. To disable it, use the following code in an <<init_scripts#sec:using_an_init_script,init script>> in the Gradle User Home:
+
+====
+[.multi-language-sample]
+=====
+.init.gradle.kts
+[source,kotlin]
+----
+beforeSettings {
+    caches {
+        // Disable cache marking for all caches
+        markingStrategy.set(MarkingStrategy.NONE)
+    }
+}
+----
+=====
+[.multi-language-sample]
+=====
+.init.gradle
+[source,groovy]
+----
+beforeSettings { settings ->
+    settings.caches {
+        // Disable cache marking for all caches
+        markingStrategy = MarkingStrategy.NONE
+    }
+}
+----
+=====
+====
+
+[[configuration_caching_options_renamed]]
+=== Configuration cache options renamed
+
+In this release, the configuration cache feature was promoted from incubating to stable, and as such, all properties
+originally mentioned in the feature documentation (which had an `unsafe` part in their names, e.g. `org.gradle.unsafe.configuration-cache`) were renamed, in some cases, by just removing the `unsafe` bit.
+
+[cols="1,1", options="header"]
+|===
+
+| Incubating property
+| Finalized property
+
+|`org.gradle.unsafe.configuration-cache`
+|`org.gradle.configuration-cache`
+
+|`org.gradle.unsafe.configuration-cache-problems`
+|`org.gradle.configuration-cache.problems`*
+
+|`org.gradle.unsafe.configuration-cache.max-problems`
+|`org.gradle.configuration-cache.max-problems`
+|===
+
+Note that the original `org.gradle.unsafe.configuration-cache...` properties continue to be honored in this release,
+and no warnings will be produced if they are used, but they will be deprecated and removed in a future release.
+
 === Potential breaking changes
 
 ==== Kotlin DSL scripts emit compilation warnings
@@ -47,31 +741,114 @@
 
 This is a potentially breaking change if you are consuming the console output of Gradle builds.
 
+==== Configuring Kotlin compiler options with the `kotlin-dsl` plugin applied
+
+If you are configuring custom Kotlin compiler options on a project with the <<kotlin_dsl.adoc#sec:kotlin-dsl_plugin, kotlin-dsl>> plugin applied you might encounter a breaking change.
+
+In previous Gradle versions, the `kotlin-dsl` plugin was adding required compiler arguments on link:{javadocPath}/org/gradle/api/Project.html#afterEvaluate-org.gradle.api.Action-[afterEvaluate {}].
+Now that the Kotlin Gradle Plugin provides <<lazy_configuration.adoc#lazy_configuration, lazy configuration>> properties, our `kotlin-dsl` plugin switched to adding required compiler arguments to the lazy properties directly.
+As a consequence, if you were setting `freeCompilerArgs` the `kotlin-dsl` plugin is now failing the build because its required compiler arguments are overridden by your configuration.
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    `kotlin-dsl`
+}
+
+tasks.withType(KotlinCompile::class).configureEach {
+    kotlinOptions { // Deprecated non-lazy configuration options
+        freeCompilerArgs = listOf("-Xcontext-receivers")
+    }
+}
+----
+=====
+====
+
+With the configuration above you would get the following build failure:
+
+[source,text]
+----
+* What went wrong
+Execution failed for task ':compileKotlin'.
+> Kotlin compiler arguments of task ':compileKotlin' do not work for the `kotlin-dsl` plugin. The 'freeCompilerArgs' property has been reassigned. It must instead be appended to. Please use 'freeCompilerArgs.addAll(\"your\", \"args\")' to fix this.
+----
+
+You must change this to adding your custom compiler arguments to the lazy configuration properties of the Kotlin Gradle Plugin in order for them to be appended to the ones required by the `kotlin-dsl` plugin:
+
+====
+[.multi-language-sample]
+=====
+.build.gradle.kts
+[source,kotlin]
+----
+plugins {
+    `kotlin-dsl`
+}
+
+tasks.withType(KotlinCompile::class).configureEach {
+    compilerOptions { // New lazy configuration options
+        freeCompilerArgs.addAll("-Xcontext-receivers")
+    }
+}
+----
+=====
+====
+
+If you were already adding to `freeCompilerArgs` instead of setting its value, then you should not experience a build failure.
+
+==== New API introduced may clash with existing Gradle DSL code
+
+When a new property or method is added to an existing type in the Gradle DSL, it may clash with names already in use in user code.
+
+When a name clash occurs, one solution is to rename the element in user code.
+
+This is a non-exhaustive list of API additions in 8.1 that may cause name collisions with existing user code.
+
+* link:{javadocPath}/org/gradle/api/tasks/JavaExec.html#getJvmArguments--[`JavaExec.getJvmArguments()`]
+* link:{javadocPath}/org/gradle/process/JavaExecSpec.html#getJvmArguments--[`JavaExecSpec.getJvmArguments()`]
+
+==== Using unsupported API to start external processes at configuration time is no longer allowed with the configuration cache enabled
+
+Since Gradle 7.5, using `Project.exec`, `Project.javaexec`, and standard Java and Groovy APIs to run external processes at configuration time has been considered an error only if the <<configuration_cache.adoc#config_cache:stable,feature preview `STABLE_CONFIGURATION_CACHE`>> was enabled.
+With the configuration cache promotion to a stable feature in Gradle 8.1, this error is detected regardless of the feature preview status.
+The <<configuration_cache#config_cache:requirements:external_processes,configuration cache chapter>> has more details to help with the migration to the new provider-based APIs to execute external processes at configuration time.
+
+Builds that do not use the configuration cache, or only start external processes at execution time are not affected by this change.
+
 === Deprecations
 
-[[custom_configuration_roles]]
-==== Custom configuration roles
-
-// TODO: There is currently no API for users to provide "custom roles" or even
-// any roles at all other than the existing role-mutating methods. We should revisit
-// this deprecation log and section when we introduce a public API for creating
-// locked-role configurations.
-
-Custom roles have been deprecated.
-Use a pre-defined role instead.
-
 [[configurations_allowed_usage]]
-==== Allowed configurations usage
+==== Mutating core plugin configuration usage
 
-The usage of configurations should be fixed at creation.
-Mutating the allowed usage on a configuration is deprecated.
+The allowed usage of a configuration should be immutable after creation.
+Mutating the allowed usage on a configuration created by a Gradle core plugin is deprecated.
 This includes calling any of the following `Configuration` methods:
 
 - `setCanBeConsumed(boolean)`
 - `setCanBeResolved(boolean)`
 
+These methods now emit deprecation warnings on these configurations, except for certain special cases which make allowances for the existing behavior of popular plugins.
+This rule does not yet apply to detached configurations or configurations created
+in buildscripts and third-party plugins.
+Calling `setCanBeConsumed(false)` on `apiElements` or `runtimeElements`
+is not yet deprecated in order to avoid warnings that would be otherwise emitted when
+using select popular third-party plugins.
+
+This change is part of a larger ongoing effort to make the intended behavior of configurations more consistent and predictable, and to unlock further speed and memory improvements in this area of Gradle.
+
 The ability to change the allowed usage of a configuration after creation will be removed in Gradle 9.0.
 
+[[reserved_configuration_names]]
+==== Reserved configuration names
+
+Configuration names "detachedConfiguration" and "detachedConfigurationX" (where X is any integer) are reserved for internal use when creating detached configurations.
+
+The ability to create non-detached configurations with these names will be removed in Gradle 9.0.
+
 [[java_extension_without_java_component]]
 ==== Calling select methods on the `JavaPluginExtension` without the `java` component present
 
@@ -101,19 +878,16 @@
 Starting in Gradle 9.0, this method will be removed without replacement.
 
 [[test_task_default_classpath]]
-==== Relying on Test task default testClassesDirs and classpath
+==== Relying on conventions for custom Test tasks
 
-By default, when applying the `JavaPlugin`, standalone `Test` tasks are configured such that their `testClassesDirs`
-and `classpath` mirror the corresponding values in the default `test` `TestSuite`. This behavior will be removed
-in Gradle 9.0.
+By default, when applying the link:java_plugin.html[`java`] plugin, the `testClassesDirs`and `classpath` of all `Test` tasks have the same convention.
+Unless otherwise changed, the default behavior is to execute the tests from the default `test` link:jvm_test_suite_plugin.html[`TestSuite`] by configuring the task with the `classpath` and `testClassesDirs` from the `test` suite.
+This behavior will be removed in Gradle 9.0.
 
-For most use cases, this existing default behavior is unnecessary, since most standalone test
-tasks are used to model separate test suites and thus run a separate set of tests than those
-of the default test task. For more information on how to properly configure a standalone test task
-to compile and run against a separate set of tests, see link:https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests[Configuring integration tests].
+While this existing default behavior is correct for the use case of executing the default unit test suite under a different environment, it does not support the use case of executing an entirely separate set of tests.
 
-However, this existing default behavior can be restored for a given
-test named `myTestTask` with the following code:
+If you wish to continue including these tests, use the following code to avoid the deprecation warning in 8.1 and prepare for the behavior change in 9.0.
+Alternatively, consider migrating to test suites.
 
 ====
 [.multi-language-sample]
@@ -121,9 +895,10 @@
 .build.gradle.kts
 [source,kotlin]
 ----
+val test by testing.suites.existing(JvmTestSuite::class)
 tasks.named<Test>("myTestTask") {
-    testClassesDirs = tasks.test.get().testClassesDirs
-    classpath = tasks.test.get().classpath
+    testClassesDirs = files(test.map { it.sources.output.classesDirs })
+    classpath = files(test.map { it.sources.runtimeClasspath })
 }
 ----
 =====
@@ -133,8 +908,8 @@
 [source,groovy]
 ----
 tasks.myTestTask {
-    testClassesDirs = tasks.test.testClassesDirs
-    classpath = tasks.test.classpath
+    testClassesDirs = testing.suites.test.sources.output.classesDirs
+    classpath = testing.suites.test.sources.runtimeClasspath
 }
 ----
 =====
@@ -243,7 +1018,7 @@
 [[validate_plugins_without_java_toolchain]]
 ==== Using `ValidatePlugins` task without a Java Toolchain
 
-Using a task of type link:{javadocPath}/org/gradle/plugin/devel/tasks/ValidatePlugins.html[ValidatePlugins] without applying the link:https://docs.gradle.org/current/userguide/toolchains.html[Java Toolchains] plugin is deprecated, and will become an error in Gradle 9.0.
+Using a task of type link:{javadocPath}/org/gradle/plugin/devel/tasks/ValidatePlugins.html[ValidatePlugins] without applying the link:toolchains.html[Java Toolchains] plugin is deprecated, and will become an error in Gradle 9.0.
 
 To avoid this warning, please apply the plugin to your project:
 
@@ -270,19 +1045,17 @@
 =====
 ====
 
-The Java Toolchains plugin is applied automatically by the link:https://docs.gradle.org/current/userguide/java_plugin.html[Java plugin],
-so you can also apply it to your project and it will fix the warning.
+The Java Toolchains plugin is applied automatically by the link:java_plugin.html[Java plugin], so you can also apply it to your project and it will fix the warning.
 
 [[org_gradle_util_reports_deprecations]]
 ==== Deprecated members of the `org.gradle.util` package now report their deprecation
 
 These members will be removed in Gradle 9.0.
 
-* `VersionNumber`
 * `WrapUtil.toDomainObjectSet(...)`
 * `GUtil.toCamelCase(...)`
 * `GUtil.toLowerCase(...)`
-* `ConfigureUtil.configureByMap(...)`
+* `ConfigureUtil`
 
 [[ibm_semeru_should_not_be_used]]
 ==== Deprecated JVM vendor IBM Semeru
@@ -312,7 +1085,8 @@
 [[disabling_user_home_cache_cleanup]]
 ==== Deprecated org.gradle.cache.cleanup property
 
-The `org.gradle.cache.cleanup` property in `gradle.properties` under Gradle user home has been deprecated.  Please use the link:https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home:configure_cache_cleanup[cache cleanup DSL] instead to disable or modify the cleanup configuration.
+The `org.gradle.cache.cleanup` property in `gradle.properties` under Gradle User Home has been deprecated.
+Please use the <<directory_layout#dir:gradle_user_home:configure_cache_cleanup,cache cleanup DSL>> instead to disable or modify the cleanup configuration.
 
 Since the `org.gradle.cache.cleanup` property may still be needed for older versions of Gradle, this property may still be present and no deprecation warnings will be printed as long as it is also configured via the DSL.
 The DSL value will always take preference over the `org.gradle.cache.cleanup` property.
@@ -366,6 +1140,24 @@
 =====
 ====
 
+[no_relative_paths_for_java_executables]
+==== Deprecated using relative paths to specify Java executables
+Using relative file paths to point to Java executables is now deprecated and will become an error in Gradle 9.
+This is done to reduce confusion about what such relative paths should resolve against.
+
+[[task_convention]]
+==== Calling `Task.getConvention()`, `Task.getExtensions()` from a task action
+
+Calling link:{javadocPath}/org/gradle/api/Task.html#getConvention--[Task.getConvention()], link:{javadocPath}/org/gradle/api/Task.html#getExtensions--[Task.getExtensions()] from a task action at execution time is now deprecated and will be made an error in Gradle 9.0.
+
+See the <<configuration_cache#config_cache:requirements:disallowed_types,configuration cache chapter>> for details on how to migrate these usages to APIs that are supported by the configuration cache.
+
+[[test_task_fail_on_no_test_executed]]
+==== Deprecated running test task successfully when no test executed
+Running the `Test` task successfully when no test was executed is now deprecated and will become an error in Gradle 9.
+Note that it is not an error when no test sources are present, in this case the `test` task is simply skipped. It is only an error when test sources are present, but no test was selected for execution.
+This is changed to avoid accidental successful test runs due to erroneous configuration.
+
 === Changes in the IDE integration
 
 [[kotlin_dsl_plugins_catalogs_workaround]]
@@ -380,4 +1172,3 @@
 After upgrading Gradle to 8.1 you will need to clear the IDE caches and restart.
 
 Also see <<upgrading_version_8.adoc#kotlin_dsl_deprecated_catalogs_plugins_block, the deprecated usages of version catalogs in the Kotlin DSL `plugins {}` block>> above.
-
diff --git a/subprojects/docs/src/docs/userguide/native/native_software.adoc b/subprojects/docs/src/docs/userguide/native/native_software.adoc
index 2a73a5c..40df0a6 100644
--- a/subprojects/docs/src/docs/userguide/native/native_software.adoc
+++ b/subprojects/docs/src/docs/userguide/native/native_software.adoc
@@ -284,6 +284,9 @@
 include::{snippetsPath}/native-binaries/cunit/tests/buildDependentComponentsReport.out[]
 ----
 
+Furthermore, the `--non-binaries` option shows non-buildable binaries in the report, `--no-non-buildable` hides them.
+Similarly, the `--test-suites` option shows test suites and `--no-test-suites` hides them.
+The option `--no-all` hides both non-buildable binaries and test suites from the report.
 
 [[sec:assemble_dependents]]
 === Assembling dependents
diff --git a/subprojects/docs/src/docs/userguide/reference/ci-systems/github-actions.adoc b/subprojects/docs/src/docs/userguide/reference/ci-systems/github-actions.adoc
index 8860bb1..0da3051 100644
--- a/subprojects/docs/src/docs/userguide/reference/ci-systems/github-actions.adoc
+++ b/subprojects/docs/src/docs/userguide/reference/ci-systems/github-actions.adoc
@@ -65,7 +65,7 @@
 To do so, add the following content to the top of your `settings.gradle[.kts]` file. The "CI" environment variable is set by GitHub Actions:
 ```
 plugins {
-    id("com.gradle.enterprise") version("3.9")
+    id("com.gradle.enterprise") version("3.13.2")
 }
 
 gradleEnterprise {
@@ -129,7 +129,7 @@
     runs-on: ubuntu-latest
     steps:
     - name: Checkout project sources
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
     - name: Setup Gradle
       uses: gradle/gradle-build-action@v2
     - name: Run build with Gradle Wrapper
diff --git a/subprojects/docs/src/docs/userguide/reference/command_line_interface.adoc b/subprojects/docs/src/docs/userguide/reference/command_line_interface.adoc
index 4248b66..1a1d2f0 100644
--- a/subprojects/docs/src/docs/userguide/reference/command_line_interface.adoc
+++ b/subprojects/docs/src/docs/userguide/reference/command_line_interface.adoc
@@ -293,6 +293,8 @@
 $ gradle tasks --all
 ----
 
+The option `--no-all` can be used to limit the report to tasks assigned to a task group.
+
 If you need to be more precise, you can display only the tasks from a specific group using the `--group` option.
 
 ----
@@ -312,6 +314,8 @@
 
 This information includes the full task path, the task type, possible <<sec:task_options,task-specific command line options>> and the description of the given task.
 
+Additionally, you can get detailed information about the task class types using the `--types` option, or use `--no-types` to hide this information.
+
 === Reporting dependencies
 
 Build scans give a full, visual report of what dependencies exist on which configurations, transitive dependencies, and dependency version selection.
@@ -621,7 +625,7 @@
 Specifies the settings file. For example: `gradle --settings-file=somewhere/else/settings.gradle`
 
 `-g`, `--gradle-user-home`::
-Specifies the Gradle user home directory. The default is the `.gradle` directory in the user's home directory.
+Specifies the Gradle User Home directory. The default is the `.gradle` directory in the user's home directory.
 
 `-p`, `--project-dir`::
 Specifies the start directory for Gradle. Defaults to current directory.
diff --git a/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc b/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc
index e9a1a71..7757dee 100644
--- a/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc
+++ b/subprojects/docs/src/docs/userguide/reference/directory_layout.adoc
@@ -20,9 +20,9 @@
 
 
 [[dir:gradle_user_home]]
-== Gradle user home directory
+== Gradle User Home directory
 
-The Gradle user home directory (`<home directory of the current user>/.gradle` by default) is used to store global configuration properties and initialization scripts as well as caches and log files.
+The Gradle User Home (`<home directory of the current user>/.gradle` by default) is used to store global configuration properties and initialization scripts as well as caches and log files.
 It is roughly structured as follows:
 
 [listing]
@@ -94,7 +94,7 @@
 The frequency at which cache cleanup is invoked is also configurable.  There are three possible settings:
 
 - *DEFAULT:* Cleanup is performed periodically in the background (currently once every 24 hours).
-- *DISABLED:* Never cleanup Gradle user home.  This is useful in cases where Gradle user home is ephemeral or it's desirable to delay cleanup until an explicit point in the future.
+- *DISABLED:* Never cleanup Gradle User Home.  This is useful in cases where Gradle User Home is ephemeral or it's desirable to delay cleanup until an explicit point in the future.
 - *ALWAYS:* Cleanup is performed at the end of each build session.  This is useful in cases where it's desirable to ensure that cleanup has actually occurred before proceeding.  However, this does perform cache cleanup during the build (rather than in the background) which can be an expensive operation, so this option should only be used when absolutely necessary.
 
 .Disabling cache cleanup
@@ -103,22 +103,22 @@
 include::sample[dir="snippets/initScripts/disableCacheCleanup/kotlin",files="gradleUserHome/init.d/cache-settings.gradle.kts"]
 ====
 
-Note that cache cleanup settings can only be configured via init scripts and should be placed under the `init.d` directory in Gradle user home.  This effectively couples the configuration of cache cleanup to the Gradle user home directory those settings apply to and limits the possibility of different conflicting settings from being applied to the same directory.
+Note that cache cleanup settings can only be configured via init scripts and should be placed under the `init.d` directory in Gradle User Home.  This effectively couples the configuration of cache cleanup to the Gradle User Home those settings apply to and limits the possibility of different conflicting settings from different projects being applied to the same directory.
 
 [[dir:gradle_user_home:multi_version_cache_cleanup]]
-==== Sharing a Gradle user home directory between multiple versions of Gradle
+==== Sharing a Gradle User Home between multiple versions of Gradle
 
-It is common to share a single Gradle user home directory between multiple versions of Gradle.
-As stated above, there are caches in Gradle user home that are version-specific.
+It is common to share a single Gradle User Home between multiple versions of Gradle.
+As stated above, there are caches in Gradle User Home that are version-specific.
 In general, different versions of Gradle will perform maintenance on only the version-specific caches associated with each version.
 On the other hand, there are other caches that are shared between versions (e.g. the dependency artifact cache or the artifact transform cache).
 Beginning with Gradle version 8.0, the cache cleanup settings can be configured to custom retention periods.  However, older versions have retention periods that are fixed (7 or 30 days depending on the cache) and so the shared caches could be accessed by versions of Gradle with different settings for the retention of cache artifacts.  This means that:
 
-- If the retention period is _not_ customized, all versions that perform cleanup will have the same retention periods and there will be no effect due to sharing a Gradle user home directory with multiple versions.
+- If the retention period is _not_ customized, all versions that perform cleanup will have the same retention periods and there will be no effect due to sharing a Gradle User Home with multiple versions.
 - If the retention period is customized for Gradle versions greater than or equal to version 8.0 to use retention periods _shorter_ than the previously fixed periods, there will also be no effect.  The versions of Gradle aware of these settings will cleanup artifacts earlier than the previously fixed retention periods and older versions will effectively not participate in the cleanup of shared caches.
-- If the retention period is customized for Gradle versions greater than or equal to version 8.0 to use retention periods _longer_ than the previously fixed periods, the older versions of Gradle may clean the shared caches earlier than what is configured.  In this case, if it is desirable to maintain these shared cache entries for newer versions for the longer retention periods, they will not be able to share a Gradle user home directory with older versions and will need to use a separate directory.
+- If the retention period is customized for Gradle versions greater than or equal to version 8.0 to use retention periods _longer_ than the previously fixed periods, the older versions of Gradle may clean the shared caches earlier than what is configured.  In this case, if it is desirable to maintain these shared cache entries for newer versions for the longer retention periods, they will not be able to share a Gradle User Home with older versions and will need to use a separate directory.
 
-Another consideration when sharing the Gradle user home directory with versions of Gradle before version 8.0 is that the DSL elements to configure the cache retention settings are not available in earlier versions, so this must be accounted for in any init script that is shared between versions.
+Another consideration when sharing the Gradle User Home with versions of Gradle before version 8.0 is that the DSL elements to configure the cache retention settings are not available in earlier versions, so this must be accounted for in any init script that is shared between versions.
 This can easily be handled by conditionally applying a version-compliant script.
 Note that the version-compliant script should reside somewhere other than the `init.d` directory (such as a sub-directory) so that it is not automatically applied.
 
@@ -133,6 +133,26 @@
 include::sample[dir="snippets/initScripts/multiVersionCacheRetention/kotlin",files="gradleUserHome/init.d/gradle8/cache-settings.gradle.kts"]
 ====
 
+[[dir:gradle_user_home:cache_marking]]
+=== Cache marking
+Beginning with Gradle version 8.1, Gradle supports marking caches with a `CACHEDIR.TAG` file.
+It follows the format described in https://bford.info/cachedir/[the Cache Directory Tagging Specification].
+The purpose of this file is to allow tools to identify the directories that do not need to be searched or backed up.
+By default, the directories `caches`, `wrapper/dists`, `daemon`, and `jdks` in the Gradle User Home are marked with this file.
+
+[[dir:gradle_user_home:configure_cache_marking]]
+==== Configuring cache marking
+
+The cache marking feature can be configured via an init script in the Gradle User Home:
+
+.Configuring cache marking
+====
+include::sample[dir="snippets/initScripts/cacheMarking/groovy",files="gradleUserHome/init.d/cache-settings.gradle"]
+include::sample[dir="snippets/initScripts/cacheMarking/kotlin",files="gradleUserHome/init.d/cache-settings.gradle.kts"]
+====
+
+Note that cache marking settings can only be configured via init scripts and should be placed under the `init.d` directory in Gradle User Home. This effectively couples the configuration of cache marking to the Gradle User Home those settings apply to and limits the possibility of different conflicting settings from different projects being applied to the same directory.
+
 [[dir:project_root]]
 == Project root directory
 
diff --git a/subprojects/docs/src/docs/userguide/reference/gradle_wrapper.adoc b/subprojects/docs/src/docs/userguide/reference/gradle_wrapper.adoc
index b554295..fba9e4b 100644
--- a/subprojects/docs/src/docs/userguide/reference/gradle_wrapper.adoc
+++ b/subprojects/docs/src/docs/userguide/reference/gradle_wrapper.adoc
@@ -64,6 +64,7 @@
 * The type of Gradle distribution. By default that’s the `-bin` distribution containing only the runtime but no sample code and documentation.
 * The Gradle version used for executing the build. By default the `wrapper` task picks the exact same Gradle version that was used to generate the Wrapper files.
 * Optionally, a timeout in ms used when downloading the gradle distribution.
+* Optionally, a boolean to set the validation of the distribution url.
 
 
 .Example: The generated distribution URL
@@ -77,6 +78,7 @@
 
 `--gradle-version`::
 The Gradle version used for downloading and executing the Wrapper.
+The resulting distribution url is validated before it is written to the properties file.
 +
 The following labels are allowed:
 +
@@ -94,6 +96,7 @@
 The full URL pointing to Gradle distribution ZIP file.
 Using this option makes `--gradle-version` and `--distribution-type` obsolete as the URL already contains this information.
 This option is extremely valuable if you want to host the Gradle distribution inside your company’s network.
+The url is validated before it is written to the properties file.
 
 `--gradle-distribution-sha256-sum`::
 The SHA256 hash sum used for <<#sec:verification,verifying the downloaded Gradle distribution>>.
@@ -101,6 +104,14 @@
 `--network-timeout`::
 The network timeout to use when downloading the gradle distribution, in ms. The default value is `10000`.
 
+`--no-validate-url`::
+Disables the validation of the configured distribution url.
+
+`--validate-url`::
+Enables the validation of the configured distribution url. Enabled by default.
+
+If the distribution url is configured with `--gradle-version` or `--gradle-distribution-url`, the url is validated by sending a HEAD request in the case of the `https` scheme or by checking the existence of the file in the case of the `file` scheme.
+
 Let’s assume the following use case to illustrate the use of the command line options. You would like to generate the Wrapper with version {gradleVersion} and use the `-all` distribution to enable your IDE to enable code-completion and being able to navigate to the Gradle source code. Those requirements are captured by the following command line execution:
 
 .Providing options to Wrapper task
diff --git a/subprojects/docs/src/docs/userguide/reference/third_party_integration.adoc b/subprojects/docs/src/docs/userguide/reference/third_party_integration.adoc
index 5495ebe..53f3dfc 100644
--- a/subprojects/docs/src/docs/userguide/reference/third_party_integration.adoc
+++ b/subprojects/docs/src/docs/userguide/reference/third_party_integration.adoc
@@ -23,7 +23,7 @@
 If you need to fine tune the import process so that the project loads correctly, you can use the <<idea_plugin.adoc#idea_plugin,IDEA Plugin for Gradle>>.
 
 NetBeans::
-Add the http://plugins.netbeans.org/plugin/44510/gradle-support[Gradle Support] plugin to NetBeans in order to import and run projects with Gradle builds.
+Built-in support for Gradle in https://netbeans.apache.org/[Apache NetBeans]
 
 Visual Studio::
 For developing C++ projects, Gradle comes with a <<visual_studio_plugin.adoc#visual_studio_plugin,Visual Studio plugin>>.
@@ -107,7 +107,11 @@
 include::sample[dir="snippets/toolingApi/runBuild/groovy",files="build.gradle[tags=use-tooling-api]"]
 ====
 
-The main entry point to the Tooling API is the link:{javadocPath}/org/gradle/tooling/GradleConnector.html[GradleConnector]. You can navigate from there to find code samples and explore the available Tooling API models. You can use link:https://docs.gradle.org/nightly/javadoc/org/gradle/tooling/GradleConnector.html#connect--[GradleConnector.connect()] to create a link:{javadocPath}/org/gradle/tooling/ProjectConnection.html[ProjectConnection]. A `ProjectConnection` connects to a single Gradle project. Using the connection you can execute tasks, tests and retrieve models relative to this project.
+The main entry point to the Tooling API is the link:{javadocPath}/org/gradle/tooling/GradleConnector.html[GradleConnector].
+You can navigate from there to find code samples and explore the available Tooling API models.
+You can use link:{javadocPath}/org/gradle/tooling/GradleConnector.html#connect--[GradleConnector.connect()] to create a link:{javadocPath}/org/gradle/tooling/ProjectConnection.html[ProjectConnection].
+A `ProjectConnection` connects to a single Gradle project.
+Using the connection you can execute tasks, tests and retrieve models relative to this project.
 
 [[sec:embedding_compatibility]]
 === Compatibility of Java and Gradle versions
diff --git a/subprojects/docs/src/docs/userguide/running-builds/build_environment.adoc b/subprojects/docs/src/docs/userguide/running-builds/build_environment.adoc
index ce8bfcb..d09f4ce 100644
--- a/subprojects/docs/src/docs/userguide/running-builds/build_environment.adoc
+++ b/subprojects/docs/src/docs/userguide/running-builds/build_environment.adoc
@@ -41,7 +41,7 @@
 * `gradle.properties` in the project's directory, then its parent project's directory up to the build's root directory.
 * `gradle.properties` in Gradle installation directory.
 
-Note that the location of the Gradle user home may have been changed beforehand via the `-Dgradle.user.home` system property passed on the command line.
+Note that the location of the Gradle User Home may have been changed beforehand via the `-Dgradle.user.home` system property passed on the command line.
 
 The following properties can be used to configure the Gradle build environment:
 
@@ -53,6 +53,21 @@
 When set to true, individual input property hashes and the build cache key for each task are logged on the console. Learn more about <<build_cache.adoc#sec:task_output_caching, task output caching>>.
 _Default is `false`._
 
+`org.gradle.configuration-cache=(true,false)`::
+Enables <<configuration_cache.adoc#config_cache:usage:enable, configuration caching>>. Gradle will try to reuse the build configuration from previous builds.
+_Default is `false`._
+
+`org.gradle.configuration-cache.problems=(fail,warn)`::
+Configures how the configuration cache <<configuration_cache.adoc#config_cache:usage:ignore_problems, handles problems>>.
+Set to `warn` to report problems without failing the build.
+Set to `fail` to report problems and fail the build if there are any problems.
+_Default is `fail`._
+
+`org.gradle.configuration-cache.max-problems=(# of problems)`::
+
+Configures the <<configuration_cache.adoc#config_cache:usage:max_problems, maximum number of configuration cache problems>> allowed as warnings until Gradle fails the build.
+_Default is `512`._
+
 `org.gradle.configureondemand=(true,false)`::
 Enables incubating <<multi_project_configuration_and_execution.adoc#sec:configuration_on_demand, configuration on demand>>, where Gradle will attempt to configure only necessary projects.
 _Default is `false`._
@@ -101,7 +116,7 @@
 
 `org.gradle.jvmargs=(JVM arguments)`::
 Specifies the JVM arguments used for the Gradle Daemon. The setting is particularly useful for <<#sec:configuring_jvm_memory,configuring JVM memory settings>> for build performance. This does not affect the JVM settings for the Gradle client VM.
-_The default is `-Xmx512m "-XX:MaxMetaspaceSize=256m"`._
+_The default is `-Xmx512m "-XX:MaxMetaspaceSize=384m"`._
 
 `org.gradle.logging.level=(quiet,warn,lifecycle,info,debug)`::
 When set to quiet, warn, lifecycle, info, or debug, Gradle will use this log level. The values are not case sensitive. See <<logging.adoc#sec:choosing_a_log_level,Choosing a log level>>.
@@ -213,7 +228,7 @@
 `gradle.wrapperPassword=(mypassword)`::
 Specify password for downloading a Gradle distribution using the Gradle wrapper.
 `gradle.user.home=(path to directory)`::
-Specify the Gradle user home directory.
+Specify the Gradle User Home directory.
 `https.protocols`::
 Specify the supported TLS versions in a comma separated format. For example: `TLSv1.2,TLSv1.3`.
 
@@ -254,7 +269,7 @@
 Specifies JVM arguments to use when starting the Gradle client VM. The client VM only handles command line input/output, so it is rare that one would need to change its VM options.
 The actual build is run by the Gradle daemon, which is not affected by this environment variable.
 `<<directory_layout.adoc#dir:gradle_user_home,GRADLE_USER_HOME>>`::
-Specifies the Gradle user home directory (which defaults to `<home directory of the current user>/.gradle` if not set).
+Specifies the Gradle User Home directory (which defaults to `<home directory of the current user>/.gradle` if not set).
 `JAVA_HOME`::
 Specifies the JDK installation directory to use for the client VM. This VM is also used for the daemon, unless a different one is specified in a Gradle properties file with `org.gradle.java.home`.
 
@@ -339,7 +354,7 @@
 
 You can adjust JVM options for Gradle in the following ways:
 
-The `org.gradle.jvmargs` Gradle property controls the VM running the build. It defaults to `-Xmx512m "-XX:MaxMetaspaceSize=256m"`
+The `org.gradle.jvmargs` Gradle property controls the VM running the build. It defaults to `-Xmx512m "-XX:MaxMetaspaceSize=384m"`
 
 .Changing JVM settings for the build VM
 ====
diff --git a/subprojects/docs/src/docs/userguide/running-builds/configuration_cache.adoc b/subprojects/docs/src/docs/userguide/running-builds/configuration_cache.adoc
index 229c3d5..7e96894 100644
--- a/subprojects/docs/src/docs/userguide/running-builds/configuration_cache.adoc
+++ b/subprojects/docs/src/docs/userguide/running-builds/configuration_cache.adoc
@@ -7,17 +7,12 @@
 // ./gradlew :docs:docsTest --tests="ExemplarExternalSamplesFunctionalTest.snippet-configuration-cache-*"
 // ./gradlew :docs:docsTest --tests="ExemplarExternalSamplesFunctionalTest.snippet-value-providers-*"
 
-[NOTE]
-====
-The configuration cache is an <<feature_lifecycle.adoc#feature_lifecycle,incubating>> feature, and the details described here may change.
-====
-
 [[config_cache:intro]]
 == Introduction
 
 The configuration cache is a feature that significantly improves build performance by caching the result of the <<build_lifecycle#build_lifecycle,configuration phase>> and reusing this for subsequent builds.
 Using the configuration cache, Gradle can skip the configuration phase entirely when nothing that affects the build configuration, such as build scripts, has changed.
-Gradle also applies some performance improvements to task execution as well.
+Gradle also applies performance improvements to task execution as well.
 
 The configuration cache is conceptually similar to the <<build_cache#build_cache,build cache>>, but caches different information.
 The build cache takes care of caching the outputs and intermediate files of the build, such as task outputs or artifact transform outputs.
@@ -26,7 +21,7 @@
 
 [IMPORTANT]
 ====
-This feature is currently *_incubating_* and not enabled by default. This feature has the following limitations:
+This feature is currently not enabled by default. This feature has the following limitations:
 
 - The configuration cache does not support all <<configuration_cache#config_cache:plugins:core, core Gradle plugins>> and <<configuration_cache#config_cache:not_yet_implemented, features>>. Full support is a work in progress.
 - Your build and the plugins you depend on might require changes to fulfil the <<configuration_cache#config_cache:requirements, requirements>>.
@@ -50,7 +45,8 @@
 
 Following the configuration phase, Gradle writes a snapshot of the task graph to a new configuration cache entry, for later Gradle invocations.
 Gradle then loads the task graph from the configuration cache, so that it can apply optimizations to the tasks, and then runs the execution phase as normal.
-Generally, you will not see any build performance improvement the first time you run a particular set of tasks.
+Configuration time will still be spent the first time you run a particular set of tasks.
+However, you should see build performance improvement immediately because <<#config_cache:intro:performance_improvements,tasks will run in parallel>>.
 
 When you subsequently run Gradle with this same set of tasks, for example by running `gradlew check` again, Gradle will load the tasks and their configuration directly from the configuration cache and skip the configuration phase entirely.
 Before using a configuration cache entry, Gradle checks that none of the "build configuration inputs", such as build scripts, for the entry have changed.
@@ -85,6 +81,7 @@
 [[config_cache:in_action]]
 === Configuration caching in action
 
+// TODO-RC redo demo so it no longer shows as incubating
 image::configuration-cache/running-help.gif[]
 
 [[config_cache:usage]]
@@ -136,11 +133,11 @@
 ❯ gradle --configuration-cache
 ----
 
-You can also enable the cache persistently in a `gradle.properties` file using the `org.gradle.unsafe.configuration-cache` property:
+You can also enable the cache persistently in a `gradle.properties` file using the `org.gradle.configuration-cache` property:
 
 [source,properties]
 ----
-org.gradle.unsafe.configuration-cache=true
+org.gradle.configuration-cache=true
 ----
 
 If enabled in a `gradle.properties` file, you can override that setting and disable the cache at build time with the `no-configuration-cache` flag:
@@ -165,7 +162,7 @@
 
 [source,properties]
 ----
-org.gradle.unsafe.configuration-cache-problems=warn
+org.gradle.configuration-cache.problems=warn
 ----
 
 [[config_cache:usage:max_problems]]
@@ -176,14 +173,14 @@
 This can be adjusted by specifying an allowed maximum number of problems on the command line:
 
 ----
-❯ gradle -Dorg.gradle.unsafe.configuration-cache.max-problems=5
+❯ gradle -Dorg.gradle.configuration-cache.max-problems=5
 ----
 
 or in a `gradle.properties` file:
 
 [source,properties]
 ----
-org.gradle.unsafe.configuration-cache.max-problems=5
+org.gradle.configuration-cache.max-problems=5
 ----
 
 [[config_cache:usage:invalidate]]
@@ -207,7 +204,7 @@
 [[config_cache:stable]]
 == Stable configuration cache
 
-Working towards the stabilization of configuration caching we implement some strictness behind a feature flag when it is too disruptive for early adopters.
+Working towards the stabilization of configuration caching we implemented some strictness behind a feature flag when it was considered too disruptive for early adopters.
 
 You can enable that feature flag as follows:
 
@@ -221,8 +218,11 @@
 Undeclared shared build service usage::
 When enabled, tasks using a <<build_services#build_services, shared build service>> without declaring the requirement via the `Task.usesService` method will emit a deprecation warning.
 
-External processes used at configuration time::
-When enabled, external processes launched at configuration time are reported as problems unless done in accordance with the <<configuration_cache#config_cache:requirements:external_processes>> requirement.
+In addition, when the configuration cache is not enabled but the feature flag is present, deprecations for the following <<config_cache:requirements, configuration cache requirements>> are also enabled:
+
+* <<config_cache:requirements:build_listeners, Registering build listeners>>
+* <<config_cache:requirements:use_project_during_execution, Using the `Project` object at execution time>>
+* <<config_cache:requirements:task_extensions, Using task extensions and conventions at execution time>>
 
 It is recommended to enable it as soon as possible in order to be ready for when we remove the flag and make the linked features the default.
 
@@ -249,7 +249,7 @@
 For example to enable the configuration cache, turning problems into warnings, add the following:
 
 ----
--Dorg.gradle.unsafe.configuration-cache=true -Dorg.gradle.unsafe.configuration-cache-problems=warn
+-Dorg.gradle.configuration-cache=true -Dorg.gradle.configuration-cache.problems=warn
 ----
 
 You can also choose to only enable it for a given run configuration.
@@ -272,8 +272,8 @@
 You can use the properties described above as system properties.
 For example to enable the configuration cache, turning problems into warnings, add the following JVM arguments:
 
-* `-Dorg.gradle.unsafe.configuration-cache=true`
-* `-Dorg.gradle.unsafe.configuration-cache-problems=warn`
+* `-Dorg.gradle.configuration-cache=true`
+* `-Dorg.gradle.configuration-cache.problems=warn`
 
 To enable it for a given run configuration, go to `Run configurations...`, find the one you want to change, go to `Project Settings`, tick the `Override project settings` checkbox and add the same system properties as a `JVM argument`.
 
@@ -320,8 +320,8 @@
 link:{gradle-issues}13463[[.green]#✓#]:: <<application_plugin.adoc#application_plugin,Application>>
 link:{gradle-issues}13466[[.green]#✓#]:: <<war_plugin.adoc#war_plugin,WAR>>
 link:{gradle-issues}13467[[.red]#✖#]:: <<ear_plugin.adoc#ear_plugin,EAR>>
-link:{gradle-issues}13468[[.green]#✓#]:: <<publishing_maven.adoc#publishing_maven,Maven Publish>>
-link:{gradle-issues}13469[[.red]#✖#]:: <<publishing_ivy.adoc#publishing_ivy,Ivy Publish>>
+link:{gradle-issues}24329[[.yellow]#⚠#]:: <<publishing_maven.adoc#publishing_maven,Maven Publish>>
+link:{gradle-issues}24328[[.yellow]#⚠#]:: <<publishing_ivy.adoc#publishing_ivy,Ivy Publish>>
 link:{gradle-issues}13464[[.green]#✓#]:: <<distribution_plugin.adoc#distribution_plugin,Distribution>>
 link:{gradle-issues}13465[[.green]#✓#]:: <<java_library_distribution_plugin.adoc#java_library_distribution_plugin,Java Library Distribution>>
 
@@ -349,11 +349,11 @@
 [horizontal]
 link:{gradle-issues}13455[[.green]#✓#]:: <<base_plugin.adoc#base_plugin,Base>>
 link:{gradle-issues}13456[[.green]#✓#]:: <<build_init_plugin.adoc#build_init_plugin,Build Init>>
-link:{gradle-issues}13470[[.red]#✖#]:: <<signing_plugin.adoc#signing_plugin,Signing>>
-link:{gradle-issues}13471[[.green]#✓#]:: <<java_gradle_plugin.adoc#java_gradle_plugin,Java Plugin Development>>
+link:{gradle-issues}13470[[.green]#✓#]:: <<signing_plugin.adoc#signing_plugin,Signing>>
+link:{gradle-issues}24537[[.green]#✓#]:: <<java_gradle_plugin.adoc#java_gradle_plugin,Java Plugin Development>>
 link:{gradle-issues}23029[[.green]#✓#]:: <<custom_plugins.adoc#sec:precompiled_plugins,Groovy DSL Plugin Development>>
 link:{gradle-issues}13472[[.green]#✓#]:: <<kotlin_dsl.adoc#sec:kotlin-dsl_plugin,Kotlin DSL Plugin Development>>
-link:{gradle-issues}13473[[.red]#✖#]:: <<project_report_plugin.adoc#project_report_plugin,Project Report Plugin>>
+link:{gradle-issues}13473[[.green]#✓#]:: <<project_report_plugin.adoc#project_report_plugin,Project Report Plugin>>
 
 |===
 
@@ -837,6 +837,12 @@
 
 Note that this requirement makes it unsupported to write tasks that configure other tasks at execution time.
 
+[[config_cache:requirements:task_extensions]]
+=== Accessing task extensions or conventions
+
+Tasks should not access conventions and extensions, including extra properties, at execution time.
+Instead, any value that's relevant for the execution of the task should be modeled as a task property.
+
 [[config_cache:requirements:build_listeners]]
 === Using build listeners
 
@@ -845,6 +851,7 @@
 For example a `BuildListener` or a `TaskExecutionListener`.
 
 These should be replaced by <<build_services#build_services,build services>>, registered to receive information about <<build_services#operation_listener, task execution>> if needed.
+Use <<dataflow_actions.adoc#dataflow_action,dataflow actions>> to handle the build result instead of `buildFinished` listeners.
 
 [[config_cache:requirements:external_processes]]
 === Running external processes
@@ -966,13 +973,17 @@
 [[config_cache:not_yet_implemented:secrets]]
 === Handling of credentials and secrets
 
-The configuration cache has currently no option to hide secrets that are used as inputs.
-It means that they end up in the serialized configuration cache entry.
+The configuration cache has currently no option to prevent storing secrets that are used as inputs, and so they might end up in the serialized configuration cache entry which, by default, is stored under `.gradle/configuration-cache` in your project directory.
 
-This means that you should:
+To mitigate the risk of accidental exposure, Gradle encrypts the configuration cache.
+Gradle transparently generates a machine-specific secret key as required, caches it under the
+`<<directory_layout.adoc#dir:gradle_user_home,GRADLE_USER_HOME>>` directory and uses it to encrypt the data in the project specific caches.
 
-* Either secure access to configuration cache entries that may contain secrets
-* Or leverage `<<directory_layout.adoc#dir:gradle_user_home,GRADLE_USER_HOME>>/gradle.properties` for storing secrets.
+
+To enhance security further, make sure to:
+
+* secure access to configuration cache entries;
+* leverage `<<directory_layout.adoc#dir:gradle_user_home,GRADLE_USER_HOME>>/gradle.properties` for storing secrets.
 The content of that file is not part of the configuration cache, only its fingerprint.
 If you store secrets in that file, care must be taken to protect access to the file content.
 
@@ -987,14 +998,6 @@
 
 See link:{gradle-issues}13510[gradle/gradle#13510].
 
-[[config_cache:not_implemented:store_parallel_execution]]
-=== Parallel task execution on cache misses
-
-When there's no configuration cache available and the work graph needs to be calculated, tasks are not executed in parallel by default.
-Only builds that reuse the configuration cache currently execute all tasks in parallel by default.
-
-See link:{gradle-issues}14624[gradle/gradle#14624].
-
 [[config_cache:not_yet_implemented:source_dependencies]]
 === Source dependencies
 
@@ -1041,21 +1044,6 @@
 
 See link:{gradle-issues}13588[gradle/gradle#13588].
 
-[[config_cache:not_yet_implemented:storing_lambdas]]
-=== Objects Storing Lambdas
-
-Currently, Gradle cannot restore objects from the configuration cache that store user-provided lambdas.
-For example, if an object provides a method that accepts a Single Abstract Method (SAM) interface argument (e.g. a `Function` argument), it is natural to implement this argument as a lambda.
-If the lambda is then stored in the object for later invocation, the object will not be compatible with the configuration cache and will cause a failure when restored.
-Instead of a lambda, the argument should be provided as a typed object implementing the SAM interface.
-
-Alternatively, for Kotlin implementations, the Kotlin compiler can be configured to generate classes instead of lambdas during SAM conversion.
-
-====
-include::sample[dir="snippets/configurationCache/samConversion/kotlin",files="build.gradle.kts[tags=configure]"]
-include::sample[dir="snippets/configurationCache/samConversion/groovy",files="build.gradle[tags=configure]"]
-====
-
 [[config_cache:not_yet_implemented:accessing_top_level_at_execution]]
 === Accessing top-level methods and variables of a build script at execution time
 
@@ -1094,3 +1082,13 @@
 <2> Define the variable in a smaller scope.
 
 See link:{gradle-issues}22879[gradle/gradle#22879].
+
+[[config_cache:not_yet_implemented:build_services_in_fingerprint]]
+=== Using build services to invalidate the configuration cache
+
+Currently, it is impossible to use a `BuildServiceProvider` or provider derived from it with `map` or `flatMap` as a parameter for the `ValueSource`, if the value of the `ValueSource` is accessed at configuration time.
+The same applies when such a `ValueSource` is obtained in a task that executes as part of the configuration phase, for example tasks of the `buildSrc` build or included builds contributing plugins.
+Note that using a `@ServiceReference` or storing `BuildServiceProvider` in an `@Internal`-annotated property of a task is safe.
+Generally speaking, this limitation makes it impossible to use a `BuildService` to invalidate the configuration cache.
+
+See link:{gradle-issues}24085[gradle/gradle#24085].
diff --git a/subprojects/docs/src/docs/userguide/running-builds/file_system_watching.adoc b/subprojects/docs/src/docs/userguide/running-builds/file_system_watching.adoc
index cf36dda..31967c4 100644
--- a/subprojects/docs/src/docs/userguide/running-builds/file_system_watching.adoc
+++ b/subprojects/docs/src/docs/userguide/running-builds/file_system_watching.adoc
@@ -88,7 +88,7 @@
 ----
 ====
 
-Or configure the property in the `gradle.properties` file in the project root or your Gradle user home:
+Or configure the property in the `gradle.properties` file in the project root or your Gradle User Home:
 
 ====
 .gradle.properties
diff --git a/subprojects/docs/src/docs/userguide/running-builds/gradle_daemon.adoc b/subprojects/docs/src/docs/userguide/running-builds/gradle_daemon.adoc
index 2fbd3ab..6c10600 100644
--- a/subprojects/docs/src/docs/userguide/running-builds/gradle_daemon.adoc
+++ b/subprojects/docs/src/docs/userguide/running-builds/gradle_daemon.adoc
@@ -74,7 +74,7 @@
 This flag overrides any settings that disable the Daemon in your project or user `gradle.properties` files.
 
 To enable the Daemon by default in older Gradle versions, add the following setting to the
-`gradle.properties` file in the project root or your Gradle user home:
+`gradle.properties` file in the project root or your Gradle User Home:
 
 ====
 .gradle.properties
diff --git a/subprojects/docs/src/docs/userguide/running-builds/intro_multi_project_builds.adoc b/subprojects/docs/src/docs/userguide/running-builds/intro_multi_project_builds.adoc
index a073660..fcade45 100644
--- a/subprojects/docs/src/docs/userguide/running-builds/intro_multi_project_builds.adoc
+++ b/subprojects/docs/src/docs/userguide/running-builds/intro_multi_project_builds.adoc
@@ -21,6 +21,17 @@
 
 For details about authoring _multi-project_ builds, consult the <<multi_project_builds#multi_project_builds,Authoring Multi-Project Builds section of the user manual>>.
 
+[[sec:project_path]]
+== Project path
+
+A project path has the following pattern: it starts with an optional colon, which denotes the root project.
+The root project, `:`, is the only project in a path that is not specified by its name.
+The rest of a project path is a colon-separated sequence of project names, where the next project is a subproject of the previous project.
+You can see the project paths when running `gradle projects` as shown in <<sec:identifying_project_structure,identifying project structure>> section.
+
+In most cases project paths reflect filesystem layout, but there are exceptions.
+Most notable one is for composite builds.
+See also how to declare <<declaring_dependencies_between_subprojects#declaring_dependencies_between_subprojects, dependencies between subprojects>> with `include` and <<composite_builds#defining_composite_builds,create composite builds>> with `includeBuild`.
 
 [[sec:identifying_project_structure]]
 == Identifying project structure
@@ -71,12 +82,7 @@
 
 You can use a task's fully qualified name to execute a specific task in a specific subproject.
 For example: `gradle :services:webservice:build` will run the `build` task of the _webservice_ subproject.
-The fully qualified name of a task is simply its project path plus the task name.
-
-A project path has the following pattern: it starts with an optional colon, which denotes the root project.
-The root project is the only project in a path that is not specified by its name.
-The rest of a project path is a colon-separated sequence of project names, where the next project is a subproject of the previous project.
-You can see the project paths when running `gradle projects` as shown in <<sec:identifying_project_structure,identifying project structure>> section.
+The fully qualified name of a task is simply its <<sec:project_path,project path>> plus the task name.
 
 This approach works for any task, so if you want to know what tasks are in a particular subproject,
 just use the `tasks` task, e.g. `gradle :services:webservice:tasks`.
diff --git a/subprojects/docs/src/docs/userguide/running-builds/performance.adoc b/subprojects/docs/src/docs/userguide/running-builds/performance.adoc
index 7145c58..67af762 100644
--- a/subprojects/docs/src/docs/userguide/running-builds/performance.adoc
+++ b/subprojects/docs/src/docs/userguide/running-builds/performance.adoc
@@ -80,7 +80,7 @@
 Parallel builds can significantly improve build times; how much depends on your project structure and
 how many dependencies you have between subprojects.
 A build whose execution time is dominated by a single subproject won't benefit much at all.
-Niether will a project with lots of inter-subproject dependencies.
+Neither will a project with lots of inter-subproject dependencies.
 But most multi-subproject builds see a reduction in build times.
 
 For more information about parallel builds, check out the
diff --git a/subprojects/docs/src/docs/userguide/troubleshooting/validation_problems.adoc b/subprojects/docs/src/docs/userguide/troubleshooting/validation_problems.adoc
index 1aafca9..73fb434 100644
--- a/subprojects/docs/src/docs/userguide/troubleshooting/validation_problems.adoc
+++ b/subprojects/docs/src/docs/userguide/troubleshooting/validation_problems.adoc
@@ -389,3 +389,12 @@
 Please find below the list of unsupported value type and links to respective sections of the user manual:
 
 `ResolvedArtifactResult`:: <<incremental_build#sec:task_input_using_dependency_resolution_results, Using dependency resolution results as task inputs>>
+
+[[unsupported_key_type_of_nested_map]]
+== Unsupported key type of nested map
+
+This error indicates that a nested map declares a key of an unsupported type.
+Gradle uses the key to generate a name for the (sub)property.
+Only allowing keys of type `Enum`, `Integer`, and `String` guarantees that these names are unique and well-formed, and is preferable to relying on `toString()` for producing such names.
+
+To fix this problem, change the type of the key to `Enum`, `Integer`, or `String`.
diff --git a/subprojects/docs/src/docs/userguide/what_is_gradle.adoc b/subprojects/docs/src/docs/userguide/what_is_gradle.adoc
index 61fd8e8a..7bac50f 100644
--- a/subprojects/docs/src/docs/userguide/what_is_gradle.adoc
+++ b/subprojects/docs/src/docs/userguide/what_is_gradle.adoc
@@ -16,86 +16,22 @@
 [[what_is_gradle]]
 = What is Gradle?
 
-Gradle is an open-source https://en.wikipedia.org/wiki/Build_automation[build automation] tool flexible enough to build almost any type of software.
-Gradle makes few assumptions about what you're trying to build or how to build it.
-This makes Gradle particularly flexible.
+== Gradle Build Tool
 
-== Design
+Gradle Build Tool is a fast, dependable, and adaptable open-source https://en.wikipedia.org/wiki/Build_automation[build automation] tool with an elegant and extensible declarative build language.
 
-Gradle bases its design on the following fundamentals:
+In this User Manual, Gradle Build Tool is called **Gradle**.
 
-High performance::
-Gradle avoids unnecessary work by only running tasks that need to do work because inputs or outputs have changed.
-Gradle uses various caches to reuse outputs from previous builds.
-With a shared build cache, you can even reuse outputs from other machines.
-JVM foundation::
-Gradle runs on the JVM. This is a bonus for users familiar with Java, since build logic can use the standard Java APIs.
-It also makes it easy to run Gradle on different platforms.
-Conventions::
-Gradle makes common types of projects easy to build through conventions. Plugins set sensible defaults to keep build scripts minimal.
-But these conventions don't limit you: you can configure settings, add your own tasks, and make many other customizations in your builds.
-Extensibility::
-Most builds have special requirements that require custom build logic.
-You can readily extend Gradle to provide your own build logic with custom tasks and plugins.
-See Android builds for an example: they add many new build concepts such as flavors and build types.
-IDE support::
-Several major IDEs provide interaction with Gradle builds, including Android Studio, IntelliJ IDEA, Eclipse, VSCode, and NetBeans.
-Gradle can also generate the solution files required to load a project into Visual Studio.
-Insight::
-https://scans.gradle.com/[Build Scan™] provides extensive information about a build that you can use to identify issues.
-You can use Build Scans to identify problems with a build's performance and even share them for debugging help.
+== Why Gradle?
 
-== Terminology
+Gradle is a widely used and mature built tool with an active community and a strong developer ecosystem. So why Gradle?
 
-It's helpful to know the following terminology before you dive into the details of Gradle.
+* Gradle is the most popular build system for JVM and the default in Android and Kotlin Multi-Platform with a rich community plugin ecosystem.
+* Gradle can automate a wide range of software build automation scenarios, either through built-in functionality, plugins, or custom build logic.
+* Gradle provides a high-level, declarative, and expressive build language that makes it easy to both read and write build logic.
+* Gradle is fast, scalable, and can build projects of any size and complexity.
+* Gradle produces dependable results, even with optimizations such as incremental builds, build caching, or parallel execution.
 
-=== Projects
+Several major IDEs support Gradle, including Android Studio, IntelliJ IDEA, Visual Studio Code, Eclipse, and NetBeans.
 
-Projects are the things that Gradle builds.
-Projects contain a build script, which is a file located in the project's root directory usually named `build.gradle` or `build.gradle.kts`.
-Builds scripts define tasks, dependencies, plugins, and other configuration for that project.
-A single build can contain one or more projects and each project can contain their own subprojects.
-
-=== Tasks
-
-Tasks contain the logic for executing some work--compiling code, running tests or deploying software.
-In most use cases, you'll use existing tasks.
-Gradle provides tasks that implement many common build system needs, like the built-in Java `Test` task that can run tests.
-Plugins provide even more types of tasks.
-
-Tasks themselves consist of:
-
- * Actions: pieces of work that do something, like copy files or compile source
- * Inputs: values, files and directories that the actions use or operate on
- * Outputs: files and directories that the actions modify or generate
-
-=== Plugins
-
-Plugins allow you to introduce new concepts into a build beyond tasks, files and dependency configurations.
-For example, most language plugins add the concept of <<building_java_projects#sec:java_source_sets,source sets>> to a build.
-
-Plugins provide a means of reusing logic and configuration across multiple projects.
-With plugins, you can write a task once and use it in multiple builds.
-Or you can store common configuration, like logging, dependencies, and version management, in one place.
-This reduces duplication in build scripts.
-Appropriately modeling build processes with plugins can greatly improve ease of use and efficiency.
-
-=== Build Phases
-
-Gradle evaluates and executes build scripts in three *build phases* of the <<build_lifecycle#build_lifecycle,Build Lifecycle>>:
-
-Initialization::
-Sets up the environment for the build and determine which projects will take part in it.
-
-Configuration::
-Constructs and configures the task graph for the build. Determines which tasks need to run and in which order, based on the task the user wants to run.
-
-Execution::
-Runs the tasks selected at the end of the configuration phase.
-
-=== Builds
-
-A build is an execution of a collection of tasks in a Gradle project.
-You run a build via the command line interface (CLI) or an IDE by specifying task selectors.
-Gradle configures the build and selects the tasks to run.
-Gradle runs the smallest complete set of tasks based on the requested tasks and their dependencies.
+Gradle provides a free service called https://scans.gradle.com/[Build Scan®] which provides extensive information about a build that you can use to identify problems or share for debugging help.
diff --git a/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/BaseSamplesTest.java b/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/BaseSamplesTest.java
index f07b8ff..856ebd8 100644
--- a/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/BaseSamplesTest.java
+++ b/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/BaseSamplesTest.java
@@ -30,6 +30,7 @@
 import org.gradle.integtests.fixtures.logging.GradleWelcomeOutputNormalizer;
 import org.gradle.integtests.fixtures.logging.NativeComponentReportOutputNormalizer;
 import org.gradle.integtests.fixtures.logging.SampleOutputNormalizer;
+import org.gradle.integtests.fixtures.logging.ToolchainDownloadOutputNormalizer;
 import org.gradle.integtests.fixtures.logging.ZincScalaCompilerOutputNormalizer;
 import org.gradle.integtests.fixtures.mirror.SetMirrorsSampleModifier;
 
@@ -45,7 +46,8 @@
     ConfigurationCacheOutputCleaner.class,
     ConfigurationCacheOutputNormalizer.class,
     EmbeddedKotlinOutputNormalizer.class,
-    ZincScalaCompilerOutputNormalizer.class
+    ZincScalaCompilerOutputNormalizer.class,
+    ToolchainDownloadOutputNormalizer.class
 })
 @SampleModifiers({
     SetMirrorsSampleModifier.class,
diff --git a/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/IntegrationTestSamplesRunner.java b/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/IntegrationTestSamplesRunner.java
index 846c88c..bf1d555 100644
--- a/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/IntegrationTestSamplesRunner.java
+++ b/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/IntegrationTestSamplesRunner.java
@@ -15,10 +15,14 @@
  */
 package org.gradle.docs.samples;
 
+import org.gradle.api.GradleException;
 import org.gradle.exemplar.executor.CommandExecutor;
 import org.gradle.exemplar.executor.ExecutionMetadata;
 import org.gradle.exemplar.model.Command;
+import org.gradle.exemplar.model.Sample;
 import org.gradle.exemplar.test.runner.SamplesRunner;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
 import org.junit.runners.model.InitializationError;
 
 import javax.annotation.Nullable;
@@ -46,4 +50,22 @@ protected File getImplicitSamplesRootDir() {
         }
         return Paths.get(samplesDir).toFile();
     }
+
+    @Override
+    protected void runChild(final Sample sample, final RunNotifier notifier) {
+        super.runChild(sample, new RunNotifierWrapper(notifier) {
+            @Override
+            public void fireTestFailure(Failure failure) {
+                String extraParameter = "configCache".equals(System.getProperty("org.gradle.integtest.executer")) ?
+                    "-PenableConfigurationCacheForDocsTests=true" : "";
+                super.fireTestFailure(new Failure(failure.getDescription(), new GradleException(
+                    "Sample test run failed.\nTo understand how docsTest works, See:\n" +
+                        "  https://github.com/gradle/gradle/blob/master/subprojects/docs/README.md#testing-docs\n" +
+                        "To reproduce this failure, run:\n" +
+                        "  ./gradlew docs:docsTest --tests '*" + sample.getId() + "*' " + extraParameter,
+                    failure.getException()
+                )));
+            }
+        });
+    }
 }
diff --git a/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/IntegrationTestSamplesRunnerTest.java b/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/IntegrationTestSamplesRunnerTest.java
new file mode 100644
index 0000000..e76aedd
--- /dev/null
+++ b/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/IntegrationTestSamplesRunnerTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.docs.samples;
+
+import org.gradle.exemplar.model.Command;
+import org.gradle.exemplar.model.Sample;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.JUnit4;
+import org.junit.runners.model.InitializationError;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Collections.emptyList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(JUnit4.class)
+public class IntegrationTestSamplesRunnerTest {
+    @Test
+    public void canAddReproductionInstructionsUponFailure() throws InitializationError {
+        IntegrationTestSamplesRunner runner = new IntegrationTestSamplesRunner(Sample.class);
+        RunNotifier notifier = new RunNotifier();
+        List<Failure> capturedFailures = new ArrayList<>();
+        Sample errorThrowingSample = new Sample("test-sample", new File(""), emptyList()) {
+            @Override
+            public List<Command> getCommands() {
+                throw new RuntimeException("I am a test failure!");
+            }
+        };
+
+        notifier.addListener(new RunListener() {
+            @Override
+            public void testFailure(Failure failure) throws Exception {
+                capturedFailures.add(failure);
+            }
+        });
+
+        runner.runChild(errorThrowingSample, notifier);
+        assertEquals(1, capturedFailures.size());
+        assertTrue(capturedFailures.get(0).getException().getMessage().contains("To reproduce this failure, run:\n  ./gradlew docs:docsTest --tests '*test-sample*' "));
+    }
+}
diff --git a/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/RunNotifierWrapper.java b/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/RunNotifierWrapper.java
new file mode 100644
index 0000000..2ac6b95
--- /dev/null
+++ b/subprojects/docs/src/docsTest/java/org/gradle/docs/samples/RunNotifierWrapper.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 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.docs.samples;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runner.notification.StoppedByUserException;
+
+public class RunNotifierWrapper extends RunNotifier {
+    private final RunNotifier notifier;
+
+    public RunNotifierWrapper(RunNotifier notifier) {
+        this.notifier = notifier;
+    }
+
+    @Override
+    public void fireTestSuiteStarted(Description description) {
+        notifier.fireTestSuiteStarted(description);
+    }
+
+    @Override
+    public void fireTestSuiteFinished(Description description) {
+        notifier.fireTestSuiteFinished(description);
+    }
+
+    @Override
+    public void fireTestStarted(Description description) throws StoppedByUserException {
+        notifier.fireTestStarted(description);
+    }
+
+    @Override
+    public void fireTestFailure(Failure failure) {
+        notifier.fireTestFailure(failure);
+    }
+
+    @Override
+    public void fireTestAssumptionFailed(Failure failure) {
+        notifier.fireTestAssumptionFailed(failure);
+    }
+
+    @Override
+    public void fireTestIgnored(Description description) {
+        notifier.fireTestIgnored(description);
+    }
+
+    @Override
+    public void fireTestFinished(Description description) {
+        notifier.fireTestFinished(description);
+    }
+
+    @Override
+    public void pleaseStop() {
+        notifier.pleaseStop();
+    }
+
+    @Override
+    public void addListener(RunListener listener) {
+        // Not meant to be called by a client
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeListener(RunListener listener) {
+        // Not meant to be called by a client
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void fireTestRunStarted(Description description) {
+        // Not meant to be called by a client
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void fireTestRunFinished(Result result) {
+        // Not meant to be called by a client
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void addFirstListener(RunListener listener) {
+        // Not meant to be called by a client
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/subprojects/docs/src/main/resources/header.html b/subprojects/docs/src/main/resources/header.html
index eae2c83..830a404 100644
--- a/subprojects/docs/src/main/resources/header.html
+++ b/subprojects/docs/src/main/resources/header.html
@@ -88,15 +88,16 @@
         <div class="search-container">
             <input type="search" name="q" id="search-input" class="search-input" placeholder="Search the docs"/>
         </div>
-        <h3 id="introduction">Introduction</h3>
+        <h3 id="overview">Overview</h3>
         <ul>
             <li><a href="../userguide/what_is_gradle.html">What is Gradle?</a></li>
-            <li><a href="https://gradle.com/">Gradle Enterprise</a></li>
+            <li><a href="../userguide/about_manual.html">The User Manual</a></li>
         </ul>
 
-        <h3 id="what-is-new">What is new?</h3>
+        <h3 id="what-is-new">Releases</h3>
         <ul>
             <li><a href="../release-notes.html">Release Notes</a></li>
+            <li><a href="../userguide/compatibility.html">Compatibility Notes</a></li>
             <li><a class="nav-dropdown" data-toggle="collapse" href="#upgrading-gradle" aria-expanded="false" aria-controls="upgrading-gradle">Upgrading Gradle</a>
                 <ul id="upgrading-gradle">
                     <li><a href="../userguide/upgrading_version_8.html">version 8.X to latest</a></li>
@@ -104,10 +105,15 @@
                     <li><a href="../userguide/upgrading_version_6.html">version 6.X to 7.0</a></li>
                     <li><a href="../userguide/upgrading_version_5.html">version 5.X to 6.0</a></li>
                     <li><a href="../userguide/upgrading_version_4.html">version 4.X to 5.0</a></li>
-                    <li><a href="../userguide/feature_lifecycle.html">Gradle's Feature Lifecycle</a></li>
                 </ul>
             </li>
-            <li><a href="../userguide/compatibility.html">Compatibility Notes</a></li>
+            <li><a class="nav-dropdown" data-toggle="collapse" href="#migrating-to-gradle" aria-expanded="false" aria-controls="migrating-to-gradle">Migrating to Gradle</a>
+                <ul id="migrating-to-gradle">
+                    <li><a href="../userguide/migrating_from_maven.html">from Maven</a></li>
+                    <li><a href="../userguide/migrating_from_ant.html">from Ant</a></li>
+                </ul>
+            </li>
+            <li><a href="../userguide/feature_lifecycle.html">Gradle's Feature Lifecycle</a></li>
         </ul>
 
         <h3 id="quick-start">Quick Start</h3>
@@ -115,22 +121,6 @@
             <li><a href="../userguide/getting_started.html">Getting Started</a></li>
             <li><a href="../userguide/installation.html">Installing Gradle</a></li>
             <li><a href="../samples/index.html">Samples</a></li>
-            <li><a class="nav-dropdown" data-toggle="collapse" href="#migrating-to-gradle" aria-expanded="false" aria-controls="migrating-to-gradle">Migrating to Gradle</a>
-                <ul id="migrating-to-gradle">
-                    <li><a href="../userguide/migrating_from_maven.html">from Maven</a></li>
-                    <li><a href="../userguide/migrating_from_ant.html">from Ant</a></li>
-                </ul>
-            </li>
-            <li><a class="nav-dropdown" data-toggle="collapse" href="#gradle-api" aria-expanded="false" aria-controls="gradle-api">Gradle DSLs and API</a>
-                <ul id="gradle-api">
-                    <li><a href="../javadoc/index.html?overview-summary.html">Javadoc</a></li>
-                    <li><a href="../userguide/groovy_build_script_primer.html">Groovy DSL Primer</a></li>
-                    <li><a href="../dsl/index.html">Groovy DSL Reference</a></li>
-                    <li><a href="../userguide/kotlin_dsl.html">Kotlin DSL Primer</a></li>
-                    <li><a href="https://gradle.github.io/kotlin-dsl-docs/api/" target="_blank">Kotlin DSL API</a></li>
-                    <li><a href="../userguide/migrating_from_groovy_to_kotlin_dsl.html">Groovy to Kotlin DSL Migration</a></li>
-                </ul>
-            </li>
             <li><a href="../userguide/troubleshooting.html">Troubleshooting Builds</a></li>
         </ul>
 
@@ -208,6 +198,7 @@
                 <ul id="other-developing-topics">
                     <li><a href="../userguide/custom_gradle_types.html">Writing Custom Gradle Types and Service Injection</a></li>
                     <li><a href="../userguide/build_services.html">Shared Build Services</a></li>
+                    <li><a href="../userguide/dataflow_actions.html">Dataflow Actions</a></li>
                     <li><a href="../userguide/test_kit.html">Testing a Build with TestKit</a></li>
                     <li><a href="../userguide/ant.html">Using Ant from Gradle</a></li>
                 </ul>
@@ -330,6 +321,16 @@
 
         <h3 id="reference">Reference</h3>
         <ul>
+            <li><a class="nav-dropdown" data-toggle="collapse" href="#gradle-api" aria-expanded="false" aria-controls="gradle-api">Gradle DSLs and API</a>
+                <ul id="gradle-api">
+                    <li><a href="../javadoc/index.html?overview-summary.html">Javadoc</a></li>
+                    <li><a href="../userguide/groovy_build_script_primer.html">Groovy DSL Primer</a></li>
+                    <li><a href="../dsl/index.html">Groovy DSL Reference</a></li>
+                    <li><a href="../userguide/kotlin_dsl.html">Kotlin DSL Primer</a></li>
+                    <li><a href="../kotlin-dsl/index.html" target="_blank">Kotlin DSL API</a></li>
+                    <li><a href="../userguide/migrating_from_groovy_to_kotlin_dsl.html">Groovy to Kotlin DSL Migration</a></li>
+                </ul>
+            </li>
             <li><a href="../userguide/plugin_reference.html">Core Plugins</a></li>
             <li id="third-party-integration"><a href="../userguide/third_party_integration.html">Gradle &amp; Third-party Tools</a></li>
             <li><a href="../userguide/userguide.pdf">User Manual PDF</a></li>
diff --git a/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/groovy/consumer/build.gradle b/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/groovy/consumer/build.gradle
index c108e19..87789a1 100644
--- a/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/groovy/consumer/build.gradle
+++ b/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/groovy/consumer/build.gradle
@@ -1,7 +1,6 @@
 configurations {
     sharedConfiguration {
         canBeConsumed = false
-        canBeResolved = true
     }
 }
 
diff --git a/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/groovy/producer/build.gradle b/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/groovy/producer/build.gradle
index 96222f8..9f00e96 100644
--- a/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/groovy/producer/build.gradle
+++ b/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/groovy/producer/build.gradle
@@ -8,7 +8,6 @@
 
 configurations {
     sharedConfiguration {
-        canBeConsumed = true
         canBeResolved = false
     }
 }
diff --git a/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/kotlin/consumer/build.gradle.kts b/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/kotlin/consumer/build.gradle.kts
index 59aec83..f655eff 100644
--- a/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/kotlin/consumer/build.gradle.kts
+++ b/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/kotlin/consumer/build.gradle.kts
@@ -1,6 +1,5 @@
 val sharedConfiguration: Configuration by configurations.creating {
     isCanBeConsumed = false
-    isCanBeResolved = true
 }
 
 dependencies {
diff --git a/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/kotlin/producer/build.gradle.kts b/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/kotlin/producer/build.gradle.kts
index f609ac9..3c8fccc 100644
--- a/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/kotlin/producer/build.gradle.kts
+++ b/subprojects/docs/src/samples/build-organization/cross-project-output-sharing/kotlin/producer/build.gradle.kts
@@ -7,7 +7,6 @@
 }
 
 val sharedConfiguration by configurations.creating {
-    isCanBeConsumed = true
     isCanBeResolved = false
 }
 
diff --git a/subprojects/docs/src/samples/build-organization/publishing-convention-plugins/groovy/convention-plugins/build.gradle b/subprojects/docs/src/samples/build-organization/publishing-convention-plugins/groovy/convention-plugins/build.gradle
index 226731e..9ee46da 100644
--- a/subprojects/docs/src/samples/build-organization/publishing-convention-plugins/groovy/convention-plugins/build.gradle
+++ b/subprojects/docs/src/samples/build-organization/publishing-convention-plugins/groovy/convention-plugins/build.gradle
@@ -35,6 +35,7 @@
     implementation 'com.github.spotbugs.snom:spotbugs-gradle-plugin:5.0.12'
     testImplementation platform("org.spockframework:spock-bom:2.2-groovy-3.0")
     testImplementation 'org.spockframework:spock-core'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 
 tasks.named('test', Test) {
diff --git a/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/commons/src/main/groovy/com.example.commons.gradle b/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/commons/src/main/groovy/com.example.commons.gradle
index 0b54ecf..42bc0c3 100644
--- a/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/commons/src/main/groovy/com.example.commons.gradle
+++ b/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/commons/src/main/groovy/com.example.commons.gradle
@@ -16,6 +16,7 @@
 
     testImplementation(platform('com.example.platform:test-platform'))
     testImplementation('org.junit.jupiter:junit-jupiter')
+    testRuntimeOnly('org.junit.platform:junit-platform-launcher')
 }
 
 tasks.named("test") {
diff --git a/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/commons/src/main/groovy/com.example.jacoco.gradle b/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/commons/src/main/groovy/com.example.jacoco.gradle
index d6b1cce..8940875 100644
--- a/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/commons/src/main/groovy/com.example.jacoco.gradle
+++ b/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/commons/src/main/groovy/com.example.jacoco.gradle
@@ -12,7 +12,6 @@
 configurations.create('transitiveSourcesElements') {
     visible = false
     canBeResolved = false
-    canBeConsumed = true
     extendsFrom(configurations.implementation)
     attributes {
         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
@@ -26,7 +25,6 @@
 configurations.create('coverageDataElements') {
     visible = false
     canBeResolved = false
-    canBeConsumed = true
     extendsFrom(configurations.implementation)
     attributes {
         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
diff --git a/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/report-aggregation/src/main/groovy/com.example.report-aggregation.gradle b/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/report-aggregation/src/main/groovy/com.example.report-aggregation.gradle
index 42900c3..05063cc 100644
--- a/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/report-aggregation/src/main/groovy/com.example.report-aggregation.gradle
+++ b/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/build-logic/report-aggregation/src/main/groovy/com.example.report-aggregation.gradle
@@ -13,7 +13,6 @@
 // Resolvable configuration to resolve the classes of all dependencies
 def classesPath = configurations.create('classesPath') {
     visible = false
-    canBeResolved = true
     canBeConsumed = false
     extendsFrom(aggregate)
     attributes {
@@ -26,7 +25,6 @@
 // A resolvable configuration to collect source code
 def sourcesPath = configurations.create('sourcesPath') {
     visible = false
-    canBeResolved = true
     canBeConsumed = false
     extendsFrom(aggregate)
     attributes {
@@ -38,7 +36,6 @@
 // A resolvable configuration to collect JaCoCo coverage data
 def coverageDataPath = configurations.create('coverageDataPath') {
     visible = false
-    canBeResolved = true
     canBeConsumed = false
     extendsFrom(aggregate)
     attributes {
diff --git a/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/platforms/plugins-platform/build.gradle b/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/platforms/plugins-platform/build.gradle
index 356c4db..4cffe97 100644
--- a/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/platforms/plugins-platform/build.gradle
+++ b/subprojects/docs/src/samples/build-organization/structuring-software-projects/groovy/platforms/plugins-platform/build.gradle
@@ -7,8 +7,8 @@
 dependencies {
     constraints {
         api('com.android.tools.build:gradle:7.3.0')
-        api('org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:1.8.10')
-        api('org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.10')
+        api('org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:1.8.21')
+        api('org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.21')
         api('org.springframework.boot:org.springframework.boot.gradle.plugin:2.7.8')
     }
 }
diff --git a/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/build-logic/commons/src/main/kotlin/com.example.commons.gradle.kts b/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/build-logic/commons/src/main/kotlin/com.example.commons.gradle.kts
index 52b0269..c931eb8 100644
--- a/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/build-logic/commons/src/main/kotlin/com.example.commons.gradle.kts
+++ b/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/build-logic/commons/src/main/kotlin/com.example.commons.gradle.kts
@@ -16,6 +16,7 @@
 
     testImplementation(platform("com.example.platform:test-platform"))
     testImplementation("org.junit.jupiter:junit-jupiter")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 
 tasks.test {
diff --git a/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/build-logic/report-aggregation/src/main/kotlin/com.example.report-aggregation.gradle.kts b/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/build-logic/report-aggregation/src/main/kotlin/com.example.report-aggregation.gradle.kts
index 559b99a..22a9e62 100644
--- a/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/build-logic/report-aggregation/src/main/kotlin/com.example.report-aggregation.gradle.kts
+++ b/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/build-logic/report-aggregation/src/main/kotlin/com.example.report-aggregation.gradle.kts
@@ -13,7 +13,6 @@
 // Resolvable configuration to resolve the classes of all dependencies
 val classesPath by configurations.creating {
     isVisible = false
-    isCanBeResolved = true
     isCanBeConsumed = false
     extendsFrom(aggregate)
     attributes {
@@ -26,7 +25,6 @@
 // A resolvable configuration to collect source code
 val sourcesPath by configurations.creating {
     isVisible = false
-    isCanBeResolved = true
     isCanBeConsumed = false
     extendsFrom(aggregate)
     attributes {
@@ -38,7 +36,6 @@
 // A resolvable configuration to collect JaCoCo coverage data
 val coverageDataPath by configurations.creating {
     isVisible = false
-    isCanBeResolved = true
     isCanBeConsumed = false
     extendsFrom(aggregate)
     attributes {
diff --git a/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/platforms/plugins-platform/build.gradle.kts b/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/platforms/plugins-platform/build.gradle.kts
index 7fb5571..9e435ae 100644
--- a/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/platforms/plugins-platform/build.gradle.kts
+++ b/subprojects/docs/src/samples/build-organization/structuring-software-projects/kotlin/platforms/plugins-platform/build.gradle.kts
@@ -7,8 +7,8 @@
 dependencies {
     constraints {
         api("com.android.tools.build:gradle:7.3.1")
-        api("org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:1.8.10")
-        api("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.10")
+        api("org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:1.8.21")
+        api("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.8.21")
         api("org.springframework.boot:org.springframework.boot.gradle.plugin:2.7.8")
     }
 }
diff --git a/subprojects/docs/src/samples/build-organization/structuring-software-projects/tests/build-server-application.sample.conf b/subprojects/docs/src/samples/build-organization/structuring-software-projects/tests/build-server-application.sample.conf
index 009de47..927fc48 100644
--- a/subprojects/docs/src/samples/build-organization/structuring-software-projects/tests/build-server-application.sample.conf
+++ b/subprojects/docs/src/samples/build-organization/structuring-software-projects/tests/build-server-application.sample.conf
@@ -2,4 +2,6 @@
     executable: gradle
     args: build
     execution-subdirectory: server-application
+    # Do not fail for deprecation warnings: Project.getConvention; Spring boot 3.0.2+ no longer uses that API
+    flags: "--warning-mode=none"
 }]
diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle b/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
index 7d22552..6d6761a8 100644
--- a/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
+++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
@@ -35,6 +35,7 @@
 
 dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 
     integrationTestImplementation project
 }
diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts b/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
index 81da129..3b02434 100644
--- a/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
+++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-additional-test-types/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
@@ -35,6 +35,7 @@
 
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 
     "integrationTestImplementation"(project)
 }
diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
index 5f64431..b8586d0 100644
--- a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
+++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
@@ -24,4 +24,5 @@
 
 dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
diff --git a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
index df212e1..9126923 100644
--- a/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
+++ b/subprojects/docs/src/samples/java/jvm-multi-project-with-toolchains/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
@@ -23,4 +23,5 @@
 
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
diff --git a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
index 9278deb..72ee188 100644
--- a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
+++ b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
@@ -41,6 +41,7 @@
 
 dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 
     integrationTestImplementation project
 }
diff --git a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
index 14b0923..f7caec2 100644
--- a/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
+++ b/subprojects/docs/src/samples/java/modules-multi-project-with-integration-tests/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
@@ -41,6 +41,7 @@
 
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 
     "integrationTestImplementation"(project)
 }
diff --git a/subprojects/docs/src/samples/java/modules-multi-project/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle b/subprojects/docs/src/samples/java/modules-multi-project/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
index d6d8581..436fd0a 100644
--- a/subprojects/docs/src/samples/java/modules-multi-project/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
+++ b/subprojects/docs/src/samples/java/modules-multi-project/groovy/buildSrc/src/main/groovy/myproject.java-conventions.gradle
@@ -15,4 +15,5 @@
 
 dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
diff --git a/subprojects/docs/src/samples/java/modules-multi-project/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts b/subprojects/docs/src/samples/java/modules-multi-project/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
index 79cd878..ccddf1e 100644
--- a/subprojects/docs/src/samples/java/modules-multi-project/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
+++ b/subprojects/docs/src/samples/java/modules-multi-project/kotlin/buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
@@ -15,4 +15,5 @@
 
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
diff --git a/subprojects/docs/src/samples/readme-templates/application-build.out.template b/subprojects/docs/src/samples/readme-templates/application-build.out.template
index 92a2f1a..e2bcc9f 100644
--- a/subprojects/docs/src/samples/readme-templates/application-build.out.template
+++ b/subprojects/docs/src/samples/readme-templates/application-build.out.template
@@ -1,7 +1,7 @@
 ${extraCompileJava.raw}> Task :${subprojectName.raw}:compile${language.raw}
 > Task :${subprojectName.raw}:processResources NO-SOURCE
 > Task :${subprojectName.raw}:classes${classesUpToDate.raw}
-${inspectClassesForKotlinICTask.raw}> Task :${subprojectName.raw}:jar
+> Task :${subprojectName.raw}:jar
 > Task :${subprojectName.raw}:startScripts
 > Task :${subprojectName.raw}:distTar
 > Task :${subprojectName.raw}:distZip
diff --git a/subprojects/docs/src/samples/readme-templates/build.sample.conf.template b/subprojects/docs/src/samples/readme-templates/build.sample.conf.template
index d5598e1..7d99e54 100644
--- a/subprojects/docs/src/samples/readme-templates/build.sample.conf.template
+++ b/subprojects/docs/src/samples/readme-templates/build.sample.conf.template
@@ -1,6 +1,9 @@
 commands: [{
     executable: gradle
     args: build
+    # We temporarily disable deprecation warnings until we can update the gradle/gradle wrapper
+    # to the new version where projects created with build-init do not emit warnings.
+    flags: "--warning-mode=none"
     expected-output-file: build.out
     allow-disordered-output: true
 }]
diff --git a/subprojects/docs/src/samples/readme-templates/library-build.out.template b/subprojects/docs/src/samples/readme-templates/library-build.out.template
index dc83d4b..0de1e08 100644
--- a/subprojects/docs/src/samples/readme-templates/library-build.out.template
+++ b/subprojects/docs/src/samples/readme-templates/library-build.out.template
@@ -1,7 +1,7 @@
 ${extraCompileJava.raw}> Task :${subprojectName.raw}:compile${language.raw}
 > Task :${subprojectName.raw}:processResources NO-SOURCE
 > Task :${subprojectName.raw}:classes${classesUpToDate.raw}
-${inspectClassesForKotlinICTask.raw}> Task :${subprojectName.raw}:jar
+> Task :${subprojectName.raw}:jar
 > Task :${subprojectName.raw}:assemble
 ${extraCompileTestJava.raw}> Task :${subprojectName.raw}:compileTest${language.raw}
 > Task :${subprojectName.raw}:processTestResources NO-SOURCE
diff --git a/subprojects/docs/src/samples/spring-boot-web-application/groovy/app/build.gradle b/subprojects/docs/src/samples/spring-boot-web-application/groovy/app/build.gradle
index 136c3c8..7fff942 100644
--- a/subprojects/docs/src/samples/spring-boot-web-application/groovy/app/build.gradle
+++ b/subprojects/docs/src/samples/spring-boot-web-application/groovy/app/build.gradle
@@ -21,6 +21,7 @@
 	testImplementation('org.springframework.boot:spring-boot-starter-test') {
 		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
 	}
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 
 tasks.named('test', Test) {
diff --git a/subprojects/docs/src/samples/spring-boot-web-application/kotlin/app/build.gradle.kts b/subprojects/docs/src/samples/spring-boot-web-application/kotlin/app/build.gradle.kts
index d1ebd2c..dc5efe3 100644
--- a/subprojects/docs/src/samples/spring-boot-web-application/kotlin/app/build.gradle.kts
+++ b/subprojects/docs/src/samples/spring-boot-web-application/kotlin/app/build.gradle.kts
@@ -21,6 +21,7 @@
     testImplementation("org.springframework.boot:spring-boot-starter-test") {
         exclude(mapOf("group" to "org.junit.vintage", "module" to "junit-vintage-engine"))
     }
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 
 tasks.named<Test>("test") {
diff --git a/subprojects/docs/src/samples/spring-boot-web-application/tests/sanityCheck.sample.conf b/subprojects/docs/src/samples/spring-boot-web-application/tests/sanityCheck.sample.conf
new file mode 100644
index 0000000..1b72008
--- /dev/null
+++ b/subprojects/docs/src/samples/spring-boot-web-application/tests/sanityCheck.sample.conf
@@ -0,0 +1,4 @@
+executable: gradle
+args: tasks
+# Do not fail for deprecation warnings: Project.getConvention; Spring boot 3.0.2+ no longer uses that API
+flags: "--warning-mode=none"
diff --git a/subprojects/docs/src/samples/spring-boot-web-application/tests/testTask.sample.conf b/subprojects/docs/src/samples/spring-boot-web-application/tests/testTask.sample.conf
index 4ea03e1..4896d34 100644
--- a/subprojects/docs/src/samples/spring-boot-web-application/tests/testTask.sample.conf
+++ b/subprojects/docs/src/samples/spring-boot-web-application/tests/testTask.sample.conf
@@ -2,4 +2,6 @@
     executable: gradle
     args: test
     expected-output-file: testTask.out
+# Do not fail for deprecation warnings: Project.getConvention; Spring boot 3.0.2+ no longer uses that API
+    flags: "--warning-mode=none"
 }]
diff --git a/subprojects/docs/src/samples/templates/java-android-application/app/src/test/java/org/gradle/samples/ExampleUnitTest.java b/subprojects/docs/src/samples/templates/java-android-application/app/src/test/java/org/gradle/samples/ExampleUnitTest.java
index 9a43858..51bd940 100644
--- a/subprojects/docs/src/samples/templates/java-android-application/app/src/test/java/org/gradle/samples/ExampleUnitTest.java
+++ b/subprojects/docs/src/samples/templates/java-android-application/app/src/test/java/org/gradle/samples/ExampleUnitTest.java
@@ -14,4 +14,4 @@ public class ExampleUnitTest {
     public void addition_isCorrect() {
         assertEquals(4, 2 + 2);
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/samples/templates/java-android-application/src/test/java/org/gradle/samples/ExampleUnitTest.java b/subprojects/docs/src/samples/templates/java-android-application/src/test/java/org/gradle/samples/ExampleUnitTest.java
index 9a43858..51bd940 100644
--- a/subprojects/docs/src/samples/templates/java-android-application/src/test/java/org/gradle/samples/ExampleUnitTest.java
+++ b/subprojects/docs/src/samples/templates/java-android-application/src/test/java/org/gradle/samples/ExampleUnitTest.java
@@ -14,4 +14,4 @@ public class ExampleUnitTest {
     public void addition_isCorrect() {
         assertEquals(4, 2 + 2);
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/ant/useExternalAntTaskWithConfig/groovy/src/Source.java b/subprojects/docs/src/snippets/ant/useExternalAntTaskWithConfig/groovy/src/Source.java
index 616ce35..ace5564 100644
--- a/subprojects/docs/src/snippets/ant/useExternalAntTaskWithConfig/groovy/src/Source.java
+++ b/subprojects/docs/src/snippets/ant/useExternalAntTaskWithConfig/groovy/src/Source.java
@@ -1,3 +1,3 @@
 public class Source {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/ant/useExternalAntTaskWithConfig/kotlin/src/Source.java b/subprojects/docs/src/snippets/ant/useExternalAntTaskWithConfig/kotlin/src/Source.java
index 616ce35..ace5564 100644
--- a/subprojects/docs/src/snippets/ant/useExternalAntTaskWithConfig/kotlin/src/Source.java
+++ b/subprojects/docs/src/snippets/ant/useExternalAntTaskWithConfig/kotlin/src/Source.java
@@ -1,3 +1,3 @@
 public class Source {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/main/groovy/org/gradle/sample/GroovyPerson.groovy b/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
index 9e6f3e6..c3e81ca 100755
--- a/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
+++ b/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
@@ -2,4 +2,4 @@
 
 class GroovyPerson {
     def String name
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/main/java/org/gradle/sample/Person.java b/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/main/java/org/gradle/sample/Person.java
index 438b0d0..6d857e2 100755
--- a/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/main/java/org/gradle/sample/Person.java
+++ b/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/main/java/org/gradle/sample/Person.java
@@ -12,4 +12,4 @@ public String getName() {
     public void setName(String name) {
         this.name = name;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/test/groovy/org/gradle/sample/PersonTest.groovy b/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/test/groovy/org/gradle/sample/PersonTest.groovy
index 3d91d49..112899b 100755
--- a/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/test/groovy/org/gradle/sample/PersonTest.groovy
+++ b/subprojects/docs/src/snippets/base/buildDashboard/groovy/src/test/groovy/org/gradle/sample/PersonTest.groovy
@@ -10,4 +10,4 @@
         person.name = 'Barry'
         assertEquals('Barry', person.name)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/main/groovy/org/gradle/sample/GroovyPerson.groovy b/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
index 9e6f3e6..c3e81ca 100755
--- a/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
+++ b/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
@@ -2,4 +2,4 @@
 
 class GroovyPerson {
     def String name
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/main/java/org/gradle/sample/Person.java b/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/main/java/org/gradle/sample/Person.java
index 438b0d0..6d857e2 100755
--- a/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/main/java/org/gradle/sample/Person.java
+++ b/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/main/java/org/gradle/sample/Person.java
@@ -12,4 +12,4 @@ public String getName() {
     public void setName(String name) {
         this.name = name;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/test/groovy/org/gradle/sample/PersonTest.groovy b/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/test/groovy/org/gradle/sample/PersonTest.groovy
index 3d91d49..112899b 100755
--- a/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/test/groovy/org/gradle/sample/PersonTest.groovy
+++ b/subprojects/docs/src/snippets/base/buildDashboard/kotlin/src/test/groovy/org/gradle/sample/PersonTest.groovy
@@ -10,4 +10,4 @@
         person.name = 'Barry'
         assertEquals('Barry', person.name)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/buildlifecycle/basic/groovy/settings.gradle b/subprojects/docs/src/snippets/buildlifecycle/basic/groovy/settings.gradle
index 5aa5475..3512e82 100644
--- a/subprojects/docs/src/snippets/buildlifecycle/basic/groovy/settings.gradle
+++ b/subprojects/docs/src/snippets/buildlifecycle/basic/groovy/settings.gradle
@@ -1,2 +1,2 @@
 rootProject.name = 'basic'
-println 'This is executed during the initialization phase.'
\ No newline at end of file
+println 'This is executed during the initialization phase.'
diff --git a/subprojects/docs/src/snippets/buildlifecycle/projectEvaluateEvents/groovy/project-a.gradle b/subprojects/docs/src/snippets/buildlifecycle/projectEvaluateEvents/groovy/project-a.gradle
index db657cc..9510d27 100644
--- a/subprojects/docs/src/snippets/buildlifecycle/projectEvaluateEvents/groovy/project-a.gradle
+++ b/subprojects/docs/src/snippets/buildlifecycle/projectEvaluateEvents/groovy/project-a.gradle
@@ -1 +1 @@
-hasTests = true
\ No newline at end of file
+hasTests = true
diff --git a/subprojects/docs/src/snippets/buildlifecycle/taskExecutionEvents/tests-groovy/taskExecutionEvents.groovy.out b/subprojects/docs/src/snippets/buildlifecycle/taskExecutionEvents/tests-groovy/taskExecutionEvents.groovy.out
index e48aded..f191f95 100644
--- a/subprojects/docs/src/snippets/buildlifecycle/taskExecutionEvents/tests-groovy/taskExecutionEvents.groovy.out
+++ b/subprojects/docs/src/snippets/buildlifecycle/taskExecutionEvents/tests-groovy/taskExecutionEvents.groovy.out
@@ -16,7 +16,6 @@
 > Run with --stacktrace option to get the stack trace.
 > Run with --info or --debug option to get more log output.
 > Run with --scan to get full insights.
-
-* Get more help at https://help.gradle.org
+> Get more help at https://help.gradle.org.
 
 BUILD FAILED in 0s
diff --git a/subprojects/docs/src/snippets/buildlifecycle/taskExecutionEvents/tests-kotlin/taskExecutionEvents.kotlin.out b/subprojects/docs/src/snippets/buildlifecycle/taskExecutionEvents/tests-kotlin/taskExecutionEvents.kotlin.out
index 02d1d9f..ed6a4fa 100644
--- a/subprojects/docs/src/snippets/buildlifecycle/taskExecutionEvents/tests-kotlin/taskExecutionEvents.kotlin.out
+++ b/subprojects/docs/src/snippets/buildlifecycle/taskExecutionEvents/tests-kotlin/taskExecutionEvents.kotlin.out
@@ -13,7 +13,6 @@
 > Run with --stacktrace option to get the stack trace.
 > Run with --info or --debug option to get more log output.
 > Run with --scan to get full insights.
-
-* Get more help at https://help.gradle.org
+> Get more help at https://help.gradle.org.
 
 BUILD FAILED in 0s
diff --git a/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/groovy/org/gradle/sample/GroovyPerson.groovy b/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
index 9e6f3e6..c3e81ca 100755
--- a/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
+++ b/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
@@ -2,4 +2,4 @@
 
 class GroovyPerson {
     def String name
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/java/org/gradle/sample/Person.java b/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/java/org/gradle/sample/Person.java
index 438b0d0..6d857e2 100755
--- a/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/java/org/gradle/sample/Person.java
+++ b/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/java/org/gradle/sample/Person.java
@@ -12,4 +12,4 @@ public String getName() {
     public void setName(String name) {
         this.name = name;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/java/org/gradle/sample/bad_name.java b/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/java/org/gradle/sample/bad_name.java
index 993119a..0a7fbff 100644
--- a/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/java/org/gradle/sample/bad_name.java
+++ b/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/main/java/org/gradle/sample/bad_name.java
@@ -2,4 +2,4 @@
 
 class bad_name {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/test/groovy/org/gradle/sample/PersonTest.groovy b/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/test/groovy/org/gradle/sample/PersonTest.groovy
index 3d91d49..112899b 100755
--- a/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/test/groovy/org/gradle/sample/PersonTest.groovy
+++ b/subprojects/docs/src/snippets/codeQuality/codeQuality/groovy/src/test/groovy/org/gradle/sample/PersonTest.groovy
@@ -10,4 +10,4 @@
         person.name = 'Barry'
         assertEquals('Barry', person.name)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/groovy/org/gradle/sample/GroovyPerson.groovy b/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
index 9e6f3e6..c3e81ca 100755
--- a/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
+++ b/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/groovy/org/gradle/sample/GroovyPerson.groovy
@@ -2,4 +2,4 @@
 
 class GroovyPerson {
     def String name
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/java/org/gradle/sample/Person.java b/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/java/org/gradle/sample/Person.java
index 438b0d0..6d857e2 100755
--- a/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/java/org/gradle/sample/Person.java
+++ b/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/java/org/gradle/sample/Person.java
@@ -12,4 +12,4 @@ public String getName() {
     public void setName(String name) {
         this.name = name;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/java/org/gradle/sample/bad_name.java b/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/java/org/gradle/sample/bad_name.java
index 993119a..0a7fbff 100644
--- a/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/java/org/gradle/sample/bad_name.java
+++ b/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/main/java/org/gradle/sample/bad_name.java
@@ -2,4 +2,4 @@
 
 class bad_name {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/test/groovy/org/gradle/sample/PersonTest.groovy b/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/test/groovy/org/gradle/sample/PersonTest.groovy
index 3d91d49..112899b 100755
--- a/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/test/groovy/org/gradle/sample/PersonTest.groovy
+++ b/subprojects/docs/src/snippets/codeQuality/codeQuality/kotlin/src/test/groovy/org/gradle/sample/PersonTest.groovy
@@ -10,4 +10,4 @@
         person.name = 'Barry'
         assertEquals('Barry', person.name)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/configurationCache/problemsGroovy/tests/fail.out b/subprojects/docs/src/snippets/configurationCache/problemsGroovy/tests/fail.out
index 17ee81b..b936b17 100644
--- a/subprojects/docs/src/snippets/configurationCache/problemsGroovy/tests/fail.out
+++ b/subprojects/docs/src/snippets/configurationCache/problemsGroovy/tests/fail.out
@@ -12,8 +12,7 @@
 > Run with --stacktrace option to get the stack trace.
 > Run with --info or --debug option to get more log output.
 > Run with --scan to get full insights.
-
-* Get more help at https://help.gradle.org
+> Get more help at https://help.gradle.org.
 
 BUILD FAILED in 0s
 1 actionable task: 1 executed
diff --git a/subprojects/docs/src/snippets/configurationCache/problemsKotlin/tests/fail.out b/subprojects/docs/src/snippets/configurationCache/problemsKotlin/tests/fail.out
index 4cc33db..49bce24 100644
--- a/subprojects/docs/src/snippets/configurationCache/problemsKotlin/tests/fail.out
+++ b/subprojects/docs/src/snippets/configurationCache/problemsKotlin/tests/fail.out
@@ -12,8 +12,7 @@
 > Run with --stacktrace option to get the stack trace.
 > Run with --info or --debug option to get more log output.
 > Run with --scan to get full insights.
-
-* Get more help at https://help.gradle.org
+> Get more help at https://help.gradle.org.
 
 BUILD FAILED in 0s
 1 actionable task: 1 executed
diff --git a/subprojects/docs/src/snippets/configurationCache/samConversion/common/src/main/kotlin/Source.kt b/subprojects/docs/src/snippets/configurationCache/samConversion/common/src/main/kotlin/Source.kt
deleted file mode 100644
index ff500d6..0000000
--- a/subprojects/docs/src/snippets/configurationCache/samConversion/common/src/main/kotlin/Source.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-class Source {
-
-}
diff --git a/subprojects/docs/src/snippets/configurationCache/samConversion/groovy/build.gradle b/subprojects/docs/src/snippets/configurationCache/samConversion/groovy/build.gradle
deleted file mode 100644
index d355b5a..0000000
--- a/subprojects/docs/src/snippets/configurationCache/samConversion/groovy/build.gradle
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-plugins {
-    id "org.jetbrains.kotlin.jvm" version "1.8.10"
-}
-
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-repositories {
-    mavenCentral()
-}
-
-// tag::configure[]
-tasks.withType(KotlinCompile).configureEach {
-    compilerOptions.freeCompilerArgs.add("-Xsam-conversions=class")
-}
-// tag::configure[]
diff --git a/subprojects/docs/src/snippets/configurationCache/samConversion/groovy/settings.gradle b/subprojects/docs/src/snippets/configurationCache/samConversion/groovy/settings.gradle
deleted file mode 100644
index 998347b..0000000
--- a/subprojects/docs/src/snippets/configurationCache/samConversion/groovy/settings.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-rootProject.name = 'samConversion'
diff --git a/subprojects/docs/src/snippets/configurationCache/samConversion/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/configurationCache/samConversion/kotlin/build.gradle.kts
deleted file mode 100644
index 246ccbb..0000000
--- a/subprojects/docs/src/snippets/configurationCache/samConversion/kotlin/build.gradle.kts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-plugins {
-    kotlin("jvm") version "1.8.10"
-}
-
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-repositories {
-    mavenCentral()
-}
-
-// tag::configure[]
-tasks.withType<KotlinCompile>().configureEach {
-    compilerOptions.freeCompilerArgs.add("-Xsam-conversions=class")
-}
-// tag::configure[]
diff --git a/subprojects/docs/src/snippets/configurationCache/samConversion/kotlin/settings.gradle.kts b/subprojects/docs/src/snippets/configurationCache/samConversion/kotlin/settings.gradle.kts
deleted file mode 100644
index ebf9beb..0000000
--- a/subprojects/docs/src/snippets/configurationCache/samConversion/kotlin/settings.gradle.kts
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-rootProject.name = "samConversion"
diff --git a/subprojects/docs/src/snippets/configurationCache/samConversion/tests/samConversion.sample.conf b/subprojects/docs/src/snippets/configurationCache/samConversion/tests/samConversion.sample.conf
deleted file mode 100644
index d21b8fd..0000000
--- a/subprojects/docs/src/snippets/configurationCache/samConversion/tests/samConversion.sample.conf
+++ /dev/null
@@ -1,2 +0,0 @@
-executable: gradle
-args: assemble
diff --git a/subprojects/docs/src/snippets/configurationCache/testKit/groovy/build.gradle b/subprojects/docs/src/snippets/configurationCache/testKit/groovy/build.gradle
index 2fd254d..0971176 100644
--- a/subprojects/docs/src/snippets/configurationCache/testKit/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/configurationCache/testKit/groovy/build.gradle
@@ -6,6 +6,7 @@
 dependencies {
     testImplementation platform("org.spockframework:spock-bom:2.2-groovy-3.0")
     testImplementation 'org.spockframework:spock-core'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 
 tasks.named('test', Test) {
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/build.gradle b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/build.gradle
new file mode 100644
index 0000000..fb25963
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/build.gradle
@@ -0,0 +1,11 @@
+tasks.register("success") {
+    doLast {
+        "Hello"
+    }
+}
+
+tasks.register("failure") {
+    doLast {
+        throw new GradleException("This task failed")
+    }
+}
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/build.gradle b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/build.gradle
new file mode 100644
index 0000000..adfacc2
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/build.gradle
@@ -0,0 +1,13 @@
+plugins {
+    id("java-gradle-plugin")
+}
+
+
+gradlePlugin {
+    plugins {
+        soundPlugin {
+            id = "org.gradle.sample.sound-feedback-plugin"
+            implementationClass = "org.gradle.sample.sound.SoundFeedbackPlugin"
+        }
+    }
+}
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundFeedbackPlugin.java b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundFeedbackPlugin.java
new file mode 100644
index 0000000..7a8fe66
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundFeedbackPlugin.java
@@ -0,0 +1,40 @@
+// tag::flow-action[]
+package org.gradle.sample.sound;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.flow.FlowProviders;
+import org.gradle.api.flow.FlowScope;
+import org.gradle.api.initialization.Settings;
+
+import javax.inject.Inject;
+import java.io.File;
+
+public abstract class SoundFeedbackPlugin implements Plugin<Settings> {
+    @Inject
+    protected abstract FlowScope getFlowScope(); // <1>
+
+    @Inject
+    protected abstract FlowProviders getFlowProviders(); // <1>
+
+    @Override
+    public void apply(Settings settings) {
+// end::flow-action[]
+        settings.getGradle().getSharedServices().registerIfAbsent("soundService", SoundService.class, spec -> {});
+
+// tag::flow-action[]
+        final File soundsDir = new File(settings.getSettingsDir(), "sounds");
+        getFlowScope().always( // <2>
+            SoundPlay.class,  // <3>
+            spec ->  // <4>
+                spec.getParameters().getMediaFile().set(
+                    getFlowProviders().getBuildWorkResult().map(result -> // <5>
+                        new File(
+                            soundsDir,
+                            result.getFailure().isPresent() ? "sad-trombone.mp3" : "tada.mp3"
+                        )
+                    )
+                )
+        );
+    }
+}
+// end::flow-action[]
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundPlay.java b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundPlay.java
new file mode 100644
index 0000000..2057d16
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundPlay.java
@@ -0,0 +1,24 @@
+package org.gradle.sample.sound;
+
+import org.gradle.api.flow.FlowAction;
+import org.gradle.api.flow.FlowParameters;
+import org.gradle.api.provider.Property;
+import org.gradle.api.services.ServiceReference;
+import org.gradle.api.tasks.Input;
+
+import java.io.File;
+
+public abstract class SoundPlay implements FlowAction<SoundPlay.Parameters> {
+    interface Parameters extends FlowParameters {
+        @ServiceReference // <1>
+        Property<SoundService> getSoundService();
+
+        @Input // <2>
+        Property<File> getMediaFile();
+    }
+
+    @Override
+    public void execute(Parameters parameters) {
+        parameters.getSoundService().get().playSoundFile(parameters.getMediaFile().get());
+    }
+}
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundService.java b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundService.java
new file mode 100644
index 0000000..e2fe00d
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/plugin/src/main/java/org/gradle/sample/sound/SoundService.java
@@ -0,0 +1,12 @@
+package org.gradle.sample.sound;
+
+import org.gradle.api.services.BuildService;
+import org.gradle.api.services.BuildServiceParameters;
+
+import java.io.File;
+
+public abstract class SoundService implements BuildService<BuildServiceParameters.None> {
+    public void playSoundFile(File path) {
+        System.out.println("Playing sound " + path.getName());
+    }
+}
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/settings.gradle b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/settings.gradle
new file mode 100644
index 0000000..2b5d298
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/groovy/settings.gradle
@@ -0,0 +1,9 @@
+pluginManagement {
+    includeBuild("plugin")
+}
+
+plugins {
+    id("org.gradle.sample.sound-feedback-plugin")
+}
+
+rootProject.name = "playSound"
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/tests/failure.out b/subprojects/docs/src/snippets/dataflowActions/playSound/tests/failure.out
new file mode 100644
index 0000000..d33bb02
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/tests/failure.out
@@ -0,0 +1,7 @@
+Playing sound sad-trombone.mp3
+
+FAILURE: Build failed with an exception.
+
+* What went wrong:
+Execution failed for task ':failure'.
+> This task failed
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/tests/failure.sample.conf b/subprojects/docs/src/snippets/dataflowActions/playSound/tests/failure.sample.conf
new file mode 100644
index 0000000..0984dad
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/tests/failure.sample.conf
@@ -0,0 +1,7 @@
+executable: gradle
+args: failure
+flags: --quiet
+expected-output-file: failure.out
+expect-failure: true
+allow-additional-output: true
+allow-disordered-output: true
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/tests/success.out b/subprojects/docs/src/snippets/dataflowActions/playSound/tests/success.out
new file mode 100644
index 0000000..a10a8ec
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/tests/success.out
@@ -0,0 +1 @@
+Playing sound tada.mp3
diff --git a/subprojects/docs/src/snippets/dataflowActions/playSound/tests/success.sample.conf b/subprojects/docs/src/snippets/dataflowActions/playSound/tests/success.sample.conf
new file mode 100644
index 0000000..347f43b
--- /dev/null
+++ b/subprojects/docs/src/snippets/dataflowActions/playSound/tests/success.sample.conf
@@ -0,0 +1,4 @@
+executable: gradle
+args: success
+flags: --quiet
+expected-output-file: success.out
diff --git a/subprojects/docs/src/snippets/dependencyManagement/attributeMatching/groovy/build.gradle b/subprojects/docs/src/snippets/dependencyManagement/attributeMatching/groovy/build.gradle
index fd81a58..c0b3da7 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/attributeMatching/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/dependencyManagement/attributeMatching/groovy/build.gradle
@@ -30,12 +30,12 @@
         // This configuration is an "outgoing" configuration, it's not meant to be resolved
         canBeResolved = false
         // As an outgoing configuration, explain that consumers may want to consume it
-        canBeConsumed = true
+        assert canBeConsumed
     }
     // A configuration meant for consumers that need the implementation of this component
     exposedRuntime {
         canBeResolved = false
-        canBeConsumed = true
+        assert canBeConsumed
     }
 }
 // end::setup-configurations[]
diff --git a/subprojects/docs/src/snippets/dependencyManagement/attributeMatching/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/dependencyManagement/attributeMatching/kotlin/build.gradle.kts
index 9cde75f..45e0a8e 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/attributeMatching/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/dependencyManagement/attributeMatching/kotlin/build.gradle.kts
@@ -33,12 +33,12 @@
         // This configuration is an "outgoing" configuration, it's not meant to be resolved
         isCanBeResolved = false
         // As an outgoing configuration, explain that consumers may want to consume it
-        isCanBeConsumed = true
+        assert(isCanBeConsumed)
     }
     // A configuration meant for consumers that need the implementation of this component
     create("exposedRuntime") {
         isCanBeResolved = false
-        isCanBeConsumed = true
+        assert(isCanBeConsumed)
     }
 }
 // end::setup-configurations[]
diff --git a/subprojects/docs/src/snippets/dependencyManagement/customizingResolution-deactivateGlobalSubstitution/groovy/build.gradle b/subprojects/docs/src/snippets/dependencyManagement/customizingResolution-deactivateGlobalSubstitution/groovy/build.gradle
index bba2c66..45bddab 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/customizingResolution-deactivateGlobalSubstitution/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/dependencyManagement/customizingResolution-deactivateGlobalSubstitution/groovy/build.gradle
@@ -11,7 +11,6 @@
 
     extendsFrom(configurations.runtimeClasspath)
     canBeConsumed = false
-    canBeResolved = true
     attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
 }
 // end::disableGlobalDependencySubstitutionRules[]
diff --git a/subprojects/docs/src/snippets/dependencyManagement/customizingResolution-deactivateGlobalSubstitution/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/dependencyManagement/customizingResolution-deactivateGlobalSubstitution/kotlin/build.gradle.kts
index f6940bc..d4f1d64 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/customizingResolution-deactivateGlobalSubstitution/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/dependencyManagement/customizingResolution-deactivateGlobalSubstitution/kotlin/build.gradle.kts
@@ -11,7 +11,6 @@
 
     extendsFrom(configurations.runtimeClasspath.get())
     isCanBeConsumed = false
-    isCanBeResolved = true
     attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
 }
 // end::disableGlobalDependencySubstitutionRules[]
diff --git a/subprojects/docs/src/snippets/dependencyManagement/dependencyVerification-disablingVerification/groovy/build.gradle b/subprojects/docs/src/snippets/dependencyManagement/dependencyVerification-disablingVerification/groovy/build.gradle
index 9dc7292..89cdb2d 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/dependencyVerification-disablingVerification/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/dependencyManagement/dependencyVerification-disablingVerification/groovy/build.gradle
@@ -10,7 +10,6 @@
     myPluginClasspath {
         extendsFrom(myPlugin)
         canBeConsumed = false
-        canBeResolved = true
     }
 }
 
diff --git a/subprojects/docs/src/snippets/dependencyManagement/dependencyVerification-disablingVerification/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/dependencyManagement/dependencyVerification-disablingVerification/kotlin/build.gradle.kts
index 6a07eab..dc064df 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/dependencyVerification-disablingVerification/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/dependencyManagement/dependencyVerification-disablingVerification/kotlin/build.gradle.kts
@@ -9,7 +9,6 @@
 val myPluginClasspath by configurations.creating {
     extendsFrom(myPlugin)
     isCanBeConsumed = false
-    isCanBeResolved = true
 }
 
 dependencies {
diff --git a/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/groovy/build.gradle b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/groovy/build.gradle
new file mode 100644
index 0000000..1911ee1
--- /dev/null
+++ b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/groovy/build.gradle
@@ -0,0 +1,13 @@
+plugins {
+    id 'java-library'
+}
+
+repositories { // <1>
+    google() // <2>
+    mavenCentral()
+}
+
+dependencies { // <3>
+    implementation 'com.google.guava:guava:31.1-jre' // <4>
+    testImplementation 'junit:junit:4.13.2'
+}
diff --git a/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/groovy/settings.gradle b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/groovy/settings.gradle
new file mode 100644
index 0000000..ba4f726
--- /dev/null
+++ b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/groovy/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = "introduction-core"
+
diff --git a/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/kotlin/build.gradle.kts
new file mode 100644
index 0000000..a743fb4
--- /dev/null
+++ b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/kotlin/build.gradle.kts
@@ -0,0 +1,13 @@
+plugins {
+    `java-library`
+}
+
+repositories { // <1>
+    google() // <2>
+    mavenCentral()
+}
+
+dependencies { // <3>
+    implementation("com.google.guava:guava:31.1-jre") // <4>
+    testImplementation("junit:junit:4.13.2")
+}
diff --git a/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/kotlin/settings.gradle.kts b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/kotlin/settings.gradle.kts
new file mode 100644
index 0000000..83c3fb6
--- /dev/null
+++ b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/kotlin/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "introduction-core"
diff --git a/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/tests/dependencyIntroReport.sample.conf b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/tests/dependencyIntroReport.sample.conf
new file mode 100644
index 0000000..1e94041
--- /dev/null
+++ b/subprojects/docs/src/snippets/dependencyManagement/introductionCoreDependencies/tests/dependencyIntroReport.sample.conf
@@ -0,0 +1,3 @@
+executable: gradle
+args: dependencies --configuration compileClasspath
+flags: --quiet
diff --git a/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForConfiguration/groovy/src/main/java/Person.java b/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForConfiguration/groovy/src/main/java/Person.java
index f842cc6..b58762b 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForConfiguration/groovy/src/main/java/Person.java
+++ b/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForConfiguration/groovy/src/main/java/Person.java
@@ -17,4 +17,4 @@ public int getAge() {
     public void setAge(int age) {
         this.age = age;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForConfiguration/kotlin/src/main/java/Person.java b/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForConfiguration/kotlin/src/main/java/Person.java
index f842cc6..b58762b 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForConfiguration/kotlin/src/main/java/Person.java
+++ b/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForConfiguration/kotlin/src/main/java/Person.java
@@ -17,4 +17,4 @@ public int getAge() {
     public void setAge(int age) {
         this.age = age;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForDependency/groovy/src/main/java/Person.java b/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForDependency/groovy/src/main/java/Person.java
index f842cc6..b58762b 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForDependency/groovy/src/main/java/Person.java
+++ b/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForDependency/groovy/src/main/java/Person.java
@@ -17,4 +17,4 @@ public int getAge() {
     public void setAge(int age) {
         this.age = age;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForDependency/kotlin/src/main/java/Person.java b/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForDependency/kotlin/src/main/java/Person.java
index f842cc6..b58762b 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForDependency/kotlin/src/main/java/Person.java
+++ b/subprojects/docs/src/snippets/dependencyManagement/managingTransitiveDependencies-excludeForDependency/kotlin/src/main/java/Person.java
@@ -17,4 +17,4 @@ public int getAge() {
     public void setAge(int age) {
         this.age = age;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced-published/tests/outgoingVariants.out b/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced-published/tests/outgoingVariants.out
index fa997ca..0d3d1ab 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced-published/tests/outgoingVariants.out
+++ b/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced-published/tests/outgoingVariants.out
@@ -2,7 +2,7 @@
 --------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-Description = API elements for main.
+API elements for the 'main' feature.
 
 Attributes
     - org.gradle.category            = library
@@ -41,7 +41,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Description = Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Attributes
     - org.gradle.category            = library
diff --git a/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced/tests/outgoingVariants.out b/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced/tests/outgoingVariants.out
index fa997ca..0d3d1ab 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced/tests/outgoingVariants.out
+++ b/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced/tests/outgoingVariants.out
@@ -2,7 +2,7 @@
 --------------------------------------------------
 Variant apiElements
 --------------------------------------------------
-Description = API elements for main.
+API elements for the 'main' feature.
 
 Attributes
     - org.gradle.category            = library
@@ -41,7 +41,7 @@
 --------------------------------------------------
 Variant runtimeElements
 --------------------------------------------------
-Description = Elements of runtime for main.
+Runtime elements for the 'main' feature.
 
 Attributes
     - org.gradle.category            = library
diff --git a/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-simple/groovy/consumer/build.gradle b/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-simple/groovy/consumer/build.gradle
index 1c5b9e9f..182186f 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-simple/groovy/consumer/build.gradle
+++ b/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-simple/groovy/consumer/build.gradle
@@ -6,7 +6,6 @@
 configurations {
     instrumentedClasspath {
         canBeConsumed = false
-        canBeResolved = true
     }
 }
 // end::resolvable-configuration[]
diff --git a/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-simple/kotlin/consumer/build.gradle.kts b/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-simple/kotlin/consumer/build.gradle.kts
index 28abf2e..2eaffe1 100644
--- a/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-simple/kotlin/consumer/build.gradle.kts
+++ b/subprojects/docs/src/snippets/dependencyManagement/modelingFeatures-crossProjectPublications-simple/kotlin/consumer/build.gradle.kts
@@ -5,7 +5,6 @@
 // tag::resolvable-configuration[]
 val instrumentedClasspath by configurations.creating {
     isCanBeConsumed = false
-    isCanBeResolved = true
 }
 // end::resolvable-configuration[]
 
diff --git a/subprojects/docs/src/snippets/developingPlugins/capabilitiesVsConventions/groovy/buildSrc/src/main/java/MyBasePlugin.java b/subprojects/docs/src/snippets/developingPlugins/capabilitiesVsConventions/groovy/buildSrc/src/main/java/MyBasePlugin.java
index 01d5d34..1e56c58 100644
--- a/subprojects/docs/src/snippets/developingPlugins/capabilitiesVsConventions/groovy/buildSrc/src/main/java/MyBasePlugin.java
+++ b/subprojects/docs/src/snippets/developingPlugins/capabilitiesVsConventions/groovy/buildSrc/src/main/java/MyBasePlugin.java
@@ -5,4 +5,4 @@ public class MyBasePlugin implements Plugin<Project> {
     public void apply(Project project) {
         // define capabilities
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/developingPlugins/capabilitiesVsConventions/groovy/buildSrc/src/main/java/MyPlugin.java b/subprojects/docs/src/snippets/developingPlugins/capabilitiesVsConventions/groovy/buildSrc/src/main/java/MyPlugin.java
index 2ef8725..edfe7a5 100644
--- a/subprojects/docs/src/snippets/developingPlugins/capabilitiesVsConventions/groovy/buildSrc/src/main/java/MyPlugin.java
+++ b/subprojects/docs/src/snippets/developingPlugins/capabilitiesVsConventions/groovy/buildSrc/src/main/java/MyPlugin.java
@@ -7,4 +7,4 @@ public void apply(Project project) {
 
         // define conventions
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/developingPlugins/testingPlugins/groovy/url-verifier-plugin/build.gradle b/subprojects/docs/src/snippets/developingPlugins/testingPlugins/groovy/url-verifier-plugin/build.gradle
index 0ddd05e..6b7c83a 100644
--- a/subprojects/docs/src/snippets/developingPlugins/testingPlugins/groovy/url-verifier-plugin/build.gradle
+++ b/subprojects/docs/src/snippets/developingPlugins/testingPlugins/groovy/url-verifier-plugin/build.gradle
@@ -50,12 +50,15 @@
 dependencies {
     testImplementation platform("org.spockframework:spock-bom:2.2-groovy-3.0")
     testImplementation 'org.spockframework:spock-core'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 
     integrationTestImplementation platform("org.spockframework:spock-bom:2.2-groovy-3.0")
     integrationTestImplementation 'org.spockframework:spock-core'
+    integrationTestRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 
     functionalTestImplementation platform("org.spockframework:spock-bom:2.2-groovy-3.0")
     functionalTestImplementation 'org.spockframework:spock-core'
+    functionalTestRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 
 tasks.withType(Test).configureEach {
diff --git a/subprojects/docs/src/snippets/developingPlugins/testingPlugins/kotlin/url-verifier-plugin/build.gradle.kts b/subprojects/docs/src/snippets/developingPlugins/testingPlugins/kotlin/url-verifier-plugin/build.gradle.kts
index 7226b8e21..3109482 100644
--- a/subprojects/docs/src/snippets/developingPlugins/testingPlugins/kotlin/url-verifier-plugin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/developingPlugins/testingPlugins/kotlin/url-verifier-plugin/build.gradle.kts
@@ -50,12 +50,15 @@
 dependencies {
     testImplementation(platform("org.spockframework:spock-bom:2.2-groovy-3.0"))
     testImplementation("org.spockframework:spock-core")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 
     "integrationTestImplementation"(platform("org.spockframework:spock-bom:2.2-groovy-3.0"))
     "integrationTestImplementation"("org.spockframework:spock-core")
+    "integrationTestRuntimeOnly"("org.junit.platform:junit-platform-launcher")
 
     "functionalTestImplementation"(platform("org.spockframework:spock-bom:2.2-groovy-3.0"))
     "functionalTestImplementation"("org.spockframework:spock-core")
+    "functionalTestRuntimeOnly"("org.junit.platform:junit-platform-launcher")
 }
 
 tasks.withType<Test>().configureEach {
diff --git a/subprojects/docs/src/snippets/ear/earCustomized/groovy/ear/build.gradle b/subprojects/docs/src/snippets/ear/earCustomized/groovy/ear/build.gradle
index 3afd105..52adba0 100644
--- a/subprojects/docs/src/snippets/ear/earCustomized/groovy/ear/build.gradle
+++ b/subprojects/docs/src/snippets/ear/earCustomized/groovy/ear/build.gradle
@@ -8,17 +8,16 @@
 dependencies {
     // The following dependencies will be the ear modules and
     // will be placed in the ear root
-    deploy project(path: ':war', configuration: 'archives')
+    deploy project(path: ':war', configuration: 'war')
 
     // The following dependencies will become ear libs and will
     // be placed in a dir configured via the libDirName property
     earlib group: 'log4j', name: 'log4j', version: '1.2.15', ext: 'jar'
 }
 
-ear {
+tasks.named('ear') {
     appDirectory = file('src/main/app')  // use application metadata found in this folder
-    // put dependent libraries into APP-INF/lib inside the generated EAR
-    libDirName 'APP-INF/lib'
+    libDirName 'APP-INF/lib' // put dependent libraries into APP-INF/lib inside the generated EAR
     deploymentDescriptor {  // custom entries for application.xml:
 //      fileName = "application.xml"  // same as the default value
 //      version = "6"  // same as the default value
diff --git a/subprojects/docs/src/snippets/ear/earCustomized/groovy/war/build.gradle b/subprojects/docs/src/snippets/ear/earCustomized/groovy/war/build.gradle
index f168fc0..ca0c7b6 100644
--- a/subprojects/docs/src/snippets/ear/earCustomized/groovy/war/build.gradle
+++ b/subprojects/docs/src/snippets/ear/earCustomized/groovy/war/build.gradle
@@ -6,6 +6,15 @@
     mavenCentral()
 }
 
+configurations {
+    war {
+        canBeResolved = false
+        outgoing {
+            artifact(tasks.war)
+        }
+    }
+}
+
 dependencies {
     implementation group: 'log4j', name: 'log4j', version: '1.2.15', ext: 'jar'
 }
diff --git a/subprojects/docs/src/snippets/ear/earCustomized/kotlin/ear/build.gradle.kts b/subprojects/docs/src/snippets/ear/earCustomized/kotlin/ear/build.gradle.kts
index af70c10..27c4f59 100644
--- a/subprojects/docs/src/snippets/ear/earCustomized/kotlin/ear/build.gradle.kts
+++ b/subprojects/docs/src/snippets/ear/earCustomized/kotlin/ear/build.gradle.kts
@@ -8,20 +8,16 @@
 dependencies {
     // The following dependencies will be the ear modules and
     // will be placed in the ear root
-    deploy(project(path = ":war", configuration = "archives"))
+    deploy(project(path = ":war", configuration = "war"))
 
     // The following dependencies will become ear libs and will
     // be placed in a dir configured via the libDirName property
     earlib(group = "log4j", name = "log4j", version = "1.2.15", ext = "jar")
 }
 
-tasks.named<Ear>("ear") {
+tasks.ear {
     appDirectory.set(file("src/main/app"))  // use application metadata found in this folder
-}
-
-ear {
-    // put dependent libraries into APP-INF/lib inside the generated EAR
-    libDirName = "APP-INF/lib"
+    libDirName = "APP-INF/lib" // put dependent libraries into APP-INF/lib inside the generated EAR
     deploymentDescriptor {  // custom entries for application.xml:
 //      fileName = "application.xml"  // same as the default value
 //      version = "6"  // same as the default value
diff --git a/subprojects/docs/src/snippets/ear/earCustomized/kotlin/war/build.gradle.kts b/subprojects/docs/src/snippets/ear/earCustomized/kotlin/war/build.gradle.kts
index 1ca0636..8c6ee66 100644
--- a/subprojects/docs/src/snippets/ear/earCustomized/kotlin/war/build.gradle.kts
+++ b/subprojects/docs/src/snippets/ear/earCustomized/kotlin/war/build.gradle.kts
@@ -6,6 +6,15 @@
     mavenCentral()
 }
 
+configurations {
+    create("war") {
+        isCanBeResolved = false
+        outgoing {
+            artifact(tasks["war"])
+        }
+    }
+}
+
 dependencies {
     implementation(group = "log4j", name = "log4j", version = "1.2.15", ext = "jar")
 }
diff --git a/subprojects/docs/src/snippets/ear/earWithWar/groovy/build.gradle b/subprojects/docs/src/snippets/ear/earWithWar/groovy/build.gradle
index af4c8bb..5a86a81 100644
--- a/subprojects/docs/src/snippets/ear/earWithWar/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/ear/earWithWar/groovy/build.gradle
@@ -9,7 +9,7 @@
 }
 
 dependencies {
-    deploy project(path: ':war', configuration: 'archives')
+    deploy project(path: ':war', configuration: 'war')
 
     earlib group: 'log4j', name: 'log4j', version: '1.2.15', ext: 'jar'
 }
diff --git a/subprojects/docs/src/snippets/ear/earWithWar/groovy/war/build.gradle b/subprojects/docs/src/snippets/ear/earWithWar/groovy/war/build.gradle
index f168fc0..ca0c7b6 100644
--- a/subprojects/docs/src/snippets/ear/earWithWar/groovy/war/build.gradle
+++ b/subprojects/docs/src/snippets/ear/earWithWar/groovy/war/build.gradle
@@ -6,6 +6,15 @@
     mavenCentral()
 }
 
+configurations {
+    war {
+        canBeResolved = false
+        outgoing {
+            artifact(tasks.war)
+        }
+    }
+}
+
 dependencies {
     implementation group: 'log4j', name: 'log4j', version: '1.2.15', ext: 'jar'
 }
diff --git a/subprojects/docs/src/snippets/ear/earWithWar/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/ear/earWithWar/kotlin/build.gradle.kts
index 2f59055..7a8f436 100644
--- a/subprojects/docs/src/snippets/ear/earWithWar/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/ear/earWithWar/kotlin/build.gradle.kts
@@ -9,7 +9,7 @@
 }
 
 dependencies {
-    deploy(project(path = ":war", configuration = "archives"))
+    deploy(project(path = ":war", configuration = "war"))
 
     earlib(group = "log4j", name = "log4j", version = "1.2.15", ext = "jar")
 }
diff --git a/subprojects/docs/src/snippets/ear/earWithWar/kotlin/war/build.gradle.kts b/subprojects/docs/src/snippets/ear/earWithWar/kotlin/war/build.gradle.kts
index 1ca0636..8c6ee66 100644
--- a/subprojects/docs/src/snippets/ear/earWithWar/kotlin/war/build.gradle.kts
+++ b/subprojects/docs/src/snippets/ear/earWithWar/kotlin/war/build.gradle.kts
@@ -6,6 +6,15 @@
     mavenCentral()
 }
 
+configurations {
+    create("war") {
+        isCanBeResolved = false
+        outgoing {
+            artifact(tasks["war"])
+        }
+    }
+}
+
 dependencies {
     implementation(group = "log4j", name = "log4j", version = "1.2.15", ext = "jar")
 }
diff --git a/subprojects/docs/src/snippets/files/misc/groovy/project2/settings.gradle b/subprojects/docs/src/snippets/files/misc/groovy/project2/settings.gradle
index 9683ad4..a6b36f5 100644
--- a/subprojects/docs/src/snippets/files/misc/groovy/project2/settings.gradle
+++ b/subprojects/docs/src/snippets/files/misc/groovy/project2/settings.gradle
@@ -1 +1 @@
-rootProject.name = 'project2'
\ No newline at end of file
+rootProject.name = 'project2'
diff --git a/subprojects/docs/src/snippets/groovy/crossCompilation/groovy/src/main/java/org/gradle/Person.java b/subprojects/docs/src/snippets/groovy/crossCompilation/groovy/src/main/java/org/gradle/Person.java
index 94650ea..8df4e4e 100644
--- a/subprojects/docs/src/snippets/groovy/crossCompilation/groovy/src/main/java/org/gradle/Person.java
+++ b/subprojects/docs/src/snippets/groovy/crossCompilation/groovy/src/main/java/org/gradle/Person.java
@@ -2,4 +2,4 @@
 
 public interface Person {
     String getName();
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/groovy/crossCompilation/groovy/src/test/groovy/org/gradle/PersonTest.groovy b/subprojects/docs/src/snippets/groovy/crossCompilation/groovy/src/test/groovy/org/gradle/PersonTest.groovy
index 724c798..36e472f 100644
--- a/subprojects/docs/src/snippets/groovy/crossCompilation/groovy/src/test/groovy/org/gradle/PersonTest.groovy
+++ b/subprojects/docs/src/snippets/groovy/crossCompilation/groovy/src/test/groovy/org/gradle/PersonTest.groovy
@@ -13,4 +13,4 @@
         Person p = new GroovyPerson(name: 'name')
         assertEquals('name', p.name)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/groovy/crossCompilation/kotlin/src/main/java/org/gradle/Person.java b/subprojects/docs/src/snippets/groovy/crossCompilation/kotlin/src/main/java/org/gradle/Person.java
index 94650ea..8df4e4e 100644
--- a/subprojects/docs/src/snippets/groovy/crossCompilation/kotlin/src/main/java/org/gradle/Person.java
+++ b/subprojects/docs/src/snippets/groovy/crossCompilation/kotlin/src/main/java/org/gradle/Person.java
@@ -2,4 +2,4 @@
 
 public interface Person {
     String getName();
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/groovy/crossCompilation/kotlin/src/test/groovy/org/gradle/PersonTest.groovy b/subprojects/docs/src/snippets/groovy/crossCompilation/kotlin/src/test/groovy/org/gradle/PersonTest.groovy
index 724c798..36e472f 100644
--- a/subprojects/docs/src/snippets/groovy/crossCompilation/kotlin/src/test/groovy/org/gradle/PersonTest.groovy
+++ b/subprojects/docs/src/snippets/groovy/crossCompilation/kotlin/src/test/groovy/org/gradle/PersonTest.groovy
@@ -13,4 +13,4 @@
         Person p = new GroovyPerson(name: 'name')
         assertEquals('name', p.name)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/groovy/customizedLayout/groovy/test/groovy/org/gradle/PersonTest.groovy b/subprojects/docs/src/snippets/groovy/customizedLayout/groovy/test/groovy/org/gradle/PersonTest.groovy
index 97a4bd3..8dda9e3 100644
--- a/subprojects/docs/src/snippets/groovy/customizedLayout/groovy/test/groovy/org/gradle/PersonTest.groovy
+++ b/subprojects/docs/src/snippets/groovy/customizedLayout/groovy/test/groovy/org/gradle/PersonTest.groovy
@@ -8,4 +8,4 @@
         Person p = new Person(name: 'name')
         assertEquals('name', p.name)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/groovy/customizedLayout/kotlin/test/groovy/org/gradle/PersonTest.groovy b/subprojects/docs/src/snippets/groovy/customizedLayout/kotlin/test/groovy/org/gradle/PersonTest.groovy
index 97a4bd3..8dda9e3 100644
--- a/subprojects/docs/src/snippets/groovy/customizedLayout/kotlin/test/groovy/org/gradle/PersonTest.groovy
+++ b/subprojects/docs/src/snippets/groovy/customizedLayout/kotlin/test/groovy/org/gradle/PersonTest.groovy
@@ -8,4 +8,4 @@
         Person p = new Person(name: 'name')
         assertEquals('name', p.name)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/initScripts/cacheMarking/groovy/build.gradle b/subprojects/docs/src/snippets/initScripts/cacheMarking/groovy/build.gradle
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/subprojects/docs/src/snippets/initScripts/cacheMarking/groovy/build.gradle
diff --git a/subprojects/docs/src/snippets/initScripts/cacheMarking/groovy/gradleUserHome/init.d/cache-settings.gradle b/subprojects/docs/src/snippets/initScripts/cacheMarking/groovy/gradleUserHome/init.d/cache-settings.gradle
new file mode 100644
index 0000000..6e12f70
--- /dev/null
+++ b/subprojects/docs/src/snippets/initScripts/cacheMarking/groovy/gradleUserHome/init.d/cache-settings.gradle
@@ -0,0 +1,6 @@
+beforeSettings { settings ->
+    settings.caches {
+        // Disable cache marking for all caches
+        markingStrategy = MarkingStrategy.NONE
+    }
+}
diff --git a/subprojects/docs/src/snippets/initScripts/cacheMarking/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/initScripts/cacheMarking/kotlin/build.gradle.kts
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/subprojects/docs/src/snippets/initScripts/cacheMarking/kotlin/build.gradle.kts
diff --git a/subprojects/docs/src/snippets/initScripts/cacheMarking/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts b/subprojects/docs/src/snippets/initScripts/cacheMarking/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts
new file mode 100644
index 0000000..ebe93d8
--- /dev/null
+++ b/subprojects/docs/src/snippets/initScripts/cacheMarking/kotlin/gradleUserHome/init.d/cache-settings.gradle.kts
@@ -0,0 +1,6 @@
+beforeSettings {
+    caches {
+        // Disable cache marking for all caches
+        markingStrategy.set(MarkingStrategy.NONE)
+    }
+}
diff --git a/subprojects/docs/src/snippets/initScripts/cacheMarking/tests-groovy/cacheRetention.sample.conf b/subprojects/docs/src/snippets/initScripts/cacheMarking/tests-groovy/cacheRetention.sample.conf
new file mode 100644
index 0000000..58b48f3
--- /dev/null
+++ b/subprojects/docs/src/snippets/initScripts/cacheMarking/tests-groovy/cacheRetention.sample.conf
@@ -0,0 +1,3 @@
+executable: gradle
+args: help
+flags: "-I gradleUserHome/init.d/cache-settings.gradle"
diff --git a/subprojects/docs/src/snippets/initScripts/cacheMarking/tests-kotlin/cacheRetention.sample.conf b/subprojects/docs/src/snippets/initScripts/cacheMarking/tests-kotlin/cacheRetention.sample.conf
new file mode 100644
index 0000000..b2dfb8f
--- /dev/null
+++ b/subprojects/docs/src/snippets/initScripts/cacheMarking/tests-kotlin/cacheRetention.sample.conf
@@ -0,0 +1,3 @@
+executable: gradle
+args: help
+flags: "-I gradleUserHome/init.d/cache-settings.gradle.kts"
diff --git a/subprojects/docs/src/snippets/ivy-publish/conditional-publishing/groovy/src/main/java/Sample.java b/subprojects/docs/src/snippets/ivy-publish/conditional-publishing/groovy/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/ivy-publish/conditional-publishing/groovy/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/ivy-publish/conditional-publishing/groovy/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/ivy-publish/publish-artifact/groovy/build.gradle b/subprojects/docs/src/snippets/ivy-publish/publish-artifact/groovy/build.gradle
index 44eaf04..183c3d4 100644
--- a/subprojects/docs/src/snippets/ivy-publish/publish-artifact/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/ivy-publish/publish-artifact/groovy/build.gradle
@@ -7,8 +7,11 @@
 version = "1.0"
 
 // tag::custom-artifact[]
+configurations {
+    conf
+}
 def rpmFile = layout.buildDirectory.file("rpms/my-package.rpm")
-def rpmArtifact = artifacts.add("archives", rpmFile.get().asFile) {
+def rpmArtifact = artifacts.add("conf", rpmFile.get().asFile) {
     type "rpm"
     builtBy "rpm"
 }
diff --git a/subprojects/docs/src/snippets/java-library/module-disabled/tests/buildJavaModule.out b/subprojects/docs/src/snippets/java-library/module-disabled/tests/buildJavaModule.out
index dbb8a74..a11c844 100644
--- a/subprojects/docs/src/snippets/java-library/module-disabled/tests/buildJavaModule.out
+++ b/subprojects/docs/src/snippets/java-library/module-disabled/tests/buildJavaModule.out
@@ -16,10 +16,7 @@
 > Compilation failed; see the compiler error output for details.
 
 * Try:
-> Run with --stacktrace option to get the stack trace.
-> Run with --info or --debug option to get more log output.
+> Run with --info option to get more log output.
 > Run with --scan to get full insights.
 
-* Get more help at https://help.gradle.org
-
 BUILD FAILED in 0s
diff --git a/subprojects/docs/src/snippets/java/basic/groovy/build.gradle b/subprojects/docs/src/snippets/java/basic/groovy/build.gradle
index 3a6418d..afcfd1d 100644
--- a/subprojects/docs/src/snippets/java/basic/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/java/basic/groovy/build.gradle
@@ -27,6 +27,7 @@
 // tag::java-basic-test-config[]
 dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 
 tasks.named('test', Test) {
@@ -55,6 +56,7 @@
 
 dependencies {
     intTestImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+    intTestRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 // end::practical-integ-test-source-set[]
 
diff --git a/subprojects/docs/src/snippets/java/basic/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/java/basic/kotlin/build.gradle.kts
index 2b393aa..1b7bb55 100644
--- a/subprojects/docs/src/snippets/java/basic/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/java/basic/kotlin/build.gradle.kts
@@ -27,6 +27,7 @@
 // tag::java-basic-test-config[]
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 
 tasks.named<Test>("test") {
@@ -51,11 +52,13 @@
 val intTestImplementation by configurations.getting {
     extendsFrom(configurations.implementation.get())
 }
+val intTestRuntimeOnly by configurations.getting
 
 configurations["intTestRuntimeOnly"].extendsFrom(configurations.runtimeOnly.get())
 
 dependencies {
     intTestImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    intTestRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 // end::practical-integ-test-source-set[]
 
diff --git a/subprojects/docs/src/snippets/java/customDirs/groovy/build.gradle b/subprojects/docs/src/snippets/java/customDirs/groovy/build.gradle
index abe71a8..b244c2f 100644
--- a/subprojects/docs/src/snippets/java/customDirs/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/java/customDirs/groovy/build.gradle
@@ -2,9 +2,11 @@
     id 'java'
 }
 
-sourceCompatibility = '1.8'
-targetCompatibility = '1.8'
 version = '1.2.1'
+java {
+    sourceCompatibility = '1.8'
+    targetCompatibility = '1.8'
+}
 
 repositories {
     mavenCentral()
@@ -49,16 +51,16 @@
 
 // tag::custom-report-dirs[]
 reporting.baseDir = "my-reports"
-testResultsDirName = "$buildDir/my-test-results"
+java.testResultsDir = layout.buildDirectory.dir("my-test-results")
 
 tasks.register('showDirs') {
     def rootDir = project.rootDir
-    def reportsDir = project.reportsDir
-    def testResultsDir = project.testResultsDir
+    def reportsDir = project.reporting.baseDirectory
+    def testResultsDir = project.java.testResultsDir
 
     doLast {
-        logger.quiet(rootDir.toPath().relativize(reportsDir.toPath()).toString())
-        logger.quiet(rootDir.toPath().relativize(testResultsDir.toPath()).toString())
+        logger.quiet(rootDir.toPath().relativize(reportsDir.get().asFile.toPath()).toString())
+        logger.quiet(rootDir.toPath().relativize(testResultsDir.get().asFile.toPath()).toString())
     }
 }
 // end::custom-report-dirs[]
diff --git a/subprojects/docs/src/snippets/java/customDirs/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/java/customDirs/kotlin/build.gradle.kts
index 5cb6ec4..a7373fc 100644
--- a/subprojects/docs/src/snippets/java/customDirs/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/java/customDirs/kotlin/build.gradle.kts
@@ -52,7 +52,7 @@
 
 // tag::custom-report-dirs[]
 reporting.baseDir = file("my-reports")
-project.setProperty("testResultsDirName", "$buildDir/my-test-results")
+java.testResultsDir.set(layout.buildDirectory.dir("my-test-results"))
 
 tasks.register("showDirs") {
     val rootDir = project.rootDir
diff --git a/subprojects/docs/src/snippets/java/fixtures/groovy/build.gradle b/subprojects/docs/src/snippets/java/fixtures/groovy/build.gradle
index 50ea4d1..a11526b 100644
--- a/subprojects/docs/src/snippets/java/fixtures/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/java/fixtures/groovy/build.gradle
@@ -28,7 +28,6 @@
     functionalTestClasspath {
         extendsFrom(functionalTest)
         canBeConsumed = false
-        canBeResolved = true
         attributes {
             attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_API))
         }
diff --git a/subprojects/docs/src/snippets/java/fixtures/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/java/fixtures/kotlin/build.gradle.kts
index b493f70..fd7bbf5 100644
--- a/subprojects/docs/src/snippets/java/fixtures/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/java/fixtures/kotlin/build.gradle.kts
@@ -27,7 +27,6 @@
 val functionalTestClasspath by configurations.creating {
     extendsFrom(functionalTest)
     isCanBeConsumed = false
-    isCanBeResolved = true
     attributes {
         attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_API))
     }
diff --git a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/build.gradle b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/build.gradle
index be64153..be7f695 100644
--- a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/build.gradle
+++ b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/build.gradle
@@ -1,3 +1,3 @@
 plugins {
     id "java"
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/src/main/java/Entity.java b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/src/main/java/Entity.java
index ccf5d7a..4c49587 100644
--- a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/src/main/java/Entity.java
+++ b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/src/main/java/Entity.java
@@ -3,4 +3,4 @@
  */
 public @interface Entity {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/src/main/java/Service.java b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/src/main/java/Service.java
index 93f47d0..2d98f47 100644
--- a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/src/main/java/Service.java
+++ b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/library/src/main/java/Service.java
@@ -3,4 +3,4 @@
  */
 public @interface Service {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/user/src/main/java/Service1.java b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/user/src/main/java/Service1.java
index 1b58913..a1aae3a 100644
--- a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/user/src/main/java/Service1.java
+++ b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/user/src/main/java/Service1.java
@@ -1,4 +1,4 @@
 @Service
 public class Service1 {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/user/src/main/java/Service2.java b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/user/src/main/java/Service2.java
index 0da3268..d3ee7a9 100644
--- a/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/user/src/main/java/Service2.java
+++ b/subprojects/docs/src/snippets/java/incrementalAnnotationProcessing/groovy/user/src/main/java/Service2.java
@@ -1,4 +1,4 @@
 @Service
 public class Service2 {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/java/toolchain-filters/tests/matchingVendorWithCC.out b/subprojects/docs/src/snippets/java/toolchain-filters/tests/matchingVendorWithCC.out
index b2b6e08..704d283 100644
--- a/subprojects/docs/src/snippets/java/toolchain-filters/tests/matchingVendorWithCC.out
+++ b/subprojects/docs/src/snippets/java/toolchain-filters/tests/matchingVendorWithCC.out
@@ -1,2 +1,2 @@
 Configuration cache state could not be cached: field `javaCompiler` of task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: error writing value of type 'org.gradle.api.internal.provider.DefaultProperty'
-> No matching toolchains found for requested specification: {languageVersion=11, vendor=matching('customString'), implementation=vendor-specific}.
+> No matching toolchains found for requested specification: {languageVersion=11, vendor=matching('customString'), implementation=vendor-specific} for %OS% on %ARCH%.
diff --git a/subprojects/docs/src/snippets/java/toolchain-filters/tests/matchingVendorWithoutCC.out b/subprojects/docs/src/snippets/java/toolchain-filters/tests/matchingVendorWithoutCC.out
index f394ecf..2e4a818 100644
--- a/subprojects/docs/src/snippets/java/toolchain-filters/tests/matchingVendorWithoutCC.out
+++ b/subprojects/docs/src/snippets/java/toolchain-filters/tests/matchingVendorWithoutCC.out
@@ -1,4 +1,4 @@
 Execution failed for task ':compileJava'.
 > Error while evaluating property 'javaCompiler' of task ':compileJava'.
    > Failed to calculate the value of task ':compileJava' property 'javaCompiler'.
-      > No matching toolchains found for requested specification: {languageVersion=11, vendor=matching('customString'), implementation=vendor-specific}.
+      > No matching toolchains found for requested specification: {languageVersion=11, vendor=matching('customString'), implementation=vendor-specific} for %OS% on %ARCH%.
diff --git a/subprojects/docs/src/snippets/java/toolchain-foojay/groovy/build.gradle b/subprojects/docs/src/snippets/java/toolchain-foojay/groovy/build.gradle
index 79acf72..b2cc23a 100644
--- a/subprojects/docs/src/snippets/java/toolchain-foojay/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/java/toolchain-foojay/groovy/build.gradle
@@ -6,4 +6,4 @@
     toolchain {
         languageVersion = JavaLanguageVersion.of(11)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/java/toolchain-foojay/groovy/settings.gradle b/subprojects/docs/src/snippets/java/toolchain-foojay/groovy/settings.gradle
index e843b2f..195a597 100644
--- a/subprojects/docs/src/snippets/java/toolchain-foojay/groovy/settings.gradle
+++ b/subprojects/docs/src/snippets/java/toolchain-foojay/groovy/settings.gradle
@@ -2,4 +2,4 @@
 plugins {
     id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0'
 }
-// end::plugin-application[]
\ No newline at end of file
+// end::plugin-application[]
diff --git a/subprojects/docs/src/snippets/java/toolchain-management/groovy/build.gradle b/subprojects/docs/src/snippets/java/toolchain-management/groovy/build.gradle
index 79acf72..b2cc23a 100644
--- a/subprojects/docs/src/snippets/java/toolchain-management/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/java/toolchain-management/groovy/build.gradle
@@ -6,4 +6,4 @@
     toolchain {
         languageVersion = JavaLanguageVersion.of(11)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/java/toolchain-management/groovy/buildSrc/src/main/java/org/myorg/JavaToolchainResolverImplementation.java b/subprojects/docs/src/snippets/java/toolchain-management/groovy/buildSrc/src/main/java/org/myorg/JavaToolchainResolverImplementation.java
index 8eee21d..3d4d773 100644
--- a/subprojects/docs/src/snippets/java/toolchain-management/groovy/buildSrc/src/main/java/org/myorg/JavaToolchainResolverImplementation.java
+++ b/subprojects/docs/src/snippets/java/toolchain-management/groovy/buildSrc/src/main/java/org/myorg/JavaToolchainResolverImplementation.java
@@ -14,4 +14,4 @@ public Optional<JavaToolchainDownload> resolve(JavaToolchainRequest request) { /
         return Optional.empty(); // custom mapping logic goes here instead
     }
 }
-// end::java-toolchain-resolver-implementation[]
\ No newline at end of file
+// end::java-toolchain-resolver-implementation[]
diff --git a/subprojects/docs/src/snippets/java/toolchain-management/groovy/buildSrc/src/main/java/org/myorg/JavaToolchainResolverPlugin.java b/subprojects/docs/src/snippets/java/toolchain-management/groovy/buildSrc/src/main/java/org/myorg/JavaToolchainResolverPlugin.java
index d4ce1d6..2e8f292 100644
--- a/subprojects/docs/src/snippets/java/toolchain-management/groovy/buildSrc/src/main/java/org/myorg/JavaToolchainResolverPlugin.java
+++ b/subprojects/docs/src/snippets/java/toolchain-management/groovy/buildSrc/src/main/java/org/myorg/JavaToolchainResolverPlugin.java
@@ -18,4 +18,4 @@ public void apply(Settings settings) {
         registry.register(JavaToolchainResolverImplementation.class);
     }
 }
-// end::java-toolchain-resolver-plugin[]
\ No newline at end of file
+// end::java-toolchain-resolver-plugin[]
diff --git a/subprojects/docs/src/snippets/java/toolchain-management/groovy/settings.gradle b/subprojects/docs/src/snippets/java/toolchain-management/groovy/settings.gradle
index acb19be..83021b0 100644
--- a/subprojects/docs/src/snippets/java/toolchain-management/groovy/settings.gradle
+++ b/subprojects/docs/src/snippets/java/toolchain-management/groovy/settings.gradle
@@ -49,4 +49,4 @@
     Optional<JavaToolchainDownload> resolve(JavaToolchainRequest request) {
         return Optional.empty()
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/kotlinDsl/accessors/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/kotlinDsl/accessors/kotlin/build.gradle.kts
index a502a85..d40fc14 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/accessors/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/kotlinDsl/accessors/kotlin/build.gradle.kts
@@ -29,6 +29,7 @@
 tasks {
     test {                                  // <5>
         testLogging.showExceptions = true
+        useJUnit()
     }
 }
 // end::accessors[]
diff --git a/subprojects/docs/src/snippets/kotlinDsl/accessors/kotlin/src/test/java/TestSource.java b/subprojects/docs/src/snippets/kotlinDsl/accessors/kotlin/src/test/java/TestSource.java
index 8ff153e..5dfc596 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/accessors/kotlin/src/test/java/TestSource.java
+++ b/subprojects/docs/src/snippets/kotlinDsl/accessors/kotlin/src/test/java/TestSource.java
@@ -1,2 +1,4 @@
 public class TestSource {
+    @org.junit.Test
+    public void test() { }
 }
diff --git a/subprojects/docs/src/snippets/kotlinDsl/androidBuild/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/kotlinDsl/androidBuild/kotlin/build.gradle.kts
index dc0095e..0315e07 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/androidBuild/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/kotlinDsl/androidBuild/kotlin/build.gradle.kts
@@ -2,7 +2,7 @@
 plugins {
     id("com.android.application") version "7.3.0" apply false
 // end::android[]
-    kotlin("android") version "1.8.10" apply false
+    kotlin("android") version "1.8.21" apply false
 // tag::android[]
 }
 // end::android[]
diff --git a/subprojects/docs/src/snippets/kotlinDsl/androidSingleBuild/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/kotlinDsl/androidSingleBuild/kotlin/build.gradle.kts
index abcc861..0f2b879 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/androidSingleBuild/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/kotlinDsl/androidSingleBuild/kotlin/build.gradle.kts
@@ -2,7 +2,7 @@
 plugins {
     id("com.android.application") version "7.3.0"
 // end::android[]
-    kotlin("android") version "1.8.10"
+    kotlin("android") version "1.8.21"
 // tag::android[]
 }
 
diff --git a/subprojects/docs/src/snippets/kotlinDsl/applying-plugins-declarative/tests/sanityCheck.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/applying-plugins-declarative/tests/sanityCheck.sample.conf
new file mode 100644
index 0000000..1b72008
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/applying-plugins-declarative/tests/sanityCheck.sample.conf
@@ -0,0 +1,4 @@
+executable: gradle
+args: tasks
+# Do not fail for deprecation warnings: Project.getConvention; Spring boot 3.0.2+ no longer uses that API
+flags: "--warning-mode=none"
diff --git a/subprojects/docs/src/snippets/kotlinDsl/applying-plugins-imperative/tests/sanityCheck.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/applying-plugins-imperative/tests/sanityCheck.sample.conf
new file mode 100644
index 0000000..1b72008
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/applying-plugins-imperative/tests/sanityCheck.sample.conf
@@ -0,0 +1,4 @@
+executable: gradle
+args: tasks
+# Do not fail for deprecation warnings: Project.getConvention; Spring boot 3.0.2+ no longer uses that API
+flags: "--warning-mode=none"
diff --git a/subprojects/docs/src/snippets/kotlinDsl/assignment/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/kotlinDsl/assignment/kotlin/build.gradle.kts
new file mode 100644
index 0000000..57aeb9d
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/assignment/kotlin/build.gradle.kts
@@ -0,0 +1,30 @@
+plugins {
+    `java-library`
+}
+
+// tag::assignment[]
+java {
+    toolchain {
+        languageVersion = JavaLanguageVersion.of(17)
+    }
+}
+
+abstract class WriteJavaVersionTask : DefaultTask() {
+    @get:Input
+    abstract val javaVersion: Property<String>
+    @get:OutputFile
+    abstract val output: RegularFileProperty
+
+    @TaskAction
+    fun execute() {
+        output.get().asFile.writeText("Java version: ${javaVersion.get()}")
+    }
+}
+
+tasks.register<WriteJavaVersionTask>("writeJavaVersion") {
+    javaVersion.set("17") // <1>
+    javaVersion = "17" // <2>
+    javaVersion = java.toolchain.languageVersion.map { it.toString() } // <3>
+    output = layout.buildDirectory.file("writeJavaVersion/javaVersion.txt")
+}
+// end::assignment[]
diff --git a/subprojects/docs/src/snippets/kotlinDsl/assignment/kotlin/settings.gradle.kts b/subprojects/docs/src/snippets/kotlinDsl/assignment/kotlin/settings.gradle.kts
new file mode 100644
index 0000000..eea7149
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/assignment/kotlin/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "assignment"
diff --git a/subprojects/docs/src/snippets/kotlinDsl/assignment/tests/accessors.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/assignment/tests/accessors.sample.conf
new file mode 100644
index 0000000..1e81660
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/assignment/tests/accessors.sample.conf
@@ -0,0 +1,2 @@
+executable: gradle
+args: build
diff --git a/subprojects/docs/src/snippets/kotlinDsl/configurations-and-dependencies-declarative/tests/dependencies.out b/subprojects/docs/src/snippets/kotlinDsl/configurations-and-dependencies-declarative/tests/dependencies.out
index c3f2ab3..d0688ba 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/configurations-and-dependencies-declarative/tests/dependencies.out
+++ b/subprojects/docs/src/snippets/kotlinDsl/configurations-and-dependencies-declarative/tests/dependencies.out
@@ -1,14 +1,14 @@
 compileClasspath - Compile classpath for source set 'main'.
 \--- com.example:lib:1.1 FAILED
 
-implementation - Implementation only dependencies for source set 'main'. (n)
+implementation - Implementation dependencies for the 'main' feature. (n)
 \--- com.example:lib:1.1 (n)
 
 runtimeClasspath - Runtime classpath of source set 'main'.
 +--- com.example:lib:1.1 FAILED
 \--- com.example:runtime:1.0 FAILED
 
-runtimeOnly - Runtime only dependencies for source set 'main'. (n)
+runtimeOnly - Runtime-only dependencies for the 'main' feature. (n)
 \--- com.example:runtime:1.0 (n)
 
 testCompileClasspath - Compile classpath for source set 'test'.
diff --git a/subprojects/docs/src/snippets/kotlinDsl/configurations-and-dependencies-imperative/tests/dependencies.out b/subprojects/docs/src/snippets/kotlinDsl/configurations-and-dependencies-imperative/tests/dependencies.out
index c3f2ab3..d0688ba 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/configurations-and-dependencies-imperative/tests/dependencies.out
+++ b/subprojects/docs/src/snippets/kotlinDsl/configurations-and-dependencies-imperative/tests/dependencies.out
@@ -1,14 +1,14 @@
 compileClasspath - Compile classpath for source set 'main'.
 \--- com.example:lib:1.1 FAILED
 
-implementation - Implementation only dependencies for source set 'main'. (n)
+implementation - Implementation dependencies for the 'main' feature. (n)
 \--- com.example:lib:1.1 (n)
 
 runtimeClasspath - Runtime classpath of source set 'main'.
 +--- com.example:lib:1.1 FAILED
 \--- com.example:runtime:1.0 FAILED
 
-runtimeOnly - Runtime only dependencies for source set 'main'. (n)
+runtimeOnly - Runtime-only dependencies for the 'main' feature. (n)
 \--- com.example:runtime:1.0 (n)
 
 testCompileClasspath - Compile classpath for source set 'test'.
diff --git a/subprojects/docs/src/snippets/kotlinDsl/configuring-tasks-spring-boot/tests/sanityCheck.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/configuring-tasks-spring-boot/tests/sanityCheck.sample.conf
new file mode 100644
index 0000000..1b72008
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/configuring-tasks-spring-boot/tests/sanityCheck.sample.conf
@@ -0,0 +1,4 @@
+executable: gradle
+args: tasks
+# Do not fail for deprecation warnings: Project.getConvention; Spring boot 3.0.2+ no longer uses that API
+flags: "--warning-mode=none"
diff --git a/subprojects/docs/src/snippets/kotlinDsl/interoperability-closure-of/tests/closure-of.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/interoperability-closure-of/tests/closure-of.sample.conf
deleted file mode 100644
index f72a538..0000000
--- a/subprojects/docs/src/snippets/kotlinDsl/interoperability-closure-of/tests/closure-of.sample.conf
+++ /dev/null
@@ -1,2 +0,0 @@
-executable: gradle
-args: help
diff --git a/subprojects/docs/src/snippets/kotlinDsl/interoperability-closure-of/tests/sanityCheck.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/interoperability-closure-of/tests/sanityCheck.sample.conf
new file mode 100644
index 0000000..a6e1b4b
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/interoperability-closure-of/tests/sanityCheck.sample.conf
@@ -0,0 +1,4 @@
+executable: gradle
+args: tasks
+# Do not fail for deprecation warnings: docs refers to old plugins anyway
+flags: "--warning-mode=none"
diff --git a/subprojects/docs/src/snippets/kotlinDsl/interoperability-delegate-closure-of/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/kotlinDsl/interoperability-delegate-closure-of/kotlin/build.gradle.kts
index 585e24a..247ba5e 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/interoperability-delegate-closure-of/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/kotlinDsl/interoperability-delegate-closure-of/kotlin/build.gradle.kts
@@ -1,12 +1,13 @@
-val implementation by configurations.creating
+import org.akhikhl.gretty.FarmExtension
+
+plugins {
+    id("org.gretty") version "4.0.3"
+}
 
 // tag::delegateClosureOf[]
-dependencies {
-    implementation("group:artifact:1.2.3") {
-        artifact(delegateClosureOf<DependencyArtifact> {
-            // configuration for the artifact
-            name = "artifact-name"
-        })
-    }
+farms {
+    farm("OldCoreWar", delegateClosureOf<FarmExtension> {
+        // Config for the war here
+    })
 }
 // end::delegateClosureOf[]
diff --git a/subprojects/docs/src/snippets/kotlinDsl/interoperability-delegate-closure-of/tests/delegate-closure-of.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/interoperability-delegate-closure-of/tests/delegate-closure-of.sample.conf
index f72a538..47dec7e 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/interoperability-delegate-closure-of/tests/delegate-closure-of.sample.conf
+++ b/subprojects/docs/src/snippets/kotlinDsl/interoperability-delegate-closure-of/tests/delegate-closure-of.sample.conf
@@ -1,2 +1,4 @@
 executable: gradle
 args: help
+# Do not fail for deprecation warnings: docs refers to old plugins anyway
+flags: "--warning-mode=none"
diff --git a/subprojects/docs/src/snippets/kotlinDsl/multiProjectBuild/tests/build.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/multiProjectBuild/tests/build.sample.conf
index 1e81660..1cfc85b 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/multiProjectBuild/tests/build.sample.conf
+++ b/subprojects/docs/src/snippets/kotlinDsl/multiProjectBuild/tests/build.sample.conf
@@ -1,2 +1,4 @@
 executable: gradle
 args: build
+# Do not fail for deprecation warnings: Project.getConvention emitted by the shadow plugin
+flags: "--warning-mode=none"
diff --git a/subprojects/docs/src/snippets/kotlinDsl/multiProjectBuild/tests/sanityCheck.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/multiProjectBuild/tests/sanityCheck.sample.conf
new file mode 100644
index 0000000..b88677f
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/multiProjectBuild/tests/sanityCheck.sample.conf
@@ -0,0 +1,4 @@
+executable: gradle
+args: tasks
+# Do not fail for deprecation warnings: Project.getConvention emitted by the shadow plugin
+flags: "--warning-mode=none"
diff --git a/subprojects/docs/src/snippets/kotlinDsl/noAccessors/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/kotlinDsl/noAccessors/kotlin/build.gradle.kts
index 2ace92c..25e37a6 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/noAccessors/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/kotlinDsl/noAccessors/kotlin/build.gradle.kts
@@ -30,7 +30,7 @@
 }
 // end::project-container-extension[]
 
-configure<JavaPluginConvention> {
+configure<JavaPluginExtension> {
     sourceCompatibility = JavaVersion.VERSION_11
     targetCompatibility = JavaVersion.VERSION_11
 }
diff --git a/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/kotlin/build.gradle.kts
index 4851df4..e8d038b 100644
--- a/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/kotlin/build.gradle.kts
@@ -1,12 +1,18 @@
-// tag::source-set-convention[]
 plugins {
-    groovy
+    java
 }
 
+interface CustomSourceSetConvention {
+    val someOption: Property<String>
+}
+
+(sourceSets["main"].extensions as Convention).plugins["custom"] = objects.newInstance<CustomSourceSetConvention>()
+
+// tag::source-set-convention[]
 sourceSets {
     main {
-        withConvention(GroovySourceSet::class) {
-            groovy.srcDir("src/core/groovy")
+        withConvention(CustomSourceSetConvention::class) {
+            someOption.set("some value")
         }
     }
 }
diff --git a/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/tests/sanityCheck.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/tests/sanityCheck.sample.conf
new file mode 100644
index 0000000..3cc7513
--- /dev/null
+++ b/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/tests/sanityCheck.sample.conf
@@ -0,0 +1,6 @@
+commands: [{
+    executable: gradle
+    args: help -q
+    # Do not fail on deprecation warnings, this snippet demonstrate using deprecated conventions
+    flags: "--warning-mode=none"
+}]
diff --git a/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/tests/source-set-convention.sample.conf b/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/tests/source-set-convention.sample.conf
deleted file mode 100644
index f72a538..0000000
--- a/subprojects/docs/src/snippets/kotlinDsl/sourceSetConvention/tests/source-set-convention.sample.conf
+++ /dev/null
@@ -1,2 +0,0 @@
-executable: gradle
-args: help
diff --git a/subprojects/docs/src/snippets/maven-publish/conditional-publishing/groovy/src/main/java/Sample.java b/subprojects/docs/src/snippets/maven-publish/conditional-publishing/groovy/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/maven-publish/conditional-publishing/groovy/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/maven-publish/conditional-publishing/groovy/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/maven-publish/conditional-publishing/kotlin/src/main/java/Sample.java b/subprojects/docs/src/snippets/maven-publish/conditional-publishing/kotlin/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/maven-publish/conditional-publishing/kotlin/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/maven-publish/conditional-publishing/kotlin/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/maven-publish/javaProject/groovy/subproject/src/main/java/org/gradle/shared/Person.java b/subprojects/docs/src/snippets/maven-publish/javaProject/groovy/subproject/src/main/java/org/gradle/shared/Person.java
index c4f58e6..4873c38 100644
--- a/subprojects/docs/src/snippets/maven-publish/javaProject/groovy/subproject/src/main/java/org/gradle/shared/Person.java
+++ b/subprojects/docs/src/snippets/maven-publish/javaProject/groovy/subproject/src/main/java/org/gradle/shared/Person.java
@@ -2,4 +2,4 @@
 
 public class Person {
     private String name;
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/maven-publish/javaProject/kotlin/subproject/src/main/java/org/gradle/shared/Person.java b/subprojects/docs/src/snippets/maven-publish/javaProject/kotlin/subproject/src/main/java/org/gradle/shared/Person.java
index c4f58e6..4873c38 100644
--- a/subprojects/docs/src/snippets/maven-publish/javaProject/kotlin/subproject/src/main/java/org/gradle/shared/Person.java
+++ b/subprojects/docs/src/snippets/maven-publish/javaProject/kotlin/subproject/src/main/java/org/gradle/shared/Person.java
@@ -2,4 +2,4 @@
 
 public class Person {
     private String name;
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/maven-publish/publish-artifact/groovy/build.gradle b/subprojects/docs/src/snippets/maven-publish/publish-artifact/groovy/build.gradle
index f22572d..0c0e49f 100644
--- a/subprojects/docs/src/snippets/maven-publish/publish-artifact/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/maven-publish/publish-artifact/groovy/build.gradle
@@ -7,8 +7,11 @@
 version = '1.0'
 
 // tag::custom-artifact[]
+configurations {
+    conf
+}
 def rpmFile = layout.buildDirectory.file('rpms/my-package.rpm')
-def rpmArtifact = artifacts.add('archives', rpmFile.get().asFile) {
+def rpmArtifact = artifacts.add('conf', rpmFile.get().asFile) {
     type 'rpm'
     builtBy 'rpm'
 }
diff --git a/subprojects/docs/src/snippets/maven-publish/publish-artifact/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/maven-publish/publish-artifact/kotlin/build.gradle.kts
index 722f101..76488dd 100644
--- a/subprojects/docs/src/snippets/maven-publish/publish-artifact/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/maven-publish/publish-artifact/kotlin/build.gradle.kts
@@ -7,8 +7,11 @@
 version = "1.0"
 
 // tag::custom-artifact[]
+configurations {
+    create("conf")
+}
 val rpmFile = layout.buildDirectory.file("rpms/my-package.rpm")
-val rpmArtifact = artifacts.add("archives", rpmFile.get().asFile) {
+val rpmArtifact = artifacts.add("conf", rpmFile.get().asFile) {
     type = "rpm"
     builtBy("rpm")
 }
diff --git a/subprojects/docs/src/snippets/maven-publish/quickstart/groovy/src/main/java/org/MyClass.java b/subprojects/docs/src/snippets/maven-publish/quickstart/groovy/src/main/java/org/MyClass.java
index 81d5a85..d03da39 100644
--- a/subprojects/docs/src/snippets/maven-publish/quickstart/groovy/src/main/java/org/MyClass.java
+++ b/subprojects/docs/src/snippets/maven-publish/quickstart/groovy/src/main/java/org/MyClass.java
@@ -2,4 +2,4 @@
 
 public class MyClass {
     
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/maven-publish/quickstart/kotlin/src/main/java/org/MyClass.java b/subprojects/docs/src/snippets/maven-publish/quickstart/kotlin/src/main/java/org/MyClass.java
index 81d5a85..d03da39 100644
--- a/subprojects/docs/src/snippets/maven-publish/quickstart/kotlin/src/main/java/org/MyClass.java
+++ b/subprojects/docs/src/snippets/maven-publish/quickstart/kotlin/src/main/java/org/MyClass.java
@@ -2,4 +2,4 @@
 
 public class MyClass {
     
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/mavenMigration/basic/groovy/build.gradle b/subprojects/docs/src/snippets/mavenMigration/basic/groovy/build.gradle
index c63867c..9ba2260 100644
--- a/subprojects/docs/src/snippets/mavenMigration/basic/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/mavenMigration/basic/groovy/build.gradle
@@ -3,10 +3,13 @@
     id 'checkstyle'
 }
 
-sourceCompatibility = '1.8'
-targetCompatibility = '1.8'
 version = '1.2.1'
 
+java {
+    sourceCompatibility = '1.8'
+    targetCompatibility = '1.8'
+}
+
 ext {
     currentBuildNumber = '1234'
 }
diff --git a/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/api/src/test/java/org/gradle/PersonTest.java b/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/api/src/test/java/org/gradle/PersonTest.java
index 5c0d2ea..3bd1388 100644
--- a/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/api/src/test/java/org/gradle/PersonTest.java
+++ b/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/api/src/test/java/org/gradle/PersonTest.java
@@ -6,4 +6,4 @@ public class PersonTest {
     @Test
     public void ok() {
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/services/person-service/src/main/java/org/gradle/sample/services/PersonService.java b/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/services/person-service/src/main/java/org/gradle/sample/services/PersonService.java
index 40c65e4..0633f8d 100644
--- a/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/services/person-service/src/main/java/org/gradle/sample/services/PersonService.java
+++ b/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/services/person-service/src/main/java/org/gradle/sample/services/PersonService.java
@@ -11,4 +11,4 @@ boolean checkPerson(Person person) {
         }
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/shared/src/test/java/org/gradle/HelperTest.java b/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/shared/src/test/java/org/gradle/HelperTest.java
index 942e0f8..fa8230d 100644
--- a/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/shared/src/test/java/org/gradle/HelperTest.java
+++ b/subprojects/docs/src/snippets/multiproject/dependencies-java/groovy/shared/src/test/java/org/gradle/HelperTest.java
@@ -6,4 +6,4 @@ public class HelperTest {
     @Test
     public void ok() {
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/api/src/test/java/org/gradle/PersonTest.java b/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/api/src/test/java/org/gradle/PersonTest.java
index 5c0d2ea..3bd1388 100644
--- a/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/api/src/test/java/org/gradle/PersonTest.java
+++ b/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/api/src/test/java/org/gradle/PersonTest.java
@@ -6,4 +6,4 @@ public class PersonTest {
     @Test
     public void ok() {
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/services/person-service/src/main/java/org/gradle/sample/services/PersonService.java b/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/services/person-service/src/main/java/org/gradle/sample/services/PersonService.java
index 40c65e4..0633f8d 100644
--- a/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/services/person-service/src/main/java/org/gradle/sample/services/PersonService.java
+++ b/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/services/person-service/src/main/java/org/gradle/sample/services/PersonService.java
@@ -11,4 +11,4 @@ boolean checkPerson(Person person) {
         }
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/shared/src/test/java/org/gradle/HelperTest.java b/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/shared/src/test/java/org/gradle/HelperTest.java
index 942e0f8..fa8230d 100644
--- a/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/shared/src/test/java/org/gradle/HelperTest.java
+++ b/subprojects/docs/src/snippets/multiproject/dependencies-java/kotlin/shared/src/test/java/org/gradle/HelperTest.java
@@ -6,4 +6,4 @@ public class HelperTest {
     @Test
     public void ok() {
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/native-binaries/cunit/tests/completeCUnitExample.out b/subprojects/docs/src/snippets/native-binaries/cunit/tests/completeCUnitExample.out
index 960a9079..3fe85b5 100644
--- a/subprojects/docs/src/snippets/native-binaries/cunit/tests/completeCUnitExample.out
+++ b/subprojects/docs/src/snippets/native-binaries/cunit/tests/completeCUnitExample.out
@@ -14,7 +14,6 @@
 > Run with --stacktrace option to get the stack trace.
 > Run with --info or --debug option to get more log output.
 > Run with --scan to get full insights.
-
-* Get more help at https://help.gradle.org
+> Get more help at https://help.gradle.org.
 
 BUILD FAILED in 0s
diff --git a/subprojects/docs/src/snippets/native-binaries/multi-project/groovy/settings.gradle b/subprojects/docs/src/snippets/native-binaries/multi-project/groovy/settings.gradle
index 2ee626b..cf37ac6 100644
--- a/subprojects/docs/src/snippets/native-binaries/multi-project/groovy/settings.gradle
+++ b/subprojects/docs/src/snippets/native-binaries/multi-project/groovy/settings.gradle
@@ -1,2 +1,2 @@
 rootProject.name = 'multi-project'
-include "exe", "lib"
\ No newline at end of file
+include "exe", "lib"
diff --git a/subprojects/docs/src/snippets/native-binaries/prebuilt/groovy/3rd-party-lib/util/settings.gradle b/subprojects/docs/src/snippets/native-binaries/prebuilt/groovy/3rd-party-lib/util/settings.gradle
index 226302f..31fdb80 100644
--- a/subprojects/docs/src/snippets/native-binaries/prebuilt/groovy/3rd-party-lib/util/settings.gradle
+++ b/subprojects/docs/src/snippets/native-binaries/prebuilt/groovy/3rd-party-lib/util/settings.gradle
@@ -1 +1 @@
-rootProject.name = "util"
\ No newline at end of file
+rootProject.name = "util"
diff --git a/subprojects/docs/src/snippets/plugins/buildscript/tests/applyPluginBuildscript.sample.conf b/subprojects/docs/src/snippets/plugins/buildscript/tests/sanityCheck.sample.conf
similarity index 100%
rename from subprojects/docs/src/snippets/plugins/buildscript/tests/applyPluginBuildscript.sample.conf
rename to subprojects/docs/src/snippets/plugins/buildscript/tests/sanityCheck.sample.conf
diff --git a/subprojects/docs/src/snippets/plugins/dsl/tests/useJavaPluginDSL.sample.conf b/subprojects/docs/src/snippets/plugins/dsl/tests/sanityCheck.sample.conf
similarity index 100%
rename from subprojects/docs/src/snippets/plugins/dsl/tests/useJavaPluginDSL.sample.conf
rename to subprojects/docs/src/snippets/plugins/dsl/tests/sanityCheck.sample.conf
diff --git a/subprojects/docs/src/snippets/providers/propertyAndProvider/groovy/build.gradle b/subprojects/docs/src/snippets/providers/propertyAndProvider/groovy/build.gradle
index 505f068..a265c79 100644
--- a/subprojects/docs/src/snippets/providers/propertyAndProvider/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/providers/propertyAndProvider/groovy/build.gradle
@@ -1,12 +1,9 @@
-// A task that displays a greeting
-abstract class Greeting extends DefaultTask {
-    // A configurable greeting
+abstract class Greeting extends DefaultTask { // <1>
     @Input
-    abstract Property<String> getGreeting()
+    abstract Property<String> getGreeting() // <2>
 
-    // Read-only property calculated from the greeting
     @Internal
-    final Provider<String> message = greeting.map { it + ' from Gradle' }
+    final Provider<String> message = greeting.map { it + ' from Gradle' } // <3>
 
     @TaskAction
     void printMessage() {
@@ -15,7 +12,6 @@
 }
 
 tasks.register("greeting", Greeting) {
-    // Configure the greeting
-    greeting.set('Hi')
-    greeting = 'Hi' // Alternative notation to calling Property.set() - only available in Groovy DSL
+    greeting.set('Hi') // <4>
+    greeting = 'Hi' // <5>
 }
diff --git a/subprojects/docs/src/snippets/providers/propertyAndProvider/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/providers/propertyAndProvider/kotlin/build.gradle.kts
index 077b081..e63caa3 100644
--- a/subprojects/docs/src/snippets/providers/propertyAndProvider/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/providers/propertyAndProvider/kotlin/build.gradle.kts
@@ -1,12 +1,9 @@
-// A task that displays a greeting
-abstract class Greeting : DefaultTask() {
-    // Configurable by the user
+abstract class Greeting : DefaultTask() { // <1>
     @get:Input
-    abstract val greeting: Property<String>
+    abstract val greeting: Property<String> // <2>
 
-    // Read-only property calculated from the greeting
     @Internal
-    val message: Provider<String> = greeting.map { it + " from Gradle" }
+    val message: Provider<String> = greeting.map { it + " from Gradle" } // <3>
 
     @TaskAction
     fun printMessage() {
@@ -15,7 +12,6 @@
 }
 
 tasks.register<Greeting>("greeting") {
-    // Configure the greeting
-    greeting.set("Hi")
-
+    greeting.set("Hi") // <4>
+    greeting = "Hi" // <5>
 }
diff --git a/subprojects/docs/src/snippets/scala/customizedLayout/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/scala/customizedLayout/kotlin/build.gradle.kts
index c9f5c98..2dc58e6 100644
--- a/subprojects/docs/src/snippets/scala/customizedLayout/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/scala/customizedLayout/kotlin/build.gradle.kts
@@ -17,17 +17,13 @@
 // tag::custom-source-locations[]
 sourceSets {
     main {
-        withConvention(ScalaSourceSet::class) {
-            scala {
-                setSrcDirs(listOf("src/scala"))
-            }
+        scala {
+            setSrcDirs(listOf("src/scala"))
         }
     }
     test {
-        withConvention(ScalaSourceSet::class) {
-            scala {
-                setSrcDirs(listOf("test/scala"))
-            }
+        scala {
+            setSrcDirs(listOf("test/scala"))
         }
     }
 }
diff --git a/subprojects/docs/src/snippets/scala/mixedJavaAndScala/groovy/src/main/scala/org/gradle/sample/JavaPerson.java b/subprojects/docs/src/snippets/scala/mixedJavaAndScala/groovy/src/main/scala/org/gradle/sample/JavaPerson.java
index cf36c00..ca522d1 100644
--- a/subprojects/docs/src/snippets/scala/mixedJavaAndScala/groovy/src/main/scala/org/gradle/sample/JavaPerson.java
+++ b/subprojects/docs/src/snippets/scala/mixedJavaAndScala/groovy/src/main/scala/org/gradle/sample/JavaPerson.java
@@ -4,4 +4,4 @@ public class JavaPerson extends Person {
     public JavaPerson(String name) {
         super(name);
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/signing/conditional/groovy/src/main/java/Sample.java b/subprojects/docs/src/snippets/signing/conditional/groovy/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/signing/conditional/groovy/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/signing/conditional/groovy/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/signing/conditional/kotlin/src/main/java/Sample.java b/subprojects/docs/src/snippets/signing/conditional/kotlin/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/signing/conditional/kotlin/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/signing/conditional/kotlin/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/signing/configurations/groovy/build.gradle b/subprojects/docs/src/snippets/signing/configurations/groovy/build.gradle
index d4dafeb..2c78b8b 100644
--- a/subprojects/docs/src/snippets/signing/configurations/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/signing/configurations/groovy/build.gradle
@@ -16,8 +16,8 @@
 project['signing.password'] = "gradle"
 project['signing.secretKeyRingFile'] = file("secKeyRingFile.gpg").absolutePath
 
-// tag::sign-archives[]
+// tag::sign-runtime-elements[]
 signing {
-    sign configurations.archives
+    sign configurations.runtimeElements
 }
-// end::sign-archives[]
+// end::sign-runtime-elements[]
diff --git a/subprojects/docs/src/snippets/signing/configurations/groovy/src/main/java/Sample.java b/subprojects/docs/src/snippets/signing/configurations/groovy/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/signing/configurations/groovy/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/signing/configurations/groovy/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/signing/configurations/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/signing/configurations/kotlin/build.gradle.kts
index 1496bce..60bf6a7 100644
--- a/subprojects/docs/src/snippets/signing/configurations/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/signing/configurations/kotlin/build.gradle.kts
@@ -16,8 +16,8 @@
 extra["signing.password"] = "gradle"
 extra["signing.secretKeyRingFile"] = file("secKeyRingFile.gpg").absolutePath
 
-// tag::sign-archives[]
+// tag::sign-runtime-elements[]
 signing {
-    sign(configurations.archives.get())
+    sign(configurations.runtimeElements.get())
 }
-// end::sign-archives[]
+// end::sign-runtime-elements[]
diff --git a/subprojects/docs/src/snippets/signing/configurations/kotlin/src/main/java/Sample.java b/subprojects/docs/src/snippets/signing/configurations/kotlin/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/signing/configurations/kotlin/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/signing/configurations/kotlin/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/signing/configurations/tests/signingArchivesOutput.out b/subprojects/docs/src/snippets/signing/configurations/tests/signingArchivesOutput.out
deleted file mode 100644
index 7bba39f..0000000
--- a/subprojects/docs/src/snippets/signing/configurations/tests/signingArchivesOutput.out
+++ /dev/null
@@ -1,8 +0,0 @@
-> Task :compileJava
-> Task :processResources
-> Task :classes
-> Task :jar
-> Task :signArchives
-
-BUILD SUCCESSFUL in 0s
-4 actionable tasks: 4 executed
diff --git a/subprojects/docs/src/snippets/signing/configurations/tests/signingArchivesOutput.sample.conf b/subprojects/docs/src/snippets/signing/configurations/tests/signingArchivesOutput.sample.conf
deleted file mode 100644
index 32307c4..0000000
--- a/subprojects/docs/src/snippets/signing/configurations/tests/signingArchivesOutput.sample.conf
+++ /dev/null
@@ -1,8 +0,0 @@
-# tag::cli[]
-# gradle signArchives
-# end::cli[]
-executable: gradle
-args: signArchives
-# Do not fail for deprecation warnings: deprecated Maven plugin
-flags: "--warning-mode=none"
-expected-output-file: signingArchivesOutput.out
diff --git a/subprojects/docs/src/snippets/signing/configurations/tests/signingRuntimeElementsOutput.out b/subprojects/docs/src/snippets/signing/configurations/tests/signingRuntimeElementsOutput.out
new file mode 100644
index 0000000..36c9b1f
--- /dev/null
+++ b/subprojects/docs/src/snippets/signing/configurations/tests/signingRuntimeElementsOutput.out
@@ -0,0 +1,8 @@
+> Task :compileJava
+> Task :processResources
+> Task :classes
+> Task :jar
+> Task :signRuntimeElements
+
+BUILD SUCCESSFUL in 0s
+4 actionable tasks: 4 executed
diff --git a/subprojects/docs/src/snippets/signing/configurations/tests/signingRuntimeElementsOutput.sample.conf b/subprojects/docs/src/snippets/signing/configurations/tests/signingRuntimeElementsOutput.sample.conf
new file mode 100644
index 0000000..4cec2fa
--- /dev/null
+++ b/subprojects/docs/src/snippets/signing/configurations/tests/signingRuntimeElementsOutput.sample.conf
@@ -0,0 +1,8 @@
+# tag::cli[]
+# gradle signArchives
+# end::cli[]
+executable: gradle
+args: signRuntimeElements
+# Do not fail for deprecation warnings: deprecated Maven plugin
+flags: "--warning-mode=none"
+expected-output-file: signingRuntimeElementsOutput.out
diff --git a/subprojects/docs/src/snippets/signing/gnupg-signatory/groovy/build.gradle b/subprojects/docs/src/snippets/signing/gnupg-signatory/groovy/build.gradle
index af66f87..f8a9189 100644
--- a/subprojects/docs/src/snippets/signing/gnupg-signatory/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/signing/gnupg-signatory/groovy/build.gradle
@@ -9,6 +9,6 @@
 // tag::configure-signatory[]
 signing {
     useGpgCmd()
-    sign configurations.archives
+    sign configurations.runtimeElements
 }
 // end::configure-signatory[]
diff --git a/subprojects/docs/src/snippets/signing/gnupg-signatory/groovy/src/main/java/Sample.java b/subprojects/docs/src/snippets/signing/gnupg-signatory/groovy/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/signing/gnupg-signatory/groovy/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/signing/gnupg-signatory/groovy/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/signing/gnupg-signatory/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/signing/gnupg-signatory/kotlin/build.gradle.kts
index c9b0c08..1cfbe4b 100644
--- a/subprojects/docs/src/snippets/signing/gnupg-signatory/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/signing/gnupg-signatory/kotlin/build.gradle.kts
@@ -9,6 +9,6 @@
 // tag::configure-signatory[]
 signing {
     useGpgCmd()
-    sign(configurations.archives.get())
+    sign(configurations.runtimeElements.get())
 }
 // end::configure-signatory[]
diff --git a/subprojects/docs/src/snippets/signing/gnupg-signatory/kotlin/src/main/java/Sample.java b/subprojects/docs/src/snippets/signing/gnupg-signatory/kotlin/src/main/java/Sample.java
index c29d257..c2ba386 100644
--- a/subprojects/docs/src/snippets/signing/gnupg-signatory/kotlin/src/main/java/Sample.java
+++ b/subprojects/docs/src/snippets/signing/gnupg-signatory/kotlin/src/main/java/Sample.java
@@ -1,2 +1,2 @@
 class Sample {
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/swift/testReport/groovy/build.gradle b/subprojects/docs/src/snippets/swift/testReport/groovy/build.gradle
index 9826436..160d67d 100644
--- a/subprojects/docs/src/snippets/swift/testReport/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/swift/testReport/groovy/build.gradle
@@ -6,7 +6,6 @@
 
 configurations {
     testReportData {
-        canBeResolved = true
         canBeConsumed = false
         attributes {
             attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
diff --git a/subprojects/docs/src/snippets/swift/testReport/groovy/buildSrc/src/main/groovy/myproject.xctest-conventions.gradle b/subprojects/docs/src/snippets/swift/testReport/groovy/buildSrc/src/main/groovy/myproject.xctest-conventions.gradle
index 8a07f6a..0352634 100644
--- a/subprojects/docs/src/snippets/swift/testReport/groovy/buildSrc/src/main/groovy/myproject.xctest-conventions.gradle
+++ b/subprojects/docs/src/snippets/swift/testReport/groovy/buildSrc/src/main/groovy/myproject.xctest-conventions.gradle
@@ -16,7 +16,6 @@
 configurations {
     binaryTestResultsElements {
         canBeResolved = false
-        canBeConsumed = true
         attributes {
             attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
             attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
diff --git a/subprojects/docs/src/snippets/swift/testReport/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/swift/testReport/kotlin/build.gradle.kts
index 4dff28d..b00a5a8 100644
--- a/subprojects/docs/src/snippets/swift/testReport/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/swift/testReport/kotlin/build.gradle.kts
@@ -4,7 +4,6 @@
 }
 
 val testReportData by configurations.creating {
-    isCanBeResolved = true
     isCanBeConsumed = false
     attributes {
         attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
diff --git a/subprojects/docs/src/snippets/tasks/commandLineOption-optionValues/groovy/settings.gradle b/subprojects/docs/src/snippets/tasks/commandLineOption-optionValues/groovy/settings.gradle
index 0cf2f60..0f6d63f 100644
--- a/subprojects/docs/src/snippets/tasks/commandLineOption-optionValues/groovy/settings.gradle
+++ b/subprojects/docs/src/snippets/tasks/commandLineOption-optionValues/groovy/settings.gradle
@@ -1 +1 @@
-rootProject.name = "option-values"
\ No newline at end of file
+rootProject.name = "option-values"
diff --git a/subprojects/docs/src/snippets/tasks/commandLineOption-stringOption/groovy/settings.gradle b/subprojects/docs/src/snippets/tasks/commandLineOption-stringOption/groovy/settings.gradle
index 94de2af..f6100fc 100644
--- a/subprojects/docs/src/snippets/tasks/commandLineOption-stringOption/groovy/settings.gradle
+++ b/subprojects/docs/src/snippets/tasks/commandLineOption-stringOption/groovy/settings.gradle
@@ -1 +1 @@
-rootProject.name = "string-option"
\ No newline at end of file
+rootProject.name = "string-option"
diff --git a/subprojects/docs/src/snippets/tasks/finalizersWithFailure/tests-groovy/taskFinalizersWithFailureGroovy.out b/subprojects/docs/src/snippets/tasks/finalizersWithFailure/tests-groovy/taskFinalizersWithFailureGroovy.out
index 90f4970..6383a29 100644
--- a/subprojects/docs/src/snippets/tasks/finalizersWithFailure/tests-groovy/taskFinalizersWithFailureGroovy.out
+++ b/subprojects/docs/src/snippets/tasks/finalizersWithFailure/tests-groovy/taskFinalizersWithFailureGroovy.out
@@ -14,7 +14,6 @@
 > Run with --stacktrace option to get the stack trace.
 > Run with --info or --debug option to get more log output.
 > Run with --scan to get full insights.
-
-* Get more help at https://help.gradle.org
+> Get more help at https://help.gradle.org.
 
 BUILD FAILED in 0s
diff --git a/subprojects/docs/src/snippets/tasks/finalizersWithFailure/tests-kotlin/taskFinalizersWithFailureKotlin.out b/subprojects/docs/src/snippets/tasks/finalizersWithFailure/tests-kotlin/taskFinalizersWithFailureKotlin.out
index 14b0a96..5903998 100644
--- a/subprojects/docs/src/snippets/tasks/finalizersWithFailure/tests-kotlin/taskFinalizersWithFailureKotlin.out
+++ b/subprojects/docs/src/snippets/tasks/finalizersWithFailure/tests-kotlin/taskFinalizersWithFailureKotlin.out
@@ -11,7 +11,6 @@
 > Run with --stacktrace option to get the stack trace.
 > Run with --info or --debug option to get more log output.
 > Run with --scan to get full insights.
-
-* Get more help at https://help.gradle.org
+> Get more help at https://help.gradle.org.
 
 BUILD FAILED in 0s
diff --git a/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionCustomTestSourceSet/groovy/build.gradle b/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionCustomTestSourceSet/groovy/build.gradle
index eb1611c..48f592e 100644
--- a/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionCustomTestSourceSet/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionCustomTestSourceSet/groovy/build.gradle
@@ -24,6 +24,7 @@
     functionalTestImplementation('org.spockframework:spock-core:2.2-groovy-3.0') {
         exclude group: 'org.codehaus.groovy'
     }
+    functionalTestRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 // end::custom-test-source-set[]
 
diff --git a/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionCustomTestSourceSet/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionCustomTestSourceSet/kotlin/build.gradle.kts
index a8516e1..f6bd230 100644
--- a/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionCustomTestSourceSet/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionCustomTestSourceSet/kotlin/build.gradle.kts
@@ -24,6 +24,7 @@
     "functionalTestImplementation"("org.spockframework:spock-core:2.2-groovy-3.0") {
         exclude(group = "org.codehaus.groovy")
     }
+    "functionalTestRuntimeOnly"("org.junit.platform:junit-platform-launcher")
 }
 // end::custom-test-source-set[]
 
diff --git a/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionQuickstart/groovy/build.gradle b/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionQuickstart/groovy/build.gradle
index 2224fc8..d2fe7d9 100644
--- a/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionQuickstart/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionQuickstart/groovy/build.gradle
@@ -8,6 +8,7 @@
     testImplementation('org.spockframework:spock-core:2.2-groovy-3.0') {
         exclude group: 'org.codehaus.groovy'
     }
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 // end::automatic-classpath[]
 
diff --git a/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionQuickstart/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionQuickstart/kotlin/build.gradle.kts
index d025c3f..62dbbe1 100644
--- a/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionQuickstart/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testKit/automaticClasspathInjectionQuickstart/kotlin/build.gradle.kts
@@ -8,6 +8,7 @@
     testImplementation("org.spockframework:spock-core:2.2-groovy-3.0") {
         exclude(group = "org.codehaus.groovy")
     }
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 // end::automatic-classpath[]
 
diff --git a/subprojects/docs/src/snippets/testKit/junitQuickstart/groovy/build.gradle b/subprojects/docs/src/snippets/testKit/junitQuickstart/groovy/build.gradle
index 805a280..5440a02 100644
--- a/subprojects/docs/src/snippets/testKit/junitQuickstart/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testKit/junitQuickstart/groovy/build.gradle
@@ -11,6 +11,7 @@
 // tag::declare-junit-dependency[]
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 
 tasks.named('test', Test) {
diff --git a/subprojects/docs/src/snippets/testKit/junitQuickstart/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testKit/junitQuickstart/kotlin/build.gradle.kts
index 7a4f379..cca5317 100644
--- a/subprojects/docs/src/snippets/testKit/junitQuickstart/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testKit/junitQuickstart/kotlin/build.gradle.kts
@@ -11,6 +11,7 @@
 // tag::declare-junit-dependency[]
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 
 tasks.named<Test>("test") {
diff --git a/subprojects/docs/src/snippets/testKit/spockQuickstart/groovy/build.gradle b/subprojects/docs/src/snippets/testKit/spockQuickstart/groovy/build.gradle
index b230b9e..6fef4a7 100644
--- a/subprojects/docs/src/snippets/testKit/spockQuickstart/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testKit/spockQuickstart/groovy/build.gradle
@@ -12,6 +12,7 @@
     testImplementation('org.spockframework:spock-core:2.2-groovy-3.0') {
         exclude group: 'org.codehaus.groovy'
     }
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 // end::declare-spock-dependency[]
 
diff --git a/subprojects/docs/src/snippets/testKit/testKitFunctionalTestSpockBuildCache/groovy/build.gradle b/subprojects/docs/src/snippets/testKit/testKitFunctionalTestSpockBuildCache/groovy/build.gradle
index b55e5a1..ac25fd6 100644
--- a/subprojects/docs/src/snippets/testKit/testKitFunctionalTestSpockBuildCache/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testKit/testKitFunctionalTestSpockBuildCache/groovy/build.gradle
@@ -7,6 +7,7 @@
     testImplementation('org.spockframework:spock-core:2.2-groovy-3.0') {
         exclude group: 'org.codehaus.groovy'
     }
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 
 tasks.named('test', Test) {
diff --git a/subprojects/docs/src/snippets/testKit/testKitFunctionalTestSpockBuildCache/groovy/settings.gradle b/subprojects/docs/src/snippets/testKit/testKitFunctionalTestSpockBuildCache/groovy/settings.gradle
index 4c39454..c927bad 100644
--- a/subprojects/docs/src/snippets/testKit/testKitFunctionalTestSpockBuildCache/groovy/settings.gradle
+++ b/subprojects/docs/src/snippets/testKit/testKitFunctionalTestSpockBuildCache/groovy/settings.gradle
@@ -1 +1 @@
-rootProject.name = 'testkit-spock-build-cache'
\ No newline at end of file
+rootProject.name = 'testkit-spock-build-cache'
diff --git a/subprojects/docs/src/snippets/testing/filtering/groovy/src/test/java/SomeIntegTest.java b/subprojects/docs/src/snippets/testing/filtering/groovy/src/test/java/SomeIntegTest.java
index a6a8fda..ac7ac17 100644
--- a/subprojects/docs/src/snippets/testing/filtering/groovy/src/test/java/SomeIntegTest.java
+++ b/subprojects/docs/src/snippets/testing/filtering/groovy/src/test/java/SomeIntegTest.java
@@ -3,4 +3,4 @@
 public class SomeIntegTest {
     @Test public void test1() {}
     @Test public void test2() {}
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/testing/filtering/groovy/src/test/java/SomeOtherTest.java b/subprojects/docs/src/snippets/testing/filtering/groovy/src/test/java/SomeOtherTest.java
index 2598cfd..9af45ef 100644
--- a/subprojects/docs/src/snippets/testing/filtering/groovy/src/test/java/SomeOtherTest.java
+++ b/subprojects/docs/src/snippets/testing/filtering/groovy/src/test/java/SomeOtherTest.java
@@ -3,4 +3,4 @@
 public class SomeOtherTest {
     @Test public void quickUiCheck() {}
     @Test public void quickServerCheck() {}
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/testing/filtering/kotlin/src/test/java/SomeIntegTest.java b/subprojects/docs/src/snippets/testing/filtering/kotlin/src/test/java/SomeIntegTest.java
index a6a8fda..ac7ac17 100644
--- a/subprojects/docs/src/snippets/testing/filtering/kotlin/src/test/java/SomeIntegTest.java
+++ b/subprojects/docs/src/snippets/testing/filtering/kotlin/src/test/java/SomeIntegTest.java
@@ -3,4 +3,4 @@
 public class SomeIntegTest {
     @Test public void test1() {}
     @Test public void test2() {}
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/testing/filtering/kotlin/src/test/java/SomeOtherTest.java b/subprojects/docs/src/snippets/testing/filtering/kotlin/src/test/java/SomeOtherTest.java
index 2598cfd..9af45ef 100644
--- a/subprojects/docs/src/snippets/testing/filtering/kotlin/src/test/java/SomeOtherTest.java
+++ b/subprojects/docs/src/snippets/testing/filtering/kotlin/src/test/java/SomeOtherTest.java
@@ -3,4 +3,4 @@
 public class SomeOtherTest {
     @Test public void quickUiCheck() {}
     @Test public void quickServerCheck() {}
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/testing/jacoco-quickstart/groovy/build.gradle b/subprojects/docs/src/snippets/testing/jacoco-quickstart/groovy/build.gradle
index 52bfd75..d14cbcb 100644
--- a/subprojects/docs/src/snippets/testing/jacoco-quickstart/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testing/jacoco-quickstart/groovy/build.gradle
@@ -10,7 +10,7 @@
 
 // tag::jacoco-configuration[]
 jacoco {
-    toolVersion = "0.8.8"
+    toolVersion = "0.8.9"
     reportsDirectory = layout.buildDirectory.dir('customJacocoReportDir')
 }
 // end::jacoco-configuration[]
diff --git a/subprojects/docs/src/snippets/testing/jacoco-quickstart/groovy/src/main/java/org/gradle/Person.java b/subprojects/docs/src/snippets/testing/jacoco-quickstart/groovy/src/main/java/org/gradle/Person.java
index 9c40af9..86d53df 100644
--- a/subprojects/docs/src/snippets/testing/jacoco-quickstart/groovy/src/main/java/org/gradle/Person.java
+++ b/subprojects/docs/src/snippets/testing/jacoco-quickstart/groovy/src/main/java/org/gradle/Person.java
@@ -21,4 +21,4 @@ public int getAge() {
     public void setAge(int age) {
         this.age = age;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/testing/jacoco-quickstart/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testing/jacoco-quickstart/kotlin/build.gradle.kts
index 95ad4ff..3a57ef2 100644
--- a/subprojects/docs/src/snippets/testing/jacoco-quickstart/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testing/jacoco-quickstart/kotlin/build.gradle.kts
@@ -9,7 +9,7 @@
 
 // tag::jacoco-configuration[]
 jacoco {
-    toolVersion = "0.8.8"
+    toolVersion = "0.8.9"
     reportsDirectory.set(layout.buildDirectory.dir("customJacocoReportDir"))
 }
 // end::jacoco-configuration[]
diff --git a/subprojects/docs/src/snippets/testing/jacoco-quickstart/kotlin/src/main/java/org/gradle/Person.java b/subprojects/docs/src/snippets/testing/jacoco-quickstart/kotlin/src/main/java/org/gradle/Person.java
index 9c40af9..86d53df 100644
--- a/subprojects/docs/src/snippets/testing/jacoco-quickstart/kotlin/src/main/java/org/gradle/Person.java
+++ b/subprojects/docs/src/snippets/testing/jacoco-quickstart/kotlin/src/main/java/org/gradle/Person.java
@@ -21,4 +21,4 @@ public int getAge() {
     public void setAge(int age) {
         this.age = age;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/testing/junit-xml-results/groovy/build.gradle b/subprojects/docs/src/snippets/testing/junit-xml-results/groovy/build.gradle
index 1454ecc..3c7f2e5 100644
--- a/subprojects/docs/src/snippets/testing/junit-xml-results/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testing/junit-xml-results/groovy/build.gradle
@@ -19,7 +19,7 @@
 // end::configure-location-task[]
 
 // tag::configure-location-convention[]
-testResultsDirName = "$buildDir/junit-xml"
+java.testResultsDir = layout.buildDirectory.dir("junit-xml")
 // end::configure-location-convention[]
 
 // tag::configure-content[]
diff --git a/subprojects/docs/src/snippets/testing/junit-xml-results/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testing/junit-xml-results/kotlin/build.gradle.kts
index 6e89bad..e39ec19 100644
--- a/subprojects/docs/src/snippets/testing/junit-xml-results/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testing/junit-xml-results/kotlin/build.gradle.kts
@@ -19,7 +19,7 @@
 // end::configure-location-task[]
 
 // tag::configure-location-convention[]
-project.setProperty("testResultsDirName", "$buildDir/junit-xml")
+java.testResultsDir.set(layout.buildDirectory.dir("junit-xml"))
 // end::configure-location-convention[]
 
 // tag::configure-content[]
diff --git a/subprojects/docs/src/snippets/testing/junitplatform-engine/groovy/build.gradle b/subprojects/docs/src/snippets/testing/junitplatform-engine/groovy/build.gradle
index 46bb8ab..623e65f 100644
--- a/subprojects/docs/src/snippets/testing/junitplatform-engine/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testing/junitplatform-engine/groovy/build.gradle
@@ -10,6 +10,7 @@
     testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
     testCompileOnly 'junit:junit:4.13'
     testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 
 // tag::filter-engine[]
diff --git a/subprojects/docs/src/snippets/testing/junitplatform-engine/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testing/junitplatform-engine/kotlin/build.gradle.kts
index 1ef59f2..6625410 100644
--- a/subprojects/docs/src/snippets/testing/junitplatform-engine/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testing/junitplatform-engine/kotlin/build.gradle.kts
@@ -10,6 +10,7 @@
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
     testCompileOnly("junit:junit:4.13")
     testRuntimeOnly("org.junit.vintage:junit-vintage-engine")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 
 // tag::filter-engine[]
diff --git a/subprojects/docs/src/snippets/testing/junitplatform-jupiter/groovy/build.gradle b/subprojects/docs/src/snippets/testing/junitplatform-jupiter/groovy/build.gradle
index a9be5da..d4e3995 100644
--- a/subprojects/docs/src/snippets/testing/junitplatform-jupiter/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testing/junitplatform-jupiter/groovy/build.gradle
@@ -9,6 +9,7 @@
 // tag::jupiter-dependencies[]
 dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 // end::jupiter-dependencies[]
 
diff --git a/subprojects/docs/src/snippets/testing/junitplatform-jupiter/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testing/junitplatform-jupiter/kotlin/build.gradle.kts
index 8aeb224..da8dfdd 100644
--- a/subprojects/docs/src/snippets/testing/junitplatform-jupiter/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testing/junitplatform-jupiter/kotlin/build.gradle.kts
@@ -9,6 +9,7 @@
 // tag::jupiter-dependencies[]
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 // end::jupiter-dependencies[]
 
diff --git a/subprojects/docs/src/snippets/testing/junitplatform-mix/groovy/build.gradle b/subprojects/docs/src/snippets/testing/junitplatform-mix/groovy/build.gradle
index 5639019..57566b5 100644
--- a/subprojects/docs/src/snippets/testing/junitplatform-mix/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testing/junitplatform-mix/groovy/build.gradle
@@ -11,6 +11,7 @@
     testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
     testCompileOnly 'junit:junit:4.13'
     testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 // end::vintage-dependencies[]
 
diff --git a/subprojects/docs/src/snippets/testing/junitplatform-mix/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testing/junitplatform-mix/kotlin/build.gradle.kts
index 81ac8709..fff2794 100644
--- a/subprojects/docs/src/snippets/testing/junitplatform-mix/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testing/junitplatform-mix/kotlin/build.gradle.kts
@@ -11,6 +11,7 @@
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
     testCompileOnly("junit:junit:4.13")
     testRuntimeOnly("org.junit.vintage:junit-vintage-engine")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 // end::vintage-dependencies[]
 
diff --git a/subprojects/docs/src/snippets/testing/junitplatform-tagging/groovy/build.gradle b/subprojects/docs/src/snippets/testing/junitplatform-tagging/groovy/build.gradle
index 6a294a7..982acce 100644
--- a/subprojects/docs/src/snippets/testing/junitplatform-tagging/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testing/junitplatform-tagging/groovy/build.gradle
@@ -8,6 +8,7 @@
 
 dependencies {
     implementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }
 
 // tag::test-tags[]
diff --git a/subprojects/docs/src/snippets/testing/junitplatform-tagging/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testing/junitplatform-tagging/kotlin/build.gradle.kts
index 8696192..c1c91ad 100644
--- a/subprojects/docs/src/snippets/testing/junitplatform-tagging/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testing/junitplatform-tagging/kotlin/build.gradle.kts
@@ -8,6 +8,7 @@
 
 dependencies {
     implementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
 
 // tag::test-tags[]
diff --git a/subprojects/docs/src/snippets/testing/patch-module/groovy/build.gradle b/subprojects/docs/src/snippets/testing/patch-module/groovy/build.gradle
index 90ece45..c21ea0a 100644
--- a/subprojects/docs/src/snippets/testing/patch-module/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testing/patch-module/groovy/build.gradle
@@ -5,6 +5,10 @@
     mavenCentral()
 }
 
+tasks.named('test') {
+    useJUnitPlatform()
+}
+
 // tag::patchArgs[]
 def moduleName = "org.gradle.sample"
 def patchArgs = ["--patch-module", "$moduleName=${tasks.compileJava.destinationDirectory.asFile.get().path}"]
@@ -16,6 +20,8 @@
 }
 // end::patchArgs[]
 
+tasks.test.useJUnitPlatform()
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
diff --git a/subprojects/docs/src/snippets/testing/patch-module/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testing/patch-module/kotlin/build.gradle.kts
index d240099..98f5642 100644
--- a/subprojects/docs/src/snippets/testing/patch-module/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testing/patch-module/kotlin/build.gradle.kts
@@ -5,6 +5,10 @@
     mavenCentral()
 }
 
+tasks.test {
+    useJUnitPlatform()
+}
+
 // tag::patchArgs[]
 val moduleName = "org.gradle.sample"
 val patchArgs = listOf("--patch-module", "$moduleName=${tasks.compileJava.get().destinationDirectory.asFile.get().path}")
@@ -16,6 +20,10 @@
 }
 // end::patchArgs[]
 
+tasks.named<Test>("test") {
+    useJUnitPlatform()
+}
 dependencies {
     testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
 }
diff --git a/subprojects/docs/src/snippets/testing/testReport/groovy/build.gradle b/subprojects/docs/src/snippets/testing/testReport/groovy/build.gradle
index d01ba60..839c539 100644
--- a/subprojects/docs/src/snippets/testing/testReport/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/testing/testReport/groovy/build.gradle
@@ -7,7 +7,6 @@
 // A resolvable configuration to collect test reports data
 configurations {
     testReportData {
-        canBeResolved = true
         canBeConsumed = false
         attributes {
             attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
diff --git a/subprojects/docs/src/snippets/testing/testReport/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/testing/testReport/kotlin/build.gradle.kts
index aa20f5c..edb1469 100644
--- a/subprojects/docs/src/snippets/testing/testReport/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/testing/testReport/kotlin/build.gradle.kts
@@ -5,7 +5,6 @@
 
 // tag::test-report[]
 val testReportData by configurations.creating {
-    isCanBeResolved = true
     isCanBeConsumed = false
     attributes {
         attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
diff --git a/subprojects/docs/src/snippets/toolingApi/customModel/groovy/plugin/src/main/java/org/gradle/sample/plugin/CustomModel.java b/subprojects/docs/src/snippets/toolingApi/customModel/groovy/plugin/src/main/java/org/gradle/sample/plugin/CustomModel.java
index efa736a..b758e38 100644
--- a/subprojects/docs/src/snippets/toolingApi/customModel/groovy/plugin/src/main/java/org/gradle/sample/plugin/CustomModel.java
+++ b/subprojects/docs/src/snippets/toolingApi/customModel/groovy/plugin/src/main/java/org/gradle/sample/plugin/CustomModel.java
@@ -11,4 +11,4 @@ public interface CustomModel extends Model {
     String getName();
 
     DomainObjectSet<String> getTasks();
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/toolingApi/customModel/groovy/plugin/src/main/java/org/gradle/sample/plugin/DefaultModel.java b/subprojects/docs/src/snippets/toolingApi/customModel/groovy/plugin/src/main/java/org/gradle/sample/plugin/DefaultModel.java
index 99d533b..b6dc617 100644
--- a/subprojects/docs/src/snippets/toolingApi/customModel/groovy/plugin/src/main/java/org/gradle/sample/plugin/DefaultModel.java
+++ b/subprojects/docs/src/snippets/toolingApi/customModel/groovy/plugin/src/main/java/org/gradle/sample/plugin/DefaultModel.java
@@ -17,4 +17,4 @@ public String getName() {
     public List<String> getTasks() {
         return Arrays.asList(":a", ":b", ":c");
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/groovy/src/main/groovy/Groovy.groovy b/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/groovy/src/main/groovy/Groovy.groovy
index 569685e..0bb35ef 100644
--- a/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/groovy/src/main/groovy/Groovy.groovy
+++ b/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/groovy/src/main/groovy/Groovy.groovy
@@ -1 +1 @@
-class Groovy { }
\ No newline at end of file
+class Groovy { }
diff --git a/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/groovy/src/main/java/Java.java b/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/groovy/src/main/java/Java.java
index a85fdcb..e8eb469 100644
--- a/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/groovy/src/main/java/Java.java
+++ b/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/groovy/src/main/java/Java.java
@@ -1 +1 @@
-public class Java { Groovy groovy = new Groovy(); }
\ No newline at end of file
+public class Java { Groovy groovy = new Groovy(); }
diff --git a/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/kotlin/src/main/groovy/Groovy.groovy b/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/kotlin/src/main/groovy/Groovy.groovy
index 569685e..0bb35ef 100644
--- a/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/kotlin/src/main/groovy/Groovy.groovy
+++ b/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/kotlin/src/main/groovy/Groovy.groovy
@@ -1 +1 @@
-class Groovy { }
\ No newline at end of file
+class Groovy { }
diff --git a/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/kotlin/src/main/java/Java.java b/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/kotlin/src/main/java/Java.java
index a85fdcb..e8eb469 100644
--- a/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/kotlin/src/main/java/Java.java
+++ b/subprojects/docs/src/snippets/tutorial/compileTaskClasspath/kotlin/src/main/java/Java.java
@@ -1 +1 @@
-public class Java { Groovy groovy = new Groovy(); }
\ No newline at end of file
+public class Java { Groovy groovy = new Groovy(); }
diff --git a/subprojects/docs/src/snippets/tutorial/manifest/groovy/build.gradle b/subprojects/docs/src/snippets/tutorial/manifest/groovy/build.gradle
index 1165365..43ab912 100644
--- a/subprojects/docs/src/snippets/tutorial/manifest/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/tutorial/manifest/groovy/build.gradle
@@ -13,12 +13,12 @@
 // end::add-to-manifest[]
 
 // tag::custom-manifest[]
-ext.sharedManifest = manifest {
+def sharedManifest = java.manifest {
     attributes("Implementation-Title": "Gradle",
                "Implementation-Version": version)
 }
 tasks.register('fooJar', Jar) {
-    manifest = project.manifest {
+    manifest = java.manifest {
         from sharedManifest
     }
 }
diff --git a/subprojects/docs/src/snippets/tutorial/manifest/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/tutorial/manifest/kotlin/build.gradle.kts
index 91a2e36..6995824 100644
--- a/subprojects/docs/src/snippets/tutorial/manifest/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/tutorial/manifest/kotlin/build.gradle.kts
@@ -15,7 +15,7 @@
 // end::add-to-manifest[]
 
 // tag::custom-manifest[]
-val sharedManifest = the<JavaPluginConvention>().manifest {
+val sharedManifest = java.manifest {
     attributes (
         "Implementation-Title" to "Gradle",
         "Implementation-Version" to version
@@ -23,7 +23,7 @@
 }
 
 tasks.register<Jar>("fooJar") {
-    manifest = project.the<JavaPluginConvention>().manifest {
+    manifest = java.manifest {
         from(sharedManifest)
     }
 }
diff --git a/subprojects/docs/src/snippets/webApplication/customized/groovy/src/main/java/org/gradle/MyClass.java b/subprojects/docs/src/snippets/webApplication/customized/groovy/src/main/java/org/gradle/MyClass.java
index 6a93b19..e3504a0 100644
--- a/subprojects/docs/src/snippets/webApplication/customized/groovy/src/main/java/org/gradle/MyClass.java
+++ b/subprojects/docs/src/snippets/webApplication/customized/groovy/src/main/java/org/gradle/MyClass.java
@@ -8,4 +8,4 @@ public void doSomething() throws Exception {
         Class.forName("org.RuntimeClass");
         Class.forName("org.ProvidedRuntimeClass");
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/webApplication/customized/groovy/src/test/java/org/gradle/MyClassTest.java b/subprojects/docs/src/snippets/webApplication/customized/groovy/src/test/java/org/gradle/MyClassTest.java
index 85aff51..2e5785d 100644
--- a/subprojects/docs/src/snippets/webApplication/customized/groovy/src/test/java/org/gradle/MyClassTest.java
+++ b/subprojects/docs/src/snippets/webApplication/customized/groovy/src/test/java/org/gradle/MyClassTest.java
@@ -4,4 +4,4 @@ public class MyClassTest extends junit.framework.TestCase {
     public void testDoSomething() throws Exception {
         new MyClass().doSomething();
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/webApplication/customized/kotlin/src/main/java/org/gradle/MyClass.java b/subprojects/docs/src/snippets/webApplication/customized/kotlin/src/main/java/org/gradle/MyClass.java
index 6a93b19..e3504a0 100644
--- a/subprojects/docs/src/snippets/webApplication/customized/kotlin/src/main/java/org/gradle/MyClass.java
+++ b/subprojects/docs/src/snippets/webApplication/customized/kotlin/src/main/java/org/gradle/MyClass.java
@@ -8,4 +8,4 @@ public void doSomething() throws Exception {
         Class.forName("org.RuntimeClass");
         Class.forName("org.ProvidedRuntimeClass");
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/webApplication/customized/kotlin/src/test/java/org/gradle/MyClassTest.java b/subprojects/docs/src/snippets/webApplication/customized/kotlin/src/test/java/org/gradle/MyClassTest.java
index 85aff51..2e5785d 100644
--- a/subprojects/docs/src/snippets/webApplication/customized/kotlin/src/test/java/org/gradle/MyClassTest.java
+++ b/subprojects/docs/src/snippets/webApplication/customized/kotlin/src/test/java/org/gradle/MyClassTest.java
@@ -4,4 +4,4 @@ public class MyClassTest extends junit.framework.TestCase {
     public void testDoSomething() throws Exception {
         new MyClass().doSomething();
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/webApplication/quickstart/groovy/src/main/java/org/gradle/sample/Greeter.java b/subprojects/docs/src/snippets/webApplication/quickstart/groovy/src/main/java/org/gradle/sample/Greeter.java
index a40eb4b..e4bb429 100644
--- a/subprojects/docs/src/snippets/webApplication/quickstart/groovy/src/main/java/org/gradle/sample/Greeter.java
+++ b/subprojects/docs/src/snippets/webApplication/quickstart/groovy/src/main/java/org/gradle/sample/Greeter.java
@@ -15,4 +15,4 @@ public String getGreeting() throws Exception {
             greetingStr.close();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/webApplication/quickstart/kotlin/src/main/java/org/gradle/sample/Greeter.java b/subprojects/docs/src/snippets/webApplication/quickstart/kotlin/src/main/java/org/gradle/sample/Greeter.java
index a40eb4b..e4bb429 100644
--- a/subprojects/docs/src/snippets/webApplication/quickstart/kotlin/src/main/java/org/gradle/sample/Greeter.java
+++ b/subprojects/docs/src/snippets/webApplication/quickstart/kotlin/src/main/java/org/gradle/sample/Greeter.java
@@ -15,4 +15,4 @@ public String getGreeting() throws Exception {
             greetingStr.close();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/groovy/build.gradle b/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/groovy/build.gradle
index e9dcd79..b318acb 100644
--- a/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/groovy/build.gradle
@@ -10,7 +10,6 @@
     }
     visible = false
     canBeConsumed = false
-    canBeResolved = true
 }
 
 dependencies {
diff --git a/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/groovy/buildSrc/src/main/java/MD5WorkParameters.java b/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/groovy/buildSrc/src/main/java/MD5WorkParameters.java
index 6ff3ca9..de7af0d 100644
--- a/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/groovy/buildSrc/src/main/java/MD5WorkParameters.java
+++ b/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/groovy/buildSrc/src/main/java/MD5WorkParameters.java
@@ -4,4 +4,4 @@
 public interface MD5WorkParameters extends WorkParameters {
     RegularFileProperty getSourceFile();
     RegularFileProperty getMD5File();
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/kotlin/build.gradle.kts
index fc3d70e..d17b8ac 100644
--- a/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/kotlin/build.gradle.kts
@@ -10,7 +10,6 @@
     }
     isVisible = false
     isCanBeConsumed = false
-    isCanBeResolved = true
 }
 
 dependencies {
diff --git a/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/kotlin/buildSrc/src/main/java/MD5WorkParameters.java b/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/kotlin/buildSrc/src/main/java/MD5WorkParameters.java
index 6ff3ca9..de7af0d 100644
--- a/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/kotlin/buildSrc/src/main/java/MD5WorkParameters.java
+++ b/subprojects/docs/src/snippets/workerApi/md5ClassloaderIsolation/kotlin/buildSrc/src/main/java/MD5WorkParameters.java
@@ -4,4 +4,4 @@
 public interface MD5WorkParameters extends WorkParameters {
     RegularFileProperty getSourceFile();
     RegularFileProperty getMD5File();
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/groovy/build.gradle b/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/groovy/build.gradle
index be01e96..066e521 100644
--- a/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/groovy/build.gradle
+++ b/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/groovy/build.gradle
@@ -10,7 +10,6 @@
     }
     visible = false
     canBeConsumed = false
-    canBeResolved = true
 }
 
 dependencies {
diff --git a/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/groovy/buildSrc/src/main/java/MD5WorkParameters.java b/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/groovy/buildSrc/src/main/java/MD5WorkParameters.java
index 6ff3ca9..de7af0d 100644
--- a/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/groovy/buildSrc/src/main/java/MD5WorkParameters.java
+++ b/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/groovy/buildSrc/src/main/java/MD5WorkParameters.java
@@ -4,4 +4,4 @@
 public interface MD5WorkParameters extends WorkParameters {
     RegularFileProperty getSourceFile();
     RegularFileProperty getMD5File();
-}
\ No newline at end of file
+}
diff --git a/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/kotlin/build.gradle.kts b/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/kotlin/build.gradle.kts
index 735113c..2d57d4a 100644
--- a/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/kotlin/build.gradle.kts
+++ b/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/kotlin/build.gradle.kts
@@ -10,7 +10,6 @@
     }
     isVisible = false
     isCanBeConsumed = false
-    isCanBeResolved = true
 }
 
 dependencies {
diff --git a/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/kotlin/buildSrc/src/main/java/MD5WorkParameters.java b/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/kotlin/buildSrc/src/main/java/MD5WorkParameters.java
index 6ff3ca9..de7af0d 100644
--- a/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/kotlin/buildSrc/src/main/java/MD5WorkParameters.java
+++ b/subprojects/docs/src/snippets/workerApi/md5ProcessIsolation/kotlin/buildSrc/src/main/java/MD5WorkParameters.java
@@ -4,4 +4,4 @@
 public interface MD5WorkParameters extends WorkParameters {
     RegularFileProperty getSourceFile();
     RegularFileProperty getMD5File();
-}
\ No newline at end of file
+}
diff --git a/subprojects/ear/src/integTest/groovy/org/gradle/plugins/ear/EarPluginIntegrationTest.groovy b/subprojects/ear/src/integTest/groovy/org/gradle/plugins/ear/EarPluginIntegrationTest.groovy
index 6438b00..9d853d4 100644
--- a/subprojects/ear/src/integTest/groovy/org/gradle/plugins/ear/EarPluginIntegrationTest.groovy
+++ b/subprojects/ear/src/integTest/groovy/org/gradle/plugins/ear/EarPluginIntegrationTest.groovy
@@ -19,7 +19,6 @@
 import groovy.xml.XmlSlurper
 import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.archives.TestReproducibleArchives
 import org.gradle.test.fixtures.archive.JarTestFixture
 import org.hamcrest.CoreMatchers
@@ -82,9 +81,10 @@
         def ear = new JarTestFixture(file('build/libs/root.ear'))
         ear.assertContainsFile("CUSTOM/lib/earLib.jar")
         ear.assertFileContent("META-INF/application.xml", CoreMatchers.containsString("cool ear"))
+        def appXml = new XmlSlurper().parseText(ear.content('META-INF/application.xml'))
+        appXml.'library-directory'.text() == 'CUSTOM/lib'
     }
 
-    @ToBeFixedForConfigurationCache(because = "jar is missing from deployment descriptor")
     void "includes modules in deployment descriptor"() {
         file('moduleA.jar').createFile()
         file('moduleB.war').createFile()
@@ -162,7 +162,6 @@
         ear.assertNotContainsFile("META-INF/application.xml")
     }
 
-    @ToBeFixedForConfigurationCache(because = "Entry META-INF/application.xml is a duplicate")
     void "uses content found in #location app folder, ignoring descriptor modification"() {
         def applicationXml = """<?xml version="1.0"?>
 <application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" version="6">
@@ -179,7 +178,7 @@
 ear {
     ${descriptorConfig}
     deploymentDescriptor {
-        applicationName = 'descriptor modification will not have any affect when application.xml already exists in source'
+        applicationName = 'descriptor modification will not have any effect when application.xml already exists in source'
     }
 }
 """
@@ -194,12 +193,11 @@
         ear.assertFileContent("META-INF/application.xml", applicationXml)
 
         where:
-        location    | descriptorConfig   | appDirectory
-        "specified" | "appDirName 'app'" | "app"
-        "default"   | ""                 | "src/main/application"
+        location    | descriptorConfig                                                                   | appDirectory
+        "specified" | "tasks.named('ear') { appDirectory = project.layout.projectDirectory.dir('app') }" | "app"
+        "default"   | ""                                                                                 | "src/main/application"
     }
 
-    @ToBeFixedForConfigurationCache(because = "Entry META-INF/application.xml is a duplicate")
     void "works with existing descriptor containing a doctype declaration"() {
         // We serve the DTD locally because the parser actually pulls on this URL,
         // and we don't want it reaching out to the Internet in our tests
@@ -319,7 +317,6 @@
         module."context-root" == "anywhere"
     }
 
-    @ToBeFixedForConfigurationCache(because = "Entry META-INF/application.xml is a duplicate")
     @Issue("GRADLE-3486")
     def "does not fail when provided with an existing descriptor without a version attribute"() {
         given:
@@ -361,7 +358,6 @@
         ear.assertContainsFile("META-INF/application.xml")
     }
 
-    @ToBeFixedForConfigurationCache(because = "Entry META-INF/application.xml is a duplicate")
     @Issue("GRADLE-3497")
     def "does not fail when provided with an existing descriptor with security roles without description"() {
         given:
@@ -389,7 +385,6 @@
         ear.assertContainsFile("META-INF/application.xml")
     }
 
-    @ToBeFixedForConfigurationCache(because = "Entry META-INF/application.xml is a duplicate")
     @Issue("GRADLE-3497")
     def "does not fail when provided with an existing descriptor with a web module without #missing"() {
         given:
@@ -443,6 +438,26 @@
         ear.assertContainsFile("lib/rootLib.jar")
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/19725")
+    def "can apply ear plugin to empty project"() {
+        given:
+        settingsFile << """
+        rootProject.name = 'empty-project'
+        """
+        buildFile << """
+            apply plugin: 'ear'
+        """
+
+        when:
+        succeeds 'assemble'
+        succeeds 'assemble'
+
+        then:
+        def ear = new JarTestFixture(file('build/libs/empty-project.ear'))
+        ear.assertContainsFile("META-INF/MANIFEST.MF")
+        ear.assertContainsFile("META-INF/application.xml")
+    }
+
     def "ear contains runtime classpath of upstream java project"() {
         given:
         file("settings.gradle") << """
diff --git a/subprojects/ear/src/main/java/org/gradle/plugins/ear/Ear.java b/subprojects/ear/src/main/java/org/gradle/plugins/ear/Ear.java
index 5efda81..aa272c5 100644
--- a/subprojects/ear/src/main/java/org/gradle/plugins/ear/Ear.java
+++ b/subprojects/ear/src/main/java/org/gradle/plugins/ear/Ear.java
@@ -31,9 +31,7 @@
 import org.gradle.api.tasks.Internal;
 import org.gradle.api.tasks.Optional;
 import org.gradle.api.tasks.bundling.Jar;
-import org.gradle.internal.UncheckedException;
 import org.gradle.internal.execution.OutputChangeListener;
-import org.gradle.internal.serialization.Cached;
 import org.gradle.plugins.ear.descriptor.DeploymentDescriptor;
 import org.gradle.plugins.ear.descriptor.EarModule;
 import org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor;
@@ -45,9 +43,7 @@
 
 import javax.annotation.Nullable;
 import javax.inject.Inject;
-import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.IOException;
 import java.io.OutputStreamWriter;
 
 import static java.util.Collections.singleton;
@@ -87,6 +83,8 @@ public Ear() {
         // this allows us to generate the deployment descriptor after recording all modules it contains
         CopySpecInternal metaInf = (CopySpecInternal) getMainSpec().addChild().into("META-INF");
         CopySpecInternal descriptorChild = metaInf.addChild();
+        // the generated descriptor should only be used if one does not already exist
+        descriptorChild.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE);
         descriptorChild.from(callable(() -> {
             final DeploymentDescriptor descriptor = getDeploymentDescriptor();
             if (descriptor != null && generateDeploymentDescriptor.get()) {
@@ -103,19 +101,16 @@ public Ear() {
                 //  so any captured manifest attribute providers are re-evaluated
                 //  on each run.
                 //  See https://github.com/gradle/configuration-cache/issues/168
-                Cached<byte[]> cachedDescriptor = cachedContentsOf(descriptor);
                 final OutputChangeListener outputChangeListener = outputChangeListener();
                 return fileCollectionFactory().generated(
                     getTemporaryDirFactory(),
                     descriptorFileName,
                     action(file -> outputChangeListener.invalidateCachesFor(singleton(file.getAbsolutePath()))),
-                    action(outputStream -> {
-                        try {
-                            outputStream.write(cachedDescriptor.get());
-                        } catch (IOException e) {
-                            throw UncheckedException.throwAsUncheckedException(e);
-                        }
-                    })
+                    action(outputStream ->
+                        // delay obtaining contents to account for descriptor changes
+                        // (for instance, due to modules discovered)
+                        descriptor.writeTo(new OutputStreamWriter(outputStream))
+                    )
                 );
             }
 
@@ -125,14 +120,6 @@ public Ear() {
         appDir = getObjectFactory().directoryProperty();
     }
 
-    private Cached<byte[]> cachedContentsOf(DeploymentDescriptor descriptor) {
-        return Cached.of(() -> {
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-            descriptor.writeTo(new OutputStreamWriter(bytes));
-            return bytes.toByteArray();
-        });
-    }
-
     private FileCollectionFactory fileCollectionFactory() {
         return getServices().get(FileCollectionFactory.class);
     }
diff --git a/subprojects/ear/src/main/java/org/gradle/plugins/ear/EarPlugin.java b/subprojects/ear/src/main/java/org/gradle/plugins/ear/EarPlugin.java
index b9972d7..3d8fa13 100644
--- a/subprojects/ear/src/main/java/org/gradle/plugins/ear/EarPlugin.java
+++ b/subprojects/ear/src/main/java/org/gradle/plugins/ear/EarPlugin.java
@@ -19,9 +19,10 @@
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.Dependency;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact;
 import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet;
 import org.gradle.api.internal.project.ProjectInternal;
@@ -30,11 +31,13 @@
 import org.gradle.api.plugins.BasePlugin;
 import org.gradle.api.plugins.JavaBasePlugin;
 import org.gradle.api.plugins.JavaPlugin;
-import org.gradle.api.plugins.internal.JavaPluginHelper;
 import org.gradle.api.plugins.PluginContainer;
+import org.gradle.api.plugins.internal.JavaPluginHelper;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.plugins.jvm.internal.JvmPluginServices;
 import org.gradle.api.tasks.TaskProvider;
 import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.plugins.ear.descriptor.DeploymentDescriptor;
 
 import javax.inject.Inject;
@@ -78,11 +81,13 @@ public void apply(final Project project) {
         project.getPluginManager().apply(JavaBasePlugin.class);
 
         EarPluginConvention earPluginConvention = objectFactory.newInstance(org.gradle.plugins.ear.internal.DefaultEarPluginConvention.class);
-        project.getConvention().getPlugins().put("ear", earPluginConvention);
-        earPluginConvention.setLibDirName(DEFAULT_LIB_DIR_NAME);
-        earPluginConvention.setAppDirName("src/main/application");
+        DeprecationLogger.whileDisabled(() -> {
+            project.getConvention().getPlugins().put("ear", earPluginConvention);
+            earPluginConvention.setLibDirName(DEFAULT_LIB_DIR_NAME);
+            earPluginConvention.setAppDirName("src/main/application");
+        });
 
-        configureConfigurations(project);
+        configureConfigurations((ProjectInternal) project);
 
         PluginContainer plugins = project.getPlugins();
         setupEarTask(project, earPluginConvention, plugins);
@@ -92,13 +97,13 @@ public void apply(final Project project) {
 
     private void wireEarTaskConventionsWithJavaPluginApplied(final Project project, PluginContainer plugins) {
         plugins.withType(JavaPlugin.class, javaPlugin -> {
-            final JvmSoftwareComponentInternal component = JavaPluginHelper.getJavaComponent(project);
+            final JvmFeatureInternal mainFeature = JavaPluginHelper.getJavaComponent(project).getMainFeature();
             project.getTasks().withType(Ear.class).configureEach(task -> {
                 task.dependsOn((Callable<FileCollection>) () ->
-                    component.getSourceSet().getRuntimeClasspath()
+                    mainFeature.getSourceSet().getRuntimeClasspath()
                 );
                 task.from((Callable<FileCollection>) () ->
-                    component.getMainOutput()
+                    mainFeature.getSourceSet().getOutput()
                 );
             });
         });
@@ -108,15 +113,15 @@ private void setupEarTask(final Project project, EarPluginConvention convention,
         TaskProvider<Ear> ear = project.getTasks().register(EAR_TASK_NAME, Ear.class, task -> {
             task.setDescription("Generates a ear archive with all the modules, the application descriptor and the libraries.");
             task.setGroup(BasePlugin.BUILD_GROUP);
-            task.getGenerateDeploymentDescriptor().convention(convention.getGenerateDeploymentDescriptor());
+            DeprecationLogger.whileDisabled(() -> task.getGenerateDeploymentDescriptor().convention(convention.getGenerateDeploymentDescriptor()));
 
             plugins.withType(JavaPlugin.class, javaPlugin -> {
                 final JvmSoftwareComponentInternal component = JavaPluginHelper.getJavaComponent(project);
-                    component.getSourceSet().getResources().srcDir(task.getAppDirectory());
+                component.getMainFeature().getSourceSet().getResources().srcDir(task.getAppDirectory());
             });
         });
 
-        DeploymentDescriptor deploymentDescriptor = convention.getDeploymentDescriptor();
+        DeploymentDescriptor deploymentDescriptor =  DeprecationLogger.whileDisabled(() -> convention.getDeploymentDescriptor());
         if (deploymentDescriptor != null) {
             if (deploymentDescriptor.getDisplayName() == null) {
                 deploymentDescriptor.setDisplayName(project.getName());
@@ -130,17 +135,17 @@ private void setupEarTask(final Project project, EarPluginConvention convention,
 
     private void wireEarTaskConventions(Project project, final EarPluginConvention earConvention) {
         project.getTasks().withType(Ear.class).configureEach(task -> {
-            task.getAppDirectory().convention(project.provider(() -> project.getLayout().getProjectDirectory().dir(earConvention.getAppDirName())));
+            task.getAppDirectory().convention(project.provider(() -> project.getLayout().getProjectDirectory().dir(DeprecationLogger.whileDisabled(() -> earConvention.getAppDirName()))));
             task.getConventionMapping().map("libDirName", new Callable<String>() {
                 @Override
                 public String call() throws Exception {
-                    return earConvention.getLibDirName();
+                    return DeprecationLogger.whileDisabled(() -> earConvention.getLibDirName());
                 }
             });
             task.getConventionMapping().map("deploymentDescriptor", new Callable<DeploymentDescriptor>() {
                 @Override
                 public DeploymentDescriptor call() throws Exception {
-                    return earConvention.getDeploymentDescriptor();
+                    return DeprecationLogger.whileDisabled(() -> earConvention.getDeploymentDescriptor());
                 }
             });
 
@@ -165,15 +170,15 @@ public DeploymentDescriptor call() throws Exception {
         });
     }
 
-    private void configureConfigurations(final Project project) {
+    private void configureConfigurations(final ProjectInternal project) {
         // Currently 'deploy' and 'earlib' are both _resolvable_ and _consumable_.
         // In the future, it would be good to split these so that the attributes can differ.
         // Then 'jvmPluginServices.configureAsRuntimeClasspath()' may be used to configure the resolving configurations.
-        ConfigurationContainer configurations = project.getConfigurations();
-        Configuration moduleConfiguration = configurations.create(DEPLOY_CONFIGURATION_NAME).setVisible(false)
+        RoleBasedConfigurationContainerInternal configurations = project.getConfigurations();
+        Configuration moduleConfiguration = configurations.createWithRole(DEPLOY_CONFIGURATION_NAME, ConfigurationRoles.LEGACY).setVisible(false)
             .setTransitive(false).setDescription("Classpath for deployable modules, not transitive.");
         jvmPluginServices.configureAttributes(moduleConfiguration, details -> details.library().runtimeUsage().withExternalDependencies());
-        Configuration earlibConfiguration = configurations.create(EARLIB_CONFIGURATION_NAME).setVisible(false)
+        Configuration earlibConfiguration = configurations.createWithRole(EARLIB_CONFIGURATION_NAME, ConfigurationRoles.LEGACY).setVisible(false)
             .setDescription("Classpath for module dependencies.");
         jvmPluginServices.configureAttributes(earlibConfiguration, details -> details.library().runtimeUsage().withExternalDependencies());
         configurations.getByName(Dependency.DEFAULT_CONFIGURATION)
diff --git a/subprojects/ear/src/main/java/org/gradle/plugins/ear/internal/DefaultEarPluginConvention.java b/subprojects/ear/src/main/java/org/gradle/plugins/ear/internal/DefaultEarPluginConvention.java
index ad33365..a70e425 100644
--- a/subprojects/ear/src/main/java/org/gradle/plugins/ear/internal/DefaultEarPluginConvention.java
+++ b/subprojects/ear/src/main/java/org/gradle/plugins/ear/internal/DefaultEarPluginConvention.java
@@ -21,6 +21,7 @@
 import org.gradle.api.provider.Property;
 import org.gradle.api.reflect.HasPublicType;
 import org.gradle.api.reflect.TypeOf;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.plugins.ear.descriptor.DeploymentDescriptor;
 import org.gradle.plugins.ear.descriptor.internal.DefaultDeploymentDescriptor;
 import org.gradle.util.internal.ConfigureUtil;
@@ -56,11 +57,13 @@ public TypeOf<?> getPublicType() {
 
     @Override
     public String getAppDirName() {
+        logDeprecation();
         return appDirName;
     }
 
     @Override
     public void setAppDirName(String appDirName) {
+        logDeprecation();
         this.appDirName = appDirName;
         if (deploymentDescriptor != null) {
             deploymentDescriptor.readFrom(new File(appDirName, "META-INF/" + deploymentDescriptor.getFileName()));
@@ -69,55 +72,72 @@ public void setAppDirName(String appDirName) {
 
     @Override
     public void appDirName(String appDirName) {
+        logDeprecation();
         this.setAppDirName(appDirName);
     }
 
     @Override
     public String getLibDirName() {
+        logDeprecation();
         return libDirName;
     }
 
     @Override
     public void setLibDirName(String libDirName) {
+        logDeprecation();
         this.libDirName = libDirName;
     }
 
     @Override
     public void libDirName(String libDirName) {
+        logDeprecation();
         this.libDirName = libDirName;
     }
 
     @Override
     public Property<Boolean> getGenerateDeploymentDescriptor() {
+        logDeprecation();
         return generateDeploymentDescriptor;
     }
 
     @Override
     public DeploymentDescriptor getDeploymentDescriptor() {
+        logDeprecation();
         return deploymentDescriptor;
     }
 
     @Override
     public void setDeploymentDescriptor(DeploymentDescriptor deploymentDescriptor) {
+        logDeprecation();
         this.deploymentDescriptor = deploymentDescriptor;
     }
 
     @Override
     public DefaultEarPluginConvention deploymentDescriptor(Closure configureClosure) {
+        logDeprecation();
         ConfigureUtil.configure(configureClosure, forceDeploymentDescriptor());
         return this;
     }
 
     @Override
     public DefaultEarPluginConvention deploymentDescriptor(Action<? super DeploymentDescriptor> configureAction) {
+        logDeprecation();
         configureAction.execute(forceDeploymentDescriptor());
         return this;
     }
 
     private DeploymentDescriptor forceDeploymentDescriptor() {
+        logDeprecation();
         if (deploymentDescriptor == null) {
             deploymentDescriptor = objectFactory.newInstance(DefaultDeploymentDescriptor.class);
         }
         return deploymentDescriptor;
     }
+
+    private static void logDeprecation() {
+        DeprecationLogger.deprecateType(org.gradle.plugins.ear.EarPluginConvention.class)
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "ear_convention_deprecation")
+            .nagUser();
+    }
 }
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifier.java b/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifier.java
deleted file mode 100644
index 8543c7c..0000000
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ComponentIdentifier.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.component;
-
-/**
- * A marker interface for identifiers for a component instance.
- * <p>
- * There are various sub-interfaces that expose specific details about the identifier.
- */
-public interface ComponentIdentifier {
-}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ModuleComponentIdentifier.java b/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ModuleComponentIdentifier.java
deleted file mode 100644
index 9bc2d09..0000000
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ModuleComponentIdentifier.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.component;
-
-/**
- * An identifier for a component instance which is available as a module version.
- **/
-public interface ModuleComponentIdentifier extends ComponentIdentifier {
-
-    String getGroup();
-
-    String getModule();
-
-    String getVersion();
-
-}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/OpaqueComponentIdentifier.java b/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/OpaqueComponentIdentifier.java
deleted file mode 100644
index e4b888b..0000000
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/OpaqueComponentIdentifier.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.component;
-
-/**
- * An opaque immutable identifier for a component instance.
- */
-public interface OpaqueComponentIdentifier extends ComponentIdentifier {
-
-    String getDisplayName();
-
-    String getClassName();
-
-}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ProjectComponentIdentifier.java b/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ProjectComponentIdentifier.java
deleted file mode 100644
index 6d7f65b..0000000
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/component/ProjectComponentIdentifier.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.component;
-
-/**
- * An identifier for a component instance that is built as part of the current build.
- */
-public interface ProjectComponentIdentifier extends ComponentIdentifier {
-
-    String getBuildPath();
-
-    String getProjectPath();
-
-}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationIdentity.java b/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationIdentity.java
deleted file mode 100644
index 7aaf5e9..0000000
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/configurations/ConfigurationIdentity.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.configurations;
-
-import javax.annotation.Nullable;
-
-/**
- * Identifies a configuration.
- */
-public interface ConfigurationIdentity {
-
-    String getBuildPath();
-
-    @Nullable
-    String getProjectPath();
-
-    String getName();
-
-}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/transform/ExecuteScheduledTransformationStepBuildOperationType.java b/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/transform/ExecuteScheduledTransformationStepBuildOperationType.java
deleted file mode 100644
index e335d59b..0000000
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/transform/ExecuteScheduledTransformationStepBuildOperationType.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.transform;
-
-import org.gradle.internal.operations.BuildOperationType;
-import org.gradle.internal.scan.NotUsedByScanPlugin;
-
-import java.util.Map;
-
-/**
- * A {@link BuildOperationType} for executing a scheduled transformation step.
- * <p>
- * Encompasses the execution of a transformation node.
- * A transformation node runs only one transformation step, though possibly on multiple files.
- */
-public class ExecuteScheduledTransformationStepBuildOperationType implements BuildOperationType<ExecuteScheduledTransformationStepBuildOperationType.Details, ExecuteScheduledTransformationStepBuildOperationType.Result> {
-
-    public interface Details {
-
-        TransformationIdentity getTransformationIdentity();
-
-        Map<String, String> getSourceAttributes();
-
-        Map<String, String> getFromAttributes();
-
-        Map<String, String> getToAttributes();
-
-        Class<?> getTransformType();
-
-        /**
-         * Returns the display name of the transformer.
-         *
-         * Not used by build scans but for TAPI events in {@code TransformOperationMapper}.
-         */
-        @NotUsedByScanPlugin
-        String getTransformerName();
-
-        /**
-         * Returns the display name of the transformation subject.
-         *
-         * Not used by build scans but for TAPI events in {@code TransformOperationMapper}.
-         */
-        @NotUsedByScanPlugin
-        String getSubjectName();
-
-    }
-
-    public interface Result {
-    }
-}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationIdentity.java b/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationIdentity.java
deleted file mode 100644
index 963745f..0000000
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/artifacts/transform/TransformationIdentity.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.artifacts.transform;
-
-import org.gradle.api.internal.artifacts.component.ComponentIdentifier;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationIdentity;
-import org.gradle.api.internal.capabilities.Capability;
-import org.gradle.internal.taskgraph.NodeIdentity;
-
-import javax.annotation.Nullable;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Identity of a transformation node in an execution plan.
- *
- * @since 8.1
- */
-public interface TransformationIdentity extends NodeIdentity {
-
-    @Override
-    default NodeType getNodeType() {
-        return NodeType.ARTIFACT_TRANSFORM;
-    }
-
-    /**
-     * Path of an included build of the consumer project.
-     */
-    String getBuildPath();
-
-    /**
-     * Consumer project path within the build.
-     */
-    String getProjectPath();
-
-    /**
-     * The component identifier of the transformed artifact.
-     */
-    ComponentIdentifier getComponentId();
-
-    /**
-     * Target attributes of the transformed artifact.
-     * <p>
-     * The attributes include the source attributes of the artifact before the transformation and
-     */
-    Map<String, String> getTargetAttributes();
-
-    /**
-     * Capabilities of the variant of the transformed artifact.
-     * <p>
-     * Artifact transforms can only change attributes, so the capabilities remain unchanged throughout the transformation chain.
-     */
-    List<? extends Capability> getCapabilities();
-
-    /**
-     * Name of the source artifact being transformed.
-     * <p>
-     * This name remains the same throughout the transformation chain.
-     */
-    String getArtifactName();
-
-    /**
-     * Configuration that contains transitive dependencies of the input artifact.
-     * <p>
-     * Present only if the artifact transform implementation declares a dependency on the input artifact dependencies.
-     */
-    @Nullable
-    ConfigurationIdentity getDependenciesConfigurationIdentity();
-
-    /**
-     * An opaque identifier distinguishes between different transformation nodes in case other identity properties are the same.
-     */
-    long getTransformationNodeId();
-
-}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/capabilities/Capability.java b/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/capabilities/Capability.java
deleted file mode 100644
index 31183c1..0000000
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/api/internal/capabilities/Capability.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2023 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.api.internal.capabilities;
-
-import javax.annotation.Nullable;
-
-/**
- * A capability provided by a component.
- *
- * @since 8.1
- */
-public interface Capability {
-
-    String getGroup();
-
-    String getName();
-
-    @Nullable
-    String getVersion();
-}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/CalculateTaskGraphBuildOperationType.java b/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/CalculateTaskGraphBuildOperationType.java
index aec4400..4570f5c 100644
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/CalculateTaskGraphBuildOperationType.java
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/CalculateTaskGraphBuildOperationType.java
@@ -56,11 +56,6 @@ public interface PlannedNode {
      */
     public interface TaskIdentity extends NodeIdentity {
 
-        @Override
-        default NodeType getNodeType() {
-            return NodeType.TASK;
-        }
-
         String getBuildPath();
 
         String getTaskPath();
@@ -81,6 +76,10 @@ public interface PlannedTask extends PlannedNode {
 
         TaskIdentity getTask();
 
+        /**
+         * @deprecated Use {@link #getNodeDependencies()} instead.
+         */
+        @Deprecated
         List<TaskIdentity> getDependencies();
 
         List<TaskIdentity> getMustRunAfter();
@@ -125,12 +124,13 @@ public interface Result {
          */
         List<PlannedTask> getTaskPlan();
 
-
         /**
-         * Returns all identifiable nodes of requested types in the execution graph in no particular order.
+         * Returns an execution plan consisting of nodes of the given types.
          * <p>
-         * Identities of dependencies could be of any type.
+         * The graph is represented as a list of nodes (in no particular order) and their {@link PlannedNode#getNodeDependencies() dependencies}.
+         * The dependencies of each node are the closest nodes in the plan whose type is in the given set.
          *
+         * @param types an inclusive range-subset of node types starting with the {@link NodeIdentity.NodeType#TASK TASK}, such as {@code [TASK, TRANSFORM_STEP]}
          * @since 8.1
          */
         List<PlannedNode> getExecutionPlan(Set<NodeIdentity.NodeType> types);
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/NodeIdentity.java b/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/NodeIdentity.java
index 555ef4d..c618aea 100644
--- a/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/NodeIdentity.java
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/NodeIdentity.java
@@ -20,13 +20,15 @@
  * The identity of a node in the execution graph.
  * <p>
  * Needs to be unique across all the execution graphs of the current build.
+ *
+ * @implNote The implementations must provide value-based equality.
+ * @since 8.1
  */
-// TODO: should identities implement equality? If not, this should be noted in the contract
 public interface NodeIdentity {
 
     enum NodeType {
         TASK,
-        ARTIFACT_TRANSFORM
+        TRANSFORM_STEP
     }
 
     NodeType getNodeType();
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/package-info.java b/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/package-info.java
new file mode 100644
index 0000000..edc09dc
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/internal/taskgraph/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * Build operations and data types for execution plan.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.internal.taskgraph;
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/configurations/ConfigurationIdentity.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/configurations/ConfigurationIdentity.java
new file mode 100644
index 0000000..40f3b77
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/configurations/ConfigurationIdentity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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.operations.dependencies.configurations;
+
+import javax.annotation.Nullable;
+
+/**
+ * Identifies a configuration.
+ *
+ * @since 8.1
+ */
+public interface ConfigurationIdentity {
+
+    String getBuildPath();
+
+    @Nullable
+    String getProjectPath();
+
+    String getName();
+
+}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/configurations/package-info.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/configurations/package-info.java
new file mode 100644
index 0000000..cbe092a
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/configurations/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * Build operations and data types for configurations.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.operations.dependencies.configurations;
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/ExecutePlannedTransformStepBuildOperationType.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/ExecutePlannedTransformStepBuildOperationType.java
new file mode 100644
index 0000000..49b92fc
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/ExecutePlannedTransformStepBuildOperationType.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 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.operations.dependencies.transforms;
+
+import org.gradle.internal.operations.BuildOperationType;
+import org.gradle.internal.scan.NotUsedByScanPlugin;
+
+/**
+ * A {@link BuildOperationType} for executing a scheduled transform step.
+ * <p>
+ * Encompasses the execution of a transform step node.
+ * The node runs only one transform, though possibly on multiple files.
+ *
+ * @since 8.1
+ */
+public class ExecutePlannedTransformStepBuildOperationType implements BuildOperationType<ExecutePlannedTransformStepBuildOperationType.Details, ExecutePlannedTransformStepBuildOperationType.Result> {
+
+    public interface Details {
+
+        /**
+         * The identity of the transform step executed in this operation.
+         */
+        PlannedTransformStepIdentity getPlannedTransformStepIdentity();
+
+        /**
+         * Class of the transformer action implementation provided as part of transform registration.
+         */
+        Class<?> getTransformActionClass();
+
+        /**
+         * Returns the display name of the transformer.
+         *
+         * Not used by build scans but for TAPI events in {@code TransformOperationMapper}.
+         */
+        @NotUsedByScanPlugin
+        String getTransformerName();
+
+        /**
+         * Returns the display name of the transform step subject.
+         *
+         * Not used by build scans but for TAPI events in {@code TransformOperationMapper}.
+         */
+        @NotUsedByScanPlugin
+        String getSubjectName();
+
+    }
+
+    public interface Result {
+    }
+}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/PlannedTransformStepIdentity.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/PlannedTransformStepIdentity.java
new file mode 100644
index 0000000..9d4129b
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/PlannedTransformStepIdentity.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023 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.operations.dependencies.transforms;
+
+import org.gradle.internal.taskgraph.NodeIdentity;
+import org.gradle.operations.dependencies.configurations.ConfigurationIdentity;
+import org.gradle.operations.dependencies.variants.Capability;
+import org.gradle.operations.dependencies.variants.ComponentIdentifier;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Identity of a transform step node in an execution plan.
+ *
+ * @since 8.1
+ */
+public interface PlannedTransformStepIdentity extends NodeIdentity {
+
+    /**
+     * Path of an included build of the consumer project.
+     */
+    String getConsumerBuildPath();
+
+    /**
+     * Consumer project path within the build.
+     */
+    String getConsumerProjectPath();
+
+    /**
+     * The component identifier of the transformed artifact.
+     */
+    ComponentIdentifier getComponentId();
+
+    /**
+     * Full set of attributes of the artifact before the transform.
+     */
+    Map<String, String> getSourceAttributes();
+
+    /**
+     * Target attributes of the transformed artifact.
+     * <p>
+     * The attributes include all source attributes of the artifact before the transform,
+     * values for some of which have been changed by the transform.
+     */
+    Map<String, String> getTargetAttributes();
+
+    /**
+     * Capabilities of the variant of the transformed artifact.
+     * <p>
+     * Artifact transforms can only change attributes, so the capabilities remain unchanged throughout the transform chain.
+     */
+    List<? extends Capability> getCapabilities();
+
+    /**
+     * Name of the source artifact being transformed.
+     * <p>
+     * This name remains the same throughout the transform chain.
+     */
+    String getArtifactName();
+
+    /**
+     * Configuration that contains transitive dependencies of the input artifact.
+     * <p>
+     * Present only if the artifact transform implementation declares a dependency on the input artifact dependencies.
+     */
+    @Nullable
+    ConfigurationIdentity getDependenciesConfigurationIdentity();
+
+    /**
+     * An opaque identifier distinguishes between different transform step nodes in case other identity properties are the same.
+     */
+    long getTransformStepNodeId();
+
+}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/package-info.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/package-info.java
new file mode 100644
index 0000000..e479416
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/transforms/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * Build operations and data types for artifact transforms.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.operations.dependencies.transforms;
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/Capability.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/Capability.java
new file mode 100644
index 0000000..2212a1a
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/Capability.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 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.operations.dependencies.variants;
+
+import javax.annotation.Nullable;
+
+/**
+ * A capability provided by a component.
+ *
+ * @since 8.1
+ */
+public interface Capability {
+
+    String getGroup();
+
+    String getName();
+
+    @Nullable
+    String getVersion();
+}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ComponentIdentifier.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ComponentIdentifier.java
new file mode 100644
index 0000000..08b80b9
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ComponentIdentifier.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.operations.dependencies.variants;
+
+/**
+ * A marker interface for identifiers for a component instance.
+ * <p>
+ * There are various sub-interfaces that expose specific details about the identifier.
+ *
+ * @since 8.1
+ */
+public interface ComponentIdentifier {
+}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ModuleComponentIdentifier.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ModuleComponentIdentifier.java
new file mode 100644
index 0000000..e8a33a7
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ModuleComponentIdentifier.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 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.operations.dependencies.variants;
+
+/**
+ * An identifier for a component instance which is available as a module version.
+ *
+ * @since 8.1
+ **/
+public interface ModuleComponentIdentifier extends ComponentIdentifier {
+
+    String getGroup();
+
+    String getModule();
+
+    String getVersion();
+
+}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/OpaqueComponentIdentifier.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/OpaqueComponentIdentifier.java
new file mode 100644
index 0000000..d9ffcdf
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/OpaqueComponentIdentifier.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.operations.dependencies.variants;
+
+/**
+ * An opaque immutable identifier for a component instance.
+ *
+ * @since 8.1
+ */
+public interface OpaqueComponentIdentifier extends ComponentIdentifier {
+
+    String getDisplayName();
+
+    String getClassName();
+
+}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ProjectComponentIdentifier.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ProjectComponentIdentifier.java
new file mode 100644
index 0000000..129c8a7
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/ProjectComponentIdentifier.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.operations.dependencies.variants;
+
+/**
+ * An identifier for a component instance that is built as part of the current build.
+ *
+ * @since 8.1
+ */
+public interface ProjectComponentIdentifier extends ComponentIdentifier {
+
+    String getBuildPath();
+
+    String getProjectPath();
+
+}
diff --git a/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/package-info.java b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/package-info.java
new file mode 100644
index 0000000..8d59605
--- /dev/null
+++ b/subprojects/enterprise-operations/src/main/java/org/gradle/operations/dependencies/variants/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * Build operations and data types for component variants.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.operations.dependencies.variants;
diff --git a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginBackgroundJobExecutorsIntegrationTest.groovy b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginBackgroundJobExecutorsIntegrationTest.groovy
index 3d9abc1..4c6a7a0 100644
--- a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginBackgroundJobExecutorsIntegrationTest.groovy
+++ b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginBackgroundJobExecutorsIntegrationTest.groovy
@@ -379,8 +379,6 @@
         given:
         ShellScript script = ShellScript.builder().printText("Hello, world").writeTo(testDirectory, "script")
 
-        withStableConfigurationCacheEnabled()
-
         buildFile << """
             import ${CompletableFuture.name}
 
@@ -407,8 +405,6 @@
         given:
         ShellScript script = ShellScript.builder().printText("Hello, world").writeTo(testDirectory, "script")
 
-        withStableConfigurationCacheEnabled()
-
         buildFile << """
             import ${CompletableFuture.name}
             import ${Inject.name}
@@ -442,10 +438,4 @@
     private static String getExecutors() {
         return """(gradle.extensions.serviceRef.get()._requiredServices.backgroundJobExecutors)"""
     }
-
-    private withStableConfigurationCacheEnabled() {
-        settingsFile("""
-            enableFeaturePreview("STABLE_CONFIGURATION_CACHE")
-        """)
-    }
 }
diff --git a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginConfigIntegrationTest.groovy b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginConfigIntegrationTest.groovy
index d0851de..479a433 100644
--- a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginConfigIntegrationTest.groovy
+++ b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginConfigIntegrationTest.groovy
@@ -17,7 +17,12 @@
 package org.gradle.internal.enterprise
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.plugin.management.internal.autoapply.AutoAppliedGradleEnterprisePlugin
+import spock.lang.IgnoreIf
+import spock.lang.Issue
+
+import javax.annotation.Nullable
 
 import static org.gradle.internal.enterprise.GradleEnterprisePluginConfig.BuildScanRequest.NONE
 import static org.gradle.internal.enterprise.GradleEnterprisePluginConfig.BuildScanRequest.REQUESTED
@@ -134,4 +139,33 @@
         outputContains("${plugin.id} is already applied")
     }
 
+    @Issue('https://github.com/gradle/gradle/issues/24023')
+    @IgnoreIf({ GradleContextualExecuter.notConfigCache })
+    def 'is correctly requested by the configuration cache'() {
+        when:
+        succeeds('t', *firstBuildArgs)
+
+        then:
+        assertRequestedOrNotApplied firstRequest
+
+        when:
+        succeeds('t', *secondBuildArgs)
+
+        then:
+        assertRequestedOrNotApplied secondRequest
+
+        where:
+        firstBuildArgs | firstRequest | secondBuildArgs | secondRequest
+        ['--scan']     | REQUESTED    | []              | null
+        []             | null         | ['--scan']      | REQUESTED
+        ['--scan']     | REQUESTED    | ['--scan']      | REQUESTED
+    }
+
+    void assertRequestedOrNotApplied(@Nullable GradleEnterprisePluginConfig.BuildScanRequest buildScanRequest) {
+        if (buildScanRequest) {
+            plugin.assertBuildScanRequest(output, buildScanRequest)
+        } else {
+            plugin.notApplied(output)
+        }
+    }
 }
diff --git a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginConfigurationCachingIntegrationTest.groovy b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginConfigurationCachingIntegrationTest.groovy
index 028a1a1..4e8abbb 100644
--- a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginConfigurationCachingIntegrationTest.groovy
+++ b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/GradleEnterprisePluginConfigurationCachingIntegrationTest.groovy
@@ -18,7 +18,9 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.util.internal.ToBeImplemented
 import spock.lang.IgnoreIf
+import spock.lang.Issue
 
 import static org.gradle.internal.enterprise.GradleEnterprisePluginConfig.BuildScanRequest.NONE
 import static org.gradle.internal.enterprise.GradleEnterprisePluginConfig.BuildScanRequest.REQUESTED
@@ -96,6 +98,8 @@
         plugin.assertBuildScanRequest(output, REQUESTED)
     }
 
+    @Issue('https://github.com/gradle/gradle/issues/24163')
+    @ToBeImplemented
     def "does not consider scan arg part of key and provides value to service"() {
         when:
         succeeds "t", "--configuration-cache"
@@ -108,14 +112,17 @@
         succeeds "t", "--configuration-cache", "--scan"
 
         then:
-        plugin.notApplied(output)
+        // TODO plugin.notApplied(output)
+        plugin.appliedOnce(output)
+
         plugin.assertBuildScanRequest(output, REQUESTED)
 
         when:
         succeeds "t", "--configuration-cache", "--no-scan"
 
         then:
-        plugin.notApplied(output)
+        // TODO plugin.notApplied(output)
+        plugin.appliedOnce(output)
         plugin.assertBuildScanRequest(output, SUPPRESSED)
     }
 
diff --git a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/core/BuildScanBuildFailureHintIntegrationTest.groovy b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/core/BuildScanBuildFailureHintIntegrationTest.groovy
index 4347971..9b115e1 100644
--- a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/core/BuildScanBuildFailureHintIntegrationTest.groovy
+++ b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/core/BuildScanBuildFailureHintIntegrationTest.groovy
@@ -18,18 +18,18 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.internal.enterprise.GradleEnterprisePluginCheckInFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import static org.gradle.initialization.StartParameterBuildOptions.BuildScanOption
-import static org.gradle.integtests.fixtures.BuildScanUserInputFixture.BUILD_SCAN_ERROR_MESSAGE_HINT
 import static org.gradle.integtests.fixtures.BuildScanUserInputFixture.DUMMY_TASK_NAME
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
 import static org.gradle.internal.logging.LoggingConfigurationBuildOptions.LogLevelOption
 import static org.gradle.internal.logging.LoggingConfigurationBuildOptions.StacktraceOption
 
 @Issue("https://github.com/gradle/gradle/issues/3516")
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 class BuildScanBuildFailureHintIntegrationTest extends AbstractIntegrationSpec {
 
     private static final List<String> DUMMY_TASK_ONLY = [DUMMY_TASK_NAME]
@@ -52,7 +52,7 @@
         succeeds(DUMMY_TASK_NAME)
 
         then:
-        result.assertNotOutput(BUILD_SCAN_ERROR_MESSAGE_HINT)
+        result.assertNotOutput(SCAN)
     }
 
     def "renders hint for failing build without applied plugin and #description"() {
@@ -64,7 +64,7 @@
 
         then:
         fixture.notApplied(output)
-        failure.assertHasResolution(BUILD_SCAN_ERROR_MESSAGE_HINT)
+        failure.assertHasResolution(SCAN)
 
         where:
         options                                             | description
@@ -86,7 +86,7 @@
 
         then:
         fixture.appliedOnce(output)
-        failure.assertNotOutput(BUILD_SCAN_ERROR_MESSAGE_HINT)
+        failure.assertNotOutput(SCAN)
     }
 
     def "never renders hint for failing build if plugin was applied in plugins DSL and not requested for generation"() {
@@ -99,7 +99,7 @@
 
         then:
         fixture.appliedOnce(output)
-        failure.assertNotOutput(BUILD_SCAN_ERROR_MESSAGE_HINT)
+        failure.assertNotOutput(SCAN)
 
         where:
         tasks << [ DUMMY_TASK_ONLY, DUMMY_TASK_AND_BUILD_SCAN ]
diff --git a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/test/TestTaskPropertiesServiceIntegrationTest.groovy b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/test/TestTaskPropertiesServiceIntegrationTest.groovy
index ceeafa9..ad04379 100644
--- a/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/test/TestTaskPropertiesServiceIntegrationTest.groovy
+++ b/subprojects/enterprise/src/integTest/groovy/org/gradle/internal/enterprise/test/TestTaskPropertiesServiceIntegrationTest.groovy
@@ -21,60 +21,69 @@
 import groovy.json.JsonSlurper
 import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
 import org.gradle.internal.jvm.Jvm
 import org.gradle.test.fixtures.plugin.PluginBuilder
 
+import javax.inject.Inject
 import java.util.stream.Stream
 
 class TestTaskPropertiesServiceIntegrationTest extends AbstractIntegrationSpec {
 
-    @UnsupportedWithConfigurationCache(because = "test-plugin does not use CC-compatible APIs")
-    def "can inject TestTaskPropertiesService into plugin"() {
+    def "provides task configuration to plugin"() {
         given:
-        def pluginBuilder = new PluginBuilder(file("buildSrc"))
-        pluginBuilder.addPlugin("""
-            project.task('testTaskProperties') { doLast {
+        new PluginBuilder(file("buildSrc")).with {
+            addPlugin("""
+                def objects = project.objects
+                def outputFile = project.file("\${project.buildDir}/testTaskProperties.json")
                 def service = project.services.get(${TestTaskPropertiesService.name})
-                def properties = service.collectProperties(project.tasks['test'])
+                project.tasks.named('test').configure {
+                    doFirst { task ->
+                        def properties = service.collectProperties(task)
 
-                def generator = new ${JsonGenerator.Options.name}()
-                    .addConverter(new groovy.json.JsonGenerator.Converter() {
-                        @Override
-                        boolean handles(Class<?> type) {
-                            ${Stream.name}.isAssignableFrom(type)
-                        }
-                        @Override
-                        Object convert(Object value, String key) {
-                            (value as ${Stream.name}).toArray()
-                        }
-                    })
-                    .addConverter(new ${JsonGenerator.Converter.name}() {
-                        @Override
-                        boolean handles(Class<?> type) {
-                            ${File.name}.isAssignableFrom(type)
-                        }
-                        @Override
-                        Object convert(Object value, String key) {
-                            (value as ${File.name}).absolutePath
-                        }
-                    })
-                    .build()
+                        def generator = new ${JsonGenerator.Options.name}()
+                            .addConverter(new groovy.json.JsonGenerator.Converter() {
+                                @Override
+                                boolean handles(Class<?> type) {
+                                    ${Stream.name}.isAssignableFrom(type)
+                                }
+                                @Override
+                                Object convert(Object value, String key) {
+                                    (value as ${Stream.name}).toArray()
+                                }
+                            })
+                            .addConverter(new ${JsonGenerator.Converter.name}() {
+                                @Override
+                                boolean handles(Class<?> type) {
+                                    ${File.name}.isAssignableFrom(type)
+                                }
+                                @Override
+                                Object convert(Object value, String key) {
+                                    (value as ${File.name}).absolutePath
+                                }
+                            })
+                            .build()
 
-                def json = ${JsonOutput.name}.prettyPrint(generator.toJson(properties))
+                        def json = ${JsonOutput.name}.prettyPrint(generator.toJson(properties))
 
-                def file = project.file("\${project.buildDir}/testTaskProperties.json")
-                file.parentFile.mkdirs()
-                file.text = json
-            }}
-        """)
-        pluginBuilder.generateForBuildSrc()
+                        outputFile.parentFile.mkdirs()
+                        outputFile.text = json
+                    }
+                }
+            """)
+            generateForBuildSrc()
+        }
 
+        and:
         buildFile << """
             plugins {
                 id 'java'
                 id 'test-plugin'
             }
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation('org.junit.jupiter:junit-jupiter:5.9.2')
+                testRuntimeOnly('org.junit.platform:junit-platform-launcher')
+            }
             test {
                 forkEvery = 42
                 filter {
@@ -89,18 +98,20 @@
                 }
                 jvmArgs('-Dkey=value')
                 environment('KEY', 'VALUE')
-                enabled = false
             }
         """
-        file('src/test/java/org/example/TestClass.java') << """
+        file('src/test/java/org/example/SomeClass.java') << """
             package org.example;
-            public class TestClass {}
+            public class SomeClass {}
         """
 
         when:
-        succeeds('test', '--tests', 'Test*', 'testTaskProperties')
+        fails('test', '--tests', 'Test*')
 
         then:
+        failureCauseContains("No tests found for given includes")
+
+        and:
         def expectedClasspath = [
             file('build/classes/java/test'),
             file('build/resources/test'),
@@ -125,7 +136,7 @@
                 workingDir == this.testDirectory.absolutePath
                 executable == expectedExecutable
                 javaMajorVersion == expectedJavaVersion
-                classpath.collect { new File(it as String) } == expectedClasspath
+                classpath.collect { new File(it as String) }.containsAll(expectedClasspath)
                 modulePath == []
                 jvmArgs.contains('-Dkey=value')
                 environment.KEY == 'VALUE'
@@ -133,14 +144,14 @@
             with(candidateClassFiles, List) {
                 size() == 1
                 with(first(), Map) {
-                    file == file("build/classes/java/test/org/example/TestClass.class").absolutePath
-                    relativePath == "org/example/TestClass.class"
+                    file == file("build/classes/java/test/org/example/SomeClass.class").absolutePath
+                    relativePath == "org/example/SomeClass.class"
                 }
             }
             with(inputFileProperties, List) {
                 !empty
                 with(it.find { it instanceof Map && it['propertyName'] == 'stableClasspath' }, Map) {
-                    files.collect { new File(it as String) } == expectedClasspath
+                    files.collect { new File(it as String) }.containsAll(expectedClasspath)
                 }
             }
             with(outputFileProperties, List) {
@@ -152,4 +163,47 @@
             }
         }
     }
+
+    def "can be used to disable storing task in build cache"() {
+        given:
+        def cacheDir = createDir("cache-dir")
+        settingsFile << """
+            buildCache {
+                local {
+                    directory = file("${cacheDir.name}")
+                }
+            }
+        """
+        buildFile << """
+            plugins {
+                id 'java'
+            }
+            ${mavenCentralRepository()}
+            testing.suites.test.useJUnit()
+            test.doLast(objects.newInstance(Disabler))
+            abstract class Disabler implements Action<Task> {
+                private final ${TestTaskPropertiesService.name} service
+                @${Inject.name} Disabler(${TestTaskPropertiesService.name} service) {
+                    this.service = service
+                }
+                @Override void execute(Task task) {
+                    service.doNotStoreInCache(task)
+                }
+            }
+        """
+        file('src/test/java/org/example/TestClass.java') << """
+            package org.example;
+            public class TestClass {
+                @org.junit.Test
+                public void test() {}
+            }
+        """
+
+        when:
+        succeeds('clean', 'test', '--build-cache')
+        succeeds('clean', 'test', '--build-cache')
+
+        then:
+        executedAndNotSkipped(':test')
+    }
 }
diff --git a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/test/TestTaskPropertiesService.java b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/test/TestTaskPropertiesService.java
index 5681ec9..da45900 100644
--- a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/test/TestTaskPropertiesService.java
+++ b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/test/TestTaskPropertiesService.java
@@ -17,9 +17,28 @@
 package org.gradle.internal.enterprise.test;
 
 import org.gradle.api.tasks.testing.Test;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
 
+/**
+ * Provides access to the configuration of {@link Test} tasks and allows
+ * disabling storing their outputs in the build cache.
+ */
+@ServiceScope(Scopes.Project.class)
 public interface TestTaskPropertiesService {
 
+    /**
+     * Collect the configuration of the given task.
+     */
     TestTaskProperties collectProperties(Test task);
 
+    /**
+     * Avoid storing the task's outputs in the build cache.
+     * <p>
+     * This allows disabling storing a task's outputs in the build cache if it only executed a subset of tests
+     * while still allowing the task to be loaded from the build cache if a prior execution with the same
+     * cache key executed all tests.
+     */
+    void doNotStoreInCache(Test task);
+
 }
diff --git a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/test/impl/DefaultTestTaskPropertiesService.java b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/test/impl/DefaultTestTaskPropertiesService.java
index f555e27..19487fd 100644
--- a/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/test/impl/DefaultTestTaskPropertiesService.java
+++ b/subprojects/enterprise/src/main/java/org/gradle/internal/enterprise/test/impl/DefaultTestTaskPropertiesService.java
@@ -21,6 +21,7 @@
 import org.gradle.api.file.EmptyFileVisitor;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileVisitDetails;
+import org.gradle.api.internal.TaskOutputsEnterpriseInternal;
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.internal.tasks.TaskPropertyUtils;
 import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter;
@@ -123,6 +124,11 @@ public void visitOutputFileProperty(
         );
     }
 
+    @Override
+    public void doNotStoreInCache(Test task) {
+        ((TaskOutputsEnterpriseInternal) task.getOutputs()).doNotStoreInCache();
+    }
+
     private ImmutableList<CandidateClassFile> collectCandidateClassFiles(Test task) {
         ImmutableList.Builder<CandidateClassFile> builder = ImmutableList.builder();
         task.getCandidateClassFiles().visit(new EmptyFileVisitor() {
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/ExecutionEngine.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/ExecutionEngine.java
index 353927a..0bee5da 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/ExecutionEngine.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/ExecutionEngine.java
@@ -97,6 +97,13 @@ interface Execution {
         // TODO Parametrize UnitOfWork with this generated result
         @Nullable
         Object getOutput();
+
+        /**
+         * Whether the outputs of this execution should be stored in the build cache.
+         */
+        default boolean canStoreOutputsInCache() {
+            return true;
+        }
     }
 
     /**
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/UnitOfWork.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/UnitOfWork.java
index b840f0b..b17a26e 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/UnitOfWork.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/UnitOfWork.java
@@ -102,6 +102,13 @@ interface WorkOutput {
          */
         @Nullable
         Object getOutput();
+
+        /**
+         * Whether this output should be stored in the build cache.
+         */
+        default boolean canStoreInCache() {
+            return true;
+        }
     }
 
     enum WorkResult {
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/caching/CachingStateFactory.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/caching/CachingStateFactory.java
index f7bafb6..744c636 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/caching/CachingStateFactory.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/caching/CachingStateFactory.java
@@ -17,8 +17,18 @@
 package org.gradle.internal.execution.caching;
 
 import com.google.common.collect.ImmutableList;
+import org.gradle.caching.BuildCacheKey;
 import org.gradle.internal.execution.history.BeforeExecutionState;
 
+import javax.annotation.Nullable;
+
 public interface CachingStateFactory {
-    CachingState createCachingState(BeforeExecutionState beforeExecutionState, ImmutableList<CachingDisabledReason> cachingDisabledReasons);
+
+    /**
+     * Creates a CachingState for beforeExecutionState, that can be either Enabled or Disabled.
+     *
+     * A method accepts also a cacheSalt, that can be used to generate different {@link BuildCacheKey} keys for different build cache types.
+     * CacheSalt is currently set just for "Next Gen" build cache and can be removed once "Next Gen" build cache becomes the only build cache.
+     */
+    CachingState createCachingState(BeforeExecutionState beforeExecutionState, @Nullable String cacheSalt, ImmutableList<CachingDisabledReason> cachingDisabledReasons);
 }
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/caching/impl/DefaultCachingStateFactory.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/caching/impl/DefaultCachingStateFactory.java
index 2f2e0db..9a53414 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/caching/impl/DefaultCachingStateFactory.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/caching/impl/DefaultCachingStateFactory.java
@@ -17,16 +17,17 @@
 package org.gradle.internal.execution.caching.impl;
 
 import com.google.common.collect.ImmutableList;
-import org.gradle.caching.BuildCacheKey;
+import org.gradle.caching.internal.DefaultBuildCacheKey;
 import org.gradle.internal.execution.caching.CachingDisabledReason;
 import org.gradle.internal.execution.caching.CachingState;
 import org.gradle.internal.execution.caching.CachingStateFactory;
 import org.gradle.internal.execution.history.BeforeExecutionState;
-import org.gradle.internal.hash.HashCode;
 import org.gradle.internal.hash.Hasher;
 import org.gradle.internal.hash.Hashing;
 import org.slf4j.Logger;
 
+import javax.annotation.Nullable;
+
 public class DefaultCachingStateFactory implements CachingStateFactory {
     private final Logger logger;
 
@@ -35,8 +36,11 @@ public DefaultCachingStateFactory(Logger logger) {
     }
 
     @Override
-    public final CachingState createCachingState(BeforeExecutionState beforeExecutionState, ImmutableList<CachingDisabledReason> cachingDisabledReasons) {
-        Hasher cacheKeyHasher = Hashing.newHasher();
+    public final CachingState createCachingState(BeforeExecutionState beforeExecutionState, @Nullable String cacheSalt, ImmutableList<CachingDisabledReason> cachingDisabledReasons) {
+        final Hasher cacheKeyHasher = Hashing.newHasher();
+        if (cacheSalt != null) {
+            cacheKeyHasher.putString(cacheSalt);
+        }
 
         logger.warn("Appending implementation to build cache key: {}",
             beforeExecutionState.getImplementation());
@@ -79,32 +83,4 @@ public final CachingState createCachingState(BeforeExecutionState beforeExecutio
             return CachingState.disabled(cachingDisabledReasons, new DefaultBuildCacheKey(cacheKeyHasher.hash()), beforeExecutionState);
         }
     }
-
-    private static class DefaultBuildCacheKey implements BuildCacheKey {
-        private final HashCode hashCode;
-
-        public DefaultBuildCacheKey(HashCode hashCode) {
-            this.hashCode = hashCode;
-        }
-
-        @Override
-        public String getHashCode() {
-            return hashCode.toString();
-        }
-
-        @Override
-        public byte[] toByteArray() {
-            return hashCode.toByteArray();
-        }
-
-        @Override
-        public String getDisplayName() {
-            return getHashCode();
-        }
-
-        @Override
-        public String toString() {
-            return getHashCode();
-        }
-    }
 }
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/model/annotations/ServiceReferencePropertyAnnotationHandler.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/model/annotations/ServiceReferencePropertyAnnotationHandler.java
index 2eb7e77..db0f2a1 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/model/annotations/ServiceReferencePropertyAnnotationHandler.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/model/annotations/ServiceReferencePropertyAnnotationHandler.java
@@ -15,7 +15,7 @@
  */
 package org.gradle.internal.execution.model.annotations;
 
-import org.apache.commons.lang.StringUtils;
+import com.google.common.reflect.TypeToken;
 import org.gradle.api.services.BuildService;
 import org.gradle.api.services.ServiceReference;
 import org.gradle.api.tasks.Optional;
@@ -28,6 +28,7 @@
 import org.gradle.internal.reflect.validation.TypeValidationContext;
 import org.gradle.model.internal.type.ModelType;
 
+import java.lang.reflect.ParameterizedType;
 import java.util.List;
 
 import static org.gradle.internal.execution.model.annotations.ModifierAnnotationCategory.OPTIONAL;
@@ -46,8 +47,10 @@ public boolean isPropertyRelevant() {
     @Override
     public void visitPropertyValue(String propertyName, PropertyValue value, PropertyMetadata propertyMetadata, PropertyVisitor visitor) {
         propertyMetadata.getAnnotation(ServiceReference.class).ifPresent(annotation -> {
-            String serviceName = StringUtils.trimToNull(annotation.value());
-            visitor.visitServiceReference(propertyName, propertyMetadata.isAnnotationPresent(Optional.class), value, serviceName);
+            String serviceName = annotation.value();
+            TypeToken<?> declaredType = propertyMetadata.getDeclaredType();
+            Class<?> serviceType = Cast.uncheckedCast(((ParameterizedType) declaredType.getType()).getActualTypeArguments()[0]);
+            visitor.visitServiceReference(propertyName, propertyMetadata.isAnnotationPresent(Optional.class), value, serviceName, Cast.uncheckedCast(serviceType));
         });
     }
 
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/BuildCacheStep.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/BuildCacheStep.java
index 5041a99..5f49a12 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/BuildCacheStep.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/BuildCacheStep.java
@@ -138,13 +138,26 @@ private AfterExecutionResult executeAndStoreInCache(CacheableWork cacheableWork,
         }
         AfterExecutionResult result = executeWithoutCache(cacheableWork.work, context);
         result.getExecution().ifSuccessfulOrElse(
-            executionResult -> result.getAfterExecutionState()
-                .ifPresent(afterExecutionState -> store(cacheableWork, cacheKey, afterExecutionState.getOutputFilesProducedByWork(), afterExecutionState.getOriginMetadata().getExecutionTime())),
+            executionResult -> storeInCacheUnlessDisabled(cacheableWork, cacheKey, result, executionResult),
             failure -> LOGGER.debug("Not storing result of {} in cache because the execution failed", cacheableWork.getDisplayName())
         );
         return result;
     }
 
+    /**
+     * Stores the results of the given work in the build cache, unless storing was disabled for this execution or work was untracked.
+     * <p>
+     * The former is currently used only for tasks and can be triggered via {@code org.gradle.api.internal.TaskOutputsEnterpriseInternal}.
+     */
+    private void storeInCacheUnlessDisabled(CacheableWork cacheableWork, BuildCacheKey cacheKey, AfterExecutionResult result, Execution executionResult) {
+        if (executionResult.canStoreOutputsInCache()) {
+            result.getAfterExecutionState()
+                .ifPresent(afterExecutionState -> store(cacheableWork, cacheKey, afterExecutionState.getOutputFilesProducedByWork(), afterExecutionState.getOriginMetadata().getExecutionTime()));
+        } else {
+            LOGGER.debug("Not storing result of {} in cache because storing was disabled for this execution", cacheableWork.getDisplayName());
+        }
+    }
+
     private void store(CacheableWork work, BuildCacheKey cacheKey, ImmutableSortedMap<String, FileSystemSnapshot> outputFilesProducedByWork, Duration executionTime) {
         try {
             buildCache.store(cacheKey, work, outputFilesProducedByWork, executionTime);
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/ExecuteStep.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/ExecuteStep.java
index ebc7e65..bd49622 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/ExecuteStep.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/ExecuteStep.java
@@ -160,5 +160,10 @@ public ExecutionOutcome getOutcome() {
         public Object getOutput() {
             return workOutput.getOutput();
         }
+
+        @Override
+        public boolean canStoreOutputsInCache() {
+            return workOutput.canStoreInCache();
+        }
     }
 }
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/ResolveCachingStateStep.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/ResolveCachingStateStep.java
index 29eaaa0..dc1ba43 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/ResolveCachingStateStep.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/steps/ResolveCachingStateStep.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.ImmutableList;
 import org.gradle.caching.BuildCacheKey;
 import org.gradle.caching.internal.controller.BuildCacheController;
+import org.gradle.caching.internal.controller.NextGenBuildCacheController;
 import org.gradle.internal.execution.UnitOfWork;
 import org.gradle.internal.execution.caching.CachingDisabledReason;
 import org.gradle.internal.execution.caching.CachingDisabledReasonCategory;
@@ -91,8 +92,9 @@ private CachingState calculateCachingState(UnitOfWork work, BeforeExecutionState
             .orElse(null);
         work.shouldDisableCaching(detectedOverlappingOutputs)
             .ifPresent(cachingDisabledReasonsBuilder::add);
+        String cacheSalt = NextGenBuildCacheController.isNextGenCachingEnabled() ? "next-gen" : null;
 
-        return cachingStateFactory.createCachingState(beforeExecutionState, cachingDisabledReasonsBuilder.build());
+        return cachingStateFactory.createCachingState(beforeExecutionState, cacheSalt, cachingDisabledReasonsBuilder.build());
     }
 
     private CachingState calculateCachingStateWithNoCapturedInputs(UnitOfWork work) {
diff --git a/subprojects/execution/src/main/java/org/gradle/internal/execution/workspace/WorkspaceProvider.java b/subprojects/execution/src/main/java/org/gradle/internal/execution/workspace/WorkspaceProvider.java
index 4093dc3..7d2d796 100644
--- a/subprojects/execution/src/main/java/org/gradle/internal/execution/workspace/WorkspaceProvider.java
+++ b/subprojects/execution/src/main/java/org/gradle/internal/execution/workspace/WorkspaceProvider.java
@@ -23,7 +23,7 @@
 
 public interface WorkspaceProvider {
     /**
-     * Provides a workspace and execution history store for executing the transformation.
+     * Provides a workspace and execution history store for executing the transform.
      */
     <T> T withWorkspace(String path, WorkspaceAction<T> action);
 
diff --git a/subprojects/execution/src/test/groovy/org/gradle/internal/execution/steps/BuildCacheStepTest.groovy b/subprojects/execution/src/test/groovy/org/gradle/internal/execution/steps/BuildCacheStepTest.groovy
index 037d148..b5a2dd7 100644
--- a/subprojects/execution/src/test/groovy/org/gradle/internal/execution/steps/BuildCacheStepTest.groovy
+++ b/subprojects/execution/src/test/groovy/org/gradle/internal/execution/steps/BuildCacheStepTest.groovy
@@ -91,6 +91,9 @@
     }
 
     def "executes work and stores in cache on cache miss"() {
+        given:
+        def execution = Mock(Execution)
+
         when:
         def result = step.execute(work, context)
 
@@ -105,7 +108,8 @@
 
         then:
         1 * delegate.execute(work, context) >> delegateResult
-        1 * delegateResult.execution >> Try.successful(Mock(Execution))
+        1 * delegateResult.execution >> Try.successful(execution)
+        1 * execution.canStoreOutputsInCache() >> true
 
         then:
         interaction { outputStored {} }
@@ -141,6 +145,9 @@
     }
 
     def "does not store untracked result in cache"() {
+        given:
+        def execution = Mock(Execution)
+
         when:
         def result = step.execute(work, context)
 
@@ -155,8 +162,9 @@
 
         then:
         1 * delegate.execute(work, context) >> delegateResult
-        1 * delegateResult.execution >> Try.successful(Mock(Execution))
+        1 * delegateResult.execution >> Try.successful(execution)
         1 * delegateResult.afterExecutionState >> Optional.empty()
+        1 * execution.canStoreOutputsInCache() >> true
 
         then:
         0 * buildCacheController.store(_)
@@ -186,6 +194,9 @@
     }
 
     def "does not load but stores when loading is disabled"() {
+        given:
+        def execution = Mock(Execution)
+
         when:
         def result = step.execute(work, context)
 
@@ -199,7 +210,8 @@
 
         then:
         1 * delegate.execute(work, context) >> delegateResult
-        1 * delegateResult.execution >> Try.successful(Mock(Execution))
+        1 * delegateResult.execution >> Try.successful(execution)
+        1 * execution.canStoreOutputsInCache() >> true
 
         then:
         interaction { outputStored {} }
@@ -207,6 +219,8 @@
     }
 
     def "fails when cache backend throws exception while storing cached result"() {
+        given:
+        def execution = Mock(Execution)
         def failure = new RuntimeException("store failure")
 
         when:
@@ -224,7 +238,8 @@
 
         then:
         1 * delegate.execute(work, context) >> delegateResult
-        1 * delegateResult.execution >> Try.successful(Mock(Execution))
+        1 * delegateResult.execution >> Try.successful(execution)
+        1 * execution.canStoreOutputsInCache() >> true
 
         then:
         interaction { outputStored { throw failure } }
@@ -243,6 +258,24 @@
         0 * _
     }
 
+    def "executes and doesn't store when storing is disabled"() {
+        given:
+        def execution = Mock(Execution)
+
+        when:
+        def result = step.execute(work, context)
+
+        then:
+        result == delegateResult
+
+        interaction { withValidCacheKey() }
+
+        1 * delegate.execute(work, context) >> delegateResult
+        1 * delegateResult.execution >> Try.successful(execution)
+        1 * execution.canStoreOutputsInCache() >> false
+        0 * _
+    }
+
     private void withValidCacheKey() {
         _ * context.cachingState >> CachingState.enabled(cacheKey, beforeExecutionState)
     }
diff --git a/subprojects/file-collections/src/main/java/org/gradle/api/internal/file/DefaultFilePropertyFactory.java b/subprojects/file-collections/src/main/java/org/gradle/api/internal/file/DefaultFilePropertyFactory.java
index 471af9a..adeb0a0 100644
--- a/subprojects/file-collections/src/main/java/org/gradle/api/internal/file/DefaultFilePropertyFactory.java
+++ b/subprojects/file-collections/src/main/java/org/gradle/api/internal/file/DefaultFilePropertyFactory.java
@@ -41,6 +41,7 @@
 import javax.annotation.Nullable;
 import java.io.File;
 
+import static org.gradle.api.internal.lambdas.SerializableLambdas.bifunction;
 import static org.gradle.api.internal.lambdas.SerializableLambdas.transformer;
 
 @ServiceScope(Scope.Global.class)
@@ -348,7 +349,7 @@ public Provider<Directory> dir(final String path) {
 
         @Override
         public Provider<Directory> dir(final Provider<? extends CharSequence> path) {
-            return new BiProvider<>(Directory.class, this, path, (dir, relativePath) -> dir.dir(relativePath.toString()));
+            return new BiProvider<>(Directory.class, this, path, bifunction((dir, relativePath) -> dir.dir(relativePath.toString())));
         }
 
         @Override
@@ -358,7 +359,7 @@ public Provider<RegularFile> file(final String path) {
 
         @Override
         public Provider<RegularFile> file(final Provider<? extends CharSequence> path) {
-            return new BiProvider<>(RegularFile.class, this, path, (dir, relativePath) -> dir.file(relativePath.toString()));
+            return new BiProvider<>(RegularFile.class, this, path, bifunction((dir, relativePath) -> dir.file(relativePath.toString())));
         }
 
         @Override
diff --git a/subprojects/file-collections/src/test/groovy/org/gradle/api/internal/file/collections/DefaultUnauthorizedDirectoryWalkerTest.groovy b/subprojects/file-collections/src/test/groovy/org/gradle/api/internal/file/collections/DefaultUnauthorizedDirectoryWalkerTest.groovy
index 0183c10..4cc89d9 100644
--- a/subprojects/file-collections/src/test/groovy/org/gradle/api/internal/file/collections/DefaultUnauthorizedDirectoryWalkerTest.groovy
+++ b/subprojects/file-collections/src/test/groovy/org/gradle/api/internal/file/collections/DefaultUnauthorizedDirectoryWalkerTest.groovy
@@ -20,15 +20,15 @@
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.api.tasks.util.PatternSet
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Issue
 import spock.lang.Specification
 
 import java.nio.file.AccessDeniedException
 
-@Requires(TestPrecondition.FILE_PERMISSIONS)
+@Requires(UnitTestPreconditions.FilePermissions)
 @Issue('https://github.com/gradle/gradle/issues/2639')
 class DefaultUnauthorizedDirectoryWalkerTest extends Specification {
 
diff --git a/subprojects/file-collections/src/test/groovy/org/gradle/api/internal/file/collections/DirectoryWalkerTest.groovy b/subprojects/file-collections/src/test/groovy/org/gradle/api/internal/file/collections/DirectoryWalkerTest.groovy
index 855cb0b..52d1bfc 100644
--- a/subprojects/file-collections/src/test/groovy/org/gradle/api/internal/file/collections/DirectoryWalkerTest.groovy
+++ b/subprojects/file-collections/src/test/groovy/org/gradle/api/internal/file/collections/DirectoryWalkerTest.groovy
@@ -23,8 +23,8 @@
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.api.tasks.util.PatternSet
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.UsesNativeServices
 
 import java.util.concurrent.atomic.AtomicInteger
@@ -120,7 +120,7 @@
         }
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "missing symbolic link causes an exception - walker: #walkerInstance.class.simpleName"() {
         given:
         def rootDir = tmpDir.createDir("root")
diff --git a/subprojects/file-collections/src/testFixtures/groovy/org/gradle/api/internal/file/collections/AbstractDirectoryWalkerTest.groovy b/subprojects/file-collections/src/testFixtures/groovy/org/gradle/api/internal/file/collections/AbstractDirectoryWalkerTest.groovy
index 52c1d2d..dbf7272 100644
--- a/subprojects/file-collections/src/testFixtures/groovy/org/gradle/api/internal/file/collections/AbstractDirectoryWalkerTest.groovy
+++ b/subprojects/file-collections/src/testFixtures/groovy/org/gradle/api/internal/file/collections/AbstractDirectoryWalkerTest.groovy
@@ -21,10 +21,10 @@
 import org.gradle.api.tasks.util.PatternSet
 import org.gradle.internal.nativeintegration.filesystem.FileSystem
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
 import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestPrecondition
 import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Issue
@@ -86,7 +86,7 @@
         }
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "symbolic links for directories are handled properly - walker: #walkerInstance.class.simpleName"() {
         given:
         def rootDir = tmpDir.createDir("root")
@@ -111,7 +111,7 @@
         walkerInstance << walkers
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "symbolic links for files are handled properly - walker: #walkerInstance.class.simpleName"() {
         given:
         def rootDir = tmpDir.createDir("root")
@@ -137,7 +137,7 @@
     }
 
     @Issue("GRADLE-3400")
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "missing symbolic link that gets filtered doesn't cause an exception - walker: #walkerInstance.class.simpleName"() {
         given:
         def rootDir = tmpDir.createDir("root")
diff --git a/subprojects/file-watching/build.gradle.kts b/subprojects/file-watching/build.gradle.kts
index 8834f29..ebf38cf 100644
--- a/subprojects/file-watching/build.gradle.kts
+++ b/subprojects/file-watching/build.gradle.kts
@@ -9,6 +9,7 @@
 
     implementation(project(":base-annotations"))
     implementation(project(":enterprise-operations"))
+    implementation(project(":functional"))
 
     implementation(libs.guava)
     implementation(libs.nativePlatform)
diff --git a/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/NoDaemonFilesystemWatchingIntegrationTest.groovy b/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/NoDaemonFilesystemWatchingIntegrationTest.groovy
index f08f425..dd2598c 100644
--- a/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/NoDaemonFilesystemWatchingIntegrationTest.groovy
+++ b/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/NoDaemonFilesystemWatchingIntegrationTest.groovy
@@ -20,11 +20,11 @@
 import org.gradle.initialization.StartParameterBuildOptions
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.FileSystemWatchingFixture
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import spock.lang.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 @LocalOnly
-@Requires({ GradleContextualExecuter.noDaemon })
+@Requires(IntegTestPreconditions.NotDaemonExecutor)
 class NoDaemonFilesystemWatchingIntegrationTest extends AbstractIntegrationSpec implements FileSystemWatchingFixture {
 
     def "is disabled by default for --no-daemon"() {
diff --git a/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/SymlinkFileSystemWatchingIntegrationTest.groovy b/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/SymlinkFileSystemWatchingIntegrationTest.groovy
index a078dc6..0b6a576 100644
--- a/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/SymlinkFileSystemWatchingIntegrationTest.groovy
+++ b/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/SymlinkFileSystemWatchingIntegrationTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.internal.watch
 
 import com.gradle.enterprise.testing.annotations.LocalOnly
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 @LocalOnly
-@Requires(TestPrecondition.SYMLINKS)
+@Requires(UnitTestPreconditions.Symlinks)
 class SymlinkFileSystemWatchingIntegrationTest extends AbstractFileSystemWatchingIntegrationTest {
     private static final String UNABLE_TO_WATCH_MESSAGE = "Unable to watch the file system for changes."
 
diff --git a/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/WatchedDirectoriesFileSystemWatchingIntegrationTest.groovy b/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/WatchedDirectoriesFileSystemWatchingIntegrationTest.groovy
index 1d54f0f..f998bf9 100644
--- a/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/WatchedDirectoriesFileSystemWatchingIntegrationTest.groovy
+++ b/subprojects/file-watching/src/integTest/groovy/org/gradle/internal/watch/WatchedDirectoriesFileSystemWatchingIntegrationTest.groovy
@@ -26,8 +26,8 @@
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.test.fixtures.server.http.MavenHttpRepository
 import org.gradle.test.fixtures.server.http.RepositoryHttpServer
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import org.junit.Rule
 import spock.lang.Issue
@@ -382,7 +382,7 @@
         result.assertNotPostBuildOutput("Some of the file system contents retained in the virtual file system are on file systems that Gradle doesn't support watching.")
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "does not start watching on unsupported file system"() {
         def testDirectoryProviderOnUnsupportedDrive = TestNameTestDirectoryProvider.forFatDrive(getClass())
         def projectDir = testDirectoryProviderOnUnsupportedDrive.createDir("project")
diff --git a/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/AbstractFileWatcherUpdater.java b/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/AbstractFileWatcherUpdater.java
index 5a1948f..9812b0c 100644
--- a/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/AbstractFileWatcherUpdater.java
+++ b/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/AbstractFileWatcherUpdater.java
@@ -18,6 +18,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
+import org.gradle.internal.Combiners;
 import org.gradle.internal.file.FileHierarchySet;
 import org.gradle.internal.file.FileMetadata;
 import org.gradle.internal.file.FileType;
diff --git a/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/Combiners.java b/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/Combiners.java
deleted file mode 100644
index 3ec1f3a..0000000
--- a/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/Combiners.java
+++ /dev/null
@@ -1,38 +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.watch.registry.impl;
-
-import java.util.function.BiFunction;
-import java.util.function.BinaryOperator;
-
-/**
- * Utility class to help with using {@link java.util.stream.Stream#reduce(Object, BiFunction, BinaryOperator)}.
- * Promote to a more shared subproject if useful.
- */
-public abstract class Combiners {
-    private static final BinaryOperator<?> NON_COMBINING = (a, b) -> {
-        throw new IllegalStateException("Not a combinable operation");
-    };
-
-    /**
-     * We know the stream we are processing is handled sequentially, and hence there is no need for a combiner.
-     */
-    @SuppressWarnings("unchecked")
-    public static <T> BinaryOperator<T> nonCombining() {
-        return (BinaryOperator<T>) NON_COMBINING;
-    }
-}
diff --git a/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/WatchableHierarchies.java b/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/WatchableHierarchies.java
index 6c99493..529ff9c 100644
--- a/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/WatchableHierarchies.java
+++ b/subprojects/file-watching/src/main/java/org/gradle/internal/watch/registry/impl/WatchableHierarchies.java
@@ -16,6 +16,7 @@
 
 package org.gradle.internal.watch.registry.impl;
 
+import org.gradle.internal.Combiners;
 import org.gradle.internal.file.FileHierarchySet;
 import org.gradle.internal.file.FileMetadata;
 import org.gradle.internal.snapshot.FileSystemLocationSnapshot;
@@ -37,7 +38,7 @@
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 
-import static org.gradle.internal.watch.registry.impl.Combiners.nonCombining;
+import static org.gradle.internal.Combiners.nonCombining;
 
 public class WatchableHierarchies {
     private static final Logger LOGGER = LoggerFactory.getLogger(WatchableHierarchies.class);
diff --git a/subprojects/file-watching/src/main/java/org/gradle/internal/watch/vfs/impl/WatchingVirtualFileSystem.java b/subprojects/file-watching/src/main/java/org/gradle/internal/watch/vfs/impl/WatchingVirtualFileSystem.java
index c1408b9..bc0547a 100644
--- a/subprojects/file-watching/src/main/java/org/gradle/internal/watch/vfs/impl/WatchingVirtualFileSystem.java
+++ b/subprojects/file-watching/src/main/java/org/gradle/internal/watch/vfs/impl/WatchingVirtualFileSystem.java
@@ -449,7 +449,7 @@ private SnapshotHierarchy withWatcherChangeErrorHandling(SnapshotHierarchy curre
 
     private void logWatchingError(Exception exception, String fileWatchingErrorMessage, @Nullable WatchMode watchMode) {
         if (exception instanceof InotifyInstanceLimitTooLowException) {
-            warningLogger.warn("{}. The inotify instance limit is too low. See {} for more details.",
+            warningLogger.warn("{}. The inotify instance limit is too low. {}",
                 fileWatchingErrorMessage,
                 daemonDocumentationIndex.getLinkToSection("sec:inotify_instances_limit")
             );
diff --git a/subprojects/files/src/main/java/org/gradle/internal/file/BufferProvider.java b/subprojects/files/src/main/java/org/gradle/internal/file/BufferProvider.java
new file mode 100644
index 0000000..413af3c
--- /dev/null
+++ b/subprojects/files/src/main/java/org/gradle/internal/file/BufferProvider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.file;
+
+/**
+ * Providers a byte buffer to be used in processing streams.
+ */
+public interface BufferProvider {
+    byte[] getBuffer();
+}
diff --git a/subprojects/files/src/main/java/org/gradle/internal/file/ThreadLocalBufferProvider.java b/subprojects/files/src/main/java/org/gradle/internal/file/ThreadLocalBufferProvider.java
new file mode 100644
index 0000000..297cdd5
--- /dev/null
+++ b/subprojects/files/src/main/java/org/gradle/internal/file/ThreadLocalBufferProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.file;
+
+/**
+ * Provides a buffer that is bound to the current thread.
+ */
+public class ThreadLocalBufferProvider implements BufferProvider {
+    private final ThreadLocal<byte[]> copyBuffers;
+
+    public ThreadLocalBufferProvider(final int bufferSize) {
+        this.copyBuffers = new ThreadLocal<byte[]>() {
+            @Override
+            protected byte[] initialValue() {
+                return new byte[bufferSize];
+            }
+        };
+    }
+
+    @Override
+    public byte[] getBuffer() {
+        return copyBuffers.get();
+    }
+}
diff --git a/subprojects/files/src/test/groovy/org/gradle/api/internal/file/archive/impl/FileZipInputTest.groovy b/subprojects/files/src/test/groovy/org/gradle/api/internal/file/archive/impl/FileZipInputTest.groovy
index 2011da1..01a6ff3 100644
--- a/subprojects/files/src/test/groovy/org/gradle/api/internal/file/archive/impl/FileZipInputTest.groovy
+++ b/subprojects/files/src/test/groovy/org/gradle/api/internal/file/archive/impl/FileZipInputTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.api.JavaVersion
 import org.gradle.internal.file.FileException
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -45,7 +45,7 @@
         zipInput?.close()
     }
 
-    @Requires(TestPrecondition.JDK11_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk11OrLater)
     def "throws FileException when zip is badly formed"() {
         def file = temporaryFolder.file("badly-formed").createFile()
 
@@ -57,7 +57,7 @@
     }
 
     // This documents current behaviour, not desired behaviour
-    @Requires(TestPrecondition.JDK10_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk10OrEarlier)
     def "silently ignores zip that is badly formed"() {
         def file = temporaryFolder.file("badly-formed").createFile()
 
@@ -72,7 +72,7 @@
         zipInput?.close()
     }
 
-    @Requires(TestPrecondition.JDK11_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk11OrLater)
     def "can read from zip input stream a second time"() {
         def file = makeZip("foo.zip")
         def zipInput = FileZipInput.create(file)
@@ -95,7 +95,7 @@
         zipInput?.close()
     }
 
-    @Requires(TestPrecondition.JDK11_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk11OrLater)
     def "can read zip entry content a second time"() {
         def file = makeZip("foo.zip")
         def zipInput = FileZipInput.create(file)
diff --git a/subprojects/files/src/test/groovy/org/gradle/internal/file/FileHierarchySetTest.groovy b/subprojects/files/src/test/groovy/org/gradle/internal/file/FileHierarchySetTest.groovy
index a50805e..a3f0c5a 100644
--- a/subprojects/files/src/test/groovy/org/gradle/internal/file/FileHierarchySetTest.groovy
+++ b/subprojects/files/src/test/groovy/org/gradle/internal/file/FileHierarchySetTest.groovy
@@ -17,8 +17,8 @@
 package org.gradle.internal.file
 
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Issue
 import spock.lang.Specification
@@ -97,14 +97,14 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/11508")
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "creates from file system root files"() {
         expect:
         def set = from(File.listRoots())
         set.contains(tmpDir.file("any"))
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def 'can handle more root dirs'() {
         expect:
         from(pathList.collect { new File(it) }).contains(target) == result
@@ -125,7 +125,7 @@
         ['C:\\any1', 'D:\\any2'] | 'D:\\any2\\thing' | true
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def 'can handle complicated roots'() {
         expect:
         rootsOf(from([
@@ -139,7 +139,7 @@
         ]
     }
 
-    @Requires(TestPrecondition.UNIX)
+    @Requires(UnitTestPreconditions.Unix)
     def 'can handle more dirs on Unix'() {
         expect:
         from(pathList.collect { new File(it) }).contains(target) == result
diff --git a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/DefaultDeleterTest.groovy b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/DefaultDeleterTest.groovy
index e1b699d..da95443 100644
--- a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/DefaultDeleterTest.groovy
+++ b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/DefaultDeleterTest.groovy
@@ -17,8 +17,9 @@
 
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -77,7 +78,7 @@
         didWork
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "cleans symlinked target directory"() {
         def linked = tmpDir.createDir("linked")
         def content = linked.createFile("content.txt")
@@ -93,7 +94,7 @@
         content.assertDoesNotExist()
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "recreates target directory when symlink is found, leaving linked content untouched"() {
         def linked = tmpDir.createDir("linked")
         def content = linked.createFile("content.txt")
@@ -159,9 +160,8 @@
     }
 
     def "reports reasonable help message when failing to delete single #description"() {
-
         if (isSymlink) {
-            assumeTrue(TestPrecondition.SYMLINKS.isFulfilled())
+            assumeTrue(TestPrecondition.doSatisfies(UnitTestPreconditions.Symlinks))
         }
 
         given:
diff --git a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/UnixDerivativeSymlinkDeleterTest.groovy b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/UnixDerivativeSymlinkDeleterTest.groovy
index a665714..770b921 100644
--- a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/UnixDerivativeSymlinkDeleterTest.groovy
+++ b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/UnixDerivativeSymlinkDeleterTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.internal.file.impl
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import java.nio.file.Files
 
-@Requires(TestPrecondition.UNIX_DERIVATIVE)
+@Requires(UnitTestPreconditions.UnixDerivative)
 class UnixDerivativeSymlinkDeleterTest extends AbstractSymlinkDeleterTest {
     @Override
     protected void createSymbolicLink(File link, TestFile target) {
diff --git a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsHardLinkDeleterTest.groovy b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsHardLinkDeleterTest.groovy
index 3e2d90d..795ffed 100644
--- a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsHardLinkDeleterTest.groovy
+++ b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsHardLinkDeleterTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.internal.file.impl
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.util.WindowsSymbolicLinkUtil.createWindowsHardLinks
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class WindowsHardLinkDeleterTest extends AbstractSymlinkDeleterTest {
     @Override
     protected void createSymbolicLink(File link, TestFile target) {
diff --git a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsJunctionDeleterTest.groovy b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsJunctionDeleterTest.groovy
index 592ca61..013f213 100644
--- a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsJunctionDeleterTest.groovy
+++ b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsJunctionDeleterTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.internal.file.impl
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.util.WindowsSymbolicLinkUtil.createWindowsJunction
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class WindowsJunctionDeleterTest extends AbstractSymlinkDeleterTest {
     @Override
     protected void createSymbolicLink(File link, TestFile target) {
diff --git a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsSymbolicLinkDeleterTest.groovy b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsSymbolicLinkDeleterTest.groovy
index 825a9b0..70b35e0 100644
--- a/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsSymbolicLinkDeleterTest.groovy
+++ b/subprojects/files/src/test/groovy/org/gradle/internal/file/impl/WindowsSymbolicLinkDeleterTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.internal.file.impl
 
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.util.WindowsSymbolicLinkUtil.createWindowsSymbolicLink
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class WindowsSymbolicLinkDeleterTest extends AbstractSymlinkDeleterTest {
     @Override
     protected void createSymbolicLink(File link, TestFile target) {
diff --git a/subprojects/functional/src/main/java/org/gradle/internal/Combiners.java b/subprojects/functional/src/main/java/org/gradle/internal/Combiners.java
new file mode 100644
index 0000000..47da814
--- /dev/null
+++ b/subprojects/functional/src/main/java/org/gradle/internal/Combiners.java
@@ -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.internal;
+
+import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
+
+/**
+ * Utility class to help with using {@link java.util.stream.Stream#reduce(Object, BiFunction, BinaryOperator)}.
+ * Promote to a more shared subproject if useful.
+ */
+public abstract class Combiners {
+    private static final BinaryOperator<?> NON_COMBINING = (a, b) -> {
+        throw new IllegalStateException("Not a combinable operation");
+    };
+
+    /**
+     * We know the stream we are processing is handled sequentially, and hence there is no need for a combiner.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> BinaryOperator<T> nonCombining() {
+        return (BinaryOperator<T>) NON_COMBINING;
+    }
+}
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/AbstractVisualStudioProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/AbstractVisualStudioProjectIntegrationTest.groovy
index d0cb4ed..058448d 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/AbstractVisualStudioProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/AbstractVisualStudioProjectIntegrationTest.groovy
@@ -24,8 +24,8 @@
 import org.gradle.nativeplatform.OperatingSystemFamily
 import org.gradle.nativeplatform.fixtures.app.CppSourceElement
 import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 import static org.gradle.language.VariantContext.dimensions
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.WINDOWS_GCC
@@ -104,7 +104,7 @@
         solutionFile.assertReferencesProject(projectFile, contexts*.asVariantName as Set)
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "build generated visual studio solution with multiple target machines"() {
         assumeFalse(toolChain.meets(WINDOWS_GCC))
@@ -202,7 +202,7 @@
         outputContains("'main' component in project ':' is not buildable.");
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "returns meaningful errors from visual studio when component product is unbuildable due to operating system"() {
         assumeFalse(toolChain.meets(WINDOWS_GCC))
@@ -237,7 +237,7 @@
         resultProject.assertOutputContains('The project "' + visualStudioProjectName + '" is not selected for building in solution configuration "unbuildable|Win32".')
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "returns meaningful errors from visual studio when component product is unbuildable due to architecture"() {
         assumeFalse(toolChain.meets(WINDOWS_GCC))
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/NativeIdeSamplesIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/NativeIdeSamplesIntegrationTest.groovy
index b788503..9e20e00 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/NativeIdeSamplesIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/NativeIdeSamplesIntegrationTest.groovy
@@ -16,14 +16,18 @@
 package org.gradle.ide.visualstudio
 
 import org.gradle.ide.visualstudio.fixtures.AbstractVisualStudioIntegrationSpec
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class NativeIdeSamplesIntegrationTest extends AbstractVisualStudioIntegrationSpec {
     @Rule public final Sample visualStudio = sample(temporaryFolder, 'visual-studio')
 
@@ -52,7 +56,7 @@
         libProjectFile.projectXml.PropertyGroup.find({it.'@Label' == 'Custom'}).ProjectDetails[0].text() == "Project is named helloLib"
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "build generated visual studio solution"() {
         useMsbuildTool()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy
index 80ac99e..ccb4027 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioMultiProjectIntegrationTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.nativeplatform.fixtures.app.CppAppWithLibrary
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 
 class VisualStudioMultiProjectIntegrationTest extends AbstractVisualStudioIntegrationSpec {
@@ -345,7 +345,7 @@
         greetLibProject.projectConfigurations['debug'].includePath == filePath("src/main/public", "src/main/headers")
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     def "can build executable that depends on static library in another project from visual studio"() {
         useMsbuildTool()
         def app = new CppAppWithLibrary()
@@ -386,7 +386,7 @@
         installation('exe/build/install/main/debug').assertInstalled()
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     def "skip unbuildable static library project when building solution from visual studio"() {
         useMsbuildTool()
         def app = new CppAppWithLibrary()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy
index a69885e..8c7d59b 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSingleProjectIntegrationTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 
 class VisualStudioSingleProjectIntegrationTest extends AbstractVisualStudioIntegrationSpec {
@@ -255,7 +255,7 @@
         mainSolution.assertReferencesProject(dllProjectFile, projectConfigurations)
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     def "can build executable from visual studio"() {
         useMsbuildTool()
         def debugBinary = executable("build/install/main/debug/lib/app")
@@ -285,7 +285,7 @@
         installation('build/install/main/debug').assertInstalled()
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     def "can build library from visual studio"() {
         useMsbuildTool()
         def debugBinaryLib = staticLibrary("build/lib/main/debug/static/lib")
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSoftwareModelMultiProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSoftwareModelMultiProjectIntegrationTest.groovy
index a0b67a2..0ce533d 100755
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSoftwareModelMultiProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSoftwareModelMultiProjectIntegrationTest.groovy
@@ -24,8 +24,8 @@
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
 import org.gradle.plugins.ide.internal.IdePlugin
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import spock.lang.IgnoreIf
 
 class VisualStudioSoftwareModelMultiProjectIntegrationTest extends AbstractVisualStudioIntegrationSpec {
@@ -292,7 +292,7 @@
         greetLibProject.projectConfigurations['debug'].includePath == filePath("src/greetings/headers")
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "can build executable that depends on static library in another project from visual studio"() {
         useMsbuildTool()
@@ -337,7 +337,7 @@
         installation('exe/build/install/main/debug').assertInstalled()
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "can clean from visual studio with dependencies"() {
         useMsbuildTool()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSoftwareModelSingleProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSoftwareModelSingleProjectIntegrationTest.groovy
index 38f9ffd..ff22ddc 100755
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSoftwareModelSingleProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/visualstudio/VisualStudioSoftwareModelSingleProjectIntegrationTest.groovy
@@ -17,8 +17,8 @@
 
 import org.gradle.ide.visualstudio.fixtures.AbstractVisualStudioIntegrationSpec
 import org.gradle.ide.visualstudio.fixtures.MSBuildExecutor
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
@@ -26,8 +26,8 @@
 import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.MixedLanguageHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.WindowsResourceHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import spock.lang.Issue
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VISUALCPP
@@ -145,7 +145,7 @@
         mainSolution.assertReferencesProject(projectFile, projectConfigurations)
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "can build executable from visual studio"() {
         useMsbuildTool()
@@ -177,7 +177,7 @@
         installation('build/install/main/win32/debug').assertInstalled()
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "can build library from visual studio"() {
         useMsbuildTool()
@@ -210,7 +210,7 @@
         debugBinaryDll.assertExists()
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "can detect build failure from visual studio"() {
         useMsbuildTool()
@@ -239,7 +239,7 @@
         resultDebug.assertHasErrorOutput("broken.cpp(1): error C2143: syntax error: missing ';' before '!'")
     }
 
-    @Requires(TestPrecondition.MSBUILD)
+    @Requires(IntegTestPreconditions.HasMsBuild)
     @ToBeFixedForConfigurationCache
     def "can clean from visual studio"() {
         useMsbuildTool()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/AbstractXcodeCppProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/AbstractXcodeCppProjectIntegrationTest.groovy
index 296800c..ca5d5b6 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/AbstractXcodeCppProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/AbstractXcodeCppProjectIntegrationTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.ide.xcode.fixtures.XcodebuildExecutor
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.app.CppSourceElement
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 abstract class AbstractXcodeCppProjectIntegrationTest extends AbstractXcodeNativeProjectIntegrationTest {
     @Override
@@ -34,7 +34,10 @@
     @Override
     protected abstract CppSourceElement getComponentUnderTest()
 
-    @Requires([TestPrecondition.XCODE, TestPrecondition.NOT_MAC_OS_X_M1]) // TODO KM: Not sure why error message is different on M1
+    @Requires([
+        UnitTestPreconditions.HasXCode,
+        UnitTestPreconditions.NotMacOsM1
+    ]) // TODO KM: Not sure why error message is different on M1
     @ToBeFixedForConfigurationCache
     def "returns meaningful errors from xcode when component product is unbuildable due to architecture"() {
         useXcodebuildTool()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/AbstractXcodeNativeProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/AbstractXcodeNativeProjectIntegrationTest.groovy
index 0c845cb..2adaf3e 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/AbstractXcodeNativeProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/AbstractXcodeNativeProjectIntegrationTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.ide.xcode.fixtures.AbstractXcodeIntegrationSpec
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.app.SourceElement
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 abstract class AbstractXcodeNativeProjectIntegrationTest extends AbstractXcodeIntegrationSpec {
     @ToBeFixedForConfigurationCache
@@ -73,7 +73,7 @@
         }
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "returns meaningful errors from xcode when component product is unbuildable due to operating system"() {
         useXcodebuildTool()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeCppApplicationProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeCppApplicationProjectIntegrationTest.groovy
index ed1554f..4a2707e 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeCppApplicationProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeCppApplicationProjectIntegrationTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.nativeplatform.fixtures.app.CppApp
 import org.gradle.nativeplatform.fixtures.app.CppAppWithLibrary
 import org.gradle.nativeplatform.fixtures.app.CppSourceElement
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.ide.xcode.internal.XcodeUtils.toSpaceSeparatedList
 
@@ -43,7 +43,7 @@
         return new CppApp()
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "can create xcode project for unbuildable C++ application with library"() {
         useXcodebuildTool()
@@ -106,7 +106,7 @@
         resultLib.assertTasksExecuted(':greeter:compileDebugCpp', ':greeter:linkDebug', ':greeter:_xcode___Greeter_Debug')
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "can create xcode project for C++ application with unbuildable library"() {
         useXcodebuildTool()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeErrorIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeErrorIntegrationTest.groovy
index 886fc8e..7ffc5e6 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeErrorIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeErrorIntegrationTest.groovy
@@ -18,13 +18,13 @@
 
 import org.gradle.ide.xcode.fixtures.AbstractXcodeIntegrationSpec
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.util.Matchers.containsText
 
 class XcodeErrorIntegrationTest extends AbstractXcodeIntegrationSpec {
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "fails to build when project code is broken"() {
         useXcodebuildTool()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSingleCppProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSingleCppProjectIntegrationTest.groovy
index 8759c2d..fcf2b67 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSingleCppProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSingleCppProjectIntegrationTest.groovy
@@ -23,8 +23,8 @@
 import org.gradle.nativeplatform.MachineArchitecture
 import org.gradle.nativeplatform.fixtures.app.CppApp
 import org.gradle.nativeplatform.fixtures.app.CppLib
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.ide.xcode.internal.XcodeUtils.toSpaceSeparatedList
 
@@ -215,7 +215,7 @@
         project.products.children[0].path == sharedLib("build/lib/main/debug/x86-64/app").absolutePath
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "returns meaningful errors from xcode when C++ executable product doesn't have test configured"() {
         useXcodebuildTool()
@@ -259,7 +259,7 @@
         resultRunner.error.contains("Scheme App is not currently configured for the test action.")
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "returns meaningful errors from xcode when C++ library doesn't have test configured"() {
         useXcodebuildTool()
@@ -303,7 +303,7 @@
         resultRunner.error.contains("Scheme App is not currently configured for the test action.")
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "can build C++ executable from Xcode"() {
         useXcodebuildTool()
@@ -347,7 +347,10 @@
         fixture(releaseBinary).assertHasDebugSymbolsFor(app.sourceFileNamesWithoutHeaders)
     }
 
-    @Requires([TestPrecondition.XCODE, TestPrecondition.NOT_MAC_OS_X_M1])
+    @Requires([
+        UnitTestPreconditions.HasXCode,
+        UnitTestPreconditions.NotMacOsM1
+    ])
     @ToBeFixedForConfigurationCache
     def "can build C++ executable from Xcode with multiple architecture"() {
         useXcodebuildTool()
@@ -394,7 +397,7 @@
         fixture(releaseBinary).assertHasDebugSymbolsFor(app.sourceFileNamesWithoutHeaders)
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "can build C++ library from Xcode"() {
         useXcodebuildTool()
@@ -438,7 +441,10 @@
         fixture(releaseBinary).assertHasDebugSymbolsFor(lib.sourceFileNamesWithoutHeaders)
     }
 
-    @Requires([TestPrecondition.XCODE, TestPrecondition.NOT_MAC_OS_X_M1])
+    @Requires([
+        UnitTestPreconditions.HasXCode,
+        UnitTestPreconditions.NotMacOsM1
+    ])
     @ToBeFixedForConfigurationCache
     def "can build C++ library from Xcode with multiple architecture"() {
         useXcodebuildTool()
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSingleSwiftProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSingleSwiftProjectIntegrationTest.groovy
index d112dd6..d73c9de 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSingleSwiftProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSingleSwiftProjectIntegrationTest.groovy
@@ -25,8 +25,8 @@
 import org.gradle.nativeplatform.fixtures.app.SwiftAppWithXCTest
 import org.gradle.nativeplatform.fixtures.app.SwiftLib
 import org.gradle.nativeplatform.fixtures.app.SwiftLibWithXCTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Ignore
 
 class XcodeSingleSwiftProjectIntegrationTest extends AbstractXcodeIntegrationSpec {
@@ -132,7 +132,7 @@
         rootXcodeProject.schemeFiles[0].schemeXml.LaunchAction.BuildableProductRunnable.size() == 0
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "can build Swift static library from xcode"() {
         useXcodebuildTool()
@@ -250,7 +250,7 @@
         }
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "returns meaningful errors from xcode when Swift application product doesn't have test configured"() {
         useXcodebuildTool()
@@ -294,7 +294,7 @@
         resultRunner.error.contains("Scheme App is not currently configured for the test action.")
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "returns meaningful errors from xcode when Swift library doesn't have test configured"() {
         useXcodebuildTool()
@@ -328,7 +328,7 @@
         resultRelease.error.contains("Scheme App is not currently configured for the test action.")
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/274")
     def "can configure test only when xctest plugin is applied"() {
@@ -368,7 +368,7 @@
         resultDebugWithXCTest.assertHasPostBuildOutput("** TEST SUCCEEDED **")
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/274")
     def "can run tests for Swift library from xcode"() {
@@ -399,7 +399,7 @@
         resultTestRunner.assertHasPostBuildOutput("** TEST SUCCEEDED **")
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/274")
     def "can run tests for Swift application from xcode"() {
@@ -432,7 +432,7 @@
         resultTestRunner.assertHasPostBuildOutput("** TEST SUCCEEDED **")
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/273")
     def "can build Swift application from xcode"() {
@@ -477,7 +477,7 @@
         fixture(releaseBinary).assertHasDebugSymbolsFor(app.sourceFileNames)
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/273")
     def "can build Swift application from xcode with multiple operating systems"() {
@@ -526,7 +526,7 @@
         fixture(releaseBinary).assertHasDebugSymbolsFor(app.sourceFileNames)
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     def "produces reasonable message when xcode uses outdated xcode configuration"() {
         useXcodebuildTool()
@@ -550,7 +550,7 @@
         result.assertHasDescription("Unknown Xcode target 'App', do you need to re-generate Xcode configuration?")
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/273")
     def "can clean from xcode"() {
@@ -583,7 +583,7 @@
         file("build").assertDoesNotExist()
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/273")
     def "can build Swift library from xcode"() {
@@ -628,7 +628,7 @@
         fixture(releaseBinary).assertHasDebugSymbolsFor(lib.sourceFileNames)
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/273")
     def "can build Swift library from xcode with multiple operating systems"() {
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSwiftApplicationProjectIntegrationTest.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSwiftApplicationProjectIntegrationTest.groovy
index 4e6a1fb..ac13f16 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSwiftApplicationProjectIntegrationTest.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/XcodeSwiftApplicationProjectIntegrationTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.nativeplatform.fixtures.app.SwiftApp
 import org.gradle.nativeplatform.fixtures.app.SwiftAppWithLibrary
 import org.gradle.nativeplatform.fixtures.app.SwiftSourceElement
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Ignore
 import spock.lang.Issue
 
@@ -46,7 +46,7 @@
         return new SwiftApp()
     }
 
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     @ToBeFixedForConfigurationCache
     @Ignore("https://github.com/gradle/gradle-native-private/issues/273")
     def "can create xcode project for unbuildable Swift application with library"() {
@@ -112,7 +112,7 @@
 
     @NotYetImplemented
     @Issue("https://github.com/gradle/gradle-native/issues/130")
-    @Requires(TestPrecondition.XCODE)
+    @Requires(UnitTestPreconditions.HasXCode)
     def "can create xcode project for Swift application with unbuildable library"() {
         useXcodebuildTool()
 
diff --git a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/fixtures/AbstractXcodeIntegrationSpec.groovy b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/fixtures/AbstractXcodeIntegrationSpec.groovy
index 4da075b..414930c 100644
--- a/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/fixtures/AbstractXcodeIntegrationSpec.groovy
+++ b/subprojects/ide-native/src/integTest/groovy/org/gradle/ide/xcode/fixtures/AbstractXcodeIntegrationSpec.groovy
@@ -30,12 +30,15 @@
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.junit.Assume.assumeTrue
 
-@Requires([TestPrecondition.XCODE, TestPrecondition.NOT_MAC_OS_X_M1]) // M1 Macs need modern Xcode to compile aarch64 binaries
+@Requires(value = [
+    UnitTestPreconditions.HasXCode,
+    UnitTestPreconditions.NotMacOsM1
+], reason = "M1 Macs need modern Xcode to compile aarch64 binaries")
 class AbstractXcodeIntegrationSpec extends AbstractIntegrationSpec implements HostPlatform {
     AvailableToolChains.InstalledToolChain toolChain = null
 
diff --git a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy
index d9e34bc..56f71f7 100644
--- a/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy
+++ b/subprojects/ide-native/src/main/groovy/org/gradle/ide/visualstudio/tasks/internal/VisualStudioFiltersFile.groovy
@@ -55,4 +55,4 @@
         fileLocationResolver.transform(it)
     }
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/m3/ToolingApiEclipseModelCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/m3/ToolingApiEclipseModelCrossVersionSpec.groovy
index 1d0b95b..5435635 100644
--- a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/m3/ToolingApiEclipseModelCrossVersionSpec.groovy
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/m3/ToolingApiEclipseModelCrossVersionSpec.groovy
@@ -23,6 +23,10 @@
 
 class ToolingApiEclipseModelCrossVersionSpec extends ToolingApiSpecification implements WithOldConfigurationsSupport {
 
+    def shouldCheckForDeprecationWarnings(){
+        false
+    }
+
     def "can build the eclipse model for a java project"() {
 
         projectDir.file('build.gradle').text = '''
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/m5/ToolingApiIdeaModelCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/m5/ToolingApiIdeaModelCrossVersionSpec.groovy
index b017b58..5262218 100644
--- a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/m5/ToolingApiIdeaModelCrossVersionSpec.groovy
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/m5/ToolingApiIdeaModelCrossVersionSpec.groovy
@@ -30,13 +30,17 @@
 
 class ToolingApiIdeaModelCrossVersionSpec extends ToolingApiSpecification implements WithOldConfigurationsSupport {
 
+    def shouldCheckForDeprecationWarnings(){
+        false
+    }
+
     def "builds the model even if idea plugin not applied"() {
 
-        file('build.gradle').text = '''
+        buildFile.text = '''
 apply plugin: 'java'
 description = 'this is a project'
 '''
-        file('settings.gradle').text = 'rootProject.name = \"test project\"'
+        settingsFile.text = 'rootProject.name = \"test project\"'
 
         when:
         IdeaProject project = loadToolingModel(IdeaProject)
@@ -52,7 +56,7 @@
 
     def "provides basic project information"() {
 
-        file('build.gradle').text = """
+        buildFile.text = """
 apply plugin: 'java'
 apply plugin: 'idea'
 
@@ -279,7 +283,7 @@
     }
 }
 """
-        file('settings.gradle').text = "include 'api', 'impl'"
+        settingsFile.text = "include 'api', 'impl'"
 
         when:
         BasicIdeaProject project = withConnection { connection -> connection.getModel(BasicIdeaProject.class) }
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r210/ConventionsExtensionsCrossVersionFixture.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r210/ConventionsExtensionsCrossVersionFixture.groovy
new file mode 100644
index 0000000..0e7d5bd
--- /dev/null
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r210/ConventionsExtensionsCrossVersionFixture.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 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.plugins.ide.tooling.r210
+
+import org.gradle.api.JavaVersion
+import org.gradle.util.GradleVersion
+
+/**
+ * Cross version testing fixture for accessing extensions and conventions.
+ */
+class ConventionsExtensionsCrossVersionFixture {
+
+    static String javaSourceCompatibility(GradleVersion targetVersion, JavaVersion javaVersion) {
+        return javaCompatibility(targetVersion, javaVersion, 'source')
+    }
+
+    static String javaTargetCompatibility(GradleVersion targetVersion, JavaVersion javaVersion) {
+        return javaCompatibility(targetVersion, javaVersion, 'target')
+    }
+
+    private static String javaCompatibility(GradleVersion targetVersion, JavaVersion javaVersion, String compatibility) {
+        if (targetVersion >= GradleVersion.version("5.0")) {
+            return "java.${compatibility}Compatibility = JavaVersion.${javaVersion.name()}"
+        } else {
+            return "${compatibility}Compatibility = '${javaVersion.toString()}'"
+        }
+    }
+}
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r210/ToolingApiEclipseModelCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r210/ToolingApiEclipseModelCrossVersionSpec.groovy
index 0a84513..92d78f4 100644
--- a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r210/ToolingApiEclipseModelCrossVersionSpec.groovy
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r210/ToolingApiEclipseModelCrossVersionSpec.groovy
@@ -22,6 +22,8 @@
 import org.gradle.tooling.model.UnsupportedMethodException
 import org.gradle.tooling.model.eclipse.EclipseProject
 
+import static org.gradle.plugins.ide.tooling.r210.ConventionsExtensionsCrossVersionFixture.javaSourceCompatibility
+
 @TargetGradleVersion(">=2.10")
 class ToolingApiEclipseModelCrossVersionSpec extends ToolingApiSpecification {
 
@@ -62,7 +64,7 @@
         given:
         buildFile << """
             apply plugin: 'java'
-            sourceCompatibility = 1.6
+            ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_6)}
         """
 
         when:
@@ -77,7 +79,7 @@
         buildFile << """
             project(':subproject-a') {
                 apply plugin: 'java'
-                sourceCompatibility = 1.1
+                ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_1)}
             }
             project(':subproject-b') {
                 apply plugin: 'java'
@@ -91,7 +93,7 @@
             project(':subproject-c') {
                 apply plugin: 'java'
                 apply plugin: 'eclipse'
-                sourceCompatibility = 1.6
+                ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_6)}
                 eclipse {
                     jdt {
                         sourceCompatibility = 1.3
@@ -114,5 +116,4 @@
         subprojectB.javaSourceSettings.sourceLanguageLevel == JavaVersion.VERSION_1_2
         subprojectC.javaSourceSettings.sourceLanguageLevel == JavaVersion.VERSION_1_3
     }
-
 }
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r211/ToolingApiEclipseModelCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r211/ToolingApiEclipseModelCrossVersionSpec.groovy
index 7b302cc..a612f42 100644
--- a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r211/ToolingApiEclipseModelCrossVersionSpec.groovy
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r211/ToolingApiEclipseModelCrossVersionSpec.groovy
@@ -22,6 +22,8 @@
 import org.gradle.tooling.model.UnsupportedMethodException
 import org.gradle.tooling.model.eclipse.EclipseProject
 
+import static org.gradle.plugins.ide.tooling.r210.ConventionsExtensionsCrossVersionFixture.javaTargetCompatibility
+
 @TargetGradleVersion(">=2.11")
 class ToolingApiEclipseModelCrossVersionSpec extends ToolingApiSpecification {
 
@@ -60,7 +62,7 @@
         given:
         buildFile << """
         apply plugin:'java'
-        targetCompatibility = 1.5
+        ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_5)}
 """
         when:
         EclipseProject rootProject = loadToolingModel(EclipseProject)
@@ -75,7 +77,7 @@
         apply plugin:'java'
         apply plugin:'eclipse'
 
-        targetCompatibility = 1.6
+        ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_6)}
 
         eclipse {
             jdt {
@@ -119,7 +121,7 @@
         buildFile << """
             project(':subproject-a') {
                 apply plugin: 'java'
-                targetCompatibility = 1.1
+                ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_1)}
             }
             project(':subproject-b') {
                 apply plugin: 'java'
@@ -133,7 +135,7 @@
             project(':subproject-c') {
                 apply plugin: 'java'
                 apply plugin: 'eclipse'
-                targetCompatibility = 1.6
+                ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_6)}
                 eclipse {
                     jdt {
                         targetCompatibility = 1.3
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r211/ToolingApiIdeaModelCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r211/ToolingApiIdeaModelCrossVersionSpec.groovy
index 8233aa8..1ed8fdb 100644
--- a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r211/ToolingApiIdeaModelCrossVersionSpec.groovy
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r211/ToolingApiIdeaModelCrossVersionSpec.groovy
@@ -22,17 +22,22 @@
 import org.gradle.tooling.model.UnsupportedMethodException
 import org.gradle.tooling.model.idea.IdeaProject
 
+import static org.gradle.plugins.ide.tooling.r210.ConventionsExtensionsCrossVersionFixture.javaSourceCompatibility
+import static org.gradle.plugins.ide.tooling.r210.ConventionsExtensionsCrossVersionFixture.javaTargetCompatibility
+
 @TargetGradleVersion(">=2.11")
 class ToolingApiIdeaModelCrossVersionSpec extends ToolingApiSpecification {
 
-    def setup(){
+    def setup() {
         settingsFile << "rootProject.name = 'root'"
     }
 
     @TargetGradleVersion(">=2.6 <2.11")
     def "older Gradle versions infer project source settings from default idea plugin language level"() {
         given:
-        if (projectAppliesJavaPlugin) { buildFile << "apply plugin: 'java'"}
+        if (projectAppliesJavaPlugin) {
+            buildFile << "apply plugin: 'java'"
+        }
 
         when:
         def ideaProject = loadIdeaProjectModel()
@@ -134,12 +139,12 @@
 
             project(':child2') {
                 apply plugin: 'java'
-                sourceCompatibility = '1.2'
+                ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_2)}
             }
 
             project(':child3') {
                 apply plugin: 'java'
-                sourceCompatibility = '1.5'
+                ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_5)}
             }
 
         """
@@ -173,12 +178,12 @@
 
             project(':child2') {
                 apply plugin: 'java'
-                sourceCompatibility = '1.2'
+                ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_2)}
             }
 
             project(':child3') {
                 apply plugin: 'java'
-                sourceCompatibility = '1.5'
+                ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_5)}
             }
 
         """
@@ -218,7 +223,7 @@
             allprojects {
                 apply plugin:'java'
                 apply plugin:'idea'
-                targetCompatibility = "1.5"
+                ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_5)}
             }
 
         """
@@ -243,7 +248,7 @@
             allprojects {
                 apply plugin:'java'
                 apply plugin:'idea'
-                targetCompatibility = "1.5"
+                ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_5)}
             }
 
         """
@@ -252,7 +257,7 @@
         def ideaProject = loadIdeaProjectModel()
 
         then:
-        ideaProject.javaLanguageSettings.targetBytecodeVersion== JavaVersion.VERSION_1_5
+        ideaProject.javaLanguageSettings.targetBytecodeVersion == JavaVersion.VERSION_1_5
         ideaProject.modules.find { it.name == 'root' }.javaLanguageSettings.targetBytecodeVersion == null
         ideaProject.modules.find { it.name == 'child1' }.javaLanguageSettings.targetBytecodeVersion == null
         ideaProject.modules.find { it.name == 'child2' }.javaLanguageSettings.targetBytecodeVersion == null
@@ -264,21 +269,21 @@
         settingsFile << "\ninclude 'root', 'child1', ':child2:child3', 'child4'"
         buildFile << """
             apply plugin:'java'
-            targetCompatibility = "1.5"
+            ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_5)}
 
             project(':child1') {
                 apply plugin:'java'
-                targetCompatibility = "1.5"
+                ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_5)}
             }
 
             project(':child2') {
                 apply plugin:'java'
-                targetCompatibility = '1.6'
+                ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_6)}
             }
 
             project(':child2:child3') {
                 apply plugin:'java'
-                targetCompatibility = '1.7'
+                ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_7)}
             }
             project(':child4') {
             }
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r212/ToolingApiIdeaModelCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r212/ToolingApiIdeaModelCrossVersionSpec.groovy
index 358818c..93fa499 100644
--- a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r212/ToolingApiIdeaModelCrossVersionSpec.groovy
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r212/ToolingApiIdeaModelCrossVersionSpec.groovy
@@ -21,9 +21,12 @@
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.tooling.model.idea.IdeaProject
 
+import static org.gradle.plugins.ide.tooling.r210.ConventionsExtensionsCrossVersionFixture.javaSourceCompatibility
+
 @TargetGradleVersion(">=2.12")
 class ToolingApiIdeaModelCrossVersionSpec extends ToolingApiSpecification {
-    def setup(){
+
+    def setup() {
         settingsFile << "rootProject.name = 'root'"
     }
 
@@ -44,12 +47,12 @@
 
             project(':child2') {
                 apply plugin: 'java'
-                sourceCompatibility = '1.2'
+                ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_2)}
             }
 
             project(':child3') {
                 apply plugin: 'java'
-                sourceCompatibility = '1.5'
+                ${javaSourceCompatibility(targetVersion, JavaVersion.VERSION_1_5)}
             }
 
         """
@@ -64,5 +67,4 @@
         ideaProject.modules.find { it.name == 'child2' }.javaLanguageSettings.languageLevel == JavaVersion.VERSION_1_2
         ideaProject.modules.find { it.name == 'child3' }.javaLanguageSettings.languageLevel == JavaVersion.VERSION_1_5
     }
-
 }
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r30/ToolingApiEclipseModelClasspathContainerCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r30/ToolingApiEclipseModelClasspathContainerCrossVersionSpec.groovy
index 4b42fc0..a450b2b 100644
--- a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r30/ToolingApiEclipseModelClasspathContainerCrossVersionSpec.groovy
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r30/ToolingApiEclipseModelClasspathContainerCrossVersionSpec.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.plugins.ide.tooling.r30
 
+import org.gradle.api.JavaVersion
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
@@ -24,6 +25,8 @@
 import org.gradle.tooling.model.eclipse.EclipseProject
 import spock.lang.Issue
 
+import static org.gradle.plugins.ide.tooling.r210.ConventionsExtensionsCrossVersionFixture.javaTargetCompatibility
+
 @ToolingApiVersion('>=3.0')
 @TargetGradleVersion('>=3.0')
 class ToolingApiEclipseModelClasspathContainerCrossVersionSpec extends ToolingApiSpecification {
@@ -148,7 +151,7 @@
         buildFile <<
         """apply plugin: 'java'
            apply plugin: 'eclipse'
-           targetCompatibility = 1.4
+           ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_4)}
         """
 
         when:
@@ -182,7 +185,7 @@
         buildFile <<
         """apply plugin: 'java'
            apply plugin: 'eclipse'
-           targetCompatibility = 1.4
+           ${javaTargetCompatibility(targetVersion, JavaVersion.VERSION_1_4)}
            eclipse {
                jdt {
                     javaRuntimeName = "customJavaRuntime"
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r72/CompositeBuildCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r72/CompositeBuildCrossVersionSpec.groovy
index 362f095..b109b6d 100644
--- a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r72/CompositeBuildCrossVersionSpec.groovy
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r72/CompositeBuildCrossVersionSpec.groovy
@@ -270,17 +270,20 @@
 
     def buildsWithBuildSrc() {
         buildSrc(projectDir)
+        def child = "child"
         settingsFile << """
-            includeBuild("child")
+            includeBuild("$child")
         """
 
-        def childBuild = file("child")
+
+        def childBuild = file(child)
         buildSrc(childBuild)
+        def nested = "nested"
         childBuild.file("settings.gradle") << """
-            includeBuild("nested")
+            includeBuild("$nested")
         """
 
-        def nestedBuild = childBuild.file("nested")
+        def nestedBuild = childBuild.file(nested)
         buildSrc(nestedBuild)
     }
 
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r82/CompositeBuildCrossVersionSpec.groovy b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r82/CompositeBuildCrossVersionSpec.groovy
new file mode 100644
index 0000000..5962a8a
--- /dev/null
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r82/CompositeBuildCrossVersionSpec.groovy
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 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.plugins.ide.tooling.r82
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.tooling.model.UnsupportedMethodException
+import org.gradle.tooling.model.gradle.GradleBuild
+
+@ToolingApiVersion(">=8.2")
+@TargetGradleVersion('>=8.2')
+class CompositeBuildCrossVersionSpec extends ToolingApiSpecification {
+
+    def "buildTreePath is available on the GradleBuild model"() {
+        given:
+        includedBuild("b1")
+        includedBuild("b2")
+
+        when:
+        def model = withConnection {
+            it.getModel(GradleBuild)
+        }
+
+        then:
+        def builds = model.includedBuilds
+        builds.size() == 2
+        builds.first().projects.first().buildTreePath == ":b1"
+        model.projects.first().buildTreePath == ":"
+    }
+
+    def "buildTreePath is available for tasks"() {
+        given:
+        includedBuild("b1")
+        includedBuild("b2")
+
+        when:
+        def model = withConnection {
+            it.action(new FetchTasksAction()).run();
+        }.collect { it.buildTreePath }
+
+        then:
+        model.containsAll([":b1:buildEnvironment", ":b2:buildEnvironment"])
+        model.every { !it.startsWith("::") }
+    }
+
+    @TargetGradleVersion('>=8.0 <8.2')
+    def "unsupported method for older versions"() {
+        given:
+        includedBuild("b1")
+        includedBuild("b2")
+
+        when:
+        withConnection {
+            it.action(new FetchTasksAction()).run();
+        }.collect { it.buildTreePath }
+
+        then:
+        thrown(UnsupportedMethodException)
+    }
+
+    TestFile includedBuild(String build) {
+        settingsFile << """
+            includeBuild("$build")
+        """
+
+        def buildDir = file(build)
+        def settings = buildDir.file("settings.gradle")
+        settings << "rootProject.name = '$build-root-name'"
+        buildDir
+    }
+}
diff --git a/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r82/FetchTasksAction.java b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r82/FetchTasksAction.java
new file mode 100644
index 0000000..ae12c1a
--- /dev/null
+++ b/subprojects/ide/src/crossVersionTest/groovy/org/gradle/plugins/ide/tooling/r82/FetchTasksAction.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 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.plugins.ide.tooling.r82;
+
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.BuildController;
+import org.gradle.tooling.model.GradleProject;
+import org.gradle.tooling.model.Task;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+public class FetchTasksAction implements BuildAction<List<Task>> {
+    @Override
+    public List<Task> execute(BuildController controller) {
+        return controller.getBuildModel().getEditableBuilds().stream()
+            .flatMap(build -> build.getProjects().stream())
+            .flatMap(project -> controller.getModel(project, GradleProject.class).getTasks().stream())
+            .collect(toList());
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy
index 7ff764d..b198c0a 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractIdeIntegrationTest.groovy
@@ -42,7 +42,7 @@
         return executer.withTasks(taskName).run()
     }
 
-    protected File getFile(Map options, String filename) {
+    protected TestFile getFile(Map options, String filename) {
         def file = options?.project ? file(options.project, filename) : file(filename)
         if (options?.print) { println file.text }
         file
@@ -53,7 +53,7 @@
         new XmlSlurper().parse(file)
     }
 
-    protected void createJavaSourceDirs(TestFile buildFile) {
+    protected static void createJavaSourceDirs(TestFile buildFile) {
         buildFile.parentFile.file("src/main/java").createDir()
         buildFile.parentFile.file("src/main/resources").createDir()
     }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractSourcesAndJavadocJarsIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractSourcesAndJavadocJarsIntegrationTest.groovy
index cd86c8a..b51c5ad 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractSourcesAndJavadocJarsIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/AbstractSourcesAndJavadocJarsIntegrationTest.groovy
@@ -16,17 +16,16 @@
 package org.gradle.plugins.ide
 
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.server.http.HttpServer
 import org.gradle.test.fixtures.server.http.IvyHttpModule
 import org.gradle.test.fixtures.server.http.IvyHttpRepository
 import org.gradle.test.fixtures.server.http.MavenHttpRepository
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testing.fixture.GroovyCoverage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
-import spock.lang.IgnoreIf
 
 import static org.gradle.util.internal.GroovyDependencyUtil.groovyGroupName
 
@@ -308,7 +307,7 @@
     }
 
     @ToBeFixedForConfigurationCache
-    @IgnoreIf({ GradleContextualExecuter.noDaemon || GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsDaemonExecutor)
     def "does not download gradleApi() sources when sources download is disabled"() {
         given:
         executer.withEnvironmentVars('GRADLE_REPO_OVERRIDE': "$server.uri/")
@@ -334,7 +333,7 @@
     }
 
     @ToBeFixedForConfigurationCache
-    @IgnoreIf({ GradleContextualExecuter.noDaemon || GradleContextualExecuter.embedded })
+    @Requires(IntegTestPreconditions.IsDaemonExecutor)
     def "does not download gradleApi() sources when offline"() {
         given:
         executer.withEnvironmentVars('GRADLE_REPO_OVERRIDE': "$server.uri/")
@@ -358,7 +357,7 @@
     }
 
     @ToBeFixedForConfigurationCache
-    @Requires(TestPrecondition.STABLE_GROOVY) // localGroovy() version cannot be swapped-out when a snapshot Groovy build is used
+    @Requires(UnitTestPreconditions.StableGroovy) // localGroovy() version cannot be swapped-out when a snapshot Groovy build is used
     def "sources for localGroovy() are downloaded and attached"() {
         given:
         def repo = givenGroovyExistsInGradleRepo()
@@ -394,7 +393,7 @@
     }
 
     @ToBeFixedForConfigurationCache
-    @Requires(TestPrecondition.STABLE_GROOVY) // localGroovy() version cannot be swapped-out when a snapshot Groovy build is used
+    @Requires(UnitTestPreconditions.StableGroovy) // localGroovy() version cannot be swapped-out when a snapshot Groovy build is used
     def "sources for localGroovy() are downloaded and attached when using gradleApi()"() {
         given:
         def repo = givenGroovyExistsInGradleRepo()
@@ -418,8 +417,10 @@
     }
 
     @ToBeFixedForConfigurationCache
-    @IgnoreIf({ GradleContextualExecuter.embedded })
-    @Requires(TestPrecondition.STABLE_GROOVY) // localGroovy() version cannot be swapped-out when a snapshot Groovy build is used
+    @Requires(
+        value = [UnitTestPreconditions.StableGroovy, IntegTestPreconditions.NotEmbeddedExecutor],
+        reason = "localGroovy() version cannot be swapped-out when a snapshot Groovy build is used"
+    )
     def "sources for localGroovy() are downloaded and attached when using gradleTestKit()"() {
         given:
         def repo = givenGroovyExistsInGradleRepo()
@@ -491,7 +492,7 @@
     }
 
     @ToBeFixedForConfigurationCache
-    @Requires(TestPrecondition.STABLE_GROOVY) // localGroovy() version cannot be swapped-out when a snapshot Groovy build is used
+    @Requires(UnitTestPreconditions.StableGroovy) // localGroovy() version cannot be swapped-out when a snapshot Groovy build is used
     def "does not add project repository to download localGroovy() sources"() {
         given:
         def repo = givenGroovyExistsInGradleRepo()
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathFixture.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathFixture.groovy
index 3226f4f..3f6beec 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathFixture.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseClasspathFixture.groovy
@@ -114,6 +114,10 @@
         String getName() {
             return entry.@path.substring(1)
         }
+
+        void assertModularDependency() {
+            assert entry.attributes.find { it.attribute[0].@name == 'module' && it.attribute[0].@value == 'true' }
+        }
     }
 
     class EclipseSourceDir extends EclipseClasspathEntry {
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest.groovy
index 39357e6..2a788c1 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest.groovy
@@ -389,8 +389,10 @@
         runEclipseTask '''
 apply plugin: 'java'
 apply plugin: 'eclipse'
+java {
     sourceCompatibility = 1.4
     targetCompatibility = 1.3
+}
 '''
         def jdt = parseJdtFile()
         assert jdt.contains('source=1.4')
@@ -403,8 +405,10 @@
         runEclipseTask '''
 apply plugin: 'java'
 apply plugin: 'eclipse'
-sourceCompatibility = 1.4
-targetCompatibility = 1.5
+java {
+    sourceCompatibility = 1.4
+    targetCompatibility = 1.5
+}
 eclipse {
     jdt {
         sourceCompatibility = 1.3
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaModulesIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaModulesIntegrationTest.groovy
index 8aa9c71..9d99d29 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaModulesIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaModulesIntegrationTest.groovy
@@ -17,14 +17,14 @@
 
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.test.fixtures.maven.MavenModule
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.test.fixtures.jpms.ModuleJarFixture.autoModuleJar
 import static org.gradle.test.fixtures.jpms.ModuleJarFixture.moduleJar
 import static org.gradle.test.fixtures.jpms.ModuleJarFixture.traditionalJar
 
-@Requires(TestPrecondition.JDK9_OR_LATER)
+@Requires(UnitTestPreconditions.Jdk9OrLater)
 class EclipseJavaModulesIntegrationTest extends AbstractEclipseIntegrationSpec {
 
     def setup() {
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaProjectIntegrationTest.groovy
index e3422f16..ea9ea37 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaProjectIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaProjectIntegrationTest.groovy
@@ -25,7 +25,7 @@
         buildFile << """
             apply plugin: 'java'
             apply plugin: 'eclipse'
-            targetCompatibility = $version
+            java.targetCompatibility = $version
         """
 
         when:
@@ -55,7 +55,7 @@
         buildFile << """
             apply plugin: 'java'
             apply plugin: 'eclipse'
-            sourceCompatibility = $version
+            java.sourceCompatibility = $version
         """
 
         when:
@@ -86,7 +86,7 @@
         buildFile << """
             apply plugin: 'java'
             apply plugin: 'eclipse'
-            targetCompatibility = $version
+            java.targetCompatibility = $version
         """
 
         when:
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaProjectModulesIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaProjectModulesIntegrationTest.groovy
new file mode 100644
index 0000000..da2ed29
--- /dev/null
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseJavaProjectModulesIntegrationTest.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 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.plugins.ide.eclipse
+
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+
+@Requires(UnitTestPreconditions.Jdk9OrLater)
+class EclipseJavaProjectModulesIntegrationTest extends AbstractEclipseIntegrationSpec {
+
+    def "depend on modular project"() {
+        setup:
+        /*
+        This is the multi-module project structure the integration test works with:
+        -root
+          -api
+          -util
+        */
+        file("/api/src/main/java/module-info.java") << """
+            module api {
+                exports api
+            }
+        """
+
+        file("/util/src/main/java/module-info.java") << """
+            module util {
+                requires api
+            }
+        """
+
+        settingsFile << """
+            rootProject.name = 'root'
+            include 'api'
+            include 'util'
+
+        """
+
+        buildFile << """
+            allprojects {
+                apply plugin: 'java'
+                apply plugin: 'eclipse'
+            }
+
+            project(':util') {
+                dependencies {
+                    implementation project(':api')
+                }
+            }
+
+        """
+
+        when:
+        succeeds "eclipse"
+
+        then:
+        def projects = classpath("util").projects
+        assert projects.size() == 1
+        projects.get(0).assertModularDependency()
+    }
+}
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarProjectIntegrationTest.groovy
index 8666b86..6f09974 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarProjectIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpEarProjectIntegrationTest.groovy
@@ -93,7 +93,10 @@
                deploy 'org.example:lib2-impl:2.0'
            }
 
-           libDirName = 'APP-INF/lib'
+           tasks.withType(Ear) {
+              ear.libDirName = 'APP-INF/lib'
+           }
+
         """
 
         when:
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy
index f832af8..14c752f 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpIntegrationTest.groovy
@@ -37,10 +37,10 @@
         libsDir.mkdir()
         libsDir.createFile("foo.jar")
 
-        // when
+        // when:
         runEclipseTask(buildFile)
 
-        // then
+        // then:
         libEntriesInClasspathFileHaveFilenames("foo.jar")
     }
 
@@ -67,13 +67,13 @@
 
     @Test
     @ToBeFixedForConfigurationCache
-    void respectsDependencySubstitutionRules() {
-        //given
+    void "respects dependency substitution rules"() {
+        // given:
         mavenRepo.module("gradle", "foo").publish()
         mavenRepo.module("gradle", "bar").publish()
         mavenRepo.module("gradle", "baz").publish()
 
-        //when
+        // when:
         runEclipseTask "include 'sub'",
         """apply plugin: 'java'
            apply plugin: 'war'
@@ -100,7 +100,7 @@
            }
         """
 
-        //then
+        // then:
         def classpath = getClasspath()
 
         classpath.assertHasLibs('bar-1.0.jar', 'baz-1.0.jar')
@@ -108,13 +108,92 @@
         classpath.lib('baz-1.0.jar').assertIsDeployedTo('/WEB-INF/lib')
     }
 
+    @Test
+    @ToBeFixedForConfigurationCache
+    void "included build"() {
+        // given:
+        createRootProject()
+        createIncludedBuild()
+
+        // when:
+        def result = executer.withTasks("eclipse").run()
+
+        // then:
+        result.error == ""
+    }
+
+    private createIncludedBuild() {
+        file("includedBuild/build.gradle.kts") << """
+            plugins {
+                id("java")
+            }
+            group = "org.example"
+            version = "1.0-SNAPSHOT"
+
+            repositories {
+                mavenCentral()
+            }
+
+            dependencies {
+                testImplementation(platform("org.junit:junit-bom:5.9.1"))
+                testImplementation("org.junit.jupiter:junit-jupiter")
+            }
+
+            tasks.test {
+                useJUnitPlatform()
+            }
+        """
+        file("includedBuild/settings.gradle.kts") << """
+            rootProject.name = "GradleEclipseWTPWithIncludedBuild_Included"
+        """
+    }
+
+    private createRootProject() {
+        file("build.gradle.kts") << """
+            plugins {
+                id("java")
+                id("eclipse")
+                id("ear")
+            }
+
+            group = "org.example"
+            version = "1.0-SNAPSHOT"
+
+            repositories {
+                mavenCentral()
+            }
+
+            dependencies {
+                implementation("org.example:GradleEclipseWTPWithIncludedBuild_Included:1.0-SNAPSHOT")
+                earlib("org.example:GradleEclipseWTPWithIncludedBuild_Included:1.0-SNAPSHOT")
+            }
+
+            tasks.test {
+                useJUnitPlatform()
+            }
+        """
+        file("settings.gradle.kts") << """
+            rootProject.name = "GradleEclipseWTPWithIncludedBuild"
+
+            includeBuild("includedBuild")
+        """
+        file("src/main/java/org/example/Main.java") << """
+            package org.example;
+
+            public class Main {
+                public static void main(String[] args) {
+                    System.out.println("Hello World!");
+                }
+            }
+        """
+    }
+
     private generateEclipseFilesForWebProject(myArtifactVersion = "1.0") {
         def repoDir = file("repo")
         maven(repoDir).module("mygroup", "myartifact", myArtifactVersion).dependsOnModules("myartifactdep").publish()
         maven(repoDir).module("mygroup", "myartifactdep").publish()
 
-        def settingsFile = file("settings.gradle")
-        settingsFile << """
+        file("settings.gradle") << """
 include("web")
 include("java1")
 include("java2")
@@ -185,7 +264,7 @@
         executer.withTasks("eclipse").run()
     }
 
-	private Set getHandleFilenames(projectModules) {
+	private static Set getHandleFilenames(projectModules) {
 		projectModules."wb-module"."dependent-module".@handle*.text().collect { it.substring(it.lastIndexOf("/") + 1) } as Set
 	}
 }
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaProjectIntegrationTest.groovy
index d3182e9..e5e62fa 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaProjectIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpJavaProjectIntegrationTest.groovy
@@ -32,7 +32,7 @@
 
            ${mavenCentralRepository()}
 
-           sourceCompatibility = 1.6
+           java.sourceCompatibility = 1.6
 
            dependencies {
                implementation 'com.google.guava:guava:18.0'
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy
index 21bb097..c70ecf3 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpModelIntegrationTest.groovy
@@ -399,7 +399,7 @@
 
           sourceSets.main.java.srcDirs 'yyySource', 'xxxSource'
 
-          appDirName = 'nonexistentAppDir'
+          ear.appDirectory = file 'nonexistentAppDir'
 
           eclipse.wtp.component {
             resource sourcePath: 'xxxResource', deployPath: 'deploy-xxx'
@@ -430,7 +430,7 @@
           apply plugin: 'ear'
           apply plugin: 'eclipse-wtp'
 
-          appDirName = 'coolAppDir'
+          ear.appDirectory = file 'coolAppDir'
 """
         //then
         def component = getComponentFile().text
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebAndJavaProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebAndJavaProjectIntegrationTest.groovy
index b003a3a..23b44f4 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebAndJavaProjectIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebAndJavaProjectIntegrationTest.groovy
@@ -36,7 +36,7 @@
            project(':web') {
                apply plugin: 'war'
 
-               sourceCompatibility = 1.6
+               java.sourceCompatibility = 1.6
 
                dependencies {
                    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
@@ -48,7 +48,7 @@
             project(':java') {
                 apply plugin: 'java'
 
-                sourceCompatibility = 1.6
+                java.sourceCompatibility = 1.6
 
                 dependencies {
                     implementation 'com.google.guava:guava:18.0'
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebProjectIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebProjectIntegrationTest.groovy
index 9d003af..88f4d24 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebProjectIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/eclipse/EclipseWtpWebProjectIntegrationTest.groovy
@@ -32,7 +32,7 @@
         """apply plugin: 'war'
            apply plugin: 'eclipse-wtp'
 
-           sourceCompatibility = 1.6
+           java.sourceCompatibility = 1.6
 
            ${mavenCentralRepository()}
 
diff --git a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaJavaLanguageSettingsIntegrationTest.groovy b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaJavaLanguageSettingsIntegrationTest.groovy
index 71109e1..cfa01f3 100644
--- a/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaJavaLanguageSettingsIntegrationTest.groovy
+++ b/subprojects/ide/src/integTest/groovy/org/gradle/plugins/ide/idea/IdeaJavaLanguageSettingsIntegrationTest.groovy
@@ -42,7 +42,7 @@
     apply plugin:'idea'
     apply plugin:'java'
 
-    sourceCompatibility = "1.7"
+    java.sourceCompatibility = "1.7"
 }
 """
         when:
@@ -64,19 +64,19 @@
     apply plugin:'idea'
     apply plugin:'java'
 
-    sourceCompatibility = 1.6
+    java.sourceCompatibility = 1.6
 }
 
 project(':child1') {
-    sourceCompatibility = 1.7
+    java.sourceCompatibility = 1.7
 }
 
 project(':child2') {
-    sourceCompatibility = 1.5
+    java.sourceCompatibility = 1.5
 }
 
 project(':child3') {
-    sourceCompatibility = 1.8
+    java.sourceCompatibility = 1.8
 }
 """
         when:
@@ -98,8 +98,8 @@
     apply plugin:'idea'
     apply plugin:'java'
 
-    sourceCompatibility = 1.4
-    targetCompatibility = 1.4
+    java.sourceCompatibility = 1.4
+    java.targetCompatibility = 1.4
 }
 
 idea {
@@ -110,18 +110,18 @@
 }
 
 project(':child1') {
-    sourceCompatibility = 1.6
-    targetCompatibility = 1.6
+    java.sourceCompatibility = 1.6
+    java.targetCompatibility = 1.6
 }
 
 project(':child2') {
-    sourceCompatibility = 1.5
-    targetCompatibility = 1.5
+    java.sourceCompatibility = 1.5
+    java.targetCompatibility = 1.5
 }
 
 project(':child3') {
-    sourceCompatibility = 1.7
-    targetCompatibility = 1.8
+    java.sourceCompatibility = 1.7
+    java.targetCompatibility = 1.8
 }
 """
         when:
@@ -158,8 +158,8 @@
             apply plugin:'idea'
             apply plugin:'java'
 
-            sourceCompatibility = 1.4
-            targetCompatibility = 1.4
+            java.sourceCompatibility = 1.4
+            java.targetCompatibility = 1.4
         }
         """
         and:
@@ -177,7 +177,7 @@
 }
 subprojects {
     apply plugin:'java'
-    sourceCompatibility = 1.7
+    java.sourceCompatibility = 1.7
 }
 """
 
@@ -197,7 +197,7 @@
 subprojects {
     apply plugin:'java'
     apply plugin: 'idea'
-    sourceCompatibility = 1.7
+    java.sourceCompatibility = 1.7
 }
 """
 
@@ -225,7 +225,7 @@
 allprojects {
     apply plugin: 'java'
     apply plugin: 'idea'
-    targetCompatibility = '1.6'
+    java.targetCompatibility = '1.6'
 }
 
 idea {
@@ -258,7 +258,7 @@
 allprojects {
     apply plugin: 'java'
     apply plugin: 'idea'
-    targetCompatibility = '1.7'
+    java.targetCompatibility = '1.7'
 }
 
 idea {
@@ -291,11 +291,11 @@
 }
 
 project(':') {
-    targetCompatibility = 1.8
+    java.targetCompatibility = 1.8
 }
 
 project(':subprojectA') {
-    targetCompatibility = 1.7
+    java.targetCompatibility = 1.7
 }
 """
 
@@ -322,11 +322,11 @@
 }
 
 project(':') {
-    sourceCompatibility = 1.8
+    java.sourceCompatibility = 1.8
 }
 
 project(':child1') {
-    sourceCompatibility = 1.7
+    java.sourceCompatibility = 1.7
 }
 """
 
@@ -348,7 +348,7 @@
         buildFile << """
 allprojects {
     apply plugin: 'java'
-    sourceCompatibility = 1.7
+    java.sourceCompatibility = 1.7
 }
 
 project(':child1') {
@@ -378,19 +378,19 @@
 configure(project(':subprojectA')) {
     apply plugin: 'java'
     apply plugin: 'idea'
-    targetCompatibility = '1.6'
+    java.targetCompatibility = '1.6'
 }
 
 configure(project(':subprojectB')) {
     apply plugin: 'java'
     apply plugin: 'idea'
-    targetCompatibility = '1.7'
+    java.targetCompatibility = '1.7'
 }
 
 configure(project(':subprojectC')) {
     apply plugin: 'java'
     apply plugin: 'idea'
-    targetCompatibility = '1.8'
+    java.targetCompatibility = '1.8'
 }
 
 configure(project(':subprojectD')) {
@@ -422,6 +422,8 @@
         file('gradle.properties') << """
 sourceCompatibility=1.3
 targetCompatibility=1.3
+java.sourceCompatibility=1.3
+java.targetCompatibility=1.3
 """
 
         buildFile << """
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/build.gradle
index abfa5fb..ecdece0 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/build.gradle
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/build.gradle
@@ -20,7 +20,7 @@
     group = 'org.gradle'
 
     plugins.withType(JavaBasePlugin) {
-        sourceCompatibility = 1.5
+        java.sourceCompatibility = 1.5
     }
 }
 
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/build.gradle
index 51ca1d7..4006902 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/build.gradle
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/common/build.gradle
@@ -1,6 +1,6 @@
 apply plugin: 'java'
 
-sourceCompatibility = 1.6
+java.sourceCompatibility = 1.6
 
 configurations {
     provided
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/build.gradle
index 41a633f..25de0c0 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/build.gradle
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/eclipse/EclipseIntegrationTest/canCreateAndDeleteMetaData/webAppJava6/build.gradle
@@ -1,6 +1,6 @@
 apply plugin: 'war'
 
-sourceCompatibility = 6
+java.sourceCompatibility = 6
 
 dependencies {
     implementation project(":common")
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/build.gradle
index 5a91209..1f3df21 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/build.gradle
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/build.gradle
@@ -1,3 +1,3 @@
 allprojects {
     apply plugin: "idea"
-}
\ No newline at end of file
+}
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
index af548d5..992d237 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/canCreateAndDeleteMetaData/build.gradle
@@ -26,7 +26,7 @@
 
     group = 'org.gradle'
     version = '1.0'
-    sourceCompatibility = '1.6'
+    java.sourceCompatibility = '1.6'
 }
 
 cleanIdea.doLast {
diff --git a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle
index a7280b6..439d281 100644
--- a/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle
+++ b/subprojects/ide/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/worksWithNonStandardLayout/root/build.gradle
@@ -2,7 +2,7 @@
     apply plugin: 'java'
     apply plugin: 'idea'
 
-    sourceCompatibility = 1.5
+    java.sourceCompatibility = 1.5
 }
 
-idea.project.jdkName = '1.7'
\ No newline at end of file
+idea.project.jdkName = '1.7'
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/EclipseWtpPlugin.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/EclipseWtpPlugin.java
index db6f7a1..1cd5f87 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/EclipseWtpPlugin.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/EclipseWtpPlugin.java
@@ -159,7 +159,7 @@ public void execute(JavaPlugin javaPlugin) {
 
                 Set<Configuration> libConfigurations = component.getLibConfigurations();
 
-                libConfigurations.add(JavaPluginHelper.getJavaComponent(project).getRuntimeClasspathConfiguration());
+                libConfigurations.add(JavaPluginHelper.getJavaComponent(project).getMainFeature().getRuntimeClasspathConfiguration());
                 component.setClassesDeployPath("/");
                 ((IConventionAware) component).getConventionMapping().map("libDeployPath", new Callable<String>() {
                     @Override
@@ -182,7 +182,7 @@ public void execute(WarPlugin warPlugin) {
                 Set<Configuration> libConfigurations = component.getLibConfigurations();
                 Set<Configuration> minusConfigurations = component.getMinusConfigurations();
 
-                libConfigurations.add(JavaPluginHelper.getJavaComponent(project).getRuntimeClasspathConfiguration());
+                libConfigurations.add(JavaPluginHelper.getJavaComponent(project).getMainFeature().getRuntimeClasspathConfiguration());
                 minusConfigurations.add(project.getConfigurations().getByName("providedRuntime"));
                 component.setClassesDeployPath("/WEB-INF/classes");
                 ConventionMapping convention = ((IConventionAware) component).getConventionMapping();
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.java
index f8c3abe..b431259 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/EclipseClasspath.java
@@ -21,20 +21,15 @@
 import groovy.lang.DelegatesTo;
 import org.gradle.api.Action;
 import org.gradle.api.Incubating;
-import org.gradle.api.Task;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.internal.file.FileTreeInternal;
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.JvmConstants;
-import org.gradle.api.internal.tasks.compile.CompilationSourceDirs;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.SetProperty;
 import org.gradle.api.tasks.SourceSet;
-import org.gradle.api.tasks.compile.JavaCompile;
-import org.gradle.internal.jvm.JavaModuleDetector;
 import org.gradle.internal.xml.XmlTransformer;
 import org.gradle.plugins.ide.api.XmlFileContentMerger;
 import org.gradle.plugins.ide.eclipse.model.internal.ClasspathFactory;
+import org.gradle.plugins.ide.eclipse.model.internal.EclipseClassPathUtil;
 import org.gradle.plugins.ide.eclipse.model.internal.FileReferenceFactory;
 import org.gradle.plugins.ide.internal.IdeArtifactRegistry;
 import org.gradle.plugins.ide.internal.resolver.DefaultGradleApiSourcesResolver;
@@ -362,17 +357,7 @@ public void file(Action<? super XmlFileContentMerger> action) {
     public List<ClasspathEntry> resolveDependencies() {
         ProjectInternal projectInternal = (ProjectInternal) this.project;
         IdeArtifactRegistry ideArtifactRegistry = projectInternal.getServices().get(IdeArtifactRegistry.class);
-        boolean inferModulePath = false;
-        Task javaCompileTask = project.getTasks().findByName(JvmConstants.COMPILE_JAVA_TASK_NAME);
-        if (javaCompileTask instanceof JavaCompile) {
-            JavaCompile javaCompile = (JavaCompile) javaCompileTask;
-            inferModulePath = javaCompile.getModularity().getInferModulePath().get();
-            if (inferModulePath) {
-                List<File> sourceRoots = CompilationSourceDirs.inferSourceRoots((FileTreeInternal) javaCompile.getSource());
-                inferModulePath = JavaModuleDetector.isModuleSource(true, sourceRoots);
-            }
-        }
-        ClasspathFactory classpathFactory = new ClasspathFactory(this, ideArtifactRegistry, new DefaultGradleApiSourcesResolver(projectInternal.newDetachedResolver()), inferModulePath);
+        ClasspathFactory classpathFactory = new ClasspathFactory(this, ideArtifactRegistry, new DefaultGradleApiSourcesResolver(projectInternal.newDetachedResolver()), EclipseClassPathUtil.isInferModulePath(this.project));
         return classpathFactory.createEntries();
     }
 
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/FileReference.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/FileReference.java
index a727784..132c569 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/FileReference.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/FileReference.java
@@ -39,4 +39,4 @@ public interface FileReference {
      * Returns true if this reference is relative to a path variable.
      */
     boolean isRelativeToPathVariable();
-}
\ No newline at end of file
+}
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseClassPathUtil.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseClassPathUtil.java
new file mode 100644
index 0000000..72fb951
--- /dev/null
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseClassPathUtil.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 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.plugins.ide.eclipse.model.internal;
+
+import org.gradle.api.NonNullApi;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.internal.file.FileTreeInternal;
+import org.gradle.api.internal.tasks.JvmConstants;
+import org.gradle.api.internal.tasks.compile.CompilationSourceDirs;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.internal.jvm.JavaModuleDetector;
+
+import java.io.File;
+import java.util.List;
+
+@NonNullApi
+public class EclipseClassPathUtil {
+
+    public static boolean isInferModulePath(Project project) {
+        Task javaCompileTask = project.getTasks().findByName(JvmConstants.COMPILE_JAVA_TASK_NAME);
+        if (javaCompileTask instanceof JavaCompile) {
+            JavaCompile javaCompile = (JavaCompile) javaCompileTask;
+            if (javaCompile.getModularity().getInferModulePath().get()) {
+                List<File> sourceRoots = CompilationSourceDirs.inferSourceRoots((FileTreeInternal) javaCompile.getSource());
+                return JavaModuleDetector.isModuleSource(true, sourceRoots);
+            }
+        }
+        return false;
+    }
+}
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java
index 5643484..280ab47 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java
@@ -124,6 +124,12 @@ public void visitProjectDependency(ResolvedArtifactResult artifact, boolean test
             if (artifactId instanceof ComponentArtifactMetadata) {
                 buildDependencies = ((ComponentArtifactMetadata) artifactId).getBuildDependencies();
             }
+            if (!asJavaModule) {
+                Project artifactProject = project.findProject(componentIdentifier.getProjectPath());
+                if (artifactProject != null) {
+                    asJavaModule = EclipseClassPathUtil.isInferModulePath(artifactProject);
+                }
+            }
             projects.add(projectDependencyBuilder.build(componentIdentifier, classpath.getFileReferenceFactory().fromFile(artifact.getFile()), buildDependencies, testDependency, asJavaModule));
         }
 
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.java
index 8bce1ce..ceba1b2 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/WtpComponentFactory.java
@@ -25,7 +25,7 @@
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.project.ProjectStateRegistry;
 import org.gradle.api.plugins.JavaPlugin;
-import org.gradle.internal.component.local.model.PublishArtifactLocalArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactMetadata;
 import org.gradle.internal.jvm.JavaModuleDetector;
 import org.gradle.plugins.ide.eclipse.model.EclipseWtpComponent;
 import org.gradle.plugins.ide.eclipse.model.FileReference;
@@ -68,7 +68,7 @@ public void configure(final EclipseWtpComponent wtp, WtpComponent component) {
         component.configure(wtp.getDeployName(), wtp.getContextPath(), entries);
     }
 
-    private Set<Configuration> configOrEmptySet(Set<Configuration> configuration) {
+    private static Set<Configuration> configOrEmptySet(Set<Configuration> configuration) {
         if (configuration == null) {
             return Collections.emptySet();
         } else {
@@ -76,7 +76,7 @@ private Set<Configuration> configOrEmptySet(Set<Configuration> configuration) {
         }
     }
 
-    private List<WbResource> getEntriesFromSourceDirs(EclipseWtpComponent wtp) {
+    private static List<WbResource> getEntriesFromSourceDirs(EclipseWtpComponent wtp) {
         List<WbResource> result = Lists.newArrayList();
         if (wtp.getSourceDirs() != null) {
             for (File dir : wtp.getSourceDirs()) {
@@ -135,7 +135,7 @@ public void visitProjectDependency(ResolvedArtifactResult artifact, boolean test
             ProjectComponentIdentifier projectId = (ProjectComponentIdentifier) artifact.getId().getComponentIdentifier();
             if (!projectId.equals(currentProjectId)) {
                 String targetProjectPath = projectDependencyBuilder.determineTargetProjectName(projectId);
-                PublishArtifactLocalArtifactMetadata identifier = (PublishArtifactLocalArtifactMetadata) artifact.getId();
+                ComponentArtifactMetadata identifier = (ComponentArtifactMetadata) artifact.getId();
                 projectEntries.add(new WbDependentModule(identifier.getName().toString(), deployPath, "module:/resource/" + targetProjectPath + "/" + targetProjectPath));
             }
         }
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/idea/IdeaPlugin.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/idea/IdeaPlugin.java
index eb8596e..993a66f 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/idea/IdeaPlugin.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/idea/IdeaPlugin.java
@@ -42,12 +42,12 @@
 import org.gradle.api.plugins.WarPlugin;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
 import org.gradle.api.plugins.jvm.JvmTestSuite;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.plugins.scala.ScalaBasePlugin;
 import org.gradle.api.tasks.SourceSetContainer;
 import org.gradle.api.tasks.TaskProvider;
 import org.gradle.internal.reflect.Instantiator;
 import org.gradle.internal.xml.XmlTransformer;
-import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
 import org.gradle.plugins.ide.api.XmlFileContentMerger;
 import org.gradle.plugins.ide.idea.internal.IdeaModuleMetadata;
 import org.gradle.plugins.ide.idea.internal.IdeaScalaConfigurer;
@@ -363,18 +363,18 @@ public void execute(JvmTestSuitePlugin testSuitePlugin) {
     }
 
     private void configureIdeaModuleForJava(final Project project) {
-        JvmSoftwareComponentInternal javaComponent = JavaPluginHelper.getJavaComponent(project);
+        JvmFeatureInternal mainFeature = JavaPluginHelper.getJavaComponent(project).getMainFeature();
         JvmTestSuite defaultTestSuite = JavaPluginHelper.getDefaultTestSuite(project);
 
         project.getTasks().withType(GenerateIdeaModule.class).configureEach(ideaModule -> {
             // Dependencies
             ideaModule.dependsOn((Callable<FileCollection>) () ->
-                javaComponent.getMainOutput().getDirs().plus(defaultTestSuite.getSources().getOutput().getDirs())
+                mainFeature.getSourceSet().getOutput().getDirs().plus(defaultTestSuite.getSources().getOutput().getDirs())
             );
         });
 
         // Defaults
-        setupScopes(javaComponent, defaultTestSuite);
+        setupScopes(mainFeature, defaultTestSuite);
 
         // Convention
         ConventionMapping convention = ((IConventionAware) ideaModel.getModule()).getConventionMapping();
@@ -426,7 +426,7 @@ public IdeaLanguageLevel call() {
         });
     }
 
-    private void setupScopes(JvmSoftwareComponentInternal javaComponent, JvmTestSuite defaultTestSuite) {
+    private void setupScopes(JvmFeatureInternal mainFeature, JvmTestSuite defaultTestSuite) {
         Map<String, Map<String, Collection<Configuration>>> scopes = Maps.newLinkedHashMap();
         for (GeneratedIdeaScope scope : GeneratedIdeaScope.values()) {
             Map<String, Collection<Configuration>> plusMinus = Maps.newLinkedHashMap();
@@ -436,10 +436,10 @@ private void setupScopes(JvmSoftwareComponentInternal javaComponent, JvmTestSuit
         }
 
         Collection<Configuration> provided = scopes.get(GeneratedIdeaScope.PROVIDED.name()).get(IdeaDependenciesProvider.SCOPE_PLUS);
-        provided.add(javaComponent.getCompileClasspathConfiguration());
+        provided.add(mainFeature.getCompileClasspathConfiguration());
 
         Collection<Configuration> runtime = scopes.get(GeneratedIdeaScope.RUNTIME.name()).get(IdeaDependenciesProvider.SCOPE_PLUS);
-        runtime.add(javaComponent.getRuntimeClasspathConfiguration());
+        runtime.add(mainFeature.getRuntimeClasspathConfiguration());
 
         ConfigurationContainer configurations = project.getConfigurations();
         Collection<Configuration> test = scopes.get(GeneratedIdeaScope.TEST.name()).get(IdeaDependenciesProvider.SCOPE_PLUS);
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/GradleBuildBuilder.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/GradleBuildBuilder.java
index 9b57671..4f7b8b1 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/GradleBuildBuilder.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/GradleBuildBuilder.java
@@ -118,8 +118,11 @@ private void addProjects(BuildState target, DefaultGradleBuild model) {
 
     private BasicGradleProject convert(BuildState owner, ProjectState project, Map<ProjectState, BasicGradleProject> convertedProjects) {
         DefaultProjectIdentifier id = new DefaultProjectIdentifier(owner.getBuildRootDir(), project.getProjectPath().getPath());
-        BasicGradleProject converted = new BasicGradleProject().setName(project.getName()).setProjectIdentifier(id);
-        converted.setProjectDirectory(project.getProjectDir());
+        BasicGradleProject converted = new BasicGradleProject()
+            .setName(project.getName())
+            .setProjectIdentifier(id)
+            .setBuildTreePath(project.getIdentityPath().getPath())
+            .setProjectDirectory(project.getProjectDir());
         if (project.getBuildParent() != null) {
             converted.setParent(convertedProjects.get(project.getBuildParent()));
         }
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilder.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilder.java
index daf0c27..ae95e66 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilder.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/GradleProjectBuilder.java
@@ -16,8 +16,10 @@
 
 package org.gradle.plugins.ide.internal.tooling;
 
+import com.google.common.collect.ImmutableList;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.TaskContainerInternal;
 import org.gradle.plugins.ide.internal.tooling.model.DefaultGradleProject;
 import org.gradle.plugins.ide.internal.tooling.model.LaunchableGradleProjectTask;
@@ -25,14 +27,13 @@
 import org.gradle.tooling.internal.gradle.DefaultProjectIdentifier;
 import org.gradle.tooling.provider.model.ToolingModelBuilder;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
-import java.util.SortedSet;
-import java.util.stream.Collectors;
+import java.util.Objects;
 
+import static java.util.stream.Collectors.toList;
 import static org.gradle.api.internal.project.ProjectHierarchyUtils.getChildProjectsForInternalUse;
 import static org.gradle.plugins.ide.internal.tooling.ToolingModelBuilderSupport.buildFromTask;
+import static org.gradle.util.Path.SEPARATOR;
 
 /**
  * Builds the GradleProject that contains the project hierarchy and task information
@@ -54,18 +55,19 @@ public DefaultGradleProject buildAll(Project project) {
     }
 
     private DefaultGradleProject buildHierarchy(Project project) {
-        List<DefaultGradleProject> children = new ArrayList<DefaultGradleProject>();
-        for (Project child : getChildProjectsForInternalUse(project)) {
-            children.add(buildHierarchy(child));
-        }
+        List<DefaultGradleProject> children = getChildProjectsForInternalUse(project).stream()
+            .map(this::buildHierarchy)
+            .collect(toList());
 
+        String projectIdentityPath = ((ProjectInternal) project).getIdentityPath().getPath();
         DefaultGradleProject gradleProject = new DefaultGradleProject()
-                .setProjectIdentifier(new DefaultProjectIdentifier(project.getRootDir(), project.getPath()))
-                .setName(project.getName())
-                .setDescription(project.getDescription())
-                .setBuildDirectory(project.getBuildDir())
-                .setProjectDirectory(project.getProjectDir())
-                .setChildren(children);
+            .setProjectIdentifier(new DefaultProjectIdentifier(project.getRootDir(), project.getPath()))
+            .setName(project.getName())
+            .setDescription(project.getDescription())
+            .setBuildDirectory(project.getBuildDir())
+            .setProjectDirectory(project.getProjectDir())
+            .setBuildTreePath(projectIdentityPath)
+            .setChildren(children);
 
         gradleProject.getBuildScript().setSourceFile(project.getBuildFile());
 
@@ -93,27 +95,37 @@ private DefaultGradleProject buildHierarchy(Project project) {
 
     private static List<LaunchableGradleTask> tasks(DefaultGradleProject owner, TaskContainerInternal tasks, String projectOptions) {
         if ("omit_all_tasks".equals(projectOptions)) {
-            return Collections.emptyList();
-        } else if ("skip_task_graph_realization".equals(projectOptions)) {
-            return tasks.getNames().stream().map(t -> buildFromTaskName(new LaunchableGradleProjectTask(), owner.getProjectIdentifier(), t)).collect(Collectors.toList());
+            return ImmutableList.of();
+        }
+        if ("skip_task_graph_realization".equals(projectOptions)) {
+            return tasks.getNames().stream()
+                .map(taskName -> buildFromTaskName(new LaunchableGradleProjectTask(), owner.getProjectIdentifier(), taskName)
+                    .setBuildTreePath(owner.getBuildTreePath() + SEPARATOR + taskName))
+                .collect(toList());
         }
 
         tasks.discoverTasks();
         tasks.realize();
-        SortedSet<String> taskNames = tasks.getNames();
-        List<LaunchableGradleTask> out = new ArrayList<LaunchableGradleTask>(taskNames.size());
-        for (String taskName : taskNames) {
-            Task t = tasks.findByName(taskName);
-            if (t != null) {
-                out.add(buildFromTask(new LaunchableGradleProjectTask(), owner.getProjectIdentifier(), t).setProject(owner));
-            }
-        }
 
-        return out;
+        return tasks.getNames().stream()
+            .map(tasks::findByName)
+            .filter(Objects::nonNull)
+            .map(task -> buildFromTask(new LaunchableGradleProjectTask(), owner.getProjectIdentifier(), task)
+                .setProject(owner)
+                .setBuildTreePath(getBuildTreePath(owner, task))).collect(toList());
+    }
+
+    private static String getBuildTreePath(DefaultGradleProject owner, Task task) {
+        String ownerBuildTreePath = owner.getBuildTreePath();
+        String buildTreePath = SEPARATOR + task.getName();
+        if (SEPARATOR.equals(ownerBuildTreePath)) {
+            return buildTreePath;
+        }
+        return ownerBuildTreePath + buildTreePath;
     }
 
     public static <T extends LaunchableGradleTask> T buildFromTaskName(T target, DefaultProjectIdentifier projectIdentifier, String taskName) {
-        String taskPath = projectIdentifier.getProjectPath() + ":" + taskName;
+        String taskPath = projectIdentifier.getProjectPath() + SEPARATOR + taskName;
         target.setPath(taskPath)
             .setName(taskName)
             .setGroup("undefined")
@@ -121,6 +133,7 @@ public static <T extends LaunchableGradleTask> T buildFromTaskName(T target, Def
             .setDescription("")
             .setPublic(true)
             .setProjectIdentifier(projectIdentifier);
+
         return target;
     }
 }
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/TasksFactory.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/TasksFactory.java
index d606d0b..c76a7fc 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/TasksFactory.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/TasksFactory.java
@@ -43,4 +43,4 @@ public Set<Task> getTasks(Project project) {
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/BasicGradleProject.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/BasicGradleProject.java
index 16d7dfd3..40bd6c5 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/BasicGradleProject.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/BasicGradleProject.java
@@ -25,6 +25,7 @@
 public class BasicGradleProject extends PartialBasicGradleProject {
     private File projectDirectory;
     private Set<BasicGradleProject> children = new LinkedHashSet<BasicGradleProject>();
+    private String buildTreePath;
 
 
     public File getProjectDirectory() {
@@ -57,4 +58,13 @@ public BasicGradleProject addChild(BasicGradleProject child) {
         children.add(child);
         return this;
     }
+
+    public String getBuildTreePath() {
+        return buildTreePath;
+    }
+
+    public BasicGradleProject setBuildTreePath(String path) {
+        buildTreePath = path;
+        return this;
+    }
 }
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/DefaultGradleProject.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/DefaultGradleProject.java
index 9731f1b..cbc1466 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/DefaultGradleProject.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/DefaultGradleProject.java
@@ -29,12 +29,13 @@ public class DefaultGradleProject implements Serializable, GradleProjectIdentity
     private final DefaultGradleScript buildScript = new DefaultGradleScript();
     private File buildDirectory;
     private File projectDirectory;
-    private List<LaunchableGradleTask> tasks = new LinkedList<LaunchableGradleTask>();
+    private List<LaunchableGradleTask> tasks = new LinkedList<>();
     private String name;
     private String description;
     private DefaultProjectIdentifier projectIdentifier;
     private DefaultGradleProject parent;
-    private List<? extends DefaultGradleProject> children = new LinkedList<DefaultGradleProject>();
+    private List<? extends DefaultGradleProject> children = new LinkedList<>();
+    private String buildTreePath;
 
     public String getName() {
         return name;
@@ -145,4 +146,13 @@ public DefaultGradleProject setProjectDirectory(File projectDirectory) {
     public DefaultGradleScript getBuildScript() {
         return buildScript;
     }
+
+    public DefaultGradleProject setBuildTreePath(String buildTreePath) {
+        this.buildTreePath = buildTreePath;
+        return this;
+    }
+
+    public String getBuildTreePath() {
+        return buildTreePath;
+    }
 }
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/LaunchableGradleTask.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/LaunchableGradleTask.java
index e5c08a1..c0155b9 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/LaunchableGradleTask.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/internal/tooling/model/LaunchableGradleTask.java
@@ -34,6 +34,7 @@ public class LaunchableGradleTask implements Serializable, InternalLaunchable, T
     private String group;
     private boolean isPublic;
     private DefaultProjectIdentifier projectIdentifier;
+    private String buildTreePath;
 
     public String getPath() {
         return path;
@@ -44,6 +45,15 @@ public LaunchableGradleTask setPath(String path) {
         return this;
     }
 
+    public String getBuildTreePath() {
+        return buildTreePath;
+    }
+
+    public LaunchableGradleTask setBuildTreePath(String buildTreePath) {
+        this.buildTreePath = buildTreePath;
+        return this;
+    }
+
     public String getName() {
         return name;
     }
diff --git a/subprojects/ide/src/main/java/org/gradle/plugins/ide/package-info.java b/subprojects/ide/src/main/java/org/gradle/plugins/ide/package-info.java
index c2ee8a59..da5d93a 100644
--- a/subprojects/ide/src/main/java/org/gradle/plugins/ide/package-info.java
+++ b/subprojects/ide/src/main/java/org/gradle/plugins/ide/package-info.java
@@ -17,4 +17,4 @@
 /**
  * General purpose IDE types.
  */
-package org.gradle.plugins.ide;
\ No newline at end of file
+package org.gradle.plugins.ide;
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilderTest.groovy
index c03861e..82a0dae 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilderTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/idea/model/internal/ModuleDependencyBuilderTest.groovy
@@ -42,7 +42,7 @@
 
     def "builds dependency for nonIdea root project"() {
         when:
-        def dependency = builder.create(newProjectId("build-1",":a"), 'compile')
+        def dependency = builder.create(newProjectId(":build-1",":a"), 'compile')
 
         then:
         dependency.scope == 'compile'
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/GradleBuildBuilderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/GradleBuildBuilderTest.groovy
index 5e2bd74..36f302f 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/GradleBuildBuilderTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/GradleBuildBuilderTest.groovy
@@ -74,8 +74,8 @@
         def rootProject = Mock(ProjectState)
         rootProject.projectPath >> Path.ROOT
 
-        def includeProject1 = Mock(ProjectState)
-        includeProject1.projectPath >> Path.ROOT
+        def includedProject1 = Mock(ProjectState)
+        includedProject1.projectPath >> Path.ROOT
 
         def includedProject2 = Mock(ProjectState)
         includedProject2.projectPath >> Path.ROOT
@@ -100,14 +100,19 @@
         def includedBuild2 = Mock(IncludedBuildInternal)
 
         rootProject.childProjects >> [].toSet()
-        includeProject1.childProjects >> [].toSet()
+        rootProject.identityPath >> Path.path(":some:identity:path")
+
+        includedProject1.childProjects >> [].toSet()
+        includedProject1.identityPath >> Path.path(":some:identity:path")
         includedProject2.childProjects >> [].toSet()
+        includedProject2.identityPath >> Path.path(":some:identity:path")
 
         rootBuild.includedBuilds() >> [includedBuild1]
 
         rootBuildState.mutableModel >> rootBuild
         rootBuildState.buildRootDir >> rootDir
         rootBuildState.projects >> rootProjects
+        rootBuildState.identityPath >> Path.path(":some:identity:path")
 
         includedBuild1.target >> includedBuildState1
         includedBuild2.target >> includedBuildState2
@@ -119,10 +124,10 @@
         rootProjects.allProjects >> [rootProject].toSet()
         rootProjects.rootProject >> rootProject
 
-        projects1.allProjects >> [includeProject1].toSet()
-        projects1.rootProject >> includeProject1
+        projects1.allProjects >> [includedProject1].toSet()
+        projects1.rootProject >> includedProject1
 
-        projects2.allProjects >> [includeProject1].toSet()
+        projects2.allProjects >> [includedProject1].toSet()
         projects2.rootProject >> includedProject2
 
         build1.includedBuilds() >> [includedBuild2]
diff --git a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/EclipseModelBuilderTest.groovy b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/EclipseModelBuilderTest.groovy
index b06e20e..2b611da 100644
--- a/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/EclipseModelBuilderTest.groovy
+++ b/subprojects/ide/src/test/groovy/org/gradle/plugins/ide/internal/tooling/eclipse/EclipseModelBuilderTest.groovy
@@ -163,11 +163,11 @@
         pluginType << [JavaPlugin, GroovyPlugin, ScalaPlugin]
     }
 
-    def "custom #type language level derived Java plugin convention"() {
+    def "custom #type language level derived Java plugin extension"() {
         given:
         def modelBuilder = createEclipseModelBuilder()
         project.plugins.apply(JavaPlugin)
-        project."$compatibilityProperty" = "1.2"
+        project.java."$compatibilityProperty" = "1.2"
 
         when:
         def eclipseModel = modelBuilder.buildAll("org.gradle.tooling.model.eclipse.EclipseProject", project)
@@ -181,12 +181,12 @@
         "target" | "targetCompatibility" | "targetBytecodeVersion"
     }
 
-    def "#type language level derived from eclipse jdt overrules java plugin convention configuration"() {
+    def "#type language level derived from eclipse jdt overrules java plugin extension configuration"() {
         given:
         def modelBuilder = createEclipseModelBuilder()
         project.plugins.apply(JavaPlugin)
         project.plugins.apply(EclipsePlugin)
-        project."$compatibilityProperty" = "1.2"
+        project.java."$compatibilityProperty" = "1.2"
         project.eclipse.jdt."$compatibilityProperty" = "1.3"
 
         when:
@@ -209,7 +209,7 @@
         child1.eclipse.jdt."$compatibilityProperty" = "1.2"
         child2.plugins.apply(JavaPlugin)
         child2.plugins.apply(EclipsePlugin)
-        child2."$compatibilityProperty" = "1.3"
+        child2.java."$compatibilityProperty" = "1.3"
         child2.eclipse.jdt."$compatibilityProperty" = "1.1"
 
         when:
diff --git a/subprojects/ide/src/testFixtures/groovy/org/gradle/plugins/ide/fixtures/IdeaProjectFixture.groovy b/subprojects/ide/src/testFixtures/groovy/org/gradle/plugins/ide/fixtures/IdeaProjectFixture.groovy
index 9e2c7cf..b648df6 100644
--- a/subprojects/ide/src/testFixtures/groovy/org/gradle/plugins/ide/fixtures/IdeaProjectFixture.groovy
+++ b/subprojects/ide/src/testFixtures/groovy/org/gradle/plugins/ide/fixtures/IdeaProjectFixture.groovy
@@ -67,7 +67,7 @@
     }
 
     static class ProjectModules {
-        List<String> modules
+        List<String> modules = []
 
         private ProjectModules(List<String> modules) {
             this.modules = modules
@@ -82,7 +82,7 @@
         }
 
         void assertHasModules(String... name) {
-            List<String> modules = Arrays.asList(name)
+            List<String> modules = name.toList()
             assert this.modules.every { modules.contains(it) }
         }
     }
diff --git a/subprojects/instrumentation-agent/build.gradle.kts b/subprojects/instrumentation-agent/build.gradle.kts
index f786f36..79ef032 100644
--- a/subprojects/instrumentation-agent/build.gradle.kts
+++ b/subprojects/instrumentation-agent/build.gradle.kts
@@ -7,13 +7,6 @@
 // Agent's premain is invoked before main(), so it should not cause the startup to fail because of the too new class file format.
 gradlebuildJava.usedForStartup()
 
-dependencies {
-    integTestDistributionRuntimeOnly(project(":distributions-core"))
-
-    integTestImplementation(project(":build-option"))
-    integTestImplementation(project(":launcher"))
-}
-
 tasks.named<Jar>("jar") {
     manifest {
         attributes(mapOf(
diff --git a/subprojects/instrumentation-agent/src/integTest/groovy/org/gradle/instrumentation/agent/AgentApplicationTest.groovy b/subprojects/instrumentation-agent/src/integTest/groovy/org/gradle/instrumentation/agent/AgentApplicationTest.groovy
deleted file mode 100644
index 3c6268e..0000000
--- a/subprojects/instrumentation-agent/src/integTest/groovy/org/gradle/instrumentation/agent/AgentApplicationTest.groovy
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright 2022 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.instrumentation.agent
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.configurationcache.ConfigurationCacheFixture
-import org.gradle.integtests.fixtures.daemon.DaemonLogsAnalyzer
-import org.gradle.integtests.fixtures.daemon.DaemonsFixture
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.internal.agents.AgentControl
-import org.gradle.launcher.daemon.configuration.DaemonBuildOptions
-import spock.lang.Requires
-
-class AgentApplicationTest extends AbstractIntegrationSpec {
-    def "agent is disabled by default"() {
-        given:
-        withDumpAgentStatusTask()
-
-        when:
-        succeeds()
-
-        then:
-        agentWasNotApplied()
-    }
-
-    def "agent is not applied if disabled in the command-line"() {
-        given:
-        withoutAgent()
-        withDumpAgentStatusTask()
-
-        when:
-        succeeds()
-
-        then:
-        agentWasNotApplied()
-    }
-
-    @Requires(value = { GradleContextualExecuter.daemon }, reason = "Agent injection is not implemented for non-daemon and embedded modes")
-    def "agent is applied to the daemon process running the build"() {
-        given:
-        withAgent()
-        withDumpAgentStatusTask()
-
-        when:
-        succeeds()
-
-        then:
-        agentWasApplied()
-    }
-
-    @Requires(value = { (GradleContextualExecuter.daemon && GradleContextualExecuter.configCache) }, reason = "Agent injection is not implemented for non-daemon and embedded modes")
-    def "keeping agent status does not invalidate the configuration cache"() {
-        def configurationCache = new ConfigurationCacheFixture(this)
-        given:
-        withDumpAgentStatusAtConfiguration()
-
-        when:
-        withAgentApplied(agentStatus)
-        succeeds()
-
-        then:
-        agentStatusWas(agentStatus)
-        configurationCache.assertStateStored()
-
-        when:
-        withAgentApplied(agentStatus)
-        succeeds()
-
-        then:
-        configurationCache.assertStateLoaded()
-
-        where:
-        agentStatus << [true, false]
-    }
-
-    @Requires(value = { (GradleContextualExecuter.daemon && GradleContextualExecuter.configCache) }, reason = "Agent injection is not implemented for non-daemon and embedded modes")
-    def "changing agent status invalidates the configuration cache"() {
-        def configurationCache = new ConfigurationCacheFixture(this)
-        given:
-        withDumpAgentStatusAtConfiguration()
-
-        when:
-        withAgentApplied(useAgentOnFirstRun)
-        succeeds()
-
-        then:
-        agentStatusWas(useAgentOnFirstRun)
-        configurationCache.assertStateStored()
-
-        when:
-        withAgentApplied(useAgentOnSecondRun)
-        succeeds()
-
-        then:
-        agentStatusWas(useAgentOnSecondRun)
-        configurationCache.assertStateStored()
-
-        where:
-        useAgentOnFirstRun | useAgentOnSecondRun
-        true               | false
-        false              | true
-    }
-
-    @Requires(value = { GradleContextualExecuter.daemon }, reason = "Testing the daemons")
-    def "daemon with the same agent status is reused"() {
-        given:
-        executer.requireIsolatedDaemons()
-        withDumpAgentStatusTask()
-
-        when:
-        withAgentApplied(useAgentOnFirstRun)
-        succeeds()
-
-        then:
-        daemons.daemon.becomesIdle()
-
-        when:
-        withAgentApplied(useAgentOnSecondRun)
-        succeeds()
-
-        then:
-        def expectedDaemonCount = shouldReuseDaemon ? 1 : 2
-
-        daemons.daemons.size() == expectedDaemonCount
-
-        where:
-        useAgentOnFirstRun | useAgentOnSecondRun || shouldReuseDaemon
-        true               | true                || true
-        false              | false               || true
-        true               | false               || false
-        false              | true                || false
-    }
-
-    private void withDumpAgentStatusTask() {
-        buildFile("""
-            import ${AgentControl.name}
-
-            tasks.register('hello') {
-                doLast {
-                    println("agent applied = \${AgentControl.isInstrumentationAgentApplied()}")
-                }
-            }
-
-            defaultTasks 'hello'
-        """)
-    }
-
-    private void withDumpAgentStatusAtConfiguration() {
-        buildFile("""
-            import ${AgentControl.name}
-
-            def status = AgentControl.isInstrumentationAgentApplied()
-            tasks.register('hello') {
-                doLast {
-                    println("agent applied = \$status")
-                }
-            }
-
-            defaultTasks 'hello'
-        """)
-    }
-
-    private void agentWasApplied() {
-        agentStatusWas(true)
-    }
-
-    private void agentWasNotApplied() {
-        agentStatusWas(false)
-    }
-
-    private void agentStatusWas(boolean applied) {
-        outputContains("agent applied = $applied")
-    }
-
-    private void withAgent() {
-        withAgentApplied(true)
-    }
-
-    private void withAgentApplied(boolean shouldApply) {
-        executer.withArgument("-D${DaemonBuildOptions.ApplyInstrumentationAgentOption.GRADLE_PROPERTY}=$shouldApply")
-    }
-
-    private void withoutAgent() {
-        withAgentApplied(false)
-    }
-
-    DaemonsFixture getDaemons() {
-        new DaemonLogsAnalyzer(executer.daemonBaseDir)
-    }
-}
diff --git a/subprojects/instrumentation-declarations/build.gradle.kts b/subprojects/instrumentation-declarations/build.gradle.kts
new file mode 100644
index 0000000..eb56972
--- /dev/null
+++ b/subprojects/instrumentation-declarations/build.gradle.kts
@@ -0,0 +1,28 @@
+plugins {
+    id("gradlebuild.distribution.api-java")
+}
+
+description = "Contains declarations for instrumentation of plugins. Adds interceptors, bytecode upgrades etc."
+
+dependencies {
+    compileOnly(project(":core"))
+    compileOnly(project(":base-services"))
+    compileOnly(project(":core-api"))
+    compileOnly(project(":model-core"))
+    compileOnly(project(":reporting"))
+    compileOnly(libs.groovy)
+    compileOnly(project(":code-quality"))
+
+    // Instrumentation dependencies
+    compileOnly(project(":internal-instrumentation-api"))
+    compileOnly(libs.asm)
+    compileOnly(libs.asmUtil)
+    compileOnly(libs.asmTree)
+    annotationProcessor(project(":internal-instrumentation-processor"))
+    annotationProcessor(platform(project(":distributions-dependencies")))
+}
+
+tasks.named<JavaCompile>("compileJava") {
+    // Without this, javac will complain about unclaimed org.gradle.api.NonNullApi annotation
+    options.compilerArgs.add("-Xlint:-processing")
+}
diff --git a/subprojects/instrumentation-declarations/src/main/java/org/gradle/internal/instrumentation/property/upgrades/PropertyUpgradeInstrumentationRegistry.java b/subprojects/instrumentation-declarations/src/main/java/org/gradle/internal/instrumentation/property/upgrades/PropertyUpgradeInstrumentationRegistry.java
new file mode 100644
index 0000000..22d6fbc
--- /dev/null
+++ b/subprojects/instrumentation-declarations/src/main/java/org/gradle/internal/instrumentation/property/upgrades/PropertyUpgradeInstrumentationRegistry.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 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.instrumentation.property.upgrades;
+
+import org.gradle.internal.instrumentation.api.annotations.VisitForInstrumentation;
+
+/**
+ * Add classes to visit to this registry to enable property upgrade instrumentation.
+ *
+ * Classes that should be visited should be added to the @VisitForInstrumentation annotation, e.g.
+ * @VisitForInstrumentation(value = {Checkstyle.class}).
+ */
+@VisitForInstrumentation(value = {})
+public interface PropertyUpgradeInstrumentationRegistry {
+}
diff --git a/subprojects/instrumentation-declarations/src/main/java/org/gradle/internal/instrumentation/property/upgrades/package-info.java b/subprojects/instrumentation-declarations/src/main/java/org/gradle/internal/instrumentation/property/upgrades/package-info.java
new file mode 100644
index 0000000..573942e
--- /dev/null
+++ b/subprojects/instrumentation-declarations/src/main/java/org/gradle/internal/instrumentation/property/upgrades/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * Contains registry for classes with property upgrades.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.internal.instrumentation.property.upgrades;
+
diff --git a/subprojects/integ-test/build.gradle.kts b/subprojects/integ-test/build.gradle.kts
index 6f604c6..3096ba7 100644
--- a/subprojects/integ-test/build.gradle.kts
+++ b/subprojects/integ-test/build.gradle.kts
@@ -6,6 +6,7 @@
 
 dependencies {
     integTestImplementation(project(":base-services"))
+    integTestImplementation(project(":build-option"))
     integTestImplementation(project(":enterprise-operations"))
     integTestImplementation(project(":native"))
     integTestImplementation(project(":logging"))
diff --git a/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/AbstractPropertyUpgradesBinaryCompatibilityCrossVersionSpec.groovy b/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/AbstractPropertyUpgradesBinaryCompatibilityCrossVersionSpec.groovy
new file mode 100644
index 0000000..d812ac6
--- /dev/null
+++ b/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/AbstractPropertyUpgradesBinaryCompatibilityCrossVersionSpec.groovy
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2023 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.integtests
+
+
+import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
+
+/**
+ * Tests that task classes with property upgrades are compiled against one version of Gradle are compatible with another version.
+ */
+abstract class AbstractPropertyUpgradesBinaryCompatibilityCrossVersionSpec extends CrossVersionIntegrationSpec {
+
+    protected List<Class<?>> importClasses() {
+        return []
+    }
+
+    protected void prepareGroovyPluginTest(String pluginApplyBody) {
+        file("producer/build.gradle") << """
+            apply plugin: 'groovy'
+            dependencies {
+                implementation gradleApi()
+            }
+        """
+
+        file("producer/src/main/groovy/SomePlugin.groovy") << """
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+            ${importClasses().collect { "import " + it.name }.join("\n")}
+
+            class SomePlugin implements Plugin<Project> {
+                void apply(Project project) {
+                    $pluginApplyBody
+                }
+            }
+            """
+
+        buildFile << """
+            buildscript {
+                dependencies { classpath fileTree(dir: "producer/build/libs", include: '*.jar') }
+            }
+
+            apply plugin: SomePlugin
+        """
+    }
+
+    protected void prepareJavaPluginTest(String pluginApplyBody) {
+        file("producer/build.gradle") << """
+            apply plugin: 'groovy'
+            dependencies {
+                implementation gradleApi()
+            }
+        """
+
+        file("producer/src/main/java/SomePlugin.java") << """
+            import org.gradle.api.Plugin;
+            import org.gradle.api.Project;
+            ${importClasses().collect { "import " + it.name + ";" }.join("\\n")}
+
+            class SomePlugin implements Plugin<Project> {
+                public void apply(Project project) {
+                    $pluginApplyBody
+                }
+            }
+            """
+
+        buildFile << """
+            buildscript {
+                dependencies { classpath fileTree(dir: "producer/build/libs", include: '*.jar') }
+            }
+
+            apply plugin: SomePlugin
+        """
+    }
+
+    protected void prepareKotlinPluginTest(String pluginApplyBody) {
+        file("producer/build.gradle.kts") << """
+            plugins {
+                `kotlin-dsl`
+            }
+            repositories {
+                mavenCentral()
+            }
+        """
+
+        file("producer/src/main/kotlin/SomePlugin.kt") << """
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+            ${importClasses().collect { "import " + it.name }.join("\\n")}
+
+            class SomePlugin: Plugin<Project> {
+                override fun apply(project: Project) {
+                    $pluginApplyBody
+                }
+            }
+            """
+
+        buildFile << """
+            buildscript {
+                dependencies { classpath fileTree(dir: "producer/build/libs", include: '*.jar') }
+            }
+
+            apply plugin: SomePlugin
+        """
+    }
+}
+
diff --git a/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/PropertyUpgradesBinaryCompatibilityCrossVersionSpec.groovy b/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/PropertyUpgradesBinaryCompatibilityCrossVersionSpec.groovy
new file mode 100644
index 0000000..9f6a096
--- /dev/null
+++ b/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/PropertyUpgradesBinaryCompatibilityCrossVersionSpec.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 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.integtests
+
+import org.gradle.api.plugins.quality.Checkstyle
+import org.gradle.integtests.fixtures.TargetVersions
+
+@TargetVersions("8.0.2")
+class PropertyUpgradesBinaryCompatibilityCrossVersionSpec extends AbstractPropertyUpgradesBinaryCompatibilityCrossVersionSpec {
+
+    @Override
+    protected List<Class<?>> importClasses() {
+        return [Checkstyle]
+    }
+
+    def "can use upgraded Checkstyle in a Groovy plugin compiled with a previous Gradle version"() {
+        given:
+        prepareGroovyPluginTest """
+            project.tasks.register("myCheckstyle", Checkstyle) {
+                maxErrors = 1
+                // int currentMaxErrors = maxErrors doesn't work yet
+                int currentMaxErrors = it.maxErrors
+                assert currentMaxErrors == 1
+            }
+        """
+
+        expect:
+        succeedsWithPluginCompiledWithPreviousVersion()
+    }
+
+    def "can use upgraded Checkstyle in a Java plugin compiled with a previous Gradle version"() {
+        given:
+        prepareJavaPluginTest """
+            project.getTasks().register("myCheckstyle", Checkstyle.class, it -> {
+                it.setMaxErrors(1);
+                int currentMaxErrors = it.getMaxErrors();
+                assert currentMaxErrors == 1;
+            });
+        """
+
+        expect:
+        succeedsWithPluginCompiledWithPreviousVersion()
+    }
+
+    def "can use upgraded Checkstyle in a Kotlin plugin compiled with a previous Gradle version"() {
+        given:
+        prepareKotlinPluginTest """
+            project.tasks.register("myCheckstyle", Checkstyle::class.java) {
+                maxErrors = 1
+                val currentMaxErrors = maxErrors
+                assert(currentMaxErrors == 1)
+            }
+        """
+
+        expect:
+        succeedsWithPluginCompiledWithPreviousVersion()
+    }
+
+    def succeedsWithPluginCompiledWithPreviousVersion() {
+        version previous withTasks 'assemble' inDirectory(file("producer")) run()
+        version current withTasks 'tasks' withStacktraceEnabled() requireDaemon() requireIsolatedDaemons() run()
+    }
+}
+
diff --git a/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/TapiAgentInstrumentationCrossVersionSpec.groovy b/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/TapiAgentInstrumentationCrossVersionSpec.groovy
new file mode 100644
index 0000000..64cb296
--- /dev/null
+++ b/subprojects/integ-test/src/crossVersionTest/groovy/org/gradle/integtests/TapiAgentInstrumentationCrossVersionSpec.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2023 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.integtests
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+
+@TargetGradleVersion("current")
+class TapiAgentInstrumentationCrossVersionSpec extends ToolingApiSpecification {
+    private String buildOutput
+
+    def setup() {
+        // TODO(mlopatkin) Figure a way to have agent-based instrumentation in the embedded TAPI mode.
+        toolingApi.requireDaemons()
+    }
+
+    def "agent is enabled in TAPI by default"() {
+        withDumpAgentStatusTask()
+
+        when:
+        runDumpTaskWithTapi()
+
+        then:
+        agentWasApplied()
+    }
+
+    def "agent is applied if enabled in settings"() {
+        given:
+        withAgentEnabledInProperties()
+        withDumpAgentStatusTask()
+
+        when:
+        runDumpTaskWithTapi()
+
+        then:
+        agentWasApplied()
+    }
+
+    def "agent is not applied if disabled in settings"() {
+        given:
+        withAgentDisabledInProperties()
+        withDumpAgentStatusTask()
+
+        when:
+        runDumpTaskWithTapi()
+
+        then:
+        agentWasNotApplied()
+    }
+
+    private void runDumpTaskWithTapi() {
+        withConnection {
+            buildOutput = withBuild {
+                it.forTasks("hello")
+            }.stdout.toString()
+        }
+    }
+
+    private void agentWasApplied() {
+        agentStatusWas(true)
+    }
+
+    private void agentWasNotApplied() {
+        agentStatusWas(false)
+    }
+
+    private void agentStatusWas(boolean applied) {
+        assert buildOutput.contains("agent applied = $applied")
+    }
+
+    private void withAgentEnabledInProperties() {
+        withAgentStatusInProperties(true)
+    }
+
+    private void withAgentDisabledInProperties() {
+        withAgentStatusInProperties(false)
+    }
+
+    private void withAgentStatusInProperties(boolean shouldApply) {
+        file("gradle.properties") << "org.gradle.internal.instrumentation.agent=$shouldApply"
+    }
+
+    private void withDumpAgentStatusTask() {
+        buildFile << """
+            import org.gradle.internal.agents.AgentStatus
+
+            tasks.register('hello') {
+                doLast {
+                    def status = services.get(AgentStatus).isAgentInstrumentationEnabled()
+                    println("agent applied = \${status}")
+                }
+            }
+
+            defaultTasks 'hello'
+        """
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleBuildRunner.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleBuildRunner.groovy
index aac6770..92e0e8b 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleBuildRunner.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleBuildRunner.groovy
@@ -29,4 +29,4 @@
         //To run/debug any build from the IDE you can tweak the parameters:
         //org.gradle.launcher.Main.main("-p", "/path/to/project/you/want/to/build", "someTask", "someOtherTask");
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleRunConfiguration.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleRunConfiguration.groovy
index 250698c..8d7e9c5 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleRunConfiguration.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/debug/GradleRunConfiguration.groovy
@@ -26,4 +26,4 @@
     public static void main(String[] args) {
         new Main().run(args)
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/instrumentation/agent/AgentApplicationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/instrumentation/agent/AgentApplicationTest.groovy
new file mode 100644
index 0000000..f9eecce
--- /dev/null
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/instrumentation/agent/AgentApplicationTest.groovy
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2022 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.instrumentation.agent
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.configurationcache.ConfigurationCacheFixture
+import org.gradle.integtests.fixtures.daemon.DaemonLogsAnalyzer
+import org.gradle.integtests.fixtures.executer.GradleHandle
+import org.gradle.internal.agents.AgentStatus
+import org.gradle.launcher.daemon.configuration.DaemonBuildOptions
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
+
+import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
+
+// This test doesn't live in :instrumentation-agent to avoid the latter being implicitly added to
+// the test runtime classpath as part of the main source set's output.
+// It is important to have the agent appended to the classpath of all integration tests.
+class AgentApplicationTest extends AbstractIntegrationSpec {
+    def "agent is enabled by default"() {
+        given:
+        withDumpAgentStatusTask()
+
+        when:
+        succeeds()
+
+        then:
+        agentWasApplied()
+    }
+
+    def "agent is not applied if disabled in the command-line"() {
+        given:
+        withoutAgent()
+        withDumpAgentStatusTask()
+
+        when:
+        succeeds()
+
+        then:
+        agentWasNotApplied()
+    }
+
+    def "agent is applied to the daemon process running the build"() {
+        given:
+        withAgent()
+        withDumpAgentStatusTask()
+
+        when:
+        succeeds()
+
+        then:
+        agentWasApplied()
+    }
+
+    @Requires(
+        value = IntegTestPreconditions.IsConfigCached,
+        reason = "Tests the configuration cache behavior"
+    )
+    def "keeping agent status does not invalidate the configuration cache"() {
+        def configurationCache = new ConfigurationCacheFixture(this)
+        given:
+        withDumpAgentStatusAtConfiguration()
+
+        when:
+        withAgentApplied(agentStatus)
+        succeeds()
+
+        then:
+        agentStatusWas(agentStatus)
+        configurationCache.assertStateStored()
+
+        when:
+        withAgentApplied(agentStatus)
+        succeeds()
+
+        then:
+        configurationCache.assertStateLoaded()
+
+        where:
+        agentStatus << [true, false]
+    }
+
+    @Requires(
+        value = IntegTestPreconditions.IsConfigCached,
+        reason = "Tests the configuration cache behavior"
+    )
+    def "changing agent status invalidates the configuration cache"() {
+        def configurationCache = new ConfigurationCacheFixture(this)
+        given:
+        withDumpAgentStatusAtConfiguration()
+
+        when:
+        withAgentApplied(useAgentOnFirstRun)
+        succeeds()
+
+        then:
+        agentStatusWas(useAgentOnFirstRun)
+        configurationCache.assertStateStored()
+
+        when:
+        withAgentApplied(useAgentOnSecondRun)
+        succeeds()
+
+        then:
+        agentStatusWas(useAgentOnSecondRun)
+        configurationCache.assertStateStored()
+
+        where:
+        useAgentOnFirstRun | useAgentOnSecondRun
+        true               | false
+        false              | true
+    }
+
+    @Requires(
+        value = IntegTestPreconditions.IsDaemonExecutor,
+        reason = "Testing the daemons"
+    )
+    def "daemon with the same agent status is reused"() {
+        given:
+        executer.requireIsolatedDaemons()
+        withDumpAgentStatusTask()
+
+        when:
+        withAgentApplied(useAgentOnFirstRun)
+        succeeds()
+
+        then:
+        daemons.daemon.becomesIdle()
+
+        when:
+        withAgentApplied(useAgentOnSecondRun)
+        succeeds()
+
+        then:
+        def expectedDaemonCount = shouldReuseDaemon ? 1 : 2
+
+        daemons.daemons.size() == expectedDaemonCount
+
+        where:
+        useAgentOnFirstRun | useAgentOnSecondRun || shouldReuseDaemon
+        true               | true                || true
+        false              | false               || true
+        true               | false               || false
+        false              | true                || false
+    }
+
+    @Requires(
+        value = IntegTestPreconditions.IsEmbeddedExecutor,
+        reason = "Tests the embedded distribution"
+    )
+    def "daemon spawned from embedded runner has agent enabled"() {
+        given:
+        executer.tap {
+            // Force a separate daemon spawned by the InProcessGradleExecuter
+            requireDaemon()
+            requireIsolatedDaemons()
+        }
+        withAgent()
+        withDumpAgentStatusTask()
+
+        when:
+        succeeds()
+
+        then:
+        agentWasApplied()
+    }
+
+    @Requires(
+        value = UnitTestPreconditions.Jdk8OrEarlier,
+        reason = "Java 9 and above needs --add-opens to make environment variable mutation work"
+    )
+    def "foreground daemon respects the feature flag"() {
+        given:
+        executer.tap {
+            requireDaemon()
+            requireIsolatedDaemons()
+        }
+
+        def foregroundDaemon = startAForegroundDaemon(agentStatus)
+        withAgentApplied(agentStatus)
+        withDumpAgentStatusTask()
+
+        when:
+        succeeds()
+
+        then:
+        // Only one (the foreground one) daemon should be present
+        daemons.getRegistry().getAll().size() == 1
+        agentStatusWas(agentStatus)
+
+        cleanup:
+        foregroundDaemon?.abort()
+
+        where:
+        agentStatus << [true, false]
+    }
+
+    private void withDumpAgentStatusTask() {
+        buildFile("""
+            import ${AgentStatus.name}
+
+            tasks.register('hello') {
+                doLast {
+                    def status = services.get(AgentStatus).isAgentInstrumentationEnabled()
+                    println("agent applied = \${status}")
+                }
+            }
+
+            defaultTasks 'hello'
+        """)
+    }
+
+    private void withDumpAgentStatusAtConfiguration() {
+        buildFile("""
+            import ${AgentStatus.name}
+
+            def status = services.get(AgentStatus).isAgentInstrumentationEnabled()
+            tasks.register('hello') {
+                doLast {
+                    println("agent applied = \$status")
+                }
+            }
+
+            defaultTasks 'hello'
+        """)
+    }
+
+    private void agentWasApplied() {
+        agentStatusWas(true)
+    }
+
+    private void agentWasNotApplied() {
+        agentStatusWas(false)
+    }
+
+    private void agentStatusWas(boolean applied) {
+        outputContains("agent applied = $applied")
+    }
+
+    private void withAgent() {
+        withAgentApplied(true)
+    }
+
+    private void withAgentApplied(boolean shouldApply) {
+        executer.withArgument("-D${DaemonBuildOptions.ApplyInstrumentationAgentOption.GRADLE_PROPERTY}=$shouldApply")
+    }
+
+    private void withoutAgent() {
+        withAgentApplied(false)
+    }
+
+    DaemonLogsAnalyzer getDaemons() {
+        new DaemonLogsAnalyzer(executer.daemonBaseDir)
+    }
+
+    GradleHandle startAForegroundDaemon(Boolean shouldApplyAgent = null) {
+        int currentSize = daemons.getRegistry().getAll().size()
+        if (shouldApplyAgent != null) {
+            withAgentApplied(shouldApplyAgent)
+        }
+        def daemon = executer.noExtraLogging().withArgument("--foreground").start()
+        // Wait for foreground daemon to be ready
+        poll() { assert daemons.getRegistry().getAll().size() == (currentSize + 1) }
+        return daemon
+    }
+}
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationSpec.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationSpec.groovy
index a320fba..c9f4eaa 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationSpec.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ApplicationIntegrationSpec.groovy
@@ -17,13 +17,11 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.ScriptExecuter
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.archives.TestReproducibleArchives
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.test.fixtures.file.TestFile
 import spock.lang.IgnoreIf
-import spock.lang.Issue
 
 import static org.hamcrest.CoreMatchers.startsWith
 
@@ -80,7 +78,7 @@
 
     def canUseDefaultJvmArgsToPassMultipleOptionsToJvmWhenRunningScript() {
         file("build.gradle") << '''
-applicationDefaultJvmArgs = ['-DtestValue=value', '-DtestValue2=some value', '-DtestValue3=some value']
+application.applicationDefaultJvmArgs = ['-DtestValue=value', '-DtestValue2=some value', '-DtestValue3=some value']
 '''
         file('src/main/java/org/gradle/test/Main.java') << '''
 package org.gradle.test;
@@ -115,7 +113,7 @@
 
     def canUseBothDefaultJvmArgsAndEnvironmentVariableToPassOptionsToJvmWhenRunningScript() {
         file("build.gradle") << '''
-applicationDefaultJvmArgs = ['-Dvar1=value1', '-Dvar2=some value2']
+application.applicationDefaultJvmArgs = ['-Dvar1=value1', '-Dvar2=some value2']
 '''
         file('src/main/java/org/gradle/test/Main.java') << '''
 package org.gradle.test;
@@ -155,7 +153,7 @@
         def testValue2 = OperatingSystem.current().windows ? 'some value$PATH' : 'some value\\\\$PATH'
         def testValue3 = 'some value%PATH%'
         file("build.gradle") << '''
-            applicationDefaultJvmArgs = [
+            application.applicationDefaultJvmArgs = [
                 '-DtestValue=value',
                 '-DtestValue2=some value$PATH',
                 '-DtestValue3=some value%PATH%',
@@ -191,11 +189,9 @@
         result.assertNormalExitValue()
     }
 
-    @Issue("https://github.com/gradle/gradle/issues/21505")
-    @ToBeFixedForConfigurationCache(because = "applicationDefaultJvmArgs")
     def canUseDefaultJvmArgsInRunTask() {
         file("build.gradle") << '''
-        applicationDefaultJvmArgs = ['-Dvar1=value1', '-Dvar2=value2']
+        application.applicationDefaultJvmArgs = ['-Dvar1=value1', '-Dvar2=value2']
         '''
         file('src/main/java/org/gradle/test/Main.java') << '''
         package org.gradle.test;
@@ -216,9 +212,10 @@
         run 'run'
     }
 
+
     def "can customize application name"() {
         file('build.gradle') << '''
-applicationName = 'mega-app'
+application.applicationName = 'mega-app'
 '''
         file('src/main/java/org/gradle/test/Main.java') << '''
 package org.gradle.test;
@@ -372,7 +369,7 @@
 
         and:
         buildFile << """
-            applicationDistribution.from("src/somewhere-else") {
+            application.applicationDistribution.from("src/somewhere-else") {
                 include "**/r2.*"
             }
         """
@@ -402,7 +399,7 @@
                 }
             }
 
-            applicationDistribution.from(createDocs) {
+            application.applicationDistribution.from(createDocs) {
                 into "docs"
                 rename 'readme(.*)', 'READ-ME\$1'
             }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
index 5bb8585..1742850 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/CommandLineIntegrationTest.groovy
@@ -18,16 +18,15 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationTest
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.executer.ExecutionFailure
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.jvm.Jvm
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.PreconditionVerifier
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
 import org.gradle.util.internal.GFileUtils
-import org.gradle.util.PreconditionVerifier
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Assume
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -39,7 +38,7 @@
     @Rule
     public final TestResources resources = new TestResources(testDirectoryProvider)
     @Rule
-    public final PreconditionVerifier verifier = new PreconditionVerifier()
+    public final PreconditionVerifier preconditionVerifier = new PreconditionVerifier()
 
     @Before
     void setup() {
@@ -53,41 +52,42 @@
     }
 
     @Test
+    @Requires(IntegTestPreconditions.NotEmbeddedExecutor)
     void canDefineJavaHomeUsingEnvironmentVariable() {
         String javaHome = Jvm.current().javaHome
         String expectedJavaHome = "-PexpectedJavaHome=${javaHome}"
 
         // Handle JAVA_HOME specified
-        executer.withEnvironmentVars('JAVA_HOME': javaHome).withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
+        executer.withJavaHome(javaHome).withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
 
         // Handle JAVA_HOME with trailing separator
-        executer.withEnvironmentVars('JAVA_HOME': javaHome + File.separator).withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
+        executer.withJavaHome(javaHome + File.separator).withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
 
         if (!OperatingSystem.current().isWindows()) {
             return
         }
 
         // Handle JAVA_HOME wrapped in quotes
-        executer.withEnvironmentVars('JAVA_HOME': "\"$javaHome\"").withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
+        executer.withJavaHome("\"$javaHome\"").withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
 
         // Handle JAVA_HOME with slash separators. This is allowed by the JVM
-        executer.withEnvironmentVars('JAVA_HOME': javaHome.replace(File.separator, '/')).withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
+        executer.withJavaHome(javaHome.replace(File.separator, '/')).withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
     }
 
     @Test
+    @Requires(IntegTestPreconditions.NotEmbeddedExecutor)
     void usesJavaCommandFromPathWhenJavaHomeNotSpecified() {
         String javaHome = Jvm.current().javaHome
         String expectedJavaHome = "-PexpectedJavaHome=${javaHome}"
 
         String path = String.format('%s%s%s', Jvm.current().javaExecutable.parentFile, File.pathSeparator, System.getenv('PATH'))
-        executer.withEnvironmentVars('PATH': path, 'JAVA_HOME': '').withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
+        executer.withEnvironmentVars('PATH': path).withJavaHome('').withArguments(expectedJavaHome).withTasks('checkJavaHome').run()
     }
 
     @Test
+    @Requires(IntegTestPreconditions.NotEmbeddedExecutor)
     void failsWhenJavaHomeDoesNotPointToAJavaInstallation() {
-        Assume.assumeFalse(GradleContextualExecuter.embedded)
-
-        def failure = executer.withEnvironmentVars('JAVA_HOME': testDirectory).withTasks('checkJavaHome').runWithFailure()
+        def failure = executer.withJavaHome(testDirectory).withTasks('checkJavaHome').runWithFailure()
         if (OperatingSystem.current().isWindows()) {
             assert failure.output.contains('ERROR: JAVA_HOME is set to an invalid directory')
         } else {
@@ -96,10 +96,8 @@
     }
 
     @Test
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires([UnitTestPreconditions.Symlinks, IntegTestPreconditions.NotEmbeddedExecutor])
     void failsWhenJavaHomeNotSetAndPathDoesNotContainJava() {
-        Assume.assumeFalse(GradleContextualExecuter.embedded)
-
         def links = ['basename', 'dirname', 'uname', 'which', 'sed', 'sh', 'bash']
         def binDir = file('fake-bin')
         try {
@@ -112,7 +110,7 @@
                 path = binDir.absolutePath
             }
 
-            def failure = executer.withEnvironmentVars('PATH': path, 'JAVA_HOME': '').withTasks('checkJavaHome').runWithFailure()
+            def failure = executer.withEnvironmentVars('PATH': path).withJavaHome('').withTasks('checkJavaHome').runWithFailure()
             assert failure.error.contains("ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.")
         } finally {
             links.each {
@@ -144,10 +142,9 @@
     }
 
     @Test
-    @Requires(TestPrecondition.NOT_EC2_AGENT)
+    @Requires([UnitTestPreconditions.NotEC2Agent, IntegTestPreconditions.NotEmbeddedExecutor])
     @Issue('https://github.com/gradle/gradle-private/issues/2876')
     void checkDefaultGradleUserHome() {
-        Assume.assumeFalse(GradleContextualExecuter.embedded)
         // the actual testing is done in the build script.
         File userHome = file('customUserHome')
         executer
@@ -166,16 +163,15 @@
     }
 
     @Test
+    @Requires(IntegTestPreconditions.NotEmbeddedExecutor)
     void canSpecifySystemPropertiesUsingGradleOptsEnvironmentVariable() {
-        Assume.assumeFalse(GradleContextualExecuter.embedded)
         // the actual testing is done in the build script.
         executer.withTasks("checkSystemProperty").withEnvironmentVars("GRADLE_OPTS": '-DcustomProp1=custom-value "-DcustomProp2=custom value"').run();
     }
 
     @Test
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires([UnitTestPreconditions.UnixDerivative, IntegTestPreconditions.NotEmbeddedExecutor])
     void canSpecifySystemPropertiesUsingGradleOptsEnvironmentVariableWithLinebreaks() {
-        Assume.assumeFalse(GradleContextualExecuter.embedded)
         // the actual testing is done in the build script.
         executer.withTasks("checkSystemProperty").withEnvironmentVars("GRADLE_OPTS": """
             -DcustomProp1=custom-value
@@ -184,8 +180,8 @@
     }
 
     @Test
+    @Requires(IntegTestPreconditions.NotEmbeddedExecutor)
     void canSpecifySystemPropertiesUsingJavaOptsEnvironmentVariable() {
-        Assume.assumeFalse(GradleContextualExecuter.embedded)
         // the actual testing is done in the build script.
         executer.withTasks("checkSystemProperty").withEnvironmentVars("JAVA_OPTS": '-DcustomProp1=custom-value "-DcustomProp2=custom value"').run();
     }
@@ -231,10 +227,8 @@
     }
 
     @Test
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires([UnitTestPreconditions.Symlinks, IntegTestPreconditions.NotEmbeddedExecutor])
     void resolvesLinksWhenDeterminingHomeDirectory() {
-        Assume.assumeFalse(GradleContextualExecuter.embedded)
-
         def script = file('bin/my app')
         script.parentFile.createDir()
         script.createLink(distribution.gradleHomeDir.file('bin/gradle'))
@@ -247,9 +241,8 @@
     }
 
     @Test
+    @Requires(IntegTestPreconditions.NotEmbeddedExecutor)
     void usesScriptBaseNameAsApplicationNameForUseInLogMessages() {
-        Assume.assumeFalse(GradleContextualExecuter.embedded)
-
         def binDir = distribution.gradleHomeDir.file('bin')
         def newScript = binDir.file(OperatingSystem.current().getScriptName('my app'))
         try {
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DifferentJnaVersionInPluginIntegrationSpec.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DifferentJnaVersionInPluginIntegrationSpec.groovy
index df02267..e2d9847 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DifferentJnaVersionInPluginIntegrationSpec.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DifferentJnaVersionInPluginIntegrationSpec.groovy
@@ -17,11 +17,11 @@
 package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class DifferentJnaVersionInPluginIntegrationSpec extends AbstractIntegrationSpec {
-    @Requires(TestPrecondition.NOT_MAC_OS_X_M1)
+    @Requires(UnitTestPreconditions.NotMacOsM1)
     def 'can build a plugin with a different jna version'() {
         given:
         buildScript """
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionLocatorIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionLocatorIntegrationTest.groovy
index 8d50c76..a51d773 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionLocatorIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/DistributionLocatorIntegrationTest.groovy
@@ -17,13 +17,13 @@
 package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
-import org.gradle.util.internal.DistributionLocator
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.util.internal.DistributionLocator
 import spock.lang.Specification
 
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 class DistributionLocatorIntegrationTest extends Specification {
     private static final int CONNECTION_TIMEOUT_SECONDS = 60 * 1000
     private static final int READ_TIMEOUT_SECONDS = 60 * 1000
@@ -38,6 +38,10 @@
         urlExist(locator.getDistributionFor(GradleVersion.version("1.12")))
     }
 
+    /**
+     * If this test fails, it means that the snapshot in `released-versions.json` is no longer available.
+     * You need to update that entry with a recent snapshot by hand.
+     */
     def "locates snapshot versions"() {
         expect:
         urlExist(locator.getDistributionFor(distributions.mostRecentReleaseSnapshot.version))
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.groovy
index e3e6c0d..a240b3b 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/JavaProjectIntegrationTest.groovy
@@ -49,13 +49,25 @@
 
     @Test
     void handlesTestSrcWhichDoesNotContainAnyTestCases() {
-        TestFile buildFile = testFile("build.gradle")
-        buildFile.writelns("apply plugin: 'java'")
+        given:
+        testFile("build.gradle") << """
+            plugins {
+                id("java")
+            }
+
+            ${mavenCentralRepository()}
+
+            testing.suites.test.useJUnit()
+        """
         testFile("src/test/java/org/gradle/NotATest.java") << """
             package org.gradle;
             public class NotATest {}
         """
 
+        expect:
+        executer.expectDocumentedDeprecationWarning("No test executed. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. There are test sources present but no test was executed. Please check your test configuration. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_fail_on_no_test_executed")
         executer.withTasks("build").run()
     }
 
@@ -83,19 +95,30 @@
 
     @Test
     void separatesOutputResourcesFromCompiledClasses() throws IOException {
-        //given
-        TestFile buildFile = testFile("build.gradle")
-        buildFile.write("apply plugin: 'java'")
+        given:
+        testFile("build.gradle") << """
+            plugins {
+                id("java")
+            }
 
+           ${mavenCentralRepository()}
+
+            testing.suites.test.useJUnit()
+        """
         testFile("src/main/resources/prod.resource") << ""
         testFile("src/main/java/Main.java") << "class Main {}"
         testFile("src/test/resources/test.resource") << "test resource"
-        testFile("src/test/java/TestFoo.java") << "class TestFoo {}"
+        testFile("src/test/java/TestFoo.java") << """
+        public class TestFoo {
+            @org.junit.Test
+            public void test() {}
+        }
+        """
 
-        //when
+        when:
         executer.withTasks("build").run()
 
-        //then
+        then:
         testFile("build/resources/main/prod.resource").assertExists()
         testFile("build/classes/java/main/prod.resource").assertDoesNotExist()
 
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MultiProjectDependencyIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MultiProjectDependencyIntegrationTest.groovy
index 35f29fa..75056a2 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MultiProjectDependencyIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/MultiProjectDependencyIntegrationTest.groovy
@@ -17,7 +17,6 @@
 package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.hamcrest.CoreMatchers
 import spock.lang.IgnoreIf
@@ -182,8 +181,8 @@
         jarsNotBuilt 'a', 'b', 'c'
     }
 
-    @ToBeFixedForConfigurationCache(because = "test can't handle parallel task execution")
     @IgnoreIf({GradleContextualExecuter.parallel})  // 'c' + 'd' _may_ be built with parallel executer
+    @IgnoreIf({GradleContextualExecuter.configCache}) // test can't handle parallel task execution
     def "project dependency a->[b,c] and c->d and b fails"() {
         projectDependency from: 'a', to: ['b', 'c']
         projectDependency from: 'c', to: ['d']
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/NestedInputKotlinImplementationTrackingIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/NestedInputKotlinImplementationTrackingIntegrationTest.groovy
index f502bce..fa18aa0 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/NestedInputKotlinImplementationTrackingIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/NestedInputKotlinImplementationTrackingIntegrationTest.groovy
@@ -19,10 +19,14 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DirectoryBuildCacheFixture
 import org.gradle.integtests.fixtures.KotlinDslTestUtil
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.integtests.fixtures.versions.KotlinGradlePluginVersions
 import org.gradle.test.fixtures.file.LeaksFileHandles
 import org.gradle.test.fixtures.file.TestFile
 import spock.lang.Issue
 
+import static org.junit.Assume.assumeFalse
+
 @LeaksFileHandles
 class NestedInputKotlinImplementationTrackingIntegrationTest extends AbstractIntegrationSpec implements DirectoryBuildCacheFixture {
 
@@ -134,9 +138,10 @@
     }
 
     def "task action defined in latest Kotlin can be tracked when using language version #kotlinVersion"() {
+        assumeFalse(GradleContextualExecuter.embedded)
         file("buildSrc/build.gradle.kts") << """
             plugins {
-                kotlin("jvm") version("1.8.10")
+                kotlin("jvm") version("${new KotlinGradlePluginVersions().latestStableOrRC}")
                 `java-gradle-plugin`
             }
 
@@ -194,13 +199,7 @@
         executedAndNotSkipped(":myTask")
 
         where:
-        kotlinVersion << [
-            "1.4",
-            "1.5",
-            "1.6",
-            "1.7",
-            "1.8",
-        ]
+        kotlinVersion << KotlinGradlePluginVersions.LANGUAGE_VERSIONS
     }
 
     private void setupTaskWithNestedAction(String actionType, String actionInvocation, TestFile projectDir = temporaryFolder.testDirectory) {
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelStaleOutputIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelStaleOutputIntegrationTest.groovy
index 01f6473..7034cbe 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelStaleOutputIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ParallelStaleOutputIntegrationTest.groovy
@@ -17,7 +17,6 @@
 package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.GradleVersion
 import spock.lang.Ignore
 import spock.lang.Issue
 
@@ -86,12 +85,18 @@
 
         // We just need to assert that the build fails with the right error message here, it doesn't matter which task is the first to fail,
         // allowing either failure to pass the test should reduce flakiness on CI.
+
+        def docLinkMessage = getDocLinkMessage()
         if (result.error.contains("Could not create task ':a:bar'.")) {
-            result.assertHasErrorOutput("Resolution of the configuration :a:myconf was attempted from a context different than the project context. See: https://docs.gradle.org/${GradleVersion.current().version}/userguide/viewing_debugging_dependencies.html#sub:resolving-unsafe-configuration-resolution-errors for more information.")
+            result.assertHasErrorOutput("Resolution of the configuration :a:myconf was attempted from a context different than the project context. " + docLinkMessage);
         } else if (result.error.contains("Could not create task ':b:bar'.")) {
-            result.assertHasErrorOutput("Resolution of the configuration :b:myconf was attempted from a context different than the project context. See: https://docs.gradle.org/${GradleVersion.current().version}/userguide/viewing_debugging_dependencies.html#sub:resolving-unsafe-configuration-resolution-errors for more information.")
+            result.assertHasErrorOutput("Resolution of the configuration :b:myconf was attempted from a context different than the project context. " + docLinkMessage)
         } else {
             throw new AssertionError("Unexpected task failure in test, see error output.")
         }
     }
+
+    def getDocLinkMessage() {
+        documentationRegistry.getDocumentationRecommendationFor("information", "viewing_debugging_dependencies", "sub:resolving-unsafe-configuration-resolution-errors")
+    }
 }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/PluginConfigurationAttributesIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/PluginConfigurationAttributesIntegrationTest.groovy
index 3ee4ece..0057c5b 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/PluginConfigurationAttributesIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/PluginConfigurationAttributesIntegrationTest.groovy
@@ -73,7 +73,7 @@
             configurations {
                 ${configuration}Consumable {
                     extendsFrom($configuration)
-                    canBeConsumed = true
+                    assert canBeConsumed
                     canBeResolved = false
                     attributes {
                         attribute(Attribute.of("test", String), "test")
@@ -87,7 +87,7 @@
             configurations {
                 consumer {
                     canBeConsumed = false
-                    canBeResolved = true
+                    assert canBeResolved
                     attributes {
                         attribute(Attribute.of("test", String), "test")
                         ${plugin == 'codenarc' ?
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
index 7b80164..24b35f5 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/ProjectLayoutIntegrationTest.groovy
@@ -52,6 +52,8 @@
     implementation 'org.scala-lang:scala-library:2.11.12'
 }
 
+testing.suites.test.useJUnitJupiter()
+
 sourceSets.each {
     configure(it) {
         resources.srcDir 'src'
@@ -74,17 +76,17 @@
         file('src/resources/org/gradle/main/resource2.txt') << 'some text'
         file('src/resources/org/gradle/test/resource2.txt') << 'some text'
         file('src/org/gradle/main/JavaClass.java') << 'package org.gradle; public class JavaClass { }'
-        file('src/org/gradle/test/JavaClassTest.java') << 'package org.gradle; class JavaClassTest { JavaClass c = new JavaClass(); }'
+        file('src/org/gradle/test/JavaClassTest.java') << 'package org.gradle; class JavaClassTest { JavaClass c = new JavaClass(); @org.junit.jupiter.api.Test public void test() { } }'
         file('src/java/org/gradle/main/JavaClass2.java') << 'package org.gradle; class JavaClass2 { }'
-        file('src/java/org/gradle/test/JavaClassTest2.java') << 'package org.gradle; class JavaClassTest2 { JavaClass c = new JavaClass(); }'
+        file('src/java/org/gradle/test/JavaClassTest2.java') << 'package org.gradle; class JavaClassTest2 { JavaClass c = new JavaClass(); @org.junit.jupiter.api.Test public void test() { } }'
         file('src/org/gradle/main/GroovyClass.groovy') << 'package org.gradle; class GroovyClass { }'
-        file('src/org/gradle/test/GroovyClassTest.groovy') << 'package org.gradle; class GroovyClassTest { GroovyClass c = new GroovyClass() }'
+        file('src/org/gradle/test/GroovyClassTest.groovy') << 'package org.gradle; class GroovyClassTest { GroovyClass c = new GroovyClass(); @org.junit.jupiter.api.Test void test() { } }'
         file('src/groovy/org/gradle/main/GroovyClass2.groovy') << 'package org.gradle; class GroovyClass2 { }'
-        file('src/groovy/org/gradle/test/GroovyClassTest2.groovy') << 'package org.gradle; class GroovyClassTest2 { GroovyClass c = new GroovyClass() }'
+        file('src/groovy/org/gradle/test/GroovyClassTest2.groovy') << 'package org.gradle; class GroovyClassTest2 { GroovyClass c = new GroovyClass(); @org.junit.jupiter.api.Test public void test() { } }'
         file('src/org/gradle/main/ScalaClass.scala') << 'package org.gradle; class ScalaClass { }'
-        file('src/org/gradle/test/ScalaClassTest.scala') << 'package org.gradle; class ScalaClassTest { val c: ScalaClass = new ScalaClass() }'
+        file('src/org/gradle/test/ScalaClassTest.scala') << 'package org.gradle; class ScalaClassTest { val c: ScalaClass = new ScalaClass(); @org.junit.jupiter.api.Test def test { } }'
         file('src/scala/org/gradle/main/ScalaClass2.scala') << 'package org.gradle; class ScalaClass2 { }'
-        file('src/scala/org/gradle/test/ScalaClassTest2.scala') << 'package org.gradle; class ScalaClassTest2 { val c: ScalaClass = new ScalaClass() }'
+        file('src/scala/org/gradle/test/ScalaClassTest2.scala') << 'package org.gradle; class ScalaClassTest2 { val c: ScalaClass = new ScalaClass(); @org.junit.jupiter.api.Test def test { } }'
 
         executer.withTasks('build').run()
 
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SubstIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SubstIntegrationTest.groovy
index 3b2c940..41fc40a 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SubstIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SubstIntegrationTest.groovy
@@ -16,11 +16,11 @@
 package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class SubstIntegrationTest extends AbstractIntegrationSpec {
     def "up to date check works from filesystem's root - input folder to output file"() {
         def drive = 'X:'
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy
index b9a5cb3..ddc460a 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/SyncTaskIntegrationTest.groovy
@@ -18,8 +18,8 @@
 import groovy.test.NotYetImplemented
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 class SyncTaskIntegrationTest extends AbstractIntegrationSpec {
@@ -406,7 +406,7 @@
         !file('dest/extra.txt').exists()
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     @ToBeFixedForConfigurationCache(because = "Task.getProject() during execution")
     def "sync fails when unable to clean-up files"() {
         given:
@@ -437,7 +437,7 @@
         ins.close()
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "sync fails when the output contains unreadable files"() {
         given:
         def input = file("readableFile.txt").createFile()
@@ -463,7 +463,7 @@
         failure.assertHasDocumentedCause("Cannot access a file in the destination directory. " +
             "Syncing to a directory which contains unreadable content is not supported. " +
             "Use a Copy task with Task.doNotTrackState() instead. " +
-            "See https://docs.gradle.org/current/userguide/incremental_build.html#disable-state-tracking for more details.")
+            documentationRegistry.getDocumentationRecommendationFor("information", "incremental_build", "disable-state-tracking"))
         failureHasCause("Failed to create MD5 hash for file '${unreadableOutput}' as it does not exist.")
 
         cleanup:
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskErrorExecutionIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskErrorExecutionIntegrationTest.groovy
index 2bcf2eb..0421ac1 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskErrorExecutionIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/TaskErrorExecutionIntegrationTest.groovy
@@ -15,13 +15,23 @@
  */
 package org.gradle.integtests
 
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.internal.reflect.problems.ValidationProblemId
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.internal.reflect.validation.ValidationTestFor
 import org.gradle.test.fixtures.file.TestFile
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+
 class TaskErrorExecutionIntegrationTest extends AbstractIntegrationSpec implements ValidationMessageChecker {
+
+    public static final String LIST_OF_PROJECTS = "Run gradle projects to get a list of available projects."
+    public static final String GET_TASKS = "Run gradle tasks to get a list of available tasks."
+    public static final String NAME_EXPANSION = new DocumentationRegistry().getDocumentationRecommendationFor("on name expansion", "command_line_interface", "sec:name_abbreviation")
+
     def setup() {
         expectReindentedValidationMessage()
         executer.beforeExecute {
@@ -29,7 +39,7 @@
         }
     }
 
-    def reportsTaskActionExecutionFailsWithError() {
+    def "reports task action execution fails with error"() {
         buildFile << """
             task('do-stuff').doFirst {
                 throw new ArithmeticException('broken')
@@ -44,7 +54,7 @@
         failure.assertHasCause("broken")
     }
 
-    def reportsTaskActionExecutionFailsWithRuntimeException() {
+    def "reports task action execution fails with runtime exception"() {
         buildFile << """
             task brokenClosure {
                 doLast {
@@ -61,7 +71,7 @@
         failure.assertHasCause("broken closure")
     }
 
-    def reportsTaskActionExecutionFailsFromJavaWithRuntimeException() {
+    def "reports task action execution fails from java with runtime exception"() {
         file("buildSrc/src/main/java/org/gradle/BrokenTask.java") << """
             package org.gradle;
 
@@ -90,7 +100,7 @@
         failure.assertHasCause("broken action")
     }
 
-    def reportsTaskInjectedByOtherProjectFailsWithRuntimeException() {
+    def "reports task injected by other project fails with runtime exception"() {
         file("settings.gradle") << "include 'a', 'b'"
         TestFile buildFile = file("b/build.gradle")
         buildFile << """
@@ -114,7 +124,7 @@
     @ValidationTestFor(
         ValidationProblemId.VALUE_NOT_SET
     )
-    def reportsTaskValidationFailure() {
+    def "reports task validation failure"() {
         buildFile << '''
             class CustomTask extends DefaultTask {
                 @InputFile File srcFile
@@ -134,7 +144,7 @@
         failureDescriptionContains(missingValueMessage { type('CustomTask').property('destFile') })
     }
 
-    def reportsUnknownTask() {
+    def "reports unknown task"() {
         settingsFile << """
             rootProject.name = 'test'
             include 'a', 'b'
@@ -151,9 +161,11 @@
         then:
         failure.assertHasDescription("Task 'someTest' not found in root project 'test' and its subprojects. Some candidates are: 'someTask', 'someTaskA', 'someTaskB'.")
         failure.assertHasResolutions(
-            "Run gradle tasks to get a list of available tasks.",
-            "Run with --info or --debug option to get more log output.",
-            "Run with --scan to get full insights.",
+            GET_TASKS,
+            NAME_EXPANSION,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP
         )
 
         when:
@@ -161,9 +173,11 @@
         then:
         failure.assertHasDescription("Cannot locate tasks that match ':someTest' as task 'someTest' not found in root project 'test'. Some candidates are: 'someTask'.")
         failure.assertHasResolutions(
-            "Run gradle tasks to get a list of available tasks.",
-            "Run with --info or --debug option to get more log output.",
-            "Run with --scan to get full insights.",
+            GET_TASKS,
+            NAME_EXPANSION,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP
         )
 
         when:
@@ -171,13 +185,15 @@
         then:
         failure.assertHasDescription("Cannot locate tasks that match 'a:someTest' as task 'someTest' not found in project ':a'. Some candidates are: 'someTask', 'someTaskA'.")
         failure.assertHasResolutions(
-            "Run gradle tasks to get a list of available tasks.",
-            "Run with --info or --debug option to get more log output.",
-            "Run with --scan to get full insights.",
+            GET_TASKS,
+            NAME_EXPANSION,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP
         )
     }
 
-    def reportsAmbiguousTask() {
+    def "reports ambiguous task"() {
         settingsFile << """
             rootProject.name = 'test'
             include 'a', 'b'
@@ -193,9 +209,11 @@
         then:
         failure.assertHasDescription("Task 'soTa' is ambiguous in root project 'test' and its subprojects. Candidates are: 'someTaskA', 'someTaskAll', 'someTaskB'.")
         failure.assertHasResolutions(
-            "Run gradle tasks to get a list of available tasks.",
-            "Run with --info or --debug option to get more log output.",
-            "Run with --scan to get full insights.",
+            GET_TASKS,
+            NAME_EXPANSION,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP
         )
 
         when:
@@ -203,13 +221,15 @@
         then:
         failure.assertHasDescription("Cannot locate tasks that match 'a:soTa' as task 'soTa' is ambiguous in project ':a'. Candidates are: 'someTaskA', 'someTaskAll'.")
         failure.assertHasResolutions(
-            "Run gradle tasks to get a list of available tasks.",
-            "Run with --info or --debug option to get more log output.",
-            "Run with --scan to get full insights.",
+            GET_TASKS,
+            NAME_EXPANSION,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP
         )
     }
 
-    def reportsUnknownProject() {
+    def "reports unknown project"() {
         settingsFile << """
             rootProject.name = 'test'
             include 'projA', 'projB'
@@ -224,13 +244,15 @@
         then:
         failure.assertHasDescription("Cannot locate tasks that match 'prog:someTask' as project 'prog' not found in root project 'test'. Some candidates are: 'projA', 'projB'.")
         failure.assertHasResolutions(
-            "Run gradle projects to get a list of available projects.",
-            "Run with --info or --debug option to get more log output.",
-            "Run with --scan to get full insights.",
+            LIST_OF_PROJECTS,
+            NAME_EXPANSION,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP
         )
     }
 
-    def reportsAmbiguousProject() {
+    def "reports ambiguous project"() {
         settingsFile << """
             rootProject.name = 'test'
             include 'projA', 'projB'
@@ -245,9 +267,11 @@
         then:
         failure.assertHasDescription("Cannot locate tasks that match 'proj:someTask' as project 'proj' is ambiguous in root project 'test'. Candidates are: 'projA', 'projB'.")
         failure.assertHasResolutions(
-            "Run gradle projects to get a list of available projects.",
-            "Run with --info or --debug option to get more log output.",
-            "Run with --scan to get full insights.",
+            LIST_OF_PROJECTS,
+            NAME_EXPANSION,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP
         )
     }
 }
diff --git a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/environment/BuildEnvironmentIntegrationTest.groovy b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/environment/BuildEnvironmentIntegrationTest.groovy
index c0db805..72ac794 100644
--- a/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/environment/BuildEnvironmentIntegrationTest.groovy
+++ b/subprojects/integ-test/src/integTest/groovy/org/gradle/integtests/environment/BuildEnvironmentIntegrationTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import spock.lang.IgnoreIf
 import spock.lang.Issue
@@ -39,7 +39,7 @@
         noExceptionThrown()
     }
 
-    @Requires(TestPrecondition.CASE_INSENSITIVE_FS)
+    @Requires(UnitTestPreconditions.CaseInsensitiveFs)
     def "canonicalizes working directory on case insensitive file system"() {
         testProject()
 
@@ -51,7 +51,7 @@
         noExceptionThrown()
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "canonicalizes working directory for short windows path"() {
         testProject()
 
@@ -86,7 +86,7 @@
         out.contains("and will be even better")
     }
 
-    @Requires(TestPrecondition.WORKING_DIR)
+    @Requires(UnitTestPreconditions.WorkingDir)
     def "build is executed with working directory set to where the build was launched from"() {
         def project1 = file("project1")
         def project2 = file("project2")
diff --git a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/settings.gradle b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/settings.gradle
index a09df0a..1798ad0 100755
--- a/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/settings.gradle
+++ b/subprojects/integ-test/src/integTest/resources/org/gradle/integtests/CommandLineIntegrationTest/shared/settings.gradle
@@ -1 +1 @@
-rootProject.name = 'commandLine'
\ No newline at end of file
+rootProject.name = 'commandLine'
diff --git a/subprojects/internal-instrumentation-api/build.gradle.kts b/subprojects/internal-instrumentation-api/build.gradle.kts
new file mode 100644
index 0000000..7cdd6f3
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/build.gradle.kts
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.
+ */
+
+plugins {
+    id("gradlebuild.internal.java")
+}
+
+dependencies {
+    implementation(project(":model-core"))
+    implementation(project(":base-annotations"))
+    implementation(project(":base-services"))
+
+    implementation(libs.groovy)
+    implementation(libs.asm)
+    implementation(libs.asmTree)
+    implementation(libs.asmCommons)
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/CallableDefinition.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/CallableDefinition.java
new file mode 100644
index 0000000..83216be
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/CallableDefinition.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+public final class CallableDefinition {
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.CLASS)
+    public @interface Name {
+        String value();
+    }
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/CallableKind.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/CallableKind.java
new file mode 100644
index 0000000..725d00d
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/CallableKind.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+public final class CallableKind {
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.CLASS)
+    public @interface AfterConstructor {
+    }
+
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.CLASS)
+    public @interface InstanceMethod {
+    }
+
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.CLASS)
+    public @interface StaticMethod {
+        Class<?> ofClass();
+    }
+
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.CLASS)
+    public @interface GroovyProperty {
+    }
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptCalls.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptCalls.java
new file mode 100644
index 0000000..b5a548e
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptCalls.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+@InterceptGroovyCalls
+@InterceptJvmCalls
+public @interface InterceptCalls {
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptGroovyCalls.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptGroovyCalls.java
new file mode 100644
index 0000000..c8afc4a
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptGroovyCalls.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+public @interface InterceptGroovyCalls {
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptInherited.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptInherited.java
new file mode 100644
index 0000000..c033d43
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptInherited.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks that a method call on subtypes should also be intercepted
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface InterceptInherited {
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptJvmCalls.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptJvmCalls.java
new file mode 100644
index 0000000..f0a18cf
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/InterceptJvmCalls.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+public @interface InterceptJvmCalls {
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/ParameterKind.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/ParameterKind.java
new file mode 100644
index 0000000..032089a
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/ParameterKind.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+public class ParameterKind {
+    @Retention(RetentionPolicy.CLASS)
+    @Target(ElementType.PARAMETER)
+    public @interface Receiver {
+    }
+
+    @Retention(RetentionPolicy.CLASS)
+    @Target(ElementType.PARAMETER)
+    public @interface CallerClassName {
+    }
+
+    @Retention(RetentionPolicy.CLASS)
+    @Target(ElementType.PARAMETER)
+    public @interface KotlinDefaultMask {
+    }
+
+    @Retention(RetentionPolicy.CLASS)
+    @Target(ElementType.PARAMETER)
+    public @interface VarargParameter {
+    }
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/SpecificGroovyCallInterceptors.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/SpecificGroovyCallInterceptors.java
new file mode 100644
index 0000000..f94cabe
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/SpecificGroovyCallInterceptors.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface SpecificGroovyCallInterceptors {
+    String generatedClassName();
+}
+
+
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/SpecificJvmCallInterceptors.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/SpecificJvmCallInterceptors.java
new file mode 100644
index 0000000..a5bbbc6
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/SpecificJvmCallInterceptors.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface SpecificJvmCallInterceptors {
+    String generatedClassName();
+}
+
+
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/UpgradedProperty.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/UpgradedProperty.java
new file mode 100644
index 0000000..90d5311
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/UpgradedProperty.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks that a property is upgraded
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD})
+public @interface UpgradedProperty {
+    /**
+     * Overrides original type that will be used for generated code.
+     * By default, the original type is determined from the lazy property type, e.g.:
+     * Property[T] -> original type becomes T (also Property[Integer] -> Integer and not int)
+     * RegularFileProperty -> original type becomes File
+     * DirectoryProperty -> original type becomes File
+     * MapProperty[K, V] -> original type becomes Map[K, V]
+     * ListProperty[T] -> original type becomes List[T]
+     * ConfigurableFileCollection -> original type becomes FileCollection
+     */
+    Class<?> originalType() default DefaultValue.class;
+
+    boolean fluentSetter() default false;
+
+    interface DefaultValue {
+    }
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/VisitForInstrumentation.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/VisitForInstrumentation.java
new file mode 100644
index 0000000..a7971a1
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/VisitForInstrumentation.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A method that can be used to visit classes for an instrumentation from other projects.
+ * TODO: We should discover classes to visit automatically.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE})
+public @interface VisitForInstrumentation {
+    Class<?>[] value();
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/features/withstaticreference/WithExtensionReferences.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/features/withstaticreference/WithExtensionReferences.java
new file mode 100644
index 0000000..439a758
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/features/withstaticreference/WithExtensionReferences.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.instrumentation.api.annotations.features.withstaticreference;
+
+/**
+ * A Groovy property access interceptor or an instance method interceptor may be marked with
+ * this annotation that specifies the class that the Groovy compiler will resolve the extension to.
+ * In this case, an additional synthetic interceptor is added for the static method of the class
+ * that "matches" the original interceptor's signature as if it was an extension (i.e. receiver is
+ * transformed to the first argument, and the callable kind becomes static). For Groovy properties,
+ * the name of the callable is also transformed from "foo" to "getFoo".
+ * It is possible to specify the custom name for the extension implementation, if needed.
+ */
+public @interface WithExtensionReferences {
+    Class<?> toClass();
+    String methodName() default "";
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/package-info.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/package-info.java
new file mode 100644
index 0000000..4474652
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/annotations/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * Annotations for generating build logic instrumentation.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.internal.instrumentation.api.annotations;
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/declarations/InterceptorDeclaration.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/declarations/InterceptorDeclaration.java
new file mode 100644
index 0000000..583d6a3
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/declarations/InterceptorDeclaration.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.instrumentation.api.declarations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class InterceptorDeclaration {
+    public static final String JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE = "org.gradle.internal.classpath.generated.InterceptorDeclaration_ConfigCacheJvmBytecode";
+    public static final String JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES = "org.gradle.internal.classpath.generated.InterceptorDeclaration_PropertyUpgradesJvmBytecode";
+    public static final String GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE = "org.gradle.internal.classpath.generated.InterceptorDeclaration_ConfigCacheGroovyInterceptors";
+    public static final String GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES = "org.gradle.internal.classpath.generated.InterceptorDeclaration_PropertyUpgradesGroovyInterceptors";
+
+    public static final List<String> JVM_BYTECODE_GENERATED_CLASS_NAMES = Collections.unmodifiableList(Arrays.asList(
+        JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE,
+        JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES
+    ));
+
+    public static final List<String> GROOVY_INTERCEPTORS_GENERATED_CLASS_NAMES = Collections.unmodifiableList(Arrays.asList(
+        GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_CONFIG_CACHE,
+        GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES
+    ));
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/jvmbytecode/JvmBytecodeCallInterceptor.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/jvmbytecode/JvmBytecodeCallInterceptor.java
new file mode 100644
index 0000000..b6dfc89
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/jvmbytecode/JvmBytecodeCallInterceptor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.instrumentation.api.jvmbytecode;
+
+import org.objectweb.asm.tree.MethodNode;
+
+import java.util.function.Supplier;
+
+public interface JvmBytecodeCallInterceptor {
+    boolean visitMethodInsn(
+            String className,
+            int opcode,
+            String owner,
+            String name,
+            String descriptor,
+            boolean isInterface,
+            Supplier<MethodNode> readMethodNode
+    );
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/metadata/InstrumentationMetadata.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/metadata/InstrumentationMetadata.java
new file mode 100644
index 0000000..9e7f5dd
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/metadata/InstrumentationMetadata.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.instrumentation.api.metadata;
+
+public interface InstrumentationMetadata {
+    boolean isInstanceOf(String type, String superType);
+}
diff --git a/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/metadata/package-info.java b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/metadata/package-info.java
new file mode 100644
index 0000000..48a7a26
--- /dev/null
+++ b/subprojects/internal-instrumentation-api/src/main/java/org/gradle/internal/instrumentation/api/metadata/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * Classes for instrumentation metadata.
+ */
+@org.gradle.api.NonNullApi
+package org.gradle.internal.instrumentation.api.metadata;
diff --git a/subprojects/internal-instrumentation-processor/build.gradle.kts b/subprojects/internal-instrumentation-processor/build.gradle.kts
new file mode 100644
index 0000000..464adf8
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/build.gradle.kts
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 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.
+ */
+
+plugins {
+    id("gradlebuild.distribution.api-java")
+}
+
+dependencies {
+    implementation(libs.javaPoet)
+
+    implementation(project(":internal-instrumentation-api"))
+    implementation(project(":base-services"))
+    implementation(project(":model-core"))
+
+    implementation(libs.asm)
+    implementation(libs.asmCommons)
+
+    testImplementation(libs.compileTesting)
+    testImplementation(project(":core"))
+}
+
+tasks.named<Test>("test").configure {
+    if (!javaVersion.isJava9Compatible) {
+        // For Java8 tools.jar is needed for com.google.testing.compile:compile-testing
+        classpath += javaLauncher.get().metadata.installationPath.files("lib/tools.jar")
+    } else {
+        // Needed for Java19 for com.google.testing.compile:compile-testing
+        jvmArgs(
+            "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+            "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
+            "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED"
+        )
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeAnnotatedMethodReader.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeAnnotatedMethodReader.java
new file mode 100644
index 0000000..47aa1d4
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeAnnotatedMethodReader.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2023 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.instrumentation.extensions.property;
+
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.file.RegularFileProperty;
+import org.gradle.api.provider.ListProperty;
+import org.gradle.api.provider.MapProperty;
+import org.gradle.api.provider.Property;
+import org.gradle.internal.instrumentation.api.annotations.UpgradedProperty;
+import org.gradle.internal.instrumentation.extensions.property.PropertyUpgradeRequestExtra.UpgradedPropertyType;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequestImpl;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.CallableInfoImpl;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.CallableOwnerInfo;
+import org.gradle.internal.instrumentation.model.CallableReturnTypeInfo;
+import org.gradle.internal.instrumentation.model.ImplementationInfoImpl;
+import org.gradle.internal.instrumentation.model.ParameterInfo;
+import org.gradle.internal.instrumentation.model.ParameterInfoImpl;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.gradle.internal.instrumentation.processor.extensibility.AnnotatedMethodReaderExtension;
+import org.gradle.internal.instrumentation.processor.modelreader.api.CallInterceptionRequestReader.Result.InvalidRequest;
+import org.gradle.internal.instrumentation.processor.modelreader.api.CallInterceptionRequestReader.Result.Success;
+import org.gradle.internal.instrumentation.processor.modelreader.impl.AnnotationUtils;
+import org.objectweb.asm.Type;
+
+import javax.annotation.Nonnull;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES;
+import static org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration.JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES;
+import static org.gradle.internal.instrumentation.model.CallableKindInfo.GROOVY_PROPERTY;
+import static org.gradle.internal.instrumentation.model.CallableKindInfo.INSTANCE_METHOD;
+import static org.gradle.internal.instrumentation.model.ParameterKindInfo.METHOD_PARAMETER;
+import static org.gradle.internal.instrumentation.model.ParameterKindInfo.RECEIVER;
+import static org.gradle.internal.instrumentation.processor.modelreader.impl.TypeUtils.extractType;
+
+public class PropertyUpgradeAnnotatedMethodReader implements AnnotatedMethodReaderExtension {
+
+    private static final Type DEFAULT_TYPE = Type.getType(UpgradedProperty.DefaultValue.class);
+
+    @Override
+    public Collection<Result> readRequest(ExecutableElement method) {
+        Optional<? extends AnnotationMirror> annotation = AnnotationUtils.findAnnotationMirror(method, UpgradedProperty.class);
+        if (!annotation.isPresent()) {
+            return Collections.emptySet();
+        }
+
+        try {
+            String propertyName = getPropertyName(method);
+            Type originalType = extractOriginalType(method, annotation.get());
+            AnnotationMirror annotationMirror = annotation.get();
+            boolean isFluentSetter = AnnotationUtils.findAnnotationValue(annotationMirror, "fluentSetter")
+                .map(v -> (Boolean) v.getValue())
+                .orElse(false);
+            CallInterceptionRequest groovyPropertyRequest = createGroovyPropertyInterceptionRequest(propertyName, method, originalType);
+            CallInterceptionRequest jvmGetterRequest = createJvmGetterInterceptionRequest(propertyName, method, originalType);
+            CallInterceptionRequest jvmSetterRequest = createJvmSetterInterceptionRequest(method, originalType, isFluentSetter);
+            return Arrays.asList(new Success(groovyPropertyRequest), new Success(jvmGetterRequest), new Success(jvmSetterRequest));
+        } catch (AnnotationReadFailure failure) {
+            return Collections.singletonList(new InvalidRequest(failure.reason));
+        }
+    }
+
+    private static Type extractOriginalType(ExecutableElement method, AnnotationMirror annotation) {
+        Optional<? extends AnnotationValue> annotationValue = AnnotationUtils.findAnnotationValue(annotation, "originalType");
+        Type type = annotationValue.map(v -> extractType((TypeMirror) v.getValue())).orElse(DEFAULT_TYPE);
+        if (!type.equals(DEFAULT_TYPE)) {
+            return type;
+        }
+        return extractOriginalTypeFromGeneric(method, method.getReturnType());
+    }
+
+    private static Type extractOriginalTypeFromGeneric(ExecutableElement method, TypeMirror typeMirror) {
+        String typeName = method.getReturnType() instanceof DeclaredType
+            ? ((DeclaredType) method.getReturnType()).asElement().toString()
+            : method.getReturnType().toString();
+        if (typeName.equals(RegularFileProperty.class.getName()) || typeName.equals(DirectoryProperty.class.getName())) {
+            return Type.getType(File.class);
+        } else if (typeName.equals(Property.class.getName()) && ((DeclaredType) typeMirror).getTypeArguments().size() == 1) {
+            return extractType(((DeclaredType) typeMirror).getTypeArguments().get(0));
+        } else if (typeName.equals(ConfigurableFileCollection.class.getName())) {
+            return Type.getType(FileCollection.class);
+        } else if (typeName.equals(MapProperty.class.getName())) {
+            return Type.getType(Map.class);
+        } else if (typeName.equals(ListProperty.class.getName())) {
+            return Type.getType(List.class);
+        } else {
+            throw new AnnotationReadFailure(String.format("Cannot extract original type for method '%s.%s: %s'. Use explicit @UpgradedProperty#originalType instead.", method.getEnclosingElement(), method, typeMirror));
+        }
+    }
+
+    private static CallInterceptionRequest createGroovyPropertyInterceptionRequest(String propertyName, ExecutableElement method, Type originalType) {
+        // TODO: Class name should be read from an annotation
+        List<RequestExtra> extras = Arrays.asList(new RequestExtra.OriginatingElement(method), new RequestExtra.InterceptGroovyCalls(GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES));
+        List<ParameterInfo> parameters = Collections.singletonList(new ParameterInfoImpl("receiver", extractType(method.getEnclosingElement().asType()), RECEIVER));
+        return new CallInterceptionRequestImpl(
+            extractCallableInfo(GROOVY_PROPERTY, method, originalType, propertyName, parameters),
+            extractImplementationInfo(method, originalType, "get", Collections.emptyList()),
+            extras
+        );
+    }
+
+    private static CallInterceptionRequest createJvmGetterInterceptionRequest(String propertyName, ExecutableElement method, Type originalType) {
+        List<RequestExtra> extras = getJvmRequestExtras(method, false);
+        String capitalize = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
+        String callableName = originalType.equals(Type.BOOLEAN_TYPE) ? "is" + capitalize : "get" + capitalize;
+        return new CallInterceptionRequestImpl(
+            extractCallableInfo(INSTANCE_METHOD, method, originalType, callableName, Collections.emptyList()),
+            extractImplementationInfo(method, originalType, "get", Collections.emptyList()),
+            extras
+        );
+    }
+
+    private static CallInterceptionRequest createJvmSetterInterceptionRequest(ExecutableElement method, Type originalType, boolean isFluentSetter) {
+        Type returnType = isFluentSetter ? extractType(method.getEnclosingElement().asType()) : Type.VOID_TYPE;
+        String callableName = method.getSimpleName().toString().replaceFirst("get", "set");
+        List<ParameterInfo> parameters = Collections.singletonList(new ParameterInfoImpl("arg0", originalType, METHOD_PARAMETER));
+        List<RequestExtra> extras = getJvmRequestExtras(method, isFluentSetter);
+        return new CallInterceptionRequestImpl(
+            extractCallableInfo(INSTANCE_METHOD, method, returnType, callableName, parameters),
+            extractImplementationInfo(method, returnType, "set", parameters),
+            extras
+        );
+    }
+
+    @Nonnull
+    private static List<RequestExtra> getJvmRequestExtras(ExecutableElement method, boolean isFluentSetter) {
+        List<RequestExtra> extras = new ArrayList<>();
+        extras.add(new RequestExtra.OriginatingElement(method));
+        // TODO: Class name should be read from an annotation
+        extras.add(new RequestExtra.InterceptJvmCalls(JVM_BYTECODE_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES));
+        String implementationClass = getGeneratedClassName(method.getEnclosingElement());
+        UpgradedPropertyType upgradedPropertyType = UpgradedPropertyType.from(extractType(method.getReturnType()));
+        extras.add(new PropertyUpgradeRequestExtra(isFluentSetter, implementationClass, method.getSimpleName().toString(), upgradedPropertyType));
+        return extras;
+    }
+
+    private static CallableInfo extractCallableInfo(CallableKindInfo kindInfo, ExecutableElement methodElement, Type returnType, String callableName, List<ParameterInfo> parameter) {
+        CallableOwnerInfo owner = new CallableOwnerInfo(extractType(methodElement.getEnclosingElement().asType()), true);
+        CallableReturnTypeInfo returnTypeInfo = new CallableReturnTypeInfo(returnType);
+        return new CallableInfoImpl(kindInfo, owner, callableName, returnTypeInfo, parameter);
+    }
+
+    private static ImplementationInfoImpl extractImplementationInfo(ExecutableElement method, Type returnType, String methodPrefix, List<ParameterInfo> parameters) {
+        Type owner = extractType(method.getEnclosingElement().asType());
+        Type implementationOwner = Type.getObjectType(getGeneratedClassName(method.getEnclosingElement()));
+        String implementationName = "access_" + methodPrefix + "_" + getPropertyName(method);
+        String implementationDescriptor = Type.getMethodDescriptor(returnType, toArray(owner, parameters));
+        return new ImplementationInfoImpl(implementationOwner, implementationName, implementationDescriptor);
+    }
+
+    private static String getGeneratedClassName(Element originalType) {
+        return "org.gradle.internal.classpath.generated." + originalType.getSimpleName() + "_Adapter";
+    }
+
+    private static Type[] toArray(Type owner, List<ParameterInfo> parameters) {
+        Type[] array = new Type[1 + parameters.size()];
+        array[0] = owner;
+        int i = 1;
+        for (ParameterInfo parameter : parameters) {
+            array[i++] = parameter.getParameterType();
+        }
+        return array;
+    }
+
+    private static String getPropertyName(ExecutableElement method) {
+        String property = method.getSimpleName().toString().replace("get", "");
+        return Character.toLowerCase(property.charAt(0)) + property.substring(1);
+    }
+
+    // TODO Consolidate with AnnotationCallInterceptionRequestReaderImpl#Failure
+    private static class AnnotationReadFailure extends RuntimeException {
+        final String reason;
+
+        private AnnotationReadFailure(String reason) {
+            this.reason = reason;
+        }
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeClassSourceGenerator.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeClassSourceGenerator.java
new file mode 100644
index 0000000..f97e90b
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeClassSourceGenerator.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2023 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.instrumentation.extensions.property;
+
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeSpec;
+import org.gradle.internal.instrumentation.extensions.property.PropertyUpgradeRequestExtra.UpgradedPropertyType;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.ImplementationInfo;
+import org.gradle.internal.instrumentation.processor.codegen.RequestGroupingInstrumentationClassSourceGenerator;
+
+import javax.lang.model.element.Modifier;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import static org.gradle.internal.instrumentation.processor.codegen.TypeUtils.typeName;
+
+public class PropertyUpgradeClassSourceGenerator extends RequestGroupingInstrumentationClassSourceGenerator {
+
+    private static final String SELF_PARAMETER_NAME = "self";
+
+    @Override
+    protected String classNameForRequest(CallInterceptionRequest request) {
+        return request.getRequestExtras().getByType(PropertyUpgradeRequestExtra.class)
+            .map(PropertyUpgradeRequestExtra::getImplementationClassName)
+            .orElse(null);
+    }
+
+    @Override
+    protected Consumer<TypeSpec.Builder> classContentForClass(
+        String className,
+        Collection<CallInterceptionRequest> requestsClassGroup,
+        Consumer<? super CallInterceptionRequest> onProcessedRequest,
+        Consumer<? super GenerationResult.HasFailures.FailureInfo> onFailure
+    ) {
+        List<MethodSpec> methods = requestsClassGroup.stream()
+            .map(request -> mapToMethodSpec(request, onProcessedRequest))
+            .collect(Collectors.toList());
+        return builder -> builder.addModifiers(Modifier.PUBLIC).addMethods(methods);
+    }
+
+    private static MethodSpec mapToMethodSpec(CallInterceptionRequest request, Consumer<? super CallInterceptionRequest> onProcessedRequest) {
+        PropertyUpgradeRequestExtra implementationExtra = request.getRequestExtras()
+            .getByType(PropertyUpgradeRequestExtra.class)
+            .orElseThrow(() -> new RuntimeException(PropertyUpgradeRequestExtra.class.getSimpleName() + " should be present at this stage!"));
+
+        CallableInfo callable = request.getInterceptedCallable();
+        ImplementationInfo implementation = request.getImplementationInfo();
+        List<ParameterSpec> parameters = callable.getParameters().stream()
+            .map(parameter -> ParameterSpec.builder(typeName(parameter.getParameterType()), parameter.getName()).build())
+            .collect(Collectors.toList());
+        onProcessedRequest.accept(request);
+        return MethodSpec.methodBuilder(implementation.getName())
+            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+            .addParameter(typeName(callable.getOwner().getType()), SELF_PARAMETER_NAME)
+            .addParameters(parameters)
+            .addCode(generateMethodBody(implementation, implementationExtra))
+            .returns(typeName(callable.getReturnType().getType()))
+            .build();
+    }
+
+    private static CodeBlock generateMethodBody(ImplementationInfo implementation, PropertyUpgradeRequestExtra implementationExtra) {
+        String propertyGetterName = implementationExtra.getInterceptedPropertyAccessorName();
+        boolean isSetter = implementation.getName().startsWith("access_set_");
+        UpgradedPropertyType upgradedPropertyType = implementationExtra.getUpgradedPropertyType();
+        if (isSetter) {
+            String setCall = getSetCall(upgradedPropertyType);
+            if (implementationExtra.isFluentSetter()) {
+                return CodeBlock.of("$N.$N()$N;\nreturn $N;", SELF_PARAMETER_NAME, propertyGetterName, setCall, SELF_PARAMETER_NAME);
+            } else {
+                return CodeBlock.of("$N.$N()$N;", SELF_PARAMETER_NAME, propertyGetterName, setCall);
+            }
+        } else {
+            String getCall = getGetCall(upgradedPropertyType);
+            return CodeBlock.of("return $N.$N()$N;", SELF_PARAMETER_NAME, propertyGetterName, getCall);
+        }
+    }
+
+    private static String getGetCall(UpgradedPropertyType upgradedPropertyType) {
+        switch (upgradedPropertyType) {
+            case FILE_SYSTEM_LOCATION_PROPERTY:
+                return ".getAsFile().get()";
+            case CONFIGURABLE_FILE_COLLECTION:
+                return "";
+            default:
+                return ".get()";
+        }
+    }
+
+    private static String getSetCall(UpgradedPropertyType upgradedPropertyType) {
+        switch (upgradedPropertyType) {
+            case FILE_SYSTEM_LOCATION_PROPERTY:
+                return ".fileValue(arg0)";
+            case CONFIGURABLE_FILE_COLLECTION:
+                return ".setFrom(arg0)";
+            default:
+                return ".set(arg0)";
+        }
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeRequestExtra.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeRequestExtra.java
new file mode 100644
index 0000000..fd8a919
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeRequestExtra.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 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.instrumentation.extensions.property;
+
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.file.RegularFileProperty;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.objectweb.asm.Type;
+
+class PropertyUpgradeRequestExtra implements RequestExtra {
+
+    enum UpgradedPropertyType {
+        PROPERTY,
+        FILE_SYSTEM_LOCATION_PROPERTY,
+        CONFIGURABLE_FILE_COLLECTION;
+
+        public static UpgradedPropertyType from(Type type) {
+            if (type.getClassName().equals(DirectoryProperty.class.getName()) || type.getClassName().equals(RegularFileProperty.class.getName())) {
+                return FILE_SYSTEM_LOCATION_PROPERTY;
+            } else if (type.getClassName().equals(ConfigurableFileCollection.class.getName())) {
+                return CONFIGURABLE_FILE_COLLECTION;
+            } else {
+                return PROPERTY;
+            }
+        }
+    }
+
+    private final boolean isFluentSetter;
+    private final String implementationClassName;
+    private final String interceptedPropertyAccessorName;
+    private final UpgradedPropertyType upgradedPropertyType;
+
+    public PropertyUpgradeRequestExtra(boolean isFluentSetter, String implementationClassName, String interceptedPropertyAccessorName, UpgradedPropertyType upgradedPropertyType) {
+        this.isFluentSetter = isFluentSetter;
+        this.implementationClassName = implementationClassName;
+        this.interceptedPropertyAccessorName = interceptedPropertyAccessorName;
+        this.upgradedPropertyType = upgradedPropertyType;
+    }
+
+    public String getImplementationClassName() {
+        return implementationClassName;
+    }
+
+    public String getInterceptedPropertyAccessorName() {
+        return interceptedPropertyAccessorName;
+    }
+
+    public UpgradedPropertyType getUpgradedPropertyType() {
+        return upgradedPropertyType;
+    }
+
+    public boolean isFluentSetter() {
+        return isFluentSetter;
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallInterceptionRequest.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallInterceptionRequest.java
new file mode 100644
index 0000000..180ec3f
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallInterceptionRequest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+public interface CallInterceptionRequest {
+    CallableInfo getInterceptedCallable();
+    ImplementationInfo getImplementationInfo();
+    RequestExtrasContainer getRequestExtras();
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallInterceptionRequestImpl.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallInterceptionRequestImpl.java
new file mode 100644
index 0000000..eef1f5f
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallInterceptionRequestImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import java.util.List;
+
+public class CallInterceptionRequestImpl implements CallInterceptionRequest {
+    private final CallableInfo interceptedCallable;
+    private final ImplementationInfo implementationInfo;
+    private final RequestExtrasContainer requestExtras;
+
+    public CallInterceptionRequestImpl(
+        CallableInfo interceptedCallable,
+        ImplementationInfo implementationInfo,
+        List<RequestExtra> requestExtras
+    ) {
+        this.interceptedCallable = interceptedCallable;
+        this.implementationInfo = implementationInfo;
+
+        this.requestExtras = new RequestExtrasContainer();
+        requestExtras.forEach(this.requestExtras::add);
+    }
+
+    @Override
+    public CallableInfo getInterceptedCallable() {
+        return interceptedCallable;
+    }
+
+    @Override
+    public ImplementationInfo getImplementationInfo() {
+        return implementationInfo;
+    }
+
+    @Override
+    public RequestExtrasContainer getRequestExtras() {
+        return requestExtras;
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableInfo.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableInfo.java
new file mode 100644
index 0000000..7464253
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableInfo.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import java.util.List;
+
+public interface CallableInfo {
+    CallableKindInfo getKind();
+    CallableOwnerInfo getOwner();
+    String getCallableName();
+    CallableReturnTypeInfo getReturnType();
+    List<ParameterInfo> getParameters();
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableInfoImpl.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableInfoImpl.java
new file mode 100644
index 0000000..5d59feb
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableInfoImpl.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import java.util.List;
+
+public class CallableInfoImpl implements CallableInfo {
+    private final CallableKindInfo kind;
+    private final CallableOwnerInfo owner;
+    private final String callableName;
+    private final CallableReturnTypeInfo returnType;
+    private final List<ParameterInfo> parameters;
+
+    public CallableInfoImpl(CallableKindInfo kind, CallableOwnerInfo owner, String callableName, CallableReturnTypeInfo returnType, List<ParameterInfo> parameters) {
+        this.kind = kind;
+        this.owner = owner;
+        this.callableName = callableName;
+        this.returnType = returnType;
+        this.parameters = parameters;
+    }
+
+    @Override
+    public CallableKindInfo getKind() {
+        return kind;
+    }
+
+    @Override
+    public CallableOwnerInfo getOwner() {
+        return owner;
+    }
+
+    @Override
+    public String getCallableName() {
+        return callableName;
+    }
+
+    @Override
+    public CallableReturnTypeInfo getReturnType() {
+        return returnType;
+    }
+
+    @Override
+    public List<ParameterInfo> getParameters() {
+        return parameters;
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableKindInfo.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableKindInfo.java
new file mode 100644
index 0000000..cb2f989
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableKindInfo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.gradle.internal.instrumentation.api.annotations.CallableKind;
+
+import java.lang.annotation.Annotation;
+
+public enum CallableKindInfo {
+    STATIC_METHOD, INSTANCE_METHOD, AFTER_CONSTRUCTOR, GROOVY_PROPERTY;
+
+    public static CallableKindInfo fromAnnotation(Annotation annotation) {
+        if (annotation instanceof CallableKind.StaticMethod) {
+            return STATIC_METHOD;
+        }
+        if (annotation instanceof CallableKind.InstanceMethod) {
+            return INSTANCE_METHOD;
+        }
+        if (annotation instanceof CallableKind.AfterConstructor) {
+            return AFTER_CONSTRUCTOR;
+        }
+        if (annotation instanceof CallableKind.GroovyProperty) {
+            return GROOVY_PROPERTY;
+        }
+        throw new IllegalArgumentException("Unexpected annotation " + annotation);
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableOwnerInfo.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableOwnerInfo.java
new file mode 100644
index 0000000..1686048
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableOwnerInfo.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.objectweb.asm.Type;
+
+public class CallableOwnerInfo {
+    private final Type type;
+    private final boolean interceptSubtypes;
+
+    public CallableOwnerInfo(Type type, boolean interceptSubtypes) {
+        this.type = type;
+        this.interceptSubtypes = interceptSubtypes;
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public boolean isInterceptSubtypes() {
+        return interceptSubtypes;
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableReturnTypeInfo.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableReturnTypeInfo.java
new file mode 100644
index 0000000..66d0761
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/CallableReturnTypeInfo.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.objectweb.asm.Type;
+
+public class CallableReturnTypeInfo {
+
+    private final Type type;
+
+    public CallableReturnTypeInfo(Type type) {
+        this.type = type;
+    }
+
+    public Type getType() {
+        return type;
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ImplementationInfo.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ImplementationInfo.java
new file mode 100644
index 0000000..a6ef03d
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ImplementationInfo.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.objectweb.asm.Type;
+
+public interface ImplementationInfo {
+    Type getOwner();
+    String getName();
+    String getDescriptor();
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ImplementationInfoImpl.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ImplementationInfoImpl.java
new file mode 100644
index 0000000..659313b
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ImplementationInfoImpl.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.objectweb.asm.Type;
+
+public class ImplementationInfoImpl implements ImplementationInfo {
+    private final Type owner;
+    private final String name;
+    private final String descriptor;
+
+    public ImplementationInfoImpl(Type owner, String name, String descriptor) {
+        this.owner = owner;
+        this.name = name;
+        this.descriptor = descriptor;
+    }
+
+    @Override
+    public Type getOwner() {
+        return owner;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public String getDescriptor() {
+        return descriptor;
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterInfo.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterInfo.java
new file mode 100644
index 0000000..15459cd
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterInfo.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.objectweb.asm.Type;
+
+public interface ParameterInfo {
+    String getName();
+    Type getParameterType();
+    ParameterKindInfo getKind();
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterInfoImpl.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterInfoImpl.java
new file mode 100644
index 0000000..ffd0b47
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterInfoImpl.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.objectweb.asm.Type;
+
+public class ParameterInfoImpl implements ParameterInfo {
+    private final String name;
+    private final Type parameterType;
+    private final ParameterKindInfo kind;
+
+    public ParameterInfoImpl(String name, Type parameterType, ParameterKindInfo kind) {
+        this.name = name;
+        this.parameterType = parameterType;
+        this.kind = kind;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Type getParameterType() {
+        return parameterType;
+    }
+
+    @Override
+    public ParameterKindInfo getKind() {
+        return kind;
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterKindInfo.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterKindInfo.java
new file mode 100644
index 0000000..0eecab9
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/ParameterKindInfo.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind;
+
+import java.lang.annotation.Annotation;
+
+public enum ParameterKindInfo {
+    RECEIVER, METHOD_PARAMETER, VARARG_METHOD_PARAMETER, CALLER_CLASS_NAME, KOTLIN_DEFAULT_MASK;
+
+    public boolean isSourceParameter() {
+        return this == METHOD_PARAMETER || this == VARARG_METHOD_PARAMETER;
+    }
+
+    public static ParameterKindInfo fromAnnotation(Annotation annotation) {
+        if (annotation instanceof ParameterKind.Receiver) {
+            return RECEIVER;
+        }
+        if (annotation instanceof ParameterKind.CallerClassName) {
+            return CALLER_CLASS_NAME;
+        }
+        if (annotation instanceof ParameterKind.KotlinDefaultMask) {
+            return KOTLIN_DEFAULT_MASK;
+        }
+        if (annotation instanceof ParameterKind.VarargParameter) {
+            return VARARG_METHOD_PARAMETER;
+        }
+        throw new IllegalArgumentException("Unexpected annotation " + annotation);
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/RequestExtra.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/RequestExtra.java
new file mode 100644
index 0000000..e07f23d
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/RequestExtra.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import javax.lang.model.element.ExecutableElement;
+
+public interface RequestExtra {
+    class InterceptJvmCalls implements RequestExtra {
+        private final String implementationClassName;
+
+        public String getImplementationClassName() {
+            return implementationClassName;
+        }
+
+        public InterceptJvmCalls(String implementationClassName) {
+            this.implementationClassName = implementationClassName;
+        }
+    }
+
+    class InterceptGroovyCalls implements RequestExtra {
+        private final String implementationClassName;
+
+        public String getImplementationClassName() {
+            return implementationClassName;
+        }
+
+        public InterceptGroovyCalls(String implementationClassName) {
+            this.implementationClassName = implementationClassName;
+        }
+    }
+
+    class OriginatingElement implements RequestExtra {
+        private final ExecutableElement element;
+
+        public ExecutableElement getElement() {
+            return element;
+        }
+
+        public OriginatingElement(ExecutableElement element) {
+            this.element = element;
+        }
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/RequestExtrasContainer.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/RequestExtrasContainer.java
new file mode 100644
index 0000000..3186efa
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/model/RequestExtrasContainer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 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.instrumentation.model;
+
+import org.gradle.internal.Cast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+public class RequestExtrasContainer {
+    private final List<RequestExtra> extras = new ArrayList<>();
+
+    public List<RequestExtra> getAll() {
+        return Collections.unmodifiableList(extras);
+    }
+
+    public <T> Optional<T> getByType(Class<T> type) {
+        return Cast.uncheckedCast(extras.stream().filter(type::isInstance).findFirst());
+    }
+
+    public void add(RequestExtra extra) {
+        extras.add(extra);
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/AbstractInstrumentationProcessor.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/AbstractInstrumentationProcessor.java
new file mode 100644
index 0000000..f9949c2
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/AbstractInstrumentationProcessor.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2023 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.instrumentation.processor;
+
+import org.gradle.internal.Cast;
+import org.gradle.internal.instrumentation.api.annotations.VisitForInstrumentation;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.processor.codegen.CompositeInstrumentationCodeGenerator;
+import org.gradle.internal.instrumentation.processor.codegen.InstrumentationCodeGeneratorHost;
+import org.gradle.internal.instrumentation.processor.extensibility.AnnotatedMethodReaderExtension;
+import org.gradle.internal.instrumentation.processor.extensibility.ClassLevelAnnotationsContributor;
+import org.gradle.internal.instrumentation.processor.extensibility.CodeGeneratorContributor;
+import org.gradle.internal.instrumentation.processor.extensibility.InstrumentationProcessorExtension;
+import org.gradle.internal.instrumentation.processor.extensibility.RequestPostProcessorExtension;
+import org.gradle.internal.instrumentation.processor.modelreader.api.CallInterceptionRequestReader;
+import org.gradle.internal.instrumentation.processor.modelreader.impl.AnnotationUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@SupportedSourceVersion(SourceVersion.RELEASE_8)
+public abstract class AbstractInstrumentationProcessor extends AbstractProcessor {
+
+    protected abstract Collection<InstrumentationProcessorExtension> getExtensions();
+
+    @Override
+    public Set<String> getSupportedOptions() {
+        return Collections.singleton("org.gradle.annotation.processing.aggregating");
+    }
+
+    @Override
+    public Set<String> getSupportedAnnotationTypes() {
+        return getSupportedAnnotations().stream().map(Class::getName).collect(Collectors.toSet());
+    }
+
+    private Set<Class<? extends Annotation>> getSupportedAnnotations() {
+        return getExtensionsByType(ClassLevelAnnotationsContributor.class).stream()
+            .flatMap(it -> it.contributeClassLevelAnnotationTypes().stream())
+            .collect(Collectors.toSet());
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        Stream<? extends Element> annotatedTypes = getSupportedAnnotations().stream()
+            .flatMap(annotation -> roundEnv.getElementsAnnotatedWith(annotation).stream())
+            .flatMap(element -> findActualTypesToVisit(element).stream())
+            .sorted(Comparator.comparing(AbstractInstrumentationProcessor::elementQualifiedName));
+        collectAndProcessRequests(annotatedTypes);
+        return true;
+    }
+
+    private Set<Element> findActualTypesToVisit(Element typeElement) {
+        Optional<? extends AnnotationMirror> annotationMirror = AnnotationUtils.findAnnotationMirror(typeElement, VisitForInstrumentation.class);
+        if (!annotationMirror.isPresent()) {
+            return Collections.singleton(typeElement);
+        }
+
+        @SuppressWarnings("unchecked")
+        List<AnnotationValue> values = (List<AnnotationValue>) AnnotationUtils.findAnnotationValue(annotationMirror.get(), "value")
+            .orElseThrow(() -> new IllegalStateException("missing annotation value"))
+            .getValue();
+        return values.stream()
+            .map(v -> processingEnv.getTypeUtils().asElement((TypeMirror) v.getValue()))
+            .collect(Collectors.toSet());
+    }
+
+    private <T extends InstrumentationProcessorExtension> Collection<T> getExtensionsByType(Class<T> type) {
+        return Cast.uncheckedCast(getExtensions().stream().filter(type::isInstance).collect(Collectors.toList()));
+    }
+
+    private void collectAndProcessRequests(Stream<? extends Element> annotatedElements) {
+        Collection<AnnotatedMethodReaderExtension> readers = getExtensionsByType(AnnotatedMethodReaderExtension.class);
+
+        List<ExecutableElement> allMethodElementsInAnnotatedClasses = getExecutableElementsFromAnnotatedElements(annotatedElements);
+
+        Map<ExecutableElement, List<CallInterceptionRequestReader.Result.InvalidRequest>> errors = new LinkedHashMap<>();
+        List<CallInterceptionRequestReader.Result.Success> successResults = new ArrayList<>();
+        readRequests(readers, allMethodElementsInAnnotatedClasses, errors, successResults);
+
+        if (!errors.isEmpty()) {
+            Messager messager = processingEnv.getMessager();
+            errors.forEach((element, elementErrors) -> elementErrors.forEach(error -> messager.printMessage(Diagnostic.Kind.ERROR, error.reason, element)));
+            return;
+        }
+
+        List<CallInterceptionRequest> requests = postProcessRequests(successResults);
+
+        runCodeGeneration(requests);
+    }
+
+    @Nonnull
+    private static List<ExecutableElement> getExecutableElementsFromAnnotatedElements(Stream<? extends Element> annotatedClassElements) {
+        return annotatedClassElements
+            .flatMap(element -> element.getKind() == ElementKind.METHOD ? Stream.of(element) : element.getEnclosedElements().stream())
+            .filter(it -> it.getKind() == ElementKind.METHOD)
+            .map(it -> (ExecutableElement) it)
+            // Ensure that the elements have a stable order, as the annotation processing engine does not guarantee that for type elements.
+            // The order in which the executable elements are listed should be the order in which they appear in the code but
+            // we take an extra measure of care here and ensure the ordering between all elements.
+            .sorted(Comparator.comparing(AbstractInstrumentationProcessor::elementQualifiedName))
+            .distinct()
+            .collect(Collectors.toList());
+    }
+
+    private static void readRequests(Collection<AnnotatedMethodReaderExtension> readers, List<ExecutableElement> allMethodElementsInAnnotatedClasses, Map<ExecutableElement, List<CallInterceptionRequestReader.Result.InvalidRequest>> errors, List<CallInterceptionRequestReader.Result.Success> successResults) {
+        for (ExecutableElement methodElement : allMethodElementsInAnnotatedClasses) {
+            for (AnnotatedMethodReaderExtension reader : readers) {
+                Collection<CallInterceptionRequestReader.Result> readerResults = reader.readRequest(methodElement);
+                for (CallInterceptionRequestReader.Result readerResult : readerResults) {
+                    if (readerResult instanceof CallInterceptionRequestReader.Result.InvalidRequest) {
+                        errors.computeIfAbsent(methodElement, key -> new ArrayList<>()).add((CallInterceptionRequestReader.Result.InvalidRequest) readerResult);
+                    } else {
+                        successResults.add((CallInterceptionRequestReader.Result.Success) readerResult);
+                    }
+                }
+            }
+        }
+    }
+
+    @Nonnull
+    private List<CallInterceptionRequest> postProcessRequests(List<CallInterceptionRequestReader.Result.Success> successResults) {
+        List<CallInterceptionRequest> requests = successResults.stream().map(CallInterceptionRequestReader.Result.Success::getRequest).collect(Collectors.toList());
+        for (RequestPostProcessorExtension postProcessor : getExtensionsByType(RequestPostProcessorExtension.class)) {
+            requests = requests.stream().flatMap(request -> postProcessor.postProcessRequest(request).stream()).collect(Collectors.toList());
+        }
+        return requests;
+    }
+
+    private void runCodeGeneration(List<CallInterceptionRequest> requests) {
+        InstrumentationCodeGeneratorHost generatorHost = new InstrumentationCodeGeneratorHost(processingEnv.getFiler(),
+            processingEnv.getMessager(),
+            new CompositeInstrumentationCodeGenerator(
+                getExtensionsByType(CodeGeneratorContributor.class).stream().map(CodeGeneratorContributor::contributeCodeGenerator).collect(Collectors.toList())
+            )
+        );
+
+        generatorHost.generateCodeForRequestedInterceptors(requests);
+    }
+
+    private static String elementQualifiedName(Element element) {
+        if (element instanceof ExecutableElement) {
+            String enclosingTypeName = ((TypeElement) element.getEnclosingElement()).getQualifiedName().toString();
+            return enclosingTypeName + "." + element.getSimpleName();
+        } else if (element instanceof TypeElement) {
+            return ((TypeElement) element).getQualifiedName().toString();
+        } else {
+            throw new IllegalArgumentException("Unsupported element type to read qualified name from: " + element.getClass());
+        }
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/AddGeneratedClassNameFlagFromClassLevelAnnotation.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/AddGeneratedClassNameFlagFromClassLevelAnnotation.java
new file mode 100644
index 0000000..a98c9a7
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/AddGeneratedClassNameFlagFromClassLevelAnnotation.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2023 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.instrumentation.processor;
+
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.gradle.internal.instrumentation.model.RequestExtra.OriginatingElement;
+import org.gradle.internal.instrumentation.processor.extensibility.RequestPostProcessorExtension;
+import org.gradle.internal.instrumentation.processor.modelreader.impl.AnnotationUtils;
+
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class AddGeneratedClassNameFlagFromClassLevelAnnotation implements RequestPostProcessorExtension {
+    public AddGeneratedClassNameFlagFromClassLevelAnnotation(
+        Predicate<? super CallInterceptionRequest> shouldAddExtraToRequestPredicate,
+        Class<? extends Annotation> generatedClassNameProvidingAnnotation,
+        Function<String, RequestExtra> produceFlagForGeneratedClassName
+    ) {
+        this.shouldAddExtraToRequestPredicate = shouldAddExtraToRequestPredicate;
+
+        this.generatedClassNameProvidingAnnotation = generatedClassNameProvidingAnnotation;
+        this.produceFlagForGeneratedClassName = produceFlagForGeneratedClassName;
+    }
+
+    private final Predicate<? super CallInterceptionRequest> shouldAddExtraToRequestPredicate;
+    private final Class<? extends Annotation> generatedClassNameProvidingAnnotation;
+    private final Function<String, RequestExtra> produceFlagForGeneratedClassName;
+
+    @Override
+    public Collection<CallInterceptionRequest> postProcessRequest(CallInterceptionRequest originalRequest) {
+        Optional<ExecutableElement> maybeOriginatingElement = originalRequest.getRequestExtras().getByType(OriginatingElement.class)
+            .map(OriginatingElement::getElement);
+
+        if (!maybeOriginatingElement.isPresent()) {
+            return Collections.singletonList(originalRequest);
+        }
+
+        boolean shouldPostProcess = shouldAddExtraToRequestPredicate.test(originalRequest);
+        if (!shouldPostProcess) {
+            return Collections.singletonList(originalRequest);
+        }
+
+        Element enclosingElement = maybeOriginatingElement.get().getEnclosingElement();
+        AnnotationUtils.findAnnotationMirror(enclosingElement, generatedClassNameProvidingAnnotation).ifPresent(annotationMirror -> {
+            Optional<? extends AnnotationValue> generatedClassName = AnnotationUtils.findAnnotationValue(annotationMirror, "generatedClassName");
+            generatedClassName.ifPresent(annotationValue -> originalRequest.getRequestExtras().add(produceFlagForGeneratedClassName.apply((String) annotationValue.getValue())));
+        });
+
+        return Collections.singletonList(originalRequest);
+    }
+
+    public static Predicate<CallInterceptionRequest> ifHasAnnotation(Class<? extends Annotation> annotationType) {
+        return request -> {
+            Optional<ExecutableElement> maybeOriginatingElement = request.getRequestExtras()
+                .getByType(OriginatingElement.class)
+                .map(OriginatingElement::getElement);
+
+            if (!maybeOriginatingElement.isPresent()) {
+                return false;
+            }
+
+            ExecutableElement originatingElement = maybeOriginatingElement.get();
+            return AnnotationUtils.findMetaAnnotationMirror(originatingElement, annotationType).isPresent();
+        };
+    }
+
+    public static Predicate<CallInterceptionRequest> ifHasExtraOfType(Class<? extends RequestExtra> extraType) {
+        return request -> request.getRequestExtras().getByType(extraType).isPresent();
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/ConfigurationCacheInstrumentationProcessor.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/ConfigurationCacheInstrumentationProcessor.java
new file mode 100644
index 0000000..1da79c0
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/ConfigurationCacheInstrumentationProcessor.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023 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.instrumentation.processor;
+
+import org.gradle.internal.instrumentation.api.annotations.InterceptGroovyCalls;
+import org.gradle.internal.instrumentation.api.annotations.InterceptJvmCalls;
+import org.gradle.internal.instrumentation.api.annotations.SpecificGroovyCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.SpecificJvmCallInterceptors;
+import org.gradle.internal.instrumentation.api.annotations.VisitForInstrumentation;
+import org.gradle.internal.instrumentation.extensions.property.PropertyUpgradeAnnotatedMethodReader;
+import org.gradle.internal.instrumentation.extensions.property.PropertyUpgradeClassSourceGenerator;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.gradle.internal.instrumentation.processor.codegen.groovy.InterceptGroovyCallsGenerator;
+import org.gradle.internal.instrumentation.processor.codegen.jvmbytecode.InterceptJvmCallsGenerator;
+import org.gradle.internal.instrumentation.processor.extensibility.ClassLevelAnnotationsContributor;
+import org.gradle.internal.instrumentation.processor.extensibility.CodeGeneratorContributor;
+import org.gradle.internal.instrumentation.processor.extensibility.InstrumentationProcessorExtension;
+import org.gradle.internal.instrumentation.processor.features.withstaticreference.WithExtensionReferencesExtra;
+import org.gradle.internal.instrumentation.processor.features.withstaticreference.WithExtensionReferencesPostProcessor;
+import org.gradle.internal.instrumentation.processor.features.withstaticreference.WithExtensionReferencesReader;
+import org.gradle.internal.instrumentation.processor.modelreader.impl.AnnotationCallInterceptionRequestReaderImpl;
+
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.gradle.internal.instrumentation.processor.AddGeneratedClassNameFlagFromClassLevelAnnotation.ifHasAnnotation;
+import static org.gradle.internal.instrumentation.processor.AddGeneratedClassNameFlagFromClassLevelAnnotation.ifHasExtraOfType;
+
+@SupportedSourceVersion(SourceVersion.RELEASE_8)
+public class ConfigurationCacheInstrumentationProcessor extends AbstractInstrumentationProcessor {
+
+    @Override
+    protected Collection<InstrumentationProcessorExtension> getExtensions() {
+        return Arrays.asList(
+            (ClassLevelAnnotationsContributor) () -> Arrays.asList(SpecificJvmCallInterceptors.class, SpecificGroovyCallInterceptors.class, VisitForInstrumentation.class),
+
+            new AnnotationCallInterceptionRequestReaderImpl(),
+
+            new WithExtensionReferencesReader(),
+            new WithExtensionReferencesPostProcessor(),
+            new AddGeneratedClassNameFlagFromClassLevelAnnotation(
+                ifHasExtraOfType(WithExtensionReferencesExtra.ProducedSynthetically.class), SpecificJvmCallInterceptors.class, RequestExtra.InterceptJvmCalls::new
+            ),
+
+            new AddGeneratedClassNameFlagFromClassLevelAnnotation(ifHasAnnotation(InterceptJvmCalls.class), SpecificJvmCallInterceptors.class, RequestExtra.InterceptJvmCalls::new),
+            new AddGeneratedClassNameFlagFromClassLevelAnnotation(ifHasAnnotation(InterceptGroovyCalls.class), SpecificGroovyCallInterceptors.class, RequestExtra.InterceptGroovyCalls::new),
+
+            (CodeGeneratorContributor) InterceptJvmCallsGenerator::new,
+            (CodeGeneratorContributor) InterceptGroovyCallsGenerator::new,
+
+            // Properties upgrade extensions
+            new PropertyUpgradeAnnotatedMethodReader(),
+            (CodeGeneratorContributor) PropertyUpgradeClassSourceGenerator::new
+        );
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/CompositeInstrumentationCodeGenerator.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/CompositeInstrumentationCodeGenerator.java
new file mode 100644
index 0000000..26dec51
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/CompositeInstrumentationCodeGenerator.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen;
+
+import com.squareup.javapoet.TypeSpec;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.processor.codegen.InstrumentationCodeGenerator.GenerationResult.CanGenerateClasses;
+import org.gradle.internal.instrumentation.processor.codegen.InstrumentationCodeGenerator.GenerationResult.HasFailures;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class CompositeInstrumentationCodeGenerator implements InstrumentationCodeGenerator {
+
+    private final Collection<InstrumentationCodeGenerator> generators;
+
+    public CompositeInstrumentationCodeGenerator(Collection<InstrumentationCodeGenerator> generators) {
+        this.generators = generators;
+    }
+
+    @Override
+    public GenerationResult generateCodeForRequestedInterceptors(Collection<CallInterceptionRequest> interceptionRequests) {
+        List<GenerationResult> results = generators.stream().map(generators -> generators.generateCodeForRequestedInterceptors(interceptionRequests)).collect(Collectors.toList());
+
+        List<HasFailures> failures = results.stream().filter(it -> it instanceof HasFailures).map(it -> (HasFailures) it).collect(Collectors.toList());
+        if (!failures.isEmpty()) {
+            return new HasFailures(failures.stream().flatMap(it -> it.getFailureDetails().stream()).collect(Collectors.toList()));
+        }
+
+        List<CanGenerateClasses> generatingResults = results.stream().map(it -> (CanGenerateClasses) it).collect(Collectors.toList());
+        Map<String, CanGenerateClasses> generatorByClassName = new LinkedHashMap<>();
+        generatingResults.forEach(result -> result.getClassNames().forEach(className -> {
+            if (generatorByClassName.put(className, result) != null) {
+                throw new IllegalStateException("multiple code generators for class name " + className);
+            }
+        }));
+
+        return new CanGenerateClasses() {
+            @Override
+            public Collection<String> getClassNames() {
+                return generatingResults.stream().flatMap(it -> it.getClassNames().stream()).collect(Collectors.toCollection(LinkedHashSet::new));
+            }
+
+            @Override
+            public void buildType(String className, TypeSpec.Builder builder) {
+                generatorByClassName.get(className).buildType(className, builder);
+            }
+
+            @Override
+            public List<CallInterceptionRequest> getCoveredRequests() {
+                return generatingResults.stream().flatMap(it -> it.getCoveredRequests().stream()).distinct().collect(Collectors.toList());
+            }
+        };
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/InstrumentationCodeGenerator.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/InstrumentationCodeGenerator.java
new file mode 100644
index 0000000..c738fb3
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/InstrumentationCodeGenerator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen;
+
+import com.squareup.javapoet.TypeSpec;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.List;
+
+public interface InstrumentationCodeGenerator {
+
+    GenerationResult generateCodeForRequestedInterceptors(
+        Collection<CallInterceptionRequest> interceptionRequests
+    );
+
+    interface GenerationResult {
+        interface CanGenerateClasses extends GenerationResult {
+            Collection<String> getClassNames();
+            void buildType(String className, TypeSpec.Builder builder);
+            Collection<CallInterceptionRequest> getCoveredRequests();
+        }
+
+        class HasFailures implements GenerationResult {
+            private final List<FailureInfo> failures;
+
+            public HasFailures(List<FailureInfo> failures) {
+                this.failures = failures;
+            }
+
+            public List<FailureInfo> getFailureDetails() {
+                return failures;
+            }
+
+            public static class FailureInfo {
+                @Nullable
+                final CallInterceptionRequest request;
+                final String reason;
+
+                public FailureInfo(@Nullable CallInterceptionRequest request, String reason) {
+                    this.request = request;
+                    this.reason = reason;
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/InstrumentationCodeGeneratorHost.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/InstrumentationCodeGeneratorHost.java
new file mode 100644
index 0000000..83f42e0
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/InstrumentationCodeGeneratorHost.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.RequestExtra.OriginatingElement;
+import org.gradle.internal.instrumentation.processor.codegen.InstrumentationCodeGenerator.GenerationResult.CanGenerateClasses;
+import org.gradle.internal.instrumentation.processor.codegen.InstrumentationCodeGenerator.GenerationResult.HasFailures;
+
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.ExecutableElement;
+import javax.tools.Diagnostic;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class InstrumentationCodeGeneratorHost {
+    private final Filer filer;
+    private final Messager messager;
+    private final InstrumentationCodeGenerator codeGenerator;
+
+    public InstrumentationCodeGeneratorHost(
+        Filer filer,
+        Messager messager,
+        InstrumentationCodeGenerator codeGenerator
+    ) {
+        this.filer = filer;
+        this.messager = messager;
+        this.codeGenerator = codeGenerator;
+    }
+
+    public void generateCodeForRequestedInterceptors(
+        Collection<CallInterceptionRequest> interceptionRequests
+    ) {
+        InstrumentationCodeGenerator.GenerationResult result = codeGenerator.generateCodeForRequestedInterceptors(interceptionRequests);
+        if (result instanceof CanGenerateClasses) {
+            for (String canonicalClassName : ((CanGenerateClasses) result).getClassNames()) {
+                ClassName className = ClassName.bestGuess(canonicalClassName);
+                TypeSpec.Builder builder = TypeSpec.classBuilder(className);
+                CanGenerateClasses generateType = (CanGenerateClasses) result;
+                getOriginatingElements(generateType).forEach(builder::addOriginatingElement);
+                generateType.buildType(canonicalClassName, builder);
+                TypeSpec generatedType = builder.build();
+                JavaFile javaFile = JavaFile.builder(className.packageName(), generatedType).indent("    ").build();
+                try {
+                    javaFile.writeTo(filer);
+                } catch (IOException e) {
+                    messager.printMessage(Diagnostic.Kind.ERROR, "Failed to write generated source file in package " + className.packageName() + ", named " + generatedType.name);
+                }
+            }
+        } else if (result instanceof HasFailures) {
+            HasFailures failure = (HasFailures) result;
+            failure.getFailureDetails().forEach(details -> {
+                Optional<ExecutableElement> maybeOriginatingElement =
+                    Optional.ofNullable(details.request)
+                        .flatMap(presentRequest -> presentRequest.getRequestExtras().getByType(OriginatingElement.class).map(OriginatingElement::getElement));
+                if (maybeOriginatingElement.isPresent()) {
+                    messager.printMessage(Diagnostic.Kind.ERROR, details.reason, maybeOriginatingElement.get());
+                } else {
+                    messager.printMessage(Diagnostic.Kind.ERROR, details.reason);
+                }
+            });
+        }
+    }
+
+    private static Set<ExecutableElement> getOriginatingElements(CanGenerateClasses result) {
+        return result.getCoveredRequests().stream().map(requests ->
+            requests.getRequestExtras().getByType(OriginatingElement.class).map(OriginatingElement::getElement).orElse(null)
+        ).filter(Objects::nonNull).collect(Collectors.toSet());
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/JavadocUtils.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/JavadocUtils.java
new file mode 100644
index 0000000..2747caa
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/JavadocUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.ParameterInfo;
+import org.gradle.internal.instrumentation.model.ParameterKindInfo;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.gradle.internal.instrumentation.processor.codegen.TypeUtils.typeName;
+
+public class JavadocUtils {
+
+    public static String callableKindForJavadoc(CallInterceptionRequest request) {
+        CallableInfo interceptedCallable = request.getInterceptedCallable();
+        return interceptedCallable.getKind() == CallableKindInfo.STATIC_METHOD ? "static method" :
+            interceptedCallable.getKind() == CallableKindInfo.INSTANCE_METHOD ? "instance method" :
+                interceptedCallable.getKind() == CallableKindInfo.AFTER_CONSTRUCTOR ? "constructor (getting notified after it)" :
+                    interceptedCallable.getKind() == CallableKindInfo.GROOVY_PROPERTY ? "Groovy property getter" : null;
+    }
+
+    public static CodeBlock interceptedCallableLink(CallInterceptionRequest request) {
+        CodeBlock.Builder result = CodeBlock.builder();
+        CallableInfo interceptedCallable = request.getInterceptedCallable();
+        ClassName className = ClassName.bestGuess(interceptedCallable.getOwner().getType().getClassName());
+        String callableNameForDocComment = interceptedCallable.getKind() == CallableKindInfo.AFTER_CONSTRUCTOR ? className.simpleName() : interceptedCallable.getCallableName();
+        List<ParameterInfo> params = request.getInterceptedCallable().getParameters();
+        List<ParameterInfo> methodParameters = params.stream().filter(parameter -> parameter.getKind().isSourceParameter()).collect(Collectors.toList());
+        result.add("{@link $L#$L", className, callableNameForDocComment);
+        if (interceptedCallable.getKind() != CallableKindInfo.GROOVY_PROPERTY) {
+            result.add("(");
+            methodParameters.forEach(parameter -> {
+                result.add("$L", parameterTypeForJavadoc(parameter, true));
+                if (parameter != methodParameters.get(methodParameters.size() - 1)) {
+                    result.add(", ");
+                }
+            });
+            result.add(")");
+        }
+        result.add("}");
+        return result.build();
+    }
+
+    public static CodeBlock interceptorImplementationLink(CallInterceptionRequest request) {
+        CodeBlock.Builder result = CodeBlock.builder();
+        List<ParameterInfo> params = request.getInterceptedCallable().getParameters();
+        result.add("{@link $T#$L(", ClassName.bestGuess(request.getImplementationInfo().getOwner().getClassName()), request.getImplementationInfo().getName());
+        params.forEach(parameter -> {
+            result.add("$L", parameterTypeForJavadoc(parameter, false));
+            if (parameter != params.get(params.size() - 1)) {
+                result.add(", ");
+            }
+        });
+        result.add(")}");
+        return result.build();
+    }
+
+    private static CodeBlock parameterTypeForJavadoc(ParameterInfo parameterInfo, boolean renderVararg) {
+        if (parameterInfo.getKind() == ParameterKindInfo.VARARG_METHOD_PARAMETER && renderVararg) {
+            return CodeBlock.of("$T...", typeName(parameterInfo.getParameterType().getElementType()));
+        }
+        return CodeBlock.of("$T", typeName(parameterInfo.getParameterType()));
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/RequestGroupingInstrumentationClassSourceGenerator.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/RequestGroupingInstrumentationClassSourceGenerator.java
new file mode 100644
index 0000000..80f27af
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/RequestGroupingInstrumentationClassSourceGenerator.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen;
+
+import com.squareup.javapoet.TypeSpec;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public abstract class RequestGroupingInstrumentationClassSourceGenerator implements InstrumentationCodeGenerator {
+    protected abstract String classNameForRequest(CallInterceptionRequest request);
+
+    protected abstract Consumer<TypeSpec.Builder> classContentForClass(
+        String className,
+        Collection<CallInterceptionRequest> requestsClassGroup,
+        Consumer<? super CallInterceptionRequest> onProcessedRequest,
+        Consumer<? super GenerationResult.HasFailures.FailureInfo> onFailure
+    );
+
+    @Override
+    public GenerationResult generateCodeForRequestedInterceptors(Collection<CallInterceptionRequest> interceptionRequests) {
+        LinkedHashMap<String, List<CallInterceptionRequest>> requestsByImplClass = interceptionRequests.stream()
+            .filter(it -> classNameForRequest(it) != null)
+            .collect(Collectors.groupingBy(this::classNameForRequest, LinkedHashMap::new, Collectors.toList()));
+
+        List<GenerationResult.HasFailures.FailureInfo> failuresInfo = new ArrayList<>();
+        Set<CallInterceptionRequest> processedRequests = new LinkedHashSet<>(interceptionRequests.size());
+        Map<String, Consumer<TypeSpec.Builder>> classContentByName = new LinkedHashMap<>();
+
+        requestsByImplClass.forEach((className, requests) ->
+            classContentByName.put(className, classContentForClass(className, requests, processedRequests::add, failuresInfo::add))
+        );
+
+        if (failuresInfo.isEmpty()) {
+            return successResult(processedRequests, classContentByName);
+        } else {
+            return new GenerationResult.HasFailures(failuresInfo);
+        }
+    }
+
+    private static GenerationResult.CanGenerateClasses successResult(Set<CallInterceptionRequest> processedRequests, Map<String, Consumer<TypeSpec.Builder>> classContentByName) {
+        return new GenerationResult.CanGenerateClasses() {
+            @Override
+            public Collection<String> getClassNames() {
+                return classContentByName.keySet();
+            }
+
+            @Override
+            public void buildType(String className, TypeSpec.Builder builder) {
+                classContentByName.get(className).accept(builder);
+            }
+
+            @Override
+            public List<CallInterceptionRequest> getCoveredRequests() {
+                return new ArrayList<>(processedRequests);
+            }
+        };
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/SignatureUtils.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/SignatureUtils.java
new file mode 100644
index 0000000..720d516
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/SignatureUtils.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen;
+
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.ParameterKindInfo;
+
+public class SignatureUtils {
+    public static boolean hasCallerClassName(CallableInfo callableInfo) {
+        return callableInfo.getParameters().stream().anyMatch(it -> it.getKind() == ParameterKindInfo.CALLER_CLASS_NAME);
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/TypeUtils.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/TypeUtils.java
new file mode 100644
index 0000000..6b678cc
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/TypeUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen;
+
+import com.squareup.javapoet.ArrayTypeName;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import org.objectweb.asm.Type;
+
+public class TypeUtils {
+    /**
+     * Converts an ASM {@link Type} to a JavaPoet {@link TypeName}.
+     */
+    public static TypeName typeName(Type type) {
+        if (type.equals(Type.VOID_TYPE)) {
+            return ClassName.VOID;
+        }
+        if (type.equals(Type.BOOLEAN_TYPE)) {
+            return ClassName.BOOLEAN;
+        }
+        if (type.equals(Type.CHAR_TYPE)) {
+            return ClassName.CHAR;
+        }
+        if (type.equals(Type.BYTE_TYPE)) {
+            return ClassName.BYTE;
+        }
+        if (type.equals(Type.SHORT_TYPE)) {
+            return ClassName.SHORT;
+        }
+        if (type.equals(Type.INT_TYPE)) {
+            return ClassName.INT;
+        }
+        if (type.equals(Type.FLOAT_TYPE)) {
+            return ClassName.FLOAT;
+        }
+        if (type.equals(Type.LONG_TYPE)) {
+            return ClassName.LONG;
+        }
+        if (type.equals(Type.DOUBLE_TYPE)) {
+            return ClassName.DOUBLE;
+        }
+        if (type.getSort() == Type.ARRAY) {
+            return ArrayTypeName.of(typeName(type.getElementType()));
+        }
+        return ClassName.bestGuess(type.getClassName().replace("$", "."));
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/CodeGeneratingSignatureTreeVisitor.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/CodeGeneratingSignatureTreeVisitor.java
new file mode 100644
index 0000000..3c24118
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/CodeGeneratingSignatureTreeVisitor.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen.groovy;
+
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.ParameterKindInfo;
+import org.gradle.internal.instrumentation.processor.codegen.TypeUtils;
+import org.objectweb.asm.Type;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Stack;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import static org.gradle.internal.instrumentation.processor.codegen.SignatureUtils.hasCallerClassName;
+import static org.gradle.internal.instrumentation.processor.codegen.groovy.ParameterMatchEntry.Kind.PARAMETER;
+import static org.gradle.internal.instrumentation.processor.codegen.groovy.ParameterMatchEntry.Kind.RECEIVER;
+import static org.gradle.internal.instrumentation.processor.codegen.groovy.ParameterMatchEntry.Kind.RECEIVER_AS_CLASS;
+import static org.gradle.internal.instrumentation.processor.codegen.groovy.ParameterMatchEntry.Kind.VARARG;
+
+class CodeGeneratingSignatureTreeVisitor {
+    private final Stack<CodeBlock> paramVariablesStack = new Stack<>();
+    private final CodeBlock.Builder result;
+
+    CodeGeneratingSignatureTreeVisitor(CodeBlock.Builder result) {
+        this.result = result;
+    }
+
+    /**
+     * @param paramIndex index of the parameter in the signatures, -1 stands for the receiver
+     */
+    void visit(SignatureTree current, int paramIndex) {
+        CallInterceptionRequest leafInCurrent = current.getLeafOrNull();
+        if (leafInCurrent != null) {
+            generateInvocationWhenArgsMatched(leafInCurrent, paramIndex);
+        }
+        Map<ParameterMatchEntry, SignatureTree> children = current.getChildrenByMatchEntry();
+        if (!children.isEmpty()) {
+            boolean hasParamMatchers = children.keySet().stream().anyMatch(it -> it.kind == PARAMETER);
+            if (hasParamMatchers) { // is not the receiver or vararg
+                result.beginControlFlow("if (invocation.getArgsCount() > $L)", paramIndex);
+                result.addStatement("Object arg$1L = invocation.getArgument($1L)", paramIndex);
+            }
+            // Visit non-vararg invocations first and varargs after:
+            children.forEach((entry, child) -> {
+                if (entry.kind != VARARG) {
+                    generateNormalCallChecksAndVisitSubtree(entry, child, paramIndex);
+                }
+            });
+            if (hasParamMatchers) {
+                result.endControlFlow();
+            }
+            children.forEach((entry, child) -> {
+                if (entry.kind == VARARG) {
+                    generateVarargCheckAndInvocation(entry, child, paramIndex);
+                }
+            });
+        }
+    }
+
+    private void generateInvocationWhenArgsMatched(CallInterceptionRequest request, int argCount) {
+        result.beginControlFlow("if (invocation.getArgsCount() == $L)", argCount);
+        CodeBlock argsCode = prepareInvocationArgs(request);
+        emitInvocationCodeWithReturn(request, argsCode);
+        result.endControlFlow();
+    }
+
+    private CodeBlock prepareInvocationArgs(CallInterceptionRequest request) {
+        boolean hasKotlinDefaultMask = request.getInterceptedCallable().getParameters().stream().anyMatch(it -> it.getKind() == ParameterKindInfo.KOTLIN_DEFAULT_MASK);
+        boolean hasCallerClassName = hasCallerClassName(request.getInterceptedCallable());
+        Stream<CodeBlock> maybeZeroForKotlinDefault = hasKotlinDefaultMask ? Stream.of(CodeBlock.of("0")) : Stream.empty();
+        Stream<CodeBlock> maybeCallerClassName = hasCallerClassName ? Stream.of(CodeBlock.of("consumer")) : Stream.empty();
+        return Stream.of(
+            paramVariablesStack.stream(),
+            maybeZeroForKotlinDefault,
+            maybeCallerClassName
+        ).flatMap(Function.identity()).collect(CodeBlock.joining(", "));
+    }
+
+    private void emitInvocationCodeWithReturn(CallInterceptionRequest request, CodeBlock argsCode) {
+        TypeName implementationOwner = TypeUtils.typeName(request.getImplementationInfo().getOwner());
+        String implementationName = request.getImplementationInfo().getName();
+        if (request.getInterceptedCallable().getKind() == CallableKindInfo.AFTER_CONSTRUCTOR) {
+            result.addStatement("$1T result = new $1T($2L)", TypeUtils.typeName(request.getInterceptedCallable().getOwner().getType()), paramVariablesStack.stream().collect(CodeBlock.joining(", ")));
+            CodeBlock interceptorArgs = CodeBlock.join(Arrays.asList(CodeBlock.of("result"), argsCode), ", ");
+            result.addStatement("$T.$L($L)", implementationOwner, implementationName, interceptorArgs);
+            result.addStatement("return result");
+        } else if (request.getInterceptedCallable().getReturnType().getType() == Type.VOID_TYPE) {
+            result.addStatement("$T.$L($L)", implementationOwner, implementationName, argsCode);
+            result.addStatement("return null");
+        } else {
+            result.addStatement("return $T.$L($L)", implementationOwner, implementationName, argsCode);
+        }
+    }
+
+    private void generateVarargCheckAndInvocation(ParameterMatchEntry entry, SignatureTree child, int paramIndex) {
+        TypeName entryParamType = TypeUtils.typeName(entry.type);
+
+        result.add("// Trying to match the vararg invocation\n");
+        CodeBlock varargVariable = CodeBlock.of("varargValues");
+        result.addStatement("$1T[] $2L = new $1T[invocation.getArgsCount() - $3L]", entryParamType, varargVariable, paramIndex);
+        CodeBlock varargMatched = CodeBlock.of("varargMatched");
+        result.addStatement("boolean $L = true", varargMatched);
+        result.beginControlFlow("for (int argIndex = $1L; argIndex < invocation.getArgsCount(); argIndex++)", paramIndex);
+
+        CodeBlock nextArg = CodeBlock.of("nextArg");
+        result.addStatement("Object $L = invocation.getArgument(argIndex)", nextArg);
+        result.beginControlFlow("if ($L instanceof $T)", nextArg, entryParamType);
+        if (entryParamType.equals(TypeName.OBJECT)) {
+            result.addStatement("$1L[argIndex - $2L] = $3L", varargVariable, paramIndex, nextArg);
+        } else {
+            result.addStatement("$1L[argIndex - $2L] = ($3T) $4L", varargVariable, paramIndex, entryParamType, nextArg);
+        }
+        result.nextControlFlow("else");
+        result.addStatement("$L = false", varargMatched);
+        result.addStatement("break");
+        result.endControlFlow();
+
+        result.endControlFlow();
+        result.beginControlFlow("if ($L)", varargMatched);
+        paramVariablesStack.push(varargVariable);
+        CallInterceptionRequest request = Objects.requireNonNull(child.getLeafOrNull());
+        emitInvocationCodeWithReturn(request, prepareInvocationArgs(request));
+        paramVariablesStack.pop();
+        result.endControlFlow();
+    }
+
+    private void generateNormalCallChecksAndVisitSubtree(ParameterMatchEntry entry, SignatureTree child, int paramIndex) {
+        CodeBlock argExpr = entry.kind == RECEIVER || entry.kind == RECEIVER_AS_CLASS
+            ? CodeBlock.of("receiver")
+            : CodeBlock.of("arg$L", paramIndex);
+
+        int childArgCount = paramIndex + 1;
+        TypeName entryChildType = TypeUtils.typeName(entry.type);
+        CodeBlock matchExpr = entry.kind == RECEIVER_AS_CLASS ?
+            CodeBlock.of("$L.equals($T.class)", argExpr, entryChildType) :
+            // Vararg fits here, too:
+            CodeBlock.of("$L instanceof $T", argExpr, entryChildType.box());
+        result.beginControlFlow("if ($L)", matchExpr);
+        boolean shouldPopParameter = false;
+        if (entry.kind != RECEIVER_AS_CLASS) {
+            shouldPopParameter = true;
+            CodeBlock paramVariable = CodeBlock.of("$LTyped", argExpr);
+            if (!entryChildType.equals(TypeName.OBJECT)) {
+                result.addStatement("$2T $1L = ($2T) $3L", paramVariable, entryChildType, argExpr);
+            } else {
+                result.addStatement("$2T $1L = $3L", paramVariable, entryChildType, argExpr);
+            }
+            paramVariablesStack.push(paramVariable);
+        }
+        visit(child, childArgCount);
+        if (shouldPopParameter) {
+            paramVariablesStack.pop();
+        }
+        result.endControlFlow();
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/InterceptGroovyCallsGenerator.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/InterceptGroovyCallsGenerator.java
new file mode 100644
index 0000000..7efac7c
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/InterceptGroovyCallsGenerator.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen.groovy;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.gradle.internal.instrumentation.processor.codegen.InstrumentationCodeGenerator.GenerationResult.HasFailures.FailureInfo;
+import org.gradle.internal.instrumentation.processor.codegen.RequestGroupingInstrumentationClassSourceGenerator;
+import org.gradle.internal.instrumentation.processor.codegen.TypeUtils;
+import org.gradle.util.internal.TextUtil;
+import org.objectweb.asm.Type;
+
+import javax.lang.model.element.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import static org.gradle.internal.instrumentation.processor.codegen.JavadocUtils.callableKindForJavadoc;
+import static org.gradle.internal.instrumentation.processor.codegen.JavadocUtils.interceptedCallableLink;
+import static org.gradle.internal.instrumentation.processor.codegen.JavadocUtils.interceptorImplementationLink;
+
+public class InterceptGroovyCallsGenerator extends RequestGroupingInstrumentationClassSourceGenerator {
+    @Override
+    protected String classNameForRequest(CallInterceptionRequest request) {
+        return request.getRequestExtras().getByType(RequestExtra.InterceptGroovyCalls.class)
+            .map(RequestExtra.InterceptGroovyCalls::getImplementationClassName)
+            .orElse(null);
+    }
+
+    @Override
+    protected Consumer<TypeSpec.Builder> classContentForClass(
+        String className,
+        Collection<CallInterceptionRequest> requestsClassGroup,
+        Consumer<? super CallInterceptionRequest> onProcessedRequest,
+        Consumer<? super FailureInfo> onFailure
+    ) {
+        List<TypeSpec> interceptorTypeSpecs = generateInterceptorClasses(requestsClassGroup);
+        MethodSpec getInterceptors = generateGetInterceptorsMethod(interceptorTypeSpecs);
+
+        return builder -> builder
+            .addModifiers(Modifier.PUBLIC)
+            .addTypes(interceptorTypeSpecs)
+            .addMethod(getInterceptors);
+    }
+
+    private static List<TypeSpec> generateInterceptorClasses(Collection<CallInterceptionRequest> interceptionRequests) {
+        List<TypeSpec> result = new ArrayList<>(interceptionRequests.size() / 2);
+
+        LinkedHashMap<String, List<CallInterceptionRequest>> namedRequests = new LinkedHashMap<>();
+        LinkedHashMap<Type, List<CallInterceptionRequest>> constructorRequests = new LinkedHashMap<>();
+
+        interceptionRequests.forEach(request -> {
+            if (request.getRequestExtras().getByType(RequestExtra.InterceptGroovyCalls.class).isPresent()) {
+                CallableInfo callable = request.getInterceptedCallable();
+                if (callable.getKind() == CallableKindInfo.AFTER_CONSTRUCTOR) {
+                    constructorRequests.computeIfAbsent(request.getInterceptedCallable().getOwner().getType(), key -> new ArrayList<>()).add(request);
+                } else {
+                    String nameKey = callable.getKind() == CallableKindInfo.GROOVY_PROPERTY
+                        ? "get" + TextUtil.capitalize(callable.getCallableName())
+                        : callable.getCallableName();
+                    namedRequests.computeIfAbsent(nameKey, key -> new ArrayList<>()).add(request);
+                }
+            }
+        });
+
+        namedRequests.entrySet().stream()
+            .map(it -> generateNamedCallableInterceptorClass(it.getKey(), it.getValue()))
+            .collect(Collectors.toCollection(() -> result));
+
+        constructorRequests.entrySet().stream()
+            .map(it -> generateConstructorInterceptorClass(it.getKey(), it.getValue()))
+            .collect(Collectors.toCollection(() -> result));
+
+        return result;
+    }
+
+    private static TypeSpec generateNamedCallableInterceptorClass(String name, List<CallInterceptionRequest> requests) {
+        String className = TextUtil.capitalize(name) + "CallInterceptor";
+        return generateInterceptorClass(className, namedCallableScopesArgs(name, requests), requests).build();
+    }
+
+    private static TypeSpec generateConstructorInterceptorClass(Type constructedType, List<CallInterceptionRequest> requests) {
+        String className = ClassName.bestGuess(constructedType.getClassName()).simpleName() + "ConstructorCallInterceptor";
+        return generateInterceptorClass(className, constructorScopeArg(TypeUtils.typeName(constructedType)), requests).build();
+    }
+
+    private static SignatureTree signatureTreeFromRequests(Collection<CallInterceptionRequest> requests) {
+        SignatureTree result = new SignatureTree();
+        requests.forEach(result::add);
+        return result;
+    }
+
+    private static TypeSpec.Builder generateInterceptorClass(String className, CodeBlock scopes, List<CallInterceptionRequest> requests) {
+        TypeSpec.Builder generatedClass = TypeSpec.classBuilder(className)
+            .superclass(CALL_INTERCEPTOR_CLASS)
+            .addJavadoc(interceptorClassJavadoc(requests))
+            .addModifiers(Modifier.PRIVATE, Modifier.STATIC);
+
+        MethodSpec constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addStatement("super($L)", scopes).build();
+        generatedClass.addMethod(constructor);
+
+        MethodSpec doIntercept = MethodSpec.methodBuilder("doIntercept")
+            .addModifiers(Modifier.PROTECTED)
+            .returns(Object.class)
+            .addAnnotation(Override.class)
+            .addParameter(INVOCATION_CLASS, "invocation")
+            .addParameter(String.class, "consumer")
+            .addException(Throwable.class)
+            .addCode(generateCodeFromInterceptorSignatureTree(signatureTreeFromRequests(requests)))
+            .build();
+
+        generatedClass.addMethod(doIntercept);
+        return generatedClass;
+    }
+
+    private static CodeBlock interceptorClassJavadoc(Collection<CallInterceptionRequest> requests) {
+        List<CodeBlock> result = new ArrayList<>();
+        result.add(CodeBlock.of("Intercepts the following declarations:<ul>"));
+        requests.stream().map(request ->
+            CodeBlock.of("<li> $L $L\n     with $L", callableKindForJavadoc(request), interceptedCallableLink(request), interceptorImplementationLink(request))
+        ).collect(Collectors.toCollection(() -> result));
+        result.add(CodeBlock.of("</ul>"));
+        return result.stream().collect(CodeBlock.joining("\n\n"));
+    }
+
+    private static CodeBlock constructorScopeArg(TypeName constructedType) {
+        return CodeBlock.of("$1T.constructorsOf($2T.class)", INTERCEPTED_SCOPE_CLASS, constructedType);
+    }
+
+    private static CodeBlock namedCallableScopesArgs(String name, List<CallInterceptionRequest> requests) {
+        List<CodeBlock> scopeExpressions = new ArrayList<>();
+
+        List<CallInterceptionRequest> propertyRequests = requests.stream().filter(it -> it.getInterceptedCallable().getKind() == CallableKindInfo.GROOVY_PROPERTY).collect(Collectors.toList());
+        propertyRequests.forEach(request -> {
+            String propertyName = request.getInterceptedCallable().getCallableName();
+            String getterName = "get" + TextUtil.capitalize(propertyName);
+            scopeExpressions.add(CodeBlock.of("$1T.readsOfPropertiesNamed($2S)", INTERCEPTED_SCOPE_CLASS, propertyName));
+            scopeExpressions.add(CodeBlock.of("$1T.methodsNamed($2S)", INTERCEPTED_SCOPE_CLASS, getterName));
+        });
+
+        List<CallableKindInfo> callableKinds = requests.stream().map(it -> it.getInterceptedCallable().getKind()).distinct().collect(Collectors.toList());
+        if (callableKinds.contains(CallableKindInfo.STATIC_METHOD) | callableKinds.contains(CallableKindInfo.INSTANCE_METHOD)) {
+            scopeExpressions.add(CodeBlock.of("$T.methodsNamed($S)", INTERCEPTED_SCOPE_CLASS, name));
+        }
+        return scopeExpressions.stream().distinct().collect(CodeBlock.joining(", "));
+    }
+
+    private static CodeBlock generateCodeFromInterceptorSignatureTree(SignatureTree tree) {
+        CodeBlock.Builder result = CodeBlock.builder();
+        result.addStatement("$T receiver = invocation.getReceiver()", Object.class);
+
+        new CodeGeneratingSignatureTreeVisitor(result).visit(tree, -1);
+
+        result.addStatement("return invocation.callOriginal()");
+        return result.build();
+    }
+
+    private static final ClassName CALL_INTERCEPTOR_CLASS = ClassName.bestGuess("org.gradle.internal.classpath.intercept.CallInterceptor");
+    private static final ClassName INTERCEPTED_SCOPE_CLASS = ClassName.bestGuess("org.gradle.internal.classpath.intercept.InterceptScope");
+    private static final ClassName INVOCATION_CLASS = ClassName.bestGuess("org.gradle.internal.classpath.intercept.Invocation");
+
+    private static MethodSpec generateGetInterceptorsMethod(List<TypeSpec> interceptorTypes) {
+        MethodSpec.Builder method = MethodSpec.methodBuilder("getCallInterceptors")
+            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+            .returns(ParameterizedTypeName.get(ClassName.get(List.class), ClassName.bestGuess("org.gradle.internal.classpath.intercept.CallInterceptor")));
+        CodeBlock[] constructorCalls = interceptorTypes.stream().map(it -> CodeBlock.builder().add("new $T()", ClassName.bestGuess(it.name)).build()).toArray(CodeBlock[]::new);
+        CodeBlock constructorCallsArgs = CodeBlock.builder().add(interceptorTypes.stream().map(it -> "$L").collect(Collectors.joining(",\n")), (Object[]) constructorCalls).build();
+        method.addCode("return $T.asList($>\n$L$<\n", Arrays.class, constructorCallsArgs);
+        method.addCode(");");
+        return method.build();
+
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/ParameterMatchEntry.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/ParameterMatchEntry.java
new file mode 100644
index 0000000..060810b
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/ParameterMatchEntry.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen.groovy;
+
+import org.objectweb.asm.Type;
+
+import java.util.Objects;
+
+class ParameterMatchEntry {
+    final Type type;
+    final Kind kind;
+
+    ParameterMatchEntry(Type type, Kind kind) {
+        this.type = type;
+        this.kind = kind;
+    }
+
+    enum Kind {
+        RECEIVER_AS_CLASS, RECEIVER, PARAMETER, VARARG
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ParameterMatchEntry)) {
+            return false;
+        }
+        ParameterMatchEntry that = (ParameterMatchEntry) o;
+        return Objects.equals(type, that.type) && kind == that.kind;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, kind);
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/SignatureTree.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/SignatureTree.java
new file mode 100644
index 0000000..129b885
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/groovy/SignatureTree.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen.groovy;
+
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.ParameterInfo;
+import org.gradle.internal.instrumentation.model.ParameterKindInfo;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.gradle.internal.instrumentation.model.CallableKindInfo.AFTER_CONSTRUCTOR;
+import static org.gradle.internal.instrumentation.processor.codegen.groovy.ParameterMatchEntry.Kind.PARAMETER;
+import static org.gradle.internal.instrumentation.processor.codegen.groovy.ParameterMatchEntry.Kind.RECEIVER;
+import static org.gradle.internal.instrumentation.processor.codegen.groovy.ParameterMatchEntry.Kind.RECEIVER_AS_CLASS;
+import static org.gradle.internal.instrumentation.processor.codegen.groovy.ParameterMatchEntry.Kind.VARARG;
+
+class SignatureTree {
+    private CallInterceptionRequest leaf = null;
+    private LinkedHashMap<ParameterMatchEntry, SignatureTree> childrenByMatchEntry = null;
+
+    @Nullable
+    public CallInterceptionRequest getLeafOrNull() {
+        return leaf;
+    }
+
+    public Map<ParameterMatchEntry, SignatureTree> getChildrenByMatchEntry() {
+        return childrenByMatchEntry != null ? childrenByMatchEntry : Collections.emptyMap();
+    }
+
+    void add(CallInterceptionRequest request) {
+        CallableInfo callable = request.getInterceptedCallable();
+        List<ParameterMatchEntry> matchEntries = parameterMatchEntries(callable);
+
+        SignatureTree current = this;
+        for (ParameterMatchEntry matchEntry : matchEntries) {
+            if (current.childrenByMatchEntry == null) {
+                current.childrenByMatchEntry = new LinkedHashMap<>();
+            }
+            if (matchEntry.kind == VARARG && current.childrenByMatchEntry.keySet().stream().anyMatch(it -> it.kind == VARARG)) {
+                // TODO better diagnostics reporting
+                throw new IllegalStateException("vararg overloads are not supported yet");
+            }
+            current = current.childrenByMatchEntry.computeIfAbsent(matchEntry, key -> new SignatureTree());
+        }
+        if (current.leaf != null) {
+            // TODO better diagnostics reporting
+            throw new IllegalStateException("duplicate request");
+        }
+        current.leaf = request;
+    }
+
+    @Nonnull
+    private static List<ParameterMatchEntry> parameterMatchEntries(CallableInfo callable) {
+        Optional<ParameterInfo> varargParameter = callable.getParameters().stream().filter(it -> it.getKind() == ParameterKindInfo.VARARG_METHOD_PARAMETER).findAny();
+        return Stream.of(
+            // Match the `Class<?>` in `receiver` for static methods and constructors
+            callable.getKind() == CallableKindInfo.STATIC_METHOD || callable.getKind() == AFTER_CONSTRUCTOR
+                ? Stream.of(new ParameterMatchEntry(callable.getOwner().getType(), RECEIVER_AS_CLASS))
+                : Stream.<ParameterMatchEntry>empty(),
+            // Or match the receiver in the first parameter
+            callable.getKind() == CallableKindInfo.INSTANCE_METHOD || callable.getKind() == CallableKindInfo.GROOVY_PROPERTY
+                ? Stream.of(new ParameterMatchEntry(callable.getParameters().get(0).getParameterType(), RECEIVER))
+                : Stream.<ParameterMatchEntry>empty(),
+            // Then match the "normal" method parameters
+            callable.getParameters().stream().filter(it -> it.getKind() == ParameterKindInfo.METHOD_PARAMETER).map(it -> new ParameterMatchEntry(it.getParameterType(), PARAMETER)),
+            // In the end, match the vararg parameter, if it is there:
+            varargParameter.map(parameterInfo -> Stream.of(new ParameterMatchEntry(parameterInfo.getParameterType().getElementType(), VARARG))).orElseGet(Stream::empty)
+        ).flatMap(Function.identity()).collect(Collectors.toList());
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/jvmbytecode/InterceptJvmCallsGenerator.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/jvmbytecode/InterceptJvmCallsGenerator.java
new file mode 100644
index 0000000..21d76ce6
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/codegen/jvmbytecode/InterceptJvmCallsGenerator.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen.jvmbytecode;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeSpec;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind;
+import org.gradle.internal.instrumentation.api.jvmbytecode.JvmBytecodeCallInterceptor;
+import org.gradle.internal.instrumentation.api.metadata.InstrumentationMetadata;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.CallableOwnerInfo;
+import org.gradle.internal.instrumentation.model.ParameterInfo;
+import org.gradle.internal.instrumentation.model.ParameterKindInfo;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.gradle.internal.instrumentation.processor.codegen.InstrumentationCodeGenerator.GenerationResult.HasFailures.FailureInfo;
+import org.gradle.internal.instrumentation.processor.codegen.JavadocUtils;
+import org.gradle.internal.instrumentation.processor.codegen.RequestGroupingInstrumentationClassSourceGenerator;
+import org.gradle.model.internal.asm.MethodVisitorScope;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.MethodNode;
+
+import javax.lang.model.element.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.gradle.internal.instrumentation.processor.codegen.SignatureUtils.hasCallerClassName;
+import static org.gradle.internal.instrumentation.processor.codegen.TypeUtils.typeName;
+import static org.gradle.util.internal.TextUtil.camelToKebabCase;
+
+public class InterceptJvmCallsGenerator extends RequestGroupingInstrumentationClassSourceGenerator {
+    @Override
+    protected String classNameForRequest(CallInterceptionRequest request) {
+        return request.getRequestExtras().getByType(RequestExtra.InterceptJvmCalls.class)
+            .map(RequestExtra.InterceptJvmCalls::getImplementationClassName)
+            .orElse(null);
+    }
+
+    @Override
+    protected Consumer<TypeSpec.Builder> classContentForClass(
+        String className,
+        Collection<CallInterceptionRequest> requestsClassGroup,
+        Consumer<? super CallInterceptionRequest> onProcessedRequest,
+        Consumer<? super FailureInfo> onFailure
+    ) {
+        Map<Type, FieldSpec> typeFieldByOwner = generateFieldsForImplementationOwners(requestsClassGroup);
+
+        MethodSpec.Builder visitMethodInsnBuilder = getVisitMethodInsnBuilder();
+        generateVisitMethodInsnCode(
+            visitMethodInsnBuilder, requestsClassGroup, typeFieldByOwner, onProcessedRequest, onFailure
+        );
+
+        return builder ->
+            builder.addMethod(constructor)
+                .addModifiers(Modifier.PUBLIC)
+                // generic stuff not related to the content:
+                .addSuperinterface(JvmBytecodeCallInterceptor.class)
+                .addMethod(BINARY_CLASS_NAME_OF)
+                .addMethod(LOAD_BINARY_CLASS_NAME)
+                .addField(METHOD_VISITOR_FIELD)
+                .addField(METADATA_FIELD)
+                .superclass(MethodVisitorScope.class)
+                // actual content:
+                .addMethod(visitMethodInsnBuilder.build())
+                .addFields(typeFieldByOwner.values());
+    }
+
+
+    private static void generateVisitMethodInsnCode(
+        MethodSpec.Builder visitMethodInsnBuilder,
+        Collection<CallInterceptionRequest> interceptionRequests,
+        Map<Type, FieldSpec> typeFieldByOwner,
+        Consumer<? super CallInterceptionRequest> onProcessedRequest,
+        Consumer<? super FailureInfo> onFailure
+    ) {
+        CodeBlock.Builder code = CodeBlock.builder();
+        interceptionRequests.stream()
+            .collect(Collectors.groupingBy(it -> it.getInterceptedCallable().getOwner(), LinkedHashMap::new, Collectors.toList()))
+            .forEach((owner, requests) -> generateCodeForOwner(owner, typeFieldByOwner, requests, code, onProcessedRequest, onFailure));
+        code.addStatement("return false");
+        visitMethodInsnBuilder.addCode(code.build());
+    }
+
+    private static Map<Type, FieldSpec> generateFieldsForImplementationOwners(Collection<CallInterceptionRequest> interceptionRequests) {
+        Set<String> knownSimpleNames = new HashSet<>();
+        return interceptionRequests.stream().map(it -> it.getImplementationInfo().getOwner()).distinct()
+            .collect(Collectors.toMap(Function.identity(), implementationType -> {
+                ClassName implementationClassName = ClassName.bestGuess(implementationType.getClassName());
+                String fieldTypeName = knownSimpleNames.add(implementationClassName.simpleName()) ?
+                    implementationClassName.simpleName() :
+                    implementationClassName.canonicalName();
+                String fullFieldName = camelToKebabCase(fieldTypeName).replace("-", "_").toUpperCase(Locale.US) + "_TYPE";
+                return FieldSpec.builder(Type.class, fullFieldName, Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
+                    .initializer("$T.getType($T.class)", Type.class, implementationClassName)
+                    .build();
+            }, (u, v) -> u, LinkedHashMap::new));
+    }
+
+    MethodSpec constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC)
+        .addParameter(MethodVisitor.class, "methodVisitor")
+        .addParameter(InstrumentationMetadata.class, "metadata")
+        .addStatement("super(methodVisitor)")
+        .addStatement("this.$N = methodVisitor", METHOD_VISITOR_FIELD)
+        .addStatement("this.$N = metadata", METADATA_FIELD)
+        .build();
+
+    private static MethodSpec.Builder getVisitMethodInsnBuilder() {
+        return MethodSpec.methodBuilder("visitMethodInsn")
+            .addAnnotation(Override.class)
+            .addModifiers(Modifier.PUBLIC)
+            .returns(boolean.class)
+            .addParameter(String.class, "className")
+            .addParameter(int.class, "opcode")
+            .addParameter(String.class, "owner")
+            .addParameter(String.class, "name")
+            .addParameter(String.class, "descriptor")
+            .addParameter(boolean.class, "isInterface")
+            .addParameter(ParameterizedTypeName.get(Supplier.class, MethodNode.class), "readMethodNode");
+    }
+
+    private static final MethodSpec BINARY_CLASS_NAME_OF = MethodSpec.methodBuilder("binaryClassNameOf")
+        .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+        .returns(String.class)
+        .addParameter(String.class, "className")
+        .addStatement("return $T.getObjectType(className).getClassName()", Type.class)
+        .build();
+
+    private static final MethodSpec LOAD_BINARY_CLASS_NAME = MethodSpec.methodBuilder("loadOwnerBinaryClassName")
+        .addModifiers(Modifier.PRIVATE)
+        .returns(void.class)
+        .addParameter(String.class, "className")
+        .addStatement("_LDC($N(className))", BINARY_CLASS_NAME_OF)
+        .build();
+
+    private static final FieldSpec METHOD_VISITOR_FIELD =
+        FieldSpec.builder(MethodVisitor.class, "methodVisitor", Modifier.PRIVATE, Modifier.FINAL).build();
+
+    private static final FieldSpec METADATA_FIELD =
+        FieldSpec.builder(InstrumentationMetadata.class, "metadata", Modifier.PRIVATE, Modifier.FINAL).build();
+
+    private static void generateCodeForOwner(
+        CallableOwnerInfo owner,
+        Map<Type, FieldSpec> implTypeFields,
+        List<CallInterceptionRequest> requestsForOwner,
+        CodeBlock.Builder code,
+        Consumer<? super CallInterceptionRequest> onProcessedRequest,
+        Consumer<? super FailureInfo> onFailure
+    ) {
+        if (owner.isInterceptSubtypes()) {
+            code.beginControlFlow("if ($N.isInstanceOf(owner, $S))", METADATA_FIELD, owner.getType().getInternalName());
+        } else {
+            code.beginControlFlow("if (owner.equals($S))", owner.getType().getInternalName());
+        }
+        for (CallInterceptionRequest request : requestsForOwner) {
+            CodeBlock.Builder nested = CodeBlock.builder();
+            try {
+                generateCodeForRequest(request, implTypeFields.get(request.getImplementationInfo().getOwner()), nested);
+            } catch (Failure failure) {
+                onFailure.accept(new FailureInfo(request, failure.reason));
+            }
+            onProcessedRequest.accept(request);
+            code.add(nested.build());
+        }
+        code.endControlFlow();
+    }
+
+    private static void generateCodeForRequest(CallInterceptionRequest request, FieldSpec implTypeField, CodeBlock.Builder code) {
+        String callableName = request.getInterceptedCallable().getCallableName();
+        CallableInfo interceptedCallable = request.getInterceptedCallable();
+        String interceptedCallableDescriptor = standardCallableDescriptor(interceptedCallable);
+        validateSignature(request.getInterceptedCallable());
+
+        CodeBlock matchOpcodeExpression = matchOpcodeExpression(interceptedCallable);
+
+        documentInterceptorGeneratedCode(request, code);
+        matchAndInterceptStandardCallableSignature(request, implTypeField, code, callableName, interceptedCallableDescriptor, matchOpcodeExpression);
+
+        if (interceptedCallable.getParameters().stream().anyMatch(it -> it.getKind() == ParameterKindInfo.KOTLIN_DEFAULT_MASK)) {
+            matchAndInterceptKotlinDefaultSignature(request, implTypeField, code, callableName, interceptedCallable, matchOpcodeExpression);
+        }
+    }
+
+    private static void matchAndInterceptStandardCallableSignature(
+        CallInterceptionRequest request,
+        FieldSpec implTypeField,
+        CodeBlock.Builder code,
+        String callableName,
+        String callableDescriptor,
+        CodeBlock matchOpcodeExpression
+    ) {
+        code.beginControlFlow("if (name.equals($S) && descriptor.equals($S) && $L)", callableName, callableDescriptor, matchOpcodeExpression);
+        generateInterceptedInvocation(request, implTypeField, code);
+        code.addStatement("return true");
+        code.endControlFlow();
+    }
+
+    private static void matchAndInterceptKotlinDefaultSignature(
+        CallInterceptionRequest request,
+        FieldSpec ownerTypeField,
+        CodeBlock.Builder code,
+        String callableName,
+        CallableInfo interceptedCallable,
+        CodeBlock matchOpcodeExpression
+    ) {
+        code.add("// Additionally intercept the signature with the Kotlin default mask and marker:\n");
+        String callableDescriptorKotlinDefault = kotlinDefaultFunctionDescriptor(interceptedCallable);
+        String defaultMethodName = callableName + "$default";
+        code.beginControlFlow("if (name.equals($S) && descriptor.equals($S) && $L)", defaultMethodName, callableDescriptorKotlinDefault, matchOpcodeExpression);
+        generateKotlinDefaultInvocation(request, ownerTypeField, code);
+        code.addStatement("return true");
+        code.endControlFlow();
+    }
+
+    private static void documentInterceptorGeneratedCode(CallInterceptionRequest request, CodeBlock.Builder code) {
+        code.add("/** \n * Intercepting $L: $L\n", JavadocUtils.callableKindForJavadoc(request), JavadocUtils.interceptedCallableLink(request));
+        code.add(" * Intercepted by $L\n*/\n", JavadocUtils.interceptorImplementationLink(request));
+    }
+
+    private static CodeBlock matchOpcodeExpression(CallableInfo interceptedCallable) {
+        CodeBlock result = interceptedCallable.getKind() == CallableKindInfo.STATIC_METHOD ? CodeBlock.of("opcode == $T.INVOKESTATIC", Opcodes.class) :
+            interceptedCallable.getKind() == CallableKindInfo.INSTANCE_METHOD ? CodeBlock.of("opcode == $T.INVOKEVIRTUAL", Opcodes.class) :
+                interceptedCallable.getKind() == CallableKindInfo.AFTER_CONSTRUCTOR ? CodeBlock.of("opcode == $T.INVOKESPECIAL", Opcodes.class) : null;
+        if (result == null) {
+            throw new Failure("Could not determine the opcode for intercepting the call");
+        }
+        return result;
+    }
+
+    // TODO: move validation earlier?
+
+    private static void generateInterceptedInvocation(CallInterceptionRequest request, FieldSpec implTypeField, CodeBlock.Builder method) {
+        CallableInfo callable = request.getInterceptedCallable();
+        String implementationName = request.getImplementationInfo().getName();
+        String implementationDescriptor = request.getImplementationInfo().getDescriptor();
+
+        if (callable.getKind() == CallableKindInfo.STATIC_METHOD || callable.getKind() == CallableKindInfo.INSTANCE_METHOD) {
+            generateNormalInterceptedInvocation(implTypeField, callable, implementationName, implementationDescriptor, method);
+        } else if (callable.getKind() == CallableKindInfo.AFTER_CONSTRUCTOR) {
+            generateInvocationAfterConstructor(implTypeField, method, callable, implementationName, implementationDescriptor);
+        }
+    }
+    private static void generateInvocationAfterConstructor(FieldSpec implOwnerField, CodeBlock.Builder code, CallableInfo callable, String implementationName, String implementationDescriptor) {
+        if (callable.getKind() != CallableKindInfo.AFTER_CONSTRUCTOR) {
+            throw new IllegalArgumentException("expected after-constructor interceptor");
+        }
+
+        List<ParameterInfo> parameters = callable.getParameters();
+        if (parameters.get(0).getKind() != ParameterKindInfo.RECEIVER) {
+            throw new Failure("Expected @" + ParameterKind.Receiver.class.getSimpleName() + " first parameter in @" + CallableKind.AfterConstructor.class.getSimpleName());
+        }
+        if (!Type.getReturnType(implementationDescriptor).equals(Type.VOID_TYPE)) {
+            throw new Failure("@" + CallableKind.AfterConstructor.class.getSimpleName() + " handlers can only return void");
+        }
+
+        CodeBlock maxLocalsVar = CodeBlock.of("maxLocals");
+        code.addStatement("int $L = readMethodNode.get().maxLocals", maxLocalsVar);
+
+        // Store the constructor arguments in local variables, so that we can duplicate them for both the constructor and the interceptor:
+        Type[] params = Type.getArgumentTypes(standardCallableDescriptor(callable));
+        for (int i = params.length - 1; i >= 0; i--) {
+            code.addStatement("$1T type$2L = $1T.getType($3T.class)", Type.class, i, typeName(params[i]));
+            code.addStatement("int var$1L = $2L + $3L", i, maxLocalsVar, i * 2 /* in case it's long or double */);
+            code.addStatement("$1N.visitVarInsn(type$2L.getOpcode($3T.ISTORE), var$2L)", METHOD_VISITOR_FIELD, i, Opcodes.class);
+        }
+        // Duplicate the receiver without storing it into a local variable, then prepare the arguments for the original invocation:
+        code.addStatement("_DUP()");
+        for (int i = 0; i < params.length; i++) {
+            code.addStatement("$1N.visitVarInsn(type$2L.getOpcode($3T.ILOAD), var$2L)", METHOD_VISITOR_FIELD, i, Opcodes.class);
+        }
+        // Put the arguments to the stack again, for the "interceptor" invocation:
+        code.addStatement("_INVOKESPECIAL(owner, name, descriptor)");
+        for (int i = 0; i < params.length; i++) {
+            code.addStatement("$1N.visitVarInsn(type$2L.getOpcode($3T.ILOAD), var$2L)", METHOD_VISITOR_FIELD, i, Opcodes.class);
+        }
+        maybeGenerateLoadBinaryClassNameCall(code, callable);
+        code.addStatement("_INVOKESTATIC($N, $S, $S)", implOwnerField, implementationName, implementationDescriptor);
+    }
+
+    private static void generateNormalInterceptedInvocation(FieldSpec ownerTypeField, CallableInfo callable, String implementationName, String implementationDescriptor, CodeBlock.Builder code) {
+        if (callable.getKind() == CallableKindInfo.GROOVY_PROPERTY) {
+            throw new IllegalArgumentException("cannot generate invocation for Groovy property");
+        }
+
+        List<ParameterInfo> parameters = callable.getParameters();
+        if (parameters.size() > 1 && parameters.get(parameters.size() - 2).getKind() == ParameterKindInfo.KOTLIN_DEFAULT_MASK) {
+            // push the default mask equal to zero, meaning that no parameters have the default values
+            code.add("// The interceptor expects a Kotlin default mask, add a zero argument:\n");
+            code.addStatement("_ICONST_0()");
+        }
+        maybeGenerateLoadBinaryClassNameCall(code, callable);
+        code.addStatement("_INVOKESTATIC($N, $S, $S)", ownerTypeField, implementationName, implementationDescriptor);
+    }
+
+    private static void generateKotlinDefaultInvocation(CallInterceptionRequest request, FieldSpec ownerTypeField, CodeBlock.Builder method) {
+        CallableInfo interceptedCallable = request.getInterceptedCallable();
+        if (interceptedCallable.getKind() == CallableKindInfo.GROOVY_PROPERTY) {
+            throw new IllegalArgumentException("cannot generate invocation for Groovy property");
+        }
+
+        String implementationName = request.getImplementationInfo().getName();
+        String implementationDescriptor = request.getImplementationInfo().getDescriptor();
+
+        method.addStatement("_POP()"); // pops the default method signature marker
+        maybeGenerateLoadBinaryClassNameCall(method, interceptedCallable);
+        method.addStatement("_INVOKESTATIC($N, $S, $S)", ownerTypeField, implementationName, implementationDescriptor);
+    }
+
+    private static void validateSignature(CallableInfo callable) {
+        if (callable.getKind() == CallableKindInfo.GROOVY_PROPERTY) {
+            throw new Failure("Groovy property access cannot be intercepted in JVM calls");
+        }
+
+        boolean hasCallerClassName = hasCallerClassName(callable);
+        if (hasCallerClassName) {
+            if (callable.getParameters().get(callable.getParameters().size() - 1).getKind() != ParameterKindInfo.CALLER_CLASS_NAME) {
+                throw new Failure("The interceptor's @" + ParameterKind.CallerClassName.class.getSimpleName() + " parameter should go last");
+            }
+            if (callable.getParameters().stream().filter(it -> it.getKind() == ParameterKindInfo.CALLER_CLASS_NAME).count() > 1) {
+                throw new Failure("An interceptor may not have more than one @" + ParameterKind.CallerClassName.class.getSimpleName() + " parameter");
+            }
+        }
+
+        if (callable.getParameters().stream().anyMatch(it -> it.getKind() == ParameterKindInfo.KOTLIN_DEFAULT_MASK)) {
+            // TODO support @AfterConstructor with Kotlin default mask? Kotlin constructors have a special DefaultConstructorMarker as the last argument
+            if (callable.getKind() != CallableKindInfo.STATIC_METHOD && callable.getKind() != CallableKindInfo.INSTANCE_METHOD) {
+                throw new Failure(
+                    "Only @" + CallableKind.StaticMethod.class.getSimpleName() + " or @" + CallableKind.InstanceMethod.class.getSimpleName() + " can use Kotlin default parameters"
+                );
+            }
+
+            int expectedKotlinDefaultMaskIndex = callable.getParameters().size() - (hasCallerClassName ? 2 : 1);
+            if (callable.getParameters().get(expectedKotlinDefaultMaskIndex).getKind() != ParameterKindInfo.KOTLIN_DEFAULT_MASK) {
+                throw new Failure(
+                    "@" + ParameterKind.KotlinDefaultMask.class.getSimpleName() + " should be the last parameter of may be followed only by @" + ParameterKind.CallerClassName.class.getSimpleName()
+                );
+            }
+        }
+    }
+
+    private static void maybeGenerateLoadBinaryClassNameCall(CodeBlock.Builder code, CallableInfo callableInfo) {
+        if (hasCallerClassName(callableInfo)) {
+            code.addStatement("$N(className)", LOAD_BINARY_CLASS_NAME);
+        }
+    }
+
+    private static String standardCallableDescriptor(CallableInfo callableInfo) {
+        Type[] parameterTypes = callableInfo.getParameters().stream()
+            .filter(it -> it.getKind().isSourceParameter())
+            .map(ParameterInfo::getParameterType).toArray(Type[]::new);
+        Type returnType = callableInfo.getReturnType().getType();
+        return Type.getMethodDescriptor(returnType, parameterTypes);
+    }
+
+    private static String kotlinDefaultFunctionDescriptor(CallableInfo callableInfo) {
+        if (callableInfo.getKind() != CallableKindInfo.INSTANCE_METHOD && callableInfo.getKind() != CallableKindInfo.STATIC_METHOD) {
+            throw new UnsupportedOperationException("Kotlin default parameters are not yet supported for " + callableInfo.getKind());
+        }
+
+        String standardDescriptor = standardCallableDescriptor(callableInfo);
+        Type returnType = Type.getReturnType(standardDescriptor);
+        Type[] argumentTypes = Type.getArgumentTypes(standardDescriptor);
+        Type[] argumentTypesWithDefault = Stream.concat(
+            Arrays.stream(argumentTypes),
+            Stream.of(Type.getType(int.class), Type.getType(Object.class))
+        ).toArray(Type[]::new);
+        return Type.getMethodDescriptor(returnType, argumentTypesWithDefault);
+    }
+
+    private static class Failure extends RuntimeException {
+        final String reason;
+
+        private Failure(String reason) {
+            this.reason = reason;
+        }
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/AnnotatedMethodReaderExtension.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/AnnotatedMethodReaderExtension.java
new file mode 100644
index 0000000..40aca4c
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/AnnotatedMethodReaderExtension.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.extensibility;
+
+import org.gradle.internal.instrumentation.processor.modelreader.api.CallInterceptionRequestReaderFromAnnotatedMethod;
+
+public interface AnnotatedMethodReaderExtension extends InstrumentationProcessorExtension, CallInterceptionRequestReaderFromAnnotatedMethod {
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/ClassLevelAnnotationsContributor.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/ClassLevelAnnotationsContributor.java
new file mode 100644
index 0000000..cf59e19
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/ClassLevelAnnotationsContributor.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.extensibility;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+public interface ClassLevelAnnotationsContributor extends InstrumentationProcessorExtension {
+    Collection<Class<? extends Annotation>> contributeClassLevelAnnotationTypes();
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/CodeGeneratorContributor.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/CodeGeneratorContributor.java
new file mode 100644
index 0000000..54eb52d
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/CodeGeneratorContributor.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.extensibility;
+
+import org.gradle.internal.instrumentation.processor.codegen.InstrumentationCodeGenerator;
+
+public interface CodeGeneratorContributor extends InstrumentationProcessorExtension {
+    InstrumentationCodeGenerator contributeCodeGenerator();
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/InstrumentationProcessorExtension.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/InstrumentationProcessorExtension.java
new file mode 100644
index 0000000..d6534ee
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/InstrumentationProcessorExtension.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.extensibility;
+
+public interface InstrumentationProcessorExtension {
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/RequestPostProcessorExtension.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/RequestPostProcessorExtension.java
new file mode 100644
index 0000000..dfe73d9
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/extensibility/RequestPostProcessorExtension.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.extensibility;
+
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+
+import java.util.Collection;
+
+public interface RequestPostProcessorExtension extends InstrumentationProcessorExtension {
+    Collection<CallInterceptionRequest> postProcessRequest(CallInterceptionRequest originalRequest);
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesExtra.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesExtra.java
new file mode 100644
index 0000000..9a8c28e
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesExtra.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.features.withstaticreference;
+
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.objectweb.asm.Type;
+
+public class WithExtensionReferencesExtra implements RequestExtra {
+    public final Type ownerType;
+    public final String methodName;
+
+    public WithExtensionReferencesExtra(Type ownerType, String methodName) {
+        this.ownerType = ownerType;
+        this.methodName = methodName;
+    }
+
+    public static class ProducedSynthetically implements RequestExtra {
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesPostProcessor.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesPostProcessor.java
new file mode 100644
index 0000000..185c2a2
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesPostProcessor.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.features.withstaticreference;
+
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequestImpl;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.CallableInfoImpl;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.CallableOwnerInfo;
+import org.gradle.internal.instrumentation.model.ParameterInfo;
+import org.gradle.internal.instrumentation.model.ParameterInfoImpl;
+import org.gradle.internal.instrumentation.model.ParameterKindInfo;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.gradle.internal.instrumentation.model.RequestExtrasContainer;
+import org.gradle.internal.instrumentation.processor.extensibility.RequestPostProcessorExtension;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class WithExtensionReferencesPostProcessor implements RequestPostProcessorExtension {
+
+    @Override
+    public Collection<CallInterceptionRequest> postProcessRequest(CallInterceptionRequest originalRequest) {
+        Optional<WithExtensionReferencesExtra> extra = originalRequest.getRequestExtras().getByType(WithExtensionReferencesExtra.class);
+        return extra
+            .map(withExtensionReferencesExtra -> Arrays.asList(originalRequest, modifiedRequest(originalRequest, withExtensionReferencesExtra)))
+            .orElseGet(() -> Collections.singletonList(originalRequest));
+    }
+
+    private static CallInterceptionRequest modifiedRequest(CallInterceptionRequest originalRequest, WithExtensionReferencesExtra extra) {
+        return new CallInterceptionRequestImpl(
+            modifiedCallableInfo(originalRequest.getInterceptedCallable(), extra),
+            originalRequest.getImplementationInfo(),
+            modifiedExtras(originalRequest.getRequestExtras())
+        );
+    }
+
+    private static CallableInfo modifiedCallableInfo(CallableInfo originalInfo, WithExtensionReferencesExtra extra) {
+        CallableOwnerInfo owner = new CallableOwnerInfo(extra.ownerType, false);
+        String methodName = extra.methodName;
+        return new CallableInfoImpl(CallableKindInfo.STATIC_METHOD, owner, methodName, originalInfo.getReturnType(), modifiedParameters(originalInfo.getParameters()));
+    }
+
+    private static List<ParameterInfo> modifiedParameters(List<ParameterInfo> originalParameters) {
+        ArrayList<ParameterInfo> result = new ArrayList<>(originalParameters);
+        if (result.size() == 0 || result.get(0).getKind() != ParameterKindInfo.RECEIVER) {
+            throw new UnsupportedOperationException("extensions with static references that do not have a receiver parameter are not supported");
+        }
+        ParameterInfo originalReceiver = result.remove(0);
+        result.add(0, new ParameterInfoImpl("receiverArg", originalReceiver.getParameterType(), ParameterKindInfo.METHOD_PARAMETER));
+        return result;
+    }
+
+    private static List<RequestExtra> modifiedExtras(RequestExtrasContainer originalExtras) {
+        return Stream.of(
+            originalExtras.getAll().stream().filter(it -> !(it instanceof WithExtensionReferencesExtra)),
+            Stream.<RequestExtra>of(new WithExtensionReferencesExtra.ProducedSynthetically())
+        ).flatMap(Function.identity()).collect(Collectors.toList());
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesReader.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesReader.java
new file mode 100644
index 0000000..b7f95ed
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/features/withstaticreference/WithExtensionReferencesReader.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.features.withstaticreference;
+
+import org.gradle.internal.instrumentation.api.annotations.features.withstaticreference.WithExtensionReferences;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.gradle.internal.instrumentation.processor.extensibility.RequestPostProcessorExtension;
+import org.gradle.internal.instrumentation.processor.modelreader.impl.AnnotationUtils;
+import org.gradle.internal.instrumentation.processor.modelreader.impl.TypeUtils;
+import org.gradle.util.internal.TextUtil;
+import org.objectweb.asm.Type;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+import java.util.Collection;
+import java.util.Collections;
+
+public class WithExtensionReferencesReader implements RequestPostProcessorExtension {
+    @Override
+    public Collection<CallInterceptionRequest> postProcessRequest(CallInterceptionRequest originalRequest) {
+        if (shouldPostProcess(originalRequest)) {
+            originalRequest.getRequestExtras().getByType(RequestExtra.OriginatingElement.class).ifPresent(originatingElement -> {
+                ExecutableElement element = originatingElement.getElement();
+                AnnotationUtils.findAnnotationMirror(element, WithExtensionReferences.class).ifPresent(annotation ->
+                    AnnotationUtils.findAnnotationValue(annotation, "toClass").ifPresent(value -> {
+                        TypeMirror typeMirror = (TypeMirror) value.getValue();
+                        Type type = TypeUtils.extractType(typeMirror);
+                        String methodName = extractMethodName(originalRequest, annotation);
+                        originalRequest.getRequestExtras().add(new WithExtensionReferencesExtra(type, methodName));
+                    }));
+            });
+        }
+        return Collections.singletonList(originalRequest);
+    }
+
+    private static String extractMethodName(CallInterceptionRequest originalRequest, AnnotationMirror annotation) {
+        return AnnotationUtils.findAnnotationValue(annotation, "methodName")
+            .map(it -> (String) it.getValue())
+            .filter(it -> !it.isEmpty())
+            .orElse(guessMethodName(originalRequest.getInterceptedCallable()));
+    }
+
+    private static String guessMethodName(CallableInfo callable) {
+        if (callable.getKind() == CallableKindInfo.GROOVY_PROPERTY) {
+            return "get" + TextUtil.capitalize(callable.getCallableName());
+        }
+        return callable.getCallableName();
+    }
+
+    private static boolean shouldPostProcess(CallInterceptionRequest request) {
+        CallableKindInfo kind = request.getInterceptedCallable().getKind();
+        return kind == CallableKindInfo.INSTANCE_METHOD || kind == CallableKindInfo.GROOVY_PROPERTY;
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/api/CallInterceptionRequestReader.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/api/CallInterceptionRequestReader.java
new file mode 100644
index 0000000..2b1d1be
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/api/CallInterceptionRequestReader.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.modelreader.api;
+
+import org.gradle.internal.instrumentation.model.CallInterceptionRequest;
+
+import java.util.Collection;
+
+public interface CallInterceptionRequestReader<T> {
+    Collection<Result> readRequest(T input);
+
+    interface Result {
+        class Success implements Result {
+            private final CallInterceptionRequest request;
+
+            public Success(CallInterceptionRequest request) {
+                this.request = request;
+            }
+
+            public CallInterceptionRequest getRequest() {
+                return request;
+            }
+        }
+
+        class InvalidRequest implements Result {
+            public final String reason;
+
+            public InvalidRequest(String reason) {
+                this.reason = reason;
+            }
+        }
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/api/CallInterceptionRequestReaderFromAnnotatedMethod.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/api/CallInterceptionRequestReaderFromAnnotatedMethod.java
new file mode 100644
index 0000000..283dd36
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/api/CallInterceptionRequestReaderFromAnnotatedMethod.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.modelreader.api;
+
+import javax.lang.model.element.ExecutableElement;
+import java.util.Collection;
+
+public interface CallInterceptionRequestReaderFromAnnotatedMethod extends CallInterceptionRequestReader<ExecutableElement> {
+    Collection<Result> readRequest(ExecutableElement input);
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/AnnotationCallInterceptionRequestReaderImpl.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/AnnotationCallInterceptionRequestReaderImpl.java
new file mode 100644
index 0000000..6876c30
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/AnnotationCallInterceptionRequestReaderImpl.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.modelreader.impl;
+
+import org.gradle.internal.Cast;
+import org.gradle.internal.instrumentation.api.annotations.CallableDefinition;
+import org.gradle.internal.instrumentation.api.annotations.CallableKind;
+import org.gradle.internal.instrumentation.api.annotations.InterceptInherited;
+import org.gradle.internal.instrumentation.api.annotations.ParameterKind;
+import org.gradle.internal.instrumentation.model.CallInterceptionRequestImpl;
+import org.gradle.internal.instrumentation.model.CallableInfo;
+import org.gradle.internal.instrumentation.model.CallableInfoImpl;
+import org.gradle.internal.instrumentation.model.CallableKindInfo;
+import org.gradle.internal.instrumentation.model.CallableOwnerInfo;
+import org.gradle.internal.instrumentation.model.CallableReturnTypeInfo;
+import org.gradle.internal.instrumentation.model.ImplementationInfoImpl;
+import org.gradle.internal.instrumentation.model.ParameterInfo;
+import org.gradle.internal.instrumentation.model.ParameterInfoImpl;
+import org.gradle.internal.instrumentation.model.ParameterKindInfo;
+import org.gradle.internal.instrumentation.model.RequestExtra;
+import org.gradle.internal.instrumentation.model.RequestExtra.OriginatingElement;
+import org.gradle.internal.instrumentation.processor.extensibility.AnnotatedMethodReaderExtension;
+import org.objectweb.asm.Type;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static org.gradle.internal.instrumentation.processor.modelreader.impl.AnnotationUtils.findAnnotationMirror;
+import static org.gradle.internal.instrumentation.processor.modelreader.impl.AnnotationUtils.findAnnotationValue;
+import static org.gradle.internal.instrumentation.processor.modelreader.impl.TypeUtils.extractMethodDescriptor;
+import static org.gradle.internal.instrumentation.processor.modelreader.impl.TypeUtils.extractReturnType;
+import static org.gradle.internal.instrumentation.processor.modelreader.impl.TypeUtils.extractType;
+
+public class AnnotationCallInterceptionRequestReaderImpl implements AnnotatedMethodReaderExtension {
+
+    @Override
+    public Collection<Result> readRequest(ExecutableElement input) {
+        if (input.getKind() != ElementKind.METHOD) {
+            return emptyList();
+        }
+
+        if (!input.getModifiers().containsAll(Arrays.asList(Modifier.STATIC, Modifier.PUBLIC))) {
+            return emptyList();
+        }
+
+        try {
+            CallableInfo callableInfo = extractCallableInfo(input);
+            ImplementationInfoImpl implementationInfo = extractImplementationInfo(input);
+            List<RequestExtra> requestExtras = Collections.singletonList(new OriginatingElement(input));
+            return singletonList(new Result.Success(new CallInterceptionRequestImpl(callableInfo, implementationInfo, requestExtras)));
+        } catch (Failure e) {
+            return singletonList(new Result.InvalidRequest(e.reason));
+        }
+    }
+
+    private static CallableInfo extractCallableInfo(ExecutableElement methodElement) {
+        CallableKindInfo kindInfo = extractCallableKind(methodElement);
+        Type ownerType = extractOwnerClass(methodElement);
+        boolean interceptInherited = isInterceptInherited(methodElement);
+        CallableOwnerInfo owner = new CallableOwnerInfo(ownerType, interceptInherited);
+        String callableName = getCallableName(methodElement, kindInfo);
+        CallableReturnTypeInfo returnType = new CallableReturnTypeInfo(extractReturnType(methodElement));
+        List<ParameterInfo> parameterInfos = extractParameters(methodElement);
+        return new CallableInfoImpl(kindInfo, owner, callableName, returnType, parameterInfos);
+    }
+
+    @Nonnull
+    private static ImplementationInfoImpl extractImplementationInfo(ExecutableElement input) {
+        Type implementationOwner = extractType(input.getEnclosingElement().asType());
+        String implementationName = input.getSimpleName().toString();
+        String implementationDescriptor = extractMethodDescriptor(input);
+        return new ImplementationInfoImpl(implementationOwner, implementationName, implementationDescriptor);
+    }
+
+    private static String getCallableName(ExecutableElement methodElement, CallableKindInfo kindInfo) {
+        CallableDefinition.Name nameAnnotation = methodElement.getAnnotation(CallableDefinition.Name.class);
+        String nameFromPattern = callableNameFromNamingConvention(methodElement.getSimpleName().toString());
+        if (kindInfo == CallableKindInfo.AFTER_CONSTRUCTOR) {
+            if (nameAnnotation != null) {
+                throw new Failure("@" + CallableKind.AfterConstructor.class.getSimpleName() + " cannot be used with @" + CallableDefinition.Name.class.getSimpleName());
+            }
+            if (nameFromPattern != null) {
+                throw new Failure("Constructor interceptors cannot follow the 'intercept_*' name pattern");
+            }
+            return "<init>";
+        } else {
+            if (nameAnnotation == null) {
+                if (nameFromPattern == null) {
+                    throw new Failure("Expected the interceptor method to be annotated with @" + CallableDefinition.Name.class.getSimpleName() + " or to have the 'intercept_*' pattern in the name");
+                }
+                return nameFromPattern;
+            } else {
+                if (nameFromPattern != null) {
+                    throw new Failure("@" + CallableDefinition.Name.class.getSimpleName() + " cannot be used with method names following the 'intercept_*' pattern");
+                }
+            }
+            return nameAnnotation.value();
+        }
+    }
+
+    @Nullable
+    private static String callableNameFromNamingConvention(String methodName) {
+        if (!isInterceptPatternName(methodName)) {
+            return null;
+        }
+        return methodName.replace(INTERCEPT_PREFIX, "");
+    }
+
+    private static final String INTERCEPT_PREFIX = "intercept_";
+
+    private static boolean isInterceptPatternName(String methodName) {
+        return methodName.startsWith(INTERCEPT_PREFIX);
+    }
+
+    private static CallableKindInfo extractCallableKind(ExecutableElement methodElement) {
+        List<Annotation> kindAnnotations = Stream.of(CALLABLE_KIND_ANNOTATION_CLASSES)
+            .map(methodElement::getAnnotation)
+            .filter(Objects::nonNull)
+            .collect(Collectors.toList());
+
+        if (kindAnnotations.size() > 1) {
+            throw new Failure("More than one callable kind annotations present: " + kindAnnotations);
+        } else if (kindAnnotations.size() == 0) {
+            throw new Failure("No callable kind annotation specified, expected one of " + Arrays.stream(CALLABLE_KIND_ANNOTATION_CLASSES).map(Class::getSimpleName).collect(Collectors.joining(", ")));
+        } else {
+            return CallableKindInfo.fromAnnotation(kindAnnotations.get(0));
+        }
+    }
+
+    private static boolean isInterceptInherited(ExecutableElement methodElement) {
+        return methodElement.getAnnotation(InterceptInherited.class) != null;
+    }
+
+    private static List<ParameterInfo> extractParameters(ExecutableElement methodElement) {
+        List<ParameterInfo> list = new ArrayList<>();
+        List<? extends VariableElement> parameters = methodElement.getParameters();
+        for (int i = 0; i < parameters.size(); i++) {
+            VariableElement variableElement = parameters.get(i);
+            boolean isVararg = methodElement.isVarArgs() && i == parameters.size() - 1;
+            ParameterInfo parameterInfo = extractParameter(variableElement, isVararg);
+            list.add(parameterInfo);
+        }
+        return list;
+    }
+
+    private static ParameterInfo extractParameter(VariableElement parameterElement, boolean isVararg) {
+        Type parameterType = extractType(parameterElement.asType());
+        ParameterKindInfo parameterKindInfo = extractParameterKind(parameterElement, isVararg);
+
+        if (parameterKindInfo == ParameterKindInfo.VARARG_METHOD_PARAMETER) {
+            if (parameterType.getSort() != Type.ARRAY) {
+                throw new Failure("a @" + ParameterKind.VarargParameter.class.getSimpleName() + " parameter must have an array type");
+            }
+        }
+
+        return new ParameterInfoImpl(parameterElement.getSimpleName().toString(), parameterType, parameterKindInfo);
+    }
+
+    private static ParameterKindInfo extractParameterKind(VariableElement parameterElement, boolean isVararg) {
+        List<Annotation> kindAnnotations = Stream.of(PARAMETER_KIND_ANNOTATION_CLASSES)
+            .map(parameterElement::getAnnotation)
+            .filter(Objects::nonNull)
+            .collect(Collectors.toList());
+
+        if (kindAnnotations.size() > 1) {
+            throw new Failure("More than one parameter kind annotations present: " + kindAnnotations);
+        } else if (kindAnnotations.size() == 0) {
+            return isVararg ? ParameterKindInfo.VARARG_METHOD_PARAMETER : ParameterKindInfo.METHOD_PARAMETER;
+        } else {
+            ParameterKindInfo parameterKindInfo = ParameterKindInfo.fromAnnotation(kindAnnotations.get(0));
+            if (isVararg && parameterKindInfo != ParameterKindInfo.VARARG_METHOD_PARAMETER) {
+                throw new Failure("a vararg parameter can only be @" + ParameterKind.VarargParameter.class.getSimpleName() + " (maybe implicitly)");
+            }
+            return parameterKindInfo;
+        }
+    }
+
+    private static Type extractOwnerClass(ExecutableElement executableElement) {
+        Optional<? extends AnnotationMirror> maybeStaticMethod = findAnnotationMirror(executableElement, CallableKind.StaticMethod.class);
+        List<VariableElement> receivers = executableElement.getParameters().stream()
+            .filter(it -> it.getAnnotation(ParameterKind.Receiver.class) != null)
+            .collect(Collectors.toList());
+
+        if (maybeStaticMethod.isPresent()) {
+            if (receivers.size() > 0) {
+                throw new Failure("Static method interceptors should not declare @" + ParameterKind.Receiver.class.getSimpleName() + " parameters");
+            }
+            TypeMirror staticMethodOwner = (TypeMirror) findAnnotationValue(maybeStaticMethod.get(), "ofClass").orElseThrow(() -> new IllegalStateException("missing annotation value")).getValue();
+            return extractType(staticMethodOwner);
+        }
+
+        if (receivers.size() == 0) {
+            throw new Failure("Expected owner defined as a @" + ParameterKind.Receiver.class.getSimpleName() + " parameter or @" + CallableKind.StaticMethod.class.getSimpleName() + " annotation");
+        }
+        if (receivers.size() > 1) {
+            throw new Failure("Only one parameter can be annotated with @" + ParameterKind.Receiver.class.getSimpleName());
+        }
+        VariableElement receiver = receivers.get(0);
+        TypeMirror receiverType = receiver.asType();
+        if (receiverType.getKind() != TypeKind.DECLARED) {
+            throw new Failure("Receiver should be a class or interface, got " + receiverType);
+        }
+        return extractType(receiverType);
+    }
+
+    private static class Failure extends RuntimeException {
+        final String reason;
+
+        private Failure(String reason) {
+            this.reason = reason;
+        }
+    }
+
+    private static final Class<? extends Annotation>[] CALLABLE_KIND_ANNOTATION_CLASSES = Cast.uncheckedNonnullCast(new Class<?>[]{
+        CallableKind.InstanceMethod.class,
+        CallableKind.StaticMethod.class,
+        CallableKind.AfterConstructor.class,
+        CallableKind.GroovyProperty.class
+    });
+
+    private static final Class<? extends Annotation>[] PARAMETER_KIND_ANNOTATION_CLASSES = Cast.uncheckedNonnullCast(new Class<?>[]{
+        ParameterKind.Receiver.class,
+        ParameterKind.CallerClassName.class,
+        ParameterKind.KotlinDefaultMask.class,
+        ParameterKind.VarargParameter.class
+    });
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/AnnotationUtils.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/AnnotationUtils.java
new file mode 100644
index 0000000..25b4ca9
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/AnnotationUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.modelreader.impl;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import java.lang.annotation.Annotation;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+public class AnnotationUtils {
+    public static Optional<? extends AnnotationMirror> findMetaAnnotationMirror(Element element, Class<? extends Annotation> annotationClass) {
+        return collectMetaAnnotations(element).stream().filter(it -> isAnnotationOfType(it, annotationClass)).findFirst();
+    }
+
+    private static Set<AnnotationMirror> collectMetaAnnotations(Element annotatedElement) {
+        Set<AnnotationMirror> result = new LinkedHashSet<>();
+
+        class Recurse {
+            void recurse(Element annotatedElement) {
+                annotatedElement.getAnnotationMirrors().forEach(annotationMirror -> {
+                    Element element = annotationMirror.getAnnotationType().asElement();
+                    if (element instanceof TypeElement && result.add(annotationMirror)) {
+                        recurse(element);
+                    }
+                });
+            }
+        }
+
+        new Recurse().recurse(annotatedElement);
+        return result;
+    }
+
+    public static Optional<? extends AnnotationMirror> findAnnotationMirror(Element element, Class<? extends Annotation> annotationClass) {
+        return element.getAnnotationMirrors().stream().filter(it -> isAnnotationOfType(it, annotationClass)).findFirst();
+    }
+
+    public static Optional<? extends AnnotationValue> findAnnotationValue(AnnotationMirror annotation, String key) {
+        return annotation.getElementValues().entrySet().stream().filter(it -> it.getKey().getSimpleName().toString().equals(key)).map(Map.Entry::getValue).findFirst();
+    }
+
+    private static boolean isAnnotationOfType(AnnotationMirror annotation, Class<? extends Annotation> type) {
+        return annotation.getAnnotationType().toString().equals(type.getCanonicalName());
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/TypeMirrorToType.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/TypeMirrorToType.java
new file mode 100644
index 0000000..82a4458
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/TypeMirrorToType.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.modelreader.impl;
+
+import org.objectweb.asm.Type;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.UnionType;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.AbstractTypeVisitor8;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class TypeMirrorToType extends AbstractTypeVisitor8<Type, Void> {
+
+    @Override
+    public Type visitPrimitive(PrimitiveType t, Void unused) {
+        TypeKind kind = t.getKind();
+        if (kind == TypeKind.INT) {
+            return Type.INT_TYPE;
+        }
+        if (kind == TypeKind.BYTE) {
+            return Type.BYTE_TYPE;
+        }
+        if (kind == TypeKind.BOOLEAN) {
+            return Type.BOOLEAN_TYPE;
+        }
+        if (kind == TypeKind.DOUBLE) {
+            return Type.DOUBLE_TYPE;
+        }
+        if (kind == TypeKind.FLOAT) {
+            return Type.FLOAT_TYPE;
+        }
+        if (kind == TypeKind.LONG) {
+            return Type.LONG_TYPE;
+        }
+        if (kind == TypeKind.SHORT) {
+            return Type.SHORT_TYPE;
+        }
+        if (kind == TypeKind.VOID) {
+            return Type.VOID_TYPE;
+        }
+        throw unsupportedType(t);
+    }
+
+    @Override
+    public Type visitNoType(NoType t, Void unused) {
+        return Type.VOID_TYPE;
+    }
+
+    @Override
+    public Type visitNull(NullType t, Void unused) {
+        throw unsupportedType(t);
+    }
+
+    @Override
+    public Type visitArray(ArrayType t, Void unused) {
+        return Type.getObjectType("[" + visit(t.getComponentType(), null).getDescriptor());
+    }
+
+    @Override
+    public Type visitDeclared(DeclaredType t, Void unused) {
+        List<Element> typeNesting = new ArrayList<>();
+        Element current = t.asElement();
+        while (current != null) {
+            typeNesting.add(current);
+            current = current.getEnclosingElement();
+        }
+        Collections.reverse(typeNesting);
+
+        // TODO: replace with javax.lang.model.util.Elements.getBinaryName, which is a more universal way but requires refactoring and passing the utility around
+        StringBuilder typeName = new StringBuilder("L");
+        typeNesting.forEach(element -> {
+            if (element instanceof PackageElement) {
+                typeName.append(((PackageElement) element).getQualifiedName().toString().replace(".", "/")).append("/");
+            } else {
+                typeName.append(element.getSimpleName().toString());
+                if (element != typeNesting.get(typeNesting.size() - 1)) {
+                    typeName.append("$");
+                }
+            }
+        });
+        typeName.append(";");
+
+        return Type.getType(typeName.toString());
+    }
+
+    @Override
+    public Type visitError(ErrorType t, Void unused) {
+        throw unsupportedType(t);
+    }
+
+    @Override
+    public Type visitTypeVariable(TypeVariable t, Void unused) {
+        throw unsupportedType(t);
+    }
+
+    @Override
+    public Type visitWildcard(WildcardType t, Void unused) {
+        throw unsupportedType(t);
+    }
+
+    @Override
+    public Type visitExecutable(ExecutableType t, Void unused) {
+        throw unsupportedType(t);
+    }
+
+    @Override
+    public Type visitIntersection(IntersectionType t, Void unused) {
+        throw unsupportedType(t);
+    }
+
+    @Override
+    public Type visitUnion(UnionType t, Void unused) {
+        throw unsupportedType(t);
+    }
+
+    private static UnsupportedOperationException unsupportedType(TypeMirror t) {
+        return new UnsupportedOperationException("unsupported type " + t);
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/TypeUtils.java b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/TypeUtils.java
new file mode 100644
index 0000000..37240f3
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/java/org/gradle/internal/instrumentation/processor/modelreader/impl/TypeUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.modelreader.impl;
+
+import org.objectweb.asm.Type;
+
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+
+public class TypeUtils {
+    public static Type extractType(TypeMirror typeMirror) {
+        return typeMirror.accept(new TypeMirrorToType(), null);
+    }
+
+    public static Type extractReturnType(ExecutableElement methodElement) {
+        return extractType(methodElement.getReturnType());
+    }
+
+    public static String extractMethodDescriptor(ExecutableElement methodElement) {
+        return Type.getMethodDescriptor(
+            extractReturnType(methodElement),
+            methodElement.getParameters().stream().map(it -> extractType(it.asType())).toArray(Type[]::new)
+        );
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/main/resources/META-INF/gradle/incremental.annotation.processors b/subprojects/internal-instrumentation-processor/src/main/resources/META-INF/gradle/incremental.annotation.processors
new file mode 100644
index 0000000..50bbc02
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/resources/META-INF/gradle/incremental.annotation.processors
@@ -0,0 +1 @@
+org.gradle.internal.instrumentation.processor.ConfigurationCacheInstrumentationProcessor,aggregating
diff --git a/subprojects/internal-instrumentation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/subprojects/internal-instrumentation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..bb1880c
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1,17 @@
+#
+# Copyright 2023 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.
+#
+
+org.gradle.internal.instrumentation.processor.ConfigurationCacheInstrumentationProcessor
diff --git a/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/InstrumentationCodeGenTest.groovy b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/InstrumentationCodeGenTest.groovy
new file mode 100644
index 0000000..ec3f711
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/InstrumentationCodeGenTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 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.instrumentation
+
+import com.google.testing.compile.Compilation
+import com.google.testing.compile.JavaFileObjects
+import org.gradle.api.JavaVersion
+import org.gradle.internal.instrumentation.processor.ConfigurationCacheInstrumentationProcessor
+import org.gradle.internal.jvm.Jvm
+import spock.lang.Specification
+
+import javax.tools.JavaFileObject
+
+import static com.google.testing.compile.Compiler.javac
+
+abstract class InstrumentationCodeGenTest extends Specification {
+
+    protected static String fqName(JavaFileObject javaFile) {
+        return javaFile.name.replace("/", ".").replace(".java", "");
+    }
+
+    protected static JavaFileObject source(String source) {
+        def packageGroup = (source =~ "\\s*package ([\\w.]+).*")
+        String packageName = packageGroup.size() > 0 ? packageGroup[0][1] : ""
+        String className = (source =~ /(?s).*?(?:class|interface|enum) ([\w$]+) .*/)[0][1]
+        return packageName.isEmpty()
+            ? JavaFileObjects.forSourceString(className, source)
+            : JavaFileObjects.forSourceString("$packageName.$className", source);
+    }
+
+    protected static Compilation compile(JavaFileObject... fileObjects) {
+        return getCompiler()
+            .withProcessors(new ConfigurationCacheInstrumentationProcessor())
+            .compile(fileObjects)
+    }
+
+    private static com.google.testing.compile.Compiler getCompiler() {
+        if (Jvm.current().javaVersion.isCompatibleWith(JavaVersion.VERSION_1_9)) {
+            return javac().withOptions("--release=8")
+        }
+        return javac()
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeCodeGenTest.groovy b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeCodeGenTest.groovy
new file mode 100644
index 0000000..801b53c
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/extensions/property/PropertyUpgradeCodeGenTest.groovy
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2023 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.instrumentation.extensions.property
+
+import com.google.testing.compile.Compilation
+import org.gradle.internal.instrumentation.InstrumentationCodeGenTest
+
+import static com.google.testing.compile.CompilationSubject.assertThat
+import static org.gradle.internal.instrumentation.api.declarations.InterceptorDeclaration.GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES
+
+class PropertyUpgradeCodeGenTest extends InstrumentationCodeGenTest {
+
+    private static final String GENERATED_CLASSES_PACKAGE_NAME = GROOVY_INTERCEPTORS_GENERATED_CLASS_NAME_FOR_PROPERTY_UPGRADES
+        .split("\\.").dropRight(1).join(".")
+
+    def "should auto generate adapter for upgraded property with originalType"() {
+        given:
+        def givenSource = source """
+            package org.gradle.test;
+
+            import org.gradle.api.provider.Property;
+            import org.gradle.internal.instrumentation.api.annotations.VisitForInstrumentation;
+            import org.gradle.internal.instrumentation.api.annotations.UpgradedProperty;
+
+            @VisitForInstrumentation(value = {Task.class})
+            public abstract class Task {
+                @UpgradedProperty(originalType = int.class)
+                public abstract Property<Integer> getMaxErrors();
+            }
+        """
+
+        when:
+        Compilation compilation = compile(givenSource)
+
+        then:
+        def generatedClass = source """
+            package $GENERATED_CLASSES_PACKAGE_NAME;
+            import org.gradle.test.Task;
+
+            public class Task_Adapter {
+                public static int access_get_maxErrors(Task self) {
+                    return self.getMaxErrors().get();
+                }
+
+                public static void access_set_maxErrors(Task self, int arg0) {
+                    self.getMaxErrors().set(arg0);
+                }
+            }
+        """
+        assertThat(compilation).succeededWithoutWarnings()
+        assertThat(compilation)
+            .generatedSourceFile(fqName(generatedClass))
+            .hasSourceEquivalentTo(generatedClass)
+    }
+
+    def "should auto generate adapter for upgraded property with boolean"() {
+        given:
+        def givenSource = source """
+            package org.gradle.test;
+
+            import org.gradle.api.provider.Property;
+            import org.gradle.internal.instrumentation.api.annotations.VisitForInstrumentation;
+            import org.gradle.internal.instrumentation.api.annotations.UpgradedProperty;
+
+            @VisitForInstrumentation(value = {Task.class})
+            public abstract class Task {
+                @UpgradedProperty(originalType = boolean.class, fluentSetter = true)
+                public abstract Property<Boolean> getIncremental();
+            }
+        """
+
+        when:
+        Compilation compilation = compile(givenSource)
+
+        then:
+        def generatedClass = source """
+            package $GENERATED_CLASSES_PACKAGE_NAME;
+            public class InterceptorDeclaration_PropertyUpgradesJvmBytecode extends MethodVisitorScope implements JvmBytecodeCallInterceptor {
+                @Override
+                public boolean visitMethodInsn(String className, int opcode, String owner, String name,
+                                               String descriptor, boolean isInterface, Supplier<MethodNode> readMethodNode) {
+                    if (metadata.isInstanceOf(owner, "org/gradle/test/Task")) {
+                         if (name.equals("isIncremental") && descriptor.equals("()Z") && opcode == Opcodes.INVOKEVIRTUAL) {
+                             _INVOKESTATIC(TASK__ADAPTER_TYPE, "access_get_incremental", "(Lorg/gradle/test/Task;)Z");
+                             return true;
+                         }
+                     }
+                     if (metadata.isInstanceOf(owner, "org/gradle/test/Task")) {
+                      if (name.equals("setIncremental") && descriptor.equals("(Z)Lorg/gradle/test/Task;") && opcode == Opcodes.INVOKEVIRTUAL) {
+                             _INVOKESTATIC(TASK__ADAPTER_TYPE, "access_set_incremental", "(Lorg/gradle/test/Task;Z)Lorg/gradle/test/Task;");
+                             return true;
+                         }
+                     }
+                    return false;
+                }
+            }
+        """
+        def adapterClass = source """
+            package $GENERATED_CLASSES_PACKAGE_NAME;
+            import org.gradle.test.Task;
+
+            public class Task_Adapter {
+                public static boolean access_get_incremental(Task self) {
+                    return self.getIncremental().get();
+                }
+
+                public static Task access_set_incremental(Task self, boolean arg0) {
+                    self.getIncremental().set(arg0);
+                    return self;
+                }
+            }
+        """
+        assertThat(compilation).succeededWithoutWarnings()
+        assertThat(compilation)
+            .generatedSourceFile(fqName(generatedClass))
+            .containsElementsIn(generatedClass)
+        assertThat(compilation)
+            .generatedSourceFile(fqName(adapterClass))
+            .hasSourceEquivalentTo(adapterClass)
+    }
+
+    def "should auto generate adapter for upgraded property with type #upgradedType"() {
+        given:
+        def givenSource = source"""
+            package org.gradle.test;
+
+            import org.gradle.api.provider.*;
+            import org.gradle.api.file.*;
+            import org.gradle.internal.instrumentation.api.annotations.VisitForInstrumentation;
+            import org.gradle.internal.instrumentation.api.annotations.UpgradedProperty;
+
+            @VisitForInstrumentation(value = {Task.class})
+            public abstract class Task {
+                @UpgradedProperty
+                public abstract $upgradedType getProperty();
+            }
+        """
+
+        when:
+        Compilation compilation = compile(givenSource)
+
+        then:
+        def generatedClass = source """
+            package $GENERATED_CLASSES_PACKAGE_NAME;
+            import $fullImport;
+            import org.gradle.test.Task;
+
+            public class Task_Adapter {
+                public static $originalType access_get_property(Task self) {
+                    return self.getProperty()$getCall;
+                }
+
+                public static void access_set_property(Task self, $originalType arg0) {
+                    self.getProperty()$setCall;
+                }
+            }
+        """
+        assertThat(compilation).succeededWithoutWarnings()
+        assertThat(compilation)
+            .generatedSourceFile(fqName(generatedClass))
+            .hasSourceEquivalentTo(generatedClass)
+
+        where:
+        upgradedType                  | originalType     | getCall              | setCall            | fullImport
+        "Property<Integer>"           | "Integer"        | ".get()"             | ".set(arg0)"       | "java.lang.Integer"
+        "Property<String>"            | "String"         | ".get()"             | ".set(arg0)"       | "java.lang.String"
+        "ListProperty<String>"        | "List"           | ".get()"             | ".set(arg0)"       | "java.util.List"
+        "MapProperty<String, String>" | "Map"            | ".get()"             | ".set(arg0)"       | "java.util.Map"
+        "RegularFileProperty"         | "File"           | ".getAsFile().get()" | ".fileValue(arg0)" | "java.io.File"
+        "DirectoryProperty"           | "File"           | ".getAsFile().get()" | ".fileValue(arg0)" | "java.io.File"
+        "ConfigurableFileCollection"  | "FileCollection" | ""                   | ".setFrom(arg0)"   | "org.gradle.api.file.FileCollection"
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/ConfigurationCacheInstrumentationProcessorTest.groovy b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/ConfigurationCacheInstrumentationProcessorTest.groovy
new file mode 100644
index 0000000..76413a9
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/ConfigurationCacheInstrumentationProcessorTest.groovy
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2023 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.instrumentation.processor
+
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.intellij.lang.annotations.Language
+import org.junit.Rule
+import spock.lang.Specification
+
+import javax.tools.Diagnostic
+import javax.tools.DiagnosticCollector
+import javax.tools.JavaCompiler
+import javax.tools.JavaFileObject
+import javax.tools.StandardJavaFileManager
+import javax.tools.StandardLocation
+import javax.tools.ToolProvider
+
+class ConfigurationCacheInstrumentationProcessorTest extends Specification {
+
+    @Rule
+    final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider(getClass())
+    final TestFile srcDir = temporaryFolder.createDir('src')
+    final TestFile outDir = temporaryFolder.createDir('out')
+    final TestFile classesDir = temporaryFolder.createDir('classes')
+
+    def "code generation is deterministic regardless of type processing order"() {
+        given:
+        withAnnotatedType 'Foo'
+        withAnnotatedType 'Bar'
+        def annotatedTypes = ['my.Foo', 'my.Bar']
+
+        when:
+        DiagnosticCollector<JavaFileObject> diagnostics = processAnnotationsOf(annotatedTypes)
+
+        then:
+        errorsFrom(diagnostics).isEmpty()
+
+        and:
+        def outputFile = outDir.file('my/Interceptors.java')
+        outputFile.file
+
+        when:
+        def originalOutput = outputFile.text
+
+        and:
+        processAnnotationsOf(annotatedTypes.reverse())
+
+        then:
+        outputFile.text == originalOutput
+    }
+
+    def "code generation is deterministic regardless of method declaration order"() {
+        given:
+        withMethodDeclarationOrder 'foo', 'bar'
+
+        when:
+        DiagnosticCollector<JavaFileObject> diagnostics = processAnnotationsOf(['my.Test'])
+
+        then:
+        errorsFrom(diagnostics).isEmpty()
+
+        and:
+        def outputFile = outDir.file('my/Interceptors.java')
+        outputFile.file
+
+        when:
+        def originalOutput = outputFile.text
+        withMethodDeclarationOrder 'bar', 'foo'
+
+        and:
+        processAnnotationsOf(['my.Test'])
+
+        then:
+        outputFile.text == originalOutput
+    }
+
+    private withAnnotatedType(String typeName) {
+        sourceFile "my/${typeName}.java", """
+            package my;
+
+            import org.gradle.internal.instrumentation.api.annotations.*;
+
+            @SpecificJvmCallInterceptors(generatedClassName = "my.Interceptors")
+            class $typeName {
+                @InterceptCalls
+                @CallableKind.StaticMethod(ofClass = my.${typeName}.class)
+                @CallableDefinition.Name("stubTarget")
+                public static void stub() {}
+            }
+        """
+    }
+
+    private void withMethodDeclarationOrder(String first, String second) {
+        sourceFile 'my/Test.java', """
+            package my;
+
+            import org.gradle.internal.instrumentation.api.annotations.*;
+
+            @SpecificJvmCallInterceptors(generatedClassName = "my.Interceptors")
+            class Test {
+                @InterceptCalls
+                @CallableKind.StaticMethod(ofClass = my.Test.class)
+                @CallableDefinition.Name("${first}Target")
+                public static void ${first}() {}
+
+                @InterceptCalls
+                @CallableKind.StaticMethod(ofClass = my.Test.class)
+                @CallableDefinition.Name("${second}Target")
+                public static void ${second}() {}
+            }
+        """
+    }
+
+    private void sourceFile(String name, @Language('java') String source) {
+        srcDir.file(name).text = source.stripIndent()
+    }
+
+    private DiagnosticCollector<JavaFileObject> processAnnotationsOf(Iterable<String> classes) {
+        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>()
+        String targetVersion = '8'
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler()
+        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
+            fileManager.tap {
+                setLocation(StandardLocation.SOURCE_PATH, [srcDir])
+                setLocation(StandardLocation.SOURCE_OUTPUT, [outDir])
+                setLocation(StandardLocation.CLASS_OUTPUT, [classesDir])
+            }
+            Iterable<? extends JavaFileObject> compilationUnits = classes.collect {
+                fileManager.getJavaFileForInput(StandardLocation.SOURCE_PATH, it, JavaFileObject.Kind.SOURCE)
+            }
+            JavaCompiler.CompilationTask task = compiler.getTask(
+                null, fileManager, diagnostics,
+                ['-processor', ConfigurationCacheInstrumentationProcessor.name,
+                 '-source', targetVersion,
+                 '-target', targetVersion],
+                null, compilationUnits
+            )
+            assert task.call()
+        }
+        return diagnostics
+    }
+
+    private List<Boolean> errorsFrom(DiagnosticCollector<JavaFileObject> diagnostics) {
+        diagnostics
+            .getDiagnostics()
+            .findAll { it.kind == Diagnostic.Kind.ERROR }
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/codegen/groovy/InterceptGroovyCallsGeneratorTest.groovy b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/codegen/groovy/InterceptGroovyCallsGeneratorTest.groovy
new file mode 100644
index 0000000..c66b22d
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/codegen/groovy/InterceptGroovyCallsGeneratorTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen.groovy
+
+import com.google.testing.compile.Compilation
+import org.gradle.internal.instrumentation.InstrumentationCodeGenTest
+
+import static com.google.testing.compile.CompilationSubject.assertThat
+
+class InterceptGroovyCallsGeneratorTest extends InstrumentationCodeGenTest {
+
+    def "should generate interceptor with public modifier and public getCallInterceptors method"() {
+        given:
+        def givenSource = source """
+            package org.gradle.test;
+            import org.gradle.internal.instrumentation.api.annotations.*;
+            import org.gradle.internal.instrumentation.api.annotations.CallableKind.*;
+            import org.gradle.internal.instrumentation.api.annotations.ParameterKind.*;
+            import java.io.File;
+
+            @SpecificGroovyCallInterceptors(generatedClassName = "my.InterceptorDeclaration_GroovyBytecodeImpl")
+            public class FileInterceptorsDeclaration {
+                @InterceptCalls
+                @InstanceMethod
+                public static File[] intercept_listFiles(@Receiver File thisFile) {
+                    return new File[0];
+                }
+            }
+        """
+
+        when:
+        Compilation compilation = compile(givenSource)
+
+        then:
+        def expectedJvmInterceptors = source """
+            package my;
+            public class InterceptorDeclaration_GroovyBytecodeImpl {
+                public static List<CallInterceptor> getCallInterceptors() {
+                    return Arrays.asList(new ListFilesCallInterceptor());
+                }
+            }
+        """
+        assertThat(compilation).succeededWithoutWarnings()
+        assertThat(compilation)
+            .generatedSourceFile(fqName(expectedJvmInterceptors))
+            .containsElementsIn(expectedJvmInterceptors)
+    }
+}
diff --git a/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/codegen/jvmbytecode/InterceptJvmCallsGeneratorTest.groovy b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/codegen/jvmbytecode/InterceptJvmCallsGeneratorTest.groovy
new file mode 100644
index 0000000..ea193cc
--- /dev/null
+++ b/subprojects/internal-instrumentation-processor/src/test/groovy/org/gradle/internal/instrumentation/processor/codegen/jvmbytecode/InterceptJvmCallsGeneratorTest.groovy
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2023 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.instrumentation.processor.codegen.jvmbytecode
+
+import com.google.testing.compile.Compilation
+import org.gradle.internal.instrumentation.InstrumentationCodeGenTest
+
+import static com.google.testing.compile.CompilationSubject.assertThat
+
+class InterceptJvmCallsGeneratorTest extends InstrumentationCodeGenTest {
+
+    def "should generate interceptors for inherited calls"() {
+        given:
+        def givenSource = source """
+            package org.gradle.test;
+            import org.gradle.internal.instrumentation.api.annotations.*;
+            import org.gradle.internal.instrumentation.api.annotations.CallableKind.*;
+            import org.gradle.internal.instrumentation.api.annotations.ParameterKind.*;
+            import org.gradle.internal.classpath.declarations.*;
+            import java.io.File;
+
+            @SpecificJvmCallInterceptors(generatedClassName = "my.InterceptorDeclaration_JvmBytecodeImpl")
+            public class FileInterceptorsDeclaration {
+                @InterceptCalls
+                @InstanceMethod
+                @InterceptInherited
+                public static File[] intercept_listFiles(@Receiver File thisFile) {
+                    return new File[0];
+                }
+            }
+        """
+
+        when:
+        Compilation compilation = compile(givenSource)
+
+        then:
+        def expectedJvmInterceptors = source """
+            package my;
+            public class InterceptorDeclaration_JvmBytecodeImpl extends MethodVisitorScope implements JvmBytecodeCallInterceptor {
+
+                private final MethodVisitor methodVisitor;
+                private final InstrumentationMetadata metadata;
+
+                public InterceptorDeclaration_JvmBytecodeImpl(MethodVisitor methodVisitor, InstrumentationMetadata metadata) {
+                    super(methodVisitor);
+                    this.methodVisitor = methodVisitor;
+                    this.metadata = metadata;
+                }
+
+                @Override
+                public boolean visitMethodInsn(String className, int opcode, String owner, String name,
+                        String descriptor, boolean isInterface, Supplier<MethodNode> readMethodNode) {
+                    if (metadata.isInstanceOf(owner, "java/io/File")) {
+                        if (name.equals("listFiles") && descriptor.equals("()[Ljava/io/File;") && opcode == Opcodes.INVOKEVIRTUAL) {
+                            _INVOKESTATIC(FILE_INTERCEPTORS_DECLARATION_TYPE, "intercept_listFiles", "(Ljava/io/File;)[Ljava/io/File;");
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            }
+        """
+        assertThat(compilation).succeededWithoutWarnings()
+        assertThat(compilation)
+            .generatedSourceFile(fqName(expectedJvmInterceptors))
+            .containsElementsIn(expectedJvmInterceptors)
+    }
+
+    def "should generate interceptor with public modifier and public constructor"() {
+        given:
+        def givenSource = source """
+            package org.gradle.test;
+            import org.gradle.internal.instrumentation.api.annotations.*;
+            import org.gradle.internal.instrumentation.api.annotations.CallableKind.*;
+            import org.gradle.internal.instrumentation.api.annotations.ParameterKind.*;
+            import java.io.File;
+
+            @SpecificJvmCallInterceptors(generatedClassName = "my.InterceptorDeclaration_JvmBytecodeImpl")
+            public class FileInterceptorsDeclaration {
+                @InterceptCalls
+                @InstanceMethod
+                public static File[] intercept_listFiles(@Receiver File thisFile) {
+                    return new File[0];
+                }
+            }
+        """
+
+        when:
+        Compilation compilation = compile(givenSource)
+
+        then:
+        def expectedJvmInterceptors = source """
+            package my;
+            public class InterceptorDeclaration_JvmBytecodeImpl extends MethodVisitorScope implements JvmBytecodeCallInterceptor {
+
+                public InterceptorDeclaration_JvmBytecodeImpl(MethodVisitor methodVisitor, InstrumentationMetadata metadata) {
+                    super(methodVisitor);
+                    this.methodVisitor = methodVisitor;
+                    this.metadata = metadata;
+                }
+            }
+        """
+        assertThat(compilation).succeededWithoutWarnings()
+        assertThat(compilation)
+            .generatedSourceFile(fqName(expectedJvmInterceptors))
+            .containsElementsIn(expectedJvmInterceptors)
+    }
+}
diff --git a/subprojects/internal-integ-testing/build.gradle.kts b/subprojects/internal-integ-testing/build.gradle.kts
index 550f0f8..46459cb 100644
--- a/subprojects/internal-integ-testing/build.gradle.kts
+++ b/subprojects/internal-integ-testing/build.gradle.kts
@@ -117,9 +117,10 @@
     mostRecentSnapshot = moduleIdentity.releasedVersions.map { it.mostRecentSnapshot.version }
 }
 
-val copyAgpVersionsInfo by tasks.registering(Copy::class) {
+val copyTestedVersionsInfo by tasks.registering(Copy::class) {
     from(rootProject.layout.projectDirectory.file("gradle/dependency-management/agp-versions.properties"))
-    into(layout.buildDirectory.dir("generated-resources/agp-versions"))
+    from(rootProject.layout.projectDirectory.file("gradle/dependency-management/kotlin-versions.properties"))
+    into(layout.buildDirectory.dir("generated-resources/tested-versions"))
 }
 
 val generateLanguageAnnotations by tasks.registering(GenerateLanguageAnnotations::class) {
@@ -131,7 +132,7 @@
 sourceSets.main {
     groovy.srcDir(generateLanguageAnnotations.flatMap { it.destDir })
     output.dir(prepareVersionsInfo.map { it.destFile.get().asFile.parentFile })
-    output.dir(copyAgpVersionsInfo)
+    output.dir(copyTestedVersionsInfo)
 }
 
 @CacheableTask
diff --git a/subprojects/internal-integ-testing/src/integTest/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuterIntegrationTest.groovy b/subprojects/internal-integ-testing/src/integTest/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuterIntegrationTest.groovy
index f778020..21b05a7 100644
--- a/subprojects/internal-integ-testing/src/integTest/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuterIntegrationTest.groovy
+++ b/subprojects/internal-integ-testing/src/integTest/groovy/org/gradle/integtests/fixtures/executer/InProcessGradleExecuterIntegrationTest.groovy
@@ -19,12 +19,13 @@
 import org.gradle.api.logging.configuration.ConsoleOutput
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.gradle.util.internal.RedirectStdOutAndErr
 import org.junit.Rule
-import spock.lang.Requires
 import spock.lang.Specification
 
-@Requires({ GradleContextualExecuter.embedded })
+@Requires(IntegTestPreconditions.IsEmbeddedExecutor)
 class InProcessGradleExecuterIntegrationTest extends Specification {
     @Rule
     RedirectStdOutAndErr outputs = new RedirectStdOutAndErr()
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/bbottema/javasocksproxyserver/TestRecordingProxyHandler.java b/subprojects/internal-integ-testing/src/main/groovy/org/bbottema/javasocksproxyserver/TestRecordingProxyHandler.java
index 21859f2..c1d541c 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/bbottema/javasocksproxyserver/TestRecordingProxyHandler.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/bbottema/javasocksproxyserver/TestRecordingProxyHandler.java
@@ -62,4 +62,4 @@ public void processRelay() {
             throw new RuntimeException(e);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/bbottema/javasocksproxyserver/TestRecordingSocksServer.java b/subprojects/internal-integ-testing/src/main/groovy/org/bbottema/javasocksproxyserver/TestRecordingSocksServer.java
index d584989..51922e0 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/bbottema/javasocksproxyserver/TestRecordingSocksServer.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/bbottema/javasocksproxyserver/TestRecordingSocksServer.java
@@ -128,4 +128,4 @@ private void handleNextClient(ServerSocket listenSocket) {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationSpec.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationSpec.groovy
index 253e0ba..26896eb 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationSpec.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AbstractIntegrationSpec.groovy
@@ -18,6 +18,7 @@
 import org.eclipse.jgit.api.Git
 import org.eclipse.jgit.lib.Config
 import org.gradle.api.Action
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.integtests.fixtures.build.BuildTestFile
 import org.gradle.integtests.fixtures.build.BuildTestFixture
 import org.gradle.integtests.fixtures.configurationcache.ConfigurationCacheBuildOperationsFixture
@@ -71,6 +72,8 @@
     public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider(getClass())
     private TestFile testDirOverride = null
 
+    protected DocumentationRegistry documentationRegistry = new DocumentationRegistry()
+
     GradleDistribution distribution = new UnderDevelopmentGradleDistribution(getBuildContext())
     private GradleExecuter executor
     private boolean ignoreCleanupAssertions
@@ -159,6 +162,10 @@
         testDirectory.file(getDefaultBuildFileName())
     }
 
+    String getTestJunitCoordinates() {
+        return "junit:junit:4.13"
+    }
+
     void buildFile(@GroovyBuildScriptLanguage String script) {
         groovyFile(buildFile, script)
     }
@@ -180,7 +187,7 @@
     }
 
     TestFile getBuildKotlinFile() {
-        testDirectory.file(getDefaultBuildKotlinFileName())
+        testDirectory.file(defaultBuildKotlinFileName)
     }
 
     protected String getDefaultBuildFileName() {
@@ -393,6 +400,11 @@
         this
     }
 
+    AbstractIntegrationSpec withBuildCacheNg() {
+        executer.withBuildCacheNgEnabled()
+        this
+    }
+
     /**
      * Synonym for succeeds()
      */
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AvailableJavaHomes.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AvailableJavaHomes.java
index 74cb6e4..496eb99 100755
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AvailableJavaHomes.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/AvailableJavaHomes.java
@@ -32,17 +32,17 @@
 import org.gradle.internal.jvm.Jvm;
 import org.gradle.internal.jvm.inspection.CachingJvmMetadataDetector;
 import org.gradle.internal.jvm.inspection.DefaultJvmMetadataDetector;
+import org.gradle.internal.jvm.inspection.JavaInstallationRegistry;
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
 import org.gradle.internal.jvm.inspection.JvmMetadataDetector;
-import org.gradle.internal.operations.TestBuildOperationExecutor;
 import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.progress.NoOpProgressLoggerFactory;
 import org.gradle.jvm.toolchain.internal.AsdfInstallationSupplier;
 import org.gradle.jvm.toolchain.internal.CurrentInstallationSupplier;
 import org.gradle.jvm.toolchain.internal.InstallationLocation;
 import org.gradle.jvm.toolchain.internal.InstallationSupplier;
 import org.gradle.jvm.toolchain.internal.IntellijInstallationSupplier;
 import org.gradle.jvm.toolchain.internal.JabbaInstallationSupplier;
-import org.gradle.jvm.toolchain.internal.JavaInstallationRegistry;
 import org.gradle.jvm.toolchain.internal.LinuxInstallationSupplier;
 import org.gradle.jvm.toolchain.internal.MavenToolchainsInstallationSupplier;
 import org.gradle.jvm.toolchain.internal.OsXInstallationSupplier;
@@ -249,9 +249,10 @@ private static List<JvmInstallationMetadata> discoverLocalInstallations() {
         DefaultJvmMetadataDetector defaultJvmMetadataDetector =
             new DefaultJvmMetadataDetector(execHandleFactory, temporaryFileProvider);
         JvmMetadataDetector metadataDetector = new CachingJvmMetadataDetector(defaultJvmMetadataDetector);
-        final List<JvmInstallationMetadata> jvms = new JavaInstallationRegistry(defaultInstallationSuppliers(), new TestBuildOperationExecutor(), OperatingSystem.current())
-            .listInstallations().stream()
-            .map(metadataDetector::getMetadata)
+        final List<JvmInstallationMetadata> jvms = new JavaInstallationRegistry(defaultInstallationSuppliers(), metadataDetector, null, OperatingSystem.current(), new NoOpProgressLoggerFactory())
+            .toolchains()
+            .stream()
+            .map(x -> x.metadata)
             .filter(JvmInstallationMetadata::isValidInstallation)
             .sorted(Comparator.comparing(JvmInstallationMetadata::getDisplayName).thenComparing(JvmInstallationMetadata::getLanguageVersion))
             .collect(Collectors.toList());
@@ -293,6 +294,11 @@ private static class EnvVariableJvmLocator implements InstallationSupplier {
         private static final Pattern JDK_PATTERN = Pattern.compile("JDK\\d\\d?");
 
         @Override
+        public String getSourceName() {
+            return "JDK env variables";
+        }
+
+        @Override
         public Set<InstallationLocation> get() {
             return System.getenv().entrySet()
                 .stream()
@@ -311,6 +317,11 @@ private BaseDirJvmLocator(String baseDir) {
         }
 
         @Override
+        public String getSourceName() {
+            return "base dir " + baseDir.getName();
+        }
+
+        @Override
         public Set<InstallationLocation> get() {
             final File[] files = baseDir.listFiles();
             if (files != null) {
@@ -318,7 +329,7 @@ public Set<InstallationLocation> get() {
                     .filter(File::isDirectory)
                     .filter(file -> file.getName().toLowerCase().contains("jdk") || file.getName().toLowerCase().contains("jre"))
                     .filter(file -> new File(file, OperatingSystem.current().getExecutableName("bin/java")).exists())
-                    .map(file -> new InstallationLocation(file, "base dir " + baseDir.getName()))
+                    .map(file -> new InstallationLocation(file, getSourceName()))
                     .collect(Collectors.toSet());
             }
             return Collections.emptySet();
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/BuildOperationNotificationsFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/BuildOperationNotificationsFixture.groovy
index b7ccde8..d01d939 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/BuildOperationNotificationsFixture.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/BuildOperationNotificationsFixture.groovy
@@ -81,6 +81,10 @@
                 }
 
                 private void verify(Object obj, String suffix = null) {
+                    if (obj.getClass().getName().equals("org.gradle.internal.operations.OperationProgressDetails")) {
+                        // This progress notification is emitted by Gradle itself.
+                        return;
+                    }
                     def matchingInterfaces = findPublicInterfaces(obj, suffix)
                     if (matchingInterfaces.empty) {
                         if (suffix == null) {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/BuildScanUserInputFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/BuildScanUserInputFixture.groovy
index 5248e85..3d6a999 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/BuildScanUserInputFixture.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/BuildScanUserInputFixture.groovy
@@ -28,7 +28,6 @@
     public static final String NO = 'no'
     public static final byte EOF = 4
     public static final String PROMPT = "$QUESTION [$YES, $NO]"
-    public static final String BUILD_SCAN_ERROR_MESSAGE_HINT = 'Run with --scan to get full insights.'
     private static final String ANSWER_PREFIX = 'License accepted:'
 
     private BuildScanUserInputFixture() {}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ConfigurationUsageChangingFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ConfigurationUsageChangingFixture.groovy
new file mode 100644
index 0000000..2b5a3f9
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/ConfigurationUsageChangingFixture.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.integtests.fixtures
+
+import groovy.transform.SelfType
+
+/**
+ * Allows adding methods to the test class that are useful for testing configuration usage changes.
+ */
+@SelfType(AbstractIntegrationSpec)
+trait ConfigurationUsageChangingFixture {
+    void expectConsumableChanging(String configurationPath, boolean current) {
+        expectChangingUsage(configurationPath, "consumable", current)
+    }
+
+    void expectResolvableChanging(String configurationPath, boolean current) {
+        expectChangingUsage(configurationPath, "resolvable", current)
+    }
+
+    void expectDeclarableChanging(String configurationPath, boolean current) {
+        expectChangingUsage(configurationPath, "declarable", current)
+    }
+
+    void expectChangingUsage(String configurationPath, String usage, boolean current) {
+        executer.expectDocumentedDeprecationWarning("Allowed usage is changing for configuration '$configurationPath', $usage was ${!current} and is now $current. Ideally, usage should be fixed upon creation. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle 9.0. Usage should be fixed upon creation. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#configurations_allowed_usage")
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/RetryConditions.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/RetryConditions.groovy
index 1a45153..05c7569 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/RetryConditions.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/RetryConditions.groovy
@@ -18,8 +18,9 @@
 
 import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.daemon.DaemonLogsAnalyzer
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.GradleVersion
-import org.gradle.util.TestPrecondition
 
 import javax.annotation.Nullable
 
@@ -210,6 +211,6 @@
     }
 
     static boolean runsOnWindowsAndJava7or8() {
-        return TestPrecondition.WINDOWS.fulfilled && [JavaVersion.VERSION_1_7, JavaVersion.VERSION_1_8].contains(JavaVersion.current())
+        return TestPrecondition.doSatisfies(UnitTestPreconditions.Windows) && [JavaVersion.VERSION_1_7, JavaVersion.VERSION_1_8].contains(JavaVersion.current())
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/SuggestionsMessages.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/SuggestionsMessages.groovy
new file mode 100644
index 0000000..55204e0
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/SuggestionsMessages.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 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.integtests.fixtures
+
+/**
+ * A collection of suggestions to be displayed to the user when a build fails.
+ * These where repeated all over the test code, so they are now centralized here.
+ */
+
+class SuggestionsMessages {
+
+    public static final String INFO_DEBUG = "Run with --info or --debug option to get more log output."
+    public static final String DEBUG = "Run with --debug option to get more log output."
+    public static final String SCAN = "Run with --scan to get full insights."
+    public static final String GET_HELP = "Get more help at https://help.gradle.org."
+    public static final String STACKTRACE_MESSAGE = "Run with --stacktrace option to get the stack trace."
+
+    static String repositoryHint(String type){
+        "If the artifact you are trying to retrieve can be found in the repository but without metadata in '$type' format, you need to adjust the 'metadataSources { ... }' of the repository declaration."
+    }
+
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestNGExecutionResult.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestNGExecutionResult.groovy
index 70af526..81b53e0 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestNGExecutionResult.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/TestNGExecutionResult.groovy
@@ -153,6 +153,11 @@
         this
     }
 
+    @Override
+    TestClassExecutionResult assertTestsExecuted(TestCase ... testCases) {
+        throw new UnsupportedOperationException("Unsupported.  Implement if you need it.")
+    }
+
     TestClassExecutionResult assertTestPassed(String name) {
         def testMethodNode = findTestMethod(name)
         assert testMethodNode.@status as String == 'PASS'
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/BuildSpec.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/BuildSpec.groovy
new file mode 100644
index 0000000..b92848a
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/BuildSpec.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.integtests.fixtures.build
+
+import org.gradle.test.fixtures.file.TestFile
+
+class BuildSpec {
+    Map<String, TestFile> scripts
+    Map<String, TestFile> appliedScripts
+    Map<String, TestFile> jars
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/ProjectSourceRoots.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/ProjectSourceRoots.groovy
new file mode 100644
index 0000000..ec42449
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/ProjectSourceRoots.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.integtests.fixtures.build
+
+
+
+
+class ProjectSourceRoots {
+
+    final File projectDir
+    final List<String> sourceSets
+    final List<String> languages
+
+    ProjectSourceRoots(File projectDir, List<String> sourceSets, List<String> languages) {
+        this.projectDir = projectDir
+        this.sourceSets = sourceSets
+        this.languages = languages
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/TestProjectInitiation.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/TestProjectInitiation.groovy
new file mode 100644
index 0000000..e34be0a
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/build/TestProjectInitiation.groovy
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2023 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.integtests.fixtures.build
+
+import groovy.transform.CompileStatic
+import org.gradle.integtests.fixtures.RepoScriptBlockUtil
+import org.gradle.test.fixtures.dsl.GradleDsl
+import org.gradle.test.fixtures.file.TestFile
+
+import java.util.zip.ZipOutputStream
+
+import static org.gradle.util.internal.TextUtil.escapeString
+
+@CompileStatic
+trait TestProjectInitiation {
+
+    abstract TestFile file(Object... path)
+
+    String defaultSettingsScript = ""
+
+    String repositoriesBlock = """
+        repositories {
+            ${RepoScriptBlockUtil.gradlePluginRepositoryDefinition(GradleDsl.KOTLIN)}
+        }
+    """.stripIndent()
+
+    BuildSpec withMultiProjectBuildWithBuildSrc() {
+        withBuildSrc()
+        def someJar = withEmptyJar("classes_some.jar")
+        def settingsJar = withEmptyJar("classes_settings.jar")
+        def rootJar = withEmptyJar("classes_root.jar")
+        def aJar = withEmptyJar("classes_a.jar")
+        def bJar = withEmptyJar("classes_b.jar")
+        def precompiledJar = withEmptyJar("classes_b_precompiled.jar")
+
+        def some = withFile("some.gradle.kts", getBuildScriptDependency(someJar))
+        def settings = withSettings("""${getBuildScriptDependency(settingsJar)}
+            apply(from = "some.gradle.kts")
+            include("a", "b")
+        """)
+        def root = withBuildScript("""
+            ${getBuildScriptDependency(rootJar)}
+            apply(from = "some.gradle.kts")
+        """)
+        def a = withBuildScriptIn("a", """
+            ${getBuildScriptDependency(aJar)}
+            apply(from = "../some.gradle.kts")
+        """)
+        def b = withBuildScriptIn("b", """
+            plugins {
+                `kotlin-dsl`
+            }
+            ${getBuildScriptDependency(bJar)}
+            apply(from = "../some.gradle.kts")
+
+            $repositoriesBlock
+
+            dependencies {
+                implementation(files("${escapeString(precompiledJar)}"))
+            }
+        """)
+        def precompiled = withFile("b/src/main/kotlin/precompiled/precompiled.gradle.kts", "")
+        return new BuildSpec(
+            scripts: [
+                settings: settings,
+                root: root,
+                a: a,
+                b: b,
+                precompiled: precompiled
+            ],
+            appliedScripts: [
+                some: some
+            ],
+            jars: [
+                some: someJar,
+                settings: settingsJar,
+                root: rootJar,
+                a: aJar,
+                b: bJar,
+                precompiled: precompiledJar
+            ]
+        )
+    }
+
+    BuildSpec withMultiProject(){
+        def settings = withSettings("""
+            include("a", "b")
+        """)
+        def a = withBuildScriptIn("a", """
+        """)
+        def b = withBuildScriptIn("b", """
+        """)
+        return new BuildSpec(
+            scripts: [
+                settings: settings,
+                a: a,
+                b: b
+            ]
+        )
+    }
+
+    String getBuildScriptDependency(someJar) {
+        """
+            buildscript {
+                dependencies {
+                    classpath(files("${escapeString(someJar)}"))
+                }
+            }
+        """
+    }
+
+    TestFile withDefaultSettings() {
+        return withSettings(defaultSettingsScript)
+    }
+
+    TestFile withSettings(String script) {
+        return withSettingsIn(".", script)
+    }
+
+    TestFile withDefaultSettingsIn(String baseDir) {
+        return withSettingsIn(baseDir, defaultSettingsScript)
+    }
+
+    TestFile withSettingsIn(String baseDir, String script) {
+        return withFile("$baseDir/$settingsKotlinFileName", script)
+    }
+
+    TestFile withSettingsGroovy(String script) {
+        return withSettingsGroovyIn(".", script)
+    }
+
+    TestFile withDefaultSettingsGroovyIn(String baseDir) {
+        return withSettingsGroovyIn(baseDir, defaultSettingsScript)
+    }
+
+    TestFile withSettingsGroovyIn(String baseDir, String script) {
+        return withFile("$baseDir/$settingsFileName", script)
+    }
+
+    TestFile withBuildScript(String script = "") {
+        return withBuildScriptIn(".", script)
+    }
+
+    TestFile withBuildScriptIn(String baseDir, String script = "") {
+        return withFile("$baseDir/$defaultBuildKotlinFileName", script)
+    }
+
+    TestFile withFile(String path, String content = "") {
+        return file(path).tap { text = content.stripIndent() }
+    }
+
+    TestFile withEmptyJar(String path) {
+        return this.file(path).tap { jarFile ->
+            jarFile.parentFile.mkdirs()
+            new ZipOutputStream(jarFile.newOutputStream()).close()
+        }
+    }
+
+    void withBuildSrc() {
+        this.withFile("buildSrc/src/main/groovy/build/Foo.groovy", """
+            package build
+            class Foo {}
+        """)
+    }
+
+    def withKotlinBuildSrc() {
+        withDefaultSettingsIn("buildSrc")
+        withBuildScriptIn("buildSrc", """
+            plugins {
+                `kotlin-dsl`
+            }
+
+            $repositoriesBlock
+        """)
+    }
+
+    ProjectSourceRoots[] withMultiProjectKotlinBuildSrc() {
+        withDefaultSettingsIn("buildSrc").append("""
+            include(":a", ":b", ":c")
+        """)
+        withFile("buildSrc/$defaultBuildKotlinFileName", """
+            plugins {
+                java
+                `kotlin-dsl` apply false
+            }
+
+            val kotlinDslProjects = listOf(project.project(":a"), project.project(":b"))
+
+            kotlinDslProjects.forEach {
+                it.apply(plugin = "org.gradle.kotlin.kotlin-dsl")
+            }
+
+            dependencies {
+                kotlinDslProjects.forEach {
+                    "runtimeOnly"(project(it.path))
+                }
+            }
+
+            allprojects {
+                $repositoriesBlock
+            }
+        """)
+        withFile("buildSrc/b/$defaultBuildKotlinFileName", """dependencies { implementation(project(":c")) }""")
+        withFile("buildSrc/c/$defaultBuildKotlinFileName", "plugins { java }")
+
+        return [
+            withMainSourceSetJavaIn("buildSrc"),
+            withMainSourceSetJavaKotlinIn("buildSrc/a"),
+            withMainSourceSetJavaKotlinIn("buildSrc/b"),
+            withMainSourceSetJavaIn("buildSrc/c")
+        ] as ProjectSourceRoots[]
+    }
+
+    ProjectSourceRoots withMainSourceSetJavaIn(String projectDir) {
+        return new ProjectSourceRoots(file(projectDir), ["main"], ["java"])
+    }
+
+    ProjectSourceRoots withMainSourceSetJavaKotlinIn(String projectDir) {
+        return new ProjectSourceRoots(file(projectDir), ["main"], ["java", "kotlin"])
+    }
+
+    TestFile getBuildFile() {
+        file(defaultBuildFileName)
+    }
+
+    TestFile getPropertiesFile() {
+        file("gradle.properties")
+    }
+
+    TestFile getBuildFileKts() {
+        file(defaultBuildKotlinFileName)
+    }
+
+    TestFile getBuildKotlinFile() {
+        getBuildFileKts()
+    }
+
+    TestFile getSettingsKotlinFile(){
+        getSettingsFileKts()
+    }
+
+    TestFile getSettingsFile() {
+        file(settingsFileName)
+    }
+
+    TestFile getSettingsFileKts() {
+        file(settingsKotlinFileName)
+    }
+
+     String getSettingsFileName() {
+        'settings.gradle'
+    }
+
+    String getSettingsKotlinFileName() {
+        'settings.gradle.kts'
+    }
+
+    String getDefaultBuildFileName() {
+        'build.gradle'
+    }
+
+    String getDefaultBuildKotlinFileName() {
+        "build.gradle.kts"
+    }
+
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/configurationcache/ConfigurationCacheFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/configurationcache/ConfigurationCacheFixture.groovy
index f82ef92..3182112 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/configurationcache/ConfigurationCacheFixture.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/configurationcache/ConfigurationCacheFixture.groovy
@@ -24,7 +24,6 @@
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 
 class ConfigurationCacheFixture {
-    static final String CONFIGURATION_CACHE_MESSAGE = "Configuration cache is an incubating feature."
     static final String ISOLATED_PROJECTS_MESSAGE = "Isolated projects is an incubating feature."
     static final String CONFIGURE_ON_DEMAND_MESSAGE = "Configuration on demand is an incubating feature."
 
@@ -44,7 +43,6 @@
      * Asserts that the configuration cache was not enabled.
      */
     void assertNotEnabled() {
-        spec.outputDoesNotContain(CONFIGURATION_CACHE_MESSAGE)
         spec.outputDoesNotContain(ISOLATED_PROJECTS_MESSAGE)
         spec.outputDoesNotContain(CONFIGURE_ON_DEMAND_MESSAGE)
         configurationCacheBuildOperations.assertNoConfigurationCache()
@@ -240,7 +238,6 @@
             // Runs in quiet mode, and does not log anything
             return
         }
-        spec.outputContains(CONFIGURATION_CACHE_MESSAGE)
         spec.outputDoesNotContain(ISOLATED_PROJECTS_MESSAGE)
         spec.outputDoesNotContain(CONFIGURE_ON_DEMAND_MESSAGE)
     }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/configurationcache/ConfigurationCacheProblemsFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/configurationcache/ConfigurationCacheProblemsFixture.groovy
index 8cd4a28..9d0180c 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/configurationcache/ConfigurationCacheProblemsFixture.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/configurationcache/ConfigurationCacheProblemsFixture.groovy
@@ -208,8 +208,8 @@
     private static Matcher<String> failureDescriptionMatcherForTooManyProblems(HasConfigurationCacheProblemsSpec spec) {
         return buildMatcherForProblemsFailureDescription(
             "Maximum number of configuration cache problems has been reached.\n" +
-                "This behavior can be adjusted, " +
-                "see ${new DocumentationRegistry().getDocumentationFor("configuration_cache", "config_cache:usage:max_problems")}",
+                "This behavior can be adjusted. " +
+                new DocumentationRegistry().getDocumentationRecommendationFor("on this", "configuration_cache", "config_cache:usage:max_problems"),
             spec
         )
     }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractGradleExecuter.java
index b9fe5f3..a745f16 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/AbstractGradleExecuter.java
@@ -21,12 +21,10 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.common.io.ByteStreams;
-import com.google.common.io.CharSource;
 import groovy.lang.Closure;
 import groovy.lang.DelegatesTo;
 import org.gradle.api.Action;
 import org.gradle.api.JavaVersion;
-import org.gradle.api.UncheckedIOException;
 import org.gradle.api.internal.artifacts.ivyservice.ArtifactCachesProvider;
 import org.gradle.api.internal.initialization.DefaultClassLoaderScope;
 import org.gradle.api.logging.Logger;
@@ -39,7 +37,9 @@
 import org.gradle.internal.ImmutableActionSet;
 import org.gradle.internal.MutableActionSet;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.agents.AgentStatus;
 import org.gradle.internal.featurelifecycle.LoggingDeprecatedFeatureHandler;
+import org.gradle.internal.jvm.JavaHomeException;
 import org.gradle.internal.jvm.Jvm;
 import org.gradle.internal.jvm.inspection.JvmVersionDetector;
 import org.gradle.internal.logging.LoggingManagerInternal;
@@ -88,26 +88,30 @@
 
 import static java.util.stream.Collectors.joining;
 import static org.gradle.api.internal.artifacts.BaseRepositoryFactory.PLUGIN_PORTAL_OVERRIDE_URL_PROPERTY;
+import static org.gradle.caching.internal.controller.NextGenBuildCacheController.NEXT_GEN_CACHE_SYSTEM_PROPERTY;
 import static org.gradle.integtests.fixtures.RepoScriptBlockUtil.gradlePluginRepositoryMirrorUrl;
 import static org.gradle.integtests.fixtures.executer.AbstractGradleExecuter.CliDaemonArgument.DAEMON;
 import static org.gradle.integtests.fixtures.executer.AbstractGradleExecuter.CliDaemonArgument.FOREGROUND;
 import static org.gradle.integtests.fixtures.executer.AbstractGradleExecuter.CliDaemonArgument.NOT_DEFINED;
 import static org.gradle.integtests.fixtures.executer.AbstractGradleExecuter.CliDaemonArgument.NO_DAEMON;
 import static org.gradle.integtests.fixtures.executer.DocumentationUtils.normalizeDocumentationLink;
-import static org.gradle.integtests.fixtures.executer.OutputScrapingExecutionResult.STACK_TRACE_ELEMENT;
 import static org.gradle.internal.service.scopes.DefaultGradleUserHomeScopeServiceRegistry.REUSE_USER_HOME_SERVICES;
 import static org.gradle.util.internal.CollectionUtils.collect;
 import static org.gradle.util.internal.CollectionUtils.join;
 import static org.gradle.util.internal.DefaultGradleVersion.VERSION_OVERRIDE_VAR;
 
 public abstract class AbstractGradleExecuter implements GradleExecuter, ResettableExpectations {
+    private static final String DEBUG_SYSPROP = "org.gradle.integtest.debug";
+    private static final String LAUNCHER_DEBUG_SYSPROP = "org.gradle.integtest.launcher.debug";
+    private static final String PROFILE_SYSPROP = "org.gradle.integtest.profile";
+    private static final String ALLOW_INSTRUMENTATION_AGENT_SYSPROP = "org.gradle.integtest.agent.allowed";
 
     protected static final ServiceRegistry GLOBAL_SERVICES = ServiceRegistryBuilder.builder()
         .displayName("Global services")
         .parent(newCommandLineProcessLogging())
         .parent(NativeServicesTestFixture.getInstance())
         .parent(ValidationServicesFixture.getServices())
-        .provider(new GlobalScopeServices(true))
+        .provider(new GlobalScopeServices(true, AgentStatus.of(isAgentInstrumentationEnabled())))
         .build();
 
     private static final JvmVersionDetector JVM_VERSION_DETECTOR = GLOBAL_SERVICES.get(JvmVersionDetector.class);
@@ -123,10 +127,6 @@ public static void doNotPropagateSystemProperty(String name) {
         PROPAGATED_SYSTEM_PROPERTIES.remove(name);
     }
 
-    private static final String DEBUG_SYSPROP = "org.gradle.integtest.debug";
-    private static final String LAUNCHER_DEBUG_SYSPROP = "org.gradle.integtest.launcher.debug";
-    private static final String PROFILE_SYSPROP = "org.gradle.integtest.profile";
-
     private final Logger logger;
 
     protected final IntegrationTestBuildContext buildContext;
@@ -148,7 +148,7 @@ public static void doNotPropagateSystemProperty(String name) {
     private String executable;
     private TestFile gradleUserHomeDir;
     private File userHomeDir;
-    private File javaHome;
+    private String javaHome;
     private File buildScript;
     private File projectDir;
     private File settingsFile;
@@ -173,7 +173,7 @@ public static void doNotPropagateSystemProperty(String name) {
     private boolean disablePluginRepositoryMirror = false;
 
     private int expectedGenericDeprecationWarnings;
-    private final List<String> expectedDeprecationWarnings = new ArrayList<>();
+    private final List<ExpectedDeprecationWarning> expectedDeprecationWarnings = new ArrayList<>();
     private boolean eagerClassLoaderCreationChecksOn = true;
     private boolean stackTraceChecksOn = true;
     private boolean jdkWarningChecksOn = false;
@@ -616,18 +616,32 @@ public GradleExecuter withUserHomeDir(File userHomeDir) {
         return this;
     }
 
-    public File getJavaHome() {
-        return javaHome == null ? Jvm.current().getJavaHome() : javaHome;
+    protected String getJavaHome() {
+        return javaHome == null ? Jvm.current().getJavaHome().getAbsolutePath() : javaHome;
+    }
+
+    protected File getJavaHomeLocation() {
+        return new File(getJavaHome());
     }
 
     @Override
-    public GradleExecuter withJavaHome(File javaHome) {
+    public GradleExecuter withJavaHome(String javaHome) {
         this.javaHome = javaHome;
         return this;
     }
 
+    @Override
+    public GradleExecuter withJavaHome(File javaHome) {
+        this.javaHome = javaHome == null ? null : javaHome.getAbsolutePath();
+        return this;
+    }
+
     private JavaVersion getJavaVersionFromJavaHome() {
-        return JVM_VERSION_DETECTOR.getJavaVersion(Jvm.forHome(getJavaHome()));
+        try {
+            return JVM_VERSION_DETECTOR.getJavaVersion(Jvm.forHome(getJavaHomeLocation()));
+        } catch (IllegalArgumentException | JavaHomeException e) {
+            return JavaVersion.current();
+        }
     }
 
     @Override
@@ -835,6 +849,10 @@ public boolean isUseDaemon() {
         return requireDaemon || cliDaemonArgument == DAEMON;
     }
 
+    public static boolean isAgentInstrumentationEnabled() {
+        return Boolean.parseBoolean(System.getProperty(ALLOW_INSTRUMENTATION_AGENT_SYSPROP, "true"));
+    }
+
     @Override
     public GradleExecuter withOwnUserHomeServices() {
         useOwnUserHomeServices = true;
@@ -1068,6 +1086,11 @@ protected List<String> getAllArgs() {
             allArgs.add("-P" + AutoDetectingInstallationSupplier.AUTO_DETECT + "=false");
         }
 
+        boolean hasAgentArgument = args.stream().anyMatch(s -> s.contains(DaemonBuildOptions.ApplyInstrumentationAgentOption.GRADLE_PROPERTY));
+        if (!hasAgentArgument && !isAgentInstrumentationEnabled()) {
+            allArgs.add("-D" + DaemonBuildOptions.ApplyInstrumentationAgentOption.GRADLE_PROPERTY + "=false");
+        }
+
         allArgs.addAll(args);
         allArgs.addAll(tasks);
         return allArgs;
@@ -1281,6 +1304,11 @@ public GradleExecuter withBuildCacheEnabled() {
         return withArgument("--build-cache");
     }
 
+    @Override
+    public GradleExecuter withBuildCacheNgEnabled() {
+        return withArguments("--build-cache", "-D" + NEXT_GEN_CACHE_SYSTEM_PROPERTY + "=true");
+    }
+
     protected Action<ExecutionResult> getResultAssertion() {
         return new ResultAssertion(
             expectedGenericDeprecationWarnings, expectedDeprecationWarnings,
@@ -1288,164 +1316,6 @@ protected Action<ExecutionResult> getResultAssertion() {
         );
     }
 
-    private static class ResultAssertion implements Action<ExecutionResult> {
-        private int expectedGenericDeprecationWarnings;
-        private final List<String> expectedDeprecationWarnings;
-        private final boolean expectStackTraces;
-        private final boolean checkDeprecations;
-        private final boolean checkJdkWarnings;
-
-        private ResultAssertion(
-            int expectedGenericDeprecationWarnings, List<String> expectedDeprecationWarnings,
-            boolean expectStackTraces, boolean checkDeprecations, boolean checkJdkWarnings
-        ) {
-            this.expectedGenericDeprecationWarnings = expectedGenericDeprecationWarnings;
-            this.expectedDeprecationWarnings = new ArrayList<>(expectedDeprecationWarnings);
-            this.expectStackTraces = expectStackTraces;
-            this.checkDeprecations = checkDeprecations;
-            this.checkJdkWarnings = checkJdkWarnings;
-        }
-
-        @Override
-        public void execute(ExecutionResult executionResult) {
-            String normalizedOutput = executionResult.getNormalizedOutput();
-            String error = executionResult.getError();
-            boolean executionFailure = executionResult instanceof ExecutionFailure;
-
-            // for tests using rich console standard out and error are combined in output of execution result
-            if (executionFailure) {
-                normalizedOutput = removeExceptionStackTraceForFailedExecution(normalizedOutput);
-            }
-
-            validate(normalizedOutput, "Standard output");
-
-            if (executionFailure) {
-                error = removeExceptionStackTraceForFailedExecution(error);
-            }
-
-            validate(error, "Standard error");
-
-            if (!expectedDeprecationWarnings.isEmpty()) {
-                throw new AssertionError(String.format("Expected the following deprecation warnings:%n%s",
-                    expectedDeprecationWarnings.stream()
-                        .map(warning -> " - " + warning)
-                        .collect(joining("\n"))));
-            }
-            if (expectedGenericDeprecationWarnings > 0) {
-                throw new AssertionError(String.format("Expected %d more deprecation warnings", expectedGenericDeprecationWarnings));
-            }
-        }
-
-        // Axe everything after the expected exception
-        private String removeExceptionStackTraceForFailedExecution(String text) {
-            int pos = text.indexOf("* Exception is:");
-            if (pos >= 0) {
-                text = text.substring(0, pos);
-            }
-            return text;
-        }
-
-        private void validate(String output, String displayName) {
-            List<String> lines;
-            try {
-                lines = CharSource.wrap(output).readLines();
-            } catch (IOException e) {
-                throw new UncheckedIOException(e);
-            }
-            int i = 0;
-            boolean insideVariantDescriptionBlock = false;
-            boolean insideKotlinCompilerFlakyStacktrace = false;
-            boolean sawVmPluginLoadFailure = false;
-            while (i < lines.size()) {
-                String line = lines.get(i);
-                if (insideVariantDescriptionBlock && line.contains("]")) {
-                    insideVariantDescriptionBlock = false;
-                } else if (!insideVariantDescriptionBlock && line.contains("variant \"")) {
-                    insideVariantDescriptionBlock = true;
-                }
-
-                // https://youtrack.jetbrains.com/issue/KT-29546
-                if (line.contains("Compilation with Kotlin compile daemon was not successful")) {
-                    insideKotlinCompilerFlakyStacktrace = true;
-                    i++;
-                } else if (line.contains("Trying to create VM plugin `org.codehaus.groovy.vmplugin.v9.Java9` by checking `java.lang.Module`")) {
-                    // a groovy warning when running on Java < 9
-                    // https://issues.apache.org/jira/browse/GROOVY-9933
-                    i++; // full stracktrace skipped in next branch
-                    sawVmPluginLoadFailure = true;
-                } else if (line.contains("java.lang.ClassNotFoundException: java.lang.Module") && sawVmPluginLoadFailure) {
-                    // a groovy warning when running on Java < 9
-                    // https://issues.apache.org/jira/browse/GROOVY-9933
-                    i++;
-                    i = skipStackTrace(lines, i);
-                } else if (insideKotlinCompilerFlakyStacktrace &&
-                    (line.contains("java.rmi.UnmarshalException") ||
-                        line.contains("java.io.EOFException")) ||
-                    // Verbose logging by Jetty when connector is shutdown
-                    // https://github.com/eclipse/jetty.project/issues/3529
-                    line.contains("java.nio.channels.CancelledKeyException")) {
-                    i++;
-                    i = skipStackTrace(lines, i);
-                } else if (line.contains("com.amazonaws.http.IdleConnectionReaper")) {
-                    /*
-                    2021-01-05T08:15:51.329+0100 [DEBUG] [com.amazonaws.http.IdleConnectionReaper] Reaper thread:
-                    java.lang.InterruptedException: sleep interrupted
-                        at java.base/java.lang.Thread.sleep(Native Method)
-                        at com.amazonaws.http.IdleConnectionReaper.run(IdleConnectionReaper.java:188)
-                     */
-                    i += 2;
-                    i = skipStackTrace(lines, i);
-                } else if (line.matches(".*use(s)? or override(s)? a deprecated API\\.")) {
-                    // A javac warning, ignore
-                    i++;
-                } else if (line.matches(".*w: .* is deprecated\\..*")) {
-                    // A kotlinc warning, ignore
-                    i++;
-                } else if (line.matches("\\[Warn] :.* is deprecated: .*")) {
-                    // A scalac warning, ignore
-                    i++;
-                } else if (isDeprecationMessageInHelpDescription(line)) {
-                    i++;
-                } else if (removeFirstExpectedDeprecationWarning(line)) {
-                    // Deprecation warning is expected
-                    i++;
-                    i = skipStackTrace(lines, i);
-                } else if (line.matches(".*\\s+deprecated.*")) {
-                    if (checkDeprecations && expectedGenericDeprecationWarnings <= 0) {
-                        throw new AssertionError(String.format("%s line %d contains a deprecation warning: %s%n=====%n%s%n=====%n", displayName, i + 1, line, output));
-                    }
-                    expectedGenericDeprecationWarnings--;
-                    // skip over stack trace
-                    i++;
-                    i = skipStackTrace(lines, i);
-                } else if (!expectStackTraces && !insideVariantDescriptionBlock && STACK_TRACE_ELEMENT.matcher(line).matches() && i < lines.size() - 1 && STACK_TRACE_ELEMENT.matcher(lines.get(i + 1)).matches()) {
-                    // 2 or more lines that look like stack trace elements
-                    throw new AssertionError(String.format("%s line %d contains an unexpected stack trace: %s%n=====%n%s%n=====%n", displayName, i + 1, line, output));
-                } else if (checkJdkWarnings && line.matches("\\s*WARNING:.*")) {
-                    throw new AssertionError(String.format("%s line %d contains unexpected JDK warning: %s%n=====%n%s%n=====%n", displayName, i + 1, line, output));
-                } else {
-                    i++;
-                }
-            }
-        }
-
-        private boolean removeFirstExpectedDeprecationWarning(String line) {
-            return expectedDeprecationWarnings.stream().filter(line::contains).findFirst()
-                .map(expectedDeprecationWarnings::remove).orElse(false);
-        }
-
-        private static int skipStackTrace(List<String> lines, int i) {
-            while (i < lines.size() && STACK_TRACE_ELEMENT.matcher(lines.get(i)).matches()) {
-                i++;
-            }
-            return i;
-        }
-
-        private boolean isDeprecationMessageInHelpDescription(String s) {
-            return s.matches(".*\\[deprecated.*]");
-        }
-    }
-
     @Override
     public GradleExecuter expectDeprecationWarning() {
         return expectDeprecationWarnings(1);
@@ -1460,14 +1330,14 @@ public GradleExecuter expectDeprecationWarnings(int count) {
     }
 
     @Override
-    public GradleExecuter expectDeprecationWarning(String warning) {
+    public GradleExecuter expectDeprecationWarning(ExpectedDeprecationWarning warning) {
         expectedDeprecationWarnings.add(warning);
         return this;
     }
 
     @Override
-    public GradleExecuter expectDocumentedDeprecationWarning(String warning) {
-        return expectDeprecationWarning(normalizeDocumentationLink(warning));
+    public GradleExecuter expectDocumentedDeprecationWarning(ExpectedDeprecationWarning warning) {
+        return expectDeprecationWarning(normalizeDocumentationLink(warning.getMessage()));
     }
 
     @Override
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DefaultGradleDistribution.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DefaultGradleDistribution.groovy
index e0dccf3..178c5ae 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DefaultGradleDistribution.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DefaultGradleDistribution.groovy
@@ -321,6 +321,11 @@
         return stdout;
     }
 
+    @Override
+    boolean isSupportsKotlinScript() {
+        return isSameOrNewer("4.10.3"); // see compatibility matrix https://docs.gradle.org/8.0/userguide/compatibility.html
+    }
+
     protected boolean isSameOrNewer(String otherVersion) {
         return isVersion(otherVersion) || version.compareTo(GradleVersion.version(otherVersion)) > 0;
     }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DocumentationUtils.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DocumentationUtils.java
index 1f90361..3222a38 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DocumentationUtils.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/DocumentationUtils.java
@@ -16,16 +16,13 @@
 
 package org.gradle.integtests.fixtures.executer;
 
-import org.gradle.util.GradleVersion;
+import static org.gradle.api.internal.DocumentationRegistry.BASE_URL;
 
 public class DocumentationUtils {
+    public static final String DOCS_GRADLE_ORG = "https://docs.gradle.org/";
+    public static final String PATTERN = DOCS_GRADLE_ORG + "current/";
+
     public static String normalizeDocumentationLink(String message) {
-        String pattern = "https://docs.gradle.org/current/";
-        String replacement = "https://docs.gradle.org/" + GradleVersion.current().getVersion() + "/";
-        String expectedMessage = message.replace(pattern, replacement);
-        if (message.equals(expectedMessage)) {
-            throw new IllegalArgumentException("Message must reference '" + pattern + "'.");
-        }
-        return expectedMessage;
+        return message.replace(PATTERN, BASE_URL + "/");
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ExpectedDeprecationWarning.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ExpectedDeprecationWarning.java
new file mode 100644
index 0000000..7d62131
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ExpectedDeprecationWarning.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 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.integtests.fixtures.executer;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
+/**
+ * Represents a deprecation warning message that is expected to be emitted by a test.
+ * <p>
+ * This class exists to support the detection of deprecation warnings that span multiple lines,
+ * which may include the keyword "{@code deprecated}" multiple times.
+ */
+public final class ExpectedDeprecationWarning {
+    private final String message;
+    private final int numLines;
+
+    public ExpectedDeprecationWarning(String message) {
+        Preconditions.checkArgument(message != null && !message.isEmpty(), "message must not be null or empty");
+        this.message = message;
+        this.numLines = message.split("\n").length;
+    }
+
+    /**
+     * Get the (possibly multi-line) message that is expected to be emitted by a test.
+     * @return the expected message
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Get the number of lines that the expected message spans.
+     * @return the number of lines in this message
+     */
+    public int getNumLines() {
+        return numLines;
+    }
+
+    /**
+     * Check if the given lines, starting at the given index, match the expected message.
+     * @param lines the lines to check
+     * @param startIndex the index of the first line to check
+     * @return {@code true} if the lines match the expected message, {@code false} otherwise
+     */
+    public boolean matchesNextLines(List<String> lines, int startIndex) {
+        if (numLines == 1) {
+            return lines.get(startIndex).equals(message); // Quicker match for single-line warnings
+        } else {
+            String actualLines = String.join("\n", lines.subList(startIndex, Math.min(startIndex + numLines, lines.size())));
+            return message.equals(actualLines);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return message;
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleDistribution.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleDistribution.java
index 42a3b0a..2b2baea 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleDistribution.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleDistribution.java
@@ -175,4 +175,9 @@ public interface GradleDistribution {
      * Returns true if this version runs tests when building `buildSrc`
      */
     boolean isRunsBuildSrcTests();
+
+    /**
+     * Returns true if it as a Gradle version that supports Kotlin scripts
+     */
+    boolean isSupportsKotlinScript();
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleExecuter.java
index 4b4ffd7..e55c14e 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/GradleExecuter.java
@@ -118,6 +118,11 @@ public interface GradleExecuter extends Stoppable {
     /**
      * Sets the java home dir. Setting to null requests that the executer use the real default java home dir rather than the default used for testing.
      */
+    GradleExecuter withJavaHome(String userHomeDir);
+
+    /**
+     * Sets the java home dir. Setting to null requests that the executer use the real default java home dir rather than the default used for testing.
+     */
     GradleExecuter withJavaHome(File userHomeDir);
 
     /**
@@ -199,6 +204,13 @@ public void connect(PipedInputStream snk) throws IOException {
     GradleExecuter withBuildCacheEnabled();
 
     /**
+     * Activates the "Next Gen" build cache
+     *
+     * @return this executer
+     */
+    GradleExecuter withBuildCacheNgEnabled();
+
+    /**
      * Don't set native services dir explicitly.
      */
     GradleExecuter withNoExplicitNativeServicesDir();
@@ -340,12 +352,20 @@ public void connect(PipedInputStream snk) throws IOException {
      * also switch to the more specific {@link #expectDocumentedDeprecationWarning(String)} if the warning includes a documentation
      * link and you don't want to (ironically) see code testing deprecation appearing as if it itself were deprecated.
      */
-    GradleExecuter expectDeprecationWarning(String warning);
+    default GradleExecuter expectDeprecationWarning(String warning) {
+        return expectDeprecationWarning(new ExpectedDeprecationWarning(warning));
+    }
+
+    GradleExecuter expectDeprecationWarning(ExpectedDeprecationWarning warning);
 
     /**
      * Expects the given deprecation warning, allowing to pass documentation url with /current/ version and asserting against the actual current version instead.
      */
-    GradleExecuter expectDocumentedDeprecationWarning(String warning);
+    default GradleExecuter expectDocumentedDeprecationWarning(String warning) {
+        return expectDocumentedDeprecationWarning(new ExpectedDeprecationWarning(warning));
+    }
+
+    GradleExecuter expectDocumentedDeprecationWarning(ExpectedDeprecationWarning warning);
 
     /**
      * Expects exactly the given number of deprecation warnings. If fewer or more warnings are produced during
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 f9652db..201d5e2 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
@@ -48,6 +48,8 @@
 import org.gradle.internal.IoActions;
 import org.gradle.internal.SystemProperties;
 import org.gradle.internal.UncheckedException;
+import org.gradle.internal.agents.AgentInitializer;
+import org.gradle.internal.agents.AgentUtils;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.event.ListenerManager;
@@ -61,6 +63,7 @@
 import org.gradle.launcher.Main;
 import org.gradle.launcher.cli.Parameters;
 import org.gradle.launcher.cli.ParametersConverter;
+import org.gradle.launcher.daemon.configuration.DaemonBuildOptions;
 import org.gradle.launcher.exec.BuildActionExecuter;
 import org.gradle.launcher.exec.BuildActionParameters;
 import org.gradle.launcher.exec.BuildActionResult;
@@ -131,6 +134,8 @@ public class InProcessGradleExecuter extends DaemonGradleExecuter {
     static {
         LoggingManagerInternal loggingManager = GLOBAL_SERVICES.getFactory(LoggingManagerInternal.class).create();
         loggingManager.start();
+
+        GLOBAL_SERVICES.get(AgentInitializer.class).maybeConfigureInstrumentationAgent();
     }
 
     public InProcessGradleExecuter(GradleDistribution distribution, TestDirectoryProvider testDirectoryProvider) {
@@ -196,7 +201,7 @@ protected ExecutionFailure doRunWithFailure() {
     }
 
     private boolean isForkRequired() {
-        if (isDaemonExplicitlyRequired() || !getJavaHome().equals(Jvm.current().getJavaHome())) {
+        if (isDaemonExplicitlyRequired() || !getJavaHomeLocation().equals(Jvm.current().getJavaHome())) {
             return true;
         }
         File gradleProperties = new File(getWorkingDir(), "gradle.properties");
@@ -204,7 +209,10 @@ private boolean isForkRequired() {
             Properties properties = GUtil.loadProperties(gradleProperties);
             return properties.getProperty("org.gradle.java.home") != null || properties.getProperty("org.gradle.jvmargs") != null;
         }
-        return false;
+        boolean isInstrumentationEnabledForProcess = isAgentInstrumentationEnabled();
+        boolean differentInstrumentationRequested = getAllArgs().stream().anyMatch(
+            ("-D" + DaemonBuildOptions.ApplyInstrumentationAgentOption.GRADLE_PROPERTY + "=" + !isInstrumentationEnabledForProcess)::equals);
+        return differentInstrumentationRequested;
     }
 
     private <T extends ExecutionResult> T assertResult(T result) {
@@ -225,9 +233,13 @@ protected Factory<JavaExecHandleBuilder> getExecHandleFactory() {
             GradleInvocation invocation = buildInvocation();
             JavaExecHandleBuilder builder = TestFiles.execFactory().newJavaExec();
             builder.workingDir(getWorkingDir());
-            builder.setExecutable(new File(getJavaHome(), "bin/java"));
+            builder.setExecutable(new File(getJavaHomeLocation(), "bin/java"));
             builder.classpath(getExecHandleFactoryClasspath());
             builder.jvmArgs(invocation.launcherJvmArgs);
+            // Apply the agent to the newly created daemon. The feature flag decides if it is going to be used.
+            for (File agent : cleanup(GLOBAL_SERVICES.get(ModuleRegistry.class).getModule(AgentUtils.AGENT_MODULE_NAME).getClasspath().getAsFiles())) {
+                builder.jvmArgs("-javaagent:" + agent.getAbsolutePath());
+            }
             builder.environment(invocation.environmentVars);
 
             builder.getMainClass().set(Main.class.getName());
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/LogContent.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/LogContent.java
index b7c4dbc..94ba869 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/LogContent.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/LogContent.java
@@ -18,6 +18,7 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
 import net.rubygrapefruit.ansi.AnsiParser;
 import net.rubygrapefruit.ansi.console.AnsiConsole;
 import net.rubygrapefruit.ansi.console.DiagnosticConsole;
@@ -57,7 +58,7 @@ public static LogContent of(String chars) {
     }
 
     private static ImmutableList<String> toLines(String chars) {
-        List<String> lines = new ArrayList<String>();
+        Builder<String> lines = ImmutableList.builder();
         int pos = 0;
         while (pos < chars.length()) {
             int next = chars.indexOf('\n', pos);
@@ -66,19 +67,22 @@ private static ImmutableList<String> toLines(String chars) {
                 pos = chars.length();
                 continue;
             }
-            if (next > pos && chars.charAt(next - 1) == '\r') {
-                lines.add(chars.substring(pos, next - 1));
-                pos = next + 1;
-            } else {
-                lines.add(chars.substring(pos, next));
-                pos = next + 1;
-            }
+
+            lines.add(chars.substring(pos, getSubstringEnd(chars, pos, next)));
+            pos = next + 1;
             if (pos == chars.length()) {
                 // trailing EOL
                 lines.add("");
             }
         }
-        return ImmutableList.copyOf(lines);
+        return lines.build();
+    }
+
+    private static int getSubstringEnd(String chars, int pos, int next) {
+        if (next > pos && chars.charAt(next - 1) == '\r') {
+             return next - 1;
+        }
+        return next;
     }
 
     /**
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/NoDaemonGradleExecuter.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/NoDaemonGradleExecuter.java
index 9a556b5..6a6809e 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/NoDaemonGradleExecuter.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/NoDaemonGradleExecuter.java
@@ -110,10 +110,8 @@ protected void transformInvocation(GradleInvocation invocation) {
         final String value = toJvmArgsString(invocation.launcherJvmArgs);
         environmentVars.put(jvmOptsEnvVar, value);
 
-        // Add a JAVA_HOME if none provided
-        if (!environmentVars.containsKey("JAVA_HOME")) {
-            environmentVars.put("JAVA_HOME", getJavaHome().getAbsolutePath());
-        }
+        // Always set JAVA_HOME, so the daemon process runs on the configured JVM
+        environmentVars.put("JAVA_HOME", getJavaHome());
     }
 
     @Override
@@ -127,7 +125,7 @@ protected List<String> getAllArgs() {
     protected List<String> getImplicitBuildJvmArgs() {
         List<String> buildJvmOptions = super.getImplicitBuildJvmArgs();
         final Jvm current = Jvm.current();
-        if (getJavaHome().equals(current.getJavaHome()) && JavaVersion.current().isJava9Compatible() && !isUseDaemon()) {
+        if (getJavaHomeLocation().equals(current.getJavaHome()) && JavaVersion.current().isJava9Compatible() && !isUseDaemon()) {
             buildJvmOptions.addAll(JpmsConfiguration.GRADLE_DAEMON_JPMS_ARGS);
         }
         return buildJvmOptions;
@@ -145,7 +143,7 @@ private void addPropagatedSystemProperties(List<String> args) {
     @Override
     protected boolean supportsWhiteSpaceInEnvVars() {
         final Jvm current = Jvm.current();
-        if (getJavaHome().equals(current.getJavaHome())) {
+        if (getJavaHomeLocation().equals(current.getJavaHome())) {
             // we can tell for sure
             return current.getJavaVersion().isJava7Compatible();
         } else {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionFailure.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionFailure.java
index 88da72e..03f3b04 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionFailure.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingExecutionFailure.java
@@ -21,12 +21,15 @@
 import org.hamcrest.Matcher;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
 
+import static java.util.stream.Collectors.joining;
+import static org.apache.commons.lang.StringUtils.repeat;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.hasItem;
@@ -38,7 +41,7 @@ public class OutputScrapingExecutionFailure extends OutputScrapingExecutionResul
     private static final Pattern CAUSE_PATTERN = Pattern.compile("(?m)(^\\s*> )");
     private static final Pattern DESCRIPTION_PATTERN = Pattern.compile("(?ms)^\\* What went wrong:$(.+?)^\\* Try:$");
     private static final Pattern LOCATION_PATTERN = Pattern.compile("(?ms)^\\* Where:((.+?)'.+?') line: (\\d+)$");
-    private static final Pattern RESOLUTION_PATTERN = Pattern.compile("(?ms)^\\* Try:$(.+?)^\\* (?:Exception is:|Get more help at https://help\\.gradle\\.org)$");
+    private static final Pattern RESOLUTION_PATTERN = Pattern.compile("(?ms)^\\* Try:$\\n(.+?)\\n(?:Exception is:)?$");
     private final String summary;
     private final List<Problem> problems = new ArrayList<>();
     private final List<Problem> problemsNotChecked = new ArrayList<>();
@@ -154,11 +157,7 @@ private Problem extract(String problem) {
     }
 
     private String toPrefixPattern(int prefix) {
-        StringBuilder builder = new StringBuilder("(?m)^");
-        for (int i = 0; i < prefix; i++) {
-            builder.append(' ');
-        }
-        return builder.toString();
+        return "(?m)^" + repeat(" ", prefix);
     }
 
     @Override
@@ -215,16 +214,10 @@ public ExecutionFailure assertHasResolution(String resolution) {
 
     @Override
     public ExecutionFailure assertHasResolutions(String... resolutions) {
-        StringBuilder builder = new StringBuilder();
-        for (int i = 0; i < resolutions.length; i++) {
-            String expected = resolutions[i];
-            if (i > 0) {
-                builder.append("\n");
-            }
-            builder.append("> ");
-            builder.append(expected);
-        }
-        assertThat(this.resolution, equalTo(builder.toString()));
+        String expected = Arrays.stream(resolutions)
+            .map(resolution -> "> " + resolution)
+            .collect(joining("\n"));
+        assertThat(this.resolution, equalTo(expected));
         return this;
     }
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingGradleHandle.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingGradleHandle.java
index 972e66f..f8fffca 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingGradleHandle.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/OutputScrapingGradleHandle.java
@@ -24,4 +24,4 @@ protected ExecutionResult toExecutionResult(String output, String error) {
     protected ExecutionResult toExecutionFailure(String output, String error) {
         return OutputScrapingExecutionFailure.from(output, error);
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ResultAssertion.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ResultAssertion.java
new file mode 100644
index 0000000..367c988
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/executer/ResultAssertion.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2023 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.integtests.fixtures.executer;
+
+import com.google.common.io.CharSource;
+import org.gradle.api.Action;
+import org.gradle.api.UncheckedIOException;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static java.util.stream.Collectors.joining;
+import static org.gradle.integtests.fixtures.executer.OutputScrapingExecutionResult.STACK_TRACE_ELEMENT;
+
+public class ResultAssertion implements Action<ExecutionResult> {
+    private int expectedGenericDeprecationWarnings;
+    private final List<ExpectedDeprecationWarning> expectedDeprecationWarnings;
+    private final boolean expectStackTraces;
+    private final boolean checkDeprecations;
+    private final boolean checkJdkWarnings;
+
+    /**
+     * The last deprecation warning that was matched. This is used to advance lines in the output being scanned
+     * by the length of lines in the match.  It will <strong>not</strong> be set back to {@code null} after the
+     * first match is found.
+     */
+    @Nullable
+    private ExpectedDeprecationWarning lastMatchedDeprecationWarning = null;
+
+    public ResultAssertion(
+        int expectedGenericDeprecationWarnings, List<ExpectedDeprecationWarning> expectedDeprecationWarnings,
+        boolean expectStackTraces, boolean checkDeprecations, boolean checkJdkWarnings
+    ) {
+        this.expectedGenericDeprecationWarnings = expectedGenericDeprecationWarnings;
+        this.expectedDeprecationWarnings = new ArrayList<>(expectedDeprecationWarnings);
+        this.expectStackTraces = expectStackTraces;
+        this.checkDeprecations = checkDeprecations;
+        this.checkJdkWarnings = checkJdkWarnings;
+    }
+
+    @Override
+    public void execute(ExecutionResult executionResult) {
+        String normalizedOutput = executionResult.getNormalizedOutput();
+        String error = executionResult.getError();
+        boolean executionFailure = executionResult instanceof ExecutionFailure;
+
+        // for tests using rich console standard out and error are combined in output of execution result
+        if (executionFailure) {
+            normalizedOutput = removeExceptionStackTraceForFailedExecution(normalizedOutput);
+        }
+
+        validate(normalizedOutput, "Standard output");
+
+        if (executionFailure) {
+            error = removeExceptionStackTraceForFailedExecution(error);
+        }
+
+        validate(error, "Standard error");
+
+        if (!expectedDeprecationWarnings.isEmpty()) {
+            throw new AssertionError(String.format("Expected the following deprecation warnings:%n%s",
+                expectedDeprecationWarnings.stream()
+                    .map(warning -> " - " + warning)
+                    .collect(joining("\n"))));
+        }
+        if (expectedGenericDeprecationWarnings > 0) {
+            throw new AssertionError(String.format("Expected %d more deprecation warnings", expectedGenericDeprecationWarnings));
+        }
+    }
+
+    // Axe everything after the expected exception
+    private String removeExceptionStackTraceForFailedExecution(String text) {
+        int pos = text.indexOf("* Exception is:");
+        if (pos >= 0) {
+            text = text.substring(0, pos);
+        }
+        return text;
+    }
+
+    public void validate(String output, String displayName) {
+        List<String> lines = getLines(output);
+        int i = 0;
+        boolean insideVariantDescriptionBlock = false;
+        boolean insideKotlinCompilerFlakyStacktrace = false;
+        boolean sawVmPluginLoadFailure = false;
+        while (i < lines.size()) {
+            String line = lines.get(i);
+            insideVariantDescriptionBlock = isInsideVariantDescriptionBlock(insideVariantDescriptionBlock, line);
+
+            // https://youtrack.jetbrains.com/issue/KT-29546
+            if (line.contains("Compilation with Kotlin compile daemon was not successful")) {
+                insideKotlinCompilerFlakyStacktrace = true;
+                i++;
+            } else if (line.contains("Trying to create VM plugin `org.codehaus.groovy.vmplugin.v9.Java9` by checking `java.lang.Module`")) {
+                // a groovy warning when running on Java < 9
+                // https://issues.apache.org/jira/browse/GROOVY-9933
+                i++; // full stracktrace skipped in next branch
+                sawVmPluginLoadFailure = true;
+            } else if (line.contains("java.lang.ClassNotFoundException: java.lang.Module") && sawVmPluginLoadFailure) {
+                // a groovy warning when running on Java < 9
+                // https://issues.apache.org/jira/browse/GROOVY-9933
+                i++;
+                i = skipStackTrace(lines, i);
+            } else if (insideKotlinCompilerFlakyStacktrace &&
+                (line.contains("java.rmi.UnmarshalException") ||
+                    line.contains("java.io.EOFException")) ||
+                // Verbose logging by Jetty when connector is shutdown
+                // https://github.com/eclipse/jetty.project/issues/3529
+                line.contains("java.nio.channels.CancelledKeyException")) {
+                i++;
+                i = skipStackTrace(lines, i);
+            } else if (line.contains("com.amazonaws.http.IdleConnectionReaper")) {
+                /*
+                2021-01-05T08:15:51.329+0100 [DEBUG] [com.amazonaws.http.IdleConnectionReaper] Reaper thread:
+                java.lang.InterruptedException: sleep interrupted
+                    at java.base/java.lang.Thread.sleep(Native Method)
+                    at com.amazonaws.http.IdleConnectionReaper.run(IdleConnectionReaper.java:188)
+                 */
+                i += 2;
+                i = skipStackTrace(lines, i);
+            } else if (line.matches(".*use(s)? or override(s)? a deprecated API\\.")) {
+                // A javac warning, ignore
+                i++;
+            } else if (line.matches(".*w: .* is deprecated\\..*")) {
+                // A kotlinc warning, ignore
+                i++;
+            } else if (line.matches("\\[Warn] :.* is deprecated: .*")) {
+                // A scalac warning, ignore
+                i++;
+            } else if (isDeprecationMessageInHelpDescription(line)) {
+                i++;
+            } else if (removeFirstExpectedDeprecationWarning(lines, i)) {
+                i += lastMatchedDeprecationWarning.getNumLines();
+                i = skipStackTrace(lines, i);
+            } else if (line.matches(".*\\s+deprecated.*") && !isConfigurationAllowedUsageChangingInfoLogMessage(line)) {
+                if (checkDeprecations && expectedGenericDeprecationWarnings <= 0) {
+                    throw new AssertionError(String.format("%s line %d contains a deprecation warning: %s%n=====%n%s%n=====%n", displayName, i + 1, line, output));
+                }
+                expectedGenericDeprecationWarnings--;
+                // skip over stack trace
+                i++;
+                i = skipStackTrace(lines, i);
+            } else if (!expectStackTraces && !insideVariantDescriptionBlock && STACK_TRACE_ELEMENT.matcher(line).matches() && i < lines.size() - 1 && STACK_TRACE_ELEMENT.matcher(lines.get(i + 1)).matches()) {
+                // 2 or more lines that look like stack trace elements
+                throw new AssertionError(String.format("%s line %d contains an unexpected stack trace: %s%n=====%n%s%n=====%n", displayName, i + 1, line, output));
+            } else if (checkJdkWarnings && line.matches("\\s*WARNING:.*")) {
+                throw new AssertionError(String.format("%s line %d contains unexpected JDK warning: %s%n=====%n%s%n=====%n", displayName, i + 1, line, output));
+            } else {
+                i++;
+            }
+        }
+    }
+
+    private static boolean isInsideVariantDescriptionBlock(boolean insideVariantDescriptionBlock, String line) {
+        if (insideVariantDescriptionBlock && line.contains("]")) {
+            return false;
+        }
+        if (!insideVariantDescriptionBlock && line.contains("variant \"")) {
+            return true;
+        }
+        return insideVariantDescriptionBlock;
+    }
+
+    private static List<String> getLines(String output) {
+        try {
+            return CharSource.wrap(output).readLines();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    /**
+     * Changes to a configuration's allowed usage contain the string "deprecated" and thus will trigger
+     * false positive identification as Deprecation warnings by the logic in {@link #validate(String, String)};
+     * this method is used to filter out those false positives.
+     * <p>
+     * The check for the "this behavior..." string ensures that deprecation warnings in this regard, as opposed
+     * to log messages, are not filtered out.
+     *
+     * @param line the output line to check
+     * @return {@code true} if the line is a configuration allowed usage changing info log message; {@code false} otherwise
+     */
+    private static boolean isConfigurationAllowedUsageChangingInfoLogMessage(String line) {
+        String msgPrefix = "Allowed usage is changing for configuration";
+        return (line.startsWith(msgPrefix) || line.contains("[org.gradle.api.internal.artifacts.configurations.DefaultConfiguration] " + msgPrefix))
+            && !line.contains("This behavior has been deprecated.");
+    }
+
+    /**
+     * Removes the first matching expected deprecation warning from the list of expected deprecations stored
+     * in {@link #expectedDeprecationWarnings} as a side-effect; conditional on a matching deprecation warning
+     * being already present in that collection.
+     * <p>
+     * A match means that the first n lines starting at {@code startIdx} match the lines of the
+     * expected deprecation warning.  If there are not n lines starting at {@code startIdx} left in
+     * the list of lines, then no match is made.
+     * <p>
+     * As a <em>second</em> side-effect, the last matched deprecation warning is stored in the
+     * {@link #lastMatchedDeprecationWarning} field.
+     *
+     * @param lines the lines of the output
+     * @param startIdx the index of the current line of the output to check for a match
+     * @return {@code true} if a matching deprecation warning was removed; {@code false} otherwise
+     */
+    private boolean removeFirstExpectedDeprecationWarning(List<String> lines, int startIdx) {
+        Optional<ExpectedDeprecationWarning> matchedWarning = expectedDeprecationWarnings.stream()
+            .filter(warning -> warning.matchesNextLines(lines, startIdx))
+            .findFirst();
+        if (matchedWarning.isPresent()) {
+            lastMatchedDeprecationWarning = matchedWarning.get();
+            expectedDeprecationWarnings.remove(lastMatchedDeprecationWarning);
+        }
+        return matchedWarning.isPresent();
+    }
+
+    private static int skipStackTrace(List<String> lines, int i) {
+        while (i < lines.size() && STACK_TRACE_ELEMENT.matcher(lines.get(i)).matches()) {
+            i++;
+        }
+        return i;
+    }
+
+    private static boolean isDeprecationMessageInHelpDescription(String s) {
+        return s.matches(".*\\[deprecated.*]");
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/extensions/FluidDependenciesResolveTest.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/extensions/FluidDependenciesResolveTest.java
index f102716..14e4288 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/extensions/FluidDependenciesResolveTest.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/extensions/FluidDependenciesResolveTest.java
@@ -24,6 +24,11 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+/**
+ * Marks a test class as needing to run twice, with and without fluid dependencies enabled.
+ * <p>
+ * When fluid dependencies are enabled, any configuration that is a task input is resolved when constructing the task graph.
+ */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 @ExtensionAnnotation(FluidDependenciesResolveTestExtension.class)
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedNodeFixture.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedNodeFixture.java
index f4fbc1f..743f8e9 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedNodeFixture.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedNodeFixture.java
@@ -29,8 +29,8 @@ public abstract class GroupedNodeFixture {
 
     private final List<String> outputs = new ArrayList<>(1);
 
-    public void addOutput(String transformationOutput) {
-        outputs.add(transformationOutput);
+    public void addOutput(String transformOutput) {
+        outputs.add(transformOutput);
     }
 
     public String getOutput() {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedOutputFixture.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedOutputFixture.java
index 9b61e57..de1015f 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedOutputFixture.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedOutputFixture.java
@@ -45,7 +45,7 @@ public class GroupedOutputFixture {
      * All tasks will start with > Task, captures everything starting with : and going until end of line
      */
     private final static String TASK_HEADER = "> Task (:[\\w:]*) ?(FAILED|FROM-CACHE|UP-TO-DATE|SKIPPED|NO-SOURCE)?\\n";
-    private final static String TRANSFORMATION_HEADER = "> Transform (file )?([^\\n]+) with ([^\\n]+)\\n";
+    private final static String TRANSFORM_HEADER = "> Transform (file )?([^\\n]+) with ([^\\n]+)\\n";
 
     private final static String EMBEDDED_BUILD_START = "> :\\w* > [:\\w]+";
     private final static String BUILD_STATUS_FOOTER = "BUILD SUCCESSFUL";
@@ -55,7 +55,7 @@ public class GroupedOutputFixture {
     /**
      * Various patterns to detect the end of the task output
      */
-    private final static String END_OF_GROUPED_OUTPUT = TASK_HEADER + "|" + TRANSFORMATION_HEADER + "|" + BUILD_STATUS_FOOTER + "|" + BUILD_FAILED_FOOTER + "|" + EMBEDDED_BUILD_START + "|" + ACTIONABLE_TASKS + "|\\z";
+    private final static String END_OF_GROUPED_OUTPUT = TASK_HEADER + "|" + TRANSFORM_HEADER + "|" + BUILD_STATUS_FOOTER + "|" + BUILD_FAILED_FOOTER + "|" + EMBEDDED_BUILD_START + "|" + ACTIONABLE_TASKS + "|\\z";
 
     /**
      * Pattern to extract task output.
@@ -65,7 +65,7 @@ public class GroupedOutputFixture {
     /**
      * Pattern to extract task output.
      */
-    private static final Pattern TRANSFORMATION_OUTPUT_PATTERN = patternForHeader(TRANSFORMATION_HEADER);
+    private static final Pattern TRANSFORM_OUTPUT_PATTERN = patternForHeader(TRANSFORM_HEADER);
 
     private static Pattern patternForHeader(String header) {
         String pattern = "(?ms)";
@@ -78,7 +78,7 @@ private static Pattern patternForHeader(String header) {
     private final LogContent originalOutput;
     private final String strippedOutput;
     private Map<String, GroupedTaskOutputFixture> tasks;
-    private Map<String, GroupedTransformationOutputFixture> transformations;
+    private Map<String, GroupedTransformOutputFixture> transforms;
 
     public GroupedOutputFixture(LogContent output) {
         this.originalOutput = output;
@@ -87,11 +87,11 @@ public GroupedOutputFixture(LogContent output) {
 
     private String parse(LogContent output) {
         tasks = new HashMap<>();
-        transformations = new HashMap<>();
+        transforms = new HashMap<>();
 
         String strippedOutput = output.ansiCharsToPlainText().withNormalizedEol();
         findOutputs(strippedOutput, TASK_OUTPUT_PATTERN, this::consumeTaskOutput);
-        findOutputs(strippedOutput, TRANSFORMATION_OUTPUT_PATTERN, this::consumeTransformationOutput);
+        findOutputs(strippedOutput, TRANSFORM_OUTPUT_PATTERN, this::consumeTransformOutput);
 
         return strippedOutput;
     }
@@ -107,8 +107,8 @@ public int getTaskCount() {
         return tasks.size();
     }
 
-    public int getTransformationCount() {
-        return transformations.size();
+    public int getTransformCount() {
+        return transforms.size();
     }
 
     public boolean hasTask(String taskName) {
@@ -128,44 +128,44 @@ public GroupedTaskOutputFixture task(String taskName) {
     /**
      * Returns grouped output for the given transformer type.
      */
-    public GroupedTransformationOutputFixture transform(String transformer) {
-        List<GroupedTransformationOutputFixture> foundTransformations = transformations.values().stream()
-            .filter(transformation -> transformation.getTransformer().equals(transformer))
+    public GroupedTransformOutputFixture transform(String transformer) {
+        List<GroupedTransformOutputFixture> foundTransforms = transforms.values().stream()
+            .filter(transform -> transform.getTransformer().equals(transformer))
             .collect(Collectors.toList());
 
-        if (foundTransformations.size() == 0) {
-            throw new AssertionError(String.format("The grouped output for transformation with transformer '%s' could not be found.%nOutput:%n%s", transformer, originalOutput));
-        } else if (foundTransformations.size() > 1) {
-            throw new AssertionError(String.format("Multiple grouped outputs for transformation with transformer '%s' were found. Consider specifying a subject.%nOutput:%n%s", transformer, originalOutput));
+        if (foundTransforms.size() == 0) {
+            throw new AssertionError(String.format("The grouped output for transform '%s' could not be found.%nOutput:%n%s", transformer, originalOutput));
+        } else if (foundTransforms.size() > 1) {
+            throw new AssertionError(String.format("Multiple grouped outputs for transform '%s' were found. Consider specifying a subject.%nOutput:%n%s", transformer, originalOutput));
         }
 
-        return foundTransformations.get(0);
+        return foundTransforms.get(0);
     }
 
     /**
-     * Returns grouped output for the given transformer type and transformation subject.
+     * Returns grouped output for the given transformer type and transform subject.
      */
-    public GroupedTransformationOutputFixture transform(String transformer, String subject) {
-        List<GroupedTransformationOutputFixture> foundTransformations = transformations.values().stream()
-            .filter(transformation -> transformation.getTransformer().equals(transformer) && transformation.getSubject().equals(subject))
+    public GroupedTransformOutputFixture transform(String transformer, String subject) {
+        List<GroupedTransformOutputFixture> foundTransforms = transforms.values().stream()
+            .filter(transform -> transform.getTransformer().equals(transformer) && transform.getSubject().equals(subject))
             .collect(Collectors.toList());
 
-        if (foundTransformations.size() == 0) {
-            throw new AssertionError(String.format("The grouped output for transformation with transformer '%s' and subject '%s' could not be found.%nOutput:%n%s", transformer, subject, originalOutput));
-        } else if (foundTransformations.size() > 1) {
-            throw new AssertionError(String.format("Multiple grouped outputs for transformation with transformer '%s' and subject '%s' were found.%nOutput:%n%s", transformer, subject, originalOutput));
+        if (foundTransforms.size() == 0) {
+            throw new AssertionError(String.format("The grouped output for transform '%s' and subject '%s' could not be found.%nOutput:%n%s", transformer, subject, originalOutput));
+        } else if (foundTransforms.size() > 1) {
+            throw new AssertionError(String.format("Multiple grouped outputs for transform '%s' and subject '%s' were found.%nOutput:%n%s", transformer, subject, originalOutput));
         }
 
-        return foundTransformations.get(0);
+        return foundTransforms.get(0);
     }
 
     /**
-     * Returns transformation subjects for the given transformer type.
+     * Returns transform subjects for the given transformer type.
      */
     public Set<String> subjectsFor(String transformer) {
-        return transformations.values().stream()
-                .filter(transformation -> transformation.getTransformer().equals(transformer))
-                .map(GroupedTransformationOutputFixture::getSubject)
+        return transforms.values().stream()
+                .filter(transform -> transform.getTransformer().equals(transformer))
+                .map(GroupedTransformOutputFixture::getSubject)
                 .collect(Collectors.toSet());
     }
 
@@ -193,20 +193,20 @@ private void consumeTaskOutput(Matcher matcher) {
         task.setOutcome(taskOutcome);
     }
 
-    private void consumeTransformationOutput(Matcher matcher) {
+    private void consumeTransformOutput(Matcher matcher) {
         String initialSubjectType = matcher.group(1);
         String subject = matcher.group(2);
         String transformer = matcher.group(3);
-        String transformationOutput = StringUtils.strip(matcher.group(4), "\n");
+        String transformOutput = StringUtils.strip(matcher.group(4), "\n");
 
         String key = initialSubjectType + ";" + subject + ";" + transformer;
 
-        GroupedTransformationOutputFixture transformation = transformations.get(key);
-        if (transformation == null) {
-            transformation = new GroupedTransformationOutputFixture(initialSubjectType, subject, transformer);
-            transformations.put(key, transformation);
+        GroupedTransformOutputFixture transform = transforms.get(key);
+        if (transform == null) {
+            transform = new GroupedTransformOutputFixture(initialSubjectType, subject, transformer);
+            transforms.put(key, transform);
         }
 
-        transformation.addOutput(transformationOutput);
+        transform.addOutput(transformOutput);
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedTransformOutputFixture.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedTransformOutputFixture.java
new file mode 100644
index 0000000..71b8729
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedTransformOutputFixture.java
@@ -0,0 +1,50 @@
+/*
+ * 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.integtests.fixtures.logging;
+
+public class GroupedTransformOutputFixture extends GroupedWorkOutputFixture {
+    private final String initialSubjectType;
+    private final String subject;
+    private final String transformer;
+
+    public GroupedTransformOutputFixture(String initialSubjectType, String subject, String transformer) {
+        this.initialSubjectType = initialSubjectType;
+        this.subject = subject;
+        this.transformer = transformer;
+    }
+
+    public String getInitialSubjectType() {
+        return initialSubjectType;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public String getTransformer() {
+        return transformer;
+    }
+
+    @Override
+    public String toString() {
+        return "GroupedTransformOutputFixture{" +
+            "initialSubjectType='" + initialSubjectType + '\'' +
+            ", subject='" + subject + '\'' +
+            ", transformer='" + transformer + '\'' +
+            '}';
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedTransformationOutputFixture.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedTransformationOutputFixture.java
deleted file mode 100644
index 95de070..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedTransformationOutputFixture.java
+++ /dev/null
@@ -1,50 +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.integtests.fixtures.logging;
-
-public class GroupedTransformationOutputFixture extends GroupedWorkOutputFixture {
-    private final String initialSubjectType;
-    private final String subject;
-    private final String transformer;
-
-    public GroupedTransformationOutputFixture(String initialSubjectType, String subject, String transformer) {
-        this.initialSubjectType = initialSubjectType;
-        this.subject = subject;
-        this.transformer = transformer;
-    }
-
-    public String getInitialSubjectType() {
-        return initialSubjectType;
-    }
-
-    public String getSubject() {
-        return subject;
-    }
-
-    public String getTransformer() {
-        return transformer;
-    }
-
-    @Override
-    public String toString() {
-        return "GroupedTransformationFixture{" +
-            "initialSubjectType='" + initialSubjectType + '\'' +
-            ", subject='" + subject + '\'' +
-            ", transformer='" + transformer + '\'' +
-            '}';
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedWorkOutputFixture.java b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedWorkOutputFixture.java
index d1fef62..48d52d8 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedWorkOutputFixture.java
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/GroupedWorkOutputFixture.java
@@ -26,7 +26,7 @@
 import static org.gradle.util.internal.CollectionUtils.join;
 
 /**
- * Collects the output entries produced by a work item such as a task or a transformation,
+ * Collects the output entries produced by a work item such as a task or an artifact transform,
  * and allows assertions to be made about the output.
  *
  * @see GroupedOutputFixture
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/ToolchainDownloadOutputNormalizer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/ToolchainDownloadOutputNormalizer.groovy
new file mode 100644
index 0000000..a444fa2
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/logging/ToolchainDownloadOutputNormalizer.groovy
@@ -0,0 +1,34 @@
+/*
+ * 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.integtests.fixtures.logging
+
+
+import org.gradle.exemplar.executor.ExecutionMetadata
+import org.gradle.exemplar.test.normalizer.OutputNormalizer
+
+/**
+ * This normalizer converts toolchain error message so it doesn't contain platform-specific information.
+ */
+class ToolchainDownloadOutputNormalizer implements OutputNormalizer {
+    @Override
+    String normalize(String commandOutput, ExecutionMetadata executionMetadata) {
+        return commandOutput
+            .replaceAll(
+                "No matching toolchains found for requested specification: (.+) for (.+).") {
+                "No matching toolchains found for requested specification: ${it[1]} for %OS% on %ARCH%."
+            }
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/longlived/PersistentBuildProcessIntegrationTest.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/longlived/PersistentBuildProcessIntegrationTest.groovy
index 460f82c..75765c3 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/longlived/PersistentBuildProcessIntegrationTest.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/longlived/PersistentBuildProcessIntegrationTest.groovy
@@ -17,13 +17,13 @@
 package org.gradle.integtests.fixtures.longlived
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 /**
  * Base class for test that test the behaviour of Gradle when running subsequent builds in the same build process.
  */
-@Requires(adhoc = { GradleContextualExecuter.longLivingProcess })
+@Requires(IntegTestPreconditions.IsLongLivingProcess)
 class PersistentBuildProcessIntegrationTest extends AbstractIntegrationSpec {
 
     def setup() {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/resolve/AbstractGenerateGraphTask.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/resolve/AbstractGenerateGraphTask.groovy
index d685296..4582ae6 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/resolve/AbstractGenerateGraphTask.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/resolve/AbstractGenerateGraphTask.groovy
@@ -26,6 +26,7 @@
 import org.gradle.api.attributes.AttributeContainer
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.result.ComponentSelectionReasonInternal
 import org.gradle.api.tasks.Internal
+import org.gradle.util.Path
 
 class AbstractGenerateGraphTask extends DefaultTask {
     @Internal
@@ -51,13 +52,7 @@
     String formatComponent(ResolvedComponentResult result) {
         String type
         if (result.id instanceof ProjectComponentIdentifier) {
-            if (result.id.build.name == ':') {
-                type = "project:${result.id.projectPath}"
-            } else if (result.id.projectPath == ':') {
-                type = "project::${result.id.build.name}"
-            } else {
-                type = "project::${result.id.build.name}${result.id.projectPath}"
-            }
+            type = "project:${Path.path(result.id.build.buildPath).append(Path.path(result.id.projectPath))}"
         } else if (result.id instanceof ModuleComponentIdentifier) {
             type = "module:${result.id.group}:${result.id.module}:${result.id.version},${result.id.moduleIdentifier.group}:${result.id.moduleIdentifier.name}"
         } else {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/resolve/ResolveTestFixture.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/resolve/ResolveTestFixture.groovy
index e82026a..afc314e 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/resolve/ResolveTestFixture.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/resolve/ResolveTestFixture.groovy
@@ -37,7 +37,6 @@
     String config
     private String defaultConfig = "default"
     private boolean buildArtifacts = true
-    private boolean strictReasonsCheck
 
     private boolean configurationCacheEnabled = GradleContextualExecuter.configCache
 
@@ -51,11 +50,6 @@
         return this
     }
 
-    ResolveTestFixture withStrictReasonsCheck() {
-        strictReasonsCheck = true
-        return this
-    }
-
     ResolveTestFixture expectDefaultConfiguration(String config) {
         defaultConfig = config
         return this
@@ -158,16 +152,16 @@
 
         def actualComponents = findLines(configDetails, 'component')
         def expectedComponents = graph.nodes.collect { baseNode ->
-            def variantDetails = ''
-            def variants = baseNode.variants.collect { variant ->
-                "variant:name:${variant.name} attributes:${variant.attributes}"
-            }
-            if (variants) {
-                variantDetails = "[${variants.join('@@')}]"
-            }
-            "[$baseNode.type][id:${baseNode.id}][mv:${baseNode.moduleVersionId}][reason:${baseNode.reason}]$variantDetails"
+            def variants = baseNode.variants
+            new ParsedNode(type: baseNode.type,
+                id: baseNode.id,
+                module: baseNode.moduleVersionId,
+                reasons: baseNode.allReasons,
+                variants: variants,
+                ignoreReasons: baseNode.ignoreReasons,
+                ignoreReasonPrefixes: baseNode.ignoreReasonPrefixes)
         }
-        compareNodes("components in graph", parseNodes(actualComponents), parseNodes(expectedComponents))
+        compareNodes("components in graph", parseNodes(actualComponents), expectedComponents)
 
         def actualEdges = findLines(configDetails, 'dependency')
         def expectedEdges = graph.edges.collect { "${it.constraint ? '[constraint]' : ''}[from:${it.from.id}][${it.requested}->${it.selected.id}]" }
@@ -320,16 +314,18 @@
             start = idx + 15 // '@@'
             variants << new Variant(name: variant, attributes: attributes)
         }
-        new ParsedNode(type: type, id: id, module: module, reasons: reasons, variants: variants, strictReasonsCheck: strictReasonsCheck)
+        new ParsedNode(type: type, id: id, module: module, reasons: reasons, variants: variants)
     }
 
     static class ParsedNode {
         String type
         String id
         String module
-        List<String> reasons
+        Set<String> reasons
+        boolean ignoreRequested
+        Set<String> ignoreReasons
+        Set<String> ignoreReasonPrefixes
         Set<Variant> variants = []
-        boolean strictReasonsCheck
 
         boolean diff(ParsedNode actual, StringBuilder sb) {
             List<String> errors = []
@@ -342,17 +338,21 @@
             if (this.module != actual.module) {
                 errors << "Expected module '${this.module}' but was: $actual.module"
             }
-            if (!actual.reasons.containsAll(reasons)) {
-                reasons.each { reason ->
-                    if (!actual.reasons.contains(reason)) {
-                        errors << "Expected reason '$reason' but wasn't found. Actual reasons: ${actual.reasons}"
-                    }
+            def actualReasons = actual.reasons.findAll {
+                if (it == "requested" && ignoreRequested) {
+                    false
+                } else if (ignoreReasons.contains(it)) {
+                    false
+                } else if (ignoreReasonPrefixes.any { prefix -> it.startsWith(prefix) }) {
+                    false
+                } else {
+                    true
                 }
+            }.toSet()
+            if (actualReasons != reasons) {
+                errors << "Expected reasons ${reasons} but was: ${actual.reasons}"
             }
-            if (strictReasonsCheck && actual.reasons.size() != reasons.size()) {
-                errors << "Unexpected reason. Expected: $reasons Actual: ${actual.reasons}"
-            }
-            variants.each { variant ->
+            this.variants.each { variant ->
                 def actualVariant = actual.variants.find { it.name == variant.name }
                 if (!actualVariant) {
                     errors << "Expected variant name $variant, but wasn't found in: $actual.variants.name"
@@ -374,7 +374,7 @@
         }
 
         String toString() {
-            "id: $id, module: ${this.module}, reasons: ${reasons}${variants}"
+            "id: $id, module: ${this.module}, reasons: ${reasons}${this.variants}"
         }
     }
 
@@ -686,7 +686,7 @@
     @Canonical
     static class Variant {
         String name
-        String attributes
+        Map<String, String> attributes
 
         String toString() {
             "variant $name, variant attributes $attributes"
@@ -708,6 +708,9 @@
         final List<String> files = []
         private final Set<ExpectedArtifact> artifacts = new LinkedHashSet<>()
         private final Set<String> reasons = new TreeSet<String>()
+        private boolean ignoreRequested
+        private final Set<String> ignoreReasons = new HashSet<>()
+        private final Set<String> ignoreReasonPrefixes = new HashSet<>()
         Set<Variant> variants = []
 
         boolean checkVariant
@@ -723,6 +726,7 @@
             if (attrs.variantName) {
                 variant(attrs.variantName, attrs.variantAttributes)
             }
+            reasons.add('requested')
         }
 
         Set<ExpectedArtifact> getArtifacts() {
@@ -730,7 +734,18 @@
         }
 
         String getReason() {
-            reasons.empty ? (this == graph.root ? 'root' : 'requested') : reasons.join('!!')
+            allReasons.join('!!')
+        }
+
+        Set<String> getAllReasons() {
+            if (this == graph.root) {
+                reasons.remove('requested')
+                reasons.add('root')
+            }
+            if (ignoreRequested) {
+                reasons.remove('requested')
+            }
+            return reasons
         }
 
         private NodeBuilder addNode(NodeBuilder node) {
@@ -890,7 +905,6 @@
             this
         }
 
-
         /**
          * Marks that this node was selected by a rule.
          */
@@ -923,8 +937,34 @@
             this
         }
 
-        NodeBuilder byRequest() {
-            reasons << 'requested'
+        NodeBuilder notRequested() {
+            reasons.remove('requested')
+            this
+        }
+
+        NodeBuilder maybeRequested() {
+            ignoreRequested = true
+            ignoreReasons.add('requested')
+            this
+        }
+
+        NodeBuilder maybeByConflictResolution() {
+            ignoreReasonPrefixes.add("conflict resolution")
+            this
+        }
+
+        NodeBuilder maybeByConstraint() {
+            ignoreReasonPrefixes.add("constraint")
+            this
+        }
+
+        NodeBuilder maybeSelectedByRule() {
+            ignoreReasonPrefixes.add("selected by rule")
+            this
+        }
+
+        NodeBuilder maybeByReason(String reason) {
+            ignoreReasons.add(reason)
             this
         }
 
@@ -957,8 +997,10 @@
             configuration(name)
             checkVariant = true
             String variantName = name
-            String variantAttributes = attributes.collect { "$it.key=$it.value" }.sort().join(',')
-            variants << new Variant(name: variantName, attributes: variantAttributes)
+            Map<String, String> stringAttributes = attributes.collectEntries { entry ->
+                [entry.key, entry.value instanceof Closure ? entry.value.call() : entry.value.toString()]
+            }
+            this.variants << new Variant(name: variantName, attributes: stringAttributes)
             this
         }
 
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/AndroidGradlePluginVersions.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/AndroidGradlePluginVersions.groovy
index f68f096..29f5745 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/AndroidGradlePluginVersions.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/AndroidGradlePluginVersions.groovy
@@ -20,6 +20,8 @@
 import org.gradle.internal.Factory
 import org.gradle.util.internal.VersionNumber
 
+import javax.annotation.Nullable
+
 import static org.junit.Assume.assumeTrue
 
 
@@ -55,6 +57,7 @@
         }
     """
 
+    private static final VersionNumber AGP_8_0 = VersionNumber.parse('8.0.0')
     private static final VersionNumber AGP_7_0 = VersionNumber.parse('7.0.0')
     private static final VersionNumber AGP_7_3 = VersionNumber.parse('7.3.0')
     private static final VersionNumber KOTLIN_1_6_20 = VersionNumber.parse('1.6.20')
@@ -85,6 +88,32 @@
         return getVersionList("latests")
     }
 
+    String getLatest() {
+        return latests.last()
+    }
+
+    List<String> getLatestsStable() {
+        return latests
+            .collect { VersionNumber.parse(it) }
+            .findAll { it.baseVersion == it }
+            .collect { it.toString() }
+    }
+
+    String getLatestStable() {
+        return latestsStable.last()
+    }
+
+    List<String> getLatestsStableOrRC() {
+        return latests.findAll {
+            def lowerCaseVersion = it.toLowerCase(Locale.US)
+            !lowerCaseVersion.contains('-alpha') && !(lowerCaseVersion.contains('-beta'))
+        }
+    }
+
+    String getLatestStableOrRC() {
+        return latestsStableOrRC.last()
+    }
+
     List<String> getNightlies() {
         return getVersionList("nightly")
     }
@@ -119,6 +148,14 @@
         return properties
     }
 
+    @Nullable
+    String getMinimumGradleBaseVersionFor(String agpVersion) {
+        if (VersionNumber.parse(agpVersion) >= AGP_7_3) {
+            return '7.4'
+        }
+        return null
+    }
+
     static void assumeCurrentJavaVersionIsSupportedBy(String agpVersion) {
         VersionNumber agpVersionNumber = VersionNumber.parse(agpVersion)
         JavaVersion current = JavaVersion.current()
@@ -130,11 +167,18 @@
         }
     }
 
-    private static JavaVersion getMinimumJavaVersionFor(VersionNumber agpVersion) {
+    static JavaVersion getMinimumJavaVersionFor(String agpVersion) {
+        return getMinimumJavaVersionFor(VersionNumber.parse(agpVersion))
+    }
+
+    static JavaVersion getMinimumJavaVersionFor(VersionNumber agpVersion) {
         if (agpVersion.baseVersion < AGP_7_0) {
             return JavaVersion.VERSION_1_8
         }
-        return JavaVersion.VERSION_11
+        if (agpVersion.baseVersion < AGP_8_0) {
+            return JavaVersion.VERSION_11
+        }
+        return JavaVersion.VERSION_17
     }
 
     private static JavaVersion getMaximumJavaVersionFor(VersionNumber agpVersion) {
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/KotlinGradlePluginVersions.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/KotlinGradlePluginVersions.groovy
index 0d46bd7..dd59375 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/KotlinGradlePluginVersions.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/integtests/fixtures/versions/KotlinGradlePluginVersions.groovy
@@ -16,19 +16,72 @@
 
 package org.gradle.integtests.fixtures.versions
 
+import org.gradle.internal.Factory
+import org.gradle.util.internal.VersionNumber
+
 /**
  * Kotlin Gradle Plugin Versions.
+ *
+ * See UpdateKotlinVersions.
  */
 class KotlinGradlePluginVersions {
 
-    // https://search.maven.org/search?q=g:org.jetbrains.kotlin%20AND%20a:kotlin-project&core=gav
-    private static final List<String> LATEST_VERSIONS = [
-        '1.6.10', '1.6.21',
-        '1.7.0', '1.7.10', "1.7.22",
-        "1.8.10", "1.8.20-Beta",
+    static final List<String> LANGUAGE_VERSIONS = [
+        "1.4",
+        "1.5",
+        "1.6",
+        "1.7",
+        "1.8",
+        "1.9",
     ]
 
+    private final Factory<Properties> propertiesFactory
+    private Properties properties
+
+    KotlinGradlePluginVersions() {
+        this(new ClasspathVersionSource("kotlin-versions.properties", KotlinGradlePluginVersions.classLoader))
+    }
+
+    private KotlinGradlePluginVersions(Factory<Properties> propertiesFactory) {
+        this.propertiesFactory = propertiesFactory
+    }
+
+    private Properties loadedProperties() {
+        if (properties == null) {
+            properties = propertiesFactory.create()
+        }
+        return properties
+    }
+
+
     List<String> getLatests() {
-        return LATEST_VERSIONS
+        def versionList = loadedProperties().getProperty("latests")
+        return (versionList == null || versionList.empty) ? [] : versionList.split(",")
+    }
+
+    String getLatest() {
+        return latests.last()
+    }
+
+    List<String> getLatestsStable() {
+        return latests
+            .collect { VersionNumber.parse(it) }
+            .findAll { it.baseVersion == it }
+            .collect { it.toString() }
+    }
+
+    String getLatestStable() {
+        return latestsStable.last()
+    }
+
+    List<String> getLatestsStableOrRC() {
+        return latests.findAll {
+            def lowerCaseVersion = it.toLowerCase(Locale.US)
+            !lowerCaseVersion.contains('-m') && !(lowerCaseVersion.contains('-beta'))
+        }
+    }
+
+    String getLatestStableOrRC() {
+        return latestsStableOrRC.last()
     }
 }
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/junitplatform/JUnitPlatformTestRewriter.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/junitplatform/JUnitPlatformTestRewriter.groovy
deleted file mode 100644
index d566e79..0000000
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/junitplatform/JUnitPlatformTestRewriter.groovy
+++ /dev/null
@@ -1,143 +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.test.fixtures.junitplatform
-
-import groovy.io.FileType
-
-class JUnitPlatformTestRewriter {
-
-    private static final Map REPLACEMENTS = Collections.unmodifiableMap([
-        'org.junit.Test': 'org.junit.jupiter.api.Test',
-        'org.junit.Before;': 'org.junit.jupiter.api.BeforeEach;',
-        'org.junit.After;': 'org.junit.jupiter.api.AfterEach;',
-        '@org.junit.Before ': '@org.junit.jupiter.api.BeforeEach ',
-        '@org.junit.After ': '@org.junit.jupiter.api.AfterEach ',
-        '@org.junit.Before\n': '@org.junit.jupiter.api.BeforeEach\n',
-        '@org.junit.After\n': '@org.junit.jupiter.api.AfterEach\n',
-        'org.junit.BeforeClass': 'org.junit.jupiter.api.BeforeAll',
-        'org.junit.AfterClass': 'org.junit.jupiter.api.AfterAll',
-        'org.junit.Ignore': 'org.junit.jupiter.api.Disabled',
-        '@Before\n': '@BeforeEach\n',
-        '@After\n': '@AfterEach\n',
-        '@Before ': '@BeforeEach ',
-        '@After ': '@AfterEach ',
-        '@BeforeClass': '@BeforeAll',
-        '@AfterClass': '@AfterAll',
-        '@Ignore': '@Disabled',
-        'import org.junit.*': 'import org.junit.jupiter.api.*',
-        'org.junit.Assume': 'org.junit.jupiter.api.Assumptions',
-        'org.junit.Assert': 'org.junit.jupiter.api.Assertions',
-        'junit.framework.Assert': 'org.junit.jupiter.api.Assertions',
-        'Assert.': 'Assertions.',
-        'Assume.': 'Assumptions.',
-    ])
-
-    static rewriteWithJupiter(File projectDir, String dependencyVersion) {
-        rewriteBuildFileWithJupiter(projectDir, dependencyVersion)
-        rewriteJavaFilesWithJupiterAnno(projectDir)
-        rewriteJavaModuleFileWithJupiterRequires(projectDir)
-    }
-
-    static replaceCategoriesWithTags(File projectDir) {
-        // http://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4-categories-support
-        File buildFile = new File(projectDir, 'build.gradle')
-        if (buildFile.exists()) {
-            String text = buildFile.text
-            text = text.replace('excludeCategories', 'excludeTags')
-            text = text.replace('includeCategories', 'includeTags')
-            buildFile.text = text
-        }
-    }
-
-    static rewriteWithVintage(File projectDir, String dependencyVersion) {
-        rewriteBuildFileWithVintage(projectDir, dependencyVersion)
-    }
-
-    static rewriteBuildFileWithJupiter(File buildFile, String dependencyVersion) {
-        rewriteBuildFileInDir(buildFile, "org.junit.jupiter:junit-jupiter:${dependencyVersion}", "org.junit.jupiter.api")
-    }
-
-    static rewriteBuildFileWithVintage(File buildFile, String dependencyVersion) {
-        rewriteBuildFileInDir(buildFile, "org.junit.vintage:junit-vintage-engine:${dependencyVersion}','junit:junit:4.13")
-    }
-
-    static rewriteJavaFilesWithJupiterAnno(File rootProject) {
-        rootProject.traverse(type: FileType.FILES, nameFilter: ~/.*\.(java|groovy)/) {
-            String text = it.text
-            REPLACEMENTS.each { key, value ->
-                text = text.replace(key, value)
-            }
-            it.text = text
-        }
-    }
-
-    static rewriteJavaModuleFileWithJupiterRequires(File rootProject) {
-        def moduleInfo = new File(rootProject, 'src/test/java/module-info.java')
-        if (moduleInfo.exists()) {
-            moduleInfo.text = moduleInfo.text.replace('requires junit', 'requires org.junit.jupiter.api')
-        }
-    }
-
-    static rewriteBuildFileInDir(File dir, String dependencies, String moduleName = null) {
-        def dirs = [dir]
-        dirs.addAll(dir.listFiles().findAll { it.isDirectory() })
-        dirs.each {
-            File buildFile = new File(it, 'build.gradle')
-            if (buildFile.exists()) {
-                rewriteBuildFile(buildFile, dependencies, moduleName)
-            }
-        }
-    }
-
-    static rewriteBuildFile(File buildFile, String dependenciesReplacement, String moduleName) {
-        String text = buildFile.text
-        // compile/testCompile/implementation/testImplementation
-        text = text.replaceFirst(/ompile ['"]junit:junit:4\.13['"]/, "ompile '${dependenciesReplacement}'")
-        text = text.replaceFirst(/mplementation ['"]junit:junit:4\.13['"]/, "mplementation '${dependenciesReplacement}'")
-        if (!text.contains('useTestNG')) {
-            // we only hack build with JUnit 4
-            // See IncrementalTestIntegrationTest.executesTestsWhenTestFrameworkChanges
-            if (text.contains("useJUnit {")) {
-                text = text.replace('useJUnit {', 'useJUnitPlatform {')
-            } else if (text.contains('useJUnit()')) {
-                text = text.replace('useJUnit()', 'useJUnitPlatform()')
-            } else if (text.contains('tasks.named("test") {')) {
-                text = text.replace('tasks.named("test") {', '''
-tasks.named("test") {
-    useJUnitPlatform()
-''')
-            } else if (text.contains('test {')) {
-                text = text.replace('test {', '''
-tasks.named("test") {
-    useJUnitPlatform()
-''')
-            } else {
-                text += '''
-tasks.named("test") {
-    useJUnitPlatform()
-}
-                '''
-            }
-        }
-        if (moduleName != null && text.contains("--add-modules")) {
-            // This is a bit aggressive but it works for now
-            text = text.replace('"junit"', '"' + moduleName + '"')
-            text = text.replace('=junit"', '=' + moduleName + '"')
-        }
-        buildFile.text = text
-    }
-}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/RepositoryServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/RepositoryServer.groovy
index 0715956..3ea56df 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/RepositoryServer.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/RepositoryServer.groovy
@@ -31,4 +31,4 @@
     void resetExpectations()
 
     String getValidCredentials()
-}
\ No newline at end of file
+}
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpBuildCacheServer.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpBuildCacheServer.groovy
index e41d2af..4062fba 100644
--- a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpBuildCacheServer.groovy
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/fixtures/server/http/HttpBuildCacheServer.groovy
@@ -19,6 +19,7 @@
 import com.google.common.base.Preconditions
 import org.eclipse.jetty.servlet.FilterHolder
 import org.eclipse.jetty.webapp.WebAppContext
+import org.gradle.internal.hash.Hashing
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestFile
 import org.junit.rules.ExternalResource
@@ -52,6 +53,14 @@
         Preconditions.checkNotNull(cacheDir)
     }
 
+    List<TestFile> listCacheFiles() {
+        cacheDir.listFiles().findAll { it.name ==~ /\p{XDigit}{${Hashing.defaultFunction().hexDigits}}/ }.sort()
+    }
+
+    void deleteCacheFiles() {
+        listCacheFiles().each { it.delete() }
+    }
+
     @Override
     WebAppContext getCustomHandler() {
         return webapp
diff --git a/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/preconditions/IntegTestPreconditions.groovy b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/preconditions/IntegTestPreconditions.groovy
new file mode 100644
index 0000000..0deaacf
--- /dev/null
+++ b/subprojects/internal-integ-testing/src/main/groovy/org/gradle/test/preconditions/IntegTestPreconditions.groovy
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2023 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.test.preconditions
+
+import org.gradle.api.JavaVersion
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.test.precondition.TestPrecondition
+
+class IntegTestPreconditions {
+
+    static final class IsLongLivingProcess implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return GradleContextualExecuter.isLongLivingProcess()
+        }
+    }
+
+    static final class IsEmbeddedExecutor implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return GradleContextualExecuter.isEmbedded()
+        }
+    }
+
+    static final class NotEmbeddedExecutor implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return notSatisfies(IsEmbeddedExecutor)
+        }
+    }
+
+    static final class NotEmbeddedExecutorOrNotWindows implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return notSatisfies(IsEmbeddedExecutor) || notSatisfies(UnitTestPreconditions.Windows)
+        }
+    }
+
+
+    static final class IsDaemonExecutor implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return GradleContextualExecuter.isDaemon()
+        }
+    }
+
+    static final class NotDaemonExecutor implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return GradleContextualExecuter.isNoDaemon()
+        }
+    }
+
+    static final class NotParallelExecutor implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return !GradleContextualExecuter.isParallel()
+        }
+    }
+
+    static final class IsConfigCached implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return GradleContextualExecuter.isConfigCache()
+        }
+    }
+
+    static final class NotConfigCached implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return GradleContextualExecuter.isNotConfigCache()
+        }
+    }
+
+    static class Java7HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(7)
+            )
+        }
+    }
+
+    /**
+     * A JVM that doesn't support running the current Gradle version is available.
+     */
+    static class UnsupportedJavaHomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return !AvailableJavaHomes.getJdks(
+                JavaVersion.toVersion(6),
+                JavaVersion.toVersion(7)
+            ).empty
+        }
+    }
+
+    static class Java8HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(8)
+            )
+        }
+    }
+
+    static class MoreThanOneJava8HomeAvailable implements  TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getAvailableJdks(
+                JavaVersion.toVersion(8)
+            ).size() > 1
+        }
+    }
+
+    static class Java9HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(9)
+            )
+        }
+    }
+
+    static class Java10HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(10)
+            )
+        }
+    }
+
+    static class Java11HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(11)
+            )
+        }
+    }
+
+    static class Java12HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(12)
+            )
+        }
+    }
+
+    static class Java13HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(13)
+            )
+        }
+    }
+
+    static class Java14HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(14)
+            )
+        }
+    }
+
+    static class Java15HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(15)
+            )
+        }
+    }
+
+    static class Java16HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(16)
+            )
+        }
+    }
+
+    static class Java17HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(17)
+            )
+        }
+    }
+
+    static class Java18HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(18)
+            )
+        }
+    }
+
+    static class Java19HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(19)
+            )
+        }
+    }
+
+    static class Java20HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(20)
+            )
+        }
+    }
+
+    static class Java21HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(21)
+            )
+        }
+    }
+
+    static class Java22HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(22)
+            )
+        }
+    }
+
+    static class Java23HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(23)
+            )
+        }
+    }
+
+    static class Java24HomeAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return AvailableJavaHomes.getJdk(
+                JavaVersion.toVersion(24)
+            )
+        }
+    }
+
+    static final class HasMsBuild implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            // Simplistic approach at detecting MSBuild by assuming Windows imply MSBuild is present
+            return doSatisfies(UnitTestPreconditions.Windows) && notSatisfies(IsEmbeddedExecutor)
+        }
+    }
+
+    static final class CanPublishToS3 implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            // The S3 publish tests require the following
+            return doSatisfies(UnitTestPreconditions.Jdk9OrLater) || notSatisfies(IsEmbeddedExecutor)
+        }
+    }
+}
diff --git a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/timeout/JavaProcessStackTracesMonitorSpec.groovy b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/timeout/JavaProcessStackTracesMonitorSpec.groovy
index 0be0c53..bf05bb9 100644
--- a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/timeout/JavaProcessStackTracesMonitorSpec.groovy
+++ b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/integtests/fixtures/timeout/JavaProcessStackTracesMonitorSpec.groovy
@@ -16,13 +16,13 @@
 
 package org.gradle.integtests.fixtures.timeout
 
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Specification
 
 class JavaProcessStackTracesMonitorSpec extends Specification {
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def 'can extract process info from unix ps()'() {
         given:
         def output = '''
@@ -50,7 +50,7 @@
         ]
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def 'can extract process info from windows wmic'() {
         given:
         def output = '''
@@ -81,7 +81,7 @@
         ]
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def 'can locate jstack on Unix'() {
         expect:
         new JavaProcessStackTracesMonitor.JavaProcessInfo('0', javaCommand).jstackCommand == jstackCommand
@@ -93,7 +93,7 @@
         '/opt/jdk/oracle-jdk-8/jre/bin/java'                       | '/opt/jdk/oracle-jdk-8/bin/jstack'
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def 'can locate jstack on Windows'() {
         expect:
         new JavaProcessStackTracesMonitor.JavaProcessInfo('0', javaCommand).jstackCommand == jstackCommand
diff --git a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/junitplatform/JUnitPlatformTestRewriterTest.groovy b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/junitplatform/JUnitPlatformTestRewriterTest.groovy
deleted file mode 100644
index 0ba42a2..0000000
--- a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/junitplatform/JUnitPlatformTestRewriterTest.groovy
+++ /dev/null
@@ -1,204 +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.test.fixtures.junitplatform
-
-import org.gradle.test.fixtures.file.CleanupTestDirectory
-import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.junit.Rule
-import spock.lang.Specification
-
-@CleanupTestDirectory
-class JUnitPlatformTestRewriterTest extends Specification {
-
-    @Rule
-    final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider(getClass())
-
-    def 'build.gradle should be rewritten'() {
-        given:
-        temporaryFolder.testDirectory.file('build.gradle') << '''
-dependencies { testCompile 'junit:junit:4.13' }
-'''
-        when:
-        JUnitPlatformTestRewriter.rewriteBuildFileWithJupiter(temporaryFolder.testDirectory,'5.7.1')
-
-        then:
-        temporaryFolder.testDirectory.file('build.gradle').text.contains(
-            "testCompile 'org.junit.jupiter:junit-jupiter:5.7.1'")
-    }
-
-    def 'modular build.gradle should be rewritten'() {
-        given:
-        temporaryFolder.testDirectory.file('build.gradle') << '''
-dependencies {
-    testImplementation 'junit:junit:4.13'
-}
-compileTestJava {
-    def args = ["--add-modules", "junit",
-                "--add-reads", "org.gradle.example=junit"]
-}
-test {
-    def args = ["--add-modules", "ALL-MODULE-PATH",
-                "--add-reads", "org.gradle.example=junit"]
-}
-'''
-        when:
-        JUnitPlatformTestRewriter.rewriteBuildFileWithJupiter(temporaryFolder.testDirectory,'5.7.1')
-
-        then:
-        temporaryFolder.testDirectory.file('build.gradle').text == '''
-dependencies {
-    testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
-}
-compileTestJava {
-    def args = ["--add-modules", "org.junit.jupiter.api",
-                "--add-reads", "org.gradle.example=org.junit.jupiter.api"]
-}
-
-tasks.named("test") {
-    useJUnitPlatform()
-
-    def args = ["--add-modules", "ALL-MODULE-PATH",
-                "--add-reads", "org.gradle.example=org.junit.jupiter.api"]
-}
-'''
-    }
-
-    def 'java source files should be rewritten'() {
-        given:
-        temporaryFolder.testDirectory.file('src/test/java/Test.java') << oldText
-
-        when:
-        JUnitPlatformTestRewriter.rewriteJavaFilesWithJupiterAnno(temporaryFolder.testDirectory)
-
-        then:
-        temporaryFolder.testDirectory.file('src/test/java/Test.java').text == newText
-
-        where:
-        oldText | newText
-        OLD1    | NEW1
-        OLD2    | NEW2
-        OLD3    | NEW3
-    }
-
-    static final String OLD1 = '''
-package org.gradle;
-
-import org.junit.Test;
-import org.junit.*;
-
-public class Test1 {
-    @Test
-    public void ok() {
-        Assert.assertTrue(true);
-    }
-}
-'''
-    static final String NEW1 = '''
-package org.gradle;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.*;
-
-public class Test1 {
-    @Test
-    public void ok() {
-        Assertions.assertTrue(true);
-    }
-}
-'''
-    static final String OLD2 = '''
-import static org.junit.Assert.*;
-import org.junit.After;
-
-public class OkTest {
-    @org.junit.Test
-    public void ok() throws Exception {
-        assertEquals("4.13", new org.junit.runner.JUnitCore().getVersion());
-    }
-
-    @After
-    public void broken() {
-        fail("failed");
-    }
-
-    @AfterClass
-    public void clean() {
-    }
-
-    @org.junit.AfterClass
-    public void clean() {
-    }
-}
-'''
-    static final String NEW2 = '''
-import static org.junit.jupiter.api.Assertions.*;
-import org.junit.jupiter.api.AfterEach;
-
-public class OkTest {
-    @org.junit.jupiter.api.Test
-    public void ok() throws Exception {
-        assertEquals("4.13", new org.junit.runner.JUnitCore().getVersion());
-    }
-
-    @AfterEach
-    public void broken() {
-        fail("failed");
-    }
-
-    @AfterAll
-    public void clean() {
-    }
-
-    @org.junit.jupiter.api.AfterAll
-    public void clean() {
-    }
-}
-'''
-    static final String OLD3 = '''
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class Junit4Test {
-    @Test
-    public void ok() {
-    }
-
-    @Test
-    @Ignore
-    public void broken() {
-        throw new RuntimeException();
-    }
-}
-'''
-    static final String NEW3 = '''
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-public class Junit4Test {
-    @Test
-    public void ok() {
-    }
-
-    @Test
-    @Disabled
-    public void broken() {
-        throw new RuntimeException();
-    }
-}
-'''
-
-}
diff --git a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/server/http/BlockingHttpServerTest.groovy b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/server/http/BlockingHttpServerTest.groovy
index 5012917..935a48a 100644
--- a/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/server/http/BlockingHttpServerTest.groovy
+++ b/subprojects/internal-integ-testing/src/test/groovy/org/gradle/test/fixtures/server/http/BlockingHttpServerTest.groovy
@@ -21,7 +21,9 @@
 import org.gradle.util.TestUtil
 import org.hamcrest.CoreMatchers
 import org.junit.Rule
+import spock.lang.IgnoreIf
 
+@IgnoreIf({ System.getenv("BUILD_TYPE_ID")?.contains("Check_Gradleception") == true })
 class BlockingHttpServerTest extends ConcurrentSpec {
     @Rule
     TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass())
diff --git a/subprojects/internal-performance-testing/src/integTest/groovy/org/gradle/performance/fixture/MavenDownloaderTest.groovy b/subprojects/internal-performance-testing/src/integTest/groovy/org/gradle/performance/fixture/MavenDownloaderTest.groovy
index 131a9d9..f7896e4 100644
--- a/subprojects/internal-performance-testing/src/integTest/groovy/org/gradle/performance/fixture/MavenDownloaderTest.groovy
+++ b/subprojects/internal-performance-testing/src/integTest/groovy/org/gradle/performance/fixture/MavenDownloaderTest.groovy
@@ -19,8 +19,8 @@
 import org.apache.commons.lang.exception.ExceptionUtils
 import org.gradle.api.JavaVersion
 import org.gradle.api.UncheckedIOException
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Assume
 import org.junit.AssumptionViolatedException
 import spock.lang.Specification
@@ -28,7 +28,7 @@
 
 import java.nio.file.Files
 
-@Requires([TestPrecondition.ONLINE])
+@Requires(UnitTestPreconditions.Online)
 class MavenDownloaderTest extends Specification {
 
     @TempDir
diff --git a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/fixture/AndroidTestProject.groovy b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/fixture/AndroidTestProject.groovy
index bc7cc26..ee4907a 100644
--- a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/fixture/AndroidTestProject.groovy
+++ b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/fixture/AndroidTestProject.groovy
@@ -16,8 +16,13 @@
 
 package org.gradle.performance.fixture
 
+import org.gradle.api.JavaVersion
+import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.fixtures.versions.AndroidGradlePluginVersions
 import org.gradle.integtests.fixtures.versions.KotlinGradlePluginVersions
+import org.gradle.profiler.BuildMutator
+import org.gradle.profiler.InvocationSettings
+import org.gradle.profiler.ScenarioContext
 
 import javax.annotation.Nullable
 
@@ -25,10 +30,6 @@
 
     private static final AndroidGradlePluginVersions AGP_VERSIONS = new AndroidGradlePluginVersions()
     private static final KotlinGradlePluginVersions KGP_VERSIONS = new KotlinGradlePluginVersions()
-    private static final String AGP_STABLE_TARGET_VERSION = "7.3"
-    private static final String AGP_LATEST_TARGET_VERSION = "7.3"
-    // AGP 7.3+ support only KGP 1.6.20+
-    private static final String KGP_STABLE_TARGET_VERSION = "1.6.21"
     public static final LARGE_ANDROID_BUILD = new AndroidTestProject(
         templateName: 'largeAndroidBuild'
     )
@@ -66,36 +67,57 @@
     void configure(GradleBuildExperimentSpec.GradleBuilder builder) {
     }
 
-    static void useStableKotlinVersion(CrossVersionPerformanceTestRunner runner) {
-        runner.args.add("-DkotlinVersion=$KGP_STABLE_TARGET_VERSION")
+    static void useKotlinLatestStableOrRcVersion(CrossVersionPerformanceTestRunner runner) {
+        runner.args.add("-DkotlinVersion=${KGP_VERSIONS.latestStableOrRC}")
     }
 
-    static void useLatestKotlinVersion(CrossVersionPerformanceTestRunner runner) {
-        runner.args.add("-DkotlinVersion=${KGP_VERSIONS.getLatests().last()}")
+    static void useKotlinLatestVersion(CrossVersionPerformanceTestRunner runner) {
+        runner.args.add("-DkotlinVersion=${KGP_VERSIONS.latest}")
     }
 
-    static void useStableAgpVersion(CrossVersionPerformanceTestRunner runner) {
-        configureForLatestAgpVersionOfMinor(runner, AGP_STABLE_TARGET_VERSION)
+    static void useAgpLatestOfMinorVersion(CrossVersionPerformanceTestRunner runner, String lowerBound) {
+        configureForAgpVersion(runner, AGP_VERSIONS.getLatestOfMinor(lowerBound))
     }
 
-    static void useStableAgpVersion(GradleBuildExperimentSpec.GradleBuilder builder) {
-        configureForLatestAgpVersionOfMinor(builder, AGP_STABLE_TARGET_VERSION)
+    static void useAgpLatestStableOrRcVersion(CrossVersionPerformanceTestRunner runner) {
+        configureForAgpVersion(runner, AGP_VERSIONS.latestStableOrRC)
     }
 
-    static void useLatestAgpVersion(CrossVersionPerformanceTestRunner runner) {
-        configureForLatestAgpVersionOfMinor(runner, AGP_LATEST_TARGET_VERSION)
+    static void useAgpLatestVersion(CrossVersionPerformanceTestRunner runner) {
+        configureForAgpVersion(runner, AGP_VERSIONS.latest)
     }
 
-    static void useLatestAgpVersion(GradleBuildExperimentSpec.GradleBuilder builder) {
-        configureForLatestAgpVersionOfMinor(builder, AGP_LATEST_TARGET_VERSION)
+    private static void configureForAgpVersion(CrossVersionPerformanceTestRunner runner, String agpVersion) {
+
+        runner.args.add("-DagpVersion=${agpVersion}")
+
+        def javaVersion = AGP_VERSIONS.getMinimumJavaVersionFor(agpVersion)
+        def buildJavaHome = AvailableJavaHomes.getJdk(javaVersion).javaHome
+        runner.addBuildMutator { invocation -> new JavaVersionMutator(invocation, javaVersion, buildJavaHome) }
+
+        def minimumGradle = AGP_VERSIONS.getMinimumGradleBaseVersionFor(agpVersion)
+        if (minimumGradle != null) {
+            runner.minimumBaseVersion = minimumGradle
+        }
     }
 
-    static void configureForLatestAgpVersionOfMinor(CrossVersionPerformanceTestRunner runner, String lowerBound) {
-        runner.args.add("-DagpVersion=${AGP_VERSIONS.getLatestOfMinor(lowerBound)}")
-    }
+    static class JavaVersionMutator implements BuildMutator {
+        private final File buildJavaHome
+        private final JavaVersion javaVersion
+        private final InvocationSettings invocation
 
-    static void configureForLatestAgpVersionOfMinor(GradleBuildExperimentSpec.GradleBuilder builder, String lowerBound) {
-        builder.invocation.args("-DagpVersion=${AGP_VERSIONS.getLatestOfMinor(lowerBound)}")
+        JavaVersionMutator(InvocationSettings invocation, JavaVersion javaVersion, File buildJavaHome) {
+            this.invocation = invocation
+            this.javaVersion = javaVersion
+            this.buildJavaHome = buildJavaHome
+        }
+
+        @Override
+        void beforeScenario(ScenarioContext context) {
+            def gradleProps = new File(invocation.projectDir, "gradle.properties")
+            gradleProps << "\norg.gradle.java.home=${buildJavaHome.absolutePath.replace("\\", "/")}\n"
+            gradleProps << "\nsystemProp.javaVersion=${javaVersion.majorVersion}\n"
+        }
     }
 
     @Override
diff --git a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/FileContentGenerator.groovy b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/FileContentGenerator.groovy
index f5c1cab..a601d5b 100644
--- a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/FileContentGenerator.groovy
+++ b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/FileContentGenerator.groovy
@@ -41,6 +41,22 @@
         this.config = config
     }
 
+    String generateVersionCatalog() {
+        return """
+        [libraries]
+        groovy = "org.codehaus.groovy:groovy:2.5.22"
+        testng = "org.testng:testng:6.4"
+        junit = "junit:junit:4.13"
+
+        ${config.externalApiDependencies
+            .collect { "${it.key} = \"${it.value}\"" }
+            .join("\n        ")}
+        ${config.externalImplementationDependencies
+            .collect { "${it.key} = \"${it.value}\"" }
+            .join("\n        ")}
+        """
+    }
+
     String generateBuildGradle(Language language, Integer subProjectNumber, DependencyTree dependencyTree) {
         def isRoot = subProjectNumber == null
         if (isRoot && config.subProjects > 0) {
@@ -68,7 +84,7 @@
 
         dependencies{
         ${
-            language == Language.GROOVY ? directDependencyDeclaration('implementation', 'org.codehaus.groovy:groovy:2.5.8') : ""
+            language == Language.GROOVY ? versionCatalogDependencyDeclaration('implementation', 'groovy') : ""
         }
         }
 
@@ -201,8 +217,8 @@
             }
             body += """
             <dependencies>
-                ${config.externalApiDependencies.collect { convertToPomDependency(it) }.join("")}
-                ${config.externalImplementationDependencies.collect { convertToPomDependency(it) }.join("")}
+                ${config.externalApiDependencies.values().collect { convertToPomDependency(it) }.join("")}
+                ${config.externalImplementationDependencies.values().collect { convertToPomDependency(it) }.join("")}
                 ${convertToPomDependency('junit:junit:4.13', 'test')}
                 ${subProjectDependencies}
             </dependencies>
@@ -424,9 +440,9 @@
             }.join("\n            ")
         }
         def block = """
-                    ${config.externalApiDependencies.collect { directDependencyDeclaration(hasParent ? api : implementation, it) }.join("\n            ")}
-                    ${config.externalImplementationDependencies.collect { directDependencyDeclaration(implementation, it) }.join("\n            ")}
-                    ${directDependencyDeclaration(testImplementation, config.useTestNG ? 'org.testng:testng:6.4' : 'junit:junit:4.13')}
+                    ${config.externalApiDependencies.keySet().collect { versionCatalogDependencyDeclaration(hasParent ? api : implementation, it) }.join("\n            ")}
+                    ${config.externalImplementationDependencies.keySet().collect { versionCatalogDependencyDeclaration(implementation, it) }.join("\n            ")}
+                    ${versionCatalogDependencyDeclaration(testImplementation, config.useTestNG ? 'testng' : 'junit')}
 
                     $subProjectDependencies
         """
@@ -459,7 +475,7 @@
     protected abstract String createTaskThatDependsOnAllIncludedBuildsTaskWithSameName(String taskName)
 
 
-    protected abstract String directDependencyDeclaration(String configuration, String notation)
+    protected abstract String versionCatalogDependencyDeclaration(String configuration, String alias)
 
     protected abstract String projectDependencyDeclaration(String configuration, int projectNumber)
 
diff --git a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/GroovyDslFileContentGenerator.groovy b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/GroovyDslFileContentGenerator.groovy
index 0814735..0e4ba7e 100644
--- a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/GroovyDslFileContentGenerator.groovy
+++ b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/GroovyDslFileContentGenerator.groovy
@@ -95,8 +95,8 @@
 
 
     @Override
-    protected String directDependencyDeclaration(String configuration, String notation) {
-        notation.endsWith('()') ? "$configuration $notation" : "$configuration '$notation'"
+    protected String versionCatalogDependencyDeclaration(String configuration, String alias) {
+        "$configuration libs.$alias"
     }
 
     @Override
diff --git a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/KotlinDslFileContentGenerator.groovy b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/KotlinDslFileContentGenerator.groovy
index 6004195..e2b229b 100644
--- a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/KotlinDslFileContentGenerator.groovy
+++ b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/KotlinDslFileContentGenerator.groovy
@@ -86,8 +86,8 @@
 
 
     @Override
-    protected String directDependencyDeclaration(String configuration, String notation) {
-        notation.endsWith('()') ? "\"$configuration\"($notation)" : "\"$configuration\"(\"$notation\")"
+    protected String versionCatalogDependencyDeclaration(String configuration, String alias) {
+        "\"$configuration\"(libs.$alias)"
     }
 
     @Override
diff --git a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/TestProjectGenerator.groovy b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/TestProjectGenerator.groovy
index 913725c..47a186c 100644
--- a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/TestProjectGenerator.groovy
+++ b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/TestProjectGenerator.groovy
@@ -74,6 +74,9 @@
             file projectDir, "WORKSPACE", bazelContentGenerator.generateWorkspace()
             file projectDir, "junit.bzl", bazelContentGenerator.generateJunitHelper()
         }
+        if (isRoot || config.compositeBuild) {
+            file projectDir, "gradle/libs.versions.toml", fileContentGenerator.generateVersionCatalog()
+        }
         file projectDir, "performance.scenarios", fileContentGenerator.generatePerformanceScenarios(isRoot)
 
         if (!isRoot || config.subProjects == 0) {
diff --git a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/TestProjectGeneratorConfiguration.groovy b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/TestProjectGeneratorConfiguration.groovy
index 7bdeca9..41514a6 100644
--- a/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/TestProjectGeneratorConfiguration.groovy
+++ b/subprojects/internal-performance-testing/src/main/groovy/org/gradle/performance/generator/TestProjectGeneratorConfiguration.groovy
@@ -34,8 +34,8 @@
 
     String[] plugins
     String[] repositories
-    String[] externalApiDependencies
-    String[] externalImplementationDependencies
+    Map<String, String> externalApiDependencies
+    Map<String, String> externalImplementationDependencies
 
     boolean buildSrc
 
@@ -122,9 +122,15 @@
 
         config.plugins = this.language == Language.GROOVY ? ['groovy', 'java', 'eclipse', 'idea'] : ['java', 'eclipse', 'idea']
         config.repositories = [mavenCentralRepositoryDefinition(this.dsl)]
-        config.externalApiDependencies = ['commons-lang:commons-lang:2.5', 'commons-httpclient:commons-httpclient:3.0',
-                                          'commons-codec:commons-codec:1.2', 'org.slf4j:jcl-over-slf4j:1.7.10']
-        config.externalImplementationDependencies = ['com.googlecode:reflectasm:1.01']
+        config.externalApiDependencies = [
+            commonsLang: 'commons-lang:commons-lang:2.5',
+            commonsHttpClient: 'commons-httpclient:commons-httpclient:3.0',
+            commonsCodec: 'commons-codec:commons-codec:1.2',
+            jclOverSlf4j: 'org.slf4j:jcl-over-slf4j:1.7.10',
+        ]
+        config.externalImplementationDependencies = [
+            reflectasm: 'com.googlecode:reflectasm:1.01',
+        ]
 
         config.subProjects = this.subProjects
         config.sourceFiles = this.sourceFiles
diff --git a/subprojects/internal-performance-testing/src/templates/settings.gradle b/subprojects/internal-performance-testing/src/templates/settings.gradle
index e51d235..d449f35 100644
--- a/subprojects/internal-performance-testing/src/templates/settings.gradle
+++ b/subprojects/internal-performance-testing/src/templates/settings.gradle
@@ -4,4 +4,4 @@
     groups.each {
         println "include " + it.value.join(", ")
     }
-%>
\ No newline at end of file
+%>
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/DefaultTestExecutionResult.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/DefaultTestExecutionResult.groovy
index dbb5ead..e857a7a 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/DefaultTestExecutionResult.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/DefaultTestExecutionResult.groovy
@@ -15,6 +15,9 @@
  */
 package org.gradle.integtests.fixtures
 
+import groovy.transform.EqualsAndHashCode
+import groovy.transform.ToString
+import org.gradle.integtests.fixtures.TestClassExecutionResult.TestCase
 import org.gradle.test.fixtures.file.TestFile
 import org.hamcrest.Matcher
 
@@ -114,6 +117,44 @@
         testNames.collect { removeParentheses(it) } as String[]
     }
 
+    static String[][] removeAllParentheses(String[]... testNames) {
+        testNames.collect { [removeParentheses(it[0]), it[1]] } as String[][]
+    }
+
+    static TestCase testCase(String name) {
+        return new DefaultTestCase(name)
+    }
+
+    static TestCase testCase(String name, String displayName) {
+        return new DefaultTestCase(name, displayName)
+    }
+
+    static TestCase testCase(String name, String displayName, List<String> messages) {
+        return new DefaultTestCase(name, displayName, messages)
+    }
+
+    @ToString
+    @EqualsAndHashCode(includes = ['name', 'displayName'])
+    private static class DefaultTestCase implements TestCase {
+        String name
+        String displayName
+        List<String> messages
+
+        DefaultTestCase(String name) {
+            this(name, name)
+        }
+
+        DefaultTestCase(String name, String displayName) {
+            this(name, displayName, [])
+        }
+
+        DefaultTestCase(String name, String displayName, List<String> messages) {
+            this.name = removeParentheses(name)
+            this.displayName = removeParentheses(displayName)
+            this.messages = messages
+        }
+    }
+
     private class DefaultTestClassExecutionResult implements TestClassExecutionResult {
         List<TestClassExecutionResult> testClassResults
 
@@ -126,6 +167,12 @@
             this
         }
 
+        @Override
+        TestClassExecutionResult assertTestsExecuted(TestCase... testCases) {
+            testClassResults*.assertTestsExecuted(testCases)
+            this
+        }
+
         TestClassExecutionResult assertTestCount(int tests, int failures, int errors) {
             testClassResults*.assertTestCount(tests, failures, errors)
             this
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/HtmlTestExecutionResult.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/HtmlTestExecutionResult.groovy
index 2e28f86..822ee00 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/HtmlTestExecutionResult.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/HtmlTestExecutionResult.groovy
@@ -15,15 +15,13 @@
  */
 package org.gradle.integtests.fixtures
 
-import groovy.transform.EqualsAndHashCode
-import groovy.transform.ToString
 import org.gradle.internal.FileUtils
 import org.gradle.util.internal.TextUtil
 import org.hamcrest.Matcher
 import org.jsoup.Jsoup
 import org.jsoup.nodes.Document
 
-import static org.gradle.integtests.fixtures.DefaultTestExecutionResult.removeParentheses
+import static org.gradle.integtests.fixtures.DefaultTestExecutionResult.testCase
 import static org.hamcrest.CoreMatchers.hasItems
 import static org.hamcrest.CoreMatchers.not
 import static org.hamcrest.MatcherAssert.assertThat
@@ -91,28 +89,6 @@
         return getExecutedTestClasses().size()
     }
 
-    @ToString
-    @EqualsAndHashCode(includes = ['name', 'displayName'])
-    private static class TestCase {
-        String name
-        String displayName
-        List<String> messages
-
-        TestCase(String name) {
-            this(name, name)
-        }
-
-        TestCase(String name, String displayName) {
-            this(name, displayName, [])
-        }
-
-        TestCase(String name, String displayName, List<String> messages) {
-            this.name = removeParentheses(name)
-            this.displayName = removeParentheses(displayName)
-            this.messages = messages
-        }
-    }
-
     private static class HtmlTestClassExecutionResult implements TestClassExecutionResult {
         private String classDisplayName
         private File htmlFile
@@ -133,7 +109,7 @@
                 def testDisplayName = it.textNodes().first().wholeText.trim()
                 def testName = hasMethodNameColumn() ? it.nextElementSibling().text() : testDisplayName
                 def failureMessage = getFailureMessages(testName)
-                def testCase = new TestCase(testName, testDisplayName, failureMessage)
+                def testCase = testCase(testName, testDisplayName, failureMessage)
                 testsExecuted << testCase
                 target << testCase
             }
@@ -156,9 +132,14 @@
         }
 
         TestClassExecutionResult assertTestsExecuted(String... testNames) {
+            return assertTestsExecuted(testNames.collect { testCase(it) } as TestCase[])
+        }
+
+        @Override
+        TestClassExecutionResult assertTestsExecuted(TestCase... testCases) {
             def executedAndNotSkipped = testsExecuted - testsSkipped
-            assert executedAndNotSkipped.containsAll(testNames.collect { new TestCase(it) })
-            assert executedAndNotSkipped.size() == testNames.size()
+            assert executedAndNotSkipped.containsAll(testCases)
+            assert executedAndNotSkipped.size() == testCases.size()
             return this
         }
 
@@ -173,13 +154,13 @@
         }
 
         TestClassExecutionResult assertTestsSkipped(String... testNames) {
-            assert testsSkipped == testNames.collect { new TestCase(it) } as Set
+            assert testsSkipped == testNames.collect { testCase(it) } as Set
             return this
         }
 
         @Override
         TestClassExecutionResult assertTestPassed(String name, String displayName) {
-            assert testsSucceeded.contains(new TestCase(name, displayName))
+            assert testsSucceeded.contains(testCase(name, displayName))
             return this
         }
 
@@ -188,7 +169,7 @@
         }
 
         TestClassExecutionResult assertTestPassed(String name) {
-            assert testsSucceeded.contains(new TestCase(name))
+            assert testsSucceeded.contains(testCase(name))
             return this
         }
 
@@ -227,7 +208,7 @@
 
         @Override
         TestClassExecutionResult assertTestSkipped(String name, String displayName) {
-            assert testsSkipped.contains(new TestCase(name, displayName))
+            assert testsSkipped.contains(testCase(name, displayName))
             return this
         }
 
@@ -250,7 +231,7 @@
         }
 
         TestClassExecutionResult assertTestSkipped(String name) {
-            assert testsSkipped.contains(new TestCase(name))
+            assert testsSkipped.contains(testCase(name))
             return this
         }
 
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitTestClassExecutionResult.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitTestClassExecutionResult.groovy
index a12b928..63761ae 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitTestClassExecutionResult.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/JUnitTestClassExecutionResult.groovy
@@ -52,6 +52,17 @@
         this
     }
 
+    /**
+     * Note that the JUnit XML schema currently does not support both name and display name, so this extra data is effectively ignored for
+     * XML test reports.  See https://github.com/junit-team/junit5/issues/373 for further information.
+     *
+     * This method exists for compatibility purposes, but is equivalent to {@link #assertTestsExecuted(java.lang.String[])}.
+     */
+    @Override
+    TestClassExecutionResult assertTestsExecuted(TestCase... testCases) {
+        return assertTestsExecuted(testCases.collect { it.displayName } as String[])
+    }
+
     TestClassExecutionResult assertTestCount(int tests, int failures, int errors) {
         assert testClassNode.@tests == tests
         assert testClassNode.@failures == failures
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
index 1ebc545..c0abe19 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/integtests/fixtures/TestClassExecutionResult.java
@@ -18,12 +18,21 @@
 
 import org.hamcrest.Matcher;
 
+import java.util.List;
+
 public interface TestClassExecutionResult {
     /**
      * Asserts that the given tests (and only the given tests) were executed for the given test class.
      */
     TestClassExecutionResult assertTestsExecuted(String... testNames);
 
+    /**
+     * Asserts that the given tests (and only the given tests) were executed for the given test class.
+     *
+     * This supports JUnit5 parameterized tests where the test name and display name may not match.
+     */
+    TestClassExecutionResult assertTestsExecuted(TestCase... testCases);
+
     TestClassExecutionResult assertTestCount(int tests, int failures, int errors);
 
     int getTestCount();
@@ -81,4 +90,10 @@ public interface TestClassExecutionResult {
     TestClassExecutionResult assertExecutionFailedWithCause(Matcher<? super String> causeMatcher);
 
     TestClassExecutionResult assertDisplayName(String classDisplayName);
+
+    interface TestCase {
+        String getName();
+        String getDisplayName();
+        List<String> getMessages();
+    }
 }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/ConcurrentTestUtil.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/ConcurrentTestUtil.groovy
index 76f2cda..20b93f8 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/ConcurrentTestUtil.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/ConcurrentTestUtil.groovy
@@ -18,6 +18,7 @@
 import org.gradle.internal.concurrent.ExecutorFactory
 import org.gradle.internal.concurrent.ManagedExecutor
 import org.gradle.internal.concurrent.ManagedScheduledExecutor
+import org.gradle.internal.concurrent.ManagedThreadPoolExecutor
 import org.junit.rules.ExternalResource
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
@@ -105,15 +106,23 @@
 
     ExecutorFactory getExecutorFactory() {
         return new ExecutorFactory() {
+            @Override
             ManagedExecutor create(String displayName) {
                 return new ManagedExecutorStub(ConcurrentTestUtil.this)
             }
 
+            @Override
             ManagedExecutor create(String displayName, int fixedSize) {
                 // Ignores size of thread pool
                 return new ManagedExecutorStub(ConcurrentTestUtil.this)
             }
 
+            @Override
+            ManagedThreadPoolExecutor createThreadPool(String displayName, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit timeUnit) {
+                throw new UnsupportedOperationException()
+            }
+
+            @Override
             ManagedScheduledExecutor createScheduled(String displayName, int fixedSize) {
                 throw new UnsupportedOperationException()
             }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/OperationListener.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/OperationListener.groovy
index f7ceca0..8eedc88 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/OperationListener.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/OperationListener.groovy
@@ -20,4 +20,4 @@
     void operationStarted()
 
     void operationFinished()
-}
\ No newline at end of file
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestExecutorFactory.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestExecutorFactory.groovy
index ab97f88..68d8282 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestExecutorFactory.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/fixtures/concurrent/TestExecutorFactory.groovy
@@ -17,8 +17,11 @@
 package org.gradle.test.fixtures.concurrent
 
 import org.gradle.internal.concurrent.ExecutorFactory
-import org.gradle.internal.concurrent.ManagedScheduledExecutor
 import org.gradle.internal.concurrent.ManagedExecutor
+import org.gradle.internal.concurrent.ManagedScheduledExecutor
+import org.gradle.internal.concurrent.ManagedThreadPoolExecutor
+
+import java.util.concurrent.TimeUnit
 
 class TestExecutorFactory implements ExecutorFactory {
     private final TestExecutor executor
@@ -27,15 +30,23 @@
         this.executor = executor
     }
 
+    @Override
     ManagedExecutor create(String displayName) {
         return new TestManagedExecutor(executor)
     }
 
+    @Override
     ManagedExecutor create(String displayName, int fixedSize) {
         // Ignores size of thread pool
         return new TestManagedExecutor(executor)
     }
 
+    @Override
+    ManagedThreadPoolExecutor createThreadPool(String displayName, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit timeUnit) {
+        throw new UnsupportedOperationException()
+    }
+
+    @Override
     ManagedScheduledExecutor createScheduled(String displayName, int fixedSize) {
         throw new UnsupportedOperationException()
     }
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/PreconditionVerifier.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/PreconditionVerifier.groovy
new file mode 100644
index 0000000..4aea683
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/PreconditionVerifier.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 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.test.precondition
+
+
+import org.jetbrains.annotations.NotNull
+import org.junit.Assume
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+class PreconditionVerifier implements TestRule {
+    @Override
+    Statement apply(@NotNull Statement base, @NotNull Description description) {
+        List<Class<? extends TestPrecondition>> preconditions = description.annotations.findAll { it instanceof Requires }*.value().flatten()
+        PredicatesFile.checkValidCombinations(preconditions, PredicatesFile.DEFAULT_ACCEPTED_COMBINATIONS)
+        TestPrecondition.doSatisfiesAll(preconditions.toArray(new Class<? extends TestPrecondition>[0])) ? base : new IgnoreStatement(preconditions)
+    }
+
+    private static class IgnoreStatement extends Statement {
+        private final List<Class<? extends TestPrecondition>> preconditions
+
+        IgnoreStatement(List<Class<? extends TestPrecondition>> preconditions) {
+            this.preconditions = preconditions
+        }
+
+        @Override
+        void evaluate() {
+            Assume.assumeTrue("Not all Requirements ${preconditions*.simpleName} are satisfied", false)
+        }
+    }
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/PredicatesFile.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/PredicatesFile.java
new file mode 100644
index 0000000..a2adaa0
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/PredicatesFile.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2023 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.test.precondition;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class PredicatesFile {
+    public static final Set<Set<String>> DEFAULT_ACCEPTED_COMBINATIONS = readAllowedCombinations("/valid-precondition-combinations.csv");
+    public static final String PREDICATE_PACKAGE_PREFIX = "org.gradle.test.preconditions.";
+
+    /**
+     * Streams a quasi-csv file containing comma-separated values.
+     * <p>
+     * Note, that for the sake of conciseness, we do not require in the file the whole package name.
+     * All class names will be prefixed with {@link #PREDICATE_PACKAGE_PREFIX} when reading them up.
+     * <p>
+     * The csv is sudo in the sense, that you can comment by using "#"
+     *
+     * @param resource the resource file being used
+     * @return a set of sets of strings representing the values in the file
+     */
+    public static Set<Set<String>> readAllowedCombinations(String resource) {
+        try (BufferedReader reader = new BufferedReader(
+            new InputStreamReader(
+                Objects.requireNonNull(
+                    PredicatesFile.class.getResourceAsStream(resource),
+                    String.format("Predicate combination list resource '%s' cannot be found", resource)
+                ),
+                StandardCharsets.UTF_8
+            )
+        )) {
+            return reader.lines()
+                // If a line starts with #, it's a comment
+                .filter(line -> !line.startsWith("#"))
+                // We are not interested in whitespaces on the line level
+                .map(String::trim)
+                // If the line was empty, or only contained space (which was cut by the trim before), we skip the line
+                .filter(line -> !line.isEmpty())
+                // We separate the values
+                .map(line -> line.split(","))
+                // For each array of entries...
+                .map(entries -> Arrays
+                    // We stream the entries
+                    .stream(entries)
+                    // Trim all unnecessary whitespaces off
+                    .map(String::trim)
+                    // Prefix the package with the implicit name of the predicates
+                    .map(name -> PREDICATE_PACKAGE_PREFIX + name)
+                    .collect(Collectors.toSet())
+                ).collect(Collectors.toSet());
+        } catch (IOException e) {
+            throw new RuntimeException("Error parsing resource " + resource, e);
+        }
+    }
+
+    /**
+     * Checks if a precondition is present in the allowed precondition list stored
+     * in {@code acceptedCombinations}
+     */
+    public static void checkValidCombinations(List<Class<?>> predicateClasses, Set<Set<String>> acceptedCombinations) {
+        if (predicateClasses.isEmpty()) {
+            return;
+        }
+        Set<String> predicateClassNames = predicateClasses.stream()
+            .map(Class::getName)
+            .collect(Collectors.toSet());
+        checkValidNameCombinations(predicateClassNames, acceptedCombinations);
+    }
+
+    static void checkValidNameCombinations(Set<String> predicateClassNames, Set<Set<String>> acceptedCombinations) {
+        boolean found = acceptedCombinations.contains(predicateClassNames);
+
+        if (!found) {
+            String message = String.format(
+                "Requested requirements [%s] were not in the list of accepted combinations. " +
+                    "Add it to 'subprojects/internal-testing/src/main/resources/valid-precondition-combinations.csv' to be accepted. " +
+                    "See the documentation of this class to learn more about this feature.",
+                String.join(", ", predicateClassNames)
+            );
+            throw new IllegalArgumentException(message);
+        }
+    }
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/Requires.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/Requires.java
new file mode 100644
index 0000000..6e0f089
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/Requires.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 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.test.precondition;
+
+import org.spockframework.runtime.extension.ExtensionAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation defining a set of requirements.
+ * These requirements should be met per test-class or method, or else the test is ignored.
+ *
+ * <p>
+ * Combinations are pre-defined. See {@link RequiresExtension} for more info how to introduce a new combination.
+ *
+ * @see RequiresExtension
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Inherited
+@ExtensionAnnotation(RequiresExtension.class)
+public @interface Requires {
+
+    /**
+     * The list of preconditions, which will be checked by {@link RequiresExtension}
+     */
+    Class<? extends TestPrecondition>[] value();
+
+    String reason() default "";
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/RequiresExtension.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/RequiresExtension.groovy
new file mode 100644
index 0000000..0b19e35
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/RequiresExtension.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 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.test.precondition
+
+import groovy.transform.CompileStatic
+import org.spockframework.runtime.extension.IAnnotationDrivenExtension
+import org.spockframework.runtime.model.FeatureInfo
+import org.spockframework.runtime.model.SpecElementInfo
+import org.spockframework.runtime.model.SpecInfo
+
+/**
+ * Test extension enforcing the {@link Requires} annotation in Spock (note, that this is a separate class from Spock's own {@link spock.lang.Requires}).
+ * <p>
+ * If you need a new combination of {@link TestPrecondition} classes, go to {@code subprojects/internal-testing/src/main/resources/valid-precondition-combinations.csv} and simply add it.
+ * <p>
+ * See the <a href="https://github.com/gradle/gradle/tree/master/subprojects/predicate-tester">predicate-tester</a> project to learn about where we use the information.
+ *
+ * @see Requires
+ */
+@CompileStatic
+class RequiresExtension implements IAnnotationDrivenExtension<Requires> {
+    private final Set<Set<String>> acceptedCombinations
+
+    /**
+     * Default constructor.
+     * <p>
+     * This will automatically load {@code subprojects/internal-testing/src/main/resources/valid-precondition-combinations.csv}.
+     */
+    RequiresExtension() {
+        this(PredicatesFile.DEFAULT_ACCEPTED_COMBINATIONS)
+    }
+
+    /**
+     * Protected constructor (for testing).
+     */
+    protected RequiresExtension(Set<Set<String>> acceptedCombinations) {
+        this.acceptedCombinations = acceptedCombinations
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void visitSpecAnnotation(Requires annotation, SpecInfo spec) {
+        visitAnnotation(annotation, spec)
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void visitFeatureAnnotation(Requires annotation, FeatureInfo feature) {
+        visitAnnotation(annotation, feature)
+    }
+
+    /**
+     * Common method for handling annotations.
+     */
+    void visitAnnotation(Requires annotation, SpecElementInfo feature) {
+        def predicateClassNames = annotation.value() as List
+
+        PredicatesFile.checkValidCombinations(predicateClassNames, acceptedCombinations)
+        // If all preconditions are met, we DON'T skip the tests
+        feature.skipped |= !TestPrecondition.doSatisfiesAll(annotation.value())
+    }
+
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/TestPrecondition.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/TestPrecondition.java
new file mode 100644
index 0000000..e473273
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/TestPrecondition.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 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.test.precondition;
+
+/**
+ * Usage:
+ * <pre>
+ * <code>@</code>Requires(TestPrecondition.JDK17_OR_LATER)
+ * def "test with environment expectations"() {
+ *     // the test is executed with Java 17 or later
+ * }
+ * </pre>
+ *
+ * @see Requires
+ */
+
+public interface TestPrecondition {
+    boolean isSatisfied() throws Exception;
+
+    static boolean doSatisfies(Class<? extends TestPrecondition> preconditionClass) throws Exception {
+        final TestPrecondition precondition = preconditionClass
+            .getDeclaredConstructor()
+            .newInstance();
+        return precondition.isSatisfied();
+    }
+
+    static boolean doSatisfiesAll(Class<? extends TestPrecondition>[] preconditionClasses) throws Exception {
+        for (Class<? extends TestPrecondition> preconditionClass : preconditionClasses) {
+            if (!doSatisfies(preconditionClass)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    static boolean notSatisfies(Class<? extends TestPrecondition> preconditionClass) throws Exception {
+        return !doSatisfies(preconditionClass);
+    }
+}
+
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/package-info.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/package-info.java
new file mode 100644
index 0000000..6f9dd63
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/precondition/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 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.
+ */
+
+@NonNullApi
+package org.gradle.test.precondition;
+
+import org.gradle.api.NonNullApi;
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/preconditions/UnitTestPreconditions.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/test/preconditions/UnitTestPreconditions.groovy
new file mode 100644
index 0000000..2c1e999
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/preconditions/UnitTestPreconditions.groovy
@@ -0,0 +1,526 @@
+/*
+ * Copyright 2023 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.test.preconditions
+
+import groovy.transform.CompileStatic
+import org.gradle.api.JavaVersion
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.precondition.TestPrecondition
+import org.testcontainers.DockerClientFactory
+
+import static org.gradle.test.precondition.TestPrecondition.doSatisfies;
+import static org.gradle.test.precondition.TestPrecondition.notSatisfies;
+
+@CompileStatic
+class UnitTestPreconditions {
+
+    static final class Symlinks implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return doSatisfies(MacOs) || doSatisfies(Linux)
+        }
+    }
+
+    static final class NoSymlinks implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(Symlinks)
+        }
+    }
+
+    static final class CaseInsensitiveFs implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return doSatisfies(MacOs) || doSatisfies(Windows)
+        }
+    }
+
+    static final class CaseSensitiveFs implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(CaseInsensitiveFs)
+        }
+    }
+
+    static final class FilePermissions implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return doSatisfies(MacOs) || doSatisfies(Linux)
+        }
+    }
+
+    static final class NoFilePermissions implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(FilePermissions)
+        }
+    }
+
+    static final class WorkingDir implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() < JavaVersion.VERSION_11
+        }
+    }
+
+    static final class MandatoryFileLockOnOpen implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return doSatisfies(Windows)
+        }
+    }
+
+    static final class NoMandatoryFileLockOnOpen implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(MandatoryFileLockOnOpen)
+        }
+    }
+
+    static final class Windows implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return OperatingSystem.current().isWindows()
+        }
+    }
+
+    static final class NotWindows implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(Windows)
+        }
+    }
+
+    static final class NotWindowsJavaBefore11 implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(Windows) || doSatisfies(Jdk11OrLater)
+        }
+    }
+
+    static final class NotWindowsJavaBefore9 implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(Windows) || doSatisfies(Jdk9OrLater)
+        }
+    }
+
+    static final class MacOs implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return OperatingSystem.current().isMacOsX()
+        }
+    }
+
+    static final class NotMacOs implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(MacOs)
+        }
+    }
+
+    static final class MacOsM1 implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return doSatisfies(MacOs) && OperatingSystem.current().toString().contains("aarch64")
+        }
+    }
+
+    static final class NotMacOsM1 implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(MacOsM1)
+        }
+    }
+
+    static final class Linux implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return OperatingSystem.current().linux
+        }
+    }
+
+    static final class NotLinux implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(Linux)
+        }
+    }
+
+    static final class Unix implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return OperatingSystem.current().isUnix()
+        }
+    }
+
+    static final class UnixDerivative implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            doSatisfies(MacOs) || doSatisfies(Linux) || doSatisfies(Unix)
+        }
+    }
+
+    static final class HasDocker implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            try {
+                DockerClientFactory.instance().client()
+            } catch (Exception ex) {
+                return false
+            }
+            return true
+        }
+    }
+
+    static final class Jdk6OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_1_6
+        }
+    }
+
+    static final class Jdk6OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_1_6
+        }
+    }
+
+    static final class Jdk7OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_1_7
+        }
+    }
+
+    static final class Jdk7OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_1_7
+        }
+    }
+
+    static final class Jdk8OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_1_8
+        }
+    }
+
+    static final class Jdk8OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_1_8
+        }
+    }
+
+    static final class Jdk9OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_1_9
+        }
+    }
+
+    static final class Jdk9OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_1_9
+        }
+    }
+
+    static final class Jdk10OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_1_10
+        }
+    }
+
+    static final class Jdk10OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_1_10
+        }
+    }
+
+    static final class Jdk11OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_11
+        }
+    }
+
+    static final class Jdk11OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_11
+        }
+    }
+
+    static final class Jdk12OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_12
+        }
+    }
+
+    static final class Jdk12OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_12
+        }
+    }
+
+    static final class Jdk13OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_13
+        }
+    }
+
+    static final class Jdk13OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_13
+        }
+    }
+
+    static final class Jdk14OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_14
+        }
+    }
+
+    static final class Jdk14OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_14
+        }
+    }
+
+    static final class Jdk15OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_15
+        }
+    }
+
+    static final class Jdk15OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_15
+        }
+    }
+
+    static final class Jdk16OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_16
+        }
+    }
+
+    static final class Jdk16OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_16
+        }
+    }
+
+    static final class Jdk17OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_17
+        }
+    }
+
+    static final class Jdk17OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_17
+        }
+    }
+
+    static final class Jdk18OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_18
+        }
+    }
+
+    static final class Jdk18OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_18
+        }
+    }
+
+    static final class Jdk19OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_19
+        }
+    }
+
+    static final class Jdk19OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_19
+        }
+    }
+
+    static final class Jdk20OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_20
+        }
+    }
+
+    static final class Jdk20OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_20
+        }
+    }
+
+    static final class Jdk21OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_21
+        }
+    }
+
+    static final class Jdk21OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_21
+        }
+    }
+
+    static final class Jdk22OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_22
+        }
+    }
+
+    static final class Jdk22OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_22
+        }
+    }
+
+    static final class Jdk23OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_23
+        }
+    }
+
+    static final class Jdk23OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_23
+        }
+    }
+
+    static final class Jdk24OrLater implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() >= JavaVersion.VERSION_24
+        }
+    }
+
+    static final class Jdk24OrEarlier implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return JavaVersion.current() <= JavaVersion.VERSION_24
+        }
+    }
+
+    static final class JdkOracle implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            System.getProperty('java.vm.vendor') == 'Oracle Corporation'
+        }
+    }
+
+    static final class Online implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            try {
+                new URL("http://google.com").openConnection().getInputStream().close()
+                return true
+            } catch (IOException ex) {
+                return false
+            }
+        }
+    }
+
+    static final class CanInstallExecutable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return doSatisfies(FilePermissions) || doSatisfies(Windows)
+        }
+    }
+
+    static final class SmartTerminalAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return System.getenv("TERM")?.toUpperCase() != "DUMB"
+        }
+    }
+
+    static final class HasXCode implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            // Simplistic approach at detecting Xcode by assuming macOS imply Xcode is present
+            return doSatisfies(MacOs)
+        }
+    }
+
+    // Currently mac agents are not that strong so we avoid running high-concurrency tests on them
+    static final class HighPerformance implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            return notSatisfies(MacOs)
+        }
+    }
+
+    static final class NotEC2Agent implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws UnknownHostException {
+            return !InetAddress.getLocalHost().getHostName().startsWith("ip-")
+        }
+    }
+
+    static final class StableGroovy implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            !GroovySystem.version.endsWith("-SNAPSHOT")
+        }
+    }
+
+    static final class NotStableGroovy implements TestPrecondition {
+        @Override
+        boolean isSatisfied() {
+            notSatisfies(StableGroovy)
+        }
+    }
+
+}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/test/preconditions/package-info.java b/subprojects/internal-testing/src/main/groovy/org/gradle/test/preconditions/package-info.java
new file mode 100644
index 0000000..6089dbf
--- /dev/null
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/test/preconditions/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 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.
+ */
+
+@NonNullApi
+package org.gradle.test.preconditions;
+
+import org.gradle.api.NonNullApi;
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/PreconditionVerifier.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/PreconditionVerifier.groovy
deleted file mode 100644
index 078b28d..0000000
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/PreconditionVerifier.groovy
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2011 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.util
-
-import org.junit.rules.TestRule
-import org.junit.runners.model.Statement
-import org.junit.runner.Description
-
-class PreconditionVerifier implements TestRule {
-    Statement apply(Statement base, Description description) {
-        def preconditions = description.annotations.findAll { it instanceof Requires }*.value().flatten()
-        preconditions.every { it.fulfilled } ? base : EmptyStatement.INSTANCE
-    }
-}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/Requires.java b/subprojects/internal-testing/src/main/groovy/org/gradle/util/Requires.java
deleted file mode 100644
index 2db001d..0000000
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/Requires.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2011 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.util;
-
-import groovy.lang.Closure;
-import org.gradle.util.internal.AlwaysTrue;
-import org.spockframework.runtime.extension.ExtensionAnnotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Inherited
-@ExtensionAnnotation(TestPreconditionExtension.class)
-public @interface Requires {
-    TestPrecondition[] value() default {TestPrecondition.NULL_REQUIREMENT};
-
-    Class<? extends Closure<?>> adhoc() default AlwaysTrue.class;
-}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPrecondition.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPrecondition.groovy
deleted file mode 100644
index 8281b9f..0000000
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPrecondition.groovy
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright 2011 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.util
-
-import org.gradle.api.JavaVersion
-import org.gradle.internal.os.OperatingSystem
-import org.testcontainers.DockerClientFactory
-
-/**
- * Usage:
- * <pre>
- * <code>@</code>Requires(TestPrecondition.JDK17_OR_LATER)
- * def "test with environment expectations"() {
- *     // the test is executed with Java 17 or later
- * }
- * </pre>
- *
- * @see Requires
- */
-enum TestPrecondition implements org.gradle.internal.Factory<Boolean> {
-    NULL_REQUIREMENT({ true }),
-    SYMLINKS({
-        MAC_OS_X.fulfilled || LINUX.fulfilled
-    }),
-    NO_SYMLINKS({
-        !SYMLINKS.fulfilled
-    }),
-    CASE_INSENSITIVE_FS({
-        MAC_OS_X.fulfilled || WINDOWS.fulfilled
-    }),
-    FILE_PERMISSIONS({
-        MAC_OS_X.fulfilled || LINUX.fulfilled
-    }),
-    NO_FILE_PERMISSIONS({
-        !FILE_PERMISSIONS.fulfilled
-    }),
-    WORKING_DIR({
-        JavaVersion.current() < JavaVersion.VERSION_11
-    }),
-    NO_FILE_LOCK_ON_OPEN({
-        MAC_OS_X.fulfilled || LINUX.fulfilled
-    }),
-    MANDATORY_FILE_LOCKING({
-        OperatingSystem.current().windows
-    }),
-    WINDOWS({
-        OperatingSystem.current().windows
-    }),
-    NOT_WINDOWS({
-        !OperatingSystem.current().windows
-    }),
-    MAC_OS_X({
-        OperatingSystem.current().macOsX
-    }),
-    MAC_OS_X_M1({
-        OperatingSystem.current().macOsX && OperatingSystem.current().toString().contains("aarch64")
-    }),
-    NOT_MAC_OS_X_M1({
-        !MAC_OS_X_M1.fulfilled
-    }),
-    NOT_MAC_OS_X({
-        !OperatingSystem.current().macOsX
-    }),
-    LINUX({
-        OperatingSystem.current().linux
-    }),
-    NOT_LINUX({
-        !LINUX.fulfilled
-    }),
-    UNIX({
-        OperatingSystem.current().unix
-    }),
-    UNIX_DERIVATIVE({
-        MAC_OS_X.fulfilled || LINUX.fulfilled || UNIX.fulfilled
-    }),
-    HAS_DOCKER({
-        try {
-            DockerClientFactory.instance().client()
-        } catch (Exception ex) {
-            return false
-        }
-        return true
-    }),
-    JDK8_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_1_8
-    }),
-    JDK9_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_1_9
-    }),
-    JDK9_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_1_9
-    }),
-    JDK10_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_1_10
-    }),
-    JDK10_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_1_10
-    }),
-    JDK11_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_11
-    }),
-    JDK11_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_11
-    }),
-    JDK12_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_12
-    }),
-    JDK13_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_13
-    }),
-    JDK13_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_13
-    }),
-    JDK14_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_14
-    }),
-    JDK15_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_15
-    }),
-    JDK16_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_16
-    }),
-    JDK16_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_16
-    }),
-    JDK17_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_17
-    }),
-    JDK17_OR_EARLIER({
-        JavaVersion.current() <= JavaVersion.VERSION_17
-    }),
-    JDK18_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_18
-    }),
-    JDK19_OR_LATER({
-        JavaVersion.current() >= JavaVersion.VERSION_19
-    }),
-    JDK_ORACLE({
-        System.getProperty('java.vm.vendor') == 'Oracle Corporation'
-    }),
-
-    ONLINE({
-        try {
-            new URL("http://google.com").openConnection().getInputStream().close()
-            true
-        } catch (IOException) {
-            false
-        }
-    }),
-    CAN_INSTALL_EXECUTABLE({
-        FILE_PERMISSIONS.fulfilled || WINDOWS.fulfilled
-    }),
-    SMART_TERMINAL({
-        System.getenv("TERM")?.toUpperCase() != "DUMB"
-    }),
-    XCODE({
-        // Simplistic approach at detecting Xcode by assuming macOS imply Xcode is present
-        MAC_OS_X.fulfilled
-    }),
-    MSBUILD({
-        // Simplistic approach at detecting MSBuild by assuming Windows imply MSBuild is present
-        WINDOWS.fulfilled && "embedded" != System.getProperty("org.gradle.integtest.executer")
-    }),
-    SUPPORTS_TARGETING_JAVA6({ !JDK12_OR_LATER.fulfilled }),
-    // Currently mac agents are not that strong so we avoid running high-concurrency tests on them
-    HIGH_PERFORMANCE(NOT_MAC_OS_X),
-    NOT_EC2_AGENT({
-        !InetAddress.getLocalHost().getHostName().startsWith("ip-")
-    }),
-    STABLE_GROOVY({ !GroovySystem.version.endsWith("-SNAPSHOT") }),
-    NOT_STABLE_GROOVY({ !STABLE_GROOVY.fulfilled })
-
-    /**
-     * A predicate for testing whether the precondition is fulfilled.
-     */
-    private Closure predicate
-
-    TestPrecondition(Closure predicate) {
-        this.predicate = predicate
-    }
-
-    TestPrecondition(TestPrecondition aliasOf) {
-        this.predicate = aliasOf.predicate
-    }
-
-    /**
-     * Tells if the precondition is fulfilled.
-     */
-    boolean isFulfilled() {
-        predicate()
-    }
-
-    @Override
-    Boolean create() {
-        return isFulfilled()
-    }
-}
-
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPreconditionExtension.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPreconditionExtension.groovy
deleted file mode 100644
index be7cd21..0000000
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/TestPreconditionExtension.groovy
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2011 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.util
-
-
-import org.spockframework.runtime.extension.IAnnotationDrivenExtension
-import org.spockframework.runtime.model.FeatureInfo
-import org.spockframework.runtime.model.SpecInfo
-
-class TestPreconditionExtension implements IAnnotationDrivenExtension<Requires> {
-    @Override
-    void visitSpecAnnotation(Requires annotation, SpecInfo spec) {
-        spec.skipped |= unsatisfied(annotation)
-    }
-
-    @Override
-    void visitFeatureAnnotation(Requires annotation, FeatureInfo feature) {
-        feature.skipped |= unsatisfied(annotation)
-    }
-
-    private boolean unsatisfied(Requires annotation) {
-        annotation.value().any { !it.fulfilled } || !annotation.adhoc().newInstance(null, null).call()
-    }
-}
diff --git a/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServices.groovy b/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServices.groovy
index cd7adc0..1fc233b 100644
--- a/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServices.groovy
+++ b/subprojects/internal-testing/src/main/groovy/org/gradle/util/UsesNativeServices.groovy
@@ -28,4 +28,4 @@
 @Target([ElementType.TYPE])
 @ExtensionAnnotation(UsesNativeServicesExtension)
 @interface UsesNativeServices {
-}
\ No newline at end of file
+}
diff --git a/subprojects/internal-testing/src/main/resources/valid-precondition-combinations.csv b/subprojects/internal-testing/src/main/resources/valid-precondition-combinations.csv
new file mode 100644
index 0000000..5284081
--- /dev/null
+++ b/subprojects/internal-testing/src/main/resources/valid-precondition-combinations.csv
@@ -0,0 +1,98 @@
+# See RequiresExtension.groovy and the "predicate-tester" project to learn more about how this file is used.
+# Note: Every class name here is implicitly prefixed with the "org.gradle.test.preconditions" package.
+
+PluginTestPreconditions$ShellcheckAvailable
+SigningTestPreconditions$GpgAvailable
+
+UnitTestPreconditions$CanInstallExecutable
+UnitTestPreconditions$CanInstallExecutable,UnitTestPreconditions$NotMacOs
+UnitTestPreconditions$CanInstallExecutable,UnitTestPreconditions$NotWindows
+UnitTestPreconditions$CaseInsensitiveFs
+UnitTestPreconditions$CaseSensitiveFs
+UnitTestPreconditions$FilePermissions
+UnitTestPreconditions$HasDocker
+UnitTestPreconditions$HasDocker,IntegTestPreconditions$NotEmbeddedExecutor
+UnitTestPreconditions$HasXCode
+UnitTestPreconditions$HasXCode,UnitTestPreconditions$NotMacOsM1
+UnitTestPreconditions$HighPerformance
+UnitTestPreconditions$Jdk10OrEarlier
+UnitTestPreconditions$Jdk11OrEarlier
+UnitTestPreconditions$Jdk11OrLater
+UnitTestPreconditions$Jdk14OrLater
+UnitTestPreconditions$Jdk14OrLater,IntegTestPreconditions$NotEmbeddedExecutor
+UnitTestPreconditions$Jdk15OrEarlier
+UnitTestPreconditions$Jdk16OrEarlier
+UnitTestPreconditions$Jdk16OrLater
+UnitTestPreconditions$Jdk17OrEarlier
+UnitTestPreconditions$Jdk17OrLater
+UnitTestPreconditions$Jdk8OrEarlier
+UnitTestPreconditions$Jdk8OrEarlier,IntegTestPreconditions$Java7HomeAvailable,IntegTestPreconditions$Java8HomeAvailable
+UnitTestPreconditions$Jdk8OrEarlier,UnitTestPreconditions$JdkOracle
+UnitTestPreconditions$Jdk9OrEarlier
+UnitTestPreconditions$Jdk9OrEarlier,UnitTestPreconditions$NotWindows
+UnitTestPreconditions$Jdk9OrLater
+UnitTestPreconditions$Jdk9OrLater,PluginTestPreconditions$BashAvailable
+UnitTestPreconditions$Jdk9OrLater,PluginTestPreconditions$DashAvailable
+UnitTestPreconditions$Jdk9OrLater,IntegTestPreconditions$NotEmbeddedExecutor,
+UnitTestPreconditions$Jdk9OrLater,IntegTestPreconditions$NotConfigCached,SmokeTestPreconditions$GradleBuildJvmSpecAvailable
+UnitTestPreconditions$Jdk9OrLater,PluginTestPreconditions$StaticShAvailable
+UnitTestPreconditions$Jdk9OrLater,IntegTestPreconditions$Java8HomeAvailable
+UnitTestPreconditions$Linux
+UnitTestPreconditions$MacOs
+UnitTestPreconditions$MandatoryFileLockOnOpen
+UnitTestPreconditions$NoFilePermissions
+UnitTestPreconditions$NoMandatoryFileLockOnOpen
+UnitTestPreconditions$NoSymlinks
+UnitTestPreconditions$NotEC2Agent
+UnitTestPreconditions$NotLinux
+UnitTestPreconditions$NotMacOs
+UnitTestPreconditions$NotMacOsM1
+UnitTestPreconditions$NotWindows
+UnitTestPreconditions$NotWindows,UnitTestPreconditions$Jdk8OrEarlier
+UnitTestPreconditions$NotWindows,UnitTestPreconditions$NotMacOs
+UnitTestPreconditions$NotWindows,UnitTestPreconditions$StableGroovy
+UnitTestPreconditions$NotWindowsJavaBefore9
+UnitTestPreconditions$NotWindowsJavaBefore11
+UnitTestPreconditions$Online
+UnitTestPreconditions$Online,TestKitPreconditions$LowestMajorGradleIsAvailable
+UnitTestPreconditions$Online,UnitTestPreconditions$Jdk11OrEarlier
+UnitTestPreconditions$SmartTerminalAvailable
+UnitTestPreconditions$StableGroovy
+UnitTestPreconditions$StableGroovy,UnitTestPreconditions$Jdk11OrLater
+UnitTestPreconditions$Symlinks
+UnitTestPreconditions$Unix
+UnitTestPreconditions$UnixDerivative
+UnitTestPreconditions$UnixDerivative,PluginTestPreconditions$BashAvailable
+UnitTestPreconditions$UnixDerivative,PluginTestPreconditions$DashAvailable
+UnitTestPreconditions$UnixDerivative,PluginTestPreconditions$StaticShAvailable
+UnitTestPreconditions$Windows
+UnitTestPreconditions$Windows,UnitTestPreconditions$Jdk7OrLater,UnitTestPreconditions$Jdk8OrEarlier
+UnitTestPreconditions$Windows,IntegTestPreconditions$IsEmbeddedExecutor
+UnitTestPreconditions$WorkingDir
+
+IntegTestPreconditions$CanPublishToS3
+IntegTestPreconditions$HasMsBuild
+IntegTestPreconditions$IsConfigCached
+IntegTestPreconditions$IsDaemonExecutor
+IntegTestPreconditions$IsEmbeddedExecutor
+IntegTestPreconditions$IsLongLivingProcess
+IntegTestPreconditions$Java11HomeAvailable
+IntegTestPreconditions$Java14HomeAvailable
+IntegTestPreconditions$Java7HomeAvailable
+IntegTestPreconditions$Java8HomeAvailable
+IntegTestPreconditions$Java8HomeAvailable,IntegTestPreconditions$Java11HomeAvailable
+IntegTestPreconditions$Java8HomeAvailable,IntegTestPreconditions$Java9HomeAvailable
+IntegTestPreconditions$MoreThanOneJava8HomeAvailable
+IntegTestPreconditions$MoreThanOneJava8HomeAvailable,IntegTestPreconditions$Java9HomeAvailable
+IntegTestPreconditions$NotDaemonExecutor
+IntegTestPreconditions$NotEmbeddedExecutor
+IntegTestPreconditions$NotEmbeddedExecutor,UnitTestPreconditions$NotWindowsJavaBefore11
+IntegTestPreconditions$NotEmbeddedExecutor,UnitTestPreconditions$UnixDerivative
+IntegTestPreconditions$NotEmbeddedExecutor,UnitTestPreconditions$StableGroovy
+IntegTestPreconditions$NotEmbeddedExecutor,UnitTestPreconditions$NotEC2Agent
+IntegTestPreconditions$NotEmbeddedExecutor,UnitTestPreconditions$Symlinks
+IntegTestPreconditions$NotEmbeddedExecutorOrNotWindows
+IntegTestPreconditions$NotConfigCached,IntegTestPreconditions$Java11HomeAvailable
+IntegTestPreconditions$NotParallelExecutor
+IntegTestPreconditions$UnsupportedJavaHomeAvailable
+IntegTestPreconditions$UnsupportedJavaHomeAvailable,IntegTestPreconditions$NotEmbeddedExecutor
diff --git a/subprojects/internal-testing/src/test/groovy/org/gradle/test/precondition/PredicateFileTest.groovy b/subprojects/internal-testing/src/test/groovy/org/gradle/test/precondition/PredicateFileTest.groovy
new file mode 100644
index 0000000..9dfb87a
--- /dev/null
+++ b/subprojects/internal-testing/src/test/groovy/org/gradle/test/precondition/PredicateFileTest.groovy
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 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.test.precondition
+
+import spock.lang.Specification
+
+class PredicateFileTest extends Specification {
+
+    Set<Set<String>> values = [
+        // Value NOT shared between single and multi cases
+        ["value1"] as Set,
+        // Value shared between single and multi cases
+        ["value2"] as Set,
+        ["value2", "value3"] as Set,
+    ] as Set
+
+    def "accept single values"() {
+        when:
+        checkValidCombinations([value])
+
+        then:
+        noExceptionThrown()
+
+        where:
+        value << ["value1", "value2"]
+    }
+
+    def "accept multiple values"() {
+        when:
+        checkValidCombinations(combination)
+
+        then:
+        noExceptionThrown()
+
+        where:
+        combination << [["value2", "value3"], ["value3", "value2"]]
+    }
+
+    def "throws exception when single value not found"() {
+        when:
+        checkValidCombinations(["nonexistent"])
+
+        then:
+        final ex = thrown(IllegalArgumentException)
+        ex.message.startsWith("Requested requirements [nonexistent] were not in the list of accepted combinations")
+    }
+
+    def "throws exception when single values are not found"() {
+        when:
+        checkValidCombinations(["nonexistent1", "nonexistent2"])
+
+        then:
+        final ex = thrown(IllegalArgumentException)
+        ex.message.startsWith("Requested requirements [nonexistent1, nonexistent2] were not in the list of accepted combinations.")
+    }
+
+    def "standard implementation loads CSV correctly"() {
+        when:
+        PredicatesFile.checkValidNameCombinations(
+            ["org.gradle.test.preconditions.UnitTestPreconditions\$Online"] as Set,
+            PredicatesFile.DEFAULT_ACCEPTED_COMBINATIONS
+        )
+
+        then:
+        noExceptionThrown()
+    }
+
+    private checkValidCombinations(List<String> combinations) {
+        PredicatesFile.checkValidNameCombinations(combinations as Set, values)
+    }
+
+}
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishFeaturesJavaIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishFeaturesJavaIntegTest.groovy
index d7c105c..cfa3682 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishFeaturesJavaIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishFeaturesJavaIntegTest.groovy
@@ -30,7 +30,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, org.gradle.api.internal.artifacts.JavaEcosystemSupport.DEPRECATED_JAVA_RUNTIME_JARS))
                     }
@@ -96,7 +96,7 @@
                 optionalFeature1RuntimeElements {
                     extendsFrom optionalFeature1Implementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, org.gradle.api.internal.artifacts.JavaEcosystemSupport.DEPRECATED_JAVA_RUNTIME_JARS))
                     }
@@ -108,7 +108,7 @@
                 optionalFeature2RuntimeElements {
                     extendsFrom optionalFeature2Implementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, org.gradle.api.internal.artifacts.JavaEcosystemSupport.DEPRECATED_JAVA_RUNTIME_JARS))
                     }
@@ -174,7 +174,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, org.gradle.api.internal.artifacts.JavaEcosystemSupport.DEPRECATED_JAVA_RUNTIME_JARS))
                     }
@@ -274,7 +274,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, org.gradle.api.internal.artifacts.JavaEcosystemSupport.DEPRECATED_JAVA_RUNTIME_JARS))
                     }
@@ -351,7 +351,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME))
                     }
diff --git a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishJavaIntegTest.groovy b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishJavaIntegTest.groovy
index 127c359..aabd4d1 100644
--- a/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishJavaIntegTest.groovy
+++ b/subprojects/ivy/src/integTest/groovy/org/gradle/api/publish/ivy/IvyPublishJavaIntegTest.groovy
@@ -17,6 +17,7 @@
 
 package org.gradle.api.publish.ivy
 
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublication
 import org.gradle.test.fixtures.ivy.IvyJavaModule
 import spock.lang.Issue
@@ -1292,7 +1293,7 @@
             ${mavenCentralRepository()}
 
             def testConf = configurations.create('testConf') {
-                canBeResolved = true
+                assert canBeResolved
                 attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.VERIFICATION))
             }
 
@@ -1312,7 +1313,12 @@
 
         expect:
         fails('publish')
-        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testConf' that contains a 'org.gradle.category' attribute with a value of 'verification'.  This attribute is reserved for test verification output and is not publishable.  See: ")
+        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testConf' that contains a 'org.gradle.category' attribute with a value of 'verification'.  " +
+            "This attribute is reserved for test verification output and is not publishable.  " + variantAttributesLink())
+    }
+
+    private variantAttributesLink() {
+        documentationRegistry.getDocumentationRecommendationFor("on this", "variant_attributes", "sec:verification_category")
     }
 
     def "can not publish variant with attribute specifying category = verification if defining new attribute with string"() {
@@ -1322,7 +1328,7 @@
             ${mavenCentralRepository()}
 
             def testConf = configurations.create('testConf') {
-                canBeResolved = true
+                assert canBeResolved
                 attributes.attribute(Attribute.of('org.gradle.category', String), 'verification')
             }
 
@@ -1342,7 +1348,8 @@
 
         expect:
         fails('publish')
-        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testConf' that contains a 'org.gradle.category' attribute with a value of 'verification'.  This attribute is reserved for test verification output and is not publishable.  See: ")
+        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testConf' that contains a 'org.gradle.category' attribute with a value of 'verification'.  " +
+            "This attribute is reserved for test verification output and is not publishable.  " + new DocumentationRegistry().getDocumentationRecommendationFor("on this", "variant_attributes", "sec:verification_category"))
     }
 
     def "can not publish test results from java test suite"() {
@@ -1387,7 +1394,8 @@
 
         expect:
         fails('test', 'publish')
-        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testResultsElementsForTest' that contains a 'org.gradle.category' attribute with a value of 'verification'.  This attribute is reserved for test verification output and is not publishable.  See: ")
+        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testResultsElementsForTest' that contains a 'org.gradle.category' attribute with a value of 'verification'.  " +
+            "This attribute is reserved for test verification output and is not publishable.  " + new DocumentationRegistry().getDocumentationRecommendationFor("on this", "variant_attributes", "sec:verification_category"))
     }
 
     def "can publish variants with attribute specifying category if value not verification"() {
@@ -1397,7 +1405,7 @@
             ${mavenCentralRepository()}
 
             def testConf = configurations.create('testConf') {
-                canBeResolved = true
+                assert canBeResolved
                 attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, 'not verification'))
             }
 
@@ -1449,7 +1457,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME))
                     }
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyArtifactSet.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyArtifactSet.java
index 318aecf..309389c 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyArtifactSet.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyArtifactSet.java
@@ -21,7 +21,7 @@
 /**
  * A Collection of {@link IvyArtifact}s to be included in an {@link IvyPublication}.
  *
- * Being a {@link DomainObjectSet}, a {@code IvyArtifactSet} provides convenient methods for querying, filtering, and applying actions to the set of {@link IvyArtifact}s.
+ * Being a {@link DomainObjectSet}, an {@code IvyArtifactSet} provides convenient methods for querying, filtering, and applying actions to the set of {@link IvyArtifact}s.
  *
  * <pre class='autoTested'>
  * plugins {
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyPublication.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyPublication.java
index 80630f7..2b97691 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyPublication.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/IvyPublication.java
@@ -23,9 +23,9 @@
 import org.gradle.internal.HasInternalProtocol;
 
 /**
- * A {@code IvyPublication} is the representation/configuration of how Gradle should publish something in Ivy format, to an Ivy repository.
+ * An {@code IvyPublication} is the representation/configuration of how Gradle should publish something in Ivy format, to an Ivy repository.
  *
- * You directly add a named Ivy Publication the project's {@code publishing.publications} container by providing {@link IvyPublication} as the type.
+ * You directly add a named Ivy publication the project's {@code publishing.publications} container by providing {@link IvyPublication} as the type.
  * <pre>
  * publishing {
  *   publications {
diff --git a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/plugins/IvyPublishPlugin.java b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/plugins/IvyPublishPlugin.java
index f651fb8..c4d6e26 100644
--- a/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/plugins/IvyPublishPlugin.java
+++ b/subprojects/ivy/src/main/java/org/gradle/api/publish/ivy/plugins/IvyPublishPlugin.java
@@ -28,7 +28,6 @@
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
 import org.gradle.api.internal.artifacts.repositories.DefaultIvyArtifactRepository;
 import org.gradle.api.internal.artifacts.repositories.descriptor.IvyRepositoryDescriptor;
-import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor;
 import org.gradle.api.internal.file.FileResolver;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.provider.DefaultProvider;
@@ -195,17 +194,13 @@ public boolean isSatisfiedBy(GenerateModuleMetadata element) {
 
     private static boolean hasStandardPattern(ArtifactRepository ivyArtifactRepository) {
         DefaultIvyArtifactRepository repo = (DefaultIvyArtifactRepository) ivyArtifactRepository;
-        RepositoryDescriptor descriptor = repo.getDescriptor();
-        if (descriptor instanceof IvyRepositoryDescriptor) {
-            IvyRepositoryDescriptor desc = (IvyRepositoryDescriptor) descriptor;
-            List<String> artifactPatterns = desc.getArtifactPatterns();
-            if (artifactPatterns.size() == 1) {
-                return artifactPatterns.get(0).equals(IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN);
-            } else {
-                return false;
-            }
+        IvyRepositoryDescriptor descriptor = repo.getDescriptor();
+        List<String> artifactPatterns = descriptor.getArtifactPatterns();
+        if (artifactPatterns.size() == 1) {
+            return artifactPatterns.get(0).equals(IvyArtifactRepository.GRADLE_ARTIFACT_PATTERN);
+        } else {
+            return false;
         }
-        return true;
     }
 
     private static class IvyPublicationFactory implements NamedDomainObjectFactory<IvyPublication> {
diff --git a/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoKotlinJvmPluginAggregationTest.groovy b/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoKotlinJvmPluginAggregationTest.groovy
index 7c74990..0bce978 100644
--- a/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoKotlinJvmPluginAggregationTest.groovy
+++ b/subprojects/jacoco/src/integTest/groovy/org/gradle/testing/jacoco/plugins/JacocoKotlinJvmPluginAggregationTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.testing.jacoco.plugins
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.versions.KotlinGradlePluginVersions
 import org.gradle.testing.jacoco.plugins.fixtures.JacocoReportXmlFixture
 import spock.lang.Issue
 
@@ -31,7 +32,8 @@
  */
 @Issue("https://github.com/gradle/gradle/issues/20532")
 class JacocoKotlinJvmPluginAggregationTest extends AbstractIntegrationSpec {
-    def kotlinVersion = "1.8.10" // Must remain >= 1.7, lower versions will produce deprecations warnings, on CI versions >= 1.7 will be used
+
+    def kotlinVersion = new KotlinGradlePluginVersions().latestStableOrRC
 
     def setup() {
         multiProjectBuild("root", ["direct", "transitive"]) {
diff --git a/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/plugins/JacocoPlugin.java b/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/plugins/JacocoPlugin.java
index d979751..d2031f8 100644
--- a/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/plugins/JacocoPlugin.java
+++ b/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/plugins/JacocoPlugin.java
@@ -28,6 +28,8 @@
 import org.gradle.api.attributes.TestSuiteType;
 import org.gradle.api.attributes.VerificationType;
 import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.file.FileOperations;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.model.ObjectFactory;
@@ -68,14 +70,14 @@ public abstract class JacocoPlugin implements Plugin<Project> {
      *
      * @since 3.4
      */
-    public static final String DEFAULT_JACOCO_VERSION = "0.8.8";
+    public static final String DEFAULT_JACOCO_VERSION = "0.8.9";
     public static final String AGENT_CONFIGURATION_NAME = "jacocoAgent";
     public static final String ANT_CONFIGURATION_NAME = "jacocoAnt";
     public static final String PLUGIN_EXTENSION_NAME = "jacoco";
     private static final String COVERAGE_DATA_ELEMENTS_VARIANT_PREFIX = "coverageDataElementsFor";
 
     private final Instantiator instantiator;
-    private Project project;
+    private ProjectInternal project;
 
     @Inject
     public JacocoPlugin(Instantiator instantiator) {
@@ -85,10 +87,9 @@ public JacocoPlugin(Instantiator instantiator) {
     @Override
     public void apply(Project project) {
         project.getPluginManager().apply(ReportingBasePlugin.class);
-        this.project = project;
+        this.project = (ProjectInternal) project;
         addJacocoConfigurations();
-        ProjectInternal projectInternal = (ProjectInternal) project;
-        JacocoAgentJar agent = instantiator.newInstance(JacocoAgentJar.class, projectInternal.getServices().get(FileOperations.class));
+        JacocoAgentJar agent = instantiator.newInstance(JacocoAgentJar.class, this.project.getServices().get(FileOperations.class));
         JacocoPluginExtension extension = project.getExtensions().create(PLUGIN_EXTENSION_NAME, JacocoPluginExtension.class, project, agent);
         extension.setToolVersion(DEFAULT_JACOCO_VERSION);
         final ReportingExtension reportingExtension = (ReportingExtension) project.getExtensions().getByName(ReportingExtension.NAME);
@@ -109,18 +110,16 @@ private void configureCoverageDataElementsVariants(Project project) {
 
             testSuites.withType(JvmTestSuite.class).configureEach(suite -> {
                 suite.getTargets().configureEach(target -> {
-                    createCoverageDataVariant(project, suite, target);
+                    createCoverageDataVariant((ProjectInternal) project, suite, target);
                 });
             });
         });
     }
 
-    private void createCoverageDataVariant(Project project, JvmTestSuite suite, JvmTestSuiteTarget target) {
-        final Configuration variant = project.getConfigurations().create(COVERAGE_DATA_ELEMENTS_VARIANT_PREFIX + StringUtils.capitalize(target.getName()));
+    private void createCoverageDataVariant(ProjectInternal project, JvmTestSuite suite, JvmTestSuiteTarget target) {
+        @SuppressWarnings("deprecation") final Configuration variant = project.getConfigurations().createWithRole(COVERAGE_DATA_ELEMENTS_VARIANT_PREFIX + StringUtils.capitalize(target.getName()), ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
         variant.setDescription("Binary data file containing results of Jacoco test coverage reporting for the " + suite.getName() + " Test Suite's " + target.getName() + " target.");
         variant.setVisible(false);
-        variant.setCanBeResolved(false);
-        variant.setCanBeConsumed(true);
 
         final ObjectFactory objects = project.getObjects();
         variant.attributes(attributes -> {
@@ -141,16 +140,15 @@ private void createCoverageDataVariant(Project project, JvmTestSuite suite, JvmT
      * Creates the configurations used by plugin.
      */
     private void addJacocoConfigurations() {
-        Configuration agentConf = project.getConfigurations().create(AGENT_CONFIGURATION_NAME);
+        RoleBasedConfigurationContainerInternal configurations = project.getConfigurations();
+        Configuration agentConf = configurations.resolvableBucket(AGENT_CONFIGURATION_NAME);
         agentConf.setVisible(false);
         agentConf.setTransitive(true);
         agentConf.setDescription("The Jacoco agent to use to get coverage data.");
-        agentConf.setCanBeConsumed(false);
-        Configuration antConf = project.getConfigurations().create(ANT_CONFIGURATION_NAME);
+        Configuration antConf = configurations.resolvableBucket(ANT_CONFIGURATION_NAME);
         antConf.setVisible(false);
         antConf.setTransitive(true);
         antConf.setDescription("The Jacoco ant tasks to use to get execute Gradle tasks.");
-        antConf.setCanBeConsumed(false);
     }
 
     /**
diff --git a/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/plugins/JacocoReportAggregationPlugin.java b/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/plugins/JacocoReportAggregationPlugin.java
index 1bfff7d..90cb4bc 100644
--- a/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/plugins/JacocoReportAggregationPlugin.java
+++ b/subprojects/jacoco/src/main/java/org/gradle/testing/jacoco/plugins/JacocoReportAggregationPlugin.java
@@ -30,6 +30,9 @@
 import org.gradle.api.attributes.TestSuiteType;
 import org.gradle.api.attributes.VerificationType;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.plugins.JavaBasePlugin;
 import org.gradle.api.plugins.jvm.JvmTestSuite;
@@ -64,19 +67,18 @@ public void apply(Project project) {
         project.getPluginManager().apply("jvm-ecosystem");
         project.getPluginManager().apply("jacoco");
 
-        Configuration jacocoAggregation = project.getConfigurations().create(JACOCO_AGGREGATION_CONFIGURATION_NAME);
+        RoleBasedConfigurationContainerInternal configurations = ((ProjectInternal) project).getConfigurations();
+        Configuration jacocoAggregation = configurations.bucket(JACOCO_AGGREGATION_CONFIGURATION_NAME);
         jacocoAggregation.setDescription("Collects project dependencies for purposes of JaCoCo coverage report aggregation");
         jacocoAggregation.setVisible(false);
-        jacocoAggregation.setCanBeConsumed(false);
-        jacocoAggregation.setCanBeResolved(false);
+        jacocoAggregation.setTransitive(true);
 
         ObjectFactory objects = project.getObjects();
-        Configuration codeCoverageResultsConf = project.getConfigurations().create("aggregateCodeCoverageReportResults");
+        @SuppressWarnings("deprecation") Configuration codeCoverageResultsConf = configurations.createWithRole("aggregateCodeCoverageReportResults", ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE);
         codeCoverageResultsConf.setDescription("Graph needed for the aggregated JaCoCo coverage report.");
         codeCoverageResultsConf.extendsFrom(jacocoAggregation);
         codeCoverageResultsConf.setVisible(false);
-        codeCoverageResultsConf.setCanBeConsumed(false);
-        codeCoverageResultsConf.setCanBeResolved(true);
+        jacocoAggregation.setTransitive(true);
 
         ArtifactView sourceDirectories = codeCoverageResultsConf.getIncoming().artifactView(view -> {
             view.withVariantReselection();
diff --git a/subprojects/jacoco/src/test/groovy/org/gradle/internal/jacoco/JacocoAgentJarTest.groovy b/subprojects/jacoco/src/test/groovy/org/gradle/internal/jacoco/JacocoAgentJarTest.groovy
index d388df6..e3c0708 100644
--- a/subprojects/jacoco/src/test/groovy/org/gradle/internal/jacoco/JacocoAgentJarTest.groovy
+++ b/subprojects/jacoco/src/test/groovy/org/gradle/internal/jacoco/JacocoAgentJarTest.groovy
@@ -45,6 +45,7 @@
         '0.8.6'               | true
         '0.8.7'               | true
         '0.8.8'               | true
+        '0.8.9'               | true
     }
 
     def "versions >= 0.7.6 support include no location classes #version -> #incNoLocationClassesSupport"() {
@@ -67,5 +68,6 @@
         '0.8.6'               | true
         '0.8.7'               | true
         '0.8.8'               | true
+        '0.8.9'               | true
     }
 }
diff --git a/subprojects/jacoco/src/test/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginSpec.groovy b/subprojects/jacoco/src/test/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginSpec.groovy
index 1e603d3c..48810b0 100644
--- a/subprojects/jacoco/src/test/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginSpec.groovy
+++ b/subprojects/jacoco/src/test/groovy/org/gradle/testing/jacoco/plugins/JacocoPluginSpec.groovy
@@ -20,8 +20,8 @@
 import org.gradle.integtests.fixtures.RepoScriptBlockUtil
 import org.gradle.language.base.plugins.LifecycleBasePlugin
 import org.gradle.test.fixtures.AbstractProjectBuilderSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 class JacocoPluginSpec extends AbstractProjectBuilderSpec {
@@ -45,7 +45,7 @@
         task.extensions.getByType(JacocoTaskExtension) != null
     }
 
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     @Issue("GRADLE-3498")
     def 'jacoco task extension can be configured. includeNoLocationClasses: #includeNoLocationClassesValue'() {
         given:
diff --git a/subprojects/jacoco/src/testFixtures/groovy/org/gradle/testing/jacoco/plugins/fixtures/JacocoCoverage.groovy b/subprojects/jacoco/src/testFixtures/groovy/org/gradle/testing/jacoco/plugins/fixtures/JacocoCoverage.groovy
index 8313756..4644817 100644
--- a/subprojects/jacoco/src/testFixtures/groovy/org/gradle/testing/jacoco/plugins/fixtures/JacocoCoverage.groovy
+++ b/subprojects/jacoco/src/testFixtures/groovy/org/gradle/testing/jacoco/plugins/fixtures/JacocoCoverage.groovy
@@ -24,21 +24,23 @@
     private JacocoCoverage() {}
 
     private static final String[] ALL = [JacocoPlugin.DEFAULT_JACOCO_VERSION, '0.7.1.201405082137', '0.7.6.201602180812', '0.8.3'].asImmutable()
+    // Order matters here, as we want to test the latest version first
+    // Relies on Groovy keeping the order of the keys in a map literal
+    private static final Map<JavaVersion, JacocoVersion> JDK_CUTOFFS = [
+        (JavaVersion.VERSION_20): JacocoVersion.SUPPORTS_JDK_20,
+        (JavaVersion.VERSION_18): JacocoVersion.SUPPORTS_JDK_18,
+        (JavaVersion.VERSION_17): JacocoVersion.SUPPORTS_JDK_17,
+        (JavaVersion.VERSION_16): JacocoVersion.SUPPORTS_JDK_16,
+        (JavaVersion.VERSION_15): JacocoVersion.SUPPORTS_JDK_15,
+        (JavaVersion.VERSION_14): JacocoVersion.SUPPORTS_JDK_14,
+        (JavaVersion.VERSION_1_9): JacocoVersion.SUPPORTS_JDK_9,
+    ]
 
-    // Release notes: https://www.jacoco.org/jacoco/trunk/doc/changes.html
     static List<String> getSupportedVersionsByJdk() {
-        if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_18)) {
-            return filter(JacocoVersion.SUPPORTS_JDK_18)
-        } else if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
-            return filter(JacocoVersion.SUPPORTS_JDK_17)
-        } else if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16)) {
-            return filter(JacocoVersion.SUPPORTS_JDK_16)
-        } else if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_15)) {
-            return filter(JacocoVersion.SUPPORTS_JDK_15)
-        } else if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_14)) {
-            return filter(JacocoVersion.SUPPORTS_JDK_14)
-        } else if (JavaVersion.current().isJava9Compatible()) {
-            return filter(JacocoVersion.SUPPORTS_JDK_9)
+        for (def cutoff : JDK_CUTOFFS) {
+            if (JavaVersion.current().isCompatibleWith(cutoff.key)) {
+                return filter(cutoff.value)
+            }
         }
         return filter(JacocoVersion.SUPPORTS_JDK_8)
     }
@@ -48,6 +50,7 @@
     }
 
     private static class JacocoVersion implements Comparable<JacocoVersion> {
+        // Release notes: https://www.jacoco.org/jacoco/trunk/doc/changes.html
         static final SUPPORTS_JDK_8 = new JacocoVersion(0, 7, 0)
         static final SUPPORTS_JDK_9 = new JacocoVersion(0, 7, 8)
         static final SUPPORTS_JDK_14 = new JacocoVersion(0, 8, 5)
@@ -55,6 +58,7 @@
         static final SUPPORTS_JDK_16 = new JacocoVersion(0, 8, 6)
         static final SUPPORTS_JDK_17 = new JacocoVersion(0, 8, 7)
         static final SUPPORTS_JDK_18 = new JacocoVersion(0, 8, 8)
+        static final SUPPORTS_JDK_20 = new JacocoVersion(0, 8, 9)
 
         private final int major
         private final int minor
diff --git a/subprojects/java-compiler-plugin/src/test/groovy/com/gradle/internal/compiler/java/listeners/ConstantsCollectorTest.groovy b/subprojects/java-compiler-plugin/src/test/groovy/com/gradle/internal/compiler/java/listeners/ConstantsCollectorTest.groovy
index 485f5a8..7726afd 100644
--- a/subprojects/java-compiler-plugin/src/test/groovy/com/gradle/internal/compiler/java/listeners/ConstantsCollectorTest.groovy
+++ b/subprojects/java-compiler-plugin/src/test/groovy/com/gradle/internal/compiler/java/listeners/ConstantsCollectorTest.groovy
@@ -19,7 +19,8 @@
 import com.gradle.internal.compiler.java.AbstractCompilerPluginTest
 import org.gradle.internal.compiler.java.TestCompiler
 import org.gradle.internal.compiler.java.listeners.constants.ConstantDependentsConsumer
-import spock.lang.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import java.nio.file.Files
 
@@ -564,7 +565,7 @@
         accessibleDependentToConstants["gradle.unit.test.package-info"] == ["gradle.unit.test.Constant"] as Set
     }
 
-    @Requires({ javaVersion >= 9 })
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "collect all constants for module-info class"() {
         String moduleDefinition = """
     import gradle.unit.test.Constant;
diff --git a/subprojects/jvm-services/src/integTest/groovy/org/gradle/jvm/toolchain/InvalidJvmInstallationReportingIntegrationTest.groovy b/subprojects/jvm-services/src/integTest/groovy/org/gradle/jvm/toolchain/InvalidJvmInstallationReportingIntegrationTest.groovy
index 4826ad2..5dbd234 100644
--- a/subprojects/jvm-services/src/integTest/groovy/org/gradle/jvm/toolchain/InvalidJvmInstallationReportingIntegrationTest.groovy
+++ b/subprojects/jvm-services/src/integTest/groovy/org/gradle/jvm/toolchain/InvalidJvmInstallationReportingIntegrationTest.groovy
@@ -71,7 +71,7 @@
         results.size() == 2
         results.every { result ->
             def expectedErrorMessages = [invalidJdkHome1, invalidJdkHome2].collect {
-                "Invalid Java installation found at '${it.canonicalPath}' (system property 'org.gradle.java.installations.paths'). " +
+                "Invalid Java installation found at '${it.canonicalPath}' (Gradle property 'org.gradle.java.installations.paths'). " +
                     "It will be re-checked in the next build. This might have performance impact if it keeps failing. " +
                     "Run the 'javaToolchains' task for more details."
             }
diff --git a/subprojects/jvm-services/src/integTest/groovy/org/gradle/jvm/toolchain/JavaInstallationRegistryIntegrationTest.groovy b/subprojects/jvm-services/src/integTest/groovy/org/gradle/jvm/toolchain/JavaInstallationRegistryIntegrationTest.groovy
index 027ab89..41bc18d 100644
--- a/subprojects/jvm-services/src/integTest/groovy/org/gradle/jvm/toolchain/JavaInstallationRegistryIntegrationTest.groovy
+++ b/subprojects/jvm-services/src/integTest/groovy/org/gradle/jvm/toolchain/JavaInstallationRegistryIntegrationTest.groovy
@@ -25,7 +25,7 @@
 
     def "installation registry has no installations without environment setup or auto-detection"() {
         buildFile << """
-            import org.gradle.jvm.toolchain.internal.JavaInstallationRegistry;
+            import org.gradle.internal.jvm.inspection.JavaInstallationRegistry;
 
             abstract class ShowPlugin implements Plugin<Project> {
                 @Inject
@@ -57,7 +57,7 @@
         def secondJavaHome = AvailableJavaHomes.availableJvms[1].javaHome.absolutePath
 
         buildFile << """
-            import org.gradle.jvm.toolchain.internal.JavaInstallationRegistry;
+            import org.gradle.internal.jvm.inspection.JavaInstallationRegistry;
 
             abstract class ShowPlugin implements Plugin<Project> {
                 @Inject
@@ -82,7 +82,7 @@
             .withTasks("show")
             .run()
         then:
-        outputContains("${File.separator}unknown${File.separator}path' (system property 'org.gradle.java.installations.paths') used for java installations does not exist")
+        outputContains("${File.separator}unknown${File.separator}path' (Gradle property 'org.gradle.java.installations.paths') used for java installations does not exist")
         outputContains("${File.separator}unknown${File.separator}env' (environment variable 'JDK1') used for java installations does not exist")
         outputContains(firstJavaHome)
         outputContains(secondJavaHome)
@@ -95,7 +95,7 @@
             .withTasks("show")
             .run()
         then:
-        outputContains("${File.separator}other${File.separator}path' (system property 'org.gradle.java.installations.paths') used for java installations does not exist")
+        outputContains("${File.separator}other${File.separator}path' (Gradle property 'org.gradle.java.installations.paths') used for java installations does not exist")
         outputContains("${File.separator}unknown${File.separator}env' (environment variable 'JDK1') used for java installations does not exist")
         outputContains(firstJavaHome)
         outputContains(secondJavaHome)
@@ -109,12 +109,12 @@
         def subprojects = [subproject]
         def rootProject = multiProjectBuild("project", subprojects) {
             buildFile << """
-                import org.gradle.jvm.toolchain.internal.JavaInstallationRegistry;
+                import org.gradle.internal.jvm.inspection.JavaInstallationRegistry;
 
                 abstract class ShowPlugin implements Plugin<Project> {
                     @Inject
                     abstract JavaInstallationRegistry getRegistry()
-    
+
                     void apply(Project project) {
                         project.tasks.register("show") {
                            registry.listInstallations().each { println it.location }
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JavaInstallationRegistry.java b/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JavaInstallationRegistry.java
new file mode 100644
index 0000000..9e38b54
--- /dev/null
+++ b/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JavaInstallationRegistry.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2023 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.jvm.inspection;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.gradle.api.GradleException;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.logging.progress.ProgressLogger;
+import org.gradle.internal.logging.progress.ProgressLoggerFactory;
+import org.gradle.internal.operations.BuildOperationContext;
+import org.gradle.internal.operations.BuildOperationDescriptor;
+import org.gradle.internal.operations.BuildOperationExecutor;
+import org.gradle.internal.operations.CallableBuildOperation;
+import org.gradle.internal.os.OperatingSystem;
+import org.gradle.internal.service.scopes.Scopes;
+import org.gradle.internal.service.scopes.ServiceScope;
+import org.gradle.jvm.toolchain.internal.InstallationLocation;
+import org.gradle.jvm.toolchain.internal.InstallationSupplier;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+@ServiceScope(Scopes.Build.class)
+public class JavaInstallationRegistry {
+    private final BuildOperationExecutor executor;
+    private final Installations installations;
+    private final JvmMetadataDetector metadataDetector;
+    private final Logger logger;
+    private final OperatingSystem os;
+
+    private final ProgressLoggerFactory progressLoggerFactory;
+
+    @Inject
+    public JavaInstallationRegistry(
+        List<InstallationSupplier> suppliers,
+        JvmMetadataDetector metadataDetector,
+        @Nullable BuildOperationExecutor executor,
+        OperatingSystem os,
+        ProgressLoggerFactory progressLoggerFactory
+    ) {
+        this(suppliers, metadataDetector, Logging.getLogger(JavaInstallationRegistry.class), executor, os, progressLoggerFactory);
+    }
+
+    private JavaInstallationRegistry(
+        List<InstallationSupplier> suppliers,
+        JvmMetadataDetector metadataDetector,
+        Logger logger,
+       @Nullable BuildOperationExecutor executor,
+        OperatingSystem os,
+        ProgressLoggerFactory progressLoggerFactory
+    ) {
+        this.logger = logger;
+        this.executor = executor;
+        this.metadataDetector = metadataDetector;
+        this.installations = new Installations(() -> maybeCollectInBuildOperation(suppliers));
+        this.os = os;
+        this.progressLoggerFactory = progressLoggerFactory;
+    }
+
+    @VisibleForTesting
+    static JavaInstallationRegistry withLogger(
+        List<InstallationSupplier> suppliers,
+        JvmMetadataDetector metadataDetector,
+        Logger logger,
+        BuildOperationExecutor executor,
+        ProgressLoggerFactory progressLoggerFactory
+    ) {
+        return new JavaInstallationRegistry(suppliers, metadataDetector, logger, executor, OperatingSystem.current(), progressLoggerFactory);
+    }
+
+    private Set<InstallationLocation> maybeCollectInBuildOperation(List<InstallationSupplier> suppliers) {
+        if (executor != null) {
+            return executor.call(new ToolchainDetectionBuildOperation(() -> collectInstallations(suppliers)));
+        } else {
+            return collectInstallations(suppliers);
+        }
+    }
+
+    protected Set<InstallationLocation> listInstallations() {
+        return installations.get();
+    }
+
+    public List<JvmToolchainMetadata> toolchains() {
+        ProgressLogger progressLogger = progressLoggerFactory.newOperation(JavaInstallationRegistry.class).start("Discovering toolchains", "Discovering toolchains");
+        List<JvmToolchainMetadata> result = listInstallations()
+            .parallelStream()
+            .peek(location -> progressLogger.progress("Extracting toolchain metadata from " + location.getDisplayName()))
+            .map(this::resolveMetadata)
+            .collect(Collectors.toList());
+        progressLogger.completed();
+        return result;
+    }
+
+    private JvmToolchainMetadata resolveMetadata(InstallationLocation location) {
+        JvmInstallationMetadata metadata = metadataDetector.getMetadata(location);
+        return new JvmToolchainMetadata(metadata, location);
+    }
+
+    public void addInstallation(InstallationLocation installation) {
+        installations.add(installation);
+    }
+
+    private Set<InstallationLocation> collectInstallations(List<InstallationSupplier> suppliers) {
+        return suppliers.parallelStream()
+            .peek(x -> logger.debug("Discovering toolchains provided via {}", x.getSourceName()))
+            .map(InstallationSupplier::get)
+            .flatMap(Set::stream)
+            .filter(this::installationExists)
+            .map(this::canonicalize)
+            .map(this::maybeGetEnclosedInstallation)
+            .filter(this::installationHasExecutable)
+            .filter(distinctByKey(InstallationLocation::getLocation))
+            .collect(Collectors.toSet());
+    }
+
+    protected boolean installationExists(InstallationLocation installationLocation) {
+        File file = installationLocation.getLocation();
+        if (!file.exists()) {
+            logger.warn("Directory {} used for java installations does not exist", installationLocation.getDisplayName());
+            return false;
+        }
+        if (!file.isDirectory()) {
+            logger.warn("Path for java installation {} points to a file, not a directory", installationLocation.getDisplayName());
+            return false;
+        }
+        return true;
+    }
+
+    protected boolean installationHasExecutable(InstallationLocation installationLocation) {
+        if (!hasJavaExecutable(installationLocation.getLocation())) {
+            logger.warn("Path for java installation {} does not contain a java executable", installationLocation.getDisplayName());
+            return false;
+        }
+        return true;
+    }
+
+    private InstallationLocation canonicalize(InstallationLocation location) {
+        final File file = location.getLocation();
+        try {
+            final File canonicalFile = file.getCanonicalFile();
+            final File javaHome = findJavaHome(canonicalFile);
+            return new InstallationLocation(javaHome, location.getSource(), location.isAutoProvisioned());
+        } catch (IOException e) {
+            throw new GradleException(String.format("Could not canonicalize path to java installation: %s.", file), e);
+        }
+    }
+
+    private InstallationLocation maybeGetEnclosedInstallation(InstallationLocation location) {
+        final File home = location.getLocation();
+        final File parentPath = home.getParentFile();
+        final boolean isEmbeddedJre = home.getName().equalsIgnoreCase("jre");
+        if (isEmbeddedJre && hasJavaExecutable(parentPath)) {
+            return new InstallationLocation(parentPath, location.getSource());
+        }
+        return location;
+    }
+
+    private File findJavaHome(File potentialHome) {
+        if (os.isMacOsX() && new File(potentialHome, "Contents/Home").exists()) {
+            return new File(potentialHome, "Contents/Home");
+        }
+        final File standaloneJre = new File(potentialHome, "jre");
+        if (!hasJavaExecutable(potentialHome) && hasJavaExecutable(standaloneJre)) {
+            return standaloneJre;
+        }
+        return potentialHome;
+    }
+
+    private boolean hasJavaExecutable(File potentialHome) {
+        return new File(potentialHome, os.getExecutableName("bin/java")).exists();
+    }
+
+    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
+        Set<Object> seen = ConcurrentHashMap.newKeySet();
+        return t -> seen.add(keyExtractor.apply(t));
+    }
+
+
+    private static class ToolchainDetectionBuildOperation implements CallableBuildOperation<Set<InstallationLocation>> {
+        private final Callable<Set<InstallationLocation>> detectionStrategy;
+
+        public ToolchainDetectionBuildOperation(Callable<Set<InstallationLocation>> detectionStrategy) {
+            this.detectionStrategy = detectionStrategy;
+        }
+
+        @Override
+        public Set<InstallationLocation> call(BuildOperationContext context) throws Exception {
+            return detectionStrategy.call();
+        }
+
+        @Override
+        public BuildOperationDescriptor.Builder description() {
+            return BuildOperationDescriptor
+                .displayName("Toolchain detection")
+                .progressDisplayName("Detecting local java toolchains");
+        }
+    }
+
+    private static class Installations {
+
+        private final Supplier<Set<InstallationLocation>> initializer;
+
+        private Set<InstallationLocation> locations = null;
+
+        Installations(Supplier<Set<InstallationLocation>> initializer) {
+            this.initializer = initializer;
+        }
+
+        synchronized Set<InstallationLocation> get() {
+            initIfNeeded();
+            return locations;
+        }
+
+        synchronized void add(InstallationLocation location) {
+            initIfNeeded();
+            locations.add(location);
+        }
+
+        private void initIfNeeded() {
+            if (locations == null) {
+                locations = initializer.get();
+            }
+        }
+
+    }
+
+}
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JvmInstallationMetadataComparator.java b/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JvmInstallationMetadataComparator.java
new file mode 100644
index 0000000..9e11a31
--- /dev/null
+++ b/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JvmInstallationMetadataComparator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.jvm.inspection;
+
+import org.gradle.util.internal.VersionNumber;
+
+import java.io.File;
+import java.util.Comparator;
+
+public class JvmInstallationMetadataComparator implements Comparator<JvmInstallationMetadata> {
+
+    private final File currentJavaHome;
+
+    public JvmInstallationMetadataComparator(File currentJavaHome) {
+        this.currentJavaHome = currentJavaHome;
+    }
+
+    @Override
+    public int compare(JvmInstallationMetadata o1, JvmInstallationMetadata o2) {
+        return Comparator
+            .comparing(this::isCurrentJvm)
+            .thenComparing(this::isJdk)
+            .thenComparing(this::extractVendor, Comparator.reverseOrder())
+            .thenComparing(this::getToolchainVersion)
+            // It is possible for different JDK builds to have exact same version. The input order
+            // may change so the installation path breaks ties to keep sorted output consistent
+            // between runs.
+            .thenComparing(JvmInstallationMetadata::getJavaHome)
+            .reversed()
+            .compare(o1, o2);
+    }
+
+    boolean isCurrentJvm(JvmInstallationMetadata metadata) {
+        return metadata.getJavaHome().toFile().equals(currentJavaHome);
+    }
+
+    private boolean isJdk(JvmInstallationMetadata metadata) {
+        return metadata.hasCapability(JvmInstallationMetadata.JavaInstallationCapability.JAVA_COMPILER);
+    }
+
+    private JvmVendor.KnownJvmVendor extractVendor(JvmInstallationMetadata metadata) {
+        return metadata.getVendor().getKnownVendor();
+    }
+
+    private VersionNumber getToolchainVersion(JvmInstallationMetadata metadata) {
+        return VersionNumber.withPatchNumber().parse(metadata.getJavaVersion());
+    }
+}
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JvmToolchainMetadata.java b/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JvmToolchainMetadata.java
new file mode 100644
index 0000000..71d9651
--- /dev/null
+++ b/subprojects/jvm-services/src/main/java/org/gradle/internal/jvm/inspection/JvmToolchainMetadata.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 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.jvm.inspection;
+
+import org.gradle.jvm.toolchain.internal.InstallationLocation;
+
+public class JvmToolchainMetadata {
+
+    public JvmInstallationMetadata metadata;
+
+    public InstallationLocation location;
+
+    public JvmToolchainMetadata(JvmInstallationMetadata metadata, InstallationLocation location) {
+        this.metadata = metadata;
+        this.location = location;
+    }
+
+}
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/AsdfInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/AsdfInstallationSupplier.java
index 3bced71..72d465ed 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/AsdfInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/AsdfInstallationSupplier.java
@@ -39,6 +39,11 @@ public AsdfInstallationSupplier(ProviderFactory factory) {
     }
 
     @Override
+    public String getSourceName() {
+        return "asdf-vm";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         return asdfDataDir.map(findJavaCandidates())
             .orElse(asdfUserHome.map(findJavaCandidates()))
@@ -48,7 +53,7 @@ protected Set<InstallationLocation> findCandidates() {
     private Transformer<Set<InstallationLocation>, String> findJavaCandidates() {
         return candidatesDir -> {
             final File root = new File(candidatesDir, "installs/java");
-            return FileBasedInstallationFactory.fromDirectory(root, "asdf-vm");
+            return FileBasedInstallationFactory.fromDirectory(root, getSourceName());
         };
     }
 
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/CurrentInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/CurrentInstallationSupplier.java
index 6fae520..6245abd 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/CurrentInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/CurrentInstallationSupplier.java
@@ -32,12 +32,17 @@ public CurrentInstallationSupplier(ProviderFactory factory) {
     }
 
     @Override
+    public String getSourceName() {
+        return "Current JVM";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         return Collections.singleton(asInstallation(Jvm.current().getJavaHome()));
     }
 
     private InstallationLocation asInstallation(File javaHome) {
-        return new InstallationLocation(javaHome, "Current JVM");
+        return new InstallationLocation(javaHome, getSourceName());
     }
 
 }
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/EnvironmentVariableListInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/EnvironmentVariableListInstallationSupplier.java
index dc42e7b..3a25fb8 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/EnvironmentVariableListInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/EnvironmentVariableListInstallationSupplier.java
@@ -41,6 +41,11 @@ public EnvironmentVariableListInstallationSupplier(ProviderFactory factory, File
     }
 
     @Override
+    public String getSourceName() {
+        return "environment variables from gradle property '" + JAVA_INSTALLATIONS_FROM_ENV_PROPERTY + "'";
+    }
+
+    @Override
     public Set<InstallationLocation> get() {
         final Provider<String> property = factory.gradleProperty(JAVA_INSTALLATIONS_FROM_ENV_PROPERTY);
         if (property.isPresent()) {
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/InstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/InstallationSupplier.java
index a09766b..ba21888 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/InstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/InstallationSupplier.java
@@ -20,5 +20,5 @@
 import java.util.function.Supplier;
 
 public interface InstallationSupplier extends Supplier<Set<InstallationLocation>> {
-
+    String getSourceName();
 }
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/IntellijInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/IntellijInstallationSupplier.java
index 962e3df..f87d3cb 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/IntellijInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/IntellijInstallationSupplier.java
@@ -51,8 +51,13 @@ private String defaultJdksDirectory(OperatingSystem os) {
     }
 
     @Override
+    public String getSourceName() {
+        return "IntelliJ IDEA";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         File directory = fileResolver.resolve(ideaJdksDirectory.get());
-        return FileBasedInstallationFactory.fromDirectory(directory, "IntelliJ IDEA");
+        return FileBasedInstallationFactory.fromDirectory(directory, getSourceName());
     }
 }
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/JabbaInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/JabbaInstallationSupplier.java
index 2b8252e..00bc32c 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/JabbaInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/JabbaInstallationSupplier.java
@@ -36,6 +36,11 @@ public JabbaInstallationSupplier(ProviderFactory factory) {
     }
 
     @Override
+    public String getSourceName() {
+        return "Jabba";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         return jabbaHomeDir.map(findJavaCandidates()).getOrElse(Collections.emptySet());
     }
@@ -43,7 +48,7 @@ protected Set<InstallationLocation> findCandidates() {
     private Transformer<Set<InstallationLocation>, String> findJavaCandidates() {
         return jabbaHome -> {
             final File root = new File(jabbaHome, "jdk");
-            return FileBasedInstallationFactory.fromDirectory(root, "jabba");
+            return FileBasedInstallationFactory.fromDirectory(root, getSourceName());
         };
     }
 
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/JavaInstallationRegistry.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/JavaInstallationRegistry.java
deleted file mode 100644
index e16ec05..0000000
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/JavaInstallationRegistry.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright 2020 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.jvm.toolchain.internal;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.gradle.api.GradleException;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.internal.operations.BuildOperationContext;
-import org.gradle.internal.operations.BuildOperationDescriptor;
-import org.gradle.internal.operations.BuildOperationExecutor;
-import org.gradle.internal.operations.CallableBuildOperation;
-import org.gradle.internal.os.OperatingSystem;
-import org.gradle.internal.service.scopes.Scopes;
-import org.gradle.internal.service.scopes.ServiceScope;
-
-import javax.inject.Inject;
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-@ServiceScope(Scopes.Build.class)
-public class JavaInstallationRegistry {
-    private final BuildOperationExecutor executor;
-    private final Installations installations;
-    private final Logger logger;
-    private final OperatingSystem os;
-
-    @Inject
-    public JavaInstallationRegistry(List<InstallationSupplier> suppliers, BuildOperationExecutor executor, OperatingSystem os) {
-        this(suppliers, Logging.getLogger(JavaInstallationRegistry.class), executor, os);
-    }
-
-    private JavaInstallationRegistry(List<InstallationSupplier> suppliers, Logger logger, BuildOperationExecutor executor, OperatingSystem os) {
-        this.logger = logger;
-        this.executor = executor;
-        this.installations = new Installations(() -> collectInBuildOperation(suppliers));
-        this.os = os;
-    }
-
-    @VisibleForTesting
-    static JavaInstallationRegistry withLogger(List<InstallationSupplier> suppliers, Logger logger, BuildOperationExecutor executor) {
-        return new JavaInstallationRegistry(suppliers, logger, executor, OperatingSystem.current());
-    }
-
-    private Set<InstallationLocation> collectInBuildOperation(List<InstallationSupplier> suppliers) {
-        return executor.call(new ToolchainDetectionBuildOperation(() -> collectInstallations(suppliers)));
-    }
-
-    public Set<InstallationLocation> listInstallations() {
-        return installations.get();
-    }
-
-    public void addInstallation(InstallationLocation installation) {
-       installations.add(installation);
-    }
-
-    private Set<InstallationLocation> collectInstallations(List<InstallationSupplier> suppliers) {
-        return suppliers.parallelStream()
-            .map(InstallationSupplier::get)
-            .flatMap(Set::stream)
-            .filter(this::installationExists)
-            .map(this::canonicalize)
-            .filter(this::installationHasExecutable)
-            .filter(distinctByKey(InstallationLocation::getLocation))
-            .collect(Collectors.toSet());
-    }
-
-    boolean installationExists(InstallationLocation installationLocation) {
-        File file = installationLocation.getLocation();
-        if (!file.exists()) {
-            logger.warn("Directory {} used for java installations does not exist", installationLocation.getDisplayName());
-            return false;
-        }
-        if (!file.isDirectory()) {
-            logger.warn("Path for java installation {} points to a file, not a directory", installationLocation.getDisplayName());
-            return false;
-        }
-        return true;
-    }
-
-    boolean installationHasExecutable(InstallationLocation installationLocation) {
-        if (!hasJavaExecutable(installationLocation.getLocation())) {
-            logger.warn("Path for java installation {} does not contain a java executable", installationLocation.getDisplayName());
-            return false;
-        }
-        return true;
-    }
-
-    private InstallationLocation canonicalize(InstallationLocation location) {
-        final File file = location.getLocation();
-        try {
-            final File canonicalFile = file.getCanonicalFile();
-            final File javaHome = findJavaHome(canonicalFile);
-            return new InstallationLocation(javaHome, location.getSource(), location.isAutoProvisioned());
-        } catch (IOException e) {
-            throw new GradleException(String.format("Could not canonicalize path to java installation: %s.", file), e);
-        }
-    }
-
-    private File findJavaHome(File potentialHome) {
-        if (os.isMacOsX() && new File(potentialHome, "Contents/Home").exists()) {
-            return new File(potentialHome, "Contents/Home");
-        }
-        final File standaloneJre = new File(potentialHome, "jre");
-        if(!hasJavaExecutable(potentialHome) && hasJavaExecutable(standaloneJre)) {
-            return standaloneJre;
-        }
-        return potentialHome;
-    }
-
-    private boolean hasJavaExecutable(File potentialHome) {
-        return new File(potentialHome, os.getExecutableName("bin/java")).exists();
-    }
-
-    public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
-        Set<Object> seen = ConcurrentHashMap.newKeySet();
-        return t -> seen.add(keyExtractor.apply(t));
-    }
-
-
-    private static class ToolchainDetectionBuildOperation implements CallableBuildOperation<Set<InstallationLocation>> {
-        private final Callable<Set<InstallationLocation>> detectionStrategy;
-
-        public ToolchainDetectionBuildOperation(Callable<Set<InstallationLocation>> detectionStrategy) {
-            this.detectionStrategy = detectionStrategy;
-        }
-
-        @Override
-        public Set<InstallationLocation> call(BuildOperationContext context) throws Exception {
-            return detectionStrategy.call();
-        }
-
-        @Override
-        public BuildOperationDescriptor.Builder description() {
-            return BuildOperationDescriptor
-                .displayName("Toolchain detection")
-                .progressDisplayName("Detecting local java toolchains");
-        }
-    }
-
-    private static class Installations {
-
-        private final Supplier<Set<InstallationLocation>> initializer;
-
-        private Set<InstallationLocation> locations = null;
-
-        Installations(Supplier<Set<InstallationLocation>> initializer) {
-            this.initializer = initializer;
-        }
-
-        synchronized Set<InstallationLocation> get() {
-            initIfNeeded();
-            return locations;
-        }
-
-        synchronized void add(InstallationLocation location) {
-            initIfNeeded();
-            locations.add(location);
-        }
-
-        private void initIfNeeded() {
-            if (locations == null) {
-                locations = initializer.get();
-            }
-        }
-
-    }
-
-}
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/LinuxInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/LinuxInstallationSupplier.java
index c24c7a3..5222882 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/LinuxInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/LinuxInstallationSupplier.java
@@ -43,10 +43,15 @@ private LinuxInstallationSupplier(ProviderFactory factory, OperatingSystem os, S
     }
 
     @Override
+    public String getSourceName() {
+        return "Common Linux Locations";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         if (os.isLinux()) {
             return Arrays.stream(roots)
-                .map(root -> FileBasedInstallationFactory.fromDirectory(new File(root), "Common Linux Locations"))
+                .map(root -> FileBasedInstallationFactory.fromDirectory(new File(root), getSourceName()))
                 .flatMap(Set::stream)
                 .collect(Collectors.toSet());
         }
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/LocationListInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/LocationListInstallationSupplier.java
index 54aa03c..24f691f 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/LocationListInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/LocationListInstallationSupplier.java
@@ -40,6 +40,11 @@ public LocationListInstallationSupplier(ProviderFactory factory, FileResolver fi
     }
 
     @Override
+    public String getSourceName() {
+        return "Gradle property '" + JAVA_INSTALLATIONS_PATHS_PROPERTY + "'";
+    }
+
+    @Override
     public Set<InstallationLocation> get() {
         final Provider<String> property = factory.gradleProperty(JAVA_INSTALLATIONS_PATHS_PROPERTY);
         return property.map(paths -> asInstallations(paths)).orElse(Collections.emptySet()).get();
@@ -48,7 +53,7 @@ public Set<InstallationLocation> get() {
     private Set<InstallationLocation> asInstallations(String listOfDirectories) {
         return Arrays.stream(listOfDirectories.split(","))
             .filter(path -> !path.trim().isEmpty())
-            .map(path -> new InstallationLocation(fileResolver.resolve(path), "system property '" + JAVA_INSTALLATIONS_PATHS_PROPERTY + "'"))
+            .map(path -> new InstallationLocation(fileResolver.resolve(path), getSourceName()))
             .collect(Collectors.toSet());
     }
 
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/MavenToolchainsInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/MavenToolchainsInstallationSupplier.java
index cd0761c..57b9513 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/MavenToolchainsInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/MavenToolchainsInstallationSupplier.java
@@ -66,6 +66,11 @@ public MavenToolchainsInstallationSupplier(ProviderFactory factory, FileResolver
     }
 
     @Override
+    public String getSourceName() {
+        return "Maven Toolchains";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         File toolchainFile = fileResolver.resolve(toolchainLocation.get());
         if (toolchainFile.exists()) {
@@ -84,7 +89,7 @@ protected Set<InstallationLocation> findCandidates() {
                     }
                 }
                 return locations.stream()
-                    .map(jdkHome -> new InstallationLocation(new File(jdkHome), "Maven Toolchains"))
+                    .map(jdkHome -> new InstallationLocation(new File(jdkHome), getSourceName()))
                     .collect(Collectors.toSet());
             } catch (IOException | ParserConfigurationException | SAXException | XPathExpressionException e) {
                 if (LOGGER.isDebugEnabled()) {
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/OsXInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/OsXInstallationSupplier.java
index 8d7d7cc..ad7ecad 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/OsXInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/OsXInstallationSupplier.java
@@ -47,6 +47,11 @@ public OsXInstallationSupplier(ExecHandleFactory execHandleFactory, ProviderFact
     }
 
     @Override
+    public String getSourceName() {
+        return "MacOS java_home";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         if (os.isMacOsX()) {
             try {
@@ -66,7 +71,7 @@ protected Set<InstallationLocation> findCandidates() {
     }
 
     private InstallationLocation asInstallation(File javaHome) {
-        return new InstallationLocation(javaHome, "macOS java_home");
+        return new InstallationLocation(javaHome, getSourceName());
     }
 
     private Reader executeJavaHome() {
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/SdkmanInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/SdkmanInstallationSupplier.java
index 04c2054..76b4ecd 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/SdkmanInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/SdkmanInstallationSupplier.java
@@ -36,6 +36,11 @@ public SdkmanInstallationSupplier(ProviderFactory factory) {
     }
 
     @Override
+    public String getSourceName() {
+        return "SDKMAN!";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         return candidatesDir.map(findJavaCandidates()).getOrElse(Collections.emptySet());
     }
@@ -47,7 +52,7 @@ private String defaultSdkmanCandidatesDirectory() {
     private Transformer<Set<InstallationLocation>, String> findJavaCandidates() {
         return candidatesDir -> {
             final File root = new File(candidatesDir, "java");
-            return FileBasedInstallationFactory.fromDirectory(root, "SDKMAN!");
+            return FileBasedInstallationFactory.fromDirectory(root, getSourceName());
         };
     }
 
diff --git a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/WindowsInstallationSupplier.java b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/WindowsInstallationSupplier.java
index f742513..8acef20 100644
--- a/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/WindowsInstallationSupplier.java
+++ b/subprojects/jvm-services/src/main/java/org/gradle/jvm/toolchain/internal/WindowsInstallationSupplier.java
@@ -41,6 +41,11 @@ public WindowsInstallationSupplier(WindowsRegistry registry, OperatingSystem os,
     }
 
     @Override
+    public String getSourceName() {
+        return "Windows Registry";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         if (os.isWindows()) {
             return findInstallationsInRegistry();
@@ -58,7 +63,7 @@ private Set<InstallationLocation> findInstallationsInRegistry() {
             "SOFTWARE\\Wow6432Node\\JavaSoft\\Java Runtime Environment"
         ).stream().map(this::findJvms).flatMap(List::stream);
         return Stream.concat(openJdkInstallations, jvms)
-            .map(javaHome -> new InstallationLocation(new File(javaHome), "Windows Registry"))
+            .map(javaHome -> new InstallationLocation(new File(javaHome), getSourceName()))
             .collect(Collectors.toSet());
     }
 
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/internal/jvm/inspection/CachingJvmMetadataDetectorTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/internal/jvm/inspection/CachingJvmMetadataDetectorTest.groovy
index 46cef2e..c89b529 100644
--- a/subprojects/jvm-services/src/test/groovy/org/gradle/internal/jvm/inspection/CachingJvmMetadataDetectorTest.groovy
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/internal/jvm/inspection/CachingJvmMetadataDetectorTest.groovy
@@ -20,9 +20,9 @@
 import org.gradle.internal.jvm.Jvm
 import org.gradle.jvm.toolchain.internal.InstallationLocation
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.Specification
 import spock.lang.TempDir
 
@@ -65,7 +65,7 @@
         metadata1.is(metadata2)
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "cached probe are not affected by symlink changes"() {
         given:
         NativeServicesTestFixture.initialize()
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/AsdfInstallationSupplierTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/AsdfInstallationSupplierTest.groovy
index cce9b81..a0ee84f 100644
--- a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/AsdfInstallationSupplierTest.groovy
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/AsdfInstallationSupplierTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.api.internal.provider.Providers
 import org.gradle.api.provider.ProviderFactory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -136,7 +136,7 @@
         directories*.source == ["asdf-vm", "asdf-vm", "asdf-vm"]
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "supplies installations with symlinked candidate"() {
         given:
         def otherLocation = temporaryFolder.createDir("other")
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JabbaInstallationSupplierTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JabbaInstallationSupplierTest.groovy
index 05571d9..902de61 100644
--- a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JabbaInstallationSupplierTest.groovy
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JabbaInstallationSupplierTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.api.provider.ProviderFactory
 import org.gradle.test.fixtures.file.CleanupTestDirectory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -93,7 +93,7 @@
 
         then:
         directoriesAsStablePaths(directories) == stablePaths([new File(candidates, "jdk/11.0.6.hs-adpt").absolutePath])
-        directories*.source == ["jabba"]
+        directories*.source == ["Jabba"]
     }
 
     def "supplies multiple installations for multiple paths"() {
@@ -112,10 +112,10 @@
             new File(candidates, "jdk/14").absolutePath,
             new File(candidates, "jdk/8.0.262.fx-librca").absolutePath
         ])
-        directories*.source == ["jabba", "jabba", "jabba"]
+        directories*.source == ["Jabba", "Jabba", "Jabba"]
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "supplies installations with symlinked candidate"() {
         given:
         def otherLocation = temporaryFolder.createDir("other")
@@ -132,7 +132,7 @@
             new File(candidates, "jdk/14-real").absolutePath,
             new File(candidates, "jdk/other-symlinked").absolutePath
         ])
-        directories*.source == ["jabba", "jabba"]
+        directories*.source == ["Jabba", "Jabba"]
     }
 
     def directoriesAsStablePaths(Set<InstallationLocation> actualDirectories) {
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaInstallationRegistryTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaInstallationRegistryTest.groovy
index 19a7e7c..fa4ad28 100644
--- a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaInstallationRegistryTest.groovy
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaInstallationRegistryTest.groovy
@@ -16,9 +16,17 @@
 
 package org.gradle.jvm.toolchain.internal
 
+import org.gradle.api.internal.file.TestFiles
 import org.gradle.api.logging.Logger
+import org.gradle.internal.SystemProperties
+import org.gradle.internal.jvm.inspection.CachingJvmMetadataDetector
+import org.gradle.internal.jvm.inspection.DefaultJvmMetadataDetector
+import org.gradle.internal.jvm.inspection.JavaInstallationRegistry
+import org.gradle.internal.jvm.inspection.JvmMetadataDetector
 import org.gradle.internal.operations.TestBuildOperationExecutor
 import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.progress.NoOpProgressLoggerFactory
+import org.gradle.internal.progress.RecordingProgressLoggerFactory
 import spock.lang.Specification
 
 class JavaInstallationRegistryTest extends Specification {
@@ -93,7 +101,7 @@
         def expectedHome = new File(tempFolder, "Contents/Home")
         createExecutable(expectedHome, OperatingSystem.MAC_OS)
 
-        def registry = new JavaInstallationRegistry([forDirectory(tempFolder)], new TestBuildOperationExecutor(), OperatingSystem.MAC_OS)
+        def registry = new JavaInstallationRegistry([forDirectory(tempFolder)], metadataDetector(), new TestBuildOperationExecutor(), OperatingSystem.MAC_OS, new NoOpProgressLoggerFactory())
 
         when:
         def installations = registry.listInstallations()
@@ -107,7 +115,7 @@
         def expectedHome = new File(tempFolder, "jre")
         createExecutable(expectedHome)
 
-        def registry = new JavaInstallationRegistry([forDirectory(tempFolder)], new TestBuildOperationExecutor(), OperatingSystem.current())
+        def registry = new JavaInstallationRegistry([forDirectory(tempFolder)], metadataDetector(), new TestBuildOperationExecutor(), OperatingSystem.current(), new NoOpProgressLoggerFactory())
 
         when:
         def installations = registry.listInstallations()
@@ -123,7 +131,7 @@
         def expectedHome = new File(rootWithMacOsLayout, "Contents/Home")
         assert expectedHome.mkdirs()
 
-        def registry = new JavaInstallationRegistry([forDirectory(rootWithMacOsLayout)], new TestBuildOperationExecutor(), OperatingSystem.LINUX)
+        def registry = new JavaInstallationRegistry([forDirectory(rootWithMacOsLayout)], metadataDetector(), new TestBuildOperationExecutor(), OperatingSystem.LINUX, new NoOpProgressLoggerFactory())
 
         when:
         def installations = registry.listInstallations()
@@ -135,13 +143,13 @@
     def "detecting installations is tracked as build operation"() {
         def executor = new TestBuildOperationExecutor()
         given:
-        def registry = new JavaInstallationRegistry(Collections.emptyList(), executor, OperatingSystem.current())
+        def registry = new JavaInstallationRegistry(Collections.emptyList(), metadataDetector(), executor, OperatingSystem.current(), new NoOpProgressLoggerFactory())
 
         when:
         registry.listInstallations()
 
         then:
-        executor.log.getDescriptors().find { it.displayName == "Toolchain detection"}
+        executor.log.getDescriptors().find { it.displayName == "Toolchain detection" }
     }
 
     def "warns and filters invalid installations, exists: #exists, directory: #directory"() {
@@ -151,7 +159,7 @@
         file.isDirectory() >> directory
         file.absolutePath >> path
         def logger = Mock(Logger)
-        def registry = JavaInstallationRegistry.withLogger([forDirectory(file)], logger, new TestBuildOperationExecutor())
+        def registry = JavaInstallationRegistry.withLogger([forDirectory(file)], metadataDetector(), logger, new TestBuildOperationExecutor(), new NoOpProgressLoggerFactory())
 
         when:
         def installations = registry.listInstallations()
@@ -170,7 +178,7 @@
         given:
         def logger = Mock(Logger)
         def tempFolder = createTempDir()
-        def registry = JavaInstallationRegistry.withLogger([forDirectory(tempFolder)], logger, new TestBuildOperationExecutor())
+        def registry = JavaInstallationRegistry.withLogger([forDirectory(tempFolder)], metadataDetector(), logger, new TestBuildOperationExecutor(), new NoOpProgressLoggerFactory())
         def logOutput = "Path for java installation {} does not contain a java executable"
 
         when:
@@ -181,8 +189,53 @@
         1 * logger.warn(logOutput, "'" + tempFolder + "' (testSource)")
     }
 
+    def "can detect enclosed jre installations"() {
+        given:
+        createExecutable(tempFolder)
+        def jreHome = new File(tempFolder, "jre")
+        createExecutable(jreHome)
+
+        def registry = newRegistry(jreHome)
+
+        when:
+        def installations = registry.listInstallations()
+
+        then:
+        installations*.location.contains(tempFolder)
+    }
+
+    def "displays progress"() {
+        given:
+        createExecutable(tempFolder)
+        def loggerFactory = new RecordingProgressLoggerFactory()
+
+        def registry = new JavaInstallationRegistry(
+            [forDirectory(tempFolder)],
+            metadataDetector(),
+            new TestBuildOperationExecutor(),
+            OperatingSystem.current(),
+            loggerFactory
+        )
+
+        when:
+        registry.toolchains()
+
+        then:
+        loggerFactory.recordedMessages.find { it.contains("Extracting toolchain metadata from '$tempFolder'") }
+    }
+
     InstallationSupplier forDirectory(File directory) {
-        { it -> Collections.singleton(new InstallationLocation(directory, "testSource")) }
+        return new InstallationSupplier() {
+            @Override
+            String getSourceName() {
+                "testSource"
+            }
+
+            @Override
+            Set<InstallationLocation> get() {
+                return Collections.singleton(new InstallationLocation(directory, getSourceName()))
+            }
+        }
     }
 
     File createTempDir() {
@@ -198,7 +251,20 @@
         executable.createNewFile()
     }
 
+    private JvmMetadataDetector metadataDetector() {
+        def execHandleFactory = TestFiles.execHandleFactory();
+        def temporaryFileProvider = TestFiles.tmpDirTemporaryFileProvider(new File(SystemProperties.getInstance().getJavaIoTmpDir()));
+        def defaultJvmMetadataDetector = new DefaultJvmMetadataDetector(execHandleFactory, temporaryFileProvider)
+        new CachingJvmMetadataDetector(defaultJvmMetadataDetector)
+    }
+
     private JavaInstallationRegistry newRegistry(File... location) {
-        new JavaInstallationRegistry(location.collect { forDirectory(it) }, new TestBuildOperationExecutor(), OperatingSystem.current())
+        new JavaInstallationRegistry(
+            location.collect { forDirectory(it) },
+            metadataDetector(),
+            new TestBuildOperationExecutor(),
+            OperatingSystem.current(),
+            new NoOpProgressLoggerFactory()
+        )
     }
 }
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataComparatorTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataComparatorTest.groovy
new file mode 100644
index 0000000..cb35d63
--- /dev/null
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataComparatorTest.groovy
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2023 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.jvm.toolchain.internal
+
+
+import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
+import org.gradle.internal.jvm.inspection.JvmInstallationMetadataComparator
+import spock.lang.Issue
+import spock.lang.Specification
+
+import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor
+import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.ADOPTOPENJDK
+import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.AMAZON
+import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.BELLSOFT
+import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.ORACLE
+import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.UNKNOWN
+
+/**
+ * Tests {@link JvmInstallationMetadataComparator}.
+ */
+class JvmInstallationMetadataComparatorTest extends Specification {
+
+    def "prefers higher major versions"() {
+        given:
+        def metadata = [
+            jvmMetadata("6.0"),
+            jvmMetadata("8.0"),
+            jvmMetadata("11.0"),
+            jvmMetadata("5.1")
+        ]
+
+        when:
+        metadata.sort(new JvmInstallationMetadataComparator(getJavaHome()))
+
+        then:
+        assertOrder(metadata, "11.0", "8.0", "6.0", "5.1")
+    }
+
+    def "deterministically matches vendor over vendor-specific tool version"() {
+        given:
+        def metadata = [
+            jvmMetadata("8.2", true, AMAZON),
+            jvmMetadata("8.3", true, AMAZON),
+            jvmMetadata("8.1", true, AMAZON),
+            jvmMetadata("8.8", true, BELLSOFT),
+            jvmMetadata("8.7", true, ORACLE),
+            jvmMetadata("8.4", true, UNKNOWN),
+        ]
+
+        when:
+        metadata.sort(new JvmInstallationMetadataComparator(getJavaHome()))
+
+        then:
+        assertOrder(metadata, "8.3", "8.2", "8.1", "8.8", "8.7", "8.4")
+    }
+
+    def "prefers higher minor versions"() {
+        given:
+        def metadata = [
+            jvmMetadata("8.0.1"),
+            jvmMetadata("8.0.123"),
+            jvmMetadata("8.0.1234"),
+        ]
+
+        when:
+        metadata.sort(new JvmInstallationMetadataComparator(getJavaHome()))
+
+        then:
+        assertOrder(metadata, "8.0.1234", "8.0.123", "8.0.1")
+    }
+
+    def "prefers jdk over jre"() {
+        def jdk = jvmMetadata("8.0.1", true)
+        def jre = jvmMetadata("8.0.1", false)
+
+        given:
+        def metadata = [jre, jdk]
+
+        when:
+        metadata.sort(new JvmInstallationMetadataComparator(getJavaHome()))
+
+        then:
+        metadata == [jdk, jre]
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/17195")
+    def "compares installation paths as a last resort"() {
+        given:
+        def prevJdk = jvmMetadata("8.0.1", true, ADOPTOPENJDK, "/jdks/openjdk-8.0.1")
+        def nextJdk = jvmMetadata("8.0.1", true, ADOPTOPENJDK, "/jdks/openjdk-8.0.1.1")
+        def metadata = [prevJdk, nextJdk]
+
+        when:
+        metadata.sort(new JvmInstallationMetadataComparator(getJavaHome()))
+
+        then:
+        metadata == [nextJdk, prevJdk]
+    }
+
+    def "current JVM is picked above all else"() {
+        given:
+        def metadata = [
+            jvmMetadata("8.2", true, AMAZON),
+            jvmMetadata("8.3", true, AMAZON),
+            jvmMetadata("8.1", true, AMAZON,),
+            jvmMetadata("8.8", true, BELLSOFT),
+            jvmMetadata("8.7", true, ORACLE),
+            jvmMetadata("8.0", false, ORACLE),
+            jvmMetadata("8.4", true, UNKNOWN),
+        ]
+
+        when:
+        metadata.sort(new JvmInstallationMetadataComparator(getJavaHome("8.0", false, null)))
+
+        then:
+        assertOrder(metadata, "8.0", "8.3", "8.2", "8.1", "8.8", "8.7", "8.4")
+    }
+
+    static void assertOrder(List<JvmInstallationMetadata> list, String[] expectedOrder) {
+        assert list*.javaVersion as String[] == expectedOrder
+    }
+
+    JvmInstallationMetadata jvmMetadata(String implementationVersion, boolean isJdk = false, KnownJvmVendor jvmVendor = ADOPTOPENJDK, String installPath = null) {
+        return Mock(JvmInstallationMetadata) {
+            getJavaHome() >> getJavaHome(implementationVersion, isJdk, installPath).toPath()
+            hasCapability(JvmInstallationMetadata.JavaInstallationCapability.JAVA_COMPILER) >> isJdk
+            getVendor() >> jvmVendor.asJvmVendor()
+            getJavaVersion() >> implementationVersion
+        }
+    }
+
+    private static File getJavaHome(String implementationVersion = "1.1", boolean isJdk = false, String installPath = null) {
+        return new File(installPath != null ? installPath : ("/" + (isJdk ? "jdk" : "jre") + "s/" + implementationVersion)).absoluteFile
+    }
+}
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/LinuxInstallationSupplierTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/LinuxInstallationSupplierTest.groovy
index 2f4eda5..7bbf5cb 100644
--- a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/LinuxInstallationSupplierTest.groovy
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/LinuxInstallationSupplierTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.test.fixtures.file.CleanupTestDirectory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -133,7 +133,7 @@
         directories*.source == ["Common Linux Locations", "Common Linux Locations"]
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "supplies installations with symlinked candidate"() {
         given:
         def otherLocation = temporaryFolder.createDir("other")
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/LocationListInstallationSupplierTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/LocationListInstallationSupplierTest.groovy
index ccb2815..7b41af2 100644
--- a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/LocationListInstallationSupplierTest.groovy
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/LocationListInstallationSupplierTest.groovy
@@ -54,7 +54,7 @@
 
         then:
         directories*.location == [new File("/foo/bar")]
-        directories*.source == ["system property 'org.gradle.java.installations.paths'"]
+        directories*.source == ["Gradle property 'org.gradle.java.installations.paths'"]
     }
 
     def "supplies multiple installations for multiple paths"() {
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/OsXInstallationSupplierTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/OsXInstallationSupplierTest.groovy
index ea707f3..61cec1c 100644
--- a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/OsXInstallationSupplierTest.groovy
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/OsXInstallationSupplierTest.groovy
@@ -64,7 +64,7 @@
 
         then:
         directories*.location == [new File("/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home")]
-        directories*.source == ["macOS java_home"]
+        directories*.source == ["MacOS java_home"]
     }
 
     def "supplies multiple installations for multiple paths"() {
@@ -86,7 +86,7 @@
             new File("/Library/Java/JavaVirtualMachines/jdk1.7.0_17.jdk/Contents/Home"),
             new File("/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home")
         ])
-        directories*.source == ["macOS java_home", "macOS java_home", "macOS java_home"]
+        directories*.source == ["MacOS java_home", "MacOS java_home", "MacOS java_home"]
     }
 
     def 'supplies no installation for failed command'() {
diff --git a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/SdkmanInstallationSupplierTest.groovy b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/SdkmanInstallationSupplierTest.groovy
index a211ace..14d2b55 100644
--- a/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/SdkmanInstallationSupplierTest.groovy
+++ b/subprojects/jvm-services/src/test/groovy/org/gradle/jvm/toolchain/internal/SdkmanInstallationSupplierTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.internal.SystemProperties
 import org.gradle.test.fixtures.file.CleanupTestDirectory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -141,7 +141,7 @@
         directories.empty
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "supplies installations with symlinked candidate"() {
         given:
         def otherLocation = temporaryFolder.createDir("other")
diff --git a/subprojects/kotlin-dsl-integ-tests/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/plugins/PrecompiledKotlinPluginCrossVersionSpec.groovy b/subprojects/kotlin-dsl-integ-tests/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/plugins/PrecompiledKotlinPluginCrossVersionSpec.groovy
index 25ec840..da79130 100644
--- a/subprojects/kotlin-dsl-integ-tests/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/plugins/PrecompiledKotlinPluginCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-integ-tests/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/plugins/PrecompiledKotlinPluginCrossVersionSpec.groovy
@@ -18,45 +18,74 @@
 
 import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
 import org.gradle.integtests.fixtures.TargetVersions
-import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.integtests.fixtures.executer.GradleDistribution
 import org.gradle.util.GradleVersion
 
 import static org.gradle.integtests.fixtures.RepoScriptBlockUtil.mavenCentralRepository
 import static org.gradle.test.fixtures.dsl.GradleDsl.KOTLIN
+import static org.junit.Assume.assumeFalse
 import static org.junit.Assume.assumeTrue
 
 @TargetVersions("5.0+")
 class PrecompiledKotlinPluginCrossVersionSpec extends CrossVersionIntegrationSpec {
 
-    def "precompiled Kotlin plugin built with Gradle 5.0+ can be used with current Gradle version"() {
+    private static final GradleVersion GRADLE_7_1 = GradleVersion.version("7.1")
+    private static final GradleVersion GRADLE_5_3 = GradleVersion.version("5.3")
+
+    boolean supportsSettingsPluginsBlock
+    boolean supportsProjectAccessors
+
+    def setup() {
+        supportsSettingsPluginsBlock = previous.version >= GRADLE_7_1 && current.version >= GRADLE_7_1
+        supportsProjectAccessors = previous.version >= GRADLE_5_3 && current.version >= GRADLE_5_3
+    }
+
+    def "precompiled Kotlin plugins can be used with current Gradle version when built with Gradle 5.0+"() {
         assumeTrue(previous.version >= GradleVersion.version('5.0'))
 
+        // See https://github.com/gradle/gradle/issues/24754
+        assumeFalse("broken", previous.version == GradleVersion.version("8.1"))
+
         given:
-        precompiledKotlinPluginBuiltWith(version(previous))
+        precompiledKotlinPluginsBuiltWith(previous)
 
         when:
-        def result = pluginAppliedWith(version(current)).run()
+        def result = pluginsAppliedWith(current)
 
         then:
-        result.assertOutputContains("My plugin applied!")
+        result.assertOutputContains("My gradle plugin applied!")
+        result.assertOutputContains("My settings plugin applied!")
+        result.assertOutputContains("settings pluginManagement {}")
+        if (supportsSettingsPluginsBlock) {
+            result.assertOutputContains("settings plugins {}")
+        }
+        result.assertOutputContains("My project plugin applied!")
         result.assertOutputContains("My task executed!")
     }
 
-    def "precompiled Kotlin plugin built with current version can be used with Gradle 6.0+"() {
+    def "precompiled Kotlin plugins built with current Gradle version can be used with Gradle 6.0+"() {
         assumeTrue(previous.version >= GradleVersion.version('6.0'))
 
         given:
-        precompiledKotlinPluginBuiltWith(version(current))
+        precompiledKotlinPluginsBuiltWith(current)
 
         when:
-        def result = pluginAppliedWith(version(previous)).run()
+        def result = pluginsAppliedWith(previous)
 
         then:
-        result.assertOutputContains("My plugin applied!")
+        result.assertOutputContains("My gradle plugin applied!")
+        result.assertOutputContains("My settings plugin applied!")
+        result.assertOutputContains("settings pluginManagement {}")
+        if (supportsSettingsPluginsBlock) {
+            result.assertOutputContains("settings plugins {}")
+        }
+        result.assertOutputContains("My project plugin applied!")
         result.assertOutputContains("My task executed!")
     }
 
-    private void precompiledKotlinPluginBuiltWith(GradleExecuter executer) {
+    private void precompiledKotlinPluginsBuiltWith(GradleDistribution distribution) {
+
         file("plugin/settings.gradle.kts").text = ""
         file("plugin/build.gradle.kts").text = """
             plugins {
@@ -72,30 +101,86 @@
                 }
             }
         """
-        file("plugin/src/main/kotlin/my-plugin.gradle.kts").text = """
-            tasks.register("myTask") {
+        file("plugin/src/main/kotlin/my-gradle-plugin.init.gradle.kts").text = """
+            println("My gradle plugin applied!")
+        """
+        file("plugin/src/main/kotlin/my-settings-plugin.settings.gradle.kts").text = """
+            pluginManagement {
+                println("settings pluginManagement {}")
+            }
+            ${supportsSettingsPluginsBlock ? """
+            plugins {
+                println("settings plugins {}")
+            }
+            """ : ""}
+            println("My settings plugin applied!")
+        """
+        file("plugin/src/main/kotlin/my-project-plugin.gradle.kts").text = """
+            plugins {
+                id("base")
+            }
+            println("My project plugin applied!")
+            val myTask = tasks.register("myTask") {
                 doLast {
                     println("My task executed!")
                 }
             }
-            println("My plugin applied!")
+            ${supportsProjectAccessors ? """
+            tasks.assemble {
+                dependsOn(myTask)
+            }
+            """ : ""}
         """
-        executer.inDirectory(file("plugin")).withTasks("publish").run()
+        version(distribution)
+            .inDirectory(file("plugin"))
+            .withTasks("publish")
+            .run()
     }
 
-    private GradleExecuter pluginAppliedWith(GradleExecuter executer) {
+    private ExecutionResult pluginsAppliedWith(GradleDistribution distribution) {
+        file("consumer/init.gradle.kts").text = """
+            initscript {
+                repositories {
+                    maven(url = "${mavenRepo.uri}")
+                }
+                dependencies {
+                    classpath("com.example:plugin:1.0")
+                }
+            }
+            apply<MyGradlePluginPlugin>()
+        """
         file("consumer/settings.gradle.kts").text = """
             pluginManagement {
                 repositories {
                     maven(url = "${mavenRepo.uri}")
                 }
             }
+            ${supportsSettingsPluginsBlock ? """
+            plugins {
+                id("my-settings-plugin") version "1.0"
+            }
+            """ : """
+            buildscript {
+                repositories {
+                    maven(url = "${mavenRepo.uri}")
+                }
+                dependencies {
+                    classpath("com.example:plugin:1.0")
+                }
+            }
+            apply<MySettingsPluginPlugin>()
+            """}
         """
         file("consumer/build.gradle.kts").text = """
             plugins {
-                id("my-plugin") version "1.0"
+                id("my-project-plugin")
             }
         """
-        return executer.inDirectory(file("consumer")).withTasks("myTask")
+        return version(distribution)
+            .inDirectory(file("consumer"))
+            .withArgument("-I")
+            .withArgument("init.gradle.kts")
+            .withTasks("myTask")
+            .run()
     }
 }
diff --git a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/DependencyManagementIntegrationTest.kt b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/DependencyManagementIntegrationTest.kt
index d859c35..a29b67d 100644
--- a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/DependencyManagementIntegrationTest.kt
+++ b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/DependencyManagementIntegrationTest.kt
@@ -102,7 +102,7 @@
                 output,
                 containsMultiLineString(
                     """
-                api - API dependencies for source set 'main'. (n)
+                api - API dependencies for the 'main' feature. (n)
                 +--- in-block:accessor (n)
                 +--- in-block:accessor-with-action (n)
                 +--- in-block:string-invoke (n)
diff --git a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/GradleKotlinDslIntegrationTest.kt b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/GradleKotlinDslIntegrationTest.kt
index 1a7282f..805dacd 100644
--- a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/GradleKotlinDslIntegrationTest.kt
+++ b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/GradleKotlinDslIntegrationTest.kt
@@ -241,11 +241,27 @@
         )
 
         executer.expectDocumentedDeprecationWarning(
+            "The org.gradle.api.plugins.JavaPluginConvention type has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#java_convention_deprecation")
+        executer.expectDocumentedDeprecationWarning(
             "The org.gradle.util.WrapUtil type has been deprecated. " +
                 "This is scheduled to be removed in Gradle 9.0. " +
                 "Consult the upgrading guide for further information: " +
                 "https://docs.gradle.org/current/userguide/upgrading_version_7.html#org_gradle_util_reports_deprecations"
         )
+        executer.expectDocumentedDeprecationWarning(
+            "The Project.getConvention() method has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: " +
+                "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+        )
+        executer.expectDocumentedDeprecationWarning(
+            "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: " +
+                "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+        )
 
         assertThat(
             build("print-kotlin-version").output,
diff --git a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/GradleKotlinDslRegressionsTest.kt b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/GradleKotlinDslRegressionsTest.kt
index bad4ce0..a778222 100644
--- a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/GradleKotlinDslRegressionsTest.kt
+++ b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/GradleKotlinDslRegressionsTest.kt
@@ -190,6 +190,7 @@
         }
     }
 
+    @Test
     @Issue("https://youtrack.jetbrains.com/issue/KT-55880")
     @Issue("https://github.com/gradle/gradle/issues/23491")
     fun `compiling standalone scripts does not emit a warning at info level`() {
@@ -198,4 +199,29 @@
             assertNotOutput("is not supposed to be used along with regular Kotlin sources, and will be ignored in the future versions by default. (Use -Xallow-any-scripts-in-source-roots command line option to opt-in for the old behavior.)")
         }
     }
+
+    @Test
+    @Issue("https://github.com/gradle/gradle/issues/24481")
+    fun `applied project scripts don't have project accessors`() {
+        withFile("applied.gradle.kts", """
+            println(java.sourceCompatibility)
+        """)
+        withBuildScript("""
+            plugins { java }
+            apply(from = "applied.gradle.kts")
+        """)
+        buildAndFail("help").apply {
+            assertHasErrorOutput("Unresolved reference: sourceCompatibility")
+        }
+
+        withFile("applied.gradle.kts", """
+            buildscript {
+                dependencies {}
+            }
+            println(java.sourceCompatibility)
+        """)
+        buildAndFail("help").apply {
+            assertHasErrorOutput("Unresolved reference: sourceCompatibility")
+        }
+    }
 }
diff --git a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinDslAssignmentIntegrationTest.kt b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinDslAssignmentIntegrationTest.kt
index 48c5574..2c56d4f 100644
--- a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinDslAssignmentIntegrationTest.kt
+++ b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinDslAssignmentIntegrationTest.kt
@@ -29,8 +29,7 @@
 class KotlinDslAssignmentIntegrationTest : AbstractKotlinIntegrationTest() {
 
     @Test
-    fun `can use assignment for properties in init scripts when assignment overload is enabled`() {
-        withAssignmentOverload()
+    fun `can use assignment for properties in init scripts`() {
         val initScript = withInitScriptWithAssignment()
 
         // Expect
@@ -41,8 +40,7 @@
     }
 
     @Test
-    fun `can use assignment for properties in settings scripts when assignment overload is enabled`() {
-        withAssignmentOverload()
+    fun `can use assignment for properties in settings scripts`() {
         withSettingsWithAssignment()
 
         // Expect
@@ -53,9 +51,8 @@
     }
 
     @Test
-    fun `can use assignment for properties in build scripts when assignment overload is enabled`() {
+    fun `can use assignment for properties in build scripts`() {
         // Given
-        withAssignmentOverload()
         val outputFile = withBuildScriptWithAssignment()
 
         // When
@@ -71,8 +68,9 @@
     }
 
     @Test
-    fun `cannot use assignment for properties in init scripts when assignment overload is not enabled`() {
+    fun `cannot use assignment for properties in init scripts when assignment overload is disabled`() {
         // Given
+        withAssignmentOverload(disabled = true)
         val initScript = withInitScriptWithAssignment()
 
         // When
@@ -83,8 +81,9 @@
     }
 
     @Test
-    fun `cannot use assignment for properties in settings scripts when assignment overload is not enabled`() {
+    fun `cannot use assignment for properties in settings scripts when assignment overload is disabled`() {
         // Given
+        withAssignmentOverload(disabled = true)
         withSettingsWithAssignment()
 
         // When
@@ -95,8 +94,9 @@
     }
 
     @Test
-    fun `cannot use assignment for properties in build scripts when assignment overload is not enabled`() {
+    fun `cannot use assignment for properties in build scripts when assignment overload is disabled`() {
         // Given
+        withAssignmentOverload(disabled = true)
         withBuildScriptWithAssignment()
 
         // When
@@ -111,14 +111,11 @@
         // Given
         val initScript = withInitScriptWithAssignment()
 
-        // When
-        withAssignmentOverload()
-
         // Then
         build("-I", initScript.absolutePath)
 
         // When
-        withAssignmentOverload(enabled = false)
+        withAssignmentOverload(disabled = true)
         val failure = buildAndFail("-I", initScript.absolutePath)
 
         // Then
@@ -136,14 +133,11 @@
         // Given
         withSettingsWithAssignment()
 
-        // When
-        withAssignmentOverload()
-
-        // Then
+        // When, Then
         build()
 
         // When
-        withAssignmentOverload(enabled = false)
+        withAssignmentOverload(disabled = true)
         val failure = buildAndFail()
 
         // Then
@@ -161,14 +155,11 @@
         // Given
         withBuildScriptWithAssignment()
 
-        // When
-        withAssignmentOverload()
-
-        // Then
+        // When, Then
         build("myTask")
 
         // When
-        withAssignmentOverload(enabled = false)
+        withAssignmentOverload(disabled = true)
         val failure = buildAndFail("myTask")
 
         // Then
@@ -184,7 +175,6 @@
     @Test
     fun `assign operator compiles with all possible Property types`() {
         // Given
-        withAssignmentOverload()
         withBuildScript("""
             abstract class MyTask : DefaultTask() {
                 @get:Input
@@ -252,6 +242,61 @@
         build("myTask")
     }
 
+    @Test
+    fun `doesn't report assignment is incubating feature when used by third party plugin`() {
+        // Given
+        val pluginDir = File(projectRoot, "plugin")
+        File(pluginDir, "src/main/kotlin").mkdirs()
+        File(pluginDir, "build.gradle.kts").writeText("""
+            plugins {
+                `kotlin-dsl`
+            }
+            repositories {
+                mavenCentral()
+            }
+            """.trimIndent()
+        )
+        File(pluginDir, "settings.gradle.kts").writeText("rootProject.name = \"plugin\"")
+        File(pluginDir, "src/main/kotlin/plugin-with-assignment.gradle.kts").writeText("""
+            abstract class MyTask : DefaultTask() {
+                @get:Input
+                abstract val input: Property<String>
+                @get:OutputFile
+                abstract val output: RegularFileProperty
+
+                @TaskAction
+                fun taskAction() {
+                    output.asFile.get().writeText(input.get())
+                }
+            }
+            tasks.register<MyTask>("myTask") {
+                input = "Hello world"
+                output = file("build/myTask/hello.txt")
+            }
+            """.trimIndent()
+        )
+        executer.inDirectory(pluginDir).withTasks("assemble").run()
+
+        // When
+        withAssignmentOverload(disabled = true)
+        withBuildScript("""
+            buildscript {
+                dependencies { classpath(fileTree(mapOf("dir" to "plugin/build/libs", "include" to "*.jar"))) }
+            }
+            apply(plugin = "plugin-with-assignment")
+            """.trimIndent()
+        )
+        val result = build("myTask")
+
+        // Then
+        assertEquals(
+            "File 'build/myTask/hello.txt' content",
+            "Hello world",
+            File(projectRoot, "build/myTask/hello.txt").readText()
+        )
+        result.assertNotOutput("Kotlin DSL property assignment is an incubating feature.")
+    }
+
     private
     fun withBuildScriptWithAssignment(): File {
         val outputFilePath = "${projectRoot.absolutePath.replace("\\", "/")}/build/myTask/hello-world.txt"
@@ -308,7 +353,7 @@
     }
 
     private
-    fun withAssignmentOverload(enabled: Boolean = true) {
-        withFile("gradle.properties", "systemProp.$ASSIGNMENT_SYSTEM_PROPERTY=$enabled")
+    fun withAssignmentOverload(disabled: Boolean = false) {
+        withFile("gradle.properties", "systemProp.$ASSIGNMENT_SYSTEM_PROPERTY=${!disabled}")
     }
 }
diff --git a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinDslJvmDefaultIntegrationTest.kt b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinDslJvmDefaultIntegrationTest.kt
index b9d2c41..8b89586 100644
--- a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinDslJvmDefaultIntegrationTest.kt
+++ b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinDslJvmDefaultIntegrationTest.kt
@@ -99,7 +99,7 @@
     @Test
     fun `kotlin-dsl java and groovy consumers can use kotlin interface default methods directly`() {
 
-        withSettings(
+        file("settings.gradle.kts").appendText(
             """
             include("kotlin-dsl-producer")
             include("java-consumer")
diff --git a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/PrecompiledScriptPluginIntegrationTest.kt b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/PrecompiledScriptPluginIntegrationTest.kt
index 2e4f86b..c2dd8bc 100644
--- a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/PrecompiledScriptPluginIntegrationTest.kt
+++ b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/PrecompiledScriptPluginIntegrationTest.kt
@@ -4,6 +4,7 @@
 import org.gradle.api.DefaultTask
 import org.gradle.api.Plugin
 import org.gradle.api.Project
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.tasks.TaskAction
 import org.gradle.integtests.fixtures.RepoScriptBlockUtil
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
@@ -12,7 +13,6 @@
 import org.gradle.test.fixtures.dsl.GradleDsl
 import org.gradle.test.fixtures.file.LeaksFileHandles
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
-import org.gradle.util.GradleVersion
 import org.gradle.util.internal.TextUtil.normaliseFileSeparators
 import org.gradle.util.internal.ToBeImplemented
 import org.hamcrest.CoreMatchers.containsString
@@ -65,6 +65,11 @@
             """.trimIndent()
         )
 
+        // From ktlint
+        executer.beforeExecute {
+            it.expectDocumentedDeprecationWarning("The Project.getConvention() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+        }
+
         build("generateScriptPluginAdapters")
 
         build("ktlintCheck", "-x", "ktlintKotlinScriptCheck")
@@ -750,12 +755,9 @@
             """
         )
 
-        val error = buildAndFail("help")
-
-        error.assertHasCause(
-            "The precompiled plugin (${"src/main/kotlin/java.gradle.kts".replace("/", File.separator)}) conflicts with the core plugin 'java'. Rename your plugin.\n\n"
-                + "See https://docs.gradle.org/${GradleVersion.current().version}/userguide/custom_plugins.html#sec:precompiled_plugins for more details."
-        )
+        buildAndFail("help")
+            .assertHasCause("The precompiled plugin (${"src/main/kotlin/java.gradle.kts".replace("/", File.separator)}) conflicts with the core plugin 'java'. Rename your plugin.")
+            .assertHasResolution(getPrecompiledPluginsLink())
     }
 
     @Test
@@ -769,12 +771,9 @@
         )
         withDefaultSettings()
 
-        val error = buildAndFail("help")
-
-        error.assertHasCause(
-            "The precompiled plugin (${"src/main/kotlin/org.gradle.my-plugin.gradle.kts".replace("/", File.separator)}) cannot start with 'org.gradle' or be in the 'org.gradle' package.\n\n"
-                + "See https://docs.gradle.org/${GradleVersion.current().version}/userguide/custom_plugins.html#sec:precompiled_plugins for more details."
-        )
+        buildAndFail("help")
+            .assertHasCause("The precompiled plugin (${"src/main/kotlin/org.gradle.my-plugin.gradle.kts".replace("/", File.separator)}) cannot start with 'org.gradle' or be in the 'org.gradle' package.")
+            .assertHasResolution(getPrecompiledPluginsLink())
     }
 
     @Test
@@ -790,14 +789,14 @@
         )
         withDefaultSettings()
 
-        val error = buildAndFail("help")
-
-        error.assertHasCause(
-            "The precompiled plugin (${"src/main/kotlin/org/gradle/my-plugin.gradle.kts".replace("/", File.separator)}) cannot start with 'org.gradle' or be in the 'org.gradle' package.\n\n"
-                + "See https://docs.gradle.org/${GradleVersion.current().version}/userguide/custom_plugins.html#sec:precompiled_plugins for more details."
-        )
+        buildAndFail("help")
+            .assertHasCause("The precompiled plugin (${"src/main/kotlin/org/gradle/my-plugin.gradle.kts".replace("/", File.separator)}) cannot start with 'org.gradle' or be in the 'org.gradle' package.")
+            .assertHasResolution(getPrecompiledPluginsLink())
     }
 
+    private
+    fun getPrecompiledPluginsLink(): String = DocumentationRegistry().getDocumentationRecommendationFor("information", "custom_plugins", "sec:precompiled_plugins")
+
     @Test
     fun `should compile correctly with Kotlin explicit api mode`() {
         assumeNonEmbeddedGradleExecuter()
@@ -825,23 +824,6 @@
     }
 
     @Test
-    @Issue("https://github.com/gradle/gradle/issues/22091")
-    fun `does not add extra task actions to kotlin compilation task`() {
-        assumeNonEmbeddedGradleExecuter()
-        withKotlinDslPlugin().appendText("""
-            gradle.taskGraph.whenReady {
-                val compileKotlinActions = allTasks.single { it.path == ":compileKotlin" }.actions.size
-                require(compileKotlinActions == 1) {
-                    ":compileKotlin has ${'$'}compileKotlinActions actions, expected 1"
-                }
-            }
-        """)
-        withPrecompiledKotlinScript("my-plugin.gradle.kts", "")
-
-        compileKotlin()
-    }
-
-    @Test
     @Issue("https://github.com/gradle/gradle/issues/23576")
     @ToBeImplemented
     fun `can compile precompiled scripts with compileOnly dependency`() {
@@ -858,6 +840,7 @@
         val pluginJarV1 = withPluginJar("my-plugin-1.0.jar", "1.0")
         val pluginJarV2 = withPluginJar("my-plugin-2.0.jar", "2.0")
 
+        withDefaultSettingsIn("buildSrc")
         withBuildScriptIn("buildSrc", """
             plugins {
                 `kotlin-dsl`
@@ -902,7 +885,7 @@
     @Issue("https://github.com/gradle/gradle/issues/23564")
     fun `respects offline start parameter on synthetic builds for accessors generation`() {
 
-        withSettings("""include("producer", "consumer")""")
+        file("settings.gradle.kts").appendText("""include("producer", "consumer")""")
 
         withKotlinDslPluginIn("producer")
         withFile("producer/src/main/kotlin/offline.gradle.kts", """
@@ -1111,6 +1094,47 @@
             server.stop()
         }
     }
+
+    @Test
+    fun `no warnings on absent directories in compilation classpath`() {
+        withDefaultSettings().appendText("""include("producer", "consumer")""")
+        withFile("producer/build.gradle.kts", """plugins { java }""")
+        withKotlinDslPluginIn("consumer").appendText("""dependencies { implementation(project(":producer")) }""")
+        withFile("consumer/src/main/kotlin/some.gradle.kts", "")
+        build(":consumer:classes").apply {
+            assertTaskExecuted(":consumer:compilePluginsBlocks")
+            assertNotOutput("w: Classpath entry points to a non-existent location")
+        }
+        assertFalse(file("producer/build/classes/java/main").exists())
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/24788")
+    @Test
+    fun `fail with a reasonable message when kotlin-dsl plugin compiler arguments have been tempered with`() {
+        assumeNonEmbeddedGradleExecuter()
+
+        withKotlinDslPlugin().appendText(
+            """
+            tasks.compileKotlin {
+                compilerOptions {
+                    freeCompilerArgs.set(listOf("some"))
+                }
+            }
+            """
+        )
+        withPrecompiledKotlinScript("some.gradle.kts", "")
+
+        buildAndFail("compileKotlin").apply {
+            assertHasFailure("Execution failed for task ':compileKotlin'.") {
+                assertHasCause(
+                    "Kotlin compiler arguments of task ':compileKotlin' do not work for the `kotlin-dsl` plugin. " +
+                        "The 'freeCompilerArgs' property has been reassigned. " +
+                        "It must instead be appended to. " +
+                        "Please use 'freeCompilerArgs.addAll(\"your\", \"args\")' to fix this."
+                )
+            }
+        }
+    }
 }
 
 
diff --git a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/ProjectSchemaAccessorsIntegrationTest.kt b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/ProjectSchemaAccessorsIntegrationTest.kt
index 8ed7937..588f506a 100644
--- a/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/ProjectSchemaAccessorsIntegrationTest.kt
+++ b/subprojects/kotlin-dsl-integ-tests/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/ProjectSchemaAccessorsIntegrationTest.kt
@@ -1019,6 +1019,9 @@
             """
         )
 
+        executer.expectDocumentedDeprecationWarning("The Project.getConvention() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+        executer.expectDocumentedDeprecationWarning("The org.gradle.api.plugins.Convention type has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+
         build("help")
     }
 
@@ -1089,6 +1092,11 @@
             """
         )
 
+        executer.beforeExecute {
+            it.expectDocumentedDeprecationWarning("The Project.getConvention() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+            it.expectDocumentedDeprecationWarning("The org.gradle.api.plugins.Convention type has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+        }
+
         assertThat(
             build("help").output,
             containsString("Type of `myConvention` receiver is Any")
diff --git a/subprojects/kotlin-dsl-plugins/build.gradle.kts b/subprojects/kotlin-dsl-plugins/build.gradle.kts
index 0a20d864..3a15836 100644
--- a/subprojects/kotlin-dsl-plugins/build.gradle.kts
+++ b/subprojects/kotlin-dsl-plugins/build.gradle.kts
@@ -6,7 +6,7 @@
 description = "Kotlin DSL Gradle Plugins deployed to the Plugin Portal"
 
 group = "org.gradle.kotlin"
-version = "4.0.8"
+version = "4.0.15"
 
 base.archivesName = "plugins"
 
diff --git a/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginCrossVersionSmokeTest.kt b/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginCrossVersionSmokeTest.kt
index c51a578..b09b1ce 100644
--- a/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginCrossVersionSmokeTest.kt
+++ b/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginCrossVersionSmokeTest.kt
@@ -50,6 +50,13 @@
         withBuildScript("""plugins { id("some") }""")
 
         executer.expectDocumentedDeprecationWarning("Using the `kotlin-dsl` plugin together with Kotlin Gradle Plugin < 1.8.0. This behavior has been deprecated. This will fail with an error in Gradle 9.0. Please let Gradle control the version of `kotlin-dsl` by removing any explicit `kotlin-dsl` version constraints from your build logic. Or use version $blessedVersion which is the expected version for this Gradle release. If you explicitly declare which version of the Kotlin Gradle Plugin to use for your build logic, update it to >= 1.8.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#kotlin_dsl_with_kgp_lt_1_8_0")
+        executer.expectDocumentedDeprecationWarning("The Project.getConvention() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+        executer.expectDocumentedDeprecationWarning(
+            "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: " +
+                "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+        )
 
         build("help").apply {
 
diff --git a/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginGradlePluginCrossVersionSmokeTest.kt b/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginGradlePluginCrossVersionSmokeTest.kt
index 6fd3779..bfd57c3 100644
--- a/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginGradlePluginCrossVersionSmokeTest.kt
+++ b/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginGradlePluginCrossVersionSmokeTest.kt
@@ -17,6 +17,7 @@
 package org.gradle.kotlin.dsl.plugins.dsl
 
 import org.gradle.integtests.fixtures.RepoScriptBlockUtil
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.kotlin.dsl.*
 import org.gradle.kotlin.dsl.fixtures.AbstractPluginTest
 import org.gradle.test.fixtures.dsl.GradleDsl
@@ -103,6 +104,13 @@
             executer.expectDocumentedDeprecationWarning("The AbstractCompile.destinationDir property has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the destinationDirectory property instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_7.html#compile_task_wiring")
         }
 
+        if (VersionNumber.parse(kotlinVersion) < VersionNumber.parse("1.7.0") && GradleContextualExecuter.isConfigCache()) {
+            executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.BasePluginConvention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#base_convention_deprecation")
+        }
         if (VersionNumber.parse(kotlinVersion) < VersionNumber.parse("1.7.20")) {
             executer.expectDocumentedDeprecationWarning(
                 "The org.gradle.util.WrapUtil type has been deprecated. " +
@@ -110,6 +118,23 @@
                     "Consult the upgrading guide for further information: " +
                     "https://docs.gradle.org/current/userguide/upgrading_version_7.html#org_gradle_util_reports_deprecations"
             )
+            executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.JavaPluginConvention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#java_convention_deprecation")
+            executer.expectDocumentedDeprecationWarning(
+                "The Project.getConvention() method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+            )
+            executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions"
+            )
         }
 
         build("classes").apply {
diff --git a/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginTest.kt b/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginTest.kt
index 4e1a375..50b09a6 100644
--- a/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginTest.kt
+++ b/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginTest.kt
@@ -10,11 +10,21 @@
 import org.hamcrest.CoreMatchers.not
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
+import spock.lang.Issue
 
 
 @LeaksFileHandles("Kotlin Compiler Daemon working directory")
 class KotlinDslPluginTest : AbstractPluginTest() {
 
+    @Issue("https://github.com/gradle/gradle/issues/24815")
+    @Test
+    fun `disables kotlin compilation avoidance`() {
+        withKotlinDslPlugin().appendText("""
+            require(property("kotlin.incremental.useClasspathSnapshot") == "false")
+        """)
+        build("help")
+    }
+
     @Test
     fun `warns on unexpected kotlin-dsl plugin version`() {
 
@@ -285,7 +295,7 @@
     }
 
     @Test
-    fun `kotlin assignment compiler plugin is not applied to production code by default`() {
+    fun `kotlin assignment compiler plugin is applied to production code by default`() {
         withKotlinDslPlugin()
         withFile(
             "src/main/kotlin/code.kt",
@@ -308,6 +318,36 @@
             """
         )
 
+        val result = build("classes")
+
+        result.assertTaskExecuted(":compileKotlin")
+    }
+
+    @Test
+    fun `kotlin assignment compiler plugin is not applied to production code with opt-out`() {
+        withKotlinDslPlugin()
+        withFile("gradle.properties", "systemProp.${KotlinDslAssignment.ASSIGNMENT_SYSTEM_PROPERTY}=false")
+        withFile(
+            "src/main/kotlin/code.kt",
+            """
+
+            import org.gradle.api.Plugin
+            import org.gradle.api.Project
+            import org.gradle.api.provider.Property
+            import org.gradle.kotlin.dsl.assign
+
+            data class MyType(val property: Property<String>)
+
+            class MyPlugin : Plugin<Project> {
+                override fun apply(project: Project) {
+                    val myType = MyType(property = project.objects.property(String::class.java))
+                    myType.property = "value"
+                }
+            }
+
+            """
+        )
+
         buildAndFail("classes").apply {
             assertHasCause("Compilation error. See log for more details")
             assertHasErrorOutput("code.kt:13:21 Val cannot be reassigned")
@@ -315,36 +355,6 @@
         }
     }
 
-    @Test
-    fun `kotlin assignment compiler plugin is applied to production code with opt-in`() {
-        withKotlinDslPlugin()
-        withFile("gradle.properties", "systemProp.${KotlinDslAssignment.ASSIGNMENT_SYSTEM_PROPERTY}=true")
-        withFile(
-            "src/main/kotlin/code.kt",
-            """
-
-            import org.gradle.api.Plugin
-            import org.gradle.api.Project
-            import org.gradle.api.provider.Property
-            import org.gradle.kotlin.dsl.assign
-
-            data class MyType(val property: Property<String>)
-
-            class MyPlugin : Plugin<Project> {
-                override fun apply(project: Project) {
-                    val myType = MyType(property = project.objects.property(String::class.java))
-                    myType.property = "value"
-                }
-            }
-
-            """
-        )
-
-        val result = build("classes")
-
-        result.assertTaskExecuted(":compileKotlin")
-    }
-
     private
     fun withBuildExercisingSamConversionForKotlinFunctions(buildSrcScript: String = "") {
 
diff --git a/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPluginTemplatesTest.kt b/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPluginTemplatesTest.kt
index 02b8a82..2261921 100644
--- a/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPluginTemplatesTest.kt
+++ b/subprojects/kotlin-dsl-plugins/src/integTest/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPluginTemplatesTest.kt
@@ -16,7 +16,6 @@
 import org.gradle.api.Task
 import org.gradle.api.initialization.Settings
 import org.gradle.api.invocation.Gradle
-import org.gradle.api.plugins.Convention
 import org.gradle.api.plugins.ObjectConfigurationAction
 import org.gradle.api.tasks.TaskContainer
 import org.gradle.api.tasks.bundling.Jar
@@ -623,7 +622,7 @@
             """
         )
 
-        val convention = mock<Convention>()
+        val convention = mock<org.gradle.api.plugins.Convention>()
         val receiver = mock<org.gradle.api.internal.HasConvention>(extraInterfaces = arrayOf(T::class)) {
             on { getConvention() } doReturn convention
         }
diff --git a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/base/KotlinDslBasePlugin.kt b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/base/KotlinDslBasePlugin.kt
index 2430251..c9a87c46 100644
--- a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/base/KotlinDslBasePlugin.kt
+++ b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/base/KotlinDslBasePlugin.kt
@@ -18,14 +18,12 @@
 
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-
+import org.gradle.kotlin.dsl.*
 import org.gradle.kotlin.dsl.plugins.dsl.KotlinDslCompilerPlugins
 import org.gradle.kotlin.dsl.plugins.dsl.KotlinDslPluginOptions
 import org.gradle.kotlin.dsl.plugins.embedded.EmbeddedKotlinPlugin
 import org.gradle.kotlin.dsl.plugins.embedded.kotlinArtifactConfigurationNames
 
-import org.gradle.kotlin.dsl.*
-
 
 /**
  * The `kotlin-dsl-base` plugin.
@@ -43,6 +41,8 @@
 abstract class KotlinDslBasePlugin : Plugin<Project> {
 
     override fun apply(project: Project): Unit = project.run {
+        disableKotlinCompilationAvoidance()
+
         apply<EmbeddedKotlinPlugin>()
 
         extensions.create("kotlinDslPluginOptions", KotlinDslPluginOptions::class.java)
@@ -56,4 +56,9 @@
         configurations.forEach {
             dependencies.add(it, gradleKotlinDsl())
         }
+
+    private
+    fun Project.disableKotlinCompilationAvoidance() {
+        extra["kotlin.incremental.useClasspathSnapshot"] = "false"
+    }
 }
diff --git a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslCompilerPlugins.kt b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslCompilerPlugins.kt
index 75d1416..a4dd587 100644
--- a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslCompilerPlugins.kt
+++ b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslCompilerPlugins.kt
@@ -70,10 +70,6 @@
                 kotlinCompile.setWarningRewriter(ExperimentalCompilerWarningSilencer(listOf(
                     "-XXLanguage:+DisableCompatibilityModeForNewInference",
                     "-XXLanguage:-TypeEnhancementImprovementsInStrictMode",
-                    "Assign Kotlin compiler plugin is an experimental feature",
-                    // Kotlin 1.8.0 has wrong warning message for assign plugin, remove once we update Kotlin to 1.8.20.
-                    // https://github.com/JetBrains/kotlin/commit/0eb34983cb38064684cfc76dacb8d4460fcc6573
-                    "Lombok Kotlin compiler plugin is an experimental feature",
                 )))
             }
         }
diff --git a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPlugin.kt b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPlugin.kt
index c1b513b..9524901 100644
--- a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPlugin.kt
+++ b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPlugin.kt
@@ -17,15 +17,11 @@
 
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-
 import org.gradle.kotlin.dsl.*
-
 import org.gradle.kotlin.dsl.plugins.appliedKotlinDslPluginsVersion
 import org.gradle.kotlin.dsl.plugins.base.KotlinDslBasePlugin
 import org.gradle.kotlin.dsl.plugins.precompiled.PrecompiledScriptPlugins
-
 import org.gradle.kotlin.dsl.support.expectedKotlinDslPluginsVersion
-
 import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin
 
 
diff --git a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginOptions.kt b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginOptions.kt
index 5b35984..86ec4f7 100644
--- a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginOptions.kt
+++ b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/dsl/KotlinDslPluginOptions.kt
@@ -20,7 +20,6 @@
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.provider.Property
 import org.gradle.internal.deprecation.DeprecationLogger
-
 import org.gradle.kotlin.dsl.*
 
 
@@ -62,4 +61,4 @@
 
 internal
 fun Project.kotlinDslPluginOptions(action: KotlinDslPluginOptions.() -> Unit) =
-    configure(action)
+    extensions.getByType<KotlinDslPluginOptions>().apply(action)
diff --git a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt
index d8e43bd..edd1cb8 100644
--- a/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt
+++ b/subprojects/kotlin-dsl-plugins/src/main/kotlin/org/gradle/kotlin/dsl/plugins/precompiled/PrecompiledScriptPlugins.kt
@@ -31,8 +31,6 @@
 import org.gradle.kotlin.dsl.provider.gradleKotlinDslJarsOf
 import org.gradle.kotlin.dsl.support.serviceOf
 
-import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
-
 
 /**
  * Exposes `*.gradle.kts` scripts from regular Kotlin source-sets as binary Gradle plugins.
@@ -58,7 +56,7 @@
         override val jvmTarget: Provider<JavaVersion> =
             DeprecationLogger.whileDisabled(Factory {
                 @Suppress("DEPRECATION")
-                project.the<KotlinDslPluginOptions>().jvmTarget.map { JavaVersion.toVersion(it) }
+                project.kotlinDslPluginOptions.jvmTarget.map { JavaVersion.toVersion(it) }
             })!!
 
         override val kotlinSourceDirectorySet: SourceDirectorySet
@@ -67,11 +65,15 @@
 }
 
 
+val Project.kotlinDslPluginOptions: KotlinDslPluginOptions
+    get() = extensions.getByType()
+
+
 private
-val Project.sourceSets
-    get() = project.the<SourceSetContainer>()
+val Project.sourceSets: SourceSetContainer
+    get() = extensions.getByType()
 
 
 private
 val SourceSet.kotlin: SourceDirectorySet
-    get() = @Suppress("deprecation") withConvention(KotlinSourceSet::class) { kotlin }
+    get() = extensions.getByName("kotlin") as SourceDirectorySet
diff --git a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/DefaultProjectSchemaProvider.kt b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/DefaultProjectSchemaProvider.kt
index 7a652c3..30b94d9 100644
--- a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/DefaultProjectSchemaProvider.kt
+++ b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/DefaultProjectSchemaProvider.kt
@@ -29,7 +29,9 @@
 import org.gradle.api.tasks.SourceSet
 import org.gradle.api.tasks.SourceSetContainer
 import org.gradle.api.tasks.TaskContainer
+import org.gradle.internal.Factory
 import org.gradle.internal.deprecation.DeprecatableConfiguration
+import org.gradle.internal.deprecation.DeprecationLogger
 import org.gradle.kotlin.dsl.accessors.ConfigurationEntry
 import org.gradle.kotlin.dsl.accessors.ProjectSchema
 import org.gradle.kotlin.dsl.accessors.ProjectSchemaEntry
@@ -81,9 +83,11 @@
         }
         if (target is Project) {
             @Suppress("deprecation")
-            accessibleConventionsSchema(target.convention.plugins).forEach { (name, type) ->
+            val plugins: Map<String, Any> = DeprecationLogger.whileDisabled(Factory { target.convention.plugins })!!
+            accessibleConventionsSchema(plugins).forEach { (name, type) ->
                 conventions.add(ProjectSchemaEntry(targetType, name, type))
-                collectSchemaOf(target.convention.plugins[name]!!, type)
+                val plugin = DeprecationLogger.whileDisabled(Factory { plugins[name] })!!
+                collectSchemaOf(plugin, type)
             }
             accessibleContainerSchema(target.tasks.collectionSchema).forEach { schema ->
                 tasks.add(ProjectSchemaEntry(typeOfTaskContainer, schema.name, schema.publicType))
@@ -115,7 +119,9 @@
 
 private
 fun accessibleConventionsSchema(plugins: Map<String, Any>) =
-    plugins.filterKeys(::isPublic).mapValues { inferPublicTypeOfConvention(it.value) }
+    DeprecationLogger.whileDisabled(Factory {
+        plugins.filterKeys(::isPublic).mapValues { inferPublicTypeOfConvention(it.value) }
+    })!!
 
 
 private
@@ -251,7 +257,7 @@
 
 private
 fun toConfigurationEntry(configuration: Configuration) = (configuration as DeprecatableConfiguration).run {
-    ConfigurationEntry(name, declarationAlternatives ?: listOf())
+    ConfigurationEntry(name, declarationAlternatives)
 }
 
 
diff --git a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/DefaultPrecompiledScriptPluginsSupport.kt b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/DefaultPrecompiledScriptPluginsSupport.kt
index 52874f8..7ca1d17 100644
--- a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/DefaultPrecompiledScriptPluginsSupport.kt
+++ b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/DefaultPrecompiledScriptPluginsSupport.kt
@@ -16,6 +16,7 @@
 package org.gradle.kotlin.dsl.provider.plugins.precompiled
 
 
+import org.gradle.api.InvalidUserCodeException
 import org.gradle.api.JavaVersion
 import org.gradle.api.Project
 import org.gradle.api.Task
@@ -318,6 +319,9 @@
         taskContainer.compileKotlin {
             if (hasLazyKotlinCompilerOptions) {
                 configureKotlinCompilerArgumentsLazily(resolverEnvironment)
+                doFirst {
+                    validateKotlinCompilerArguments()
+                }
             } else {
                 doFirst {
                     configureKotlinCompilerArgumentsEagerly(resolverEnvironment)
@@ -347,6 +351,25 @@
 
 
 private
+fun Task.validateKotlinCompilerArguments() {
+    withGroovyBuilder {
+        getProperty("compilerOptions").withGroovyBuilder {
+            @Suppress("unchecked_cast")
+            val freeCompilerArgs = getProperty("freeCompilerArgs") as ListProperty<String>
+            if (!freeCompilerArgs.get().containsAll(scriptTemplatesArgs)) {
+                throw InvalidUserCodeException(
+                    "Kotlin compiler arguments of ${this@validateKotlinCompilerArguments} do not work for the `kotlin-dsl` plugin. " +
+                        "The 'freeCompilerArgs' property has been reassigned. " +
+                        "It must instead be appended to. " +
+                        "Please use 'freeCompilerArgs.addAll(\"your\", \"args\")' to fix this."
+                )
+            }
+        }
+    }
+}
+
+
+private
 fun Task.configureKotlinCompilerArgumentsEagerly(resolverEnvironment: Provider<String>) {
     DeprecationLogger.deprecateBehaviour("Using the `kotlin-dsl` plugin together with Kotlin Gradle Plugin < 1.8.0.")
         .withAdvice(
@@ -443,17 +466,14 @@
         .files
 
 
-/**
- * Uses the Groovy builder to access the `kotlin` source set because KGP types are not available here.
- */
 private
 val SourceSet.kotlin: SourceDirectorySet
-    get() = withGroovyBuilder { getProperty("kotlin") } as SourceDirectorySet
+    get() = extensions.getByName("kotlin") as SourceDirectorySet
 
 
 private
-val Project.gradlePlugin
-    get() = the<GradlePluginDevelopmentExtension>()
+val Project.gradlePlugin: GradlePluginDevelopmentExtension
+    get() = extensions.getByType()
 
 
 private
@@ -462,18 +482,23 @@
     if (scriptPlugin.id == DefaultPluginManager.CORE_PLUGIN_NAMESPACE || scriptPlugin.id.startsWith(DefaultPluginManager.CORE_PLUGIN_PREFIX)) {
         throw PrecompiledScriptException(
             String.format(
-                "The precompiled plugin (%s) cannot start with '%s' or be in the '%s' package.\n\n%s", this.relativePath(scriptPlugin.scriptFile),
-                DefaultPluginManager.CORE_PLUGIN_NAMESPACE, DefaultPluginManager.CORE_PLUGIN_NAMESPACE,
-                PRECOMPILED_SCRIPT_MANUAL.consultDocumentationMessage()
-            )
+                "The precompiled plugin (%s) cannot start with '%s' or be in the '%s' package.", this.relativePath(scriptPlugin.scriptFile),
+                DefaultPluginManager.CORE_PLUGIN_NAMESPACE, DefaultPluginManager.CORE_PLUGIN_NAMESPACE
+            ),
+            null,
+            PRECOMPILED_SCRIPT_MANUAL.consultDocumentationMessage()
         )
     }
     val existingPlugin = plugins.findPlugin(scriptPlugin.id)
     if (existingPlugin != null && existingPlugin.javaClass.getPackage().name.startsWith(DefaultPluginManager.CORE_PLUGIN_PREFIX)) {
         throw PrecompiledScriptException(
             String.format(
-                "The precompiled plugin (%s) conflicts with the core plugin '%s'. Rename your plugin.\n\n%s", this.relativePath(scriptPlugin.scriptFile), scriptPlugin.id, PRECOMPILED_SCRIPT_MANUAL.consultDocumentationMessage()
-            )
+                "The precompiled plugin (%s) conflicts with the core plugin '%s'. Rename your plugin.",
+                this.relativePath(scriptPlugin.scriptFile),
+                scriptPlugin.id
+            ),
+            null,
+            PRECOMPILED_SCRIPT_MANUAL.consultDocumentationMessage()
         )
     }
 }
@@ -482,7 +507,7 @@
 private
 fun Project.declareScriptPlugins(scriptPlugins: List<PrecompiledScriptPlugin>) {
 
-    configure<GradlePluginDevelopmentExtension> {
+    gradlePlugin.apply {
         for (scriptPlugin in scriptPlugins) {
             plugins.create(scriptPlugin.id) {
                 it.id = scriptPlugin.id
@@ -527,15 +552,15 @@
 
 
 private
-val Project.sourceSets
-    get() = project.the<SourceSetContainer>()
+val Project.sourceSets: SourceSetContainer
+    get() = extensions.getByType()
 
 
 private
-val Project.javaToolchainService
-    get() = serviceOf<JavaToolchainService>()
+val Project.javaToolchainService: JavaToolchainService
+    get() = serviceOf()
 
 
 private
-val Project.java
-    get() = the<JavaPluginExtension>()
+val Project.java: JavaPluginExtension
+    get() = extensions.getByType()
diff --git a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/PrecompiledScriptException.kt b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/PrecompiledScriptException.kt
index 5475ed0..07f1615 100644
--- a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/PrecompiledScriptException.kt
+++ b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/PrecompiledScriptException.kt
@@ -19,12 +19,15 @@
 import org.gradle.api.GradleException
 import org.gradle.internal.exceptions.Contextual
 import org.gradle.internal.exceptions.LocationAwareException
+import org.gradle.internal.exceptions.ResolutionProvider
 import java.lang.reflect.InvocationTargetException
 
 
 @Contextual
 class PrecompiledScriptException
-internal constructor(message: String, cause: Throwable? = null) : GradleException(message, reduceCause(cause))
+internal constructor(message: String, cause: Throwable? = null, private val resolution: String? = null) : GradleException(message, reduceCause(cause)), ResolutionProvider {
+    override fun getResolutions(): List<String> = listOfNotNull(resolution)
+}
 
 
 private
diff --git a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/CompilePrecompiledScriptPluginPlugins.kt b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/CompilePrecompiledScriptPluginPlugins.kt
index 0e970e1..72cecd4 100644
--- a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/CompilePrecompiledScriptPluginPlugins.kt
+++ b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/CompilePrecompiledScriptPluginPlugins.kt
@@ -101,10 +101,10 @@
                         PrecompiledPluginsBlock::class,
                         implicitImportsForPrecompiledScriptPlugins(implicitImports)
                     ),
-                    classPathFiles,
+                    classPathFiles.filter { it.exists() },
                     logger,
-                    { it } // TODO: translate paths
-                )
+                    allWarningsAsErrors = false
+                ) { it } // TODO: translate paths
         }
     }
 
diff --git a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/GeneratePrecompiledScriptPluginAccessors.kt b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/GeneratePrecompiledScriptPluginAccessors.kt
index ca7b296..f995831 100644
--- a/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/GeneratePrecompiledScriptPluginAccessors.kt
+++ b/subprojects/kotlin-dsl-provider-plugins/src/main/kotlin/org/gradle/kotlin/dsl/provider/plugins/precompiled/tasks/GeneratePrecompiledScriptPluginAccessors.kt
@@ -558,31 +558,38 @@
         isCapturing!!.set(true)
     }
 
-    override fun write(b: ByteArray) {
-        if (isCapturing!!.get()) captureOutput.write(b)
-        else super.write(b)
-    }
-
-    override fun write(buf: ByteArray, off: Int, len: Int) {
-        if (isCapturing!!.get()) captureOutput.write(buf, off, len)
+    override fun write(buf: ByteArray, off: Int, len: Int) = safely {
+        if (doCapture) captureOutput.write(buf, off, len)
         else super.write(buf, off, len)
     }
 
-    override fun write(b: Int) {
-        if (isCapturing!!.get()) captureOutput.write(b)
+    override fun write(b: Int) = safely {
+        if (doCapture) captureOutput.write(b)
         else super.write(b)
     }
 
-    override fun flush() {
+    override fun flush() = safely {
         captureOutput.flush()
         super.flush()
     }
 
-    fun stop() {
+    fun stop() = safely {
         isCapturing!!.remove()
         // Give a chance for other threads' weak references to the local to be GCed if any
         isCapturing = null
     }
+
+    private
+    fun safely(block: () -> Unit) =
+        try {
+            synchronized(this, block)
+        } catch (ex: InterruptedException) {
+            Thread.currentThread().interrupt()
+        }
+
+    private
+    val doCapture: Boolean
+        get() = isCapturing != null && isCapturing!!.get()
 }
 
 
diff --git a/subprojects/kotlin-dsl-tooling-builders/build.gradle.kts b/subprojects/kotlin-dsl-tooling-builders/build.gradle.kts
index 40e408e..facaa1c 100644
--- a/subprojects/kotlin-dsl-tooling-builders/build.gradle.kts
+++ b/subprojects/kotlin-dsl-tooling-builders/build.gradle.kts
@@ -19,7 +19,10 @@
     implementation(project(":logging"))
 
     testImplementation(testFixtures(project(":kotlin-dsl")))
+    integTestImplementation(testFixtures(project(":tooling-api")))
+
     integTestImplementation(project(":internal-testing"))
+    testFixturesImplementation(project(":internal-integ-testing"))
 
     crossVersionTestImplementation(project(":persistent-cache"))
     crossVersionTestImplementation(libs.slf4jApi)
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/AbstractKotlinScriptModelCrossVersionTest.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/AbstractKotlinScriptModelCrossVersionTest.groovy
index 444b65e..43feebd 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/AbstractKotlinScriptModelCrossVersionTest.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/AbstractKotlinScriptModelCrossVersionTest.groovy
@@ -17,16 +17,15 @@
 package org.gradle.kotlin.dsl.tooling.builders
 
 import groovy.transform.CompileStatic
-import org.gradle.integtests.fixtures.RepoScriptBlockUtil
+import org.gradle.integtests.fixtures.build.ProjectSourceRoots
+import org.gradle.integtests.fixtures.build.TestProjectInitiation
 import org.gradle.integtests.tooling.fixture.TextUtil
 import org.gradle.integtests.tooling.fixture.ToolingApiAdditionalClasspath
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
-import org.gradle.test.fixtures.archive.JarTestFixture
-import org.gradle.test.fixtures.dsl.GradleDsl
-import org.gradle.test.fixtures.file.TestFile
-
 import org.gradle.kotlin.dsl.tooling.models.KotlinBuildScriptModel
+import org.gradle.test.fixtures.archive.JarTestFixture
+import org.gradle.test.fixtures.file.TestFile
 import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
 import org.gradle.util.GradleVersion
 import org.hamcrest.Description
@@ -36,10 +35,8 @@
 import java.util.function.Consumer
 import java.util.function.Predicate
 import java.util.regex.Pattern
-import java.util.zip.ZipOutputStream
 
 import static org.gradle.kotlin.dsl.resolver.KotlinBuildScriptModelRequestKt.fetchKotlinBuildScriptModelFor
-
 import static org.hamcrest.CoreMatchers.allOf
 import static org.hamcrest.CoreMatchers.containsString
 import static org.hamcrest.CoreMatchers.hasItem
@@ -47,25 +44,10 @@
 import static org.hamcrest.CoreMatchers.not
 import static org.hamcrest.MatcherAssert.assertThat
 
-
-class ProjectSourceRoots {
-
-    final File projectDir
-    final List<String> sourceSets
-    final List<String> languages
-
-    ProjectSourceRoots(File projectDir, List<String> sourceSets, List<String> languages) {
-        this.projectDir = projectDir
-        this.sourceSets = sourceSets
-        this.languages = languages
-    }
-}
-
-
 @ToolingApiVersion(">=4.1")
 @ToolingApiAdditionalClasspath(KotlinDslToolingModelsClasspathProvider)
 @CompileStatic
-abstract class AbstractKotlinScriptModelCrossVersionTest extends ToolingApiSpecification {
+abstract class AbstractKotlinScriptModelCrossVersionTest extends ToolingApiSpecification  implements TestProjectInitiation {
 
     def setup() {
         // Required for the lenient classpath mode
@@ -81,14 +63,6 @@
         }
     }
 
-    private String defaultSettingsScript = ""
-
-    protected String repositoriesBlock = """
-        repositories {
-            ${RepoScriptBlockUtil.gradlePluginRepositoryDefinition(GradleDsl.KOTLIN)}
-        }
-    """.stripIndent()
-
     private String targetKotlinVersion
 
     protected String getTargetKotlinVersion() {
@@ -105,96 +79,6 @@
         return new Properties().tap { load(new StringReader(props)) }.getProperty("kotlin")
     }
 
-    protected TestFile withDefaultSettings() {
-        return withSettings(defaultSettingsScript)
-    }
-
-    protected TestFile withSettings(String script) {
-        return withSettingsIn(".", script)
-    }
-
-    protected TestFile withDefaultSettingsIn(String baseDir) {
-        return withSettingsIn(baseDir, defaultSettingsScript)
-    }
-
-    private TestFile withSettingsIn(String baseDir, String script) {
-        return withFile("$baseDir/settings.gradle.kts", script)
-    }
-
-    protected TestFile withBuildScript(String script = "") {
-        return withBuildScriptIn(".", script)
-    }
-
-    protected TestFile withBuildScriptIn(String baseDir, String script = "") {
-        return withFile("$baseDir/build.gradle.kts", script)
-    }
-
-    protected TestFile withFile(String path, String content = "") {
-        return file(path).tap { text = content.stripIndent() }
-    }
-
-    protected TestFile withEmptyJar(String path) {
-        return file(path).tap { jarFile ->
-            jarFile.parentFile.mkdirs()
-            new ZipOutputStream(jarFile.newOutputStream()).close()
-        }
-    }
-
-    protected void withBuildSrc() {
-        projectDir.createFile("buildSrc/src/main/groovy/build/Foo.groovy") << """
-            package build
-            class Foo {}
-        """.stripIndent()
-    }
-
-    protected void withKotlinBuildSrc() {
-        withDefaultSettingsIn("buildSrc")
-        withBuildScriptIn("buildSrc", """
-            plugins {
-                `kotlin-dsl`
-            }
-
-            $repositoriesBlock
-        """)
-    }
-
-    protected ProjectSourceRoots[] withMultiProjectKotlinBuildSrc() {
-        withDefaultSettingsIn("buildSrc").append("""
-            include(":a", ":b", ":c")
-        """)
-        withFile("buildSrc/build.gradle.kts", """
-            plugins {
-                java
-                `kotlin-dsl` apply false
-            }
-
-            val kotlinDslProjects = listOf(project.project(":a"), project.project(":b"))
-
-            kotlinDslProjects.forEach {
-                it.apply(plugin = "org.gradle.kotlin.kotlin-dsl")
-            }
-
-            dependencies {
-                kotlinDslProjects.forEach {
-                    "runtimeOnly"(project(it.path))
-                }
-            }
-
-            allprojects {
-                $repositoriesBlock
-            }
-        """)
-        withFile("buildSrc/b/build.gradle.kts", """dependencies { implementation(project(":c")) }""")
-        withFile("buildSrc/c/build.gradle.kts", "plugins { java }")
-
-        return [
-            withMainSourceSetJavaIn("buildSrc"),
-            withMainSourceSetJavaKotlinIn("buildSrc/a"),
-            withMainSourceSetJavaKotlinIn("buildSrc/b"),
-            withMainSourceSetJavaIn("buildSrc/c")
-        ] as ProjectSourceRoots[]
-    }
-
     protected List<File> canonicalClassPath() {
         return canonicalClassPathFor(projectDir)
     }
@@ -293,14 +177,6 @@
         hasItem("buildSrc.jar")
     }
 
-    private ProjectSourceRoots withMainSourceSetJavaIn(String projectDir) {
-        return new ProjectSourceRoots(file(projectDir), ["main"], ["java"])
-    }
-
-    protected ProjectSourceRoots withMainSourceSetJavaKotlinIn(String projectDir) {
-        return new ProjectSourceRoots(file(projectDir), ["main"], ["java", "kotlin"])
-    }
-
     protected static Matcher<Iterable<? super File>> matchesProjectsSourceRoots(ProjectSourceRoots... projectSourceRoots) {
         return allOf(projectSourceRoots.findAll { !it.languages.isEmpty() }.collectMany { sourceRoots ->
 
@@ -354,28 +230,6 @@
         return TextUtil.normaliseFileSeparators(file.path)
     }
 
-    protected static class BuildSpec {
-        Map<String, TestFile> scripts
-        Map<String, TestFile> appliedScripts
-        Map<String, TestFile> jars
-    }
-
-    protected KotlinDslScriptsModel kotlinDslScriptsModelFor(boolean lenient = false, File... scripts) {
-        return kotlinDslScriptsModelFor(lenient, true, scripts.toList())
-    }
-
-    protected KotlinDslScriptsModel kotlinDslScriptsModelFor(boolean lenient = false, boolean explicitlyRequestPreparationTasks = true, Iterable<File> scripts) {
-        return withConnection { connection ->
-            new KotlinDslScriptsModelClient().fetchKotlinDslScriptsModel(
-                connection,
-                new KotlinDslScriptsModelRequest(
-                    scripts.toList(),
-                    null, null, [], [], lenient, explicitlyRequestPreparationTasks
-                )
-            )
-        }
-    }
-
     protected static List<File> canonicalClasspathOf(KotlinDslScriptsModel model, File script) {
         return model.scriptModels[script].classPath.collect { it.canonicalFile }
     }
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinDslScriptsModelClient.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinDslScriptsModelClient.groovy
deleted file mode 100644
index 27de04c..0000000
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinDslScriptsModelClient.groovy
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2019 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.kotlin.dsl.tooling.builders
-
-import org.gradle.tooling.ProjectConnection
-import org.gradle.tooling.model.kotlin.dsl.KotlinDslModelsParameters
-import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
-
-import javax.annotation.Nullable
-
-import static org.gradle.integtests.fixtures.RepoScriptBlockUtil.gradlePluginRepositoryMirrorUrl
-import static org.gradle.kotlin.dsl.resolver.KotlinBuildScriptModelRequestKt.newCorrelationId
-
-
-class KotlinDslScriptsModelClient {
-
-    /**
-     * Fetches Kotlin DSL model for a set of scripts.
-     *
-     * @param connection the TAPI connection
-     * @param request the model request parameters
-     * @return the model for all requested scripts
-     */
-    KotlinDslScriptsModel fetchKotlinDslScriptsModel(ProjectConnection connection, KotlinDslScriptsModelRequest request) {
-        return connection.model(KotlinDslScriptsModel).tap {
-
-            if (request.environmentVariables != null && !request.environmentVariables.isEmpty()) {
-                setEnvironmentVariables(request.environmentVariables)
-            }
-            if (request.javaHome != null) {
-                setJavaHome(request.javaHome)
-            }
-
-            if (request.lenient) {
-                setJvmArguments(request.jvmOptions + KotlinDslModelsParameters.CLASSPATH_MODE_SYSTEM_PROPERTY_DECLARATION)
-            } else {
-                setJvmArguments(request.jvmOptions + KotlinDslModelsParameters.STRICT_CLASSPATH_MODE_SYSTEM_PROPERTY_DECLARATION)
-            }
-
-            if (request.explicitlyRequestPreparationTasks) {
-                forTasks(KotlinDslModelsParameters.PREPARATION_TASK_NAME)
-            }
-
-            def arguments = request.options +
-                "-P${KotlinDslModelsParameters.CORRELATION_ID_GRADLE_PROPERTY_NAME}=${request.correlationId}".toString() +
-                "-Dorg.gradle.internal.plugins.portal.url.override=${gradlePluginRepositoryMirrorUrl()}".toString()
-
-            if (!request.scripts.isEmpty()) {
-                arguments += "-P${KotlinDslScriptsModel.SCRIPTS_GRADLE_PROPERTY_NAME}=${request.scripts.collect { it.canonicalPath }.join("|")}".toString()
-            }
-            withArguments(arguments)
-
-        }.get()
-    }
-}
-
-/**
- * Kotlin DSL model request for a set of scripts.
- */
-class KotlinDslScriptsModelRequest {
-
-    /**
-     * The set of scripts for which a model is requested.
-     * If empty, the set of scripts known to participate in this build will be used.
-     */
-    final List<File> scripts
-
-    /**
-     * Environment variables for the Gradle process.
-     * Defaults will be used if `null` or empty.
-     */
-    final Map<String, String> environmentVariables
-
-    /**
-     * Java home for the Gradle process.
-     * Defaults will be used if `null`.
-     */
-    final File javaHome
-
-    /**
-     * JVM options for the Gradle process.
-     * Defaults to an empty list.
-     */
-    final List<String> jvmOptions
-
-    /**
-     * Gradle options.
-     * Defaults to an empty list.
-     */
-    final List<String> options
-
-    /**
-     * Sets the leniency of the model builder.
-     *
-     * When set to `false` the model builder will fail on the first encountered problem.
-     *
-     * When set to `true` the model builder will make a best effort to collect problems,
-     * answer a reasonable model with editor reports for each script.
-     *
-     * Defaults to `false`.
-     */
-    final Boolean lenient
-
-    /**
-     * Request the kotlin-dsl preparation tasks to be run on the client side.
-     */
-    final Boolean explicitlyRequestPreparationTasks
-
-    /**
-     * Request correlation identifier.
-     * For client/Gradle logs correlation.
-     * Defaults to a time based identifier.
-     */
-    final String correlationId
-
-    KotlinDslScriptsModelRequest(
-        List<File> scripts,
-        @Nullable Map<String, String> environmentVariables = null,
-        @Nullable File javaHome = null,
-        List<String> jvmOptions = [],
-        List<String> options = [],
-        Boolean lenient = false,
-        Boolean explicitlyRequestPreparationTasks = true,
-        String correlationId = newCorrelationId()
-    ) {
-        this.scripts = scripts
-        this.environmentVariables = environmentVariables
-        this.javaHome = javaHome
-        this.jvmOptions = jvmOptions
-        this.options = options
-        this.lenient = lenient
-        this.explicitlyRequestPreparationTasks = explicitlyRequestPreparationTasks
-        this.correlationId = correlationId
-    }
-}
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/AbstractKotlinDslScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/AbstractKotlinDslScriptsModelCrossVersionSpec.groovy
index 23eee32..dd6428c 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/AbstractKotlinDslScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/AbstractKotlinDslScriptsModelCrossVersionSpec.groovy
@@ -16,95 +16,13 @@
 
 package org.gradle.kotlin.dsl.tooling.builders.r60
 
+import org.gradle.integtests.fixtures.build.BuildSpec
 import org.gradle.kotlin.dsl.tooling.builders.AbstractKotlinScriptModelCrossVersionTest
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
 
-import static org.gradle.integtests.tooling.fixture.TextUtil.escapeString
-
 class AbstractKotlinDslScriptsModelCrossVersionSpec extends AbstractKotlinScriptModelCrossVersionTest {
 
-    protected BuildSpec withMultiProjectBuildWithBuildSrc() {
-        withBuildSrc()
-        def someJar = withEmptyJar("classes_some.jar")
-        def settingsJar = withEmptyJar("classes_settings.jar")
-        def rootJar = withEmptyJar("classes_root.jar")
-        def aJar = withEmptyJar("classes_a.jar")
-        def bJar = withEmptyJar("classes_b.jar")
-        def precompiledJar = withEmptyJar("classes_b_precompiled.jar")
-
-        def some = withFile("some.gradle.kts", """
-            buildscript {
-                dependencies {
-                    classpath(files("${escapeString(someJar)}"))
-                }
-            }
-        """)
-        def settings = withSettings("""
-            buildscript {
-                dependencies {
-                    classpath(files("${escapeString(settingsJar)}"))
-                }
-            }
-            apply(from = "some.gradle.kts")
-            include("a", "b")
-        """)
-        def root = withBuildScript("""
-            buildscript {
-                dependencies {
-                    classpath(files("${escapeString(rootJar)}"))
-                }
-            }
-            apply(from = "some.gradle.kts")
-        """)
-        def a = withBuildScriptIn("a", """
-            buildscript {
-                dependencies {
-                    classpath(files("${escapeString(aJar)}"))
-                }
-            }
-            apply(from = "../some.gradle.kts")
-        """)
-        def b = withBuildScriptIn("b", """
-            plugins {
-                `kotlin-dsl`
-            }
-            buildscript {
-                dependencies {
-                    classpath(files("${escapeString(bJar)}"))
-                }
-            }
-            apply(from = "../some.gradle.kts")
-
-            $repositoriesBlock
-
-            dependencies {
-                implementation(files("${escapeString(precompiledJar)}"))
-            }
-        """)
-        def precompiled = withFile("b/src/main/kotlin/precompiled/precompiled.gradle.kts", "")
-        return new BuildSpec(
-            scripts: [
-                settings: settings,
-                root: root,
-                a: a,
-                b: b,
-                precompiled: precompiled
-            ],
-            appliedScripts: [
-                some: some
-            ],
-            jars: [
-                some: someJar,
-                settings: settingsJar,
-                root: rootJar,
-                a: aJar,
-                b: bJar,
-                precompiled: precompiledJar
-            ]
-        )
-    }
-
     protected static void assertModelMatchesBuildSpec(KotlinDslScriptsModel model, BuildSpec spec) {
 
         model.scriptModels.values().each { script ->
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslDefaultScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslDefaultScriptsModelCrossVersionSpec.groovy
index a7bfaf1..4aaa798 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslDefaultScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslDefaultScriptsModelCrossVersionSpec.groovy
@@ -19,6 +19,9 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.test.fixtures.Flaky
 import org.gradle.test.fixtures.file.LeaksFileHandles
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
+
+import static org.gradle.kotlin.dsl.tooling.builders.KotlinScriptModelParameters.setModelParameters
 
 @TargetGradleVersion(">=6.0")
 @LeaksFileHandles("Kotlin Compiler Daemon taking time to shut down")
@@ -32,7 +35,9 @@
         def spec = withMultiProjectBuildWithBuildSrc()
 
         when:
-        def model = kotlinDslScriptsModelFor()
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, false)
+        }
 
         then:
         model.scriptModels.keySet() == spec.scripts.values() as Set
@@ -52,7 +57,9 @@
         """
 
         when:
-        def model = kotlinDslScriptsModelFor(true)
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, true)
+        }
 
         then:
         model.scriptModels.keySet() == spec.scripts.values() as Set
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslGivenScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslGivenScriptsModelCrossVersionSpec.groovy
index 815dd23..1241f24 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslGivenScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslGivenScriptsModelCrossVersionSpec.groovy
@@ -19,6 +19,9 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.test.fixtures.Flaky
 import org.gradle.test.fixtures.file.LeaksFileHandles
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
+
+import static org.gradle.kotlin.dsl.tooling.builders.KotlinScriptModelParameters.setModelParameters
 
 @TargetGradleVersion(">=6.0")
 @LeaksFileHandles("Kotlin Compiler Daemon taking time to shut down")
@@ -32,7 +35,9 @@
         def requestedScripts = spec.scripts.values() + spec.appliedScripts.some
 
         when:
-        def model = kotlinDslScriptsModelFor(requestedScripts)
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, false, true, requestedScripts)
+        }
 
         then:
         model.scriptModels.keySet() == requestedScripts as Set
@@ -56,7 +61,9 @@
         """
 
         when:
-        def model = kotlinDslScriptsModelFor(true, requestedScripts)
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, true, true, requestedScripts)
+        }
 
         then:
         model.scriptModels.keySet() == requestedScripts as Set
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslScriptsModelCrossVersionSpec.groovy
index a7834b7..8595735 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r60/KotlinDslScriptsModelCrossVersionSpec.groovy
@@ -18,12 +18,15 @@
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.kotlin.dsl.tooling.models.KotlinBuildScriptModel
+import org.gradle.test.fixtures.Flaky
 import org.gradle.test.fixtures.file.LeaksFileHandles
 import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptModel
-import org.gradle.test.fixtures.Flaky
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
 
 import java.lang.reflect.Proxy
 
+import static org.gradle.kotlin.dsl.tooling.builders.KotlinScriptModelParameters.setModelParameters
+
 @TargetGradleVersion(">=6.0")
 @LeaksFileHandles("Kotlin Compiler Daemon taking time to shut down")
 @Flaky(because = 'https://github.com/gradle/gradle-private/issues/3414')
@@ -37,8 +40,13 @@
         and:
         def spec = withMultiProjectBuildWithBuildSrc()
 
+
         when:
-        Map<File, KotlinDslScriptModel> singleRequestModels = kotlinDslScriptsModelFor().scriptModels
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, false, true, [])
+        }
+
+        Map<File, KotlinDslScriptModel> singleRequestModels = model.scriptModels
 
         and:
         Map<File, KotlinBuildScriptModel> multiRequestsModels = spec.scripts.values().collectEntries {
@@ -60,7 +68,10 @@
         buildFileKts << ""
 
         when:
-        def model = kotlinDslScriptsModelFor(buildFileKts)
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, true, true, [buildFileKts])
+        }
+
 
         then:
         def source = Proxy.getInvocationHandler(model).sourceObject
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/AbstractKotlinDslScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/AbstractKotlinDslScriptsModelCrossVersionSpec.groovy
index 545d0e9..a57817e 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/AbstractKotlinDslScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/AbstractKotlinDslScriptsModelCrossVersionSpec.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.kotlin.dsl.tooling.builders.r68
 
+import org.gradle.integtests.fixtures.build.BuildSpec
 import org.gradle.kotlin.dsl.tooling.builders.AbstractKotlinScriptModelCrossVersionTest
 import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
 
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslDefaultScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslDefaultScriptsModelCrossVersionSpec.groovy
index dd2f1d8..c3a7ddc 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslDefaultScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslDefaultScriptsModelCrossVersionSpec.groovy
@@ -19,6 +19,9 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.test.fixtures.Flaky
 import org.gradle.test.fixtures.file.LeaksFileHandles
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
+
+import static org.gradle.kotlin.dsl.tooling.builders.KotlinScriptModelParameters.setModelParameters
 
 @TargetGradleVersion(">=6.8")
 @Flaky(because = 'https://github.com/gradle/gradle-private/issues/3414')
@@ -31,7 +34,9 @@
         def spec = withBuildSrcAndInitScripts()
 
         when:
-        def model = kotlinDslScriptsModelFor()
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, false)
+        }
 
         then:
         model.scriptModels.keySet() == spec.scripts.values() as Set
@@ -51,7 +56,9 @@
         """
 
         when:
-        def model = kotlinDslScriptsModelFor(true)
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, true)
+        }
 
         then:
         model.scriptModels.keySet() == spec.scripts.values() as Set
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslGivenScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslGivenScriptsModelCrossVersionSpec.groovy
index 1d07625..1f6d01f 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslGivenScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslGivenScriptsModelCrossVersionSpec.groovy
@@ -18,6 +18,9 @@
 
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.test.fixtures.file.LeaksFileHandles
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
+
+import static org.gradle.kotlin.dsl.tooling.builders.KotlinScriptModelParameters.setModelParameters
 
 @TargetGradleVersion(">=6.8")
 @LeaksFileHandles("Kotlin Compiler Daemon taking time to shut down")
@@ -30,7 +33,9 @@
         def requestedScripts = spec.scripts.values()
 
         when:
-        def model = kotlinDslScriptsModelFor(requestedScripts)
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, false, true, requestedScripts)
+        }
 
         then:
         model.scriptModels.keySet() == requestedScripts as Set
@@ -51,7 +56,9 @@
         """
 
         when:
-        def model = kotlinDslScriptsModelFor(true, requestedScripts)
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, true, true, [buildFileKts])
+        }
 
         then:
         model.scriptModels.keySet() == requestedScripts as Set
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslScriptsModelCrossVersionSpec.groovy
index f43701e..9c8b2f1 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r68/KotlinDslScriptsModelCrossVersionSpec.groovy
@@ -20,6 +20,9 @@
 import org.gradle.kotlin.dsl.tooling.models.KotlinBuildScriptModel
 import org.gradle.test.fixtures.file.LeaksFileHandles
 import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptModel
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
+
+import static org.gradle.kotlin.dsl.tooling.builders.KotlinScriptModelParameters.setModelParameters
 
 @TargetGradleVersion(">=6.8")
 @LeaksFileHandles("Kotlin Compiler Daemon taking time to shut down")
@@ -30,8 +33,12 @@
         given:
         def spec = withBuildSrcAndInitScripts()
 
+
         when:
-        Map<File, KotlinDslScriptModel> singleRequestModels = kotlinDslScriptsModelFor().scriptModels
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, false, true, [])
+        }
+        Map<File, KotlinDslScriptModel> singleRequestModels = model.scriptModels
 
         and:
         Map<File, KotlinBuildScriptModel> multiRequestsModels = spec.scripts.values().collectEntries {
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r71/KotlinDslScriptsModelCrossVersionSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r71/KotlinDslScriptsModelCrossVersionSpec.groovy
index b3010bb..1c9af9d 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r71/KotlinDslScriptsModelCrossVersionSpec.groovy
+++ b/subprojects/kotlin-dsl-tooling-builders/src/crossVersionTest/groovy/org/gradle/kotlin/dsl/tooling/builders/r71/KotlinDslScriptsModelCrossVersionSpec.groovy
@@ -19,7 +19,9 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.kotlin.dsl.tooling.builders.AbstractKotlinScriptModelCrossVersionTest
 import org.gradle.test.fixtures.Flaky
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
 
+import static org.gradle.kotlin.dsl.tooling.builders.KotlinScriptModelParameters.setModelParameters
 
 @TargetGradleVersion(">=7.1")
 class KotlinDslScriptsModelCrossVersionSpec extends AbstractKotlinScriptModelCrossVersionTest {
@@ -35,7 +37,9 @@
         file("src/main/kotlin/myplugin.gradle.kts") << ''
 
         when:
-        def model = kotlinDslScriptsModelFor(false, false, [])
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            setModelParameters(it, false)
+        }
 
         then:
         !model.scriptModels.isEmpty()
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/integTest/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinDslScriptsModelSpec.groovy b/subprojects/kotlin-dsl-tooling-builders/src/integTest/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinDslScriptsModelSpec.groovy
new file mode 100644
index 0000000..5c872a6
--- /dev/null
+++ b/subprojects/kotlin-dsl-tooling-builders/src/integTest/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinDslScriptsModelSpec.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 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.kotlin.dsl.tooling.builders
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.build.TestProjectInitiation
+import org.gradle.integtests.tooling.fixture.TestOutputStream
+import org.gradle.integtests.tooling.fixture.ToolingApi
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
+import org.junit.Rule
+import org.junit.rules.RuleChain
+
+class KotlinDslScriptsModelSpec extends AbstractIntegrationSpec implements TestProjectInitiation{
+    @Delegate
+    ToolingApi toolingApi = new ToolingApi(distribution, temporaryFolder)
+    TestOutputStream stderr = new TestOutputStream()
+    TestOutputStream stdout = new TestOutputStream()
+
+    @Rule
+    public RuleChain cleanupRule = RuleChain.outerRule(temporaryFolder).around(toolingApi)
+
+    def 'exceptions in different scripts are reported on the corresponding scripts'() {
+
+        given:
+        requireIsolatedUserHome()
+
+        when:
+        def spec = withMultiProject()
+        spec.scripts["a"] << "throw RuntimeException(\"ex1\")"
+        spec.scripts["b"] << "throw RuntimeException(\"ex2\")"
+
+
+        def model = loadValidatedToolingModel(KotlinDslScriptsModel) {
+            KotlinScriptModelParameters.setModelParameters(it, true, true, [])
+        }
+
+        Map<File, KotlinDslScriptsModel> singleRequestModels = model.scriptModels
+
+        then:
+
+        singleRequestModels[spec.scripts["a"]].exceptions.size() == 1
+        singleRequestModels[spec.scripts["b"]].exceptions.size() == 1
+        singleRequestModels[spec.scripts["settings"]].exceptions.isEmpty()
+    }
+}
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/main/kotlin/org/gradle/kotlin/dsl/tooling/builders/EditorReportsBuilder.kt b/subprojects/kotlin-dsl-tooling-builders/src/main/kotlin/org/gradle/kotlin/dsl/tooling/builders/EditorReportsBuilder.kt
index a35954d..4374ec8 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/main/kotlin/org/gradle/kotlin/dsl/tooling/builders/EditorReportsBuilder.kt
+++ b/subprojects/kotlin-dsl-tooling-builders/src/main/kotlin/org/gradle/kotlin/dsl/tooling/builders/EditorReportsBuilder.kt
@@ -79,7 +79,7 @@
     reports: MutableList<EditorReport>
 ) {
     val actualLinesRange = if (locationAwareHints) scriptFile.readLinesRange() else LongRange.EMPTY
-    exceptions.runtimeFailuresLocatedIn(scriptFile.path).forEach { failure ->
+    exceptions.runtimeFailuresLocatedInAndNotCausedScriptCompilation(scriptFile.path).forEach { failure ->
         if (locationAwareHints && failure.lineNumber in actualLinesRange) {
             reports.add(lineWarning(messageForLocationAwareEditorHint(failure), failure.lineNumber))
         } else {
@@ -108,15 +108,18 @@
 
 
 private
-fun Sequence<LocationAwareException>.runtimeFailuresLocatedIn(scriptPath: String): Sequence<LocationAwareException> =
+fun Sequence<Exception>.runtimeFailuresLocatedInAndNotCausedScriptCompilation(scriptPath: String): Sequence<LocationAwareException> =
+    mapNotNull { it.runtimeFailureLocatedIn(scriptPath) }.filter { !it.isCausedByScriptCompilationException }
+
+
+fun Sequence<Exception>.runtimeFailuresLocatedIn(scriptPath: String): Sequence<LocationAwareException> =
     mapNotNull { it.runtimeFailureLocatedIn(scriptPath) }
 
 
 private
 tailrec fun Throwable.runtimeFailureLocatedIn(scriptPath: String): LocationAwareException? {
     if (this is LocationAwareException && message?.contains(scriptPath) == true) {
-        return if (isCausedByScriptCompilationException) null
-        else this
+        return this
     }
     val next = cause ?: return null
     return next.runtimeFailureLocatedIn(scriptPath)
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/main/kotlin/org/gradle/kotlin/dsl/tooling/builders/KotlinBuildScriptModelBuilder.kt b/subprojects/kotlin-dsl-tooling-builders/src/main/kotlin/org/gradle/kotlin/dsl/tooling/builders/KotlinBuildScriptModelBuilder.kt
index 8750cf8..653b78c 100644
--- a/subprojects/kotlin-dsl-tooling-builders/src/main/kotlin/org/gradle/kotlin/dsl/tooling/builders/KotlinBuildScriptModelBuilder.kt
+++ b/subprojects/kotlin-dsl-tooling-builders/src/main/kotlin/org/gradle/kotlin/dsl/tooling/builders/KotlinBuildScriptModelBuilder.kt
@@ -36,8 +36,8 @@
 import org.gradle.internal.time.Time.startTimer
 import org.gradle.kotlin.dsl.*
 import org.gradle.kotlin.dsl.accessors.AccessorsClassPath
-import org.gradle.kotlin.dsl.accessors.Stage1BlocksAccessorClassPathGenerator
 import org.gradle.kotlin.dsl.accessors.ProjectAccessorsClassPathGenerator
+import org.gradle.kotlin.dsl.accessors.Stage1BlocksAccessorClassPathGenerator
 import org.gradle.kotlin.dsl.execution.EvalOption
 import org.gradle.kotlin.dsl.precompile.PrecompiledScriptDependenciesResolver
 import org.gradle.kotlin.dsl.provider.ClassPathModeExceptionCollector
@@ -411,16 +411,26 @@
                 additionalImports()
             } ?: emptyList()
 
+        val exceptions = classPathModeExceptionCollector.exceptions
         return StandardKotlinBuildScriptModel(
             (scriptClassPath + accessorsClassPath.bin).asFiles,
             (gradleSource() + classpathSources + accessorsClassPath.src).asFiles,
             implicitImports + additionalImports,
-            buildEditorReportsFor(classPathModeExceptionCollector.exceptions),
-            classPathModeExceptionCollector.exceptions.map(::exceptionToString),
+            buildEditorReportsFor(exceptions),
+            getExceptionsForFile(exceptions, this.scriptFile),
             enclosingScriptProjectDir
         )
     }
 
+
+    private
+    fun getExceptionsForFile(exceptions: List<Exception>, scriptFile: File?): List<String> {
+        return if (scriptFile == null)
+            emptyList()
+        else
+            exceptions.asSequence().runtimeFailuresLocatedIn(scriptFile.path).map(::exceptionToString).toList()
+    }
+
     private
     fun gradleSource() =
         SourcePathProvider.sourcePathFor(
diff --git a/subprojects/kotlin-dsl-tooling-builders/src/testFixtures/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinScriptModelParameters.groovy b/subprojects/kotlin-dsl-tooling-builders/src/testFixtures/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinScriptModelParameters.groovy
new file mode 100644
index 0000000..02cb10b
--- /dev/null
+++ b/subprojects/kotlin-dsl-tooling-builders/src/testFixtures/groovy/org/gradle/kotlin/dsl/tooling/builders/KotlinScriptModelParameters.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 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.kotlin.dsl.tooling.builders
+
+
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslModelsParameters
+import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
+
+import static org.gradle.integtests.fixtures.RepoScriptBlockUtil.gradlePluginRepositoryMirrorUrl
+import static org.gradle.kotlin.dsl.resolver.KotlinBuildScriptModelRequestKt.newCorrelationId
+
+class KotlinScriptModelParameters {
+    static setModelParameters(modelBuilder, boolean lenient, boolean explicitlyRequestPreparationTasks = true, Iterable<File> scripts = []) {
+        if (lenient) {
+            modelBuilder.setJvmArguments([KotlinDslModelsParameters.CLASSPATH_MODE_SYSTEM_PROPERTY_DECLARATION])
+        } else {
+            modelBuilder.setJvmArguments([KotlinDslModelsParameters.STRICT_CLASSPATH_MODE_SYSTEM_PROPERTY_DECLARATION])
+        }
+
+        if (explicitlyRequestPreparationTasks) {
+            modelBuilder.forTasks(KotlinDslModelsParameters.PREPARATION_TASK_NAME)
+        }
+        def correlationId = newCorrelationId()
+
+        def arguments =
+            ["-P${KotlinDslModelsParameters.CORRELATION_ID_GRADLE_PROPERTY_NAME}=${correlationId}".toString(),
+             "-Dorg.gradle.internal.plugins.portal.url.override=${gradlePluginRepositoryMirrorUrl()}".toString()]
+
+        if (!scripts.toList().isEmpty()) {
+            arguments += "-P${KotlinDslScriptsModel.SCRIPTS_GRADLE_PROPERTY_NAME}=${scripts.toList().collect { it.canonicalPath }.join("|")}".toString()
+        }
+        modelBuilder.withArguments(arguments)
+    }
+}
diff --git a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/AbstractCompileAvoidanceIntegrationTest.kt b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/AbstractCompileAvoidanceIntegrationTest.kt
new file mode 100644
index 0000000..e8e726d
--- /dev/null
+++ b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/AbstractCompileAvoidanceIntegrationTest.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2023 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.kotlin.dsl.compile
+
+import org.gradle.integtests.fixtures.BuildOperationsFixture
+import org.gradle.kotlin.dsl.fixtures.AbstractKotlinIntegrationTest
+import org.hamcrest.CoreMatchers
+import org.hamcrest.MatcherAssert
+import org.junit.Before
+import java.io.File
+import java.util.UUID
+
+
+abstract class AbstractCompileAvoidanceIntegrationTest : AbstractKotlinIntegrationTest() {
+
+    companion object CacheBuster {
+        var cacheBuster = UUID.randomUUID()
+    }
+
+    @Before
+    fun init() {
+        cacheBuster = UUID.randomUUID()
+
+        withSettings(
+            """
+            rootProject.name = "test-project"
+            """
+        )
+    }
+
+    protected
+    fun givenJavaClassInBuildSrcContains(classBody: String, classAnnotations: String = ""): String =
+        javaSourceFile("buildSrc", classBody, classAnnotations)
+
+    protected
+    fun givenKotlinClassInBuildSrcContains(classBody: String): String {
+        withKotlinDslPluginInBuildSrc()
+        return kotlinClassSourceFile("buildSrc", classBody)
+    }
+
+    protected
+    fun givenKotlinScriptInBuildSrcContains(scriptName: String, scriptBody: String, scriptPrefix: String = ""): String {
+        withKotlinDslPluginInBuildSrc()
+        return kotlinScriptSourceFile("buildSrc", scriptName, scriptBody, scriptPrefix)
+    }
+
+    protected
+    fun withKotlinDslPluginInBuildSrc() {
+        // this is to force buildSrc/build.gradle.kts to be written to test-local buildscript cache
+        // and not to be reused from daemon's cache from other tests when daemon is in use
+        withBuildScriptIn("buildSrc", scriptWithKotlinDslPlugin())
+            .bustScriptCache()
+    }
+
+    private
+    fun File.bustScriptCache() {
+        appendText(
+            """
+                val cacheBuster = "$cacheBuster"
+            """
+        )
+    }
+
+    protected
+    fun javaSourceFile(baseDir: String, classBody: String, classAnnotations: String = ""): String {
+        val className = "Foo"
+        withFile(
+            "$baseDir/src/main/java/com/example/$className.java",
+            """
+            package com.example;
+            $classAnnotations
+            public class $className {
+                $classBody
+            }
+            """
+        )
+        return "com.example.$className"
+    }
+
+    private
+    fun kotlinScriptSourceFile(baseDir: String, scriptName: String, scriptBody: String, scriptPrefix: String = ""): String {
+        withFile(
+            "$baseDir/src/main/kotlin/com/example/$scriptName.kt",
+            """
+            $scriptPrefix
+            package com.example
+            $scriptBody
+            """
+        )
+        return "com.example"
+    }
+
+    protected
+    fun kotlinClassSourceFile(baseDir: String, classBody: String): String {
+        val className = "Foo"
+        val packageName = kotlinScriptSourceFile(
+            baseDir,
+            className,
+            """
+            class $className {
+                $classBody
+            }
+            """
+        )
+        return "$packageName.$className"
+    }
+
+    protected
+    fun withUniqueScript(script: String) = withBuildScript(script).apply {
+        bustScriptCache()
+    }
+
+    protected
+    fun configureProject(vararg tasks: String): BuildOperationsAssertions {
+        val buildOperations = BuildOperationsFixture(executer, testDirectoryProvider)
+        val output = executer.withTasks(*tasks).run().normalizedOutput
+        return BuildOperationsAssertions(buildOperations, output)
+    }
+
+    protected
+    fun configureProjectAndExpectCompileAvoidanceWarnings(vararg tasks: String): BuildOperationsAssertions {
+        val buildOperations = BuildOperationsFixture(executer, testDirectoryProvider)
+        val output = executer.withArgument("--info").withTasks(*tasks).run().normalizedOutput
+        return BuildOperationsAssertions(buildOperations, output, true)
+    }
+
+    protected
+    fun configureProjectAndExpectCompileFailure(expectedFailure: String) {
+        val error = executer.runWithFailure().error
+        MatcherAssert.assertThat(error, CoreMatchers.containsString(expectedFailure))
+    }
+}
diff --git a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildOperationsAssertions.kt b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildOperationsAssertions.kt
new file mode 100644
index 0000000..75db1af
--- /dev/null
+++ b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildOperationsAssertions.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 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.kotlin.dsl.compile
+
+import org.gradle.integtests.fixtures.BuildOperationsFixture
+import org.gradle.util.Matchers
+import org.hamcrest.CoreMatchers
+import org.hamcrest.MatcherAssert
+import java.util.regex.Pattern
+
+
+class BuildOperationsAssertions(buildOperationsFixture: BuildOperationsFixture, val output: String, val expectWarnings: Boolean = false) {
+    private
+    val classpathCompileOperations = buildOperationsFixture.all(Pattern.compile("Compile script build.gradle.kts \\(CLASSPATH\\)"))
+
+    private
+    val bodyCompileOperations = buildOperationsFixture.all(Pattern.compile("Compile script build.gradle.kts \\(BODY\\)"))
+
+    private
+    val compileAvoidanceWarnings = output.lines()
+        .filter { it.startsWith("Cannot use Kotlin build script compile avoidance with") }
+        // filter out avoidance warnings for versioned jars - those come from Kotlin/libraries that don't change when code under test changes
+        .filterNot { it.contains(Regex("\\d.jar: ")) }
+
+    init {
+        if (!expectWarnings) {
+            MatcherAssert.assertThat(compileAvoidanceWarnings, Matchers.isEmpty())
+        }
+    }
+
+    fun assertBuildScriptCompiled(): BuildOperationsAssertions {
+        if (classpathCompileOperations.isNotEmpty() || bodyCompileOperations.isNotEmpty()) {
+            return this
+        }
+        throw AssertionError("Expected script to be compiled, but it wasn't.")
+    }
+
+    fun assertBuildScriptBodyRecompiled(): BuildOperationsAssertions {
+        if (bodyCompileOperations.size == 1) {
+            return this
+        }
+        if (bodyCompileOperations.isEmpty()) {
+            throw AssertionError("Expected build script body to be recompiled, but it wasn't.")
+        }
+        throw AssertionError("Expected build script body to be recompiled, but there was more than one body compile operation: $bodyCompileOperations")
+    }
+
+    fun assertBuildScriptCompilationAvoided(): BuildOperationsAssertions {
+        if (classpathCompileOperations.isEmpty() && bodyCompileOperations.isEmpty()) {
+            return this
+        }
+        throw AssertionError(
+            "Expected script compilation to be avoided, but the buildscript was recompiled. " +
+                "classpath compile operations: $classpathCompileOperations, body compile operations: $bodyCompileOperations"
+        )
+    }
+
+    fun assertOutputContains(expectedOutput: String): BuildOperationsAssertions {
+        MatcherAssert.assertThat(output, CoreMatchers.containsString(expectedOutput))
+        return this
+    }
+
+    fun assertContainsCompileAvoidanceWarning(end: String): BuildOperationsAssertions {
+        MatcherAssert.assertThat(compileAvoidanceWarnings, CoreMatchers.hasItem(CoreMatchers.endsWith(end)))
+        return this
+    }
+
+    fun assertNumberOfCompileAvoidanceWarnings(n: Int): BuildOperationsAssertions {
+        MatcherAssert.assertThat(compileAvoidanceWarnings, org.hamcrest.Matchers.hasSize(n))
+        return this
+    }
+}
diff --git a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildScriptCompileAvoidanceIntegrationTest.kt b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildScriptCompileAvoidanceIntegrationTest.kt
new file mode 100644
index 0000000..6dc1392
--- /dev/null
+++ b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildScriptCompileAvoidanceIntegrationTest.kt
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2023 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.kotlin.dsl.compile
+
+import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
+import org.gradle.kotlin.dsl.provider.KOTLIN_SCRIPT_COMPILATION_AVOIDANCE_ENABLED_PROPERTY
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+
+class BuildScriptCompileAvoidanceIntegrationTest : AbstractCompileAvoidanceIntegrationTest() {
+
+    @Test
+    fun `script compilation avoidance can be disabled via a system property`() {
+
+        withFile("gradle.properties", "systemProp.$KOTLIN_SCRIPT_COMPILATION_AVOIDANCE_ENABLED_PROPERTY=false")
+
+        val className = givenJavaClassInBuildSrcContains("""public void foo() { System.out.println("foo"); }""")
+        withUniqueScript("$className().foo()")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        givenJavaClassInBuildSrcContains("""public void foo() { System.out.println("bar"); }""")
+        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
+    }
+
+    @Test
+    @UnsupportedWithConfigurationCache(because = "test rely on configuration phase output")
+    fun `avoids buildscript recompilation on included build JAR rebuild`() {
+
+        withDefaultSettingsIn("build-logic")
+            .appendText("""rootProject.name = "build-logic"""")
+        withKotlinDslPluginIn("build-logic")
+        withFile("build-logic/src/main/kotlin/my-plugin.gradle.kts", "")
+        val className = kotlinClassSourceFile("build-logic", """
+            inline fun foo() { println("bar") }
+        """)
+        withSettings(""" pluginManagement { includeBuild("build-logic") } """)
+
+        withUniqueScript("""
+            plugins { id("my-plugin") }
+            $className().foo()
+        """)
+        configureProject().assertBuildScriptCompiled().assertOutputContains("bar")
+
+        // Delete the JAR as this is not cacheable and by default JARs are not reproducible
+        require(existing("build-logic/build/libs/build-logic.jar").delete())
+
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
+    }
+
+    @Test
+    fun `avoids buildscript recompilation on non ABI change in buildSrc`() {
+        val className = givenJavaClassInBuildSrcContains(
+            """
+            public void foo() {
+                System.out.println("foo");
+            }
+            """
+        )
+        withUniqueScript("$className().foo()")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        givenJavaClassInBuildSrcContains(
+            """
+            public void foo() {
+                System.out.println("bar");
+            }
+            """
+        )
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
+    }
+
+    @Test
+    fun `avoids buildscript recompilation on resource file change in buildSrc`() {
+        val className = givenJavaClassInBuildSrcContains(
+            """
+            public void foo() {
+                System.out.println("foo");
+            }
+            """
+        )
+        withFile("buildSrc/src/main/resources/foo.txt", "foo")
+        withUniqueScript("$className().foo()")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        withFile("buildSrc/src/main/resources/foo.txt", "bar")
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("foo")
+    }
+
+    @Test
+    fun `avoids buildscript recompilation on non-code change in buildSrc`() {
+        val className = givenJavaClassInBuildSrcContains(
+            """
+            public void foo() {
+                System.out.println("foo");
+            }
+            """
+        )
+        withUniqueScript("$className().foo()")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        givenJavaClassInBuildSrcContains(
+            """
+            public void foo() {
+                // a comment
+                System.out.println("foo");
+            }
+            """
+        )
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("foo")
+    }
+
+    @Test
+    fun `recompiles buildscript on ABI change in buildSrc`() {
+        val className = givenJavaClassInBuildSrcContains(
+            """
+            public void foo() {
+                System.out.println("foo");
+            }
+            """
+        )
+        withUniqueScript("$className().foo()")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        givenJavaClassInBuildSrcContains(
+            """
+            public void foo() {
+                System.out.println("bar");
+            }
+            public void bar() {}
+            """
+        )
+        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
+    }
+
+    @Test
+    @UnsupportedWithConfigurationCache(because = "test rely on configuration phase output")
+    fun `avoids buildscript recompilation on non ABI change in buildscript classpath`() {
+        val (className, jarPath) = buildJarForBuildScriptClasspath(
+            """
+            public void foo() {
+                System.out.println("foo");
+            }
+            """
+        )
+
+        withUniqueScript(
+            """
+            buildscript {
+                dependencies { classpath(files("$jarPath")) }
+            }
+            $className().foo()
+            """
+        )
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        buildJarForBuildScriptClasspath(
+            """
+            public void foo() {
+                System.out.println("bar");
+            }
+            """
+        )
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
+    }
+
+    @Test
+    @UnsupportedWithConfigurationCache(because = "test rely on configuration phase output")
+    fun `recompiles buildscript on ABI change in buildscript classpath`() {
+        val (className, jarPath) = buildJarForBuildScriptClasspath(
+            """
+            public void foo() {
+                System.out.println("foo");
+            }
+            """
+        )
+
+        withUniqueScript(
+            """
+            buildscript {
+                dependencies { classpath(files("$jarPath")) }
+            }
+            $className().foo()
+            """
+        )
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        buildJarForBuildScriptClasspath(
+            """
+            public void foo() {
+                System.out.println("bar");
+            }
+            public void bar() {}
+            """
+        )
+        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
+    }
+
+    @Test
+    @UnsupportedWithConfigurationCache(because = "test rely on configuration phase output")
+    fun `avoids buildscript recompilation when jar that can not be used for compile avoidance initially on buildsript classpath is touched`() {
+        val (className, jarPath) = buildKotlinJarForBuildScriptClasspath(
+            """
+            inline fun foo() {
+                val sum: (Int, Int) -> Int = { x, y -> x + y }
+                println("foo = " + sum(2, 2))
+            }
+            """
+        )
+
+        withUniqueScript(
+            """
+            buildscript {
+                dependencies { classpath(files("$jarPath")) }
+            }
+            $className().foo()
+            """
+        )
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo = 4")
+
+        existing(jarPath).setLastModified(1)
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("foo = 4")
+    }
+
+    @Test
+    fun `recompiles buildscript on inline function change in buildSrc class`() {
+        val className = givenKotlinClassInBuildSrcContains(
+            """
+            inline fun foo() {
+                println("foo")
+            }
+            """
+        )
+        withUniqueScript("$className().foo()")
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: inline fun foo(): compile avoidance is not supported with public inline functions")
+
+        givenKotlinClassInBuildSrcContains(
+            """
+            inline fun foo() {
+                println("bar")
+            }
+            """
+        )
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: inline fun foo(): compile avoidance is not supported with public inline functions")
+    }
+
+    @Test
+    fun `recompiles buildscript on inline lambda function change in buildSrc class`() {
+        val className = givenKotlinClassInBuildSrcContains(
+            """
+            inline fun foo() {
+                val sum: (Int, Int) -> Int = { x, y -> x + y }
+                println("foo = " + sum(2, 2))
+            }
+            """
+        )
+        withUniqueScript("$className().foo()")
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo = 4")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: inline fun foo(): compile avoidance is not supported with public inline functions")
+            .assertNumberOfCompileAvoidanceWarnings(1)
+
+        givenKotlinClassInBuildSrcContains(
+            """
+            inline fun foo() {
+                val sum: (Int, Int) -> Int = { x, y -> x - y }
+                println("foo = " + sum(2, 2))
+            }
+            """
+        )
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo = 0")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: inline fun foo(): compile avoidance is not supported with public inline functions")
+            .assertNumberOfCompileAvoidanceWarnings(1)
+    }
+
+    @Test
+    @UnsupportedWithConfigurationCache(because = "test rely on configuration phase output")
+    fun `avoids buildscript recompilation when resource file metadata is changed`() {
+        val className = givenKotlinClassInBuildSrcContains(
+            """
+            inline fun foo() {
+                val sum: (Int, Int) -> Int = { x, y -> x + y }
+                println("foo = " + sum(2, 2))
+            }
+            """
+        )
+        withUniqueScript("$className().foo()")
+        val resourceFile = withFile("buildSrc/src/main/resources/foo.txt", "foo")
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        resourceFile.setLastModified(1)
+        resourceFile.setReadOnly()
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompilationAvoided().assertOutputContains("foo")
+    }
+
+    @Test
+    fun `avoids buildscript recompilation on internal inline function change in buildSrc class`() {
+        val className = givenKotlinClassInBuildSrcContains(
+            """
+            fun foo() = bar()
+            internal inline fun bar() {
+                println("foo")
+            }
+            """
+        )
+        withUniqueScript("$className().foo()")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        givenKotlinClassInBuildSrcContains(
+            """
+            fun foo() = bar()
+            internal inline fun bar() {
+                println("bar")
+            }
+            """
+        )
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
+    }
+
+    @Test
+    fun `avoids buildscript recompilation on non ABI changes to multifile class in buildSrc`() {
+        val multifileAnnotations = """
+            @file:JvmName("Utils")
+            @file:JvmMultifileClass
+        """
+        val packageName = givenKotlinScriptInBuildSrcContains(
+            "foo",
+            """
+            fun foo() = "foo"
+            """,
+            multifileAnnotations
+        )
+        givenKotlinScriptInBuildSrcContains(
+            "bar",
+            """
+            fun bar() = "bar"
+            """,
+            multifileAnnotations
+        )
+        withUniqueScript("println($packageName.foo() + $packageName.bar())")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foobar")
+
+        givenKotlinScriptInBuildSrcContains(
+            "foo",
+            """
+            fun foo() = "bar"
+            """,
+            multifileAnnotations
+        )
+        givenKotlinScriptInBuildSrcContains(
+            "bar",
+            """
+            fun bar() = "foo"
+            """,
+            multifileAnnotations
+        )
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("barfoo")
+    }
+
+    @Test
+    fun `recompiles buildscript when inline function changes in multifile class in buildSrc`() {
+        val multifileAnnotations = """
+            @file:JvmName("Utils")
+            @file:JvmMultifileClass
+        """
+        val packageName = givenKotlinScriptInBuildSrcContains(
+            "foo",
+            """
+            inline fun foo() = "foo"
+            """,
+            multifileAnnotations
+        )
+        givenKotlinScriptInBuildSrcContains(
+            "bar",
+            """
+            inline fun bar() = "bar"
+            """,
+            multifileAnnotations
+        )
+        withUniqueScript("println($packageName.foo() + $packageName.bar())")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foobar")
+
+        givenKotlinScriptInBuildSrcContains(
+            "foo",
+            """
+            inline fun foo() = "bar"
+            """,
+            multifileAnnotations
+        )
+        givenKotlinScriptInBuildSrcContains(
+            "bar",
+            """
+            inline fun bar() = "foo"
+            """,
+            multifileAnnotations
+        )
+        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("barfoo")
+    }
+
+    @Test
+    fun `avoids buildscript recompilation on internal inline function changes in multifile class in buildSrc`() {
+        val multifileAnnotations = """
+            @file:JvmName("Utils")
+            @file:JvmMultifileClass
+        """
+        val packageName = givenKotlinScriptInBuildSrcContains(
+            "foo",
+            """
+            fun foo() = fooInternal()
+            internal inline fun fooInternal() = "foo"
+            """,
+            multifileAnnotations
+        )
+        givenKotlinScriptInBuildSrcContains(
+            "bar",
+            """
+            fun bar() = barInternal()
+            internal inline fun barInternal() = "bar"
+            """,
+            multifileAnnotations
+        )
+        withUniqueScript("println($packageName.foo() + $packageName.bar())")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foobar")
+
+        givenKotlinScriptInBuildSrcContains(
+            "foo",
+            """
+            fun foo() = fooInternal()
+            internal inline fun fooInternal() = "bar"
+            """,
+            multifileAnnotations
+        )
+        givenKotlinScriptInBuildSrcContains(
+            "bar",
+            """
+            fun bar() = barInternal()
+            internal inline fun barInternal() = "foo"
+            """,
+            multifileAnnotations
+        )
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("barfoo")
+    }
+
+    @Test
+    fun `recompiles buildscript when not able to determine Kotlin metadata kind for class on buildscript classpath`() {
+        givenJavaClassInBuildSrcContains(
+            """
+            public static String foo() {
+                return "foo";
+            }
+            """,
+            "@kotlin.Metadata(k=42, mv={1, 4, 0})"
+        )
+        withUniqueScript("println(\"foo\")")
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: Unknown Kotlin metadata with kind: 42 on class com/example/Foo - this can happen if this class is compiled with a later Kotlin version than the Kotlin compiler used by Gradle")
+
+        givenJavaClassInBuildSrcContains(
+            """
+            public static String foo() {
+                return "bar";
+            }
+            """,
+            "@kotlin.Metadata(k=42, mv={1, 4, 0})"
+        )
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptBodyRecompiled().assertOutputContains("foo")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: Unknown Kotlin metadata with kind: 42 on class com/example/Foo - this can happen if this class is compiled with a later Kotlin version than the Kotlin compiler used by Gradle")
+    }
+
+    @Test
+    fun `avoids recompiling buildscript when not able to determine Kotlin metadata kind for unchanged class on buildscript classpath`() {
+        givenJavaClassInBuildSrcContains(
+            """
+            public static String bar() {
+                return "bar";
+            }
+            """,
+            "@kotlin.Metadata(k=42, mv={1, 4, 0})"
+        )
+        withUniqueScript("println(\"foo\")")
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: Unknown Kotlin metadata with kind: 42 on class com/example/Foo - this can happen if this class is compiled with a later Kotlin version than the Kotlin compiler used by Gradle")
+        configureProject().assertBuildScriptCompilationAvoided()
+    }
+
+    private
+    fun buildJarForBuildScriptClasspath(classBody: String): Pair<String, String> {
+        val baseDir = "buildscript"
+        withSettingsIn(
+            baseDir,
+            """
+                rootProject.name = "buildscript"
+            """
+        )
+        withBuildScriptIn(
+            baseDir,
+            """
+                plugins {
+                    id("java-library")
+                }
+            """
+        )
+        val className = javaSourceFile(baseDir, classBody)
+        build(existing(baseDir), "build")
+        val jarPath = "$baseDir/build/libs/buildscript.jar"
+        assertTrue(existing(jarPath).exists())
+        return Pair(className, jarPath)
+    }
+
+    private
+    fun buildKotlinJarForBuildScriptClasspath(classBody: String): Pair<String, String> {
+        val baseDir = "buildscript"
+        withSettingsIn(
+            baseDir,
+            """
+                rootProject.name = "buildscript"
+            """
+        )
+        withBuildScriptIn(
+            baseDir,
+            """
+                plugins {
+                    `kotlin-dsl`
+                    id("java-library")
+                }
+                repositories {
+                    mavenCentral()
+                }
+            """
+        )
+        val className = kotlinClassSourceFile(baseDir, classBody)
+        build(existing(baseDir), "build")
+        val jarPath = "$baseDir/build/libs/buildscript.jar"
+        assertTrue(existing(jarPath).exists())
+        return Pair(className, jarPath)
+    }
+}
diff --git a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildSrcScriptCompileAvoidanceIntegrationTest.kt b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildSrcScriptCompileAvoidanceIntegrationTest.kt
new file mode 100644
index 0000000..ddbaaeb
--- /dev/null
+++ b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/BuildSrcScriptCompileAvoidanceIntegrationTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 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.kotlin.dsl.compile
+
+import org.junit.Test
+
+
+class BuildSrcScriptCompileAvoidanceIntegrationTest : AbstractCompileAvoidanceIntegrationTest() {
+
+    @Test
+    fun `avoids buildscript recompilation on public function change in buildSrc script`() {
+        val packageName = givenKotlinScriptInBuildSrcContains(
+            "Foo",
+            """
+            fun foo() {
+                println("foo")
+            }
+            """
+        )
+        withUniqueScript("$packageName.foo()")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        givenKotlinScriptInBuildSrcContains(
+            "Foo",
+            """
+            fun foo() {
+                println("bar")
+            }
+            """
+        )
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
+    }
+
+    @Test
+    fun `recompiles buildscript on inline function change in buildSrc script`() {
+        val packageName = givenKotlinScriptInBuildSrcContains(
+            "Foo",
+            """
+            inline fun foo() {
+                println("foo")
+            }
+            """
+        )
+        withUniqueScript("$packageName.foo()")
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/FooKt: inline fun foo(): compile avoidance is not supported with public inline functions")
+
+        givenKotlinScriptInBuildSrcContains(
+            "Foo",
+            """
+            inline fun foo() {
+                println("bar")
+            }
+            """
+        )
+        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
+            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/FooKt: inline fun foo(): compile avoidance is not supported with public inline functions")
+    }
+
+    @Test
+    fun `recompiles buildscript on const val field change in buildSrc script`() {
+        val packageName = givenKotlinScriptInBuildSrcContains(
+            "foo",
+            """
+            const val FOO = "foo"
+            """
+        )
+        withUniqueScript("println($packageName.FOO)")
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        givenKotlinScriptInBuildSrcContains(
+            "foo",
+            """
+            const val FOO = "bar"
+            """
+        )
+        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
+    }
+}
diff --git a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/PrecompiledPluginsCompileAvoidanceIntegrationTest.kt b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/PrecompiledPluginsCompileAvoidanceIntegrationTest.kt
new file mode 100644
index 0000000..7e941d4
--- /dev/null
+++ b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/compile/PrecompiledPluginsCompileAvoidanceIntegrationTest.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2023 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.kotlin.dsl.compile
+
+import org.junit.Test
+
+
+class PrecompiledPluginsCompileAvoidanceIntegrationTest : AbstractCompileAvoidanceIntegrationTest() {
+
+    @Test
+    fun `avoids buildscript recompilation when task is configured in precompiled script plugin`() {
+        val pluginId = "my-plugin"
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                println("foo")
+                tasks.register("foo")
+            """
+        )
+        withUniqueScript(
+            """
+                plugins {
+                    id("$pluginId")
+                }
+            """
+        )
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                tasks.register("foo") { doLast { println("bar from task") } }
+            """
+        )
+        configureProject("foo").assertBuildScriptCompilationAvoided().assertOutputContains("bar from task")
+    }
+
+    @Test
+    fun `recompiles buildscript when plugins applied from a precompiled plugin change`() {
+        val pluginId = "my-plugin"
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                plugins {
+                    id("java-library")
+                }
+                println("foo")
+            """
+        )
+        withUniqueScript(
+            """
+                plugins {
+                    id("$pluginId")
+                }
+            """
+        )
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                plugins {
+                    id("java")
+                }
+                println("bar")
+            """
+        )
+        configureProject().assertBuildScriptCompiled().assertOutputContains("bar")
+    }
+
+    @Test
+    fun `recompiles buildscript when plugin extension registration name changes from a precompiled plugin`() {
+        val pluginId = "my-plugin"
+        val extensionClass = """
+            open class TestExtension {
+                var message = "some-message"
+            }
+        """
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                $extensionClass
+                project.extensions.create<TestExtension>("foo")
+            """
+        )
+        withUniqueScript(
+            """
+                plugins {
+                    id("$pluginId")
+                }
+                foo {
+                    message = "foo"
+                }
+            """
+        )
+        configureProject().assertBuildScriptCompiled()
+
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                $extensionClass
+                project.extensions.create<TestExtension>("bar")
+            """
+        )
+        configureProjectAndExpectCompileFailure("Unresolved reference: foo")
+    }
+
+    @Test
+    fun `avoids buildscript recompilation on non ABI change in precompiled script plugin`() {
+        val pluginId = "my-plugin"
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                println("foo")
+            """
+        )
+        withUniqueScript(
+            """
+                plugins {
+                    id("$pluginId")
+                }
+            """
+        )
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                println("bar")
+            """
+        )
+        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
+    }
+
+    @Test
+    fun `recompiles buildscript when new task is registered in precompiled script plugin`() {
+        val pluginId = "my-plugin"
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                println("foo")
+            """
+        )
+        withUniqueScript(
+            """
+                plugins {
+                    id("$pluginId")
+                }
+            """
+        )
+        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
+
+        withPrecompiledScriptPluginInBuildSrc(
+            pluginId,
+            """
+                println("bar")
+                tasks.register("foo")
+            """
+        )
+        configureProject().assertBuildScriptCompiled().assertOutputContains("bar")
+    }
+
+    private
+    fun withPrecompiledScriptPluginInBuildSrc(pluginId: String, pluginSource: String) {
+        withKotlinDslPluginInBuildSrc()
+        withFile("buildSrc/src/main/kotlin/$pluginId.gradle.kts", pluginSource)
+    }
+}
diff --git a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/BuildScriptCompileAvoidanceIntegrationTest.kt b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/BuildScriptCompileAvoidanceIntegrationTest.kt
deleted file mode 100644
index d40d2b7..0000000
--- a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/BuildScriptCompileAvoidanceIntegrationTest.kt
+++ /dev/null
@@ -1,923 +0,0 @@
-package org.gradle.kotlin.dsl.integration
-
-import org.gradle.integtests.fixtures.BuildOperationsFixture
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.kotlin.dsl.fixtures.AbstractKotlinIntegrationTest
-import org.gradle.kotlin.dsl.provider.BUILDSCRIPT_COMPILE_AVOIDANCE_ENABLED
-import org.gradle.util.Matchers.isEmpty
-import org.hamcrest.CoreMatchers.containsString
-import org.hamcrest.CoreMatchers.endsWith
-import org.hamcrest.CoreMatchers.hasItem
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.hasSize
-import org.junit.Assert.assertTrue
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Test
-import java.io.File
-import java.util.UUID
-import java.util.regex.Pattern
-
-
-class BuildScriptCompileAvoidanceIntegrationTest : AbstractKotlinIntegrationTest() {
-
-    companion object CacheBuster {
-        var cacheBuster = UUID.randomUUID()
-    }
-
-    @Before
-    fun init() {
-        assumeTrue(BUILDSCRIPT_COMPILE_AVOIDANCE_ENABLED)
-
-        cacheBuster = UUID.randomUUID()
-
-        withSettings(
-            """
-            rootProject.name = "test-project"
-            """
-        )
-    }
-
-    @Test
-    fun `avoids buildscript recompilation on non ABI change in buildSrc`() {
-        val className = givenJavaClassInBuildSrcContains(
-            """
-            public void foo() {
-                System.out.println("foo");
-            }
-            """
-        )
-        withUniqueScript("$className().foo()")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        givenJavaClassInBuildSrcContains(
-            """
-            public void foo() {
-                System.out.println("bar");
-            }
-            """
-        )
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
-    }
-
-    @Test
-    fun `avoids buildscript recompilation on resource file change in buildSrc`() {
-        val className = givenJavaClassInBuildSrcContains(
-            """
-            public void foo() {
-                System.out.println("foo");
-            }
-            """
-        )
-        withFile("buildSrc/src/main/resources/foo.txt", "foo")
-        withUniqueScript("$className().foo()")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        withFile("buildSrc/src/main/resources/foo.txt", "bar")
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("foo")
-    }
-
-    @Test
-    fun `avoids buildscript recompilation on non-code change in buildSrc`() {
-        val className = givenJavaClassInBuildSrcContains(
-            """
-            public void foo() {
-                System.out.println("foo");
-            }
-            """
-        )
-        withUniqueScript("$className().foo()")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        givenJavaClassInBuildSrcContains(
-            """
-            public void foo() {
-                // a comment
-                System.out.println("foo");
-            }
-            """
-        )
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("foo")
-    }
-
-    @Test
-    fun `recompiles buildscript on ABI change in buildSrc`() {
-        val className = givenJavaClassInBuildSrcContains(
-            """
-            public void foo() {
-                System.out.println("foo");
-            }
-            """
-        )
-        withUniqueScript("$className().foo()")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        givenJavaClassInBuildSrcContains(
-            """
-            public void foo() {
-                System.out.println("bar");
-            }
-            public void bar() {}
-            """
-        )
-        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
-    }
-
-    @ToBeFixedForConfigurationCache(because = "test rely on configuration phase output")
-    @Test
-    fun `avoids buildscript recompilation on non ABI change in buildscript classpath`() {
-        val (className, jarPath) = buildJarForBuildScriptClasspath(
-            """
-            public void foo() {
-                System.out.println("foo");
-            }
-            """
-        )
-
-        withUniqueScript(
-            """
-            buildscript {
-                dependencies { classpath(files("$jarPath")) }
-            }
-            $className().foo()
-            """
-        )
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        buildJarForBuildScriptClasspath(
-            """
-            public void foo() {
-                System.out.println("bar");
-            }
-            """
-        )
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
-    }
-
-    @ToBeFixedForConfigurationCache(because = "test rely on configuration phase output")
-    @Test
-    fun `recompiles buildscript on ABI change in buildscript classpath`() {
-        val (className, jarPath) = buildJarForBuildScriptClasspath(
-            """
-            public void foo() {
-                System.out.println("foo");
-            }
-            """
-        )
-
-        withUniqueScript(
-            """
-            buildscript {
-                dependencies { classpath(files("$jarPath")) }
-            }
-            $className().foo()
-            """
-        )
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        buildJarForBuildScriptClasspath(
-            """
-            public void foo() {
-                System.out.println("bar");
-            }
-            public void bar() {}
-            """
-        )
-        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
-    }
-
-    @ToBeFixedForConfigurationCache(because = "test rely on configuration phase output")
-    @Test
-    fun `avoids buildscript recompilation when jar that can not be used for compile avoidance initially on buildsript classpath is touched`() {
-        val (className, jarPath) = buildKotlinJarForBuildScriptClasspath(
-            """
-            inline fun foo() {
-                val sum: (Int, Int) -> Int = { x, y -> x + y }
-                println("foo = " + sum(2, 2))
-            }
-            """
-        )
-
-        withUniqueScript(
-            """
-            buildscript {
-                dependencies { classpath(files("$jarPath")) }
-            }
-            $className().foo()
-            """
-        )
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo = 4")
-
-        existing(jarPath).setLastModified(1)
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("foo = 4")
-    }
-
-    @Test
-    fun `avoids buildscript recompilation on non ABI change in precompiled script plugin`() {
-        val pluginId = "my-plugin"
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                println("foo")
-            """
-        )
-        withUniqueScript(
-            """
-                plugins {
-                    id("$pluginId")
-                }
-            """
-        )
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                println("bar")
-            """
-        )
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
-    }
-
-    @Test
-    fun `recompiles buildscript when new task is registered in precompiled script plugin`() {
-        val pluginId = "my-plugin"
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                println("foo")
-            """
-        )
-        withUniqueScript(
-            """
-                plugins {
-                    id("$pluginId")
-                }
-            """
-        )
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                println("bar")
-                tasks.register("foo")
-            """
-        )
-        configureProject().assertBuildScriptCompiled().assertOutputContains("bar")
-    }
-
-    @Test
-    fun `avoids buildscript recompilation when task is configured in precompiled script plugin`() {
-        val pluginId = "my-plugin"
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                println("foo")
-                tasks.register("foo")
-            """
-        )
-        withUniqueScript(
-            """
-                plugins {
-                    id("$pluginId")
-                }
-            """
-        )
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                tasks.register("foo") { doLast { println("bar from task") } }
-            """
-        )
-        configureProject("foo").assertBuildScriptCompilationAvoided().assertOutputContains("bar from task")
-    }
-
-    @Test
-    fun `recompiles buildscript when plugins applied from a precompiled plugin change`() {
-        val pluginId = "my-plugin"
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                plugins {
-                    id("java-library")
-                }
-                println("foo")
-            """
-        )
-        withUniqueScript(
-            """
-                plugins {
-                    id("$pluginId")
-                }
-            """
-        )
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                plugins {
-                    id("java")
-                }
-                println("bar")
-            """
-        )
-        configureProject().assertBuildScriptCompiled().assertOutputContains("bar")
-    }
-
-    @Test
-    fun `recompiles buildscript on inline function change in buildSrc class`() {
-        val className = givenKotlinClassInBuildSrcContains(
-            """
-            inline fun foo() {
-                println("foo")
-            }
-            """
-        )
-        withUniqueScript("$className().foo()")
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: inline fun foo(): compile avoidance is not supported with public inline functions")
-
-        givenKotlinClassInBuildSrcContains(
-            """
-            inline fun foo() {
-                println("bar")
-            }
-            """
-        )
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: inline fun foo(): compile avoidance is not supported with public inline functions")
-    }
-
-    @Test
-    fun `recompiles buildscript on inline lambda function change in buildSrc class`() {
-        val className = givenKotlinClassInBuildSrcContains(
-            """
-            inline fun foo() {
-                val sum: (Int, Int) -> Int = { x, y -> x + y }
-                println("foo = " + sum(2, 2))
-            }
-            """
-        )
-        withUniqueScript("$className().foo()")
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo = 4")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: inline fun foo(): compile avoidance is not supported with public inline functions")
-            .assertNumberOfCompileAvoidanceWarnings(1)
-
-        givenKotlinClassInBuildSrcContains(
-            """
-            inline fun foo() {
-                val sum: (Int, Int) -> Int = { x, y -> x - y }
-                println("foo = " + sum(2, 2))
-            }
-            """
-        )
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo = 0")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: inline fun foo(): compile avoidance is not supported with public inline functions")
-            .assertNumberOfCompileAvoidanceWarnings(1)
-    }
-
-    @ToBeFixedForConfigurationCache(because = "test rely on configuration phase output")
-    @Test
-    fun `avoids buildscript recompilation when resource file metadata is changed`() {
-        val className = givenKotlinClassInBuildSrcContains(
-            """
-            inline fun foo() {
-                val sum: (Int, Int) -> Int = { x, y -> x + y }
-                println("foo = " + sum(2, 2))
-            }
-            """
-        )
-        withUniqueScript("$className().foo()")
-        val resourceFile = withFile("buildSrc/src/main/resources/foo.txt", "foo")
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        resourceFile.setLastModified(1)
-        resourceFile.setReadOnly()
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompilationAvoided().assertOutputContains("foo")
-    }
-
-    @Test
-    fun `avoids buildscript recompilation on public function change in buildSrc script`() {
-        val packageName = givenKotlinScriptInBuildSrcContains(
-            "Foo",
-            """
-            fun foo() {
-                println("foo")
-            }
-            """
-        )
-        withUniqueScript("$packageName.foo()")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        givenKotlinScriptInBuildSrcContains(
-            "Foo",
-            """
-            fun foo() {
-                println("bar")
-            }
-            """
-        )
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
-    }
-
-    @Test
-    fun `recompiles buildscript on inline function change in buildSrc script`() {
-        val packageName = givenKotlinScriptInBuildSrcContains(
-            "Foo",
-            """
-            inline fun foo() {
-                println("foo")
-            }
-            """
-        )
-        withUniqueScript("$packageName.foo()")
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/FooKt: inline fun foo(): compile avoidance is not supported with public inline functions")
-
-        givenKotlinScriptInBuildSrcContains(
-            "Foo",
-            """
-            inline fun foo() {
-                println("bar")
-            }
-            """
-        )
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/FooKt: inline fun foo(): compile avoidance is not supported with public inline functions")
-    }
-
-    @Test
-    fun `avoids buildscript recompilation on internal inline function change in buildSrc class`() {
-        val className = givenKotlinClassInBuildSrcContains(
-            """
-            fun foo() = bar()
-            internal inline fun bar() {
-                println("foo")
-            }
-            """
-        )
-        withUniqueScript("$className().foo()")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        givenKotlinClassInBuildSrcContains(
-            """
-            fun foo() = bar()
-            internal inline fun bar() {
-                println("bar")
-            }
-            """
-        )
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("bar")
-    }
-
-    @Test
-    fun `recompiles buildscript on const val field change in buildSrc script`() {
-        val packageName = givenKotlinScriptInBuildSrcContains(
-            "foo",
-            """
-            const val FOO = "foo"
-            """
-        )
-        withUniqueScript("println($packageName.FOO)")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foo")
-
-        givenKotlinScriptInBuildSrcContains(
-            "foo",
-            """
-            const val FOO = "bar"
-            """
-        )
-        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("bar")
-    }
-
-    @Test
-    fun `recompiles buildscript when plugin extension registration name changes from a precompiled plugin`() {
-        val pluginId = "my-plugin"
-        val extensionClass = """
-            open class TestExtension {
-                var message = "some-message"
-            }
-        """
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                $extensionClass
-                project.extensions.create<TestExtension>("foo")
-            """
-        )
-        withUniqueScript(
-            """
-                plugins {
-                    id("$pluginId")
-                }
-                foo {
-                    message = "foo"
-                }
-            """
-        )
-        configureProject().assertBuildScriptCompiled()
-
-        withPrecompiledScriptPluginInBuildSrc(
-            pluginId,
-            """
-                $extensionClass
-                project.extensions.create<TestExtension>("bar")
-            """
-        )
-        configureProjectAndExpectCompileFailure("Unresolved reference: foo")
-    }
-
-    @Test
-    fun `avoids buildscript recompilation on non ABI changes to multifile class in buildSrc`() {
-        val multifileAnnotations = """
-            @file:JvmName("Utils")
-            @file:JvmMultifileClass
-        """
-        val packageName = givenKotlinScriptInBuildSrcContains(
-            "foo",
-            """
-            fun foo() = "foo"
-            """,
-            multifileAnnotations
-        )
-        givenKotlinScriptInBuildSrcContains(
-            "bar",
-            """
-            fun bar() = "bar"
-            """,
-            multifileAnnotations
-        )
-        withUniqueScript("println($packageName.foo() + $packageName.bar())")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foobar")
-
-        givenKotlinScriptInBuildSrcContains(
-            "foo",
-            """
-            fun foo() = "bar"
-            """,
-            multifileAnnotations
-        )
-        givenKotlinScriptInBuildSrcContains(
-            "bar",
-            """
-            fun bar() = "foo"
-            """,
-            multifileAnnotations
-        )
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("barfoo")
-    }
-
-    @Test
-    fun `recompiles buildscript when inline function changes in multifile class in buildSrc`() {
-        val multifileAnnotations = """
-            @file:JvmName("Utils")
-            @file:JvmMultifileClass
-        """
-        val packageName = givenKotlinScriptInBuildSrcContains(
-            "foo",
-            """
-            inline fun foo() = "foo"
-            """,
-            multifileAnnotations
-        )
-        givenKotlinScriptInBuildSrcContains(
-            "bar",
-            """
-            inline fun bar() = "bar"
-            """,
-            multifileAnnotations
-        )
-        withUniqueScript("println($packageName.foo() + $packageName.bar())")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foobar")
-
-        givenKotlinScriptInBuildSrcContains(
-            "foo",
-            """
-            inline fun foo() = "bar"
-            """,
-            multifileAnnotations
-        )
-        givenKotlinScriptInBuildSrcContains(
-            "bar",
-            """
-            inline fun bar() = "foo"
-            """,
-            multifileAnnotations
-        )
-        configureProject().assertBuildScriptBodyRecompiled().assertOutputContains("barfoo")
-    }
-
-    @Test
-    fun `avoids buildscript recompilation on internal inline function changes in multifile class in buildSrc`() {
-        val multifileAnnotations = """
-            @file:JvmName("Utils")
-            @file:JvmMultifileClass
-        """
-        val packageName = givenKotlinScriptInBuildSrcContains(
-            "foo",
-            """
-            fun foo() = fooInternal()
-            internal inline fun fooInternal() = "foo"
-            """,
-            multifileAnnotations
-        )
-        givenKotlinScriptInBuildSrcContains(
-            "bar",
-            """
-            fun bar() = barInternal()
-            internal inline fun barInternal() = "bar"
-            """,
-            multifileAnnotations
-        )
-        withUniqueScript("println($packageName.foo() + $packageName.bar())")
-        configureProject().assertBuildScriptCompiled().assertOutputContains("foobar")
-
-        givenKotlinScriptInBuildSrcContains(
-            "foo",
-            """
-            fun foo() = fooInternal()
-            internal inline fun fooInternal() = "bar"
-            """,
-            multifileAnnotations
-        )
-        givenKotlinScriptInBuildSrcContains(
-            "bar",
-            """
-            fun bar() = barInternal()
-            internal inline fun barInternal() = "foo"
-            """,
-            multifileAnnotations
-        )
-        configureProject().assertBuildScriptCompilationAvoided().assertOutputContains("barfoo")
-    }
-
-    @Test
-    fun `recompiles buildscript when not able to determine Kotlin metadata kind for class on buildscript classpath`() {
-        givenJavaClassInBuildSrcContains(
-            """
-            public static String foo() {
-                return "foo";
-            }
-            """,
-            "@kotlin.Metadata(k=42, mv={1, 4, 0})"
-        )
-        withUniqueScript("println(\"foo\")")
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: Unknown Kotlin metadata with kind: 42 on class com/example/Foo - this can happen if this class is compiled with a later Kotlin version than the Kotlin compiler used by Gradle")
-
-        givenJavaClassInBuildSrcContains(
-            """
-            public static String foo() {
-                return "bar";
-            }
-            """,
-            "@kotlin.Metadata(k=42, mv={1, 4, 0})"
-        )
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptBodyRecompiled().assertOutputContains("foo")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: Unknown Kotlin metadata with kind: 42 on class com/example/Foo - this can happen if this class is compiled with a later Kotlin version than the Kotlin compiler used by Gradle")
-    }
-
-    @Test
-    fun `avoids recompiling buildscript when not able to determine Kotlin metadata kind for unchanged class on buildscript classpath`() {
-        givenJavaClassInBuildSrcContains(
-            """
-            public static String bar() {
-                return "bar";
-            }
-            """,
-            "@kotlin.Metadata(k=42, mv={1, 4, 0})"
-        )
-        withUniqueScript("println(\"foo\")")
-        configureProjectAndExpectCompileAvoidanceWarnings().assertBuildScriptCompiled().assertOutputContains("foo")
-            .assertContainsCompileAvoidanceWarning("buildSrc.jar: class com/example/Foo: Unknown Kotlin metadata with kind: 42 on class com/example/Foo - this can happen if this class is compiled with a later Kotlin version than the Kotlin compiler used by Gradle")
-        configureProject().assertBuildScriptCompilationAvoided()
-    }
-
-    private
-    fun withUniqueScript(script: String) = withBuildScript(script).apply {
-        bustScriptCache()
-    }
-
-    private
-    fun withKotlinDslPluginInBuildSrc() {
-        // this is to force buildSrc/build.gradle.kts to be written to test-local buildscript cache
-        // and not to be reused from daemon's cache from other tests when daemon is in use
-        withBuildScriptIn("buildSrc", scriptWithKotlinDslPlugin())
-            .bustScriptCache()
-    }
-
-    private
-    fun File.bustScriptCache() {
-        appendText(
-            """
-                val cacheBuster = "$cacheBuster"
-            """
-        )
-    }
-
-    private
-    fun withPrecompiledScriptPluginInBuildSrc(pluginId: String, pluginSource: String) {
-        withKotlinDslPluginInBuildSrc()
-        withFile("buildSrc/src/main/kotlin/$pluginId.gradle.kts", pluginSource)
-    }
-
-    private
-    fun buildJarForBuildScriptClasspath(classBody: String): Pair<String, String> {
-        val baseDir = "buildscript"
-        withSettingsIn(
-            baseDir,
-            """
-                rootProject.name = "buildscript"
-            """
-        )
-        withBuildScriptIn(
-            baseDir,
-            """
-                plugins {
-                    id("java-library")
-                }
-            """
-        )
-        val className = javaSourceFile(baseDir, classBody)
-        build(existing(baseDir), "build")
-        val jarPath = "$baseDir/build/libs/buildscript.jar"
-        assertTrue(existing(jarPath).exists())
-        return Pair(className, jarPath)
-    }
-
-    private
-    fun buildKotlinJarForBuildScriptClasspath(classBody: String): Pair<String, String> {
-        val baseDir = "buildscript"
-        withSettingsIn(
-            baseDir,
-            """
-                rootProject.name = "buildscript"
-            """
-        )
-        withBuildScriptIn(
-            baseDir,
-            """
-                plugins {
-                    `kotlin-dsl`
-                    id("java-library")
-                }
-                repositories {
-                    mavenCentral()
-                }
-            """
-        )
-        val className = kotlinClassSourceFile(baseDir, classBody)
-        build(existing(baseDir), "build")
-        val jarPath = "$baseDir/build/libs/buildscript.jar"
-        assertTrue(existing(jarPath).exists())
-        return Pair(className, jarPath)
-    }
-
-    private
-    fun givenJavaClassInBuildSrcContains(classBody: String, classAnnotations: String = ""): String =
-        javaSourceFile("buildSrc", classBody, classAnnotations)
-
-    private
-    fun givenKotlinClassInBuildSrcContains(classBody: String): String {
-        withKotlinDslPluginInBuildSrc()
-        return kotlinClassSourceFile("buildSrc", classBody)
-    }
-
-    private
-    fun givenKotlinScriptInBuildSrcContains(scriptName: String, scriptBody: String, scriptPrefix: String = ""): String {
-        withKotlinDslPluginInBuildSrc()
-        return kotlinScriptSourceFile("buildSrc", scriptName, scriptBody, scriptPrefix)
-    }
-
-    private
-    fun javaSourceFile(baseDir: String, classBody: String, classAnnotations: String = ""): String {
-        val className = "Foo"
-        withFile(
-            "$baseDir/src/main/java/com/example/$className.java",
-            """
-            package com.example;
-            $classAnnotations
-            public class $className {
-                $classBody
-            }
-            """
-        )
-        return "com.example.$className"
-    }
-
-    private
-    fun kotlinClassSourceFile(baseDir: String, classBody: String): String {
-        val className = "Foo"
-        val packageName = kotlinScriptSourceFile(
-            baseDir,
-            className,
-            """
-            class $className {
-                $classBody
-            }
-            """
-        )
-        return "$packageName.$className"
-    }
-
-    private
-    fun kotlinScriptSourceFile(baseDir: String, scriptName: String, scriptBody: String, scriptPrefix: String = ""): String {
-        withFile(
-            "$baseDir/src/main/kotlin/com/example/$scriptName.kt",
-            """
-            $scriptPrefix
-            package com.example
-            $scriptBody
-            """
-        )
-        return "com.example"
-    }
-
-    private
-    fun configureProject(vararg tasks: String): BuildOperationsAssertions {
-        val buildOperations = BuildOperationsFixture(executer, testDirectoryProvider)
-        val output = executer.withTasks(*tasks).run().normalizedOutput
-        return BuildOperationsAssertions(buildOperations, output)
-    }
-
-    private
-    fun configureProjectAndExpectCompileAvoidanceWarnings(vararg tasks: String): BuildOperationsAssertions {
-        val buildOperations = BuildOperationsFixture(executer, testDirectoryProvider)
-        val output = executer.withArgument("--info").withTasks(*tasks).run().normalizedOutput
-        return BuildOperationsAssertions(buildOperations, output, true)
-    }
-
-    private
-    fun configureProjectAndExpectCompileFailure(expectedFailure: String) {
-        val error = executer.runWithFailure().error
-        assertThat(error, containsString(expectedFailure))
-    }
-}
-
-
-private
-class BuildOperationsAssertions(buildOperationsFixture: BuildOperationsFixture, val output: String, val expectWarnings: Boolean = false) {
-    private
-    val classpathCompileOperations = buildOperationsFixture.all(Pattern.compile("Compile script build.gradle.kts \\(CLASSPATH\\)"))
-
-    private
-    val bodyCompileOperations = buildOperationsFixture.all(Pattern.compile("Compile script build.gradle.kts \\(BODY\\)"))
-
-    private
-    val compileAvoidanceWarnings = output.lines()
-        .filter { it.startsWith("Cannot use Kotlin build script compile avoidance with") }
-        // filter out avoidance warnings for versioned jars - those come from Kotlin/libraries that don't change when code under test changes
-        .filterNot { it.contains(Regex("\\d.jar: ")) }
-
-    init {
-        if (!expectWarnings) {
-            assertThat(compileAvoidanceWarnings, isEmpty())
-        }
-    }
-
-    fun assertBuildScriptCompiled(): BuildOperationsAssertions {
-        if (classpathCompileOperations.isNotEmpty() || bodyCompileOperations.isNotEmpty()) {
-            return this
-        }
-        throw AssertionError("Expected script to be compiled, but it wasn't.")
-    }
-
-    fun assertBuildScriptBodyRecompiled(): BuildOperationsAssertions {
-        if (bodyCompileOperations.size == 1) {
-            return this
-        }
-        if (bodyCompileOperations.isEmpty()) {
-            throw AssertionError("Expected build script body to be recompiled, but it wasn't.")
-        }
-        throw AssertionError("Expected build script body to be recompiled, but there was more than one body compile operation: $bodyCompileOperations")
-    }
-
-    fun assertBuildScriptCompilationAvoided(): BuildOperationsAssertions {
-        if (classpathCompileOperations.isEmpty() && bodyCompileOperations.isEmpty()) {
-            return this
-        }
-        throw AssertionError(
-            "Expected script compilation to be avoided, but the buildscript was recompiled. " +
-                "classpath compile operations: $classpathCompileOperations, body compile operations: $bodyCompileOperations"
-        )
-    }
-
-    fun assertOutputContains(expectedOutput: String): BuildOperationsAssertions {
-        assertThat(output, containsString(expectedOutput))
-        return this
-    }
-
-    fun assertContainsCompileAvoidanceWarning(end: String): BuildOperationsAssertions {
-        assertThat(compileAvoidanceWarnings, hasItem(endsWith(end)))
-        return this
-    }
-
-    fun assertNumberOfCompileAvoidanceWarnings(n: Int): BuildOperationsAssertions {
-        assertThat(compileAvoidanceWarnings, hasSize(n))
-        return this
-    }
-}
diff --git a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinStandaloneScriptWarningsIntegrationTest.kt b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinStandaloneScriptWarningsIntegrationTest.kt
new file mode 100644
index 0000000..8fd9061
--- /dev/null
+++ b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/integration/KotlinStandaloneScriptWarningsIntegrationTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2023 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.kotlin.dsl.integration
+
+import org.gradle.integtests.fixtures.executer.ExecutionFailure
+import org.gradle.kotlin.dsl.fixtures.AbstractKotlinIntegrationTest
+import org.gradle.kotlin.dsl.fixtures.clickableUrlFor
+import org.junit.Before
+import org.junit.Test
+import java.io.File
+
+
+class KotlinStandaloneScriptWarningsIntegrationTest : AbstractKotlinIntegrationTest() {
+
+    @Before
+    fun setup() {
+        withFile("gradle.properties", "org.gradle.kotlin.dsl.allWarningsAsErrors=true")
+        executer.beforeExecute { noDeprecationChecks() }
+    }
+
+    @Test
+    fun `fails on warning in project script`() {
+        val script = withBuildScript(scriptContentWithWarning)
+        buildAndFail("help").apply {
+            assertHasWarningLineFor(script)
+            assertHasDetailedErrorOutput()
+        }
+    }
+
+    @Test
+    fun `fails on warning in settings script`() {
+        val script = withSettings(scriptContentWithWarning)
+        buildAndFail("help").apply {
+            assertHasWarningLineFor(script)
+            assertHasDetailedErrorOutput()
+        }
+    }
+
+    @Test
+    fun `fails on warning in initialization script`() {
+        val script = withFile("my-init.gradle.kts", scriptContentWithWarning)
+        buildAndFail("help", "-I", script.name).apply {
+            assertHasWarningLineFor(script)
+            assertHasDetailedErrorOutput()
+        }
+    }
+
+    @Test
+    fun `fails on warning in applied script`() {
+        val script = withFile("my-script.gradle.kts", scriptContentWithWarning)
+        withBuildScript("""apply(from = "${script.name}")""")
+        buildAndFail("help").apply {
+            assertHasWarningLineFor(script)
+            assertHasDetailedErrorOutput()
+        }
+    }
+
+    private
+    val scriptContentWithWarning =
+        """
+        @Deprecated("BECAUSE")
+        class SomeDeprecatedType
+        SomeDeprecatedType()
+        """.trimIndent()
+
+    private
+    fun ExecutionFailure.assertHasWarningLineFor(script: File) =
+        assertHasErrorOutput("w: ${clickableUrlFor(script)}:3:1: 'SomeDeprecatedType' is deprecated. BECAUSE")
+
+    private
+    fun ExecutionFailure.assertHasDetailedErrorOutput() =
+        assertHasErrorOutput(
+            """
+            * What went wrong:
+            Script compilation error:
+
+              Line 3: SomeDeprecatedType()
+                      ^ 'SomeDeprecatedType' is deprecated. BECAUSE
+
+            1 error
+            """.trimIndent()
+        )
+}
diff --git a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/resolver/KotlinScriptDependenciesResolverTest.kt b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/resolver/KotlinScriptDependenciesResolverTest.kt
index a618017..f9fc8e8 100644
--- a/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/resolver/KotlinScriptDependenciesResolverTest.kt
+++ b/subprojects/kotlin-dsl/src/integTest/kotlin/org/gradle/kotlin/dsl/resolver/KotlinScriptDependenciesResolverTest.kt
@@ -19,27 +19,21 @@
 import com.nhaarman.mockito_kotlin.doReturn
 import com.nhaarman.mockito_kotlin.mock
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-
 import org.gradle.kotlin.dsl.fixtures.AbstractKotlinIntegrationTest
 import org.gradle.test.fixtures.Flaky
-
 import org.hamcrest.CoreMatchers.equalTo
 import org.hamcrest.CoreMatchers.hasItems
 import org.hamcrest.CoreMatchers.instanceOf
 import org.hamcrest.CoreMatchers.notNullValue
 import org.hamcrest.CoreMatchers.nullValue
 import org.hamcrest.Matcher
-
-import org.junit.Assert.assertSame
 import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Assert.assertSame
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
-
 import java.io.File
-
 import kotlin.reflect.KClass
-
 import kotlin.script.dependencies.KotlinScriptExternalDependencies
 import kotlin.script.dependencies.ScriptContents
 import kotlin.script.dependencies.ScriptContents.Position
@@ -283,9 +277,6 @@
 
     @Test
     fun `do not report file warning on script compilation failure in currently edited script`() {
-        // because the IDE already provides user feedback for those
-        assumeNonEmbeddedGradleExecuter()
-
         val editedScript = withBuildScript(
             """
             doNotExists()
@@ -302,33 +293,6 @@
         }
     }
 
-    @Test
-    fun `report file warning on script compilation failure in another script`() {
-        assumeNonEmbeddedGradleExecuter()
-
-        withDefaultSettings().appendText(
-            """
-            include("a", "b")
-            """
-        )
-        withBuildScript("")
-        withBuildScriptIn(
-            "a",
-            """
-            doNotExists()
-            """
-        )
-        val editedScript = withBuildScriptIn("b", "")
-
-        resolvedScriptDependencies(editedScript).apply {
-            assertContainsBasicDependencies()
-        }
-
-        recorder.apply {
-            assertLastEventIsInstanceOf(ResolvedDependenciesWithErrors::class)
-            assertSingleFileWarningReport(EditorMessages.buildConfigurationFailed)
-        }
-    }
 
     @Test
     fun `report file warning on runtime failure in currently edited script`() {
@@ -376,35 +340,6 @@
         }
     }
 
-    @Test
-    fun `report file warning on runtime failure in another script`() {
-        assumeNonEmbeddedGradleExecuter()
-
-        withDefaultSettings().appendText(
-            """
-            include("a", "b")
-            """
-        )
-        withBuildScript("")
-        withBuildScriptIn(
-            "a",
-            """
-            configurations.getByName("doNotExists")
-            """
-        )
-        val editedScript = withBuildScriptIn("b", "")
-
-
-        resolvedScriptDependencies(editedScript).apply {
-            assertContainsBasicDependencies()
-        }
-
-        recorder.apply {
-            assertLastEventIsInstanceOf(ResolvedDependenciesWithErrors::class)
-            assertSingleFileWarningReport(EditorMessages.buildConfigurationFailed)
-        }
-    }
-
     private
     val recorder = ResolverTestRecorder()
 
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConfigurableFileCollectionExtensions.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConfigurableFileCollectionExtensions.kt
index 0001128..63c5414 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConfigurableFileCollectionExtensions.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConfigurableFileCollectionExtensions.kt
@@ -16,8 +16,10 @@
 
 package org.gradle.kotlin.dsl
 
+import org.gradle.api.Incubating
 import org.gradle.api.file.ConfigurableFileCollection
-
+import org.gradle.api.file.FileCollection
+import org.gradle.kotlin.dsl.assignment.internal.KotlinDslAssignment
 import kotlin.reflect.KProperty
 
 
@@ -37,3 +39,16 @@
  */
 operator fun ConfigurableFileCollection.setValue(receiver: Any?, property: KProperty<*>, value: Iterable<*>) =
     setFrom(value)
+
+
+/**
+ * Sets the ConfigurableFileCollection to contain the source paths of passed collection.
+ * This is the same as calling ConfigurableFileCollection.setFrom(fileCollection: FileCollection).
+ *
+ * @since 8.2
+ */
+@Incubating
+fun ConfigurableFileCollection.assign(fileCollection: FileCollection) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.setFrom(fileCollection)
+}
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConfigurationDeprecatedExtensions.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConfigurationDeprecatedExtensions.kt
index 9d644ce..99f216a 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConfigurationDeprecatedExtensions.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConfigurationDeprecatedExtensions.kt
@@ -377,6 +377,7 @@
 /**
  * See [Configuration.all].
  */
+@Suppress("DEPRECATION")
 @Deprecated(deprecationMessage, replaceWith = ReplaceWith("get().all"), level = DeprecationLevel.HIDDEN)
 val <T : Configuration> NamedDomainObjectProvider<T>.all: Set<Configuration>
     get() = get().all
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConventionExtensions.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConventionExtensions.kt
index 06a2b43..44310b4 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConventionExtensions.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ConventionExtensions.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION")
 package org.gradle.kotlin.dsl
 
 import org.gradle.api.plugins.Convention
-
 import org.gradle.kotlin.dsl.accessors.runtime.conventionOf
 import org.gradle.kotlin.dsl.accessors.runtime.conventionPluginByName
 
@@ -35,7 +35,6 @@
  */
 @Deprecated("The concept of conventions is deprecated. Use extensions instead.")
 inline fun <reified T : Any> Convention.getPluginByName(name: String): T =
-    @Suppress("deprecation")
     conventionPluginByName(this, name).let {
         (it as T?) ?: throw IllegalStateException("Convention '$name' of type '${it::class.java.name}' cannot be cast to '${T::class.java.name}'.")
     }
@@ -49,6 +48,7 @@
  * @throws [IllegalStateException] when there is no such object contained in this convention, or when there are multiple such objects
  * @see [Convention.getPlugin]
  */
+@Deprecated("The concept of conventions is deprecated. Use extensions instead.")
 inline fun <reified T : Any> Convention.getPlugin(): T =
     getPlugin(T::class)
 
@@ -61,6 +61,7 @@
  * @throws [IllegalStateException] when there is no such object contained in this convention, or when there are multiple such objects
  * @see [Convention.getPlugin]
  */
+@Deprecated("The concept of conventions is deprecated. Use extensions instead.")
 fun <T : Any> Convention.getPlugin(conventionType: KClass<T>): T =
     getPlugin(conventionType.java)
 
@@ -73,6 +74,7 @@
  * @throws [IllegalStateException] when there are multiple matching objects
  * @see [Convention.findPlugin]
  */
+@Deprecated("The concept of conventions is deprecated. Use extensions instead.")
 inline fun <reified T : Any> Convention.findPlugin(): T? =
     findPlugin(T::class)
 
@@ -85,6 +87,7 @@
  * @throws [IllegalStateException] when there are multiple matching objects
  * @see [Convention.findPlugin]
  */
+@Deprecated("The concept of conventions is deprecated. Use extensions instead.")
 fun <T : Any> Convention.findPlugin(conventionType: KClass<T>): T? =
     findPlugin(conventionType.java)
 
@@ -104,5 +107,4 @@
     conventionType: KClass<ConventionType>,
     function: ConventionType.() -> ReturnType
 ): ReturnType =
-    @Suppress("deprecation")
     conventionOf(this).getPlugin(conventionType.java).run(function)
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/KotlinAssignmentExtensions.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/KotlinAssignmentExtensions.kt
deleted file mode 100644
index cd989f5..0000000
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/KotlinAssignmentExtensions.kt
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-/**
- * @since 8.1
- */
-@file:Incubating
-package org.gradle.kotlin.dsl
-
-import org.gradle.api.Incubating
-import org.gradle.api.file.ConfigurableFileCollection
-import org.gradle.api.file.FileCollection
-import org.gradle.api.file.FileSystemLocation
-import org.gradle.api.file.FileSystemLocationProperty
-import org.gradle.api.provider.HasMultipleValues
-import org.gradle.api.provider.MapProperty
-import org.gradle.api.provider.Property
-import org.gradle.api.provider.Provider
-import org.gradle.util.internal.IncubationLogger
-import java.io.File
-
-
-/**
- * Assign value: T to a property with assign operator
- *
- * @since 8.1
- */
-@Incubating
-fun <T> Property<T>.assign(value: T?) {
-    emitIncubatingLogMessage()
-    this.set(value)
-}
-
-
-/**
- * Assign value: Provider<T> to a property with assign operator
- *
- * @since 8.1
- */
-@Incubating
-fun <T> Property<T>.assign(value: Provider<out T?>) {
-    emitIncubatingLogMessage()
-    this.set(value)
-}
-
-
-/**
- * Assign file to a FileSystemLocationProperty with assign operator
- *
- * @since 8.1
- */
-@Incubating
-fun <T : FileSystemLocation> FileSystemLocationProperty<T>.assign(file: File?) {
-    emitIncubatingLogMessage()
-    this.set(file)
-}
-
-
-/**
- * Assign file provided by a Provider to a FileSystemLocationProperty with assign operator
- *
- * @since 8.1
- */
-@Incubating
-fun <T : FileSystemLocation> FileSystemLocationProperty<T>.assign(provider: Provider<File?>) {
-    emitIncubatingLogMessage()
-    this.fileProvider(provider)
-}
-
-
-/**
- * Sets the value of the property to the elements of the given iterable, and replaces any existing value
- *
- * @since 8.1
- */
-@Incubating
-fun <T> HasMultipleValues<T>.assign(elements: Iterable<T?>?) {
-    emitIncubatingLogMessage()
-    this.set(elements)
-}
-
-
-/**
- * Sets the property to have the same value of the given provider, and replaces any existing value
- *
- * @since 8.1
- */
-@Incubating
-fun <T> HasMultipleValues<T>.assign(provider: Provider<out Iterable<T?>?>) {
-    emitIncubatingLogMessage()
-    this.set(provider)
-}
-
-
-/**
- * Sets the value of this property to the entries of the given Map, and replaces any existing value
- *
- * @since 8.1
- */
-@Incubating
-fun <K, V> MapProperty<K, V>.assign(entries: Map<out K?, V?>?) {
-    emitIncubatingLogMessage()
-    this.set(entries)
-}
-
-
-/**
- * Sets the property to have the same value of the given provider, and replaces any existing value
- *
- * @since 8.1
- */
-@Incubating
-fun <K, V> MapProperty<K, V>.assign(provider: Provider<out Map<out K?, V?>?>) {
-    emitIncubatingLogMessage()
-    this.set(provider)
-}
-
-
-/**
- * Sets the ConfigurableFileCollection to contain the source paths of passed collection.
- * This is the same as calling ConfigurableFileCollection.setFrom(fileCollection).
- *
- * @since 8.1
- */
-@Incubating
-fun ConfigurableFileCollection.assign(fileCollection: FileCollection) {
-    emitIncubatingLogMessage()
-    this.setFrom(fileCollection)
-}
-
-
-private
-fun emitIncubatingLogMessage() = IncubationLogger.incubatingFeatureUsed("Kotlin DSL property assignment")
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ProjectExtensions.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ProjectExtensions.kt
index 01f4ab6..4ae88a9 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ProjectExtensions.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/ProjectExtensions.kt
@@ -30,11 +30,12 @@
 import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactoryInternal
 import org.gradle.api.internal.file.FileCollectionInternal
 
-import org.gradle.api.plugins.Convention
 import org.gradle.api.plugins.PluginAware
 
 import org.gradle.api.tasks.TaskContainer
+import org.gradle.internal.Factory
 import org.gradle.internal.component.local.model.OpaqueComponentIdentifier
+import org.gradle.internal.deprecation.DeprecationLogger
 
 import org.gradle.kotlin.dsl.provider.fileCollectionOf
 import org.gradle.kotlin.dsl.provider.gradleKotlinDslOf
@@ -78,50 +79,58 @@
 
 
 /**
- * Executes the given configuration block against the [plugin convention]
- * [Convention.getPlugin] or extension of the specified type.
+ * Executes the given configuration block against the [project extension]
+ * [org.gradle.api.plugins.ExtensionAware] of the specified type.
  *
+ * If no extension is found, configures a project convention if available.
  * Note, that the concept of conventions is deprecated and scheduled for
- * removal in Gradle 8.
+ * removal in Gradle 9.
  *
- * @param T the plugin convention type.
+ * @param T the project extension type.
  * @param configuration the configuration block.
- * @see [Convention.getPlugin]
+ * @see [org.gradle.api.plugins.ExtensionAware]
  */
 inline fun <reified T : Any> Project.configure(noinline configuration: T.() -> Unit): Unit =
     @Suppress("deprecation")
     typeOf<T>().let { type ->
-        convention.findByType(type)?.let(configuration)
-            ?: convention.findPlugin<T>()?.let(configuration)
-            ?: convention.configure(type, configuration)
+        val c: org.gradle.api.plugins.Convention = DeprecationLogger.whileDisabled(Factory { convention })!!
+        c.findByType(type)?.let(configuration) ?: c.findPlugin<T>()?.let(configuration) ?: c.configure(type, configuration)
     }
 
 
 /**
- * Returns the plugin convention or extension of the specified type.
+ * Returns the [project extension][org.gradle.api.plugins.ExtensionAware] of the specified type.
  *
+ * If no extension is found, returns a project convention if available.
  * Note, that the concept of conventions is deprecated and scheduled for
- * removal in Gradle 8.
+ * removal in Gradle 9.
+ *
+ * @param T the project extension type.
+ * @see [org.gradle.api.plugins.ExtensionAware]
  */
 inline fun <reified T : Any> Project.the(): T =
     @Suppress("deprecation")
     typeOf<T>().let { type ->
-        convention.findByType(type)
-            ?: convention.findPlugin(T::class.java)
-            ?: convention.getByType(type)
+        val c: org.gradle.api.plugins.Convention = DeprecationLogger.whileDisabled(Factory { convention })!!
+        c.findByType(type) ?: c.findPlugin(T::class.java) ?: c.getByType(type)
     }
 
 
 /**
- * Returns the plugin convention or extension of the specified type.
+ * Returns the [project extension][org.gradle.api.plugins.ExtensionAware] of the specified type.
  *
+ * If no extension is found, returns a project convention if available.
  * Note, that the concept of conventions is deprecated and scheduled for
- * removal in Gradle 8.
+ * removal in Gradle 9.
+ *
+ * @param T the project extension type.
+ * @see [org.gradle.api.plugins.ExtensionAware]
  */
-fun <T : Any> Project.the(extensionType: KClass<T>): T =
-    @Suppress("deprecation") convention.findByType(extensionType.java)
-        ?: @Suppress("deprecation") convention.findPlugin(extensionType.java)
-        ?: @Suppress("deprecation") convention.getByType(extensionType.java)
+@Suppress("deprecation")
+fun <T : Any> Project.the(extensionType: KClass<T>): T {
+    val c = DeprecationLogger.whileDisabled(Factory { convention })!!
+    return c.findByType(extensionType.java) ?: c.findPlugin(extensionType.java) ?: c.getByType(extensionType.java)
+}
 
 
 /**
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/PropertyExtensions.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/PropertyExtensions.kt
new file mode 100644
index 0000000..1545a9c
--- /dev/null
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/PropertyExtensions.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 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.
+ */
+
+@file:Incubating
+package org.gradle.kotlin.dsl
+
+import org.gradle.api.Incubating
+import org.gradle.api.file.FileSystemLocation
+import org.gradle.api.file.FileSystemLocationProperty
+import org.gradle.api.provider.HasMultipleValues
+import org.gradle.api.provider.MapProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.provider.Provider
+import org.gradle.kotlin.dsl.assignment.internal.KotlinDslAssignment
+import java.io.File
+
+
+/**
+ * Assign value: T to a property with assign operator
+ *
+ * @since 8.2
+ */
+@Incubating
+fun <T> Property<T>.assign(value: T?) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.set(value)
+}
+
+
+/**
+ * Assign value: Provider<T> to a property with assign operator
+ *
+ * @since 8.2
+ */
+@Incubating
+fun <T> Property<T>.assign(value: Provider<out T?>) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.set(value)
+}
+
+
+/**
+ * Assign file to a FileSystemLocationProperty with assign operator
+ *
+ * @since 8.2
+ */
+@Incubating
+fun <T : FileSystemLocation> FileSystemLocationProperty<T>.assign(file: File?) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.set(file)
+}
+
+
+/**
+ * Assign file provided by a Provider to a FileSystemLocationProperty with assign operator
+ *
+ * @since 8.2
+ */
+@Incubating
+fun <T : FileSystemLocation> FileSystemLocationProperty<T>.assign(provider: Provider<File?>) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.fileProvider(provider)
+}
+
+
+/**
+ * Sets the value of the property to the elements of the given iterable, and replaces any existing value
+ *
+ * @since 8.2
+ */
+@Incubating
+fun <T> HasMultipleValues<T>.assign(elements: Iterable<T?>?) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.set(elements)
+}
+
+
+/**
+ * Sets the property to have the same value of the given provider, and replaces any existing value
+ *
+ * @since 8.2
+ */
+@Incubating
+fun <T> HasMultipleValues<T>.assign(provider: Provider<out Iterable<T?>?>) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.set(provider)
+}
+
+
+/**
+ * Sets the value of this property to the entries of the given Map, and replaces any existing value
+ *
+ * @since 8.2
+ */
+@Incubating
+fun <K, V> MapProperty<K, V>.assign(entries: Map<out K?, V?>?) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.set(entries)
+}
+
+
+/**
+ * Sets the property to have the same value of the given provider, and replaces any existing value
+ *
+ * @since 8.2
+ */
+@Incubating
+fun <K, V> MapProperty<K, V>.assign(provider: Provider<out Map<out K?, V?>?>) {
+    KotlinDslAssignment.emitIncubatingLogMessage()
+    this.set(provider)
+}
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/runtime/Runtime.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/runtime/Runtime.kt
index 1b6d324..6d5ad60 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/runtime/Runtime.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/accessors/runtime/Runtime.kt
@@ -21,10 +21,11 @@
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.ExternalModuleDependency
 import org.gradle.api.artifacts.dsl.DependencyHandler
-import org.gradle.api.plugins.Convention
 import org.gradle.api.plugins.ExtensionAware
 import org.gradle.api.provider.Provider
 import org.gradle.api.provider.ProviderConvertible
+import org.gradle.internal.Factory
+import org.gradle.internal.deprecation.DeprecationLogger
 
 import org.gradle.kotlin.dsl.support.mapOfNonNullValuesOf
 import org.gradle.kotlin.dsl.support.uncheckedCast
@@ -39,14 +40,15 @@
     conventionPluginByName(conventionOf(target), name)
 
 
-fun conventionPluginByName(convention: Convention, name: String): Any =
+@Suppress("deprecation")
+fun conventionPluginByName(convention: org.gradle.api.plugins.Convention, name: String): Any =
     convention.plugins[name] ?: throw IllegalStateException("A convention named '$name' could not be found.")
 
 
 @Suppress("deprecation")
-fun conventionOf(target: Any): Convention = when (target) {
-    is Project -> target.convention
-    is org.gradle.api.internal.HasConvention -> target.convention
+fun conventionOf(target: Any): org.gradle.api.plugins.Convention = when (target) {
+    is Project -> DeprecationLogger.whileDisabled(Factory { target.convention })!!
+    is org.gradle.api.internal.HasConvention -> DeprecationLogger.whileDisabled(Factory { target.convention })!!
     else -> throw IllegalStateException("Object `$target` doesn't support conventions!")
 }
 
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/assignment/internal/KotlinDslAssignment.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/assignment/internal/KotlinDslAssignment.kt
index f8cfdb5..db9701e 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/assignment/internal/KotlinDslAssignment.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/assignment/internal/KotlinDslAssignment.kt
@@ -16,11 +16,21 @@
 
 package org.gradle.kotlin.dsl.assignment.internal
 
+import org.gradle.util.internal.IncubationLogger
+
 
 object KotlinDslAssignment {
 
     const val ASSIGNMENT_SYSTEM_PROPERTY = "org.gradle.unsafe.kotlin.assignment"
 
     fun isAssignmentOverloadEnabled() =
-        System.getProperty(ASSIGNMENT_SYSTEM_PROPERTY, "false").trim() == "true"
+        System.getProperty(ASSIGNMENT_SYSTEM_PROPERTY, "true").trim() != "false"
+
+    internal
+    fun emitIncubatingLogMessage() {
+        if (isAssignmentOverloadEnabled()) {
+            // If the assignment overload is disabled for a project but some plugin was compiled using it, we should not do any logging.
+            IncubationLogger.incubatingFeatureUsed("Kotlin DSL property assignment")
+        }
+    }
 }
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/codegen/ApiExtensionsJar.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/codegen/ApiExtensionsJar.kt
index 0e7f46a..fa42f32 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/codegen/ApiExtensionsJar.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/codegen/ApiExtensionsJar.kt
@@ -21,6 +21,7 @@
 import org.gradle.kotlin.dsl.support.zipTo
 
 import org.gradle.api.internal.file.temp.TemporaryFileProvider
+import org.gradle.kotlin.dsl.support.EmbeddedKotlinCompilerWarning
 import org.gradle.kotlin.dsl.support.bytecode.GradleJvmVersion
 
 import java.io.File
@@ -120,7 +121,8 @@
         "gradle-api-extensions",
         sourceFiles,
         logger,
-        classPath = classPath
+        classPath = classPath,
+        onCompilerWarning = EmbeddedKotlinCompilerWarning.DEBUG
     )
 
     if (!success) {
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/Interpreter.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/Interpreter.kt
index 0a84c85..2ff2250 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/Interpreter.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/Interpreter.kt
@@ -32,12 +32,10 @@
 import org.gradle.internal.exceptions.LocationAwareException
 import org.gradle.internal.hash.HashCode
 import org.gradle.internal.service.ServiceRegistry
-import org.gradle.kotlin.dsl.accessors.ProjectAccessorsClassPathGenerator
 import org.gradle.kotlin.dsl.assignment.internal.KotlinDslAssignment
 import org.gradle.kotlin.dsl.support.KotlinScriptHost
 import org.gradle.kotlin.dsl.support.ScriptCompilationException
 import org.gradle.kotlin.dsl.support.loggerFor
-import org.gradle.kotlin.dsl.support.serviceOf
 import org.gradle.kotlin.dsl.support.serviceRegistryOf
 import org.gradle.plugin.management.internal.PluginRequests
 import java.io.File
@@ -110,6 +108,10 @@
             scriptHost: KotlinScriptHost<*>
         ): ClassPath
 
+        fun accessorsClassPathFor(
+            scriptHost: KotlinScriptHost<*>
+        ): ClassPath
+
         fun loadClassInChildScopeOf(
             classLoaderScope: ClassLoaderScope,
             childScopeId: String,
@@ -140,6 +142,8 @@
 
         val jvmTarget: JavaVersion
 
+        val allWarningsAsErrors: Boolean
+
         fun serviceRegistryFor(programTarget: ProgramTarget, target: Any): ServiceRegistry = when (programTarget) {
             ProgramTarget.Project -> serviceRegistryOf(target as Project)
             ProgramTarget.Settings -> serviceRegistryOf(target as Settings)
@@ -252,8 +256,8 @@
 
         // TODO: consider computing stage 1 accessors only when there's a buildscript or plugins block
         // TODO: consider splitting buildscript/plugins block accessors
-        val stage1BlocksAccessorsClassPath = when {
-            requiresAccessors(programTarget, programKind) -> host.stage1BlocksAccessorsFor(scriptHost)
+        val stage1BlocksAccessorsClassPath = when (programTarget) {
+            ProgramTarget.Project -> host.stage1BlocksAccessorsFor(scriptHost)
             else -> ClassPath.EMPTY
         }
 
@@ -317,6 +321,7 @@
                 ResidualProgramCompiler(
                     outputDir = cachedDir,
                     jvmTarget = host.jvmTarget,
+                    allWarningsAsErrors = host.allWarningsAsErrors,
                     classPath = compilationClassPath,
                     originalSourceHash = programId.sourceHash,
                     programKind = programKind,
@@ -439,14 +444,8 @@
             eval(specializedProgram, scriptHost)
         }
 
-        override fun accessorsClassPathFor(scriptHost: KotlinScriptHost<*>): ClassPath {
-            val project = scriptHost.target as Project
-            val projectAccessorsClassPathGenerator = project.serviceOf<ProjectAccessorsClassPathGenerator>()
-            return projectAccessorsClassPathGenerator.projectAccessorsClassPath(
-                project,
-                host.compilationClassPathOf(scriptHost.targetScope)
-            ).bin
-        }
+        override fun accessorsClassPathFor(scriptHost: KotlinScriptHost<*>): ClassPath =
+            host.accessorsClassPathFor(scriptHost)
 
         override fun compileSecondStageOf(
             program: ExecutableProgram.StagedProgram,
@@ -482,6 +481,7 @@
                                 ResidualProgramCompiler(
                                     outputDir,
                                     host.jvmTarget,
+                                    host.allWarningsAsErrors,
                                     compilationClassPath,
                                     sourceHash,
                                     programKind,
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/PartialEvaluator.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/PartialEvaluator.kt
index f4c03aea..ca18c53 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/PartialEvaluator.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/PartialEvaluator.kt
@@ -98,20 +98,11 @@
 
         when (programTarget) {
 
-            ProgramTarget.Project -> when (programKind) {
-
-                ProgramKind.TopLevel -> Static(
-                    SetupEmbeddedKotlin,
-                    CollectProjectScriptDependencies(fragmentHolderSourceFor(program)),
-                    defaultStageTransition()
-                )
-
-                ProgramKind.ScriptPlugin -> Static(
-                    SetupEmbeddedKotlin,
-                    Eval(fragmentHolderSourceFor(program)),
-                    defaultStageTransition()
-                )
-            }
+            ProgramTarget.Project -> Static(
+                SetupEmbeddedKotlin,
+                CollectProjectScriptDependencies(fragmentHolderSourceFor(program)),
+                defaultStageTransition()
+            )
 
             else -> Static(
                 SetupEmbeddedKotlin,
@@ -187,23 +178,12 @@
 
             when (programTarget) {
 
-                ProgramTarget.Project ->
-
-                    when (programKind) {
-                        ProgramKind.TopLevel -> Static(
-                            SetupEmbeddedKotlin,
-                            CollectProjectScriptDependencies(fragmentHolderSourceFor(stage1)),
-                            ApplyDefaultPluginRequests,
-                            ApplyBasePlugins
-                        )
-
-                        ProgramKind.ScriptPlugin -> Static(
-                            SetupEmbeddedKotlin,
-                            Eval(fragmentHolderSourceFor(stage1)),
-                            ApplyDefaultPluginRequests,
-                            ApplyBasePlugins
-                        )
-                    }
+                ProgramTarget.Project -> Static(
+                    SetupEmbeddedKotlin,
+                    CollectProjectScriptDependencies(fragmentHolderSourceFor(stage1)),
+                    ApplyDefaultPluginRequests,
+                    ApplyBasePlugins
+                )
 
                 else -> reduceBuildscriptProgram(stage1)
             }
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/ResidualProgramCompiler.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/ResidualProgramCompiler.kt
index 530b9a0..2f63d80 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/ResidualProgramCompiler.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/execution/ResidualProgramCompiler.kt
@@ -85,6 +85,7 @@
 class ResidualProgramCompiler(
     private val outputDir: File,
     private val jvmTarget: JavaVersion,
+    private val allWarningsAsErrors: Boolean,
     private val classPath: ClassPath = ClassPath.EMPTY,
     private val originalSourceHash: HashCode,
     private val programKind: ProgramKind,
@@ -486,7 +487,8 @@
         LDC(programTarget.name + "/" + programKind.name + "/stage2")
         // Move HashCode value to a static field so it's cached across invocations
         loadHashCode(originalSourceHash)
-        if (requiresAccessors()) emitAccessorsClassPathForScriptHost() else GETSTATIC(ClassPath::EMPTY)
+        if (requiresSecondStageAccessors(programTarget, programKind)) emitAccessorsClassPathForScriptHost()
+        else GETSTATIC(ClassPath::EMPTY)
         invokeHost(
             ExecutableProgram.Host::evaluateSecondStageOf.name,
             "(" +
@@ -500,6 +502,10 @@
     }
 
     private
+    fun requiresSecondStageAccessors(programTarget: ProgramTarget, programKind: ProgramKind) =
+        programTarget == ProgramTarget.Project && programKind == ProgramKind.TopLevel
+
+    private
     val stagedProgram = Type.getType(ExecutableProgram.StagedProgram::class.java)
 
     private
@@ -526,10 +532,6 @@
     )
 
     private
-    fun requiresAccessors() =
-        requiresAccessors(programTarget, programKind)
-
-    private
     fun MethodVisitor.emitAccessorsClassPathForScriptHost() {
         ALOAD(Vars.ProgramHost)
         ALOAD(Vars.ScriptHost)
@@ -713,7 +715,7 @@
                 scriptFile,
                 scriptDefinition,
                 compileClassPath.asFiles,
-                messageCollectorFor(logger) { path ->
+                messageCollectorFor(logger, allWarningsAsErrors) { path ->
                     if (path == scriptFile.path) originalPath
                     else path
                 }
@@ -789,8 +791,3 @@
     fun implicitReceiverOf(template: KClass<*>) =
         template.annotations.filterIsInstance<ImplicitReceiver>().map { it.type }.firstOrNull()
 }
-
-
-internal
-fun requiresAccessors(programTarget: ProgramTarget, programKind: ProgramKind) =
-    programTarget == ProgramTarget.Project && programKind == ProgramKind.TopLevel
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/normalization/KotlinCompileClasspathFingerprinter.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/normalization/KotlinCompileClasspathFingerprinter.kt
index 52d113d..3a19df9 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/normalization/KotlinCompileClasspathFingerprinter.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/normalization/KotlinCompileClasspathFingerprinter.kt
@@ -19,11 +19,16 @@
 import org.gradle.api.internal.cache.StringInterner
 import org.gradle.api.internal.changedetection.state.AbiExtractingClasspathResourceHasher
 import org.gradle.api.internal.changedetection.state.CachingResourceHasher
+import org.gradle.api.internal.changedetection.state.PropertiesFileFilter
+import org.gradle.api.internal.changedetection.state.ResourceEntryFilter
+import org.gradle.api.internal.changedetection.state.ResourceFilter
 import org.gradle.api.internal.changedetection.state.ResourceSnapshotterCacheService
+import org.gradle.api.internal.changedetection.state.RuntimeClasspathResourceHasher
 import org.gradle.api.internal.changedetection.state.ZipHasher
 import org.gradle.internal.execution.FileCollectionSnapshotter
 import org.gradle.internal.execution.model.InputNormalizer
 import org.gradle.internal.fingerprint.FileNormalizer
+import org.gradle.internal.fingerprint.LineEndingSensitivity
 import org.gradle.internal.fingerprint.classpath.CompileClasspathFingerprinter
 import org.gradle.internal.fingerprint.classpath.impl.ClasspathFingerprintingStrategy
 import org.gradle.internal.fingerprint.impl.AbstractFileCollectionFingerprinter
@@ -37,8 +42,18 @@
     fileCollectionSnapshotter: FileCollectionSnapshotter,
     stringInterner: StringInterner
 ) : AbstractFileCollectionFingerprinter(
-    ClasspathFingerprintingStrategy.compileClasspath(
-        CachingResourceHasher(AbiExtractingClasspathResourceHasher.withoutFallback(KotlinApiClassExtractor()), cacheService),
+    ClasspathFingerprintingStrategy.compileClasspathFallbackToRuntimeClasspath(
+        CachingResourceHasher(
+            AbiExtractingClasspathResourceHasher.withoutFallback(KotlinApiClassExtractor()),
+            cacheService
+        ),
+        ClasspathFingerprintingStrategy.runtimeClasspathResourceHasher(
+            RuntimeClasspathResourceHasher(),
+            LineEndingSensitivity.DEFAULT,
+            PropertiesFileFilter.FILTER_NOTHING,
+            ResourceEntryFilter.FILTER_NOTHING,
+            ResourceFilter.FILTER_NOTHING
+        ),
         cacheService,
         stringInterner,
         CompileAvoidanceExceptionReporter()
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/precompile/v1/PrecompiledScriptTemplates.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/precompile/v1/PrecompiledScriptTemplates.kt
index 0145745..990fc8b 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/precompile/v1/PrecompiledScriptTemplates.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/precompile/v1/PrecompiledScriptTemplates.kt
@@ -99,9 +99,9 @@
      * @see [PluginDependenciesSpec]
      */
     @Suppress("unused")
-    fun plugins(block: PluginDependenciesSpecScope.() -> Unit) {
+    fun plugins(block: PluginDependenciesSpec.() -> Unit) {
         block(
-            PluginDependenciesSpecScope(object : PluginDependenciesSpec {
+            object : PluginDependenciesSpec {
                 override fun id(id: String): PluginDependencySpec {
                     pluginManager.apply(id)
                     return NullPluginDependencySpec
@@ -115,7 +115,7 @@
                 override fun alias(notation: ProviderConvertible<PluginDependency>): PluginDependencySpec {
                     return alias(notation.asProvider())
                 }
-            })
+            }
         )
     }
 }
@@ -168,9 +168,9 @@
      * @see [PluginDependenciesSpec]
      */
     @Suppress("unused")
-    fun plugins(block: PluginDependenciesSpecScope.() -> Unit) {
+    fun plugins(block: PluginDependenciesSpec.() -> Unit) {
         block(
-            PluginDependenciesSpecScope(object : PluginDependenciesSpec {
+            object : PluginDependenciesSpec {
                 override fun id(id: String): PluginDependencySpec {
                     pluginManager.apply(id)
                     return NullPluginDependencySpec
@@ -184,7 +184,7 @@
                 override fun alias(notation: ProviderConvertible<PluginDependency>): PluginDependencySpec {
                     return alias(notation.asProvider())
                 }
-            })
+            }
         )
     }
 }
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/BuildServices.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/BuildServices.kt
index 13be148..5edf3b6 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/BuildServices.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/BuildServices.kt
@@ -27,6 +27,7 @@
 import org.gradle.cache.internal.GeneratedGradleJarCache
 import org.gradle.groovy.scripts.internal.ScriptSourceHasher
 import org.gradle.initialization.ClassLoaderScopeRegistry
+import org.gradle.initialization.GradlePropertiesController
 import org.gradle.internal.classloader.ClasspathHasher
 import org.gradle.internal.classpath.CachedClasspathTransformer
 import org.gradle.internal.event.ListenerManager
@@ -46,7 +47,8 @@
 
 
 internal
-const val BUILDSCRIPT_COMPILE_AVOIDANCE_ENABLED = true
+const val KOTLIN_SCRIPT_COMPILATION_AVOIDANCE_ENABLED_PROPERTY =
+    "org.gradle.kotlin.dsl.scriptCompilationAvoidance"
 
 
 internal
@@ -105,7 +107,8 @@
         workspaceProvider: KotlinDslWorkspaceProvider,
         @Suppress("UNUSED_PARAMETER") kotlinCompilerContextDisposer: KotlinCompilerContextDisposer,
         fileCollectionFactory: FileCollectionFactory,
-        inputFingerprinter: InputFingerprinter
+        inputFingerprinter: InputFingerprinter,
+        gradlePropertiesController: GradlePropertiesController,
     ): KotlinScriptEvaluator =
 
         StandardKotlinScriptEvaluator(
@@ -126,7 +129,8 @@
             executionEngine,
             workspaceProvider,
             fileCollectionFactory,
-            inputFingerprinter
+            inputFingerprinter,
+            gradlePropertiesController,
         )
 
     @Suppress("unused")
@@ -138,7 +142,7 @@
         classpathFingerprinter: ClasspathFingerprinter
     ) =
         DefaultClasspathHasher(
-            if (BUILDSCRIPT_COMPILE_AVOIDANCE_ENABLED) {
+            if (isKotlinScriptCompilationAvoidanceEnabled) {
                 KotlinCompileClasspathFingerprinter(
                     cacheService,
                     fileCollectionSnapshotter,
@@ -157,4 +161,8 @@
     private
     fun versionedJarCacheFor(jarCache: GeneratedGradleJarCache): JarCache =
         { id, creator -> jarCache[id, creator] }
+
+    private
+    val isKotlinScriptCompilationAvoidanceEnabled: Boolean
+        get() = System.getProperty(KOTLIN_SCRIPT_COMPILATION_AVOIDANCE_ENABLED_PROPERTY, "true") == "true"
 }
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptEvaluator.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptEvaluator.kt
index d3b7ccc..5f60355 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptEvaluator.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptEvaluator.kt
@@ -28,6 +28,7 @@
 import org.gradle.groovy.scripts.ScriptSource
 import org.gradle.groovy.scripts.internal.ScriptSourceHasher
 import org.gradle.initialization.ClassLoaderScopeOrigin
+import org.gradle.initialization.GradlePropertiesController
 import org.gradle.internal.classloader.ClasspathHasher
 import org.gradle.internal.classpath.CachedClasspathTransformer
 import org.gradle.internal.classpath.CachedClasspathTransformer.StandardTransform.BuildLogic
@@ -51,6 +52,7 @@
 import org.gradle.internal.scripts.CompileScriptBuildOperationType.Result
 import org.gradle.internal.scripts.ScriptExecutionListener
 import org.gradle.internal.snapshot.ValueSnapshot
+import org.gradle.kotlin.dsl.accessors.ProjectAccessorsClassPathGenerator
 import org.gradle.kotlin.dsl.accessors.Stage1BlocksAccessorClassPathGenerator
 import org.gradle.kotlin.dsl.cache.KotlinDslWorkspaceProvider
 import org.gradle.kotlin.dsl.execution.CompiledScript
@@ -101,7 +103,8 @@
     private val executionEngine: ExecutionEngine,
     private val workspaceProvider: KotlinDslWorkspaceProvider,
     private val fileCollectionFactory: FileCollectionFactory,
-    private val inputFingerprinter: InputFingerprinter
+    private val inputFingerprinter: InputFingerprinter,
+    private val gradlePropertiesController: GradlePropertiesController,
 ) : KotlinScriptEvaluator {
 
     override fun evaluate(
@@ -149,11 +152,19 @@
         JavaVersion.current()
 
     private
-    val interpreter by lazy {
-        Interpreter(InterpreterHost(jvmTarget))
+    val allWarningsAsErrors: Boolean by lazy {
+        gradlePropertiesController.gradleProperties.find("org.gradle.kotlin.dsl.allWarningsAsErrors") == "true"
     }
 
-    inner class InterpreterHost(override val jvmTarget: JavaVersion) : Interpreter.Host {
+    private
+    val interpreter by lazy {
+        Interpreter(InterpreterHost(jvmTarget, allWarningsAsErrors))
+    }
+
+    inner class InterpreterHost(
+        override val jvmTarget: JavaVersion,
+        override val allWarningsAsErrors: Boolean,
+    ) : Interpreter.Host {
 
         override fun stage1BlocksAccessorsFor(scriptHost: KotlinScriptHost<*>): ClassPath =
             (scriptHost.target as? ProjectInternal)?.let {
@@ -161,6 +172,15 @@
                 stage1BlocksAccessorClassPathGenerator.stage1BlocksAccessorClassPath(it).bin
             } ?: ClassPath.EMPTY
 
+        override fun accessorsClassPathFor(scriptHost: KotlinScriptHost<*>): ClassPath {
+            val project = scriptHost.target as Project
+            val projectAccessorsClassPathGenerator = project.serviceOf<ProjectAccessorsClassPathGenerator>()
+            return projectAccessorsClassPathGenerator.projectAccessorsClassPath(
+                project,
+                compilationClassPathOf(scriptHost.targetScope)
+            ).bin
+        }
+
         override fun runCompileBuildOperation(scriptPath: String, stage: String, action: () -> String): String =
 
             buildOperationExecutor.call(object : CallableBuildOperation<String> {
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptPluginFactory.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptPluginFactory.kt
index 5c078b1..77a95be 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptPluginFactory.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/provider/KotlinScriptPluginFactory.kt
@@ -16,7 +16,6 @@
 
 package org.gradle.kotlin.dsl.provider
 
-import org.gradle.StartParameter
 import org.gradle.api.initialization.dsl.ScriptHandler
 import org.gradle.api.internal.initialization.ClassLoaderScope
 
@@ -24,7 +23,6 @@
 import org.gradle.configuration.ScriptPluginFactory
 
 import org.gradle.groovy.scripts.ScriptSource
-import org.gradle.internal.deprecation.DeprecationLogger
 
 import org.gradle.kotlin.dsl.execution.EvalOption
 import org.gradle.kotlin.dsl.execution.defaultEvalOptions
@@ -36,8 +34,7 @@
 
 @Suppress("unused") // The name of this class is hardcoded in Gradle
 class KotlinScriptPluginFactory @Inject internal constructor(
-    private val kotlinScriptEvaluator: KotlinScriptEvaluator,
-    private val startParameter: StartParameter
+    private val kotlinScriptEvaluator: KotlinScriptEvaluator
 ) : ScriptPluginFactory {
 
     override fun create(
@@ -46,12 +43,10 @@
         targetScope: ClassLoaderScope,
         baseScope: ClassLoaderScope,
         topLevelScript: Boolean
-    ): ScriptPlugin {
-        if (startParameter.isConfigureOnDemand) {
-            warnAboutConfigurationOnDemand()
-        }
+    ): ScriptPlugin =
 
-        return KotlinScriptPlugin(scriptSource) { target ->
+        KotlinScriptPlugin(scriptSource) { target ->
+
             kotlinScriptEvaluator
                 .evaluate(
                     target,
@@ -63,20 +58,11 @@
                     kotlinScriptOptions()
                 )
         }
-    }
 
     private
     fun kotlinScriptOptions(): EnumSet<EvalOption> =
         if (inLenientMode()) lenientModeScriptOptions
         else defaultEvalOptions
-
-    private
-    fun warnAboutConfigurationOnDemand() {
-        DeprecationLogger.deprecateBehaviour("Using the configuration on demand feature with the Kotlin DSL.")
-            .willBecomeAnErrorInGradle9()
-            .withUserManual("kotlin_dsl", "kotdsl:limitations")
-            .nagUser()
-    }
 }
 
 
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/KotlinCompiler.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/KotlinCompiler.kt
index 6a04fbf..e50785a 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/KotlinCompiler.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/KotlinCompiler.kt
@@ -112,6 +112,7 @@
     scriptDef: ScriptDefinition,
     classPath: Iterable<File>,
     logger: Logger,
+    allWarningsAsErrors: Boolean,
     pathTranslation: (String) -> String
 ) = compileKotlinScriptModuleTo(
     outputDirectory,
@@ -120,7 +121,7 @@
     scriptFiles,
     scriptDef,
     classPath,
-    LoggingMessageCollector(logger, pathTranslation)
+    LoggingMessageCollector(logger, onCompilerWarningsFor(allWarningsAsErrors), pathTranslation)
 )
 
 
@@ -241,11 +242,12 @@
     moduleName: String,
     sourceFiles: Iterable<File>,
     logger: Logger,
-    classPath: Iterable<File>
+    classPath: Iterable<File>,
+    onCompilerWarning: EmbeddedKotlinCompilerWarning = EmbeddedKotlinCompilerWarning.WARN,
 ): Boolean {
 
     withRootDisposable {
-        withMessageCollectorFor(logger) { messageCollector ->
+        withMessageCollectorFor(logger, onCompilerWarning) { messageCollector ->
             val configuration = compilerConfigurationFor(messageCollector, jvmTarget).apply {
                 addKotlinSourceRoots(sourceFiles.map { it.canonicalPath })
                 put(OUTPUT_DIRECTORY, outputDirectory)
@@ -277,8 +279,8 @@
 
 
 private
-inline fun <T> withMessageCollectorFor(log: Logger, action: (MessageCollector) -> T): T {
-    val messageCollector = messageCollectorFor(log)
+inline fun <T> withMessageCollectorFor(log: Logger, onCompilerWarning: EmbeddedKotlinCompilerWarning, action: (MessageCollector) -> T): T {
+    val messageCollector = messageCollectorFor(log, onCompilerWarning)
     withCompilationExceptionHandler(messageCollector) {
         return action(messageCollector)
     }
@@ -386,7 +388,7 @@
 
 internal
 fun JavaVersion.toKotlinJvmTarget(): JvmTarget {
-    // JvmTarget.fromString(JavaVersion.majorVersion) works from Java 9 to Java 18
+    // JvmTarget.fromString(JavaVersion.majorVersion) works from Java 9 to Java 19
     return JvmTarget.fromString(majorVersion)
         ?: if (this <= JavaVersion.VERSION_1_8) JVM_1_8
         else JVM_19
@@ -454,8 +456,21 @@
 
 
 internal
-fun messageCollectorFor(log: Logger, pathTranslation: (String) -> String = { it }): LoggingMessageCollector =
-    LoggingMessageCollector(log, pathTranslation)
+fun messageCollectorFor(
+    log: Logger,
+    allWarningsAsErrors: Boolean,
+    pathTranslation: (String) -> String,
+): LoggingMessageCollector =
+    messageCollectorFor(log, onCompilerWarningsFor(allWarningsAsErrors), pathTranslation)
+
+
+internal
+fun messageCollectorFor(
+    log: Logger,
+    onCompilerWarning: EmbeddedKotlinCompilerWarning = EmbeddedKotlinCompilerWarning.WARN,
+    pathTranslation: (String) -> String = { it }
+): LoggingMessageCollector =
+    LoggingMessageCollector(log, onCompilerWarning, pathTranslation)
 
 
 internal
@@ -529,9 +544,22 @@
 
 
 internal
+enum class EmbeddedKotlinCompilerWarning {
+    FAIL, WARN, DEBUG
+}
+
+
+private
+fun onCompilerWarningsFor(allWarningsAsErrors: Boolean) =
+    if (allWarningsAsErrors) EmbeddedKotlinCompilerWarning.FAIL
+    else EmbeddedKotlinCompilerWarning.WARN
+
+
+internal
 class LoggingMessageCollector(
     internal val log: Logger,
-    private val pathTranslation: (String) -> String
+    private val onCompilerWarning: EmbeddedKotlinCompilerWarning,
+    private val pathTranslation: (String) -> String,
 ) : MessageCollector {
 
     val errors = arrayListOf<ScriptCompilationError>()
@@ -555,15 +583,24 @@
         fun taggedMsg() =
             "${severity.presentableName[0]}: ${msg()}"
 
-        when (severity) {
-            CompilerMessageSeverity.ERROR, CompilerMessageSeverity.EXCEPTION -> {
-                errors += ScriptCompilationError(message, location)
-                log.error { taggedMsg() }
-            }
+        fun onError() {
+            errors += ScriptCompilationError(message, location)
+            log.error { taggedMsg() }
+        }
 
+        fun onWarning() {
+            when (onCompilerWarning) {
+                EmbeddedKotlinCompilerWarning.FAIL -> onError()
+                EmbeddedKotlinCompilerWarning.WARN -> log.warn { taggedMsg() }
+                EmbeddedKotlinCompilerWarning.DEBUG -> log.debug { taggedMsg() }
+            }
+        }
+
+        when (severity) {
+            CompilerMessageSeverity.ERROR, CompilerMessageSeverity.EXCEPTION -> onError()
             in CompilerMessageSeverity.VERBOSE -> log.trace { msg() }
-            CompilerMessageSeverity.STRONG_WARNING -> log.warn { taggedMsg() }
-            CompilerMessageSeverity.WARNING -> log.warn { taggedMsg() }
+            CompilerMessageSeverity.STRONG_WARNING -> onWarning()
+            CompilerMessageSeverity.WARNING -> onWarning()
             CompilerMessageSeverity.INFO -> log.info { msg() }
             else -> log.debug { taggedMsg() }
         }
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/delegates/ProjectDelegate.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/delegates/ProjectDelegate.kt
index cb74092..0a35009 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/delegates/ProjectDelegate.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/support/delegates/ProjectDelegate.kt
@@ -44,7 +44,6 @@
 import org.gradle.api.logging.Logger
 import org.gradle.api.logging.LoggingManager
 import org.gradle.api.model.ObjectFactory
-import org.gradle.api.plugins.Convention
 import org.gradle.api.plugins.ExtensionContainer
 import org.gradle.api.plugins.ObjectConfigurationAction
 import org.gradle.api.plugins.PluginContainer
@@ -97,7 +96,7 @@
         delegate.defaultTasks
 
     @Deprecated("The concept of conventions is deprecated. Use extensions instead.")
-    override fun getConvention(): Convention =
+    override fun getConvention(): @Suppress("deprecation") org.gradle.api.plugins.Convention =
         @Suppress("deprecation")
         delegate.convention
 
diff --git a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/template/KotlinBuildScriptTemplateAdditionalCompilerArgumentsProvider.kt b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/template/KotlinBuildScriptTemplateAdditionalCompilerArgumentsProvider.kt
index 4f48708..fad7456 100644
--- a/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/template/KotlinBuildScriptTemplateAdditionalCompilerArgumentsProvider.kt
+++ b/subprojects/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl/template/KotlinBuildScriptTemplateAdditionalCompilerArgumentsProvider.kt
@@ -51,7 +51,7 @@
                 load(it)
             }
             // This class is loaded from the IDE, so we have to hardcode system property
-            properties.getProperty("systemProp.org.gradle.unsafe.kotlin.assignment")?.trim() == "true"
+            properties.getProperty("systemProp.org.gradle.unsafe.kotlin.assignment", "true").trim() != "false"
         }
     }
 }
diff --git a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/ProjectExtensionsTest.kt b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/ProjectExtensionsTest.kt
index 25219a4..71c1a88 100644
--- a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/ProjectExtensionsTest.kt
+++ b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/ProjectExtensionsTest.kt
@@ -12,7 +12,6 @@
 import org.gradle.api.NamedDomainObjectFactory
 import org.gradle.api.Project
 import org.gradle.api.UnknownDomainObjectException
-import org.gradle.api.plugins.Convention
 import org.gradle.api.reflect.TypeOf
 
 import org.junit.Assert.fail
@@ -28,7 +27,7 @@
     fun `can get generic project extension by type`() {
 
         val project = mock<Project>()
-        val convention = mock<Convention>()
+        val convention = mock<org.gradle.api.plugins.Convention>()
         val extension = mock<NamedDomainObjectContainer<List<String>>>()
         val extensionType = typeOf<NamedDomainObjectContainer<List<String>>>()
 
@@ -49,7 +48,7 @@
     fun `can configure generic project extension by type`() {
 
         val project = mock<Project>()
-        val convention = mock<Convention>()
+        val convention = mock<org.gradle.api.plugins.Convention>()
         val extension = mock<NamedDomainObjectContainer<List<String>>>()
         val extensionType = typeOf<NamedDomainObjectContainer<List<String>>>()
 
@@ -70,7 +69,7 @@
     fun `can get convention by type`() {
 
         val project = mock<Project>()
-        val convention = mock<Convention>()
+        val convention = mock<org.gradle.api.plugins.Convention>()
         val javaConvention = mock<CustomConvention>()
 
         whenever(project.convention)
@@ -91,7 +90,7 @@
     fun `can configure convention by type`() {
 
         val project = mock<Project>()
-        val convention = mock<Convention>()
+        val convention = mock<org.gradle.api.plugins.Convention>()
         val javaConvention = mock<CustomConvention>()
 
         whenever(project.convention)
@@ -114,7 +113,7 @@
     fun `the() falls back to throwing getByType when not found`() {
 
         val project = mock<Project>()
-        val convention = mock<Convention>()
+        val convention = mock<org.gradle.api.plugins.Convention>()
         val conventionType = typeOf<CustomConvention>()
 
         whenever(project.convention)
@@ -141,7 +140,7 @@
     fun `configure() falls back to throwing configure when not found`() {
 
         val project = mock<Project>()
-        val convention = mock<Convention>()
+        val convention = mock<org.gradle.api.plugins.Convention>()
         val conventionType = typeOf<CustomConvention>()
 
         whenever(project.convention)
diff --git a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/accessors/ProjectAccessorsClassPathTest.kt b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/accessors/ProjectAccessorsClassPathTest.kt
index dd885e5..60dee7cc 100644
--- a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/accessors/ProjectAccessorsClassPathTest.kt
+++ b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/accessors/ProjectAccessorsClassPathTest.kt
@@ -30,16 +30,15 @@
 import org.gradle.api.Project
 import org.gradle.api.Task
 import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.DependencyConstraint
 import org.gradle.api.artifacts.ExternalModuleDependency
 import org.gradle.api.artifacts.ProjectDependency
 import org.gradle.api.artifacts.dsl.DependencyConstraintHandler
 import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import org.gradle.api.internal.plugins.ExtensionContainerInternal
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.tasks.TaskContainerInternal
-import org.gradle.api.plugins.Convention
 import org.gradle.api.reflect.TypeOf.parameterizedTypeOf
 import org.gradle.api.tasks.Delete
 import org.gradle.api.tasks.SourceSet
@@ -291,7 +290,7 @@
             )
 
         val apiConfiguration = mock<NamedDomainObjectProvider<Configuration>>()
-        val configurations = mock<ConfigurationContainer> {
+        val configurations = mock<RoleBasedConfigurationContainerInternal> {
             on { named(any<String>(), any<Class<Configuration>>()) } doReturn apiConfiguration
         }
         val sourceSet = mock<NamedDomainObjectProvider<SourceSet>>()
@@ -318,7 +317,8 @@
             on { named(any<String>(), eq(Delete::class.java)) } doReturn clean
         }
         val customConvention = mock<CustomConvention>()
-        val convention = mock<Convention> {
+        @Suppress("deprecation")
+        val convention = mock<org.gradle.api.plugins.Convention> {
             on { plugins } doReturn mapOf("customConvention" to customConvention)
         }
         val project = mock<ProjectInternal> {
@@ -444,11 +444,13 @@
             // val i
             @Suppress("deprecation")
             verify(project).convention
+            @Suppress("deprecation")
             verify(convention).plugins
 
             // val j
             @Suppress("deprecation")
             verify(project).convention
+            @Suppress("deprecation")
             verify(convention).plugins
 
             // val k
diff --git a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/InterpreterTest.kt b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/InterpreterTest.kt
index 1a32391..5ba8f93 100644
--- a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/InterpreterTest.kt
+++ b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/InterpreterTest.kt
@@ -101,8 +101,8 @@
         val stage1CacheDir = root.resolve("stage1").apply { mkdir() }
         val stage2CacheDir = root.resolve("stage2").apply { mkdir() }
 
-        val stage1ProgramId = ProgramId(stage1TemplateId, sourceHash, parentClassLoader)
-        val stage2ProgramId = ProgramId(stage2TemplateId, sourceHash, targetScopeExportClassLoader, null, compilationClassPathHash)
+        val stage1ProgramId = ProgramId(stage1TemplateId, sourceHash, parentClassLoader, assignmentOverloadEnabled = true)
+        val stage2ProgramId = ProgramId(stage2TemplateId, sourceHash, targetScopeExportClassLoader, null, compilationClassPathHash, assignmentOverloadEnabled = true)
 
         val mockServiceRegistry = mock<ServiceRegistry> {
             on { get(GradleUserHomeTemporaryFileProvider::class.java) } doReturn GradleUserHomeTemporaryFileProvider {
diff --git a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/PartialEvaluatorTest.kt b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/PartialEvaluatorTest.kt
index 28a0a8b..40807f9 100644
--- a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/PartialEvaluatorTest.kt
+++ b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/PartialEvaluatorTest.kt
@@ -428,7 +428,7 @@
             isResidualProgram(
                 Static(
                     SetupEmbeddedKotlin,
-                    Eval(fragment.source),
+                    CollectProjectScriptDependencies(fragment.source),
                     CloseTargetScope
                 )
             )
@@ -458,7 +458,7 @@
                 Dynamic(
                     Static(
                         SetupEmbeddedKotlin,
-                        Eval(buildscriptFragment.source),
+                        CollectProjectScriptDependencies(buildscriptFragment.source),
                         ApplyDefaultPluginRequests,
                         ApplyBasePlugins
                     ),
diff --git a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/TestWithCompiler.kt b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/TestWithCompiler.kt
index 9734871..861aa0f 100644
--- a/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/TestWithCompiler.kt
+++ b/subprojects/kotlin-dsl/src/test/kotlin/org/gradle/kotlin/dsl/execution/TestWithCompiler.kt
@@ -75,6 +75,7 @@
         ResidualProgramCompiler(
             outputDir,
             JavaVersion.current(),
+            false,
             testRuntimeClassPath,
             sourceHash,
             programKind,
diff --git a/subprojects/kotlin-dsl/src/testFixtures/kotlin/org/gradle/kotlin/dsl/fixtures/SimplifiedKotlinScriptEvaluator.kt b/subprojects/kotlin-dsl/src/testFixtures/kotlin/org/gradle/kotlin/dsl/fixtures/SimplifiedKotlinScriptEvaluator.kt
index 5add726..2d67d7d 100644
--- a/subprojects/kotlin-dsl/src/testFixtures/kotlin/org/gradle/kotlin/dsl/fixtures/SimplifiedKotlinScriptEvaluator.kt
+++ b/subprojects/kotlin-dsl/src/testFixtures/kotlin/org/gradle/kotlin/dsl/fixtures/SimplifiedKotlinScriptEvaluator.kt
@@ -167,6 +167,9 @@
         override fun stage1BlocksAccessorsFor(scriptHost: KotlinScriptHost<*>): ClassPath =
             ClassPath.EMPTY
 
+        override fun accessorsClassPathFor(scriptHost: KotlinScriptHost<*>): ClassPath =
+            ClassPath.EMPTY
+
         override fun startCompilerOperation(description: String): AutoCloseable =
             mock()
 
@@ -205,6 +208,9 @@
 
         override val jvmTarget: JavaVersion
             get() = JavaVersion.current()
+
+        override val allWarningsAsErrors: Boolean
+            get() = false
     }
 }
 
diff --git a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java
index 52c837d..26bba16 100644
--- a/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java
+++ b/subprojects/language-groovy/src/main/java/org/gradle/api/internal/tasks/compile/ApiGroovyCompiler.java
@@ -29,9 +29,11 @@
 import org.codehaus.groovy.control.CompilationUnit;
 import org.codehaus.groovy.control.CompilePhase;
 import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.MultipleCompilationErrorsException;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.control.customizers.CompilationCustomizer;
 import org.codehaus.groovy.control.customizers.ImportCustomizer;
+import org.codehaus.groovy.control.messages.ExceptionMessage;
 import org.codehaus.groovy.control.messages.SimpleMessage;
 import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
 import org.codehaus.groovy.tools.javac.JavaCompiler;
@@ -283,6 +285,12 @@ private void copyJavaCompilerResult(ApiCompilerResult javaCompilerResult) {
             unit.compile();
             return result;
         } catch (org.codehaus.groovy.control.CompilationFailedException e) {
+            if (isFatalException(e)) {
+                // This indicates a compiler bug and not a user error,
+                // so we cannot recover from such error: we need to force full recompilation.
+                throw new CompilationFatalException(e);
+            }
+
             System.err.println(e.getMessage());
             // Explicit flush, System.err is an auto-flushing PrintWriter unless it is replaced.
             System.err.flush();
@@ -297,6 +305,27 @@ private void copyJavaCompilerResult(ApiCompilerResult javaCompilerResult) {
         }
     }
 
+    /**
+     * Returns true if the exception is fatal, unrecoverable for the incremental compilation. Example of such error:
+     * <pre>
+     * error: startup failed:
+     * General error during instruction selection: java.lang.NoClassDefFoundError: Unable to load class ClassName due to missing dependency DependencyName
+     *   java.lang.RuntimeException: java.lang.NoClassDefFoundError: Unable to load class ClassName due to missing dependency DependencyName
+     *      at org.codehaus.groovy.control.CompilationUnit$IPrimaryClassNodeOperation.doPhaseOperation(CompilationUnit.java:977)
+     *      at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:672)
+     *      at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:636)
+     *      at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:611)
+     * </pre>
+     */
+    private static boolean isFatalException(org.codehaus.groovy.control.CompilationFailedException e) {
+        if (e instanceof MultipleCompilationErrorsException) {
+            // Groovy compiler wraps any uncontrolled exception (e.g. IOException, NoClassDefFoundError and similar) in a `ExceptionMessage`
+            return ((MultipleCompilationErrorsException) e).getErrorCollector().getErrors().stream()
+                .anyMatch(message -> message instanceof ExceptionMessage);
+        }
+        return false;
+    }
+
     private static boolean shouldProcessAnnotations(GroovyJavaJointCompileSpec spec) {
         return spec.getGroovyCompileOptions().isJavaAnnotationProcessing() && spec.annotationProcessingConfigured();
     }
diff --git a/subprojects/language-groovy/src/testFixtures/groovy/org/gradle/testing/fixture/GroovyCoverage.groovy b/subprojects/language-groovy/src/testFixtures/groovy/org/gradle/testing/fixture/GroovyCoverage.groovy
index a6b9ce8..cb9b829 100644
--- a/subprojects/language-groovy/src/testFixtures/groovy/org/gradle/testing/fixture/GroovyCoverage.groovy
+++ b/subprojects/language-groovy/src/testFixtures/groovy/org/gradle/testing/fixture/GroovyCoverage.groovy
@@ -22,8 +22,8 @@
 import javax.annotation.Nullable
 
 class GroovyCoverage {
-    private static final String[] PREVIOUS = ['1.5.8', '1.6.9', '1.7.11', '1.8.8', '2.0.5', '2.1.9', '2.2.2', '2.3.10', '2.4.15', '2.5.8', '3.0.13']
-    private static final String[] FUTURE = ['4.0.7']
+    private static final String[] PREVIOUS = ['1.5.8', '1.6.9', '1.7.11', '1.8.8', '2.0.5', '2.1.9', '2.2.2', '2.3.10', '2.4.15', '2.5.8', '3.0.17']
+    private static final String[] FUTURE = ['4.0.8', '4.0.9']
 
     static final Set<String> SUPPORTED_BY_JDK
 
@@ -46,16 +46,16 @@
 
     static {
         SUPPORTED_BY_JDK = groovyVersionsSupportedByJdk(JavaVersion.current())
-        SUPPORTS_GROOVYDOC = versionsAbove(SUPPORTED_BY_JDK, "1.6.9")
+        SUPPORTS_GROOVYDOC = versionsAtLeast(SUPPORTED_BY_JDK, "1.6.9")
         // Indy compilation doesn't work in 2.2.2 and before
-        SUPPORTS_INDY = versionsAbove(SUPPORTED_BY_JDK, "2.3.0")
-        SUPPORTS_TIMESTAMP = versionsAbove(SUPPORTED_BY_JDK, "2.4.6")
-        SUPPORTS_PARAMETERS = versionsAbove(SUPPORTED_BY_JDK, "2.5.0")
-        SUPPORTS_DISABLING_AST_TRANSFORMATIONS = versionsAbove(SUPPORTED_BY_JDK, "2.0.0")
-        SINCE_3_0 = versionsAbove(SUPPORTED_BY_JDK, "3.0.0")
+        SUPPORTS_INDY = versionsAtLeast(SUPPORTED_BY_JDK, "2.3.0")
+        SUPPORTS_TIMESTAMP = versionsAtLeast(SUPPORTED_BY_JDK, "2.4.6")
+        SUPPORTS_PARAMETERS = versionsAtLeast(SUPPORTED_BY_JDK, "2.5.0")
+        SUPPORTS_DISABLING_AST_TRANSFORMATIONS = versionsAtLeast(SUPPORTED_BY_JDK, "2.0.0")
+        SINCE_3_0 = versionsAtLeast(SUPPORTED_BY_JDK, "3.0.0")
         CURRENT_STABLE = isCurrentGroovyVersionStable()
             ? GroovySystem.version
-            : versionsBelow(SUPPORTED_BY_JDK, GroovySystem.version).last()
+            : versionsAtMost(SUPPORTED_BY_JDK, GroovySystem.version).last()
         MINIMAL_GROOVY_3 = versionsBelow(SINCE_3_0, "4.0.0").first()
     }
 
@@ -86,10 +86,12 @@
 
         allVersions.addAll(FUTURE)
 
-        if (javaVersion.isCompatibleWith(JavaVersion.VERSION_15)) {
-            return versionsAbove(allVersions, '3.0.0')
+        if (javaVersion.isCompatibleWith(JavaVersion.VERSION_20)) {
+            return versionsAbove(allVersions, '3.0.13')
+        } else if (javaVersion.isCompatibleWith(JavaVersion.VERSION_15)) {
+            return versionsAtLeast(allVersions, '3.0.0')
         } else if (javaVersion.isCompatibleWith(JavaVersion.VERSION_14)) {
-            return versionsBetween(allVersions, '2.2.2', '2.5.10')
+            return versionsBetweenInclusive(allVersions, '2.2.2', '2.5.10')
         } else {
             return allVersions
         }
@@ -99,28 +101,37 @@
         !GroovySystem.version.endsWith("-SNAPSHOT")
     }
 
-    private static Set<String> versionsAbove(Collection<String> versionsToFilter, String threshold) {
-        filterVersions(versionsToFilter, threshold, null)
+    private static Set<String> versionsAtLeast(Collection<String> versionsToFilter, String fromVersion) {
+        versionsBetweenInclusive(versionsToFilter, fromVersion, null)
     }
 
-    private static Set<String> versionsBelow(Collection<String> versionsToFilter, String threshold) {
-        filterVersions(versionsToFilter, null, threshold)
+    private static Set<String> versionsAtMost(Collection<String> versionsToFilter, String toVersion) {
+        versionsBetweenInclusive(versionsToFilter, null, toVersion)
     }
 
-    private static Set<String> versionsBetween(Collection<String> versionsToFilter, String lowerBound, String upperBound) {
-        filterVersions(versionsToFilter, lowerBound, upperBound)
+    private static Set<String> versionsAbove(Collection<String> versionsToFilter, String fromVersion) {
+        versionsBetweenExclusive(versionsToFilter, fromVersion, null)
     }
 
-    private static Set<String> filterVersions(Collection<String> versionsToFilter, @Nullable String lowerBound, @Nullable String upperBound) {
-        versionsToFilter.findAll {
-            def version = VersionNumber.parse(it)
-            if (lowerBound != null && version < VersionNumber.parse(lowerBound)) {
-                return false
-            }
-            if (upperBound != null && version > VersionNumber.parse(upperBound)) {
-                return false
-            }
-            return true
-        }.toSet().asImmutable()
+    private static Set<String> versionsBelow(Collection<String> versionsToFilter, String toVersion) {
+        versionsBetweenExclusive(versionsToFilter, null, toVersion)
+    }
+
+    private static Set<String> versionsBetweenInclusive(Collection<String> versionsToFilter, @Nullable String from, @Nullable String to) {
+        return filterVersions(versionsToFilter, from, to) { version, fromVersion, toVersion ->
+            return (fromVersion == null || fromVersion <= version) && (toVersion == null || version <= toVersion)
+        }
+    }
+
+    private static Set<String> versionsBetweenExclusive(Collection<String> versionsToFilter, @Nullable String from, @Nullable String to) {
+        return filterVersions(versionsToFilter, from, to) { version, fromVersion, toVersion ->
+            return (fromVersion == null || fromVersion < version) && (toVersion == null || version < toVersion)
+        }
+    }
+
+    private static Set<String> filterVersions(Collection<String> versionsToFilter, @Nullable String lowerBound, @Nullable String upperBound, Closure filter) {
+        def fromVersion = lowerBound == null ? null : VersionNumber.parse(lowerBound)
+        def toVersion = upperBound == null ? null : VersionNumber.parse(upperBound)
+        versionsToFilter.findAll { filter(VersionNumber.parse(it), fromVersion, toVersion) }.toSet().asImmutable()
     }
 }
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/JavaToolchainBuildOperationsIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/JavaToolchainBuildOperationsIntegrationTest.groovy
index e10f645..82388b6 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/JavaToolchainBuildOperationsIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/JavaToolchainBuildOperationsIntegrationTest.groovy
@@ -28,11 +28,12 @@
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.util.internal.TextUtil
 import org.gradle.util.internal.VersionNumber
+import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 class JavaToolchainBuildOperationsIntegrationTest extends AbstractIntegrationSpec implements JavaToolchainFixture, JavaToolchainBuildOperationsFixture {
 
-    static kgpLatestVersions = new KotlinGradlePluginVersions().latests.toList()
+    static kgpLatestVersions = new KotlinGradlePluginVersions().latests
 
     def setup() {
         captureBuildOperations()
@@ -394,6 +395,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/21368")
+    @IgnoreIf({GradleContextualExecuter.embedded})
     def "emits toolchain usages when configuring toolchains for #kotlinPlugin Kotlin plugin '#kotlinPluginVersion'"() {
         JvmInstallationMetadata jdkMetadata = AvailableJavaHomes.getJvmInstallationMetadata(AvailableJavaHomes.differentVersion)
 
@@ -444,10 +446,28 @@
                 }
             }
             executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.BasePluginConvention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#base_convention_deprecation")
+            executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.JavaPluginConvention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#java_convention_deprecation")
+            executer.expectDocumentedDeprecationWarning(
                 "The AbstractCompile.destinationDir property has been deprecated. " +
                     "This is scheduled to be removed in Gradle 9.0. " +
                     "Please use the destinationDirectory property instead. " +
                     "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_7.html#compile_task_wiring")
+            executer.expectDocumentedDeprecationWarning(
+                "The Project.getConvention() method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+            executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
         }
         withInstallations(jdkMetadata).run(":compileKotlin", ":test")
         def eventsOnCompile = toolchainEvents(":compileKotlin")
@@ -468,6 +488,22 @@
         assertToolchainUsages(eventsOnTest, jdkMetadata, "JavaLauncher")
 
         when:
+        if (isKotlin1dot6 && GradleContextualExecuter.notConfigCache) {
+            executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.JavaPluginConvention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#java_convention_deprecation")
+            executer.expectDocumentedDeprecationWarning(
+                "The Project.getConvention() method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+            executer.expectDocumentedDeprecationWarning(
+                "The org.gradle.api.plugins.Convention type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    "https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions")
+        }
         withInstallations(jdkMetadata).run(":compileKotlin", ":test")
         eventsOnCompile = toolchainEvents(":compileKotlin")
         eventsOnTest = toolchainEvents(":test")
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileIntegrationTest.groovy
index 503f11b..d1a9422 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileIntegrationTest.groovy
@@ -16,12 +16,12 @@
 
 package org.gradle.api.tasks.compile
 
-import groovy.test.NotYetImplemented
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.Resources
 import org.gradle.util.internal.TextUtil
 import org.junit.Rule
@@ -29,6 +29,9 @@
 
 import java.nio.file.Paths
 
+import static org.gradle.api.internal.DocumentationRegistry.BASE_URL
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
+
 class JavaCompileIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule
@@ -49,10 +52,11 @@
         file("src/main/java/Foo.java") << "public class Foo {}"
 
         when:
-        executer.expectDeprecationWarning("Configuring a Java executable via a relative path. " +
-                "This behavior has been deprecated. This will fail with an error in Gradle 9.0. " +
-                "Resolving relative file paths might yield unexpected results, there is no single clear location it would make sense to resolve against. " +
-                "Configure an absolute path to a Java executable instead.")
+        executer.expectDocumentedDeprecationWarning("Configuring a Java executable via a relative path. " +
+            "This behavior has been deprecated. This will fail with an error in Gradle 9.0. " +
+            "Resolving relative file paths might yield unexpected results, there is no single clear location it would make sense to resolve against. " +
+            "Configure an absolute path to a Java executable instead. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#no_relative_paths_for_java_executables")
         run("compileJava")
 
         then:
@@ -202,7 +206,7 @@
         """
     }
 
-    @Requires(TestPrecondition.LINUX)
+    @Requires(UnitTestPreconditions.Linux)
     def "can compile after package case-rename"() {
         buildFile << """
             plugins {
@@ -575,7 +579,7 @@
     }
 
     @Issue("gradle/gradle#1358")
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     // Java 9 compiler throws error already: 'zip END header not found'
     def "compile classpath snapshotting should warn when jar on classpath is malformed"() {
         buildFile << '''
@@ -598,7 +602,7 @@
     }
 
     @Issue("gradle/gradle#1581")
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     def "compile classpath snapshotting on Java 8 and earlier should warn when jar on classpath has non-utf8 characters in filenames"() {
         buildFile << '''
             apply plugin: 'java'
@@ -756,7 +760,7 @@
         skipped(':compileJava')
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "compile a module"() {
         given:
         buildFile << '''
@@ -781,7 +785,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/2537")
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "compile a module with --module-source-path"() {
         given:
         buildFile << '''
@@ -825,7 +829,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/2537")
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "compile a module with --module-source-path and sourcepath warns and removes sourcepath"() {
         given:
         buildFile << '''
@@ -929,7 +933,7 @@
         failureHasCause("Cannot specify -J flags via `CompileOptions.compilerArgs`. Use the `CompileOptions.forkOptions.jvmArgs` property instead.")
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdk7() && AvailableJavaHomes.getJdk8() && TestPrecondition.JDK8_OR_EARLIER.fulfilled })
+    @Requires([UnitTestPreconditions.Jdk8OrEarlier, IntegTestPreconditions.Java7HomeAvailable, IntegTestPreconditions.Java8HomeAvailable ])
     // bootclasspath has been removed in Java 9+
     def "bootclasspath can be set"() {
         def jdk7 = AvailableJavaHomes.getJdk7()
@@ -970,7 +974,7 @@
     }
 
     // bootclasspath has been removed in Java 9+
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     @Issue("https://github.com/gradle/gradle/issues/19817")
     def "fails if bootclasspath is provided as a path instead of a single file"() {
         def jre = AvailableJavaHomes.getBestJre()
@@ -1131,15 +1135,18 @@
             package com.example;
             public class Main {}
         """
-        executer.expectDocumentedDeprecationWarning("The CompileOptions.annotationProcessorGeneratedSourcesDirectory property has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the generatedSourceOutputDirectory property instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html#org.gradle.api.tasks.compile.CompileOptions:annotationProcessorGeneratedSourcesDirectory for more details.")
+        expectAnnotationProcessorGeneratedSourcesDirectoryDeprecation()
 
         then:
         succeeds("compileJava")
     }
 
-    // Enable deprecation nagging: https://github.com/gradle/gradle/issues/16782
-    @NotYetImplemented
-    def "CompileOptions.setAnnotationProcessorGeneratedSourcesDirectory is deprecated"() {
+    private expectAnnotationProcessorGeneratedSourcesDirectoryDeprecation() {
+        executer.expectDocumentedDeprecationWarning("The CompileOptions.annotationProcessorGeneratedSourcesDirectory property has been deprecated. " +
+            "This is scheduled to be removed in Gradle 9.0. Please use the generatedSourceOutputDirectory property instead. ${getCompileOptionsLink()}")
+    }
+
+    def "CompileOptions.setAnnotationProcessorGeneratedSourcesDirectory(File) is deprecated"() {
         when:
         buildFile << """
             plugins {
@@ -1153,14 +1160,38 @@
             package com.example;
             public class Main {}
         """
-        executer.expectDocumentedDeprecationWarning("The CompileOptions.annotationProcessorGeneratedSourcesDirectory property has been deprecated. This is scheduled to be removed in Gradle 8.0. Please use the generatedSourceOutputDirectory property instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html#org.gradle.api.tasks.compile.CompileOptions:annotationProcessorGeneratedSourcesDirectory for more details.")
+        expectAnnotationProcessorGeneratedSourcesDirectoryDeprecation()
 
         then:
         succeeds("compileJava")
     }
 
+    def "CompileOptions.setAnnotationProcessorGeneratedSourcesDirectory(Provider<File>) is deprecated"() {
+        when:
+        buildFile << """
+            plugins {
+                id("java")
+            }
+            tasks.withType(JavaCompile) {
+                options.annotationProcessorGeneratedSourcesDirectory = provider(() -> file("build/annotation-processor-out"))
+            }
+        """
+        file("src/main/java/com/example/Main.java") << """
+            package com.example;
+            public class Main {}
+        """
+        expectAnnotationProcessorGeneratedSourcesDirectoryDeprecation()
+
+        then:
+        succeeds("compileJava")
+    }
+
+    private getCompileOptionsLink() {
+        String.format(RECOMMENDATION, "information", "${BASE_URL}/dsl/org.gradle.api.tasks.compile.CompileOptions.html#org.gradle.api.tasks.compile.CompileOptions:annotationProcessorGeneratedSourcesDirectory")
+    }
+
     @Issue("https://github.com/gradle/gradle/issues/18262")
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "should compile sources from source with -sourcepath option for modules"() {
         given:
         buildFile << """
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileJavaVersionIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileJavaVersionIntegrationTest.groovy
index 880fa3b..3e53f99 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileJavaVersionIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileJavaVersionIntegrationTest.groovy
@@ -19,13 +19,14 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.gradle.util.internal.TextUtil
 
 import static org.gradle.api.JavaVersion.VERSION_1_8
 import static org.gradle.api.JavaVersion.VERSION_1_9
 
-@Requires(adhoc = { AvailableJavaHomes.getJdk(VERSION_1_8) && AvailableJavaHomes.getJdk(VERSION_1_9) })
+@Requires([IntegTestPreconditions.Java8HomeAvailable, IntegTestPreconditions.Java9HomeAvailable ])
 class JavaCompileJavaVersionIntegrationTest extends AbstractIntegrationSpec {
 
     /**
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileTaskOperationResultIntegTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileTaskOperationResultIntegTest.groovy
new file mode 100644
index 0000000..57adb23
--- /dev/null
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileTaskOperationResultIntegTest.groovy
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 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.api.tasks.compile
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.language.fixtures.HelperProcessorFixture
+import spock.lang.Issue
+
+class JavaCompileTaskOperationResultIntegTest extends AbstractIntegrationSpec {
+    def setup() {
+        settingsFile << """
+            include 'processor'
+        """
+        buildFile << """
+            allprojects {
+                apply plugin: 'java'
+            }
+            dependencies {
+                compileOnly project(':processor')
+                annotationProcessor project(':processor')
+            }
+        """
+        file("src/main/java/SomeClass.java") << """
+            @Helper class SomeClass {}
+        """
+        def processorProjectDir = file("processor")
+        def fixture = new HelperProcessorFixture()
+        fixture.writeApiTo(processorProjectDir)
+        fixture.writeAnnotationProcessorTo(processorProjectDir)
+        fixture.writeSupportLibraryTo(processorProjectDir)
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/22999")
+    def "listener added during doFirst of JavaCompile can subscribe to task completion events but does not get a callback"() {
+        buildFile << """
+            import javax.inject.Inject
+            import org.gradle.api.services.BuildService
+            import org.gradle.api.services.BuildServiceParameters
+            import org.gradle.tooling.events.OperationCompletionListener
+            import org.gradle.tooling.events.FinishEvent
+            import org.gradle.tooling.events.task.java.JavaCompileTaskOperationResult
+
+            apply plugin: JavaCompileModifyingPlugin
+
+            abstract class JavaCompileListener implements BuildService<BuildServiceParameters.None>, OperationCompletionListener {
+                void onFinish(FinishEvent event) {
+                    println("EVENT: \$event")
+                }
+            }
+
+            abstract class JavaCompileModifyingPlugin implements Plugin<Project> {
+                @Inject
+                abstract BuildEventsListenerRegistry getRegistry();
+
+                void apply(Project project) {
+                    def listener = project.gradle.sharedServices.registerIfAbsent("listener", JavaCompileListener) { }
+                    project.tasks.withType(JavaCompile) { task ->
+                        task.doFirst {
+                            registry.onTaskCompletion(listener)
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        run("compileJava")
+
+        then:
+        output.count("EVENT: ") == 0
+    }
+}
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileToolchainIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileToolchainIntegrationTest.groovy
index 35ed63db..31617e0 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileToolchainIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/compile/JavaCompileToolchainIntegrationTest.groovy
@@ -19,15 +19,21 @@
 import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.executer.DocumentationUtils
 import org.gradle.integtests.fixtures.jvm.JavaToolchainFixture
 import org.gradle.internal.jvm.Jvm
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import spock.lang.Issue
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
 import static org.junit.Assume.assumeNotNull
 
 class JavaCompileToolchainIntegrationTest extends AbstractIntegrationSpec implements JavaToolchainFixture {
@@ -293,11 +299,16 @@
             .runWithFailure()
 
         then:
-        failure.assertHasDocumentedCause("No locally installed toolchains match (see https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection) " +
-                "and toolchain auto-provisioning is not enabled (see https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection).")
+        failure.assertHasCause("No locally installed toolchains match and toolchain auto-provisioning is not enabled.")
+            .assertHasResolutions(
+                DocumentationUtils.normalizeDocumentationLink("Learn more about toolchain auto-detection at https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection."),
+                STACKTRACE_MESSAGE,
+                INFO_DEBUG,
+                SCAN,
+                GET_HELP)
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdk(JavaVersion.VERSION_1_7) != null })
+    @Requires(IntegTestPreconditions.Java7HomeAvailable)
     def "can use toolchains to compile java 1.7 code"() {
         def jdk = AvailableJavaHomes.getJdk(JavaVersion.VERSION_1_7)
         buildFile << """
@@ -358,8 +369,13 @@
         fails("compileJava")
 
         then:
-        failure.assertHasDocumentedCause("No locally installed toolchains match (see https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection) " +
-                "and toolchain auto-provisioning is not enabled (see https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection).")
+        failure.assertHasCause("No locally installed toolchains match and toolchain auto-provisioning is not enabled.")
+            .assertHasResolutions(
+                DocumentationUtils.normalizeDocumentationLink("Learn more about toolchain auto-detection at https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection."),
+                STACKTRACE_MESSAGE,
+                INFO_DEBUG,
+                SCAN,
+                GET_HELP)
     }
 
     def "can use compile daemon with tools jar"() {
@@ -415,7 +431,7 @@
      * This test covers the case where in Java8 the class name becomes fully qualified in the deprecation message which is
      * somehow caused by invoking javacTask.getElements() in the IncrementalCompileTask of the incremental compiler plugin.
      */
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "Java deprecation messages with different JDKs"() {
         def jdk = AvailableJavaHomes.getJdk(javaVersion)
 
@@ -469,6 +485,92 @@
         JavaVersion.current()   | "[deprecation] foo() in Foo has been deprecated"
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/23990")
+    def "can compile with a custom compiler executable"() {
+        def otherJdk = AvailableJavaHomes.getJdk(JavaVersion.current())
+        def jdk = AvailableJavaHomes.getDifferentVersion {
+            def v = it.languageVersion.majorVersion.toInteger()
+            11 <= v && v <= 18 // Java versions supported by ECJ releases used in the test
+        }
+
+        buildFile << """
+            plugins {
+                id("java")
+            }
+
+            java {
+                toolchain {
+                    languageVersion = JavaLanguageVersion.of(${otherJdk.javaVersion.majorVersion})
+                }
+            }
+
+            configurations {
+                ecj {
+                    canBeConsumed = false
+                    assert canBeResolved
+                }
+            }
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                def changed = providers.gradleProperty("changed").isPresent()
+                ecj(!changed ? "org.eclipse.jdt:ecj:3.31.0" : "org.eclipse.jdt:ecj:3.32.0")
+            }
+
+            // Make sure the provider is up-to-date only if the ECJ classpath does not change
+            class EcjClasspathProvider implements CommandLineArgumentProvider {
+                @Classpath
+                final FileCollection ecjClasspath
+
+                EcjClasspathProvider(FileCollection ecjClasspath) {
+                    this.ecjClasspath = ecjClasspath
+                }
+
+                @Override
+                List<String> asArguments() {
+                    return ["-cp", ecjClasspath.asPath, "org.eclipse.jdt.internal.compiler.batch.Main"]
+                 }
+            }
+
+            compileJava {
+                def customJavaLauncher = javaToolchains.launcherFor {
+                    languageVersion.set(JavaLanguageVersion.of(${jdk.javaVersion.majorVersion}))
+                }.get()
+
+                // ECJ does not support generating JNI headers
+                options.headerOutputDirectory.set(provider { null })
+                options.fork = true
+                options.forkOptions.executable = customJavaLauncher.executablePath.asFile.absolutePath
+                options.forkOptions.jvmArgumentProviders.add(new EcjClasspathProvider(configurations.ecj))
+            }
+        """
+
+        when:
+        withInstallations(jdk, otherJdk).run(":compileJava", "--info")
+        then:
+        executedAndNotSkipped(":compileJava")
+        outputContains("Compiling with toolchain '${jdk.javaHome.absolutePath}'")
+        outputContains("Compiling with Java command line compiler '${jdk.javaExecutable.absolutePath}'")
+        classJavaVersion(javaClassFile("Foo.class")) == jdk.javaVersion
+
+        // Test up-to-date checks
+        when:
+        withInstallations(jdk, otherJdk).run(":compileJava")
+        then:
+        skipped(":compileJava")
+
+        when:
+        withInstallations(jdk, otherJdk).run(":compileJava", "-Pchanged")
+        then:
+        executedAndNotSkipped(":compileJava")
+
+        when:
+        withInstallations(jdk, otherJdk).run(":compileJava", "-Pchanged")
+        then:
+        skipped(":compileJava")
+    }
+
     private TestFile configureForkOptionsExecutable(Jvm jdk) {
         buildFile << """
             compileJava {
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/javadoc/JavadocIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/javadoc/JavadocIntegrationTest.groovy
index 1cfe1a5..ea69e90 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/javadoc/JavadocIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/javadoc/JavadocIntegrationTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.internal.jvm.Jvm
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import org.junit.Rule
 import spock.lang.Issue
@@ -30,10 +30,12 @@
 import java.nio.file.Paths
 
 class JavadocIntegrationTest extends AbstractIntegrationSpec {
-    @Rule TestResources testResources = new TestResources(temporaryFolder)
+    @Rule
+    TestResources testResources = new TestResources(temporaryFolder)
 
     @Issue("GRADLE-1563")
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)  // JDK 9 requires an @Deprecated annotation that breaks this same test on Java 7 on Windows.
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
+    // JDK 9 requires an @Deprecated annotation that breaks this same test on Java 7 on Windows.
     def handlesTagsAndTaglets() {
         when:
         run("javadoc")
@@ -46,7 +48,7 @@
     }
 
     @Issue(["GRADLE-2520", "https://github.com/gradle/gradle/issues/4993"])
-    @Requires(TestPrecondition.JDK9_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk9OrEarlier)
     def canCombineLocalOptionWithOtherOptions() {
         when:
         run("javadoc")
@@ -65,7 +67,8 @@
 
         writeSourceFile()
 
-        when: run("javadoc", "-i")
+        when:
+        run("javadoc", "-i")
         then:
         file("build/docs/javadoc/Foo.html").text.contains("""Hey Joe!""")
     }
@@ -106,7 +109,10 @@
         file('build/docs/javadoc/Foo.html').text.contains('myHeader')
     }
 
-    @Requires([TestPrecondition.NOT_WINDOWS, TestPrecondition.JDK8_OR_EARLIER])  // JDK 9 Breaks multiline -header arguments.
+    @Requires(value = [
+        UnitTestPreconditions.NotWindows,
+        UnitTestPreconditions.Jdk8OrEarlier
+    ], reason = "JDK 9 Breaks multiline -header arguments.")
     @Issue("GRADLE-3099")
     def "writes multiline header"() {
         buildFile << """
@@ -119,7 +125,8 @@
 
         writeSourceFile()
 
-        when: run("javadoc", "-i")
+        when:
+        run("javadoc", "-i")
         then:
         file("build/docs/javadoc/Foo.html").text.contains("""Hey
 Joe!""")
@@ -140,10 +147,11 @@
         writeSourceFile()
 
         when:
-        executer.expectDeprecationWarning("Configuring a Java executable via a relative path. " +
+        executer.expectDocumentedDeprecationWarning("Configuring a Java executable via a relative path. " +
                 "This behavior has been deprecated. This will fail with an error in Gradle 9.0. " +
                 "Resolving relative file paths might yield unexpected results, there is no single clear location it would make sense to resolve against. " +
-                "Configure an absolute path to a Java executable instead.")
+                "Configure an absolute path to a Java executable instead. " +
+                "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#no_relative_paths_for_java_executables")
         run("javadoc")
 
         then:
@@ -184,7 +192,7 @@
         when:
         run "javadoc"
         then:
-        executedAndNotSkipped( ":javadoc")
+        executedAndNotSkipped(":javadoc")
 
         when:
         run "javadoc"
@@ -342,7 +350,7 @@
         when: "running the task the first time, it is executed and succeeds"
         run "javadoc"
         then:
-        executedAndNotSkipped( ":javadoc")
+        executedAndNotSkipped(":javadoc")
 
         when: "running the task the second time after adding a custom option, it is executed and succeeds and NOT marked UP-TO-DATE"
         buildFile.text = """
@@ -360,7 +368,7 @@
         executedAndNotSkipped(":javadoc")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     @Issue("https://github.com/gradle/gradle/issues/4841")
     def "adding custom javadoc options makes task out-of-date with html5 option"() {
         given: "a javadoc task without custom options"
@@ -379,7 +387,7 @@
         when: "running the task the first time, it is executed and succeeds"
         run "javadoc"
         then:
-        executedAndNotSkipped( ":javadoc")
+        executedAndNotSkipped(":javadoc")
 
         when: "running the task the second time after adding a custom option, it is executed and succeeds and NOT marked UP-TO-DATE"
         buildFile.text = """
@@ -397,7 +405,7 @@
         executedAndNotSkipped(":javadoc")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     @Issue("https://github.com/gradle/gradle/issues/4841")
     def "changing the value of a custom javadoc options makes task out-of-date"() {
         given: "a javadoc task with a custom options"
@@ -417,7 +425,7 @@
         when: "running the task the first time, it is executed and succeeds"
         run "javadoc"
         then:
-        executedAndNotSkipped( ":javadoc")
+        executedAndNotSkipped(":javadoc")
 
         when: "running the task the second time after changing the custom option, it is executed and succeeds and NOT marked UP-TO-DATE"
         buildFile.text = """
@@ -435,7 +443,7 @@
         executedAndNotSkipped(":javadoc")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     @Issue("https://github.com/gradle/gradle/issues/4841")
     def "changing which custom javadoc options are available makes task out-of-date"() {
         given: "a javadoc task with a custom options"
@@ -455,7 +463,7 @@
         when: "running the task the first time, it is executed and succeeds"
         run "javadoc"
         then:
-        executedAndNotSkipped( ":javadoc")
+        executedAndNotSkipped(":javadoc")
 
         when: "running the task the second time after changing the custom option, it is executed and succeeds and NOT marked UP-TO-DATE"
         buildFile.text = """
@@ -473,7 +481,7 @@
         executedAndNotSkipped(":javadoc")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     @Issue("https://github.com/gradle/gradle/issues/4841")
     def "unchanged custom javadoc option does not make task out-of-date"() {
         given: "a javadoc task with a custom options"
@@ -502,7 +510,7 @@
     }
 
     // bootclasspath has been removed in Java 9+
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     @Issue("https://github.com/gradle/gradle/issues/19817")
     def "shows deprecation if bootclasspath is provided as a path instead of a single file"() {
         def jre = AvailableJavaHomes.getBestJre()
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/javadoc/JavadocModularizedJavaIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/javadoc/JavadocModularizedJavaIntegrationTest.groovy
index 70c6f4f..c4f0900 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/javadoc/JavadocModularizedJavaIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/api/tasks/javadoc/JavadocModularizedJavaIntegrationTest.groovy
@@ -18,11 +18,11 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
-@Requires(TestPrecondition.JDK9_OR_LATER)
+@Requires(UnitTestPreconditions.Jdk9OrLater)
 class JavadocModularizedJavaIntegrationTest extends AbstractIntegrationSpec {
 
     TestFile testBuildFile
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/AbstractJavaGroovyIncrementalCompilationSupport.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/AbstractJavaGroovyIncrementalCompilationSupport.groovy
index fd3cad7..0c27ab2 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/AbstractJavaGroovyIncrementalCompilationSupport.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/AbstractJavaGroovyIncrementalCompilationSupport.groovy
@@ -53,7 +53,7 @@
             def packageGroup = (body =~ "\\s*package ([\\w.]+).*")
             String packageName = packageGroup.size() > 0 ? packageGroup[0][1] : ""
             String packageFolder = packageName.replaceAll("[.]", "/")
-            def className = (body =~ /(?s).*?(?:class|interface|enum) ([\w$]+) .*/)[0][1]
+            def className = (body =~ /(?s).*?(?:class|interface|enum|trait) ([\w$]+) .*/)[0][1]
             assert className: "unable to find class name"
             def f
             if (packageFolder.isEmpty()) {
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/BaseIncrementalCompilationAfterFailureIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/BaseIncrementalCompilationAfterFailureIntegrationTest.groovy
index 453cb80..3b1e203 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/BaseIncrementalCompilationAfterFailureIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/BaseIncrementalCompilationAfterFailureIntegrationTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.api.internal.tasks.compile.incremental.recomp.PreviousCompilationAccess
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.fixtures.CompiledLanguage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import spock.lang.Issue
 
@@ -347,7 +347,7 @@
 class JavaIncrementalCompilationAfterFailureIntegrationTest extends BaseIncrementalCompilationAfterFailureIntegrationTest {
     CompiledLanguage language = CompiledLanguage.JAVA
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "incremental compilation after failure works with modules #description"() {
         file("impl/build.gradle") << """
             def layout = project.layout
@@ -543,4 +543,43 @@
         "Java"      | "java"              | ""
         "Groovy"    | "groovy"            | COMPILE_STATIC_ANNOTATION
     }
+
+    @Issue("https://github.com/gradle/gradle/issues/22814")
+    def 'does full recompilation on fatal failure'() {
+        given:
+        def a = source("class A extends ABase implements WithTrait { def m() { println('a') } }")
+        source "class ABase { def mBase() { println(A) } }"
+        source """
+            import groovy.transform.SelfType
+            @SelfType(ABase)
+            trait WithTrait {
+                final AllPluginsValidation allPlugins = new AllPluginsValidation(this)
+
+                static class AllPluginsValidation {
+                    final ABase base
+                    AllPluginsValidation(ABase base) {
+                        this.base = base
+                    }
+                }
+            }
+        """
+        run "compileGroovy"
+        outputs.recompiledClasses('ABase', 'A', 'WithTrait', 'WithTrait$Trait$FieldHelper', 'WithTrait$AllPluginsValidation', 'WithTrait$Trait$Helper')
+
+        when:
+        a.text = "class A extends ABase implements WithTrait { def m() { println('b') } }"
+
+        then:
+        executer.withStackTraceChecksDisabled()
+        def execution = runAndFail "compileGroovy"
+        execution.assertHasCause("Unrecoverable compilation error")
+
+        when:
+        executer.withStacktraceEnabled()
+        run"compileGroovy", "--info"
+
+        then:
+        outputs.recompiledClasses('ABase', 'A', 'WithTrait', 'WithTrait$Trait$FieldHelper', 'WithTrait$AllPluginsValidation', 'WithTrait$Trait$Helper')
+        outputContains("Full recompilation is required")
+    }
 }
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskConstantChangesIncrementalJavaCompilationIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskConstantChangesIncrementalJavaCompilationIntegrationTest.groovy
index 93c047a..143ea28 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskConstantChangesIncrementalJavaCompilationIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskConstantChangesIncrementalJavaCompilationIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import groovy.test.NotYetImplemented
 import org.gradle.integtests.fixtures.CompiledLanguage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 abstract class CrossTaskConstantChangesIncrementalJavaCompilationIntegrationTest extends AbstractCrossTaskConstantChangesIncrementalCompilationIntegrationTest {
     CompiledLanguage language = CompiledLanguage.JAVA
@@ -428,7 +428,7 @@
         impl.recompiledClasses 'B'
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "recompiles all if constant used by annotation on module-info is changed"() {
         given:
         source api: "package constant; public class Const { public static final String CONST = \"unchecked\"; }"
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskIncrementalJavaCompilationIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskIncrementalJavaCompilationIntegrationTest.groovy
index 91bda57..b619dc8 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskIncrementalJavaCompilationIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/CrossTaskIncrementalJavaCompilationIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 
 import org.gradle.integtests.fixtures.CompiledLanguage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 abstract class CrossTaskIncrementalJavaCompilationIntegrationTest extends AbstractCrossTaskIncrementalCompilationIntegrationTest {
@@ -39,7 +39,7 @@
     }
 
     // This behavior is kept for backward compatibility - may be removed in the future
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "recompiles when upstream module-info changes with manual module path"() {
         source api: ["package a; public class A {}"], impl: ["package b; import a.A; class B extends A {}"]
         def moduleInfo = file("api/src/main/${language.name}/module-info.${language.name}")
@@ -73,7 +73,7 @@
         result.hasErrorOutput("package a is not visible")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     @Issue("https://github.com/gradle/gradle/issues/23067")
     def "incremental compilation works with modules #description"() {
         file("impl/build.gradle") << """
@@ -120,7 +120,7 @@
         "with manual module-path"   | "false"         | "[\"--module-path=\${classpath.join(File.pathSeparator)}\"]" | "classpath = layout.files()"
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "incremental compilation detects if some exported package for compiled module was deleted #description"() {
         file("impl/build.gradle") << """
             def layout = project.layout
@@ -155,7 +155,7 @@
         "with manual module-path"   | "false"         | "[\"--module-path=\${classpath.join(File.pathSeparator)}\"]" | "classpath = layout.files()"
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "incremental compilation works for multi-module project with manual module paths"() {
         file("impl/build.gradle") << """
             def layout = project.layout
@@ -205,7 +205,7 @@
         impl.recompiledFqn("my.module.first.b.B", "my.module.second.c.C", "my.module.first.module-info", "my.module.second.module-info", "my.module.unrelated.module-info")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "recompiles when upstream module-info changes"() {
         given:
         settingsFile << "include 'otherApi'"
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaClassChangeIncrementalCompilationIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaClassChangeIncrementalCompilationIntegrationTest.groovy
index 93bca0c..b4f5b6e 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaClassChangeIncrementalCompilationIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaClassChangeIncrementalCompilationIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 
 import org.gradle.integtests.fixtures.CompiledLanguage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 class JavaClassChangeIncrementalCompilationIntegrationTest extends BaseJavaClassChangeIncrementalCompilationIntegrationTest {
@@ -118,7 +118,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/14744")
-    @Requires(TestPrecondition.JDK16_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk16OrLater)
     def "recompiles only affected classes when Java records are used"() {
         given:
         file("src/main/${languageName}/Person.${languageName}") << """
@@ -149,7 +149,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/14744")
-    @Requires(TestPrecondition.JDK16_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk16OrLater)
     def "recompiles only annotation of record when changed"() {
         given:
         file("src/main/${languageName}/MyRecordAnnotation.${languageName}") << """
@@ -182,7 +182,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/14744")
-    @Requires(TestPrecondition.JDK16_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk16OrLater)
     def "recompiles record when changed"() {
         given:
         file("src/main/${languageName}/Library.${languageName}") << """
@@ -209,7 +209,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/14744")
-    @Requires(TestPrecondition.JDK16_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk16OrLater)
     def "recompiles record consumer and record when record is changed"() {
         given:
         file("src/main/${languageName}/Library.${languageName}") << """
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaCompileAvoidanceWithIncrementalCompilationIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaCompileAvoidanceWithIncrementalCompilationIntegrationTest.groovy
index cf6bfcd..66857eb 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaCompileAvoidanceWithIncrementalCompilationIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaCompileAvoidanceWithIncrementalCompilationIntegrationTest.groovy
@@ -17,13 +17,13 @@
 package org.gradle.java.compile.incremental
 
 import org.gradle.integtests.fixtures.CompiledLanguage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class JavaCompileAvoidanceWithIncrementalCompilationIntegrationTest extends AbstractCompileAvoidanceWithIncrementalCompilationIntegrationTest {
     CompiledLanguage language = CompiledLanguage.JAVA
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "fails when malformed jars are on the compile classpath"() {
         buildFile << """
             apply plugin: '${language.name}'
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaSourceIncrementalCompilationIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaSourceIncrementalCompilationIntegrationTest.groovy
index 0888f06..0306c21 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaSourceIncrementalCompilationIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/incremental/JavaSourceIncrementalCompilationIntegrationTest.groovy
@@ -17,8 +17,8 @@
 package org.gradle.java.compile.incremental
 
 import org.gradle.integtests.fixtures.CompiledLanguage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import java.nio.file.Files
@@ -27,7 +27,7 @@
 class JavaSourceIncrementalCompilationIntegrationTest extends BaseJavaSourceIncrementalCompilationIntegrationTest {
     CompiledLanguage language = CompiledLanguage.JAVA
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "recompiles when module info changes"() {
         given:
         source("""
@@ -56,7 +56,7 @@
         result.assertHasErrorOutput("package java.util.logging is not visible")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "recompiles when module info is added"() {
         given:
         source("""
@@ -80,7 +80,7 @@
         result.assertHasErrorOutput("package java.util.logging is not visible")
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     @Issue("https://github.com/gradle/gradle/issues/9202")
     def "source mapping file works with symlinks"() {
         given:
@@ -121,7 +121,7 @@
         outputs.recompiledClasses('MyClass', 'Other')
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "recompiles all when constant used by annotation on module-info is changed"() {
         given:
         source("""
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/AbstractJavaModuleIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/AbstractJavaModuleIntegrationTest.groovy
index 5413b9f..4a1f65e 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/AbstractJavaModuleIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/AbstractJavaModuleIntegrationTest.groovy
@@ -19,14 +19,14 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.maven.MavenModule
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.test.fixtures.jpms.ModuleJarFixture.autoModuleJar
 import static org.gradle.test.fixtures.jpms.ModuleJarFixture.moduleJar
 import static org.gradle.test.fixtures.jpms.ModuleJarFixture.traditionalJar
 
-@Requires(TestPrecondition.JDK9_OR_LATER)
+@Requires(UnitTestPreconditions.Jdk9OrLater)
 abstract class AbstractJavaModuleIntegrationTest extends AbstractIntegrationSpec {
 
     def setup() {
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/compile/JavaModuleCompileIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/compile/JavaModuleCompileIntegrationTest.groovy
index a2fdfc8..cbd15aa 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/compile/JavaModuleCompileIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/compile/JavaModuleCompileIntegrationTest.groovy
@@ -17,7 +17,8 @@
 package org.gradle.java.compile.jpms.compile
 
 import org.gradle.java.compile.jpms.AbstractJavaModuleCompileIntegrationTest
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class JavaModuleCompileIntegrationTest extends AbstractJavaModuleCompileIntegrationTest {
 
@@ -190,7 +191,7 @@
     }
 
     private static String unnamedModuleReadError(String packageName, String producer, String consumer) {
-        if (TestPrecondition.JDK13_OR_EARLIER.fulfilled) {
+        if (TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk13OrEarlier)) {
             // bug in JDK < 14 that prints the producer (instead of the consumer) name in the error message
             "(package $packageName is declared in the unnamed module, but module $producer does not read it)"
         } else {
diff --git a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/test/JavaModuleBackboxTestExcutionIntegrationTest.groovy b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/test/JavaModuleBackboxTestExcutionIntegrationTest.groovy
index 013a917..10f30f7 100644
--- a/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/test/JavaModuleBackboxTestExcutionIntegrationTest.groovy
+++ b/subprojects/language-java/src/integTest/groovy/org/gradle/java/compile/jpms/test/JavaModuleBackboxTestExcutionIntegrationTest.groovy
@@ -17,7 +17,8 @@
 package org.gradle.java.compile.jpms.test
 
 import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.gradle.util.internal.TextUtil
 
 class JavaModuleBackboxTestExcutionIntegrationTest extends AbstractJavaModuleTestingIntegrationTest {
@@ -71,10 +72,7 @@
     def "runs JUnit5 blackbox test as module using the module path"() {
         given:
         buildFile << """
-            test { useJUnitPlatform() }
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
-            }
+            testing.suites.test.useJUnitJupiter()
         """
 
         when:
@@ -111,7 +109,7 @@
     // In all places where we support Java Modules, we do not check if we actually run on Java 9 or later.
     // Instead, we just let javac/java/javadoc fail. We could improve by checking ourselves and throwing a different error.
     // But we should do that in all places then.
-    @Requires(adhoc = { AvailableJavaHomes.getJdk8() })
+    @Requires(IntegTestPreconditions.Java8HomeAvailable)
     def "fails testing a Java module on Java 8"() {
         given:
         buildFile << """
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AbstractJavaCompileSpecFactory.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AbstractJavaCompileSpecFactory.java
index 27f408f..b55778a 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AbstractJavaCompileSpecFactory.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/AbstractJavaCompileSpecFactory.java
@@ -42,17 +42,7 @@ public T create() {
         }
 
         if (compileOptions.isFork()) {
-            File customJavaHome = compileOptions.getForkOptions().getJavaHome();
-            if (customJavaHome != null) {
-                return getCommandLineSpec(Jvm.forHome(customJavaHome).getJavacExecutable());
-            }
-
-            String customExecutable = compileOptions.getForkOptions().getExecutable();
-            if (customExecutable != null) {
-                return getCommandLineSpec(JavaExecutableUtils.resolveExecutable(customExecutable));
-            }
-
-            return getForkingSpec(Jvm.current().getJavaHome());
+            return chooseSpecFromCompileOptions(Jvm.current().getJavaHome());
         }
 
         return getDefaultSpec();
@@ -65,13 +55,7 @@ private T chooseSpecForToolchain() {
         }
 
         if (compileOptions.isFork()) {
-            // Presence of the fork options means that the user has explicitly requested a command-line compiler
-            if (compileOptions.getForkOptions().getJavaHome() != null || compileOptions.getForkOptions().getExecutable() != null) {
-                // We use the toolchain path because the fork options must agree with the selected toolchain
-                return getCommandLineSpec(Jvm.forHome(toolchainJavaHome).getJavacExecutable());
-            }
-
-            return getForkingSpec(toolchainJavaHome);
+            return chooseSpecFromCompileOptions(toolchainJavaHome);
         }
 
         if (!toolchain.isCurrentJvm()) {
@@ -81,6 +65,20 @@ private T chooseSpecForToolchain() {
         return getDefaultSpec();
     }
 
+    private T chooseSpecFromCompileOptions(File fallbackJavaHome) {
+        File forkJavaHome = compileOptions.getForkOptions().getJavaHome();
+        if (forkJavaHome != null) {
+            return getCommandLineSpec(Jvm.forHome(forkJavaHome).getJavacExecutable());
+        }
+
+        String forkExecutable = compileOptions.getForkOptions().getExecutable();
+        if (forkExecutable != null) {
+            return getCommandLineSpec(JavaExecutableUtils.resolveExecutable(forkExecutable));
+        }
+
+        return getForkingSpec(fallbackJavaHome);
+    }
+
     abstract protected T getCommandLineSpec(File executable);
 
     abstract protected T getForkingSpec(File javaHome);
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFailedException.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFailedException.java
index 33d8c97..5f540da 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFailedException.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFailedException.java
@@ -15,10 +15,12 @@
  */
 package org.gradle.api.internal.tasks.compile;
 
+import org.gradle.internal.exceptions.CompilationFailedIndicator;
+
 import javax.annotation.Nullable;
 import java.util.Optional;
 
-public class CompilationFailedException extends RuntimeException {
+public class CompilationFailedException extends RuntimeException implements CompilationFailedIndicator {
 
     private final ApiCompilerResult compilerPartialResult;
 
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFatalException.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFatalException.java
new file mode 100644
index 0000000..21d751f
--- /dev/null
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompilationFatalException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.compile;
+
+import org.gradle.internal.exceptions.CompilationFailedIndicator;
+
+/**
+ * Indicates a fatal error during compilation. Gradle will not try to recover output files from a previous compilation.
+ */
+public class CompilationFatalException extends RuntimeException implements CompilationFailedIndicator {
+
+    public CompilationFatalException(Throwable cause) {
+        super("Unrecoverable compilation error: " + cause.getMessage(), cause);
+    }
+}
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompileJavaBuildOperationReportingCompiler.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompileJavaBuildOperationReportingCompiler.java
index 0e8a25e..199e56c 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompileJavaBuildOperationReportingCompiler.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompileJavaBuildOperationReportingCompiler.java
@@ -33,9 +33,6 @@
 
 public class CompileJavaBuildOperationReportingCompiler implements Compiler<JavaCompileSpec> {
 
-    private static final CompileJavaBuildOperationType.Details DETAILS = new CompileJavaBuildOperationType.Details() {
-    };
-
     private final TaskInternal task;
     private final Compiler<JavaCompileSpec> delegate;
     private final BuildOperationExecutor buildOperationExecutor;
@@ -51,7 +48,15 @@ public WorkResult execute(final JavaCompileSpec spec) {
         return buildOperationExecutor.call(new CallableBuildOperation<WorkResult>() {
             @Override
             public BuildOperationDescriptor.Builder description() {
-                return BuildOperationDescriptor.displayName("Compile Java for " + task.getIdentityPath()).details(DETAILS);
+                String taskIdentityPath = task.getIdentityPath().getPath();
+                return BuildOperationDescriptor
+                    .displayName("Compile Java for " + taskIdentityPath)
+                    .details(new CompileJavaBuildOperationType.Details() {
+                    @Override
+                    public String getTaskIdentityPath() {
+                        return taskIdentityPath;
+                    }
+                });
             }
 
             @Override
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompileJavaBuildOperationType.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompileJavaBuildOperationType.java
index 959c771..f91249d 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompileJavaBuildOperationType.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/CompileJavaBuildOperationType.java
@@ -30,6 +30,10 @@
 public class CompileJavaBuildOperationType implements BuildOperationType<CompileJavaBuildOperationType.Details, CompileJavaBuildOperationType.Result> {
 
     public interface Details {
+        /**
+         * Returns the name of the task that is executing the compilation.
+         */
+        String getTaskIdentityPath();
     }
 
     public interface Result {
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DaemonJavaCompiler.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DaemonJavaCompiler.java
index 3c4d298..15a4578 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DaemonJavaCompiler.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/DaemonJavaCompiler.java
@@ -32,6 +32,8 @@
 import java.io.File;
 
 public class DaemonJavaCompiler extends AbstractDaemonCompiler<JavaCompileSpec> {
+
+    private static final String KEEP_DAEMON_ALIVE_PROPERTY = "org.gradle.internal.java.compile.daemon.keepAlive";
     private final Class<? extends Compiler<JavaCompileSpec>> compilerClass;
     private final Object[] compilerConstructorArguments;
     private final JavaForkOptionsFactory forkOptionsFactory;
@@ -68,10 +70,18 @@ protected DaemonForkOptions toDaemonForkOptions(JavaCompileSpec spec) {
         ClassPath compilerClasspath = classPathRegistry.getClassPath("JAVA-COMPILER");
         FlatClassLoaderStructure classLoaderStructure = new FlatClassLoaderStructure(new VisitableURLClassLoader.Spec("compiler", compilerClasspath.getAsURLs()));
 
+        String keepAliveModeStr = System.getProperty(KEEP_DAEMON_ALIVE_PROPERTY, KeepAliveMode.SESSION.name());
+        KeepAliveMode keepAliveMode;
+        try {
+            keepAliveMode = KeepAliveMode.valueOf(keepAliveModeStr);
+        } catch (IllegalArgumentException e) {
+            throw new IllegalStateException("Invalid value for system property " + KEEP_DAEMON_ALIVE_PROPERTY + ": " + keepAliveModeStr, e);
+        }
+
         return new DaemonForkOptionsBuilder(forkOptionsFactory)
             .javaForkOptions(javaForkOptions)
             .withClassLoaderStructure(classLoaderStructure)
-            .keepAliveMode(KeepAliveMode.SESSION)
+            .keepAliveMode(keepAliveMode)
             .build();
     }
 
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/tooling/JavaCompileTaskSuccessResultPostProcessor.java b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/tooling/JavaCompileTaskSuccessResultPostProcessor.java
index bfd635d..502f2f5 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/tooling/JavaCompileTaskSuccessResultPostProcessor.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/internal/tasks/compile/tooling/JavaCompileTaskSuccessResultPostProcessor.java
@@ -16,9 +16,12 @@
 
 package org.gradle.api.internal.tasks.compile.tooling;
 
+import org.gradle.api.internal.TaskInternal;
 import org.gradle.api.internal.tasks.compile.CompileJavaBuildOperationType;
 import org.gradle.api.internal.tasks.compile.CompileJavaBuildOperationType.Result.AnnotationProcessorDetails;
-import org.gradle.api.internal.tasks.execution.ExecuteTaskBuildOperationType;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.compile.JavaCompile;
 import org.gradle.internal.build.event.OperationResultPostProcessor;
 import org.gradle.internal.build.event.types.AbstractTaskResult;
 import org.gradle.internal.build.event.types.DefaultAnnotationProcessorResult;
@@ -26,7 +29,6 @@
 import org.gradle.internal.build.event.types.DefaultTaskSuccessResult;
 import org.gradle.internal.operations.BuildOperationDescriptor;
 import org.gradle.internal.operations.OperationFinishEvent;
-import org.gradle.internal.operations.OperationIdentifier;
 import org.gradle.internal.operations.OperationStartEvent;
 import org.gradle.tooling.internal.protocol.events.InternalJavaCompileTaskOperationResult.InternalAnnotationProcessorResult;
 
@@ -37,43 +39,35 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 public class JavaCompileTaskSuccessResultPostProcessor implements OperationResultPostProcessor {
+    private static final Logger LOGGER = Logging.getLogger(JavaCompileTaskSuccessResultPostProcessor.class);
 
-    private static final Object TASK_MARKER = new Object();
-    private final Map<OperationIdentifier, CompileJavaBuildOperationType.Result> results = new ConcurrentHashMap<>();
-    private final Map<OperationIdentifier, Object> parentsOfOperationsWithJavaCompileTaskAncestor = new ConcurrentHashMap<>();
+    private final Map<String, CompileJavaBuildOperationType.Result> results = new ConcurrentHashMap<>();
 
     @Override
     public void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) {
-        if (buildOperation.getDetails() instanceof ExecuteTaskBuildOperationType.Details) {
-            parentsOfOperationsWithJavaCompileTaskAncestor.put(buildOperation.getId(), TASK_MARKER);
-        } else if (buildOperation.getParentId() != null && parentsOfOperationsWithJavaCompileTaskAncestor.containsKey(buildOperation.getParentId())) {
-            parentsOfOperationsWithJavaCompileTaskAncestor.put(buildOperation.getId(), buildOperation.getParentId());
-        }
     }
 
     @Override
     public void finished(BuildOperationDescriptor buildOperation, OperationFinishEvent finishEvent) {
         if (finishEvent.getResult() instanceof CompileJavaBuildOperationType.Result) {
             CompileJavaBuildOperationType.Result result = (CompileJavaBuildOperationType.Result) finishEvent.getResult();
-            OperationIdentifier taskBuildOperationId = findTaskOperationId(buildOperation.getParentId());
-            results.put(taskBuildOperationId, result);
+            CompileJavaBuildOperationType.Details details = (CompileJavaBuildOperationType.Details) buildOperation.getDetails();
+            if (details == null) {
+                throw new IllegalStateException("No details for " + buildOperation.getDisplayName() + ", which is required for proper result tracking");
+            }
+            results.put(details.getTaskIdentityPath(), result);
         }
-        parentsOfOperationsWithJavaCompileTaskAncestor.remove(buildOperation.getId());
-    }
-
-    private OperationIdentifier findTaskOperationId(OperationIdentifier id) {
-        Object parent = parentsOfOperationsWithJavaCompileTaskAncestor.get(id);
-        if (parent == TASK_MARKER) {
-            return id;
-        }
-        return findTaskOperationId((OperationIdentifier) parent);
     }
 
     @Override
-    public AbstractTaskResult process(AbstractTaskResult taskResult, OperationIdentifier taskBuildOperationId) {
-        CompileJavaBuildOperationType.Result compileResult = results.remove(taskBuildOperationId);
-        if (taskResult instanceof DefaultTaskSuccessResult && compileResult != null) {
-            return new DefaultJavaCompileTaskSuccessResult((DefaultTaskSuccessResult) taskResult, toAnnotationProcessorResults(compileResult.getAnnotationProcessorDetails()));
+    public AbstractTaskResult process(AbstractTaskResult taskResult, TaskInternal taskInternal) {
+        CompileJavaBuildOperationType.Result compileResult = results.remove(taskInternal.getIdentityPath().getPath());
+        if (taskResult instanceof DefaultTaskSuccessResult) {
+            if (compileResult != null) {
+                return new DefaultJavaCompileTaskSuccessResult((DefaultTaskSuccessResult) taskResult, toAnnotationProcessorResults(compileResult.getAnnotationProcessorDetails()));
+            } else if (taskInternal instanceof JavaCompile) {
+                LOGGER.info("No compile result for " + taskInternal.getIdentityPath());
+            }
         }
         return taskResult;
     }
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/JavaExec.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/JavaExec.java
index 9223ed7..7f038a2 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/tasks/JavaExec.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/JavaExec.java
@@ -23,6 +23,7 @@
 import org.gradle.api.internal.ConventionTask;
 import org.gradle.api.jvm.ModularitySpec;
 import org.gradle.api.model.ObjectFactory;
+import org.gradle.api.provider.ListProperty;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.provider.ProviderFactory;
@@ -52,6 +53,8 @@
 import java.util.List;
 import java.util.Map;
 
+import static java.util.Collections.emptyList;
+
 /**
  * Executes a Java application in a child process.
  * <p>
@@ -111,12 +114,14 @@
  */
 @DisableCachingByDefault(because = "Gradle would require more information to cache this task")
 public abstract class JavaExec extends ConventionTask implements JavaExecSpec {
+
     private final DefaultJavaExecSpec javaExecSpec;
     private final Property<String> mainModule;
     private final Property<String> mainClass;
     private final ModularitySpec modularity;
     private final Property<ExecResult> execResult;
     private final Property<JavaLauncher> javaLauncher;
+    private final ListProperty<String> jvmArguments;
 
     public JavaExec() {
         ObjectFactory objectFactory = getObjectFactory();
@@ -124,8 +129,12 @@ public JavaExec() {
         mainClass = objectFactory.property(String.class);
         modularity = objectFactory.newInstance(DefaultModularitySpec.class);
         execResult = objectFactory.property(ExecResult.class);
-
         javaExecSpec = objectFactory.newInstance(DefaultJavaExecSpec.class);
+
+        Provider<Iterable<String>> jvmArgumentsConvention = getProviderFactory().provider(this::jvmArgsConventionValue);
+
+        jvmArguments = objectFactory.listProperty(String.class).convention(jvmArgumentsConvention);
+
         javaExecSpec.getMainClass().convention(mainClass);
         javaExecSpec.getMainModule().convention(mainModule);
         javaExecSpec.getModularity().getInferModulePath().convention(modularity.getInferModulePath());
@@ -142,7 +151,12 @@ public JavaExec() {
     @TaskAction
     public void exec() {
         validateExecutableMatchesToolchain();
-        setJvmArgs(getJvmArgs()); // convention mapping for 'jvmArgs'
+
+        List<String> jvmArgs = jvmArguments.getOrNull();
+        if (jvmArgs != null) {
+            javaExecSpec.setExtraJvmArgs(jvmArgs);
+        }
+
         JavaExecAction javaExecAction = getExecActionFactory().newJavaExecAction();
         javaExecSpec.copyTo(javaExecAction);
         String effectiveExecutable = getJavaLauncher().get().getExecutablePath().toString();
@@ -155,8 +169,8 @@ private void validateExecutableMatchesToolchain() {
         File toolchainExecutable = getJavaLauncher().get().getExecutablePath().getAsFile();
         String customExecutable = getExecutable();
         JavaExecutableUtils.validateExecutable(
-                customExecutable, "Toolchain from `executable` property",
-                toolchainExecutable, "toolchain from `javaLauncher` property");
+            customExecutable, "Toolchain from `executable` property",
+            toolchainExecutable, "toolchain from `javaLauncher` property");
     }
 
     /**
@@ -188,7 +202,7 @@ public void setAllJvmArgs(Iterable<?> arguments) {
      */
     @Override
     public List<String> getJvmArgs() {
-        return javaExecSpec.getJvmArgs();
+        return jvmArguments.getOrNull();
     }
 
     /**
@@ -196,7 +210,8 @@ public List<String> getJvmArgs() {
      */
     @Override
     public void setJvmArgs(List<String> arguments) {
-        javaExecSpec.setJvmArgs(arguments);
+        jvmArguments.empty();
+        jvmArgs(arguments);
     }
 
     /**
@@ -204,7 +219,8 @@ public void setJvmArgs(List<String> arguments) {
      */
     @Override
     public void setJvmArgs(Iterable<?> arguments) {
-        javaExecSpec.setJvmArgs(arguments);
+        jvmArguments.empty();
+        jvmArgs(arguments);
     }
 
     /**
@@ -212,16 +228,23 @@ public void setJvmArgs(Iterable<?> arguments) {
      */
     @Override
     public JavaExec jvmArgs(Iterable<?> arguments) {
-        javaExecSpec.jvmArgs(arguments);
+        addJvmArguments(arguments);
+        javaExecSpec.checkDebugConfiguration(arguments);
         return this;
     }
 
+    private void addJvmArguments(Iterable<?> arguments) {
+        for (Object arg : arguments) {
+            jvmArguments.add(arg.toString());
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
     @Override
     public JavaExec jvmArgs(Object... arguments) {
-        javaExecSpec.jvmArgs(arguments);
+        jvmArgs(Arrays.asList(arguments));
         return this;
     }
 
@@ -728,6 +751,14 @@ public List<CommandLineArgumentProvider> getJvmArgumentProviders() {
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ListProperty<String> getJvmArguments() {
+        return jvmArguments;
+    }
+
+    /**
      * Returns the result for the command run by this task. The provider has no value if this task has not been executed yet.
      *
      * @return A provider of the result.
@@ -767,4 +798,9 @@ protected JavaToolchainService getJavaToolchainService() {
     protected ProviderFactory getProviderFactory() {
         throw new UnsupportedOperationException();
     }
+
+    private Iterable<String> jvmArgsConventionValue() {
+        Iterable<String> jvmArgs = getConventionMapping().getConventionValue(null, "jvmArgs", false);
+        return jvmArgs != null ? jvmArgs : emptyList();
+    }
 }
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/CompileOptions.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/CompileOptions.java
index 43e111b..b3d1bd0 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/CompileOptions.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/CompileOptions.java
@@ -544,12 +544,11 @@ public File getAnnotationProcessorGeneratedSourcesDirectory() {
      */
     @Deprecated
     public void setAnnotationProcessorGeneratedSourcesDirectory(@Nullable File file) {
-        // Enable this deprecation in 8.1+. See: https://github.com/gradle/gradle/issues/16782
-//        DeprecationLogger.deprecateProperty(CompileOptions.class, "annotationProcessorGeneratedSourcesDirectory")
-//            .replaceWith("generatedSourceOutputDirectory")
-//            .willBeRemovedInGradle9()
-//            .withDslReference()
-//            .nagUser();
+        DeprecationLogger.deprecateProperty(CompileOptions.class, "annotationProcessorGeneratedSourcesDirectory")
+            .replaceWith("generatedSourceOutputDirectory")
+            .willBeRemovedInGradle9()
+            .withDslReference()
+            .nagUser();
 
         this.generatedSourceOutputDirectory.set(file);
     }
@@ -558,14 +557,16 @@ public void setAnnotationProcessorGeneratedSourcesDirectory(@Nullable File file)
      * Sets the directory to place source files generated by annotation processors.
      *
      * @since 4.3
+     *
+     * @deprecated Use {@link #getGeneratedSourceOutputDirectory()}.set() instead. This method will be removed in Gradle 9.0.
      */
+    @Deprecated
     public void setAnnotationProcessorGeneratedSourcesDirectory(Provider<File> file) {
-        // Enable this deprecation in 8.1+.
-//        DeprecationLogger.deprecateProperty(CompileOptions.class, "annotationProcessorGeneratedSourcesDirectory")
-//            .replaceWith("generatedSourceOutputDirectory")
-//            .willBeRemovedInGradle9()
-//            .withDslReference()
-//            .nagUser();
+        DeprecationLogger.deprecateProperty(CompileOptions.class, "annotationProcessorGeneratedSourcesDirectory")
+            .replaceWith("generatedSourceOutputDirectory")
+            .willBeRemovedInGradle9()
+            .withDslReference()
+            .nagUser();
 
         this.generatedSourceOutputDirectory.fileProvider(file);
     }
diff --git a/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/JavaCompile.java b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/JavaCompile.java
index e6749cb5..6193838 100644
--- a/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/JavaCompile.java
+++ b/subprojects/language-java/src/main/java/org/gradle/api/tasks/compile/JavaCompile.java
@@ -75,8 +75,6 @@
 import java.util.List;
 import java.util.concurrent.Callable;
 
-import static com.google.common.base.Preconditions.checkState;
-
 /**
  * Compiles Java source files.
  *
@@ -258,19 +256,26 @@ private void validateForkOptionsMatchToolchain() {
 
         JavaCompiler javaCompilerTool = getJavaCompiler().get();
         File toolchainJavaHome = javaCompilerTool.getMetadata().getInstallationPath().getAsFile();
-        File toolchainExecutable = javaCompilerTool.getExecutablePath().getAsFile();
 
         ForkOptions forkOptions = getOptions().getForkOptions();
         File customJavaHome = forkOptions.getJavaHome();
-        checkState(
-            customJavaHome == null || customJavaHome.equals(toolchainJavaHome),
-            "Toolchain from `javaHome` property on `ForkOptions` does not match toolchain from `javaCompiler` property"
-        );
+        if (customJavaHome != null) {
+            JavaExecutableUtils.validateMatchingFiles(
+                customJavaHome, "Toolchain from `javaHome` property on `ForkOptions`",
+                toolchainJavaHome, "toolchain from `javaCompiler` property"
+            );
+        }
 
         String customExecutablePath = forkOptions.getExecutable();
-        JavaExecutableUtils.validateExecutable(
-                customExecutablePath, "Toolchain from `executable` property on `ForkOptions`",
-                toolchainExecutable, "toolchain from `javaCompiler` property");
+        if (customExecutablePath != null) {
+            // We do not match the custom executable against the compiler executable from the toolchain (javac),
+            // because the custom executable can be set to the path of another tool in the toolchain such as a launcher (java).
+            File customExecutableJavaHome = JavaExecutableUtils.resolveJavaHomeOfExecutable(customExecutablePath);
+            JavaExecutableUtils.validateMatchingFiles(
+                customExecutableJavaHome, "Toolchain from `executable` property on `ForkOptions`",
+                toolchainJavaHome, "toolchain from `javaCompiler` property"
+            );
+        }
     }
 
     private boolean isToolchainCompatibleWithJava8() {
diff --git a/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/package-info.java b/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/package-info.java
index 3db466c..aa79566 100644
--- a/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/package-info.java
+++ b/subprojects/language-java/src/main/java/org/gradle/language/java/artifact/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Classes representing artifacts relevant to the Java language.
  */
-package org.gradle.language.java.artifact;
\ No newline at end of file
+package org.gradle.language.java.artifact;
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JdkToolsTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JdkToolsTest.groovy
index 405a5e3..164c135 100644
--- a/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JdkToolsTest.groovy
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/internal/tasks/compile/JdkToolsTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.api.JavaVersion
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Specification
 import spock.lang.Subject
 
@@ -49,7 +49,7 @@
         thrown IllegalStateException
     }
 
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     def "throws when tools doesn't contain compiler"() {
         when:
         if (defaultCompilerClassAlreadyInjectedIntoExtensionClassLoader()) {
diff --git a/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/JavaExecTest.groovy b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/JavaExecTest.groovy
index aa60eed..b5bdfca 100644
--- a/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/JavaExecTest.groovy
+++ b/subprojects/language-java/src/test/groovy/org/gradle/api/tasks/JavaExecTest.groovy
@@ -24,6 +24,15 @@
 
 class JavaExecTest extends AbstractProjectBuilderSpec {
 
+    def 'Jvm arguments are empty by default'() {
+        when:
+        def task = project.tasks.create("run", JavaExec)
+
+        then:
+        task.jvmArgs != null
+        task.jvmArgs.isEmpty()
+    }
+
     def 'fails if custom executable does not exist'() {
         def task = project.tasks.create("run", JavaExec)
         def invalidExecutable = temporaryFolder.file("invalid")
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/package-info.java b/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/package-info.java
index 8713f48..9ed8f58 100644
--- a/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/package-info.java
+++ b/subprojects/language-jvm/src/main/java/org/gradle/api/tasks/compile/package-info.java
@@ -17,4 +17,4 @@
 /**
  * The compilation {@link org.gradle.api.Task} implementations.
  */
-package org.gradle.api.tasks.compile;
\ No newline at end of file
+package org.gradle.api.tasks.compile;
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/JvmPluginServiceRegistry.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/JvmPluginServiceRegistry.java
index e637bb8..476420c 100644
--- a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/JvmPluginServiceRegistry.java
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/internal/JvmPluginServiceRegistry.java
@@ -31,7 +31,7 @@ public void registerBuildServices(ServiceRegistration registration) {
     }
 
     private static class ComponentRegistrationAction {
-        /***
+        /**
          * @param registration unused parameter required by convention, see {@link org.gradle.internal.service.DefaultServiceRegistry}.
          */
         public void configure(ServiceRegistration registration,
diff --git a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/package-info.java b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/package-info.java
index 4095f79..a6d5ade 100644
--- a/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/package-info.java
+++ b/subprojects/language-jvm/src/main/java/org/gradle/language/jvm/tasks/package-info.java
@@ -17,5 +17,4 @@
 /**
  * Tasks for support for JVM languages.
  */
-@org.gradle.api.Incubating
 package org.gradle.language.jvm.tasks;
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalBuildIntegrationTest.groovy
index 4330749..f15de68 100755
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalBuildIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIncrementalBuildIntegrationTest.groovy
@@ -23,11 +23,10 @@
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.GUtil
 import org.junit.Assume
-import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
@@ -104,7 +103,7 @@
         allSkipped()
     }
 
-    @IgnoreIf({!TestPrecondition.CAN_INSTALL_EXECUTABLE.fulfilled})
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "rebuilds executable with source file change"() {
         given:
@@ -158,7 +157,7 @@
         }
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "recompiles library and relinks executable after library source file change"() {
         given:
@@ -292,7 +291,7 @@
         }
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "rebuilds binary with compiler option change"() {
         given:
@@ -327,7 +326,7 @@
         install.exec().out == app.frenchOutput
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @RequiresInstalledToolChain(SUPPORTS_32_AND_64)
     @ToBeFixedForConfigurationCache
     def "rebuilds binary with target platform change"() {
@@ -468,7 +467,7 @@
         then:
         String objectFilesPath = "build/objs/hello/static/hello${sourceType}"
         def oldObjFile = objectFileFor(librarySourceFiles[0], objectFilesPath)
-        def newObjFile = objectFileFor( librarySourceFiles[0].getParentFile().file("changed_${librarySourceFiles[0].name}"), objectFilesPath)
+        def newObjFile = objectFileFor(librarySourceFiles[0].getParentFile().file("changed_${librarySourceFiles[0].name}"), objectFilesPath)
         assert oldObjFile.file
         assert !newObjFile.file
 
@@ -504,7 +503,7 @@
     @ToBeFixedForConfigurationCache
     def "recompiles binary when imported header file changes"() {
         sourceFile.text = sourceFile.text.replaceFirst('#include "hello.h"', "#import \"hello.h\"")
-        if(buildingCorCppWithGcc()) {
+        if (buildingCorCppWithGcc()) {
             buildFile << """
                 model {
                     //support for #import on c/cpp is deprecated in gcc
@@ -529,7 +528,7 @@
         executedAndNotSkipped libraryCompileTask
         executedAndNotSkipped mainCompileTask
 
-        if(objectiveCWithAslr){
+        if (objectiveCWithAslr) {
             executed ":linkHelloSharedLibrary", ":helloSharedLibrary"
             executed ":linkMainExecutable", ":mainExecutable"
         } else {
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIntegrationTest.groovy
index b0537e6..4bfcc63 100755
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativeLanguageIntegrationTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 abstract class AbstractNativeLanguageIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
 
@@ -109,7 +109,7 @@
         mainExecutable.exec().out == helloWorldApp.frenchOutput
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "install and run executable with dependencies"() {
         given:
@@ -143,7 +143,7 @@
         install.exec().out == helloWorldApp.englishOutput
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "install and run executable with dependencies and customized installation"() {
         given:
@@ -177,7 +177,7 @@
         install.exec().out == helloWorldApp.englishOutput
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "build shared library and link into executable"() {
         given:
@@ -211,7 +211,7 @@
         install.exec().out == helloWorldApp.englishOutput
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "build static library and link into executable"() {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativePreCompiledHeaderIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativePreCompiledHeaderIntegrationTest.groovy
index 7ead9af..b97fa04 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativePreCompiledHeaderIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/AbstractNativePreCompiledHeaderIntegrationTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.language
 
 import org.apache.commons.lang.StringUtils
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.hamcrest.CoreMatchers
 import org.spockframework.util.TextUtil
 
@@ -417,7 +417,7 @@
         failure.assertThatCause(CoreMatchers.containsString("compiler failed while compiling prefix-headers"))
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "can build and run an executable with library using pch" () {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/DuplicateBaseNamesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/DuplicateBaseNamesIntegrationTest.groovy
index 6b567f5..cc2f226 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/DuplicateBaseNamesIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/DuplicateBaseNamesIntegrationTest.groovy
@@ -16,8 +16,8 @@
 
 package org.gradle.language
 
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.SourceFile
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.language.fixtures.app.DuplicateAssemblerBaseNamesTestApp
 import org.gradle.language.fixtures.app.DuplicateCBaseNamesTestApp
 import org.gradle.language.fixtures.app.DuplicateCppBaseNamesTestApp
@@ -28,8 +28,8 @@
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.TestNativeComponent
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.SUPPORTS_32
@@ -151,7 +151,7 @@
     }
 
     @RequiresInstalledToolChain(GCC_COMPATIBLE)
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @ToBeFixedForConfigurationCache
     def "can have objectiveC and objectiveCpp source files with same name in different directories"() {
         setup:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIncrementalBuildIntegrationTest.groovy
index 736e878..5d4a0d9 100755
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIncrementalBuildIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/assembler/AssemblyLanguageIncrementalBuildIntegrationTest.groovy
@@ -24,8 +24,8 @@
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.MixedLanguageHelloWorldApp
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 @RequiresInstalledToolChain(ToolChainRequirement.SUPPORTS_32_AND_64)
 @UnsupportedWithConfigurationCache(because = "setup fails")
@@ -76,7 +76,7 @@
         allSkipped()
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     def "reassembles binary with assembler option change"() {
         when:
         buildFile << """
@@ -104,7 +104,10 @@
         install.exec().out == app.englishOutput
     }
 
-    @Requires([TestPrecondition.NOT_WINDOWS, TestPrecondition.CAN_INSTALL_EXECUTABLE])
+    @Requires([
+        UnitTestPreconditions.CanInstallExecutable,
+        UnitTestPreconditions.NotWindows
+    ])
     def "reassembles binary with target platform change"() {
         when:
         buildFile.text = buildFile.text.replace("i386", "x86-64")
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIntegrationTest.groovy
index 90725d1..57e9acc 100755
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CLanguageIntegrationTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.nativeplatform.fixtures.app.CCompilerDetectingTestApp
 import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import static org.gradle.util.Matchers.containsText
@@ -250,7 +250,7 @@
         }
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     @ToBeFixedForConfigurationCache
     def "can compile and link C code using standard macOS framework"() {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CPreCompiledHeaderSourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CPreCompiledHeaderSourcesIntegrationTest.groovy
index e2d17e8..51c146f 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CPreCompiledHeaderSourcesIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/c/CPreCompiledHeaderSourcesIntegrationTest.groovy
@@ -20,11 +20,11 @@
 import org.gradle.language.AbstractNativePreCompiledHeaderIntegrationTest
 import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class CPreCompiledHeaderSourcesIntegrationTest extends AbstractNativePreCompiledHeaderIntegrationTest {
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     @ToBeFixedForConfigurationCache
     def "can compile and link C code with precompiled headers using standard macOS framework" () {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppApplicationPublishingIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppApplicationPublishingIntegrationTest.groovy
index 03becac..d05e374 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppApplicationPublishingIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppApplicationPublishingIntegrationTest.groovy
@@ -24,8 +24,8 @@
 import org.gradle.nativeplatform.fixtures.app.CppAppWithLibrary
 import org.gradle.nativeplatform.fixtures.app.CppLogger
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.MachineArchitecture.ARCHITECTURE_ATTRIBUTE
 import static org.gradle.nativeplatform.MachineArchitecture.X86
@@ -466,7 +466,7 @@
     }
 
     // macOS can only build 64-bit under 10.14+
-    @Requires(TestPrecondition.NOT_MAC_OS_X)
+    @Requires(UnitTestPreconditions.NotMacOs)
     @RequiresInstalledToolChain(ToolChainRequirement.SUPPORTS_32_AND_64)
     @ToBeFixedForConfigurationCache
     def "can publish the binaries of an application with multiple target architectures to a Maven repository"() {
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppFrameworkIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppFrameworkIntegrationTest.groovy
index 4b7e027..91429e0 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppFrameworkIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppFrameworkIntegrationTest.groovy
@@ -18,11 +18,11 @@
 
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class CppFrameworkIntegrationTest extends AbstractInstalledToolChainIntegrationSpec implements CppTaskNames {
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     @ToBeFixedForConfigurationCache
     def "can compile and link C++ code using standard macOS framework"() {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIntegrationTest.groovy
index e60d2ba..da16331 100755
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageIntegrationTest.groovy
@@ -23,8 +23,8 @@
 import org.gradle.nativeplatform.fixtures.app.CppCompilerDetectingTestApp
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Assume
 
 import static org.gradle.util.Matchers.containsText
@@ -85,7 +85,7 @@
         output.contains("Found all include files for ':compileMainSharedLibraryMainCpp'")
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     @ToBeFixedForConfigurationCache
     def "can compile and link C++ code using standard macOS framework"() {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageParallelIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageParallelIntegrationTest.groovy
index 6971f44..69406fa 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageParallelIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppLanguageParallelIntegrationTest.groovy
@@ -23,14 +23,14 @@
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 
 class CppLanguageParallelIntegrationTest extends AbstractNativeSoftwareModelParallelIntegrationTest {
     HelloWorldApp app = new CppHelloWorldApp()
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "can produce multiple executables that use a library from a single project in parallel"() {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppPreCompiledHeaderSourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppPreCompiledHeaderSourcesIntegrationTest.groovy
index 5d8f25e..04fad1c 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppPreCompiledHeaderSourcesIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppPreCompiledHeaderSourcesIntegrationTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.language.AbstractNativePreCompiledHeaderIntegrationTest
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class CppPreCompiledHeaderSourcesIntegrationTest extends AbstractNativePreCompiledHeaderIntegrationTest implements DirectoryBuildCacheFixture {
 
@@ -48,7 +48,7 @@
             "  'Pre-compiled headers are used' satisfied"
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     @ToBeFixedForConfigurationCache
     def "can compile and link C++ code with precompiled headers using standard macOS framework" () {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppToolChainChangesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppToolChainChangesIntegrationTest.groovy
index c36d5bd..0199bbf 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppToolChainChangesIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/cpp/CppToolChainChangesIntegrationTest.groovy
@@ -21,12 +21,11 @@
 import org.gradle.nativeplatform.fixtures.AvailableToolChains
 import org.gradle.nativeplatform.fixtures.AvailableToolChains.InstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.opentest4j.TestAbortedException
 
-import static org.gradle.util.TestPrecondition.NOT_MAC_OS_X_M1
-
-@Requires(NOT_MAC_OS_X_M1)
+@Requires(UnitTestPreconditions.NotMacOsM1)
 class CppToolChainChangesIntegrationTest extends AbstractIntegrationSpec {
 
     def setup() {
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/NativeLanguageSamplesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/NativeLanguageSamplesIntegrationTest.groovy
index 56d2d7e..74f73d5 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/NativeLanguageSamplesIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/nativeplatform/NativeLanguageSamplesIntegrationTest.groovy
@@ -15,21 +15,24 @@
  */
 package org.gradle.language.nativeplatform
 
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.SUPPORTS_32_AND_64
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.VISUALCPP
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class NativeLanguageSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     @Rule final TestNameTestDirectoryProvider testDirProvider = new TestNameTestDirectoryProvider(getClass())
     @Rule public final Sample assembler = sample(testDirProvider, 'assembler')
@@ -96,7 +99,7 @@
     }
 
     @RequiresInstalledToolChain(GCC_COMPATIBLE)
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @ToBeFixedForConfigurationCache
     def "objectiveC"() {
         given:
@@ -113,7 +116,7 @@
     }
 
     @RequiresInstalledToolChain(GCC_COMPATIBLE)
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @ToBeFixedForConfigurationCache
     def "objectiveCpp"() {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/MixedObjectiveCIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/MixedObjectiveCIntegrationTest.groovy
index 6663f2f..bf5e1bd 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/MixedObjectiveCIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/MixedObjectiveCIntegrationTest.groovy
@@ -20,13 +20,16 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.MixedObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires([TestPrecondition.NOT_WINDOWS, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.NotWindows,
+    UnitTestPreconditions.NotMacOs
+])
 class MixedObjectiveCIntegrationTest extends AbstractNativeLanguageIntegrationTest {
 
     @Override
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy
index 824a27d..a5be55b 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalBuildIntegrationTest.groovy
@@ -21,13 +21,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCLanguageIncrementalBuildIntegrationTest extends AbstractNativeLanguageIncrementalBuildIntegrationTest {
 
     @Override
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy
index 75482e2..e98d6cf 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIncrementalCompileIntegrationTest.groovy
@@ -21,13 +21,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCLanguageIncrementalCompileIntegrationTest extends AbstractNativeLanguageIncrementalCompileIntegrationTest {
     @Override
     IncrementalHelloWorldApp getHelloWorldApp() {
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy
index 59475ff..c79d3d9 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageIntegrationTest.groovy
@@ -19,13 +19,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest{
 
     @Override
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageParallelIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageParallelIntegrationTest.groovy
index 7ecbee4..7de0382 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageParallelIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCLanguageParallelIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCLanguageParallelIntegrationTest extends AbstractNativeSoftwareModelParallelIntegrationTest {
     HelloWorldApp app = new ObjectiveCHelloWorldApp()
 }
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCPreCompiledHeaderSourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCPreCompiledHeaderSourcesIntegrationTest.groovy
index afd77bc..8ab6601 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCPreCompiledHeaderSourcesIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivec/ObjectiveCPreCompiledHeaderSourcesIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCPreCompiledHeaderSourcesIntegrationTest extends AbstractNativePreCompiledHeaderIntegrationTest {
     @Override
     IncrementalHelloWorldApp getApp() {
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy
index def31b9..d07b129 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalBuildIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCppLanguageIncrementalBuildIntegrationTest  extends ObjectiveCLanguageIncrementalBuildIntegrationTest{
     @Override
     IncrementalHelloWorldApp getHelloWorldApp() {
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy
index e086b80..9bd6fda 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIncrementalCompileIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCppLanguageIncrementalCompileIntegrationTest extends ObjectiveCLanguageIncrementalCompileIntegrationTest {
     @Override
     IncrementalHelloWorldApp getHelloWorldApp() {
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy
index 2fd5558..c740f01 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCppLanguageIntegrationTest extends AbstractNativeLanguageIntegrationTest {
 
     @Override
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageParallelIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageParallelIntegrationTest.groovy
index 7b1663b..41147fe 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageParallelIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppLanguageParallelIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCppLanguageParallelIntegrationTest extends AbstractNativeSoftwareModelParallelIntegrationTest {
     HelloWorldApp app = new ObjectiveCppHelloWorldApp()
 }
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppPreCompiledHeaderSourcesIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppPreCompiledHeaderSourcesIntegrationTest.groovy
index cfd7541..c38b11f 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppPreCompiledHeaderSourcesIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/objectivecpp/ObjectiveCppPreCompiledHeaderSourcesIntegrationTest.groovy
@@ -20,13 +20,13 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.IncrementalHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ObjectiveCppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class ObjectiveCppPreCompiledHeaderSourcesIntegrationTest extends AbstractNativePreCompiledHeaderIntegrationTest {
     @Override
     IncrementalHelloWorldApp getApp() {
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesUnsupportedIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesUnsupportedIntegrationTest.groovy
index 8635ffd..6f88716 100755
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesUnsupportedIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/rc/WindowsResourcesUnsupportedIntegrationTest.groovy
@@ -21,14 +21,14 @@
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class WindowsResourcesUnsupportedIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
 
     HelloWorldApp helloWorldApp = new CppHelloWorldApp()
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @ToBeFixedForConfigurationCache
     def "resource files are ignored on unsupported platforms"() {
         given:
@@ -60,7 +60,7 @@
         notExecuted(":compileMainExecutableMainRc")
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     @RequiresInstalledToolChain(ToolChainRequirement.GCC_COMPATIBLE)
     def "reasonable error message when attempting to compile resource files with unsupported tool chain"() {
         given:
diff --git a/subprojects/language-native/src/integTest/groovy/org/gradle/language/swift/SwiftIncrementalCppInteroperabilityIntegrationTest.groovy b/subprojects/language-native/src/integTest/groovy/org/gradle/language/swift/SwiftIncrementalCppInteroperabilityIntegrationTest.groovy
index 6d891b6..e52d0f6 100644
--- a/subprojects/language-native/src/integTest/groovy/org/gradle/language/swift/SwiftIncrementalCppInteroperabilityIntegrationTest.groovy
+++ b/subprojects/language-native/src/integTest/groovy/org/gradle/language/swift/SwiftIncrementalCppInteroperabilityIntegrationTest.groovy
@@ -20,10 +20,10 @@
 import org.gradle.nativeplatform.fixtures.app.IncrementalSwiftModifyCppDepApp
 import org.gradle.nativeplatform.fixtures.app.IncrementalSwiftModifyCppDepHeadersApp
 import org.gradle.nativeplatform.fixtures.app.IncrementalSwiftModifyCppDepModuleMapApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.NOT_MAC_OS_X)
+@Requires(UnitTestPreconditions.NotMacOs)
 class SwiftIncrementalCppInteroperabilityIntegrationTest extends AbstractSwiftMixedLanguageIntegrationTest {
     @ToBeFixedForConfigurationCache
     def "relinks but does not recompile when c++ sources change"() {
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppBinary.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppBinary.java
index be5f705..12aca4c 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppBinary.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppBinary.java
@@ -18,11 +18,12 @@
 
 import org.gradle.api.artifacts.ArtifactView;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
 import org.gradle.api.attributes.Usage;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.ProjectLayout;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.file.temp.TemporaryFileProvider;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.Property;
@@ -54,7 +55,7 @@ public class DefaultCppBinary extends DefaultNativeBinary implements CppBinary {
     private final Property<CppCompile> compileTaskProperty;
     private final NativeVariantIdentity identity;
 
-    public DefaultCppBinary(Names names, ObjectFactory objects, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, ConfigurationContainer configurations, Configuration componentImplementation, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity) {
+    public DefaultCppBinary(Names names, ObjectFactory objects, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, RoleBasedConfigurationContainerInternal configurations, Configuration componentImplementation, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity) {
         super(names, objects, componentImplementation);
         this.baseName = baseName;
         this.sourceFiles = sourceFiles;
@@ -66,8 +67,7 @@ public DefaultCppBinary(Names names, ObjectFactory objects, Provider<String> bas
 
         // TODO - reduce duplication with Swift binary
 
-        includePathConfiguration = configurations.create(names.withPrefix("cppCompile"));
-        includePathConfiguration.setCanBeConsumed(false);
+        includePathConfiguration = configurations.resolvableBucket(names.withPrefix("cppCompile"));
         includePathConfiguration.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.C_PLUS_PLUS_API));
         includePathConfiguration.getAttributes().attribute(DEBUGGABLE_ATTRIBUTE, identity.isDebuggable());
         includePathConfiguration.getAttributes().attribute(OPTIMIZED_ATTRIBUTE, identity.isOptimized());
@@ -75,8 +75,7 @@ public DefaultCppBinary(Names names, ObjectFactory objects, Provider<String> bas
         includePathConfiguration.getAttributes().attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, identity.getTargetMachine().getArchitecture());
         includePathConfiguration.extendsFrom(getImplementationDependencies());
 
-        Configuration nativeLink = configurations.create(names.withPrefix("nativeLink"));
-        nativeLink.setCanBeConsumed(false);
+        Configuration nativeLink = configurations.resolvableBucket(names.withPrefix("nativeLink"));
         nativeLink.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.NATIVE_LINK));
         nativeLink.getAttributes().attribute(DEBUGGABLE_ATTRIBUTE, identity.isDebuggable());
         nativeLink.getAttributes().attribute(OPTIMIZED_ATTRIBUTE, identity.isOptimized());
@@ -84,8 +83,7 @@ public DefaultCppBinary(Names names, ObjectFactory objects, Provider<String> bas
         nativeLink.getAttributes().attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, identity.getTargetMachine().getArchitecture());
         nativeLink.extendsFrom(getImplementationDependencies());
 
-        Configuration nativeRuntime = configurations.create(names.withPrefix("nativeRuntime"));
-        nativeRuntime.setCanBeConsumed(false);
+        @SuppressWarnings("deprecation") Configuration nativeRuntime = configurations.createWithRole(names.withPrefix("nativeRuntime"), ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE);
         nativeRuntime.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.NATIVE_RUNTIME));
         nativeRuntime.getAttributes().attribute(DEBUGGABLE_ATTRIBUTE, identity.isDebuggable());
         nativeRuntime.getAttributes().attribute(OPTIMIZED_ATTRIBUTE, identity.isOptimized());
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppExecutable.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppExecutable.java
index 9565ff6..f92af88 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppExecutable.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppExecutable.java
@@ -18,7 +18,6 @@
 
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.file.ConfigurableFileCollection;
@@ -26,6 +25,7 @@
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.RegularFile;
 import org.gradle.api.file.RegularFileProperty;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.component.SoftwareComponentInternal;
 import org.gradle.api.internal.component.UsageContext;
 import org.gradle.api.model.ObjectFactory;
@@ -59,7 +59,7 @@ public class DefaultCppExecutable extends DefaultCppBinary implements CppExecuta
     private final RegularFileProperty debuggerExecutableFile;
 
     @Inject
-    public DefaultCppExecutable(Names names, ObjectFactory objectFactory, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, ConfigurationContainer configurations, Configuration implementation, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity) {
+    public DefaultCppExecutable(Names names, ObjectFactory objectFactory, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, RoleBasedConfigurationContainerInternal configurations, Configuration implementation, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity) {
         super(names, objectFactory, baseName, sourceFiles, componentHeaderDirs, configurations, implementation, targetPlatform, toolChain, platformToolProvider, identity);
         this.executableFile = objectFactory.fileProperty();
         this.executableFileProducer = objectFactory.property(Task.class);
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppLibrary.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppLibrary.java
index a2aba00..bb4bef7 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppLibrary.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppLibrary.java
@@ -18,13 +18,14 @@
 
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.attributes.Usage;
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.Property;
@@ -55,8 +56,9 @@ public class DefaultCppLibrary extends DefaultCppComponent implements CppLibrary
     private final MainLibraryVariant mainVariant;
     private final DefaultLibraryDependencies dependencies;
 
+    @SuppressWarnings("deprecation")
     @Inject
-    public DefaultCppLibrary(String name, ObjectFactory objectFactory, ConfigurationContainer configurations, ImmutableAttributesFactory immutableAttributesFactory) {
+    public DefaultCppLibrary(String name, ObjectFactory objectFactory, RoleBasedConfigurationContainerInternal configurations, ImmutableAttributesFactory immutableAttributesFactory) {
         super(name, objectFactory);
         this.objectFactory = objectFactory;
         this.developmentBinary = objectFactory.property(CppBinary.class);
@@ -70,9 +72,8 @@ public DefaultCppLibrary(String name, ObjectFactory objectFactory, Configuration
 
         Usage apiUsage = objectFactory.named(Usage.class, Usage.C_PLUS_PLUS_API);
 
-        apiElements = configurations.create(getNames().withSuffix("cppApiElements"));
+        apiElements = configurations.createWithRole(getNames().withSuffix("cppApiElements"), ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
         apiElements.extendsFrom(dependencies.getApiDependencies());
-        apiElements.setCanBeResolved(false);
         apiElements.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, apiUsage);
         apiElements.getAttributes().attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.DIRECTORY_TYPE);
 
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppSharedLibrary.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppSharedLibrary.java
index 585d7c1..43183d1 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppSharedLibrary.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppSharedLibrary.java
@@ -19,12 +19,12 @@
 import com.google.common.collect.Sets;
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.RegularFileProperty;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.component.SoftwareComponentInternal;
 import org.gradle.api.internal.component.UsageContext;
 import org.gradle.api.model.ObjectFactory;
@@ -56,7 +56,7 @@ public class DefaultCppSharedLibrary extends DefaultCppBinary implements CppShar
     private final ConfigurableFileCollection outputs;
 
     @Inject
-    public DefaultCppSharedLibrary(Names names, ObjectFactory objectFactory, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, ConfigurationContainer configurations, Configuration implementation, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity) {
+    public DefaultCppSharedLibrary(Names names, ObjectFactory objectFactory, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, RoleBasedConfigurationContainerInternal configurations, Configuration implementation, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity) {
         super(names, objectFactory, baseName, sourceFiles, componentHeaderDirs, configurations, implementation, targetPlatform, toolChain, platformToolProvider, identity);
         this.linkFile = objectFactory.fileProperty();
         this.linkFileProducer = objectFactory.property(Task.class);
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppStaticLibrary.java b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppStaticLibrary.java
index 2223756..0cecbe1 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppStaticLibrary.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/cpp/internal/DefaultCppStaticLibrary.java
@@ -19,13 +19,13 @@
 import com.google.common.collect.Sets;
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.RegularFile;
 import org.gradle.api.file.RegularFileProperty;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.component.SoftwareComponentInternal;
 import org.gradle.api.internal.component.UsageContext;
 import org.gradle.api.internal.provider.Providers;
@@ -57,7 +57,7 @@ public class DefaultCppStaticLibrary extends DefaultCppBinary implements CppStat
     private final ConfigurableFileCollection outputs;
 
     @Inject
-    public DefaultCppStaticLibrary(Names names, ObjectFactory objectFactory, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, ConfigurationContainer configurations, Configuration implementation, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity) {
+    public DefaultCppStaticLibrary(Names names, ObjectFactory objectFactory, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, RoleBasedConfigurationContainerInternal configurations, Configuration implementation, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity) {
         super(names, objectFactory, baseName, sourceFiles, componentHeaderDirs, configurations, implementation, targetPlatform, toolChain, platformToolProvider, identity);
         this.linkFile = objectFactory.fileProperty();
         this.linkFileProducer = objectFactory.property(Task.class);
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/internal/DefaultComponentDependencies.java b/subprojects/language-native/src/main/java/org/gradle/language/internal/DefaultComponentDependencies.java
index 93716f4..b179787 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/internal/DefaultComponentDependencies.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/internal/DefaultComponentDependencies.java
@@ -18,9 +18,9 @@
 
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ExternalModuleDependency;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.language.ComponentDependencies;
 
 import javax.inject.Inject;
@@ -29,10 +29,8 @@ public class DefaultComponentDependencies implements ComponentDependencies {
     private final Configuration implementation;
 
     @Inject
-    public DefaultComponentDependencies(ConfigurationContainer configurations, String implementationName) {
-        implementation = configurations.create(implementationName);
-        implementation.setCanBeConsumed(false);
-        implementation.setCanBeResolved(false);
+    public DefaultComponentDependencies(RoleBasedConfigurationContainerInternal configurations, String implementationName) {
+        implementation = configurations.bucket(implementationName);
     }
 
     public Configuration getImplementationDependencies() {
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/internal/DefaultLibraryDependencies.java b/subprojects/language-native/src/main/java/org/gradle/language/internal/DefaultLibraryDependencies.java
index be78a0e..d4b3db7 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/internal/DefaultLibraryDependencies.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/internal/DefaultLibraryDependencies.java
@@ -18,8 +18,8 @@
 
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ExternalModuleDependency;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.language.LibraryDependencies;
 
 import javax.inject.Inject;
@@ -28,11 +28,9 @@ public class DefaultLibraryDependencies extends DefaultComponentDependencies imp
     private final Configuration apiDependencies;
 
     @Inject
-    public DefaultLibraryDependencies(ConfigurationContainer configurations, String implementationName, String apiName) {
+    public DefaultLibraryDependencies(RoleBasedConfigurationContainerInternal configurations, String implementationName, String apiName) {
         super(configurations, implementationName);
-        apiDependencies = configurations.create(apiName);
-        apiDependencies.setCanBeConsumed(false);
-        apiDependencies.setCanBeResolved(false);
+        apiDependencies = configurations.bucket(apiName);
         getImplementationDependencies().extendsFrom(apiDependencies);
     }
 
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/plugins/NativeBasePlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/plugins/NativeBasePlugin.java
index a120a10..d2a480d 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/plugins/NativeBasePlugin.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/plugins/NativeBasePlugin.java
@@ -22,7 +22,6 @@
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ModuleVersionIdentifier;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
@@ -38,7 +37,10 @@
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.RegularFile;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.artifacts.transform.UnzipTransform;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.provider.Provider;
@@ -150,7 +152,7 @@ public void apply(final Project project) {
         addHeaderZipTransform(dependencyHandler, objects);
 
         // Add outgoing configurations and publications
-        final ConfigurationContainer configurations = project.getConfigurations();
+        final RoleBasedConfigurationContainerInternal configurations = ((ProjectInternal) project).getConfigurations();
 
         project.getDependencies().getAttributesSchema().attribute(LINKAGE_ATTRIBUTE).getDisambiguationRules().add(LinkageSelectionRule.class);
 
@@ -335,13 +337,12 @@ private void addTasksForComponentWithStaticLibrary(final TaskContainer tasks, fi
         });
     }
 
-    private void addOutgoingConfigurationForLinkUsage(SoftwareComponentContainer components, final ConfigurationContainer configurations) {
+    private void addOutgoingConfigurationForLinkUsage(SoftwareComponentContainer components, final RoleBasedConfigurationContainerInternal configurations) {
         components.withType(ConfigurableComponentWithLinkUsage.class, component -> {
             Names names = component.getNames();
 
-            Configuration linkElements = configurations.create(names.withSuffix("linkElements"));
+            @SuppressWarnings("deprecation") Configuration linkElements = configurations.createWithRole(names.withSuffix("linkElements"), ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
             linkElements.extendsFrom(component.getImplementationDependencies());
-            linkElements.setCanBeResolved(false);
             AttributeContainer attributes = component.getLinkAttributes();
             copyAttributesTo(attributes, linkElements);
 
@@ -351,13 +352,12 @@ private void addOutgoingConfigurationForLinkUsage(SoftwareComponentContainer com
         });
     }
 
-    private void addOutgoingConfigurationForRuntimeUsage(SoftwareComponentContainer components, final ConfigurationContainer configurations) {
+    private void addOutgoingConfigurationForRuntimeUsage(SoftwareComponentContainer components, final RoleBasedConfigurationContainerInternal configurations) {
         components.withType(ConfigurableComponentWithRuntimeUsage.class, component -> {
             Names names = component.getNames();
 
-            Configuration runtimeElements = configurations.create(names.withSuffix("runtimeElements"));
+            @SuppressWarnings("deprecation") Configuration runtimeElements = configurations.createWithRole(names.withSuffix("runtimeElements"), ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
             runtimeElements.extendsFrom(component.getImplementationDependencies());
-            runtimeElements.setCanBeResolved(false);
 
             AttributeContainer attributes = component.getRuntimeAttributes();
             copyAttributesTo(attributes, runtimeElements);
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/swift/internal/DefaultSwiftBinary.java b/subprojects/language-native/src/main/java/org/gradle/language/swift/internal/DefaultSwiftBinary.java
index 86847cd..27fe20e 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/swift/internal/DefaultSwiftBinary.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/swift/internal/DefaultSwiftBinary.java
@@ -29,6 +29,8 @@
 import org.gradle.api.attributes.Usage;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.RegularFileProperty;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.file.collections.FileCollectionAdapter;
 import org.gradle.api.internal.file.collections.MinimalFileSet;
 import org.gradle.api.internal.tasks.TaskDependencyFactory;
@@ -86,27 +88,25 @@ public DefaultSwiftBinary(Names names, final ObjectFactory objectFactory, TaskDe
         this.platformToolProvider = platformToolProvider;
 
         // TODO - reduce duplication with C++ binary
-        importPathConfiguration = configurations.create(names.withPrefix("swiftCompile"));
+        RoleBasedConfigurationContainerInternal rbConfigurations = (RoleBasedConfigurationContainerInternal) configurations;
+        importPathConfiguration = rbConfigurations.resolvableBucket(names.withPrefix("swiftCompile"));
         importPathConfiguration.extendsFrom(getImplementationDependencies());
-        importPathConfiguration.setCanBeConsumed(false);
         importPathConfiguration.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.SWIFT_API));
         importPathConfiguration.getAttributes().attribute(DEBUGGABLE_ATTRIBUTE, identity.isDebuggable());
         importPathConfiguration.getAttributes().attribute(OPTIMIZED_ATTRIBUTE, identity.isOptimized());
         importPathConfiguration.getAttributes().attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, identity.getTargetMachine().getOperatingSystemFamily());
         importPathConfiguration.getAttributes().attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, identity.getTargetMachine().getArchitecture());
 
-        Configuration nativeLink = configurations.create(names.withPrefix("nativeLink"));
+        Configuration nativeLink = rbConfigurations.resolvableBucket(names.withPrefix("nativeLink"));
         nativeLink.extendsFrom(getImplementationDependencies());
-        nativeLink.setCanBeConsumed(false);
         nativeLink.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.NATIVE_LINK));
         nativeLink.getAttributes().attribute(DEBUGGABLE_ATTRIBUTE, identity.isDebuggable());
         nativeLink.getAttributes().attribute(OPTIMIZED_ATTRIBUTE, identity.isOptimized());
         nativeLink.getAttributes().attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, identity.getTargetMachine().getOperatingSystemFamily());
         nativeLink.getAttributes().attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, identity.getTargetMachine().getArchitecture());
 
-        Configuration nativeRuntime = configurations.create(names.withPrefix("nativeRuntime"));
+        @SuppressWarnings("deprecation") Configuration nativeRuntime = rbConfigurations.createWithRole(names.withPrefix("nativeRuntime"), ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE);
         nativeRuntime.extendsFrom(getImplementationDependencies());
-        nativeRuntime.setCanBeConsumed(false);
         nativeRuntime.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.NATIVE_RUNTIME));
         nativeRuntime.getAttributes().attribute(DEBUGGABLE_ATTRIBUTE, identity.isDebuggable());
         nativeRuntime.getAttributes().attribute(OPTIMIZED_ATTRIBUTE, identity.isOptimized());
diff --git a/subprojects/language-native/src/main/java/org/gradle/language/swift/plugins/SwiftLibraryPlugin.java b/subprojects/language-native/src/main/java/org/gradle/language/swift/plugins/SwiftLibraryPlugin.java
index 317eb9f..a681e30 100644
--- a/subprojects/language-native/src/main/java/org/gradle/language/swift/plugins/SwiftLibraryPlugin.java
+++ b/subprojects/language-native/src/main/java/org/gradle/language/swift/plugins/SwiftLibraryPlugin.java
@@ -19,9 +19,11 @@
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.attributes.Usage;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.ProviderFactory;
@@ -84,7 +86,7 @@ public SwiftLibraryPlugin(NativeComponentFactory componentFactory, ToolChainSele
     public void apply(final Project project) {
         project.getPluginManager().apply(SwiftBasePlugin.class);
 
-        final ConfigurationContainer configurations = project.getConfigurations();
+        final RoleBasedConfigurationContainerInternal configurations = ((ProjectInternal) project).getConfigurations();
         final ObjectFactory objectFactory = project.getObjects();
         final ProviderFactory providers = project.getProviders();
 
@@ -146,12 +148,11 @@ private Stream<SwiftBinary> getDebugStaticHostStream() {
 
             library.getBinaries().whenElementKnown(SwiftSharedLibrary.class, sharedLibrary -> {
                 Names names = ((ComponentWithNames) sharedLibrary).getNames();
-                Configuration apiElements = configurations.create(names.withSuffix("SwiftApiElements"));
+                @SuppressWarnings("deprecation") Configuration apiElements = configurations.createWithRole(names.withSuffix("SwiftApiElements"), ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
                 // TODO This should actually extend from the api dependencies, but since Swift currently
                 // requires all dependencies to be treated like api dependencies (with transitivity) we just
                 // use the implementation dependencies here.  See https://bugs.swift.org/browse/SR-1393.
                 apiElements.extendsFrom(((DefaultSwiftSharedLibrary) sharedLibrary).getImplementationDependencies());
-                apiElements.setCanBeResolved(false);
                 apiElements.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.SWIFT_API));
                 apiElements.getAttributes().attribute(LINKAGE_ATTRIBUTE, Linkage.SHARED);
                 apiElements.getAttributes().attribute(DEBUGGABLE_ATTRIBUTE, sharedLibrary.isDebuggable());
@@ -162,12 +163,11 @@ private Stream<SwiftBinary> getDebugStaticHostStream() {
 
             library.getBinaries().whenElementKnown(SwiftStaticLibrary.class, staticLibrary -> {
                 Names names = ((ComponentWithNames) staticLibrary).getNames();
-                Configuration apiElements = configurations.create(names.withSuffix("SwiftApiElements"));
+                @SuppressWarnings("deprecation") Configuration apiElements = configurations.createWithRole(names.withSuffix("SwiftApiElements"), ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
                 // TODO This should actually extend from the api dependencies, but since Swift currently
                 // requires all dependencies to be treated like api dependencies (with transitivity) we just
                 // use the implementation dependencies here.  See https://bugs.swift.org/browse/SR-1393.
                 apiElements.extendsFrom(((DefaultSwiftStaticLibrary) staticLibrary).getImplementationDependencies());
-                apiElements.setCanBeResolved(false);
                 apiElements.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objectFactory.named(Usage.class, Usage.SWIFT_API));
                 apiElements.getAttributes().attribute(LINKAGE_ATTRIBUTE, Linkage.STATIC);
                 apiElements.getAttributes().attribute(DEBUGGABLE_ATTRIBUTE, staticLibrary.isDebuggable());
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/internal/DefaultCppBinaryTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/internal/DefaultCppBinaryTest.groovy
index d0d4283..175d3bc 100644
--- a/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/internal/DefaultCppBinaryTest.groovy
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/internal/DefaultCppBinaryTest.groovy
@@ -17,9 +17,9 @@
 package org.gradle.language.cpp.internal
 
 import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import org.gradle.api.provider.Provider
 import org.gradle.language.cpp.CppPlatform
 import org.gradle.language.nativeplatform.internal.Names
@@ -41,7 +41,7 @@
     def compile = Stub(Configuration)
     def link = Stub(Configuration)
     def runtime = Stub(Configuration)
-    def configurations = Stub(ConfigurationContainer)
+    def configurations = Stub(RoleBasedConfigurationContainerInternal)
 
     DefaultCppBinary binary
 
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppLibraryPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppLibraryPluginTest.groovy
index f16e0cb..92fda79 100644
--- a/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppLibraryPluginTest.groovy
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/cpp/plugins/CppLibraryPluginTest.groovy
@@ -31,9 +31,11 @@
 import org.gradle.testfixtures.ProjectBuilder
 import org.gradle.util.UsesNativeServices
 import org.junit.Rule
+import spock.lang.Ignore
 import spock.lang.Specification
 
 @UsesNativeServices
+@Ignore("https://github.com/gradle/gradle-private/issues/3818")
 class CppLibraryPluginTest extends Specification {
     @Rule
     TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass())
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/internal/DefaultComponentDependenciesTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/internal/DefaultComponentDependenciesTest.groovy
index f1de023..fe14538 100644
--- a/subprojects/language-native/src/test/groovy/org/gradle/language/internal/DefaultComponentDependenciesTest.groovy
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/internal/DefaultComponentDependenciesTest.groovy
@@ -18,23 +18,23 @@
 
 import org.gradle.api.Action
 import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.DependencySet
 import org.gradle.api.artifacts.ExternalModuleDependency
 import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import spock.lang.Specification
 
 
 class DefaultComponentDependenciesTest extends Specification {
-    def configurations = Stub(ConfigurationContainer)
+    def configurations = Stub(RoleBasedConfigurationContainerInternal)
     def dependencyFactory = Mock(DependencyHandler)
     def implDeps = Mock(Configuration)
     def deps = Mock(DependencySet)
     DefaultComponentDependencies dependencies
 
     def setup() {
-        configurations.create("impl") >> implDeps
+        configurations.bucket("impl") >> implDeps
         implDeps.dependencies >> deps
 
         dependencies = new DefaultComponentDependencies(configurations, "impl") {
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/internal/DefaultLibraryDependenciesTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/internal/DefaultLibraryDependenciesTest.groovy
index 94d0a5e..8f56f10 100644
--- a/subprojects/language-native/src/test/groovy/org/gradle/language/internal/DefaultLibraryDependenciesTest.groovy
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/internal/DefaultLibraryDependenciesTest.groovy
@@ -18,22 +18,22 @@
 
 import org.gradle.api.Action
 import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.DependencySet
 import org.gradle.api.artifacts.ExternalModuleDependency
 import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import spock.lang.Specification
 
 class DefaultLibraryDependenciesTest extends Specification {
-    def configurations = Stub(ConfigurationContainer)
+    def configurations = Stub(RoleBasedConfigurationContainerInternal)
     def dependencyFactory = Mock(DependencyHandler)
     def apiDeps = Mock(Configuration)
     def deps = Mock(DependencySet)
     DefaultLibraryDependencies dependencies
 
     def setup() {
-        configurations.create("api") >> apiDeps
+        configurations.bucket("api") >> apiDeps
         apiDeps.dependencies >> deps
 
         dependencies = new DefaultLibraryDependencies(configurations, "impl", "api") {
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginTest.groovy
index bcf9222..9b3b348 100644
--- a/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginTest.groovy
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/objectivecpp/plugins/ObjectiveCppPluginTest.groovy
@@ -22,7 +22,9 @@
 import org.gradle.language.objectivecpp.ObjectiveCppSourceSet
 import org.gradle.language.AbstractNativeComponentPluginTest
 import org.gradle.language.objectivecpp.tasks.ObjectiveCppCompile
+import spock.lang.Ignore
 
+@Ignore("https://github.com/gradle/gradle-private/issues/3818")
 class ObjectiveCppPluginTest extends AbstractNativeComponentPluginTest {
     @Override
     Class<? extends Plugin> getPluginClass() {
diff --git a/subprojects/language-native/src/test/groovy/org/gradle/language/swift/internal/DefaultSwiftBinaryTest.groovy b/subprojects/language-native/src/test/groovy/org/gradle/language/swift/internal/DefaultSwiftBinaryTest.groovy
index 345a066..ebbff4d 100644
--- a/subprojects/language-native/src/test/groovy/org/gradle/language/swift/internal/DefaultSwiftBinaryTest.groovy
+++ b/subprojects/language-native/src/test/groovy/org/gradle/language/swift/internal/DefaultSwiftBinaryTest.groovy
@@ -18,10 +18,11 @@
 
 import org.gradle.api.artifacts.ArtifactCollection
 import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.ResolvableDependencies
 import org.gradle.api.file.FileCollection
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import org.gradle.api.provider.Provider
 import org.gradle.language.cpp.internal.NativeVariantIdentity
 import org.gradle.language.nativeplatform.internal.Names
@@ -43,16 +44,16 @@
     def compile = Stub(Configuration)
     def link = Stub(Configuration)
     def runtime = Stub(Configuration)
-    def configurations = Stub(ConfigurationContainer)
+    def configurations = Stub(RoleBasedConfigurationContainerInternal)
     def incoming = Mock(ResolvableDependencies)
     DefaultSwiftBinary binary
 
     def setup() {
-        _ * configurations.create("swiftCompileDebug") >> compile
-        _ * configurations.create("nativeLinkDebug") >> link
-        _ * configurations.create("nativeRuntimeDebug") >> runtime
+        _ * configurations.resolvableBucket("swiftCompileDebug") >> compile
+        _ * configurations.resolvableBucket("nativeLinkDebug") >> link
+        _ * configurations.createWithRole('nativeRuntimeDebug', ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE) >> runtime
 
-        binary = new DefaultSwiftBinary(Names.of("mainDebug"), project.objects, project.taskDependencyFactory, Stub(Provider), false, Stub(FileCollection),  configurations, implementation, Stub(SwiftPlatform), Stub(NativeToolChainInternal), Stub(PlatformToolProvider), Stub(NativeVariantIdentity))
+        binary = new DefaultSwiftBinary(Names.of("mainDebug"), project.objects, project.taskDependencyFactory, Stub(Provider), false, Stub(FileCollection), configurations, implementation, Stub(SwiftPlatform), Stub(NativeToolChainInternal), Stub(PlatformToolProvider), Stub(NativeVariantIdentity))
     }
 
     def "compileModules is a transformed view of compile"() {
diff --git a/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/LanguageTaskNames.groovy b/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/LanguageTaskNames.groovy
index 9f6d0df..8d6006b 100644
--- a/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/LanguageTaskNames.groovy
+++ b/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/LanguageTaskNames.groovy
@@ -242,4 +242,4 @@
             return result
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/swift/SwiftTaskNames.groovy b/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/swift/SwiftTaskNames.groovy
index 23f7954..7af4352 100644
--- a/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/swift/SwiftTaskNames.groovy
+++ b/subprojects/language-native/src/testFixtures/groovy/org/gradle/language/swift/SwiftTaskNames.groovy
@@ -41,4 +41,4 @@
             return []
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/build.gradle.kts b/subprojects/launcher/build.gradle.kts
index 3295d8c..d418fca 100644
--- a/subprojects/launcher/build.gradle.kts
+++ b/subprojects/launcher/build.gradle.kts
@@ -42,6 +42,7 @@
     runtimeOnly(libs.commonsIo)
     runtimeOnly(libs.commonsLang)
     runtimeOnly(libs.slf4jApi)
+    runtimeOnly(project(":instrumentation-declarations"))
 
     manifestClasspath(project(":bootstrap"))
     manifestClasspath(project(":base-services"))
@@ -50,6 +51,8 @@
     manifestClasspath(project(":core"))
     manifestClasspath(project(":persistent-cache"))
 
+    agentsClasspath(project(":instrumentation-agent"))
+
     testImplementation(project(":internal-integ-testing"))
     testImplementation(project(":native"))
     testImplementation(project(":cli"))
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/CommandLineIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/CommandLineIntegrationSpec.groovy
index 942e46f..abd6766 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/CommandLineIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/CommandLineIntegrationSpec.groovy
@@ -22,8 +22,8 @@
 import org.gradle.integtests.fixtures.jvm.JDWPUtil
 import org.gradle.test.fixtures.ConcurrentTestUtil
 import org.gradle.test.fixtures.Flaky
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Assume
 import spock.lang.IgnoreIf
 import spock.lang.Issue
@@ -133,7 +133,7 @@
         jdwpClient.close()
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "can debug on explicitly any host"() {
         given:
         executer.requireDaemon().requireIsolatedDaemons()
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/ConfigurationOnDemandIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/ConfigurationOnDemandIntegrationTest.groovy
index 8422e5b..360b92e 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/ConfigurationOnDemandIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/ConfigurationOnDemandIntegrationTest.groovy
@@ -46,4 +46,4 @@
         then:
         fixture.assertProjectsConfigured(":", ":api")
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/ContinueIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/ContinueIntegrationTest.groovy
new file mode 100644
index 0000000..6bb357a
--- /dev/null
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/ContinueIntegrationTest.groovy
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 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
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.file.TestFile
+
+class ContinueIntegrationTest extends AbstractIntegrationSpec {
+
+    def "--continue flag PRESENT"() {
+        given:
+        buildScript()
+
+        when:
+        args("--continue")
+        fails(":failTask", ":successTask")
+
+        then:
+        output.contains("2 actionable tasks: 2 executed")
+    }
+
+    def "--continue flag MISSING"() {
+        given:
+        buildScript()
+
+        when:
+        fails(":failTask", ":successTask")
+
+        then:
+        output.contains("1 actionable task: 1 executed")
+    }
+
+    def "-Dorg.gradle.continue property TRUE"() {
+        given:
+        buildScript()
+
+        when:
+        args("-Dorg.gradle.continue=true")
+        fails(":failTask", ":successTask")
+
+        then:
+        output.contains("2 actionable tasks: 2 executed")
+    }
+
+    def "-Dorg.gradle.continue property FALSE"() {
+        given:
+        buildScript()
+
+        when:
+        args("-Dorg.gradle.continue=false")
+        fails(":failTask", ":successTask")
+
+        then:
+        output.contains("1 actionable task: 1 executed")
+    }
+
+    private TestFile buildScript() {
+        buildScript """
+            tasks.register("failTask") {
+                doFirst {
+                    throw new RuntimeException("failTask failed")
+                }
+            }
+
+            tasks.register("successTask") {
+                doFirst {
+                    println("successTask success")
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleConfigurabilityIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleConfigurabilityIntegrationSpec.groovy
index af3ea1d..4a72347 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleConfigurabilityIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/GradleConfigurabilityIntegrationSpec.groovy
@@ -19,8 +19,8 @@
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.internal.jvm.JavaInfo
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import spock.lang.IgnoreIf
 
@@ -55,7 +55,7 @@
         failure.assertHasDescription("Value '${dummyJdk.absolutePath}' given for org.gradle.java.home Gradle property is invalid (Java home supplied seems to be invalid)")
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "handles java home that is a symlink"() {
         given:
         def javaHome = Jvm.current().javaHome
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/SupportedBuildJvmIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/SupportedBuildJvmIntegrationTest.groovy
index 5a9489d..1ee4a9f 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/SupportedBuildJvmIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/SupportedBuildJvmIntegrationTest.groovy
@@ -18,19 +18,17 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.jvm.Jvm
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.IgnoreIf
 import spock.lang.Issue
 
-
 class SupportedBuildJvmIntegrationTest extends AbstractIntegrationSpec {
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "can start Gradle with a JDK that contains symlinks"() {
         // Zulu sets their Java distribution up like this
         def installedJdk = Jvm.current().javaHome
@@ -46,7 +44,7 @@
     // This test deletes a JDK installation while the daemon is running.
     // This is difficult to setup on Windows since you can't delete files
     // that are in use.
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @Issue("https://github.com/gradle/gradle/issues/16816")
     def "can successful start after a running daemon's JDK has been removed"() {
         def installedJdk = Jvm.current().javaHome
@@ -68,8 +66,10 @@
         succeeds("help")
     }
 
-    @IgnoreIf({ GradleContextualExecuter.embedded }) // This test requires to start Gradle from scratch with the wrong Java version
-    @Requires(adhoc = { AvailableJavaHomes.getJdks("1.6", "1.7") })
+    @Requires(
+        value = [IntegTestPreconditions.UnsupportedJavaHomeAvailable, IntegTestPreconditions.NotEmbeddedExecutor],
+        reason = "This test requires to start Gradle from scratch with the wrong Java version"
+    )
     def "provides reasonable failure message when attempting to run under java #jdk.javaVersion"() {
         given:
         executer.withJavaHome(jdk.javaHome)
@@ -82,7 +82,7 @@
         jdk << AvailableJavaHomes.getJdks("1.6", "1.7")
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdks("1.6", "1.7") })
+    @Requires(IntegTestPreconditions.UnsupportedJavaHomeAvailable)
     def "fails when build is configured to use Java #jdk.javaVersion"() {
         given:
         file("gradle.properties").writeProperties("org.gradle.java.home": jdk.javaHome.canonicalPath)
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/SystemClassLoaderTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/SystemClassLoaderTest.groovy
index 8611f8a..eb23b8a 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/SystemClassLoaderTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/SystemClassLoaderTest.groovy
@@ -17,6 +17,7 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.internal.agents.AgentUtils
 import spock.lang.IgnoreIf
 
 /**
@@ -96,7 +97,7 @@
             it.contains("gradle-launcher")
         }
         !maybeHasAgent || libraries.any {
-            it.contains("gradle-instrumentation-agent")
+            it.contains(AgentUtils.AGENT_MODULE_NAME)
         }
     }
 }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/cli/NotificationsIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/cli/NotificationsIntegrationTest.groovy
index 4dd5d10..8e0b4d3 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/cli/NotificationsIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/cli/NotificationsIntegrationTest.groovy
@@ -71,7 +71,7 @@
 
     def "show reasonable error message for invalid configuration property"() {
         when:
-        file("gradle.properties") << "org.gradle.welcome=foo"
+        propertiesFile << "org.gradle.welcome=foo"
         fails()
 
         then:
@@ -80,7 +80,7 @@
 
     def "abort rendering welcome message using configuration property"() {
         when:
-        file("gradle.properties") << "org.gradle.welcome=never"
+        propertiesFile << "org.gradle.welcome=never"
         succeeds()
 
         then:
@@ -95,7 +95,7 @@
 
    Debug level logging will leak security sensitive information!
 
-   ${DOCUMENTATION_REGISTRY.getDocumentationFor("logging", "sec:debug_security")}
+   ${new DocumentationRegistry().getDocumentationRecommendationFor("details", "logging", "sec:debug_security")}
 #############################################################################
 """
         withDebugLogging()
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/CancellationContinuousIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/CancellationContinuousIntegrationTest.groovy
index 794de00..9e34b09 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/CancellationContinuousIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/CancellationContinuousIntegrationTest.groovy
@@ -17,8 +17,8 @@
 package org.gradle.launcher.continuous
 
 import org.gradle.integtests.fixtures.AbstractContinuousIntegrationTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class CancellationContinuousIntegrationTest extends AbstractContinuousIntegrationTest {
 
@@ -65,7 +65,7 @@
         cancelsAndExits()
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     // GradleHandle.abort() is unsafe on Windows - this is a test infrastructure problem
     def "does not cancel on EOT or by closing System.in when not interactive"() {
         when:
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/MultiProjectContinuousIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/MultiProjectContinuousIntegrationTest.groovy
index a8d4c32..7061a0b 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/MultiProjectContinuousIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/MultiProjectContinuousIntegrationTest.groovy
@@ -17,11 +17,10 @@
 package org.gradle.launcher.continuous
 
 import org.gradle.integtests.fixtures.AbstractContinuousIntegrationTest
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Ignore
 
-import static org.gradle.util.TestPrecondition.MAC_OS_X
-import static org.gradle.util.TestPrecondition.NOT_MAC_OS_X_M1
-
 class MultiProjectContinuousIntegrationTest extends AbstractContinuousIntegrationTest {
 
     def upstreamSource, downstreamSource
@@ -48,7 +47,7 @@
         // MacOS builds with Intel machines can be too fast on CI and
         // changes generated during build are picked up only after the build which retriggers the build
         // Fix for issue: https://github.com/gradle/gradle-private/issues/3728
-        if (MAC_OS_X.fulfilled && NOT_MAC_OS_X_M1.fulfilled) {
+        if (TestPrecondition.doSatisfies(UnitTestPreconditions.MacOs) && TestPrecondition.notSatisfies(UnitTestPreconditions.MacOsM1)) {
             def quietPeriod = 250
             waitAtEndOfBuildForQuietPeriod(quietPeriod)
         }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SimpleJavaContinuousIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SimpleJavaContinuousIntegrationTest.groovy
index 7d83d9c..47ab462 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SimpleJavaContinuousIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SimpleJavaContinuousIntegrationTest.groovy
@@ -17,8 +17,9 @@
 package org.gradle.launcher.continuous
 
 import org.gradle.integtests.fixtures.AbstractContinuousIntegrationTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 // NB: there's nothing specific about Java support and continuous.
 //     this spec just lays out some more practical use cases than the other targeted tests.
@@ -49,7 +50,7 @@
         executedAndNotSkipped ":compileJava", ":build"
 
         when:
-        if (TestPrecondition.WINDOWS) {
+        if (TestPrecondition.doSatisfies(UnitTestPreconditions.Windows)) {
             //the main src dir might be locked, only delete children
             file("src/main/java").listFiles().each {
                 assert !it.deleteDir().exists()
@@ -164,7 +165,7 @@
     }
 
     // Just exercises the dependency management layers to shake out any weirdness
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     def "can resolve dependencies from remote repository"() {
         when:
         def sourceFile = file("src/main/java/Thing.java") << "class Thing {}"
@@ -252,7 +253,7 @@
         executedAndNotSkipped ":compileJava"
     }
 
-    @Requires(TestPrecondition.NOT_LINUX)
+    @Requires(UnitTestPreconditions.NotLinux)
     def "creation of initial source file triggers build for hierarchical watchers"() {
         expect:
         succeeds("build")
@@ -267,7 +268,7 @@
         executedAndNotSkipped ":compileJava"
     }
 
-    @Requires(TestPrecondition.LINUX)
+    @Requires(UnitTestPreconditions.Linux)
     def "creation of initial source file does not trigger build for non-hierarchical watchers"() {
         expect:
         succeeds("build")
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SmokeContinuousIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SmokeContinuousIntegrationTest.groovy
index 3f9ea8a..efbbb4f9 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SmokeContinuousIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SmokeContinuousIntegrationTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.environment.GradleBuildEnvironment
 import org.gradle.internal.os.OperatingSystem
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Ignore
 import spock.lang.Issue
 
@@ -563,7 +563,7 @@
         executedAndNotSkipped(":a")
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "exit hint does not mention enter when not on windows"() {
         when:
         file("a").touch()
@@ -574,7 +574,7 @@
         output.endsWith("(ctrl-d to exit)\n")
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "exit hint mentions enter when on windows"() {
         when:
         file("a").touch()
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SymlinkContinuousIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SymlinkContinuousIntegrationTest.groovy
index 3271f1a..47aabec 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SymlinkContinuousIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/continuous/SymlinkContinuousIntegrationTest.groovy
@@ -17,13 +17,13 @@
 package org.gradle.launcher.continuous
 
 import org.gradle.integtests.fixtures.AbstractContinuousIntegrationTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import java.nio.file.Files
 import java.nio.file.Paths
 
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class SymlinkContinuousIntegrationTest extends AbstractContinuousIntegrationTest {
     def "can use symlink for input"() {
         given:
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonFeedbackIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonFeedbackIntegrationSpec.groovy
index c0ae2e9..83e8e8f 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonFeedbackIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonFeedbackIntegrationSpec.groovy
@@ -22,9 +22,9 @@
 import org.gradle.launcher.daemon.logging.DaemonMessages
 import org.gradle.test.fixtures.file.LeaksFileHandles
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.Issue
 
 import static org.gradle.test.fixtures.ConcurrentTestUtil.poll
@@ -165,7 +165,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/8833")
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "daemon log restricts permissions to owner"() {
         given:
         file("build.gradle") << """
@@ -184,7 +184,7 @@
     }
 
     //Java 9 and above needs --add-opens to make environment variable mutation work
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     @ToBeFixedForConfigurationCache(because = "asserts on configuration phase logging")
     def "foreground daemon log honors log levels for logging"() {
         given:
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonJvmSettingsIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonJvmSettingsIntegrationTest.groovy
index efea725..c20c215 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonJvmSettingsIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonJvmSettingsIntegrationTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.daemon.DaemonIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.IgnoreIf
 
 class DaemonJvmSettingsIntegrationTest extends DaemonIntegrationSpec {
@@ -101,7 +101,7 @@
         javaToolOptions << ["-Xms513m", "-Xmx255m", "-Xms128m -Xmx256m"]
     }
 
-    @Requires(TestPrecondition.JDK16_OR_EARLIER) // TraceClassLoading option has been deprecated and is removed in JDK17
+    @Requires(UnitTestPreconditions.Jdk16OrEarlier) // TraceClassLoading option has been deprecated and is removed in JDK17
     def 'can start the daemon with ClassLoading tracing enabled'() {
         given:
         file('build.gradle') << """
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonLifecycleSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonLifecycleSpec.groovy
index ce7a30c..88dab3a 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonLifecycleSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonLifecycleSpec.groovy
@@ -27,8 +27,8 @@
 import org.gradle.launcher.daemon.server.DaemonStateCoordinator
 import org.gradle.launcher.daemon.server.api.HandleStop
 import org.gradle.launcher.daemon.testing.DaemonEventSequenceBuilder
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Ignore
 import spock.lang.IgnoreIf
 
@@ -254,7 +254,7 @@
     }
 
     //Java 9 and above needs --add-opens to make environment variable mutation work
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     def "existing foreground idle daemons are used"() {
         when:
         startForegroundDaemon()
@@ -527,7 +527,7 @@
     }
 
     // GradleHandle.abort() does not work reliably on windows and creates flakiness
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @Ignore("TODO: Fix GradleHandle.abort() so that it doesn't hang")
     def "daemon stops immediately if stop is requested and then client disconnects"() {
         when:
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonPriorityIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonPriorityIntegrationTest.groovy
index 1aecb5c..c105069 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonPriorityIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonPriorityIntegrationTest.groovy
@@ -17,9 +17,8 @@
 package org.gradle.launcher.daemon
 
 import org.gradle.integtests.fixtures.daemon.DaemonIntegrationSpec
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.util.TestPrecondition
-import spock.lang.IgnoreIf
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 /**
  * For forking a daemon process in embedded mode:
@@ -27,7 +26,7 @@
  * The few extra arguments that the priority change requires seem to push it over the edge.
  * Therefore, we do not run these tests embedded on Windows.
  **/
-@IgnoreIf({ TestPrecondition.WINDOWS.fulfilled && GradleContextualExecuter.embedded})
+@Requires(IntegTestPreconditions.NotEmbeddedExecutorOrNotWindows)
 class DaemonPriorityIntegrationTest extends DaemonIntegrationSpec {
 
     def "forks new daemon when priority is set to a different value via command line"() {
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonReuseIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonReuseIntegrationTest.groovy
index b336ec5..b44b060 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonReuseIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/DaemonReuseIntegrationTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.launcher.daemon.logging.DaemonMessages
 import org.gradle.test.fixtures.ConcurrentTestUtil
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Issue
 
@@ -211,7 +211,7 @@
     }
 
     // GradleHandle.abort() does not work reliably on windows and creates flakiness
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "prefers an idle daemon when daemons with canceled builds are available"() {
         given:
         expectEvent("started1")
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ProcessCrashHandlingIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ProcessCrashHandlingIntegrationTest.groovy
index 075c93c..7878caa 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ProcessCrashHandlingIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/ProcessCrashHandlingIntegrationTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.launcher.daemon.client.DaemonDisappearedException
 import org.gradle.launcher.daemon.logging.DaemonMessages
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
 class ProcessCrashHandlingIntegrationTest extends DaemonIntegrationSpec {
@@ -91,7 +91,7 @@
      * a native executable that can retrieve the session id of a process so that we can verify that the session id
      * of the daemon is different than the session id of the client.
      */
-    @Requires([TestPrecondition.NOT_WINDOWS])
+    @Requires([UnitTestPreconditions.NotWindows])
     @ToBeFixedForConfigurationCache(because = "fixture uses the software model")
     def "session id of daemon is different from daemon client"() {
         given:
@@ -129,7 +129,7 @@
      * that allows us to attach to the same console some other process is attached to.  If that process is not attached
      * to any console, we get a specific error that we check for.
      */
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "daemon is not attached to a console"() {
         given:
         withAttachConsoleProject()
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/server/health/gc/GarbageCollectionMonitoringIntegrationTest.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/server/health/gc/GarbageCollectionMonitoringIntegrationTest.groovy
index 5a885ef..d55fc54 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/server/health/gc/GarbageCollectionMonitoringIntegrationTest.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/server/health/gc/GarbageCollectionMonitoringIntegrationTest.groovy
@@ -25,9 +25,15 @@
 import org.gradle.integtests.fixtures.daemon.DaemonIntegrationSpec
 import org.gradle.integtests.fixtures.daemon.JavaGarbageCollector
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.launcher.daemon.server.health.HealthExpirationStrategy
 
+import static org.gradle.api.JavaVersion.VERSION_14
+import static org.gradle.integtests.fixtures.daemon.JavaGarbageCollector.ORACLE_SERIAL9
 import static org.gradle.launcher.daemon.server.DaemonStateCoordinator.DAEMON_STOPPING_IMMEDIATELY_MESSAGE
 import static org.gradle.launcher.daemon.server.DaemonStateCoordinator.DAEMON_WILL_STOP_MESSAGE
+import static org.gradle.launcher.daemon.server.health.gc.GarbageCollectorMonitoringStrategy.ORACLE_G1
+import static org.gradle.launcher.daemon.server.health.gc.GarbageCollectorMonitoringStrategy.ORACLE_PARALLEL_CMS
+import static org.gradle.launcher.daemon.server.health.gc.GarbageCollectorMonitoringStrategy.ORACLE_SERIAL
 
 @TargetCoverage({ garbageCollectors })
 @MultiVersionTest
@@ -35,6 +41,8 @@
 class GarbageCollectionMonitoringIntegrationTest extends DaemonIntegrationSpec {
     static def version
     static final String MEMORY_URL = new DocumentationRegistry().getDocumentationFor("build_environment", "sec:configuring_jvm_memory")
+    public static final GString COMMON_HINT = """${new DocumentationRegistry().getDocumentationRecommendationFor("information on how to set these values", "build_environment", "sec:configuring_jvm_memory")}
+To disable this warning, set 'org.gradle.daemon.performance.disable-logging=true'."""
     GarbageCollectorUnderTest garbageCollector
 
     def setup() {
@@ -43,6 +51,17 @@
         executer.withEnvironmentVars(JAVA_TOOL_OPTIONS: "-D${DefaultGarbageCollectionMonitor.DISABLE_POLLING_SYSTEM_PROPERTY}=true")
     }
 
+    def "does not expire daemon when performance monitoring is disabled"() {
+        given:
+        configureGarbageCollectionHeapEventsFor(256, 512, 35, garbageCollector.monitoringStrategy.gcRateThreshold + 0.2)
+
+        when:
+        run "injectEvents", "-D${HealthExpirationStrategy.ENABLE_PERFORMANCE_MONITORING}=false"
+
+        then:
+        !daemons.daemon.log.contains(DAEMON_WILL_STOP_MESSAGE)
+    }
+
     def "expires daemon when heap leaks slowly"() {
         given:
         configureGarbageCollectionHeapEventsFor(256, 512, 35, garbageCollector.monitoringStrategy.gcRateThreshold + 0.2)
@@ -60,8 +79,7 @@
 The daemon will restart for the next build, which may increase subsequent build times.
 These settings can be adjusted by setting 'org.gradle.jvmargs' in 'gradle.properties'.
 The currently configured max heap space is '512 MiB' and the configured max metaspace is 'unknown'.
-For more information on how to set these values, visit the user guide at ${MEMORY_URL}
-To disable this warning, set 'org.gradle.daemon.performance.disable-logging=true'.""")
+${COMMON_HINT}""")
     }
 
     def "expires daemon immediately when garbage collector is thrashing"() {
@@ -91,8 +109,7 @@
 The memory settings for this project must be adjusted to avoid this failure.
 These settings can be adjusted by setting 'org.gradle.jvmargs' in 'gradle.properties'.
 The currently configured max heap space is '512 MiB' and the configured max metaspace is 'unknown'.
-For more information on how to set these values, visit the user guide at ${MEMORY_URL}
-To disable this warning, set 'org.gradle.daemon.performance.disable-logging=true'.""")
+${COMMON_HINT}""")
     }
 
     @ToBeFixedForConfigurationCache(because = "Gradle.buildFinished")
@@ -127,8 +144,7 @@
 The daemon will restart for the next build, which may increase subsequent build times.
 These settings can be adjusted by setting 'org.gradle.jvmargs' in 'gradle.properties'.
 The currently configured max heap space is '512 MiB' and the configured max metaspace is 'unknown'.
-For more information on how to set these values, visit the user guide at ${MEMORY_URL}
-To disable this warning, set 'org.gradle.daemon.performance.disable-logging=true'.""")
+${COMMON_HINT}""")
     }
 
     def "expires daemon when metaspace leaks"() {
@@ -148,8 +164,7 @@
 The daemon will restart for the next build, which may increase subsequent build times.
 These settings can be adjusted by setting 'org.gradle.jvmargs' in 'gradle.properties'.
 The currently configured max heap space is 'unknown' and the configured max metaspace is '512 MiB'.
-For more information on how to set these values, visit the user guide at ${MEMORY_URL}
-To disable this warning, set 'org.gradle.daemon.performance.disable-logging=true'.""")
+${COMMON_HINT}""")
     }
 
     def "does not expire daemon when leak does not consume heap threshold"() {
@@ -296,16 +311,16 @@
     }
 
     static List<GarbageCollectorUnderTest> getGarbageCollectors() {
-        if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_14)) {
+        if (JavaVersion.current().isCompatibleWith(VERSION_14)) {
             return [
-                new GarbageCollectorUnderTest(JavaGarbageCollector.ORACLE_SERIAL9, GarbageCollectorMonitoringStrategy.ORACLE_SERIAL),
-                new GarbageCollectorUnderTest(JavaGarbageCollector.ORACLE_G1, GarbageCollectorMonitoringStrategy.ORACLE_G1)
+                new GarbageCollectorUnderTest(ORACLE_SERIAL9, ORACLE_SERIAL),
+                new GarbageCollectorUnderTest(JavaGarbageCollector.ORACLE_G1, ORACLE_G1)
             ]
         } else {
             return [
-                new GarbageCollectorUnderTest(JavaGarbageCollector.ORACLE_PARALLEL_CMS, GarbageCollectorMonitoringStrategy.ORACLE_PARALLEL_CMS),
-                new GarbageCollectorUnderTest(JavaGarbageCollector.ORACLE_SERIAL9, GarbageCollectorMonitoringStrategy.ORACLE_SERIAL),
-                new GarbageCollectorUnderTest(JavaGarbageCollector.ORACLE_G1, GarbageCollectorMonitoringStrategy.ORACLE_G1)
+                new GarbageCollectorUnderTest(JavaGarbageCollector.ORACLE_PARALLEL_CMS, ORACLE_PARALLEL_CMS),
+                new GarbageCollectorUnderTest(ORACLE_SERIAL9, ORACLE_SERIAL),
+                new GarbageCollectorUnderTest(JavaGarbageCollector.ORACLE_G1, ORACLE_G1)
             ]
         }
     }
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/server/scaninfo/DaemonScanInfoIntegrationSpec.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/server/scaninfo/DaemonScanInfoIntegrationSpec.groovy
index 03c8088..3c93c2d 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/server/scaninfo/DaemonScanInfoIntegrationSpec.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/server/scaninfo/DaemonScanInfoIntegrationSpec.groovy
@@ -22,9 +22,9 @@
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.timeout.IntegrationTestTimeout
 import org.gradle.launcher.daemon.client.SingleUseDaemonClient
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.GFileUtils
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.IgnoreIf
 
 @IntegrationTestTimeout(300)
@@ -56,7 +56,7 @@
     }
 
     //Java 9 and above needs --add-opens to make environment variable mutation work
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     def "should capture basic data when a foreground daemon runs multiple builds"() {
         given:
         buildFile << """
diff --git a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonsStateCheckpoint.groovy b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonsStateCheckpoint.groovy
index 5f49680..6c117eb 100644
--- a/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonsStateCheckpoint.groovy
+++ b/subprojects/launcher/src/integTest/groovy/org/gradle/launcher/daemon/testing/DaemonsStateCheckpoint.groovy
@@ -41,4 +41,4 @@
             false
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ExecutionCompleter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ExecutionCompleter.java
index be350bc..2a90da7 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ExecutionCompleter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ExecutionCompleter.java
@@ -18,4 +18,4 @@
 public interface ExecutionCompleter {
     void complete();
     void completeWithFailure(Throwable t);
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ProcessBootstrap.java b/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ProcessBootstrap.java
index 5cfb615..969ef0f 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ProcessBootstrap.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/bootstrap/ProcessBootstrap.java
@@ -19,7 +19,6 @@
 import org.gradle.api.internal.DefaultClassPathProvider;
 import org.gradle.api.internal.DefaultClassPathRegistry;
 import org.gradle.api.internal.classpath.DefaultModuleRegistry;
-import org.gradle.internal.agents.DefaultClassFileTransformer;
 import org.gradle.internal.classloader.ClassLoaderFactory;
 import org.gradle.internal.classloader.ClassLoaderUtils;
 import org.gradle.internal.classloader.DefaultClassLoaderFactory;
@@ -55,7 +54,6 @@ private static void runNoExit(String mainClassName, String[] args) throws Except
         Thread.currentThread().setContextClassLoader(runtimeClassLoader);
 
         try {
-            DefaultClassFileTransformer.tryInstallInClassLoader(runtimeClassLoader);
             Class<?> mainClass = runtimeClassLoader.loadClass(mainClassName);
             Object entryPoint = mainClass.getConstructor().newInstance();
             Method mainMethod = mainClass.getMethod("run", String[].class);
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/BuildActionsFactory.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/BuildActionsFactory.java
index 38b9095..792828b 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/BuildActionsFactory.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/BuildActionsFactory.java
@@ -28,6 +28,8 @@
 import org.gradle.initialization.layout.BuildLayoutFactory;
 import org.gradle.internal.Actions;
 import org.gradle.internal.SystemProperties;
+import org.gradle.internal.agents.AgentInitializer;
+import org.gradle.internal.agents.AgentStatus;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.concurrent.Stoppable;
@@ -95,7 +97,8 @@ public Action<? super ExecutionListener> createAction(CommandLineParser parser,
         if (parameters.getDaemonParameters().isForeground()) {
             DaemonParameters daemonParameters = parameters.getDaemonParameters();
             ForegroundDaemonConfiguration conf = new ForegroundDaemonConfiguration(
-                UUID.randomUUID().toString(), daemonParameters.getBaseDir(), daemonParameters.getIdleTimeout(), daemonParameters.getPeriodicCheckInterval(), fileCollectionFactory);
+                UUID.randomUUID().toString(), daemonParameters.getBaseDir(), daemonParameters.getIdleTimeout(), daemonParameters.getPeriodicCheckInterval(), fileCollectionFactory,
+                daemonParameters.shouldApplyInstrumentationAgent());
             return Actions.toAction(new ForegroundDaemonAction(loggingServices, conf));
         }
         if (parameters.getDaemonParameters().isEnabled()) {
@@ -141,9 +144,11 @@ private Runnable runBuildInProcess(StartParameterInternal startParameter, Daemon
             .displayName("Global services")
             .parent(loggingServices)
             .parent(NativeServices.getInstance())
-            .provider(new GlobalScopeServices(startParameter.isContinuous()))
+            .provider(new GlobalScopeServices(startParameter.isContinuous(), AgentStatus.of(daemonParameters.shouldApplyInstrumentationAgent())))
             .build();
 
+        globalServices.get(AgentInitializer.class).maybeConfigureInstrumentationAgent();
+
         // Force the user home services to be stopped first, the dependencies between the user home services and the global services are not preserved currently
         return runBuildAndCloseServices(startParameter, daemonParameters, globalServices.get(BuildExecuter.class), globalServices, globalServices.get(GradleUserHomeScopeServiceRegistry.class));
     }
@@ -172,7 +177,7 @@ private ServiceRegistry createGlobalClientServices(boolean usingDaemon) {
         if (usingDaemon) {
             builder.parent(basicServices);
         } else {
-            builder.provider(new GlobalScopeServices(false));
+            builder.provider(new GlobalScopeServices(false, AgentStatus.disabled()));
         }
         return builder.provider(new DaemonClientGlobalServices()).build();
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/DebugLoggerWarningAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/DebugLoggerWarningAction.java
index b727d12..b6f4cc6 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/DebugLoggerWarningAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/DebugLoggerWarningAction.java
@@ -29,22 +29,14 @@
 
 final class DebugLoggerWarningAction implements Action<ExecutionListener> {
 
-    static final String WARNING_MESSAGE_BODY;
-
-    static {
-        @SuppressWarnings("StringBufferReplaceableByString") // Readability is better this way.
-        final StringBuilder sb = new StringBuilder();
-        sb.append('\n');
-        sb.append("#############################################################################\n");
-        sb.append("   WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n");
-        sb.append('\n');
-        sb.append("   Debug level logging will leak security sensitive information!\n");
-        sb.append('\n');
-        sb.append("   ").append(new DocumentationRegistry().getDocumentationFor("logging", "sec:debug_security")).append('\n');
-        sb.append("#############################################################################\n");
-        WARNING_MESSAGE_BODY = sb.toString();
-    }
-
+    static final String WARNING_MESSAGE_BODY = "\n" +
+        "#############################################################################\n" +
+        "   WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n" +
+        "\n" +
+        "   Debug level logging will leak security sensitive information!\n" +
+        "\n" +
+        "   " + new DocumentationRegistry().getDocumentationRecommendationFor("details", "logging", "sec:debug_security") + "\n" +
+        "#############################################################################\n";
 
     private final Logger logger;
     private final LoggingConfiguration loggingConfiguration;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/LayoutToPropertiesConverter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/LayoutToPropertiesConverter.java
index a8489c2..27843cf 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/LayoutToPropertiesConverter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/cli/converter/LayoutToPropertiesConverter.java
@@ -103,10 +103,11 @@ private void maybeConfigureFrom(File propertiesFile, Map<String, String> result)
         }
 
         for (final Object key : properties.keySet()) {
+            String keyAsString = key.toString();
             BuildOption<?> validOption = CollectionUtils.findFirst(allBuildOptions, new Spec<BuildOption<?>>() {
                 @Override
                 public boolean isSatisfiedBy(BuildOption<?> option) {
-                    return option.getGradleProperty() != null ? option.getGradleProperty().equals(key.toString()) : false;
+                    return keyAsString.equals(option.getGradleProperty()) || keyAsString.equals(option.getDeprecatedGradleProperty());
                 }
             });
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonMain.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonMain.java
index f713c02..8ffe1dd 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonMain.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/DaemonMain.java
@@ -20,6 +20,7 @@
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.internal.agents.AgentInitializer;
 import org.gradle.internal.classpath.DefaultClassPath;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.logging.LoggingManagerInternal;
@@ -121,6 +122,8 @@ protected void doAction(String[] args, ExecutionListener listener) {
 
         LOGGER.debug("Assuming the daemon was started with following jvm opts: {}", startupOpts);
 
+        daemonServices.get(AgentInitializer.class).maybeConfigureInstrumentationAgent();
+
         Daemon daemon = daemonServices.get(Daemon.class);
         daemon.start();
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonAction.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonAction.java
index 364eb27..4e5ea37 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonAction.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/bootstrap/ForegroundDaemonAction.java
@@ -15,6 +15,7 @@
  */
 package org.gradle.launcher.daemon.bootstrap;
 
+import org.gradle.internal.agents.AgentInitializer;
 import org.gradle.internal.classpath.DefaultClassPath;
 import org.gradle.internal.logging.LoggingManagerInternal;
 import org.gradle.internal.service.ServiceRegistry;
@@ -46,6 +47,7 @@ public void run() {
         Daemon daemon = daemonServices.get(Daemon.class);
         DaemonRegistry daemonRegistry = daemonServices.get(DaemonRegistry.class);
         DaemonExpirationStrategy expirationStrategy = daemonServices.get(MasterExpirationStrategy.class);
+        daemonServices.get(AgentInitializer.class).maybeConfigureInstrumentationAgent();
 
         daemon.start();
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClient.java
index 39fc127..d5cb9a1 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClient.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonClient.java
@@ -27,7 +27,6 @@
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.concurrent.ExecutorFactory;
-import org.gradle.internal.deprecation.Documentation;
 import org.gradle.internal.id.IdGenerator;
 import org.gradle.internal.invocation.BuildAction;
 import org.gradle.internal.logging.ConsoleRenderer;
@@ -168,8 +167,7 @@ public BuildActionResult execute(BuildAction action, BuildActionParameters param
         } catch (DaemonInitialConnectException e) {
             // This means we could not connect to the daemon we just started.  fail and don't try again
             throw new NoUsableDaemonFoundException("A new daemon was started but could not be connected to: " +
-                "pid=" + connection.getDaemon() + ", address= " + connection.getDaemon().getAddress() + ". " +
-                Documentation.userManual("troubleshooting", "network_connection").consultDocumentationMessage(),
+                "pid=" + connection.getDaemon() + ", address= " + connection.getDaemon().getAddress() + ".",
                 accumulatedExceptions);
         } finally {
             connection.stop();
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonDisappearedException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonDisappearedException.java
index 8fa3d77..ef79d25 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonDisappearedException.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonDisappearedException.java
@@ -27,4 +27,4 @@ public class DaemonDisappearedException extends GradleException {
     public DaemonDisappearedException() {
         super(MESSAGE);
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonGreeter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonGreeter.java
index 1fffc1b..7be407e 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonGreeter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DaemonGreeter.java
@@ -39,21 +39,18 @@ public DaemonStartupInfo parseDaemonOutput(String output, List<String> startupAr
         }
         String[] lines = output.split("\n");
         //Assuming that the diagnostics were printed out to the last line. It's not bullet-proof but seems to be doing fine.
-        String lastLine = lines[lines.length-1];
+        String lastLine = lines[lines.length - 1];
         return startupCommunication.readDiagnostics(lastLine);
     }
 
     private String prepareMessage(String output, List<String> startupArgs) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(DaemonMessages.UNABLE_TO_START_DAEMON);
-        sb.append("\nThis problem might be caused by incorrect configuration of the daemon.");
-        sb.append("\nFor example, an unrecognized jvm option is used.");
-        sb.append("\nPlease refer to the User Manual chapter on the daemon at ");
-        sb.append(documentationRegistry.getDocumentationFor("gradle_daemon"));
-        sb.append("\nProcess command line: ").append(Joiner.on(" ").join(startupArgs));
-        sb.append("\nPlease read the following process output to find out more:");
-        sb.append("\n-----------------------\n");
-        sb.append(output);
-        return sb.toString();
+        return DaemonMessages.UNABLE_TO_START_DAEMON +
+            "\nThis problem might be caused by incorrect configuration of the daemon." +
+            "\nFor example, an unrecognized jvm option is used." +
+            documentationRegistry.getDocumentationRecommendationFor("details on the daemon", "gradle_daemon") +
+            "\nProcess command line: " + Joiner.on(" ").join(startupArgs) +
+            "\nPlease read the following process output to find out more:" +
+            "\n-----------------------\n" +
+            output;
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonStarter.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonStarter.java
index 4053868..98922ef 100755
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonStarter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/DefaultDaemonStarter.java
@@ -21,6 +21,7 @@
 import org.gradle.api.internal.classpath.ModuleRegistry;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
+import org.gradle.internal.agents.AgentUtils;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.concurrent.CompositeStoppable;
 import org.gradle.internal.installation.CurrentGradleInstallation;
@@ -77,19 +78,15 @@ public DaemonStartupInfo startDaemon(boolean singleUse) {
         ModuleRegistry registry = new DefaultModuleRegistry(gradleInstallation);
         ClassPath classpath;
         List<File> searchClassPath;
-        ClassPath agentClasspath;
+
         if (gradleInstallation == null) {
             // When not running from a Gradle distro, need runtime impl for launcher plus the search path to look for other modules
             classpath = registry.getModule("gradle-launcher").getAllRequiredModulesClasspath();
             searchClassPath = registry.getAdditionalClassPath().getAsFiles();
-            // TODO(mlopatkin) This disables agent-based instrumentation when starting the daemon from e.g. embedded Gradle runner.
-            //  There should be a backup plan to find the agent jar, e.g. load it from the resources.
-            agentClasspath = ClassPath.EMPTY;
         } else {
             // When running from a Gradle distro, only need launcher jar. The daemon can find everything from there.
             classpath = registry.getModule("gradle-launcher").getImplementationClasspath();
             searchClassPath = Collections.emptyList();
-            agentClasspath = registry.getModule("gradle-instrumentation-agent").getImplementationClasspath();
         }
         if (classpath.isEmpty()) {
             throw new IllegalStateException("Unable to construct a bootstrap classpath when starting the daemon");
@@ -110,6 +107,7 @@ public DaemonStartupInfo startDaemon(boolean singleUse) {
             daemonArgs.add(JvmOptions.getDebugArgument(true, true, "5005"));
         }
 
+        ClassPath agentClasspath = registry.getModule(AgentUtils.AGENT_MODULE_NAME).getImplementationClasspath();
         if (daemonParameters.shouldApplyInstrumentationAgent()) {
             if (agentClasspath.isEmpty()) {
                 throw new IllegalStateException("Cannot find the agent JAR");
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/NoUsableDaemonFoundException.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/NoUsableDaemonFoundException.java
index c1e712f..dc92597 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/NoUsableDaemonFoundException.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/NoUsableDaemonFoundException.java
@@ -16,10 +16,23 @@
 
 package org.gradle.launcher.daemon.client;
 
+import org.gradle.internal.deprecation.Documentation;
 import org.gradle.internal.exceptions.DefaultMultiCauseException;
+import org.gradle.internal.exceptions.ResolutionProvider;
 
-public class NoUsableDaemonFoundException extends DefaultMultiCauseException {
+import java.util.Collections;
+import java.util.List;
+
+public class NoUsableDaemonFoundException extends DefaultMultiCauseException implements ResolutionProvider {
+
+    private static final List<String> RESOLUTION = Collections.singletonList(Documentation.userManual("troubleshooting", "network_connection").consultDocumentationMessage());
+
     public NoUsableDaemonFoundException(String message, Iterable<? extends Throwable> causes) {
         super(message, causes);
     }
+
+    @Override
+    public List<String> getResolutions() {
+        return RESOLUTION;
+    }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/ReportDaemonStatusClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/ReportDaemonStatusClient.java
index db24858..4b90c80 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/ReportDaemonStatusClient.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/ReportDaemonStatusClient.java
@@ -90,8 +90,7 @@ public void listAll() {
         printStoppedDaemons(stopEvents);
 
         LOGGER.quiet("");
-
-        LOGGER.quiet(STATUS_FOOTER + " See " + documentationRegistry.getDocumentationFor("gradle_daemon", "sec:status"));
+        LOGGER.quiet(STATUS_FOOTER + " " + documentationRegistry.getDocumentationRecommendationFor("on this", "gradle_daemon", "sec:status"));
     }
 
     @VisibleForTesting
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/SingleUseDaemonClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/SingleUseDaemonClient.java
index 00e2ea7..4eb2c59 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/SingleUseDaemonClient.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/client/SingleUseDaemonClient.java
@@ -46,7 +46,7 @@ public SingleUseDaemonClient(DaemonConnector connector, OutputEventListener outp
 
     @Override
     public BuildActionResult execute(BuildAction action, BuildActionParameters parameters, BuildRequestContext buildRequestContext) {
-        LOGGER.lifecycle(MESSAGE + " See {}.", documentationRegistry.getDocumentationFor("gradle_daemon", "sec:disabling_the_daemon"));
+        LOGGER.lifecycle(MESSAGE + " {}", documentationRegistry.getDocumentationRecommendationFor("on this", "gradle_daemon", "sec:disabling_the_daemon"));
 
         DaemonClientConnection daemonConnection = getConnector().startSingleUseDaemon();
         Build build = new Build(getIdGenerator().generateId(), daemonConnection.getDaemon().getToken(), action, buildRequestContext.getClient(), buildRequestContext.getStartTime(), buildRequestContext.isInteractive(), parameters);
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/BuildProcess.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/BuildProcess.java
index 641ea49..92e5f80 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/BuildProcess.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/BuildProcess.java
@@ -19,21 +19,26 @@
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.internal.agents.AgentControl;
+import org.gradle.internal.agents.AgentStatus;
 import org.gradle.internal.jvm.JavaInfo;
 import org.gradle.process.internal.CurrentProcess;
 import org.gradle.process.internal.JvmOptions;
+
 import java.util.List;
 import java.util.Properties;
 
 public class BuildProcess extends CurrentProcess {
+    private final AgentStatus agentStatus;
 
     public BuildProcess(FileCollectionFactory fileCollectionFactory) {
         super(fileCollectionFactory);
+        // For purposes of this class, it is better to check if the agent is actually applied, regardless of the feature flag status.
+        this.agentStatus = AgentStatus.allowed();
     }
 
-    protected BuildProcess(JavaInfo jvm, JvmOptions effectiveJvmOptions) {
+    protected BuildProcess(JavaInfo jvm, JvmOptions effectiveJvmOptions, AgentStatus agentStatus) {
         super(jvm, effectiveJvmOptions);
+        this.agentStatus = agentStatus;
     }
 
     /**
@@ -43,7 +48,8 @@ protected BuildProcess(JavaInfo jvm, JvmOptions effectiveJvmOptions) {
      */
     public boolean configureForBuild(DaemonParameters requiredBuildParameters) {
         boolean javaHomeMatch = getJvm().equals(requiredBuildParameters.getEffectiveJvm());
-        boolean javaAgentStateMatch = AgentControl.isInstrumentationAgentApplied() == requiredBuildParameters.shouldApplyInstrumentationAgent();
+        // Even if the agent is applied to this process, it is possible to run the build with the legacy instrumentation mode.
+        boolean javaAgentStateMatch = agentStatus.isAgentInstrumentationEnabled() || !requiredBuildParameters.shouldApplyInstrumentationAgent();
 
         boolean immutableJvmArgsMatch = true;
         if (requiredBuildParameters.hasUserDefinedImmutableJvmArgs()) {
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonParameters.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonParameters.java
index b7ddfbf..e3fe31d 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonParameters.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonParameters.java
@@ -37,8 +37,8 @@ public class DaemonParameters {
     static final int DEFAULT_IDLE_TIMEOUT = 3 * 60 * 60 * 1000;
     public static final int DEFAULT_PERIODIC_CHECK_INTERVAL_MILLIS = 10 * 1000;
 
-    public static final List<String> DEFAULT_JVM_ARGS = ImmutableList.of("-Xmx512m", "-Xms256m", "-XX:MaxPermSize=256m", "-XX:+HeapDumpOnOutOfMemoryError");
-    public static final List<String> DEFAULT_JVM_8_ARGS = ImmutableList.of("-Xmx512m", "-Xms256m", "-XX:MaxMetaspaceSize=256m", "-XX:+HeapDumpOnOutOfMemoryError");
+    public static final List<String> DEFAULT_JVM_ARGS = ImmutableList.of("-Xmx512m", "-Xms256m", "-XX:MaxPermSize=384m", "-XX:+HeapDumpOnOutOfMemoryError");
+    public static final List<String> DEFAULT_JVM_8_ARGS = ImmutableList.of("-Xmx512m", "-Xms256m", "-XX:MaxMetaspaceSize=384m", "-XX:+HeapDumpOnOutOfMemoryError");
     public static final List<String> ALLOW_ENVIRONMENT_VARIABLE_OVERWRITE = ImmutableList.of("--add-opens=java.base/java.util=ALL-UNNAMED");
 
     private final File gradleUserHomeDir;
@@ -48,7 +48,7 @@ public class DaemonParameters {
 
     private int periodicCheckInterval = DEFAULT_PERIODIC_CHECK_INTERVAL_MILLIS;
     private final DaemonJvmOptions jvmOptions;
-    private boolean applyInstrumentationAgent;
+    private boolean applyInstrumentationAgent = true;
     private Map<String, String> envVariables;
     private boolean enabled = true;
     private boolean hasJvmArgs;
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonServerConfiguration.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonServerConfiguration.java
index c1466ef..7ece0b9 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonServerConfiguration.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DaemonServerConfiguration.java
@@ -34,4 +34,6 @@ public interface DaemonServerConfiguration {
     DaemonParameters.Priority getPriority();
 
     boolean isSingleUse();
+
+    boolean isInstrumentationAgentAllowed();
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DefaultDaemonServerConfiguration.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DefaultDaemonServerConfiguration.java
index 28dfd62..cc8c504 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DefaultDaemonServerConfiguration.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/DefaultDaemonServerConfiguration.java
@@ -28,8 +28,23 @@ public class DefaultDaemonServerConfiguration implements DaemonServerConfigurati
     private final boolean singleUse;
     private final DaemonParameters.Priority priority;
     private final List<String> jvmOptions;
+    private final boolean instrumentationAgentAllowed;
 
+    /**
+     * Creates the DefaultDaemonConfiguration that allows the use of the instrumentation agent if the latter is applied.
+     */
     public DefaultDaemonServerConfiguration(String daemonUid, File daemonBaseDir, int idleTimeoutMs, int periodicCheckIntervalMs, boolean singleUse, DaemonParameters.Priority priority, List<String> jvmOptions) {
+        // Using the available agent is correct for the forked daemon processes, because the forking
+        // code takes the desired agent status into account when configuring the daemon command line.
+        // The daemon that shouldn't use the agent won't have the agent applied.
+        this(daemonUid, daemonBaseDir, idleTimeoutMs, periodicCheckIntervalMs, singleUse, priority, jvmOptions, true);
+    }
+
+    public DefaultDaemonServerConfiguration(String daemonUid, File daemonBaseDir, int idleTimeoutMs, int periodicCheckIntervalMs, boolean singleUse, DaemonParameters.Priority priority, List<String> jvmOptions, boolean instrumentationAgentAllowed) {
+        // There is at least one case when the daemon shouldn't use the available agent: if the foreground
+        // daemon is started with feature flag disabled.
+        // The start script cannot look into the feature flags, so the agent is always applied to the foreground daemon.
+        // The state of the flag has to be communicated to the daemon setup code explicitly, which is what this constructor allows.
         this.daemonUid = daemonUid;
         this.daemonBaseDir = daemonBaseDir;
         this.idleTimeoutMs = idleTimeoutMs;
@@ -37,6 +52,7 @@ public DefaultDaemonServerConfiguration(String daemonUid, File daemonBaseDir, in
         this.singleUse = singleUse;
         this.priority = priority;
         this.jvmOptions = jvmOptions;
+        this.instrumentationAgentAllowed = instrumentationAgentAllowed;
     }
 
     @Override
@@ -73,4 +89,9 @@ public List<String> getJvmOptions() {
     public boolean isSingleUse() {
         return singleUse;
     }
+
+    @Override
+    public boolean isInstrumentationAgentAllowed() {
+        return instrumentationAgentAllowed;
+    }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/ForegroundDaemonConfiguration.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/ForegroundDaemonConfiguration.java
index 92a558b..aa51a31 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/ForegroundDaemonConfiguration.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/configuration/ForegroundDaemonConfiguration.java
@@ -22,8 +22,9 @@
 import java.io.File;
 
 public class ForegroundDaemonConfiguration extends DefaultDaemonServerConfiguration {
-    public ForegroundDaemonConfiguration(String daemonUid, File daemonBaseDir, int idleTimeoutMs, int periodicCheckIntervalMs, FileCollectionFactory fileCollectionFactory) {
+
+    public ForegroundDaemonConfiguration(String daemonUid, File daemonBaseDir, int idleTimeoutMs, int periodicCheckIntervalMs, FileCollectionFactory fileCollectionFactory, boolean instrumentationAgentAllowed) {
         // Foreground daemon cannot be 'told' what's his startup options as the client sits in the same process so we will infer the jvm opts from the inputArguments()
-        super(daemonUid, daemonBaseDir, idleTimeoutMs, periodicCheckIntervalMs, false, DaemonParameters.Priority.NORMAL, new CurrentProcess(fileCollectionFactory).getJvmOptions().getAllImmutableJvmArgs());
+        super(daemonUid, daemonBaseDir, idleTimeoutMs, periodicCheckIntervalMs, false, DaemonParameters.Priority.NORMAL, new CurrentProcess(fileCollectionFactory).getJvmOptions().getAllImmutableJvmArgs(), instrumentationAgentAllowed);
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildStarted.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildStarted.java
index 71f3960..6f9327d 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildStarted.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/BuildStarted.java
@@ -32,4 +32,4 @@ public BuildStarted(DaemonDiagnostics diagnostics) {
     public DaemonDiagnostics getDiagnostics() {
         return diagnostics;
     }
-}
\ No newline at end of file
+}
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 61c237b..e2cd283 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
@@ -281,12 +281,10 @@ public Build read(Decoder decoder) throws Exception {
     private static class BuildActionParametersSerializer implements Serializer<BuildActionParameters> {
         private final Serializer<LogLevel> logLevelSerializer;
         private final Serializer<List<File>> classPathSerializer;
-
         BuildActionParametersSerializer() {
             logLevelSerializer = new BaseSerializerFactory().getSerializerFor(LogLevel.class);
             classPathSerializer = new ListSerializer<>(FILE_SERIALIZER);
         }
-
         @Override
         public void write(Encoder encoder, BuildActionParameters parameters) throws Exception {
             FILE_SERIALIZER.write(encoder, parameters.getCurrentDir());
@@ -296,7 +294,6 @@ public void write(Encoder encoder, BuildActionParameters parameters) throws Exce
             encoder.writeBoolean(parameters.isUseDaemon()); // Can probably skip this
             classPathSerializer.write(encoder, parameters.getInjectedPluginClasspath().getAsFiles());
         }
-
         @Override
         public BuildActionParameters read(Decoder decoder) throws Exception {
             File currentDir = FILE_SERIALIZER.read(decoder);
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Failure.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Failure.java
index 61df033..2b6b995 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Failure.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Failure.java
@@ -33,4 +33,4 @@ private static Throwable assertNotNull(Throwable value) {
         
         return value;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/InputMessage.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/InputMessage.java
index ea07af3..0e4e3f2 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/InputMessage.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/InputMessage.java
@@ -16,4 +16,4 @@
 package org.gradle.launcher.daemon.protocol;
 
 public abstract class InputMessage extends Message {
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/OutputMessage.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/OutputMessage.java
index ac0b840..bc52383 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/OutputMessage.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/OutputMessage.java
@@ -27,4 +27,4 @@ public OutputMessage(OutputEvent event) {
     public OutputEvent getEvent() {
         return event;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Success.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Success.java
index dd4abc1..d784759 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Success.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/protocol/Success.java
@@ -29,4 +29,4 @@ public class Success extends Result<Object> {
     public Success(Object value) {
         super(value);
     }
-}
\ No newline at end of file
+}
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 8de5e76..23b7949 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
@@ -18,7 +18,7 @@
 import com.google.common.collect.ImmutableList;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
-import org.gradle.internal.agents.AgentControl;
+import org.gradle.internal.agents.AgentStatus;
 import org.gradle.internal.classpath.ClassPath;
 import org.gradle.internal.concurrent.ExecutorFactory;
 import org.gradle.internal.event.ListenerManager;
@@ -83,10 +83,10 @@ public DaemonServices(DaemonServerConfiguration configuration, ServiceRegistry l
         this.loggingManager = loggingManager;
 
         addProvider(new DaemonRegistryServices(configuration.getBaseDir()));
-        addProvider(new GlobalScopeServices(!configuration.isSingleUse(), additionalModuleClassPath));
+        addProvider(new GlobalScopeServices(!configuration.isSingleUse(), AgentStatus.of(configuration.isInstrumentationAgentAllowed()), additionalModuleClassPath));
     }
 
-    protected DaemonContext createDaemonContext() {
+    protected DaemonContext createDaemonContext(AgentStatus agentStatus) {
         DaemonContextBuilder builder = new DaemonContextBuilder(get(ProcessEnvironment.class));
         builder.setDaemonRegistryDir(configuration.getBaseDir());
         builder.setIdleTimeout(configuration.getIdleTimeout());
@@ -96,7 +96,7 @@ protected DaemonContext createDaemonContext() {
         LOGGER.debug("Creating daemon context with opts: {}", configuration.getJvmOptions());
 
         builder.setDaemonOpts(configuration.getJvmOptions());
-        builder.setApplyInstrumentationAgent(AgentControl.isInstrumentationAgentApplied());
+        builder.setApplyInstrumentationAgent(agentStatus.isAgentInstrumentationEnabled());
 
         return builder.create();
     }
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/LogToClient.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/LogToClient.java
index ee2c634..bdc3a06 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/LogToClient.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/exec/LogToClient.java
@@ -84,13 +84,11 @@ public void onOutput(OutputEvent event) {
                         dispatcher.submit(event);
                     }
                 }
-
                 private boolean isProgressEvent(OutputEvent event) {
                     return event instanceof ProgressStartEvent || event instanceof ProgressEvent || event instanceof ProgressCompleteEvent;
                 }
-
                 private boolean isMatchingBuildLogLevel(OutputEvent event) {
-                    return event.getLogLevel() != null && event.getLogLevel().compareTo(buildLogLevel) >= 0;
+                    return buildLogLevel != null && event.getLogLevel() != null && event.getLogLevel().compareTo(buildLogLevel) >= 0;
                 }
             };
             LOGGER.debug(DaemonMessages.ABOUT_TO_START_RELAYING_LOGS);
diff --git a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HealthExpirationStrategy.java b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HealthExpirationStrategy.java
index 15529f7..7b9ffc9 100644
--- a/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HealthExpirationStrategy.java
+++ b/subprojects/launcher/src/main/java/org/gradle/launcher/daemon/server/health/HealthExpirationStrategy.java
@@ -75,7 +75,6 @@ public class HealthExpirationStrategy implements DaemonExpirationStrategy {
     private final DaemonHealthStats stats;
     private final GarbageCollectorMonitoringStrategy strategy;
     private final Logger logger;
-    private final boolean enabled;
 
     public HealthExpirationStrategy(DaemonHealthStats stats, GarbageCollectorMonitoringStrategy strategy) {
         this(stats, strategy, LoggerFactory.getLogger(HealthExpirationStrategy.class));
@@ -85,12 +84,12 @@ public HealthExpirationStrategy(DaemonHealthStats stats, GarbageCollectorMonitor
         this.stats = stats;
         this.strategy = strategy;
         this.logger = logger;
-        this.enabled = Boolean.parseBoolean(System.getProperty(ENABLE_PERFORMANCE_MONITORING, "true"));
     }
 
     @Override
     public DaemonExpirationResult checkExpiration() {
-        if (!enabled) {
+        // We cannot check this in the constructor since system properties are copied to the daemon after initialization.
+        if (!Boolean.parseBoolean(System.getProperty(ENABLE_PERFORMANCE_MONITORING, "true"))) {
             return DaemonExpirationResult.NOT_TRIGGERED;
         }
 
@@ -134,14 +133,14 @@ public DaemonExpirationResult checkExpiration() {
 
             String maxHeap = heapStats.isValid() ? NumberUtil.formatBytes(heapStats.getMaxSizeInBytes()) : "unknown";
             String maxMetaspace = nonHeapStats.isValid() ? NumberUtil.formatBytes(nonHeapStats.getMaxSizeInBytes()) : "unknown";
-            String url = new DocumentationRegistry().getDocumentationFor("build_environment", "sec:configuring_jvm_memory");
+            String url = new DocumentationRegistry().getDocumentationRecommendationFor("information on how to set these values", "build_environment", "sec:configuring_jvm_memory");
 
             logger.warn(EXPIRE_DAEMON_MESSAGE + when + " " + reason + ".\n"
                 + "The project memory settings are likely not configured or are configured to an insufficient value.\n"
                 + extraInfo + ".\n"
                 + "These settings can be adjusted by setting 'org.gradle.jvmargs' in 'gradle.properties'.\n"
                 + "The currently configured max heap space is '" + maxHeap + "' and the configured max metaspace is '" + maxMetaspace + "'.\n"
-                + "For more information on how to set these values, visit the user guide at " + url + "\n"
+                + url + "\n"
                 + "To disable this warning, set '" + DISABLE_PERFORMANCE_LOGGING + "=true'.");
         }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConnectionScopeServices.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConnectionScopeServices.java
index 284392a..c7ddd9a 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConnectionScopeServices.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/ConnectionScopeServices.java
@@ -18,6 +18,7 @@
 
 import org.gradle.api.internal.file.FileCollectionFactory;
 import org.gradle.initialization.layout.BuildLayoutFactory;
+import org.gradle.internal.agents.AgentStatus;
 import org.gradle.internal.event.ListenerManager;
 import org.gradle.internal.jvm.inspection.JvmVersionDetector;
 import org.gradle.internal.logging.events.OutputEventListener;
@@ -45,7 +46,7 @@
  */
 public class ConnectionScopeServices {
     void configure(ServiceRegistration serviceRegistration) {
-        serviceRegistration.addProvider(new GlobalScopeServices(true));
+        serviceRegistration.addProvider(new GlobalScopeServices(true, AgentStatus.disabled()));
         serviceRegistration.addProvider(new DaemonClientGlobalServices());
     }
 
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonBuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonBuildActionExecuter.java
index da0a504..9fb2890 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonBuildActionExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/DaemonBuildActionExecuter.java
@@ -40,7 +40,7 @@ public BuildActionResult execute(BuildAction action, ConnectionOperationParamete
         ClassPath classPath = DefaultClassPath.of(operationParameters.getInjectedPluginClasspath());
 
         DaemonParameters daemonParameters = parameters.getDaemonParameters();
-        BuildActionParameters actionParameters = new DefaultBuildActionParameters(parameters.getTapiSystemProperties(), daemonParameters.getEnvironmentVariables(), SystemProperties.getInstance().getCurrentDir(), operationParameters.getBuildLogLevel(), daemonParameters.isEnabled(), classPath);
+        BuildActionParameters actionParameters = new DefaultBuildActionParameters(parameters.getTapiSystemProperties(), daemonParameters.getEnvironmentVariables(), SystemProperties.getInstance().getCurrentDir(), action.getStartParameter().getLogLevel(), daemonParameters.isEnabled(), classPath);
         return executer.execute(action, actionParameters, buildRequestContext);
     }
 }
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuter.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuter.java
index 3438d4d..abd11e8 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuter.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/LoggingBridgingBuildActionExecuter.java
@@ -46,11 +46,7 @@ public LoggingBridgingBuildActionExecuter(BuildActionExecuter<ConnectionOperatio
     @Override
     public BuildActionResult execute(BuildAction action, ConnectionOperationParameters parameters, BuildRequestContext buildRequestContext) {
         ProviderOperationParameters actionParameters = parameters.getOperationParameters();
-        if (Boolean.TRUE.equals(actionParameters.isColorOutput()) && actionParameters.getStandardOutput() != null) {
-            loggingManager.attachConsole(actionParameters.getStandardOutput(), notNull(actionParameters.getStandardError()), ConsoleOutput.Rich);
-        } else if (actionParameters.getStandardOutput() != null || actionParameters.getStandardError() != null) {
-            loggingManager.attachConsole(notNull(actionParameters.getStandardOutput()), notNull(actionParameters.getStandardError()), ConsoleOutput.Plain);
-        }
+        attachConsole(actionParameters);
         ProgressListenerVersion1 progressListener = actionParameters.getProgressListener();
         OutputEventListenerAdapter listener = new OutputEventListenerAdapter(progressListener);
         loggingManager.addOutputEventListener(listener);
@@ -63,6 +59,16 @@ public BuildActionResult execute(BuildAction action, ConnectionOperationParamete
         }
     }
 
+    private void attachConsole(ProviderOperationParameters actionParameters) {
+        OutputStream stdOut = actionParameters.getStandardOutput();
+        OutputStream stdErr = actionParameters.getStandardError();
+        if (Boolean.TRUE.equals(actionParameters.isColorOutput()) && stdOut != null) {
+            loggingManager.attachConsole(stdOut, notNull(stdErr), ConsoleOutput.Rich);
+        } else if (stdOut != null || stdErr != null) {
+            loggingManager.attachConsole(notNull(stdOut), notNull(stdErr), ConsoleOutput.Plain);
+        }
+    }
+
     private OutputStream notNull(OutputStream outputStream) {
         if (outputStream == null) {
             return NullOutputStream.INSTANCE;
diff --git a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixIn.java b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixIn.java
index 9d18b75..fe32faf 100644
--- a/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixIn.java
+++ b/subprojects/launcher/src/main/java/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixIn.java
@@ -21,32 +21,74 @@
 import org.gradle.cli.CommandLineConverter;
 import org.gradle.cli.CommandLineParser;
 import org.gradle.cli.ParsedCommandLine;
-import org.gradle.internal.logging.DefaultLoggingConfiguration;
+import org.gradle.cli.SystemPropertiesCommandLineConverter;
 import org.gradle.internal.logging.LoggingConfigurationBuildOptions;
+import org.gradle.internal.logging.LoggingConfigurationBuildOptions.LogLevelOption;
 
+import javax.annotation.Nonnull;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static java.util.Optional.empty;
 
 public class BuildLogLevelMixIn {
-    private final ProviderOperationParameters parameters;
+    private final LogLevel logLevel;
 
     public BuildLogLevelMixIn(ProviderOperationParameters parameters) {
-        this.parameters = parameters;
+        this.logLevel = calcBuildLogLevel(parameters);
     }
 
     public LogLevel getBuildLogLevel() {
+        return this.logLevel;
+    }
+
+    private LogLevel calcBuildLogLevel(ProviderOperationParameters parameters) {
         LoggingConfigurationBuildOptions loggingBuildOptions = new LoggingConfigurationBuildOptions();
         CommandLineConverter<LoggingConfiguration> converter = loggingBuildOptions.commandLineConverter();
+
+        SystemPropertiesCommandLineConverter propertiesCommandLineConverter = new SystemPropertiesCommandLineConverter();
         CommandLineParser parser = new CommandLineParser().allowUnknownOptions().allowMixedSubcommandsAndOptions();
+
         converter.configure(parser);
+        propertiesCommandLineConverter.configure(parser);
+
         List<String> arguments = parameters.getArguments();
         ParsedCommandLine parsedCommandLine = parser.parse(arguments == null ? Collections.<String>emptyList() : arguments);
-        //configure verbosely only if arguments do not specify any log level.
-        if (parameters.getVerboseLogging() && !parsedCommandLine.hasAnyOption(loggingBuildOptions.getLogLevelOptions())) {
-            return LogLevel.DEBUG;
-        }
 
-        LoggingConfiguration loggingConfiguration = converter.convert(parsedCommandLine, new DefaultLoggingConfiguration());
-        return loggingConfiguration.getLogLevel();
+        //configure verbosely only if arguments do not specify any log level.
+        return getLogLevelFromCommandLineOptions(loggingBuildOptions, parsedCommandLine)
+            .orElseGet(() ->
+                getLogLevelFromCommandLineProperties(parameters, propertiesCommandLineConverter, parsedCommandLine).orElseGet(() -> {
+                    if (parameters.getVerboseLogging()) {
+                        return LogLevel.DEBUG;
+                    }
+                    return null;
+                })
+            );
+    }
+
+    @Nonnull
+    private static Optional<LogLevel> getLogLevelFromCommandLineOptions(LoggingConfigurationBuildOptions loggingBuildOptions, ParsedCommandLine parsedCommandLine) {
+        return loggingBuildOptions.getLongLogLevelOptions().stream()
+            .filter(parsedCommandLine::hasOption)
+            .map(LogLevelOption::parseLogLevel)
+            .findFirst();
+    }
+
+    private static Optional<LogLevel> getLogLevelFromCommandLineProperties(ProviderOperationParameters parameters, SystemPropertiesCommandLineConverter propertiesCommandLineConverter, ParsedCommandLine parsedCommandLine) {
+        Map<String, String> properties = propertiesCommandLineConverter.convert(parsedCommandLine, new HashMap<>());
+        String logLevelCommandLineProperty = properties.get(LogLevelOption.GRADLE_PROPERTY);
+        if (logLevelCommandLineProperty != null) {
+            try {
+                return Optional.of(LogLevelOption.parseLogLevel(logLevelCommandLineProperty));
+            } catch (IllegalArgumentException e) {
+                // fall through
+            }
+        }
+        return empty();
+
     }
 }
diff --git a/subprojects/launcher/src/main/resources/release-features.txt b/subprojects/launcher/src/main/resources/release-features.txt
index dffa762..e69de29 100644
--- a/subprojects/launcher/src/main/resources/release-features.txt
+++ b/subprojects/launcher/src/main/resources/release-features.txt
@@ -1,3 +0,0 @@
- - Improvements to the Kotlin DSL
- - Fine-grained parallelism from the first build with configuration cache
- - Configurable Gradle user home cache cleanup
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/StartParameterConverterTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/StartParameterConverterTest.groovy
index f1b1402..375a557 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/StartParameterConverterTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/cli/converter/StartParameterConverterTest.groovy
@@ -91,20 +91,20 @@
     def "can provide start parameter option as command-line option"() {
         expect:
         def parameter = convert("--configuration-cache")
-        parameter.configurationCache
+        parameter.configurationCache.get()
     }
 
     def "can provide start parameter option as system property on command-line"() {
         expect:
         def parameter = convert("-Dorg.gradle.unsafe.configuration-cache=true")
-        parameter.configurationCache
+        parameter.configurationCache.get()
     }
 
     def "can provide start parameter option as persistent property"() {
         expect:
         userHome.file("gradle.properties") << "org.gradle.unsafe.configuration-cache=true"
         def parameter = convert()
-        parameter.configurationCache
+        parameter.configurationCache.get()
     }
 
     def "system property on command-line has precedence over persistent property"() {
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientTest.groovy
index 4b4a11c..a96b958 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/DaemonClientTest.groovy
@@ -16,6 +16,7 @@
 package org.gradle.launcher.daemon.client
 
 import org.gradle.api.BuildCancelledException
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.initialization.BuildCancellationToken
 import org.gradle.initialization.BuildRequestContext
 import org.gradle.internal.id.UUIDGenerator
@@ -203,6 +204,6 @@
         0 * connection3.stop()
         def exception = thrown(NoUsableDaemonFoundException)
         exception.message.contains 'A new daemon was started but could not be connected to'
-        exception.message.contains 'userguide/troubleshooting.html#network_connection for more details'
+        exception.resolutions[0].contains new DocumentationRegistry().getDocumentationRecommendationFor("information", "troubleshooting", "network_connection")
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/ReportDaemonStatusClientTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/ReportDaemonStatusClientTest.groovy
index 9f2f94f..ffc0aaf 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/ReportDaemonStatusClientTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/client/ReportDaemonStatusClientTest.groovy
@@ -42,7 +42,7 @@
         then:
         1 * registry.getAll() >> []
         1 * registry.getStopEvents() >> []
-        1 * documentationRegistry.getDocumentationFor('gradle_daemon', _) >> { "DOCUMENTATION_URL" }
+        1 * documentationRegistry.getDocumentationRecommendationFor('on this', 'gradle_daemon', 'sec:status') >> { "DOCUMENTATION_URL" }
         0 * _
     }
 
@@ -66,7 +66,7 @@
         1 * registry.getStopEvents() >> []
 
         and:
-        1 * documentationRegistry.getDocumentationFor('gradle_daemon', _) >> { "DOCUMENTATION_URL" }
+        1 * documentationRegistry.getDocumentationRecommendationFor('on this', 'gradle_daemon', 'sec:status') >> { "DOCUMENTATION_URL" }
         0 * _
     }
 
@@ -99,7 +99,7 @@
         1 * registry.getStopEvents() >> []
 
         and:
-        1 * documentationRegistry.getDocumentationFor('gradle_daemon', _) >> { "DOCUMENTATION_URL" }
+        1 * documentationRegistry.getDocumentationRecommendationFor('on this', 'gradle_daemon', 'sec:status') >> { "DOCUMENTATION_URL" }
         0 * _
     }
 
@@ -115,7 +115,7 @@
         1 * registry.getStopEvents() >> [stopEvent1]
 
         and:
-        1 * documentationRegistry.getDocumentationFor('gradle_daemon', _) >> { "DOCUMENTATION_URL" }
+        1 * documentationRegistry.getDocumentationRecommendationFor('on this', 'gradle_daemon', 'sec:status') >> { "DOCUMENTATION_URL" }
         0 * _
     }
 
@@ -130,7 +130,7 @@
         1 * registry.getAll() >> { [daemon1] as List<DaemonInfo> }
         1 * connector.maybeConnect(daemon1) >> { null }
         1 * registry.getStopEvents() >> []
-        1 * documentationRegistry.getDocumentationFor('gradle_daemon', _) >> { "DOCUMENTATION_URL" }
+        1 * documentationRegistry.getDocumentationRecommendationFor('on this', 'gradle_daemon', 'sec:status') >> { "DOCUMENTATION_URL" }
         0 * _
     }
 }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/BuildProcessTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/BuildProcessTest.groovy
index 14e600b..6e8e9ea 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/BuildProcessTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/configuration/BuildProcessTest.groovy
@@ -18,6 +18,7 @@
 
 import org.gradle.api.JavaVersion
 import org.gradle.api.internal.file.TestFiles
+import org.gradle.internal.agents.AgentStatus
 import org.gradle.internal.jvm.JavaInfo
 import org.gradle.launcher.configuration.BuildLayoutResult
 import org.gradle.process.internal.JvmOptions
@@ -36,6 +37,9 @@
 
     private def fileCollectionFactory = TestFiles.fileCollectionFactory(tmpDir.testDirectory)
     private def currentJvm = Stub(JavaInfo)
+    private def agentStatus = Stub(AgentStatus) {
+        isAgentInstrumentationEnabled() >> true
+    }
 
     def "current and requested build vm match if vm arguments match"() {
         given:
@@ -45,7 +49,7 @@
         currentJvmOptions.jvmArgs = ["-XX:+HeapDumpOnOutOfMemoryError"]
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, currentJvmOptions)
+        def buildProcess = createBuildProcess(currentJvmOptions)
 
         then:
         buildProcess.configureForBuild(buildParameters(["-Xms16m", "-Xmx256m", "-XX:+HeapDumpOnOutOfMemoryError"]))
@@ -59,7 +63,7 @@
         currentJvmOptions.jvmArgs = ["-XX:+HeapDumpOnOutOfMemoryError"]
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, currentJvmOptions)
+        def buildProcess = createBuildProcess(currentJvmOptions)
 
         then:
         !buildProcess.configureForBuild(buildParameters(["-Xms16m", "-Xmx256m", "-XX:+HeapDumpOnOutOfMemoryError"]))
@@ -67,7 +71,7 @@
 
     def "current and requested build vm match if java home matches"() {
         when:
-        def buildProcess = new BuildProcess(currentJvm, new JvmOptions(fileCollectionFactory))
+        def buildProcess = createBuildProcess()
 
         then:
         buildProcess.configureForBuild(buildParameters(currentJvm))
@@ -81,7 +85,7 @@
         currentJvmOptions.setAllJvmArgs(["-Dfile.encoding=$notDefaultEncoding", "-Xmx100m", "-XX:SomethingElse"])
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, currentJvmOptions)
+        def buildProcess = createBuildProcess(currentJvmOptions)
 
         then:
         !buildProcess.configureForBuild(buildParameters(["-Dfile.encoding=$notDefaultEncoding"])) //only properties match
@@ -100,7 +104,7 @@
         def emptyRequest = buildParameters([])
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, currentJvmOptions)
+        def buildProcess = createBuildProcess(currentJvmOptions)
 
         then:
         buildProcess.configureForBuild(emptyRequest)
@@ -113,7 +117,7 @@
         def defaultRequest = buildParameters(null as Iterable)
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, currentJvmOptions)
+        def buildProcess = createBuildProcess(currentJvmOptions)
 
         then:
         !buildProcess.configureForBuild(defaultRequest)
@@ -126,7 +130,7 @@
         def requestWithDefaults = buildParameters((Iterable) null)
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, new JvmOptions(fileCollectionFactory))
+        def buildProcess = createBuildProcess()
 
         then:
         requestWithDefaults.getEffectiveJvmArgs().containsAll(DaemonParameters.DEFAULT_JVM_ARGS)
@@ -142,7 +146,7 @@
         def requestWithMutableArgument = buildParameters(["-Dfoo=bar"])
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, currentJvmOptions)
+        def buildProcess = createBuildProcess(currentJvmOptions)
 
         then:
         buildProcess.configureForBuild(requestWithMutableArgument)
@@ -154,7 +158,7 @@
         currentJvmOptions.setAllJvmArgs(["-Xmx100m", "-XX:SomethingElse", "-Dfoo=bar", "-Dbaz"])
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, currentJvmOptions)
+        def buildProcess = createBuildProcess(currentJvmOptions)
 
         then:
         !buildProcess.configureForBuild(buildParameters(["-Xms10m", "-Dfoo=bar", "-Dbaz"]))
@@ -171,7 +175,7 @@
         debugDisabled.setDebug(false)
 
         when:
-        BuildProcess buildProcess = new BuildProcess(currentJvm, new JvmOptions(fileCollectionFactory))
+        BuildProcess buildProcess = createBuildProcess()
 
         then:
         !buildProcess.configureForBuild(debugEnabled)
@@ -186,7 +190,7 @@
         currentJvmOptions.setAllJvmArgs(["-Xmx100m", "-XX:SomethingElse", "-Dfoo=bar", "-Dbaz"])
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, currentJvmOptions)
+        def buildProcess = createBuildProcess(currentJvmOptions)
 
         then:
         buildProcess.configureForBuild(buildParameters(["-Dfile.encoding=${Charset.defaultCharset().name()}"]))
@@ -202,11 +206,11 @@
         def notDefaultEncoding = ["UTF-8", "US-ASCII"].collect { Charset.forName(it) } find { it != Charset.defaultCharset() }
 
         when:
-        BuildProcess buildProcess = new BuildProcess(currentJvm, new JvmOptions(fileCollectionFactory))
+        BuildProcess buildProcess = createBuildProcess()
 
         then:
-        buildProcess.configureForBuild(buildParameters([], [ "file.encoding" : Charset.defaultCharset().name() ]))
-        !buildProcess.configureForBuild(buildParameters([], [ "file.encoding" : notDefaultEncoding.toString() ]))
+        buildProcess.configureForBuild(buildParameters([], ["file.encoding": Charset.defaultCharset().name()]))
+        !buildProcess.configureForBuild(buildParameters([], ["file.encoding": notDefaultEncoding.toString()]))
     }
 
     def "sets all mutable system properties before running build"() {
@@ -214,7 +218,7 @@
         def parameters = buildParameters(["-Dfoo=bar", "-Dbaz"])
 
         then:
-        new BuildProcess(currentJvm, new JvmOptions(fileCollectionFactory)).configureForBuild(parameters)
+        createBuildProcess().configureForBuild(parameters)
 
         and:
         System.getProperty('foo') == 'bar'
@@ -226,7 +230,7 @@
         def emptyBuildParameters = buildParameters([])
 
         when:
-        new BuildProcess(currentJvm, new JvmOptions(fileCollectionFactory)).configureForBuild(emptyBuildParameters)
+        createBuildProcess().configureForBuild(emptyBuildParameters)
 
         then:
         !emptyBuildParameters.getEffectiveJvmArgs().containsAll(DaemonParameters.DEFAULT_JVM_ARGS)
@@ -237,12 +241,47 @@
         def parametersWithDefaults = buildParameters(DaemonParameters.DEFAULT_JVM_ARGS)
 
         when:
-        def buildProcess = new BuildProcess(currentJvm, new JvmOptions(fileCollectionFactory))
+        def buildProcess = createBuildProcess()
 
         then:
         !buildProcess.configureForBuild(parametersWithDefaults)
     }
 
+    def "instrumentation agent status is considered during matching"() {
+        given:
+        def desiredParameters = buildParameters([])
+        desiredParameters.setApplyInstrumentationAgent(desiredInstrumentationStatus)
+
+        when:
+        def currentAgentStatus = Stub(AgentStatus) {
+            isAgentInstrumentationEnabled() >> agentApplied
+        }
+        BuildProcess buildProcess = createBuildProcess(currentAgentStatus)
+
+        then:
+        buildProcess.configureForBuild(desiredParameters) == expectProcessToBeUsable
+
+        where:
+        desiredInstrumentationStatus | agentApplied || expectProcessToBeUsable
+        // process without the agent applied can be used if no agent instrumentation is requested
+        false                        | false        || true
+        // process with the agent applied can be used if no agent instrumentation is requested.
+        // We expect the code to avoid using the agent in this case
+        false                        | true         || true
+        // process without the agent applied cannot be used if the agent instrumentation is requested
+        true                         | false        || false
+        // process with the agent applied can be used if the agent instrumentation is requested
+        true                         | true         || true
+    }
+
+    private BuildProcess createBuildProcess(AgentStatus agentStatus) {
+        return createBuildProcess(new JvmOptions(fileCollectionFactory), agentStatus)
+    }
+
+    private BuildProcess createBuildProcess(JvmOptions jvmOptions = new JvmOptions(fileCollectionFactory), AgentStatus agentStatus = this.agentStatus) {
+        return new BuildProcess(currentJvm, jvmOptions, agentStatus)
+    }
+
     private DaemonParameters buildParameters(Iterable<String> jvmArgs) {
         return buildParameters(currentJvm, jvmArgs)
     }
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/context/DaemonCompatibilitySpecSpec.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/context/DaemonCompatibilitySpecSpec.groovy
index ce602ba..5aa5318 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/context/DaemonCompatibilitySpecSpec.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/context/DaemonCompatibilitySpecSpec.groovy
@@ -15,14 +15,13 @@
  */
 package org.gradle.launcher.daemon.context
 
-
 import org.gradle.internal.nativeintegration.ProcessEnvironment
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.launcher.daemon.configuration.DaemonParameters
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.ConfigureUtil
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -85,7 +84,7 @@
         unsatisfiedReason.contains "Java home is different"
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "contexts with symlinked javaHome are compatible"() {
         // Make something that looks like a Java installation
         def jdk = tmp.testDirectory.file("jdk").createDir()
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/DaemonRegistryServicesTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/DaemonRegistryServicesTest.groovy
index 1125c75..8345ea7 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/DaemonRegistryServicesTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/launcher/daemon/registry/DaemonRegistryServicesTest.groovy
@@ -54,12 +54,14 @@
 
     def "the registry can be concurrently written to"() {
         when:
+        // obtain localhost address ahead of time as the first call in a JVM can take multiple secs in some systems
+        def localhost = Inet6Address.getLocalHost()
         def registry = registry("someDir").get(DaemonRegistry)
         5.times { idx ->
             concurrent.start {
                 def context = new DefaultDaemonContext("$idx", new File("$idx"), new File("$idx"), idx, 5000, [], false, DaemonParameters.Priority.NORMAL)
                 registry.store(new DaemonInfo(
-                    new SocketInetAddress(Inet6Address.getLocalHost(), (int)(8888 + idx)), context, "foo-$idx".bytes, Idle))
+                    new SocketInetAddress(localhost, (int)(8888 + idx)), context, "foo-$idx".bytes, Idle))
             }
         }
         concurrent.finished()
diff --git a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixInTest.groovy b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixInTest.groovy
index d738287..0c04bf3 100644
--- a/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixInTest.groovy
+++ b/subprojects/launcher/src/test/groovy/org/gradle/tooling/internal/provider/connection/BuildLogLevelMixInTest.groovy
@@ -16,55 +16,37 @@
 
 package org.gradle.tooling.internal.provider.connection
 
+
 import org.gradle.api.logging.LogLevel
 import spock.lang.Specification
 
 class BuildLogLevelMixInTest extends Specification {
 
     final parameters = Mock(ProviderOperationParameters)
-    final mixin = new BuildLogLevelMixIn(parameters)
 
-    def "knows build log level for mixed set of arguments"() {
-        when:
-        parameters.getArguments() >> args
-        parameters.getVerboseLogging() >> false
-
-        then:
-        mixin.getBuildLogLevel() == logLevel
-
-        where:
-        args                     | logLevel
-        ['-i']                   | LogLevel.INFO
-        ['-q']                   | LogLevel.QUIET
-        ['-w']                   | LogLevel.WARN
-        ['foo', '--info', 'bar'] | LogLevel.INFO
-        ['-i', 'foo', 'bar']     | LogLevel.INFO
-        ['foo', 'bar', '-i']     | LogLevel.INFO
-    }
-
-    def "verbose flag is only used when no log level arguments"() {
+    def "knows build log level for mixed set of arguments (or not arguments)"() {
         when:
         parameters.getArguments() >> args
         parameters.getVerboseLogging() >> verbose
+        def mixin = new BuildLogLevelMixIn(parameters)
 
         then:
         mixin.getBuildLogLevel() == logLevel
 
         where:
-        args                    | verbose | logLevel
-        ['-q']                  | false   | LogLevel.QUIET
-        ['-q']                  | true    | LogLevel.QUIET
-        ['noLogLevelArguments'] | true    | LogLevel.DEBUG
-        null                    | false   | LogLevel.LIFECYCLE
-        null                    | true    | LogLevel.DEBUG
-    }
+        args                                | verbose | logLevel
+        ['-i']                              | false   | LogLevel.INFO
+        ['no log level arguments']          | false   | null
+        null                                | false   | null
+        ['-q']                              | false   | LogLevel.QUIET
+        ['-w']                              | false   | LogLevel.WARN
+        ['foo', '--info', 'bar']            | false   | LogLevel.INFO
+        ['-i', 'foo', 'bar']                | false   | LogLevel.INFO
+        ['foo', 'bar', '-i']                | false   | LogLevel.INFO
+        ['-Dorg.gradle.logging.level=info'] | false   | LogLevel.INFO
+        ['-q']                              | true    | LogLevel.QUIET
+        ['no log level arguments']          | true    | LogLevel.DEBUG
+        null                                | true    | LogLevel.DEBUG
 
-    def "default log level is lifecycle"() {
-        when:
-        parameters.getArguments() >> ['no log level arguments']
-        parameters.getVerboseLogging() >> false
-
-        then:
-        mixin.getBuildLogLevel() == LogLevel.LIFECYCLE
     }
 }
diff --git a/subprojects/logging-api/src/main/java/org/gradle/api/logging/configuration/package-info.java b/subprojects/logging-api/src/main/java/org/gradle/api/logging/configuration/package-info.java
index 150a8f6..040f29c 100644
--- a/subprojects/logging-api/src/main/java/org/gradle/api/logging/configuration/package-info.java
+++ b/subprojects/logging-api/src/main/java/org/gradle/api/logging/configuration/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Classes for logging configuration.
  */
-package org.gradle.api.logging.configuration;
\ No newline at end of file
+package org.gradle.api.logging.configuration;
diff --git a/subprojects/logging-api/src/main/java/org/gradle/api/logging/package-info.java b/subprojects/logging-api/src/main/java/org/gradle/api/logging/package-info.java
index 801f849..64d3423 100644
--- a/subprojects/logging-api/src/main/java/org/gradle/api/logging/package-info.java
+++ b/subprojects/logging-api/src/main/java/org/gradle/api/logging/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Classes for managing logging in Gradle.
  */
-package org.gradle.api.logging;
\ No newline at end of file
+package org.gradle.api.logging;
diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy
index eb03033..9843a57 100644
--- a/subprojects/logging/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy
+++ b/subprojects/logging/src/integTest/groovy/org/gradle/DeprecationHandlingIntegrationTest.groovy
@@ -108,7 +108,7 @@
         and:
         output.contains(LoggingDeprecatedFeatureHandler.WARNING_SUMMARY) == warningsSummary
         output.contains("You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.") == warningsSummary
-        output.contains(LoggingDeprecatedFeatureHandler.WARNING_LOGGING_DOCS_MESSAGE) == warningsSummary
+        output.contains(documentationRegistry.getDocumentationRecommendationFor("on this", "command_line_interface", "sec:command_line_warnings")) == warningsSummary
 
         and: "system stack frames are filtered"
         !output.contains('jdk.internal.')
diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/AbstractConsoleJvmTestWorkerFunctionalTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/AbstractConsoleJvmTestWorkerFunctionalTest.groovy
index d1773db..1c02773 100644
--- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/AbstractConsoleJvmTestWorkerFunctionalTest.groovy
+++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/AbstractConsoleJvmTestWorkerFunctionalTest.groovy
@@ -18,6 +18,7 @@
 
 import org.gradle.api.logging.configuration.ConsoleOutput
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.RepoScriptBlockUtil
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.ConcurrentTestUtil
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
@@ -27,7 +28,6 @@
 import static org.gradle.internal.logging.console.jvm.TestedProjectFixture.JavaTestClass
 import static org.gradle.internal.logging.console.jvm.TestedProjectFixture.containsTestExecutionWorkInProgressLine
 import static org.gradle.internal.logging.console.jvm.TestedProjectFixture.testClass
-import static org.gradle.internal.logging.console.jvm.TestedProjectFixture.testableJavaProject
 
 @IgnoreIf({ GradleContextualExecuter.isParallel() })
 abstract class AbstractConsoleJvmTestWorkerFunctionalTest extends AbstractIntegrationSpec {
@@ -48,8 +48,20 @@
 
     def "shows test class execution #description test class name in work-in-progress area of console for single project build"() {
         given:
-        buildFile << testableJavaProject(testDependency(), MAX_WORKERS)
-        buildFile << testFrameworkConfiguration()
+        buildFile << """
+            plugins {
+                id 'java-library'
+            }
+
+            ${mavenCentralRepository()}
+
+            tasks.withType(Test) {
+                maxParallelForks = $MAX_WORKERS
+            }
+
+            ${testFrameworkConfiguration()}
+        """
+
         file("src/test/java/${testClass1.fileRepresentation}") << testClass(testAnnotationClass(), testClass1.classNameWithoutPackage, SERVER_RESOURCE_1, server)
         file("src/test/java/${testClass2.fileRepresentation}") << testClass(testAnnotationClass(), testClass2.classNameWithoutPackage, SERVER_RESOURCE_2, server)
         def testExecution = server.expectConcurrentAndBlock(2, SERVER_RESOURCE_1, SERVER_RESOURCE_2)
@@ -78,7 +90,14 @@
         settingsFile << "include 'project1', 'project2'"
         buildFile << """
             subprojects {
-                ${testableJavaProject(testDependency(), MAX_WORKERS)}
+                apply plugin: 'java-library'
+
+                ${RepoScriptBlockUtil.mavenCentralRepository()}
+
+                tasks.withType(Test) {
+                    maxParallelForks = $MAX_WORKERS
+                }
+
                 ${testFrameworkConfiguration()}
             }
         """
@@ -106,6 +125,5 @@
     }
 
     abstract String testAnnotationClass()
-    abstract String testDependency()
     abstract String testFrameworkConfiguration()
 }
diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleJUnitPlatformTestWorkerFunctionalTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleJUnitPlatformTestWorkerFunctionalTest.groovy
index fe1cf7d..38cc265 100644
--- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleJUnitPlatformTestWorkerFunctionalTest.groovy
+++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleJUnitPlatformTestWorkerFunctionalTest.groovy
@@ -16,8 +16,6 @@
 
 package org.gradle.internal.logging.console.jvm
 
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
-
 class ConsoleJUnitPlatformTestWorkerFunctionalTest extends AbstractConsoleJvmTestWorkerFunctionalTest {
 
     @Override
@@ -26,16 +24,7 @@
     }
 
     @Override
-    String testDependency() {
-        "org.junit.jupiter:junit-jupiter:$LATEST_JUPITER_VERSION"
-    }
-
-    @Override
     String testFrameworkConfiguration() {
-        """
-            tasks.withType(Test) {
-                useJUnitPlatform()
-            }
-        """
+        'testing.suites.test.useJUnitJupiter()'
     }
 }
diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleJUnitTestWorkerFunctionalTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleJUnitTestWorkerFunctionalTest.groovy
index 03c3ab9..f08872c 100644
--- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleJUnitTestWorkerFunctionalTest.groovy
+++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleJUnitTestWorkerFunctionalTest.groovy
@@ -24,12 +24,7 @@
     }
 
     @Override
-    String testDependency() {
-        'junit:junit:4.13'
-    }
-
-    @Override
     String testFrameworkConfiguration() {
-        ''
+        'testing.suites.test.useJUnit()'
     }
 }
diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleTestNGTestWorkerFunctionalTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleTestNGTestWorkerFunctionalTest.groovy
index 49e49ad..3a976e8 100644
--- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleTestNGTestWorkerFunctionalTest.groovy
+++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleTestNGTestWorkerFunctionalTest.groovy
@@ -24,16 +24,7 @@
     }
 
     @Override
-    String testDependency() {
-        'org.testng:testng:6.9.13.6'
-    }
-
-    @Override
     String testFrameworkConfiguration() {
-        """
-            tasks.withType(Test) {
-                useTestNG()
-            }
-        """
+        'testing.suites.test.useTestNG()'
     }
 }
diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleTestNGUnsupportedTestWorkerFunctionalTest.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleTestNGUnsupportedTestWorkerFunctionalTest.groovy
index 521aa94..f940089 100644
--- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleTestNGUnsupportedTestWorkerFunctionalTest.groovy
+++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/ConsoleTestNGUnsupportedTestWorkerFunctionalTest.groovy
@@ -22,15 +22,13 @@
 import org.junit.Rule
 
 import static org.gradle.internal.logging.console.jvm.TestedProjectFixture.testClass
-import static org.gradle.internal.logging.console.jvm.TestedProjectFixture.testableJavaProject
-import static org.gradle.internal.logging.console.jvm.TestedProjectFixture.useTestNG
 
 class ConsoleTestNGUnsupportedTestWorkerFunctionalTest extends AbstractIntegrationSpec {
 
     private static final int MAX_WORKERS = 2
     private static final String SERVER_RESOURCE_1 = 'test-1'
     private static final String SERVER_RESOURCE_2 = 'test-2'
-    private static final String TESTNG_DEPENDENCY = 'org.testng:testng:6.3.1'
+    private static final String TESTNG_VERSION = '6.3.1'
     private static final String TESTNG_ANNOTATION = 'org.testng.annotations.Test'
     private static final String TEST_CLASS_1 = 'Test1'
     private static final String TEST_CLASS_2 = 'Test2'
@@ -46,8 +44,20 @@
 
     def "omits parallel test execution if TestNG version does not emit class listener events"() {
         given:
-        buildFile << testableJavaProject(TESTNG_DEPENDENCY, MAX_WORKERS)
-        buildFile << useTestNG()
+        buildFile << """
+            plugins {
+                id 'java-library'
+            }
+
+            ${mavenCentralRepository()}
+
+            testing.suites.test {
+                useTestNG('$TESTNG_VERSION')
+                targets.test.testTask.configure {
+                    maxParallelForks = $MAX_WORKERS
+                }
+            }
+        """
         file("src/test/java/${TEST_CLASS_1}.java") << testClass(TESTNG_ANNOTATION, TEST_CLASS_1, SERVER_RESOURCE_1, server)
         file("src/test/java/${TEST_CLASS_2}.java") << testClass(TESTNG_ANNOTATION, TEST_CLASS_2, SERVER_RESOURCE_2, server)
         server.expectConcurrent(SERVER_RESOURCE_1, SERVER_RESOURCE_2)
diff --git a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/TestedProjectFixture.groovy b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/TestedProjectFixture.groovy
index 7d48e5e..ff7b0fd 100644
--- a/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/TestedProjectFixture.groovy
+++ b/subprojects/logging/src/integTest/groovy/org/gradle/internal/logging/console/jvm/TestedProjectFixture.groovy
@@ -16,7 +16,6 @@
 
 package org.gradle.internal.logging.console.jvm
 
-import org.gradle.integtests.fixtures.RepoScriptBlockUtil
 import org.gradle.integtests.fixtures.RichConsoleStyling
 import org.gradle.integtests.fixtures.executer.GradleHandle
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
@@ -38,30 +37,6 @@
         """
     }
 
-    static String testableJavaProject(String testDependency, int maxWorkers) {
-        """
-            apply plugin: 'java'
-
-            ${RepoScriptBlockUtil.mavenCentralRepository()}
-
-            dependencies {
-                testImplementation '${testDependency}'
-            }
-
-            tasks.withType(Test) {
-                maxParallelForks = $maxWorkers
-            }
-        """
-    }
-
-    static String useTestNG() {
-        """
-            tasks.withType(Test) {
-                useTestNG()
-            }
-        """
-    }
-
     static void containsTestExecutionWorkInProgressLine(GradleHandle gradleHandle, String taskPath, String testName) {
         RichConsoleStyling.assertHasWorkInProgress(gradleHandle, "> $taskPath > Executing test $testName")
     }
diff --git a/subprojects/logging/src/integTest/resources/org/gradle/internal/logging/LoggingIntegrationTest/logging/project2/build.gradle b/subprojects/logging/src/integTest/resources/org/gradle/internal/logging/LoggingIntegrationTest/logging/project2/build.gradle
index 585751b..1eee277 100644
--- a/subprojects/logging/src/integTest/resources/org/gradle/internal/logging/LoggingIntegrationTest/logging/project2/build.gradle
+++ b/subprojects/logging/src/integTest/resources/org/gradle/internal/logging/LoggingIntegrationTest/logging/project2/build.gradle
@@ -11,4 +11,4 @@
 
 gradle.taskGraph.whenReady {
     println 'quietProject2CallbackOut'
-}
\ No newline at end of file
+}
diff --git a/subprojects/logging/src/main/java/org/gradle/api/internal/DocumentationRegistry.java b/subprojects/logging/src/main/java/org/gradle/api/internal/DocumentationRegistry.java
index c734429..02492ad 100644
--- a/subprojects/logging/src/main/java/org/gradle/api/internal/DocumentationRegistry.java
+++ b/subprojects/logging/src/main/java/org/gradle/api/internal/DocumentationRegistry.java
@@ -25,33 +25,64 @@
  */
 @ServiceScope(Scope.Global.class)
 public class DocumentationRegistry {
-    private final GradleVersion gradleVersion;
+    public final static String BASE_URL = "https://docs.gradle.org/" + GradleVersion.current().getVersion();
+    public static final String RECOMMENDATION = "For more %s, please refer to %s in the Gradle documentation.";
+    public static final String DSL_PROPERTY_URL_FORMAT = "%s/dsl/%s.html#%s:%s";
+    public static final String LEARN_MORE_STRING = "To learn more about Gradle by exploring our Samples at ";
 
     public DocumentationRegistry() {
-        this.gradleVersion = GradleVersion.current();
     }
 
+
     /**
      * Returns the location of the documentation for the given feature, referenced by id. The location may be local or remote.
      */
     public String getDocumentationFor(String id) {
-        return String.format("https://docs.gradle.org/%s/userguide/%s.html", gradleVersion.getVersion(), id);
+        return String.format("%s/userguide/%s.html", BASE_URL, id);
     }
 
+
+    /**
+     * Returns the location of the documentation for the given feature, referenced by id and section. The location may be local or remote.
+     */
     public String getDocumentationFor(String id, String section) {
-        return String.format("https://docs.gradle.org/%s/userguide/%s.html#%s", gradleVersion.getVersion(), id, section);
+        return getDocumentationFor(id) + "#" + section;
     }
 
     public String getDslRefForProperty(Class<?> clazz, String property) {
         String className = clazz.getName();
-        return String.format("https://docs.gradle.org/%s/dsl/%s.html#%s:%s", gradleVersion.getVersion(), className, className, property);
+        return String.format(DSL_PROPERTY_URL_FORMAT, BASE_URL, className, className, property);
+    }
+
+    public String getDslRefForProperty(String className, String property) {
+        return String.format(DSL_PROPERTY_URL_FORMAT, BASE_URL, className, className, property);
     }
 
     public String getSampleIndex() {
-        return String.format("https://docs.gradle.org/%s/samples", gradleVersion.getVersion());
+        return BASE_URL + "/samples";
     }
 
     public String getSampleFor(String id) {
-        return String.format("https://docs.gradle.org/%s/samples/sample_%s.html", gradleVersion.getVersion(), id);
+        return String.format(getSampleIndex() + "/sample_%s.html", id);
+    }
+
+    public String getSampleForMessage(String id) {
+        return LEARN_MORE_STRING + getSampleFor(id);
+    }
+
+    public String getSampleForMessage() {
+        return LEARN_MORE_STRING + getSampleIndex();
+    }
+
+    public String getDocumentationRecommendationFor(String topic, String id) {
+        return getRecommendationString(topic, getDocumentationFor(id));
+    }
+
+    public String getDocumentationRecommendationFor(String topic, String id, String section) {
+        return getRecommendationString(topic, getDocumentationFor(id, section));
+    }
+
+    private static String getRecommendationString(String topic, String url) {
+        return String.format(RECOMMENDATION, topic.trim(), url);
     }
 }
diff --git a/subprojects/logging/src/main/java/org/gradle/internal/deprecation/DeprecatedFeatureUsage.java b/subprojects/logging/src/main/java/org/gradle/internal/deprecation/DeprecatedFeatureUsage.java
index eb0b285..b2b94d3 100644
--- a/subprojects/logging/src/main/java/org/gradle/internal/deprecation/DeprecatedFeatureUsage.java
+++ b/subprojects/logging/src/main/java/org/gradle/internal/deprecation/DeprecatedFeatureUsage.java
@@ -22,6 +22,8 @@
 
 import javax.annotation.Nullable;
 
+import static org.apache.commons.lang.StringUtils.isNotEmpty;
+
 public class DeprecatedFeatureUsage extends FeatureUsage {
 
     private final String removalDetails;
@@ -145,10 +147,9 @@ public String formattedMessage() {
         return outputBuilder.toString();
     }
 
-    private void append(StringBuilder outputBuilder, String message) {
-        if (message != null && message.length() > 0) {
+    private static void append(StringBuilder outputBuilder, @Nullable String message) {
+        if (isNotEmpty(message)) {
             outputBuilder.append(" ").append(message);
         }
     }
-
 }
diff --git a/subprojects/logging/src/main/java/org/gradle/internal/deprecation/Documentation.java b/subprojects/logging/src/main/java/org/gradle/internal/deprecation/Documentation.java
index 02c975b..948f6ed 100644
--- a/subprojects/logging/src/main/java/org/gradle/internal/deprecation/Documentation.java
+++ b/subprojects/logging/src/main/java/org/gradle/internal/deprecation/Documentation.java
@@ -21,6 +21,8 @@
 
 import javax.annotation.Nullable;
 
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION;
+
 public abstract class Documentation {
     private static final DocumentationRegistry DOCUMENTATION_REGISTRY = new DocumentationRegistry();
 
@@ -47,7 +49,7 @@ public static Documentation dslReference(Class<?> targetClass, String property)
 
     @Nullable
     public String consultDocumentationMessage() {
-        return String.format("See %s for more details.", documentationUrl());
+        return String.format(RECOMMENDATION, "information", documentationUrl());
     }
 
     public static abstract class AbstractBuilder<T> {
diff --git a/subprojects/logging/src/main/java/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java b/subprojects/logging/src/main/java/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java
index cd52ce2..9b94024 100644
--- a/subprojects/logging/src/main/java/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java
+++ b/subprojects/logging/src/main/java/org/gradle/internal/featurelifecycle/LoggingDeprecatedFeatureHandler.java
@@ -108,10 +108,10 @@ public void reset() {
 
     public void reportSuppressedDeprecations() {
         if (warningMode == WarningMode.Summary && deprecationsFound) {
-            LOGGER.warn("\n{} {}.\n\nYou can use '--{} {}' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.\n\n{} {}",
+            LOGGER.warn("\n{} {}.\n\nYou can use '--{} {}' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.\n\n{}",
                 WARNING_SUMMARY, DefaultGradleVersion.current().getNextMajorVersion().getVersion(),
                 LoggingConfigurationBuildOptions.WarningsOption.LONG_OPTION, WarningMode.All.name().toLowerCase(),
-                WARNING_LOGGING_DOCS_MESSAGE, DOCUMENTATION_REGISTRY.getDocumentationFor("command_line_interface", "sec:command_line_warnings"));
+                DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("on this", "command_line_interface", "sec:command_line_warnings"));
         }
     }
 
diff --git a/subprojects/logging/src/main/java/org/gradle/internal/logging/LoggingConfigurationBuildOptions.java b/subprojects/logging/src/main/java/org/gradle/internal/logging/LoggingConfigurationBuildOptions.java
index 4bdf67b..e6102a1 100644
--- a/subprojects/logging/src/main/java/org/gradle/internal/logging/LoggingConfigurationBuildOptions.java
+++ b/subprojects/logging/src/main/java/org/gradle/internal/logging/LoggingConfigurationBuildOptions.java
@@ -16,6 +16,7 @@
 
 package org.gradle.internal.logging;
 
+import com.google.common.collect.ImmutableList;
 import org.gradle.api.logging.LogLevel;
 import org.gradle.api.logging.configuration.ConsoleOutput;
 import org.gradle.api.logging.configuration.LoggingConfiguration;
@@ -31,26 +32,18 @@
 import org.gradle.internal.buildoption.StringBuildOption;
 import org.gradle.util.internal.TextUtil;
 
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
 public class LoggingConfigurationBuildOptions extends BuildOptionSet<LoggingConfiguration> {
 
-    private static List<BuildOption<LoggingConfiguration>> options;
-
-    static {
-        List<BuildOption<LoggingConfiguration>> options = new ArrayList<BuildOption<LoggingConfiguration>>();
-        options.add(new LogLevelOption());
-        options.add(new StacktraceOption());
-        options.add(new ConsoleOption());
-        options.add(new WarningsOption());
-        LoggingConfigurationBuildOptions.options = Collections.unmodifiableList(options);
-    }
+    private static List<BuildOption<LoggingConfiguration>> options = ImmutableList.<BuildOption<LoggingConfiguration>>of(
+        new LogLevelOption(),
+        new StacktraceOption(),
+        new ConsoleOption(),
+        new WarningsOption());
 
     public static List<BuildOption<LoggingConfiguration>> get() {
         return options;
@@ -61,15 +54,11 @@ public List<? extends BuildOption<? super LoggingConfiguration>> getAllOptions()
         return options;
     }
 
-    public Collection<String> getLogLevelOptions() {
-        return Arrays.asList(
-            LogLevelOption.DEBUG_SHORT_OPTION,
+    public Collection<String> getLongLogLevelOptions() {
+        return ImmutableList.of(
             LogLevelOption.DEBUG_LONG_OPTION,
-            LogLevelOption.WARN_SHORT_OPTION,
             LogLevelOption.WARN_LONG_OPTION,
-            LogLevelOption.INFO_SHORT_OPTION,
             LogLevelOption.INFO_LONG_OPTION,
-            LogLevelOption.QUIET_SHORT_OPTION,
             LogLevelOption.QUIET_LONG_OPTION);
     }
 
@@ -127,17 +116,17 @@ public void applyFromCommandLine(ParsedCommandLine options, LoggingConfiguration
             }
         }
 
-        private LogLevel parseLogLevel(String value) {
-            LogLevel logLevel = null;
+        public static LogLevel parseLogLevel(String value) {
             try {
-                logLevel = LogLevel.valueOf(value.toUpperCase(Locale.ENGLISH));
+                LogLevel logLevel = LogLevel.valueOf(value.toUpperCase(Locale.ENGLISH));
                 if (logLevel == LogLevel.ERROR) {
                     throw new IllegalArgumentException("Log level cannot be set to 'ERROR'.");
                 }
+                return logLevel;
             } catch (IllegalArgumentException e) {
                 Origin.forGradleProperty(GRADLE_PROPERTY).handleInvalidValue(value, "must be one of quiet, warn, lifecycle, info, or debug)");
             }
-            return logLevel;
+            return null;
         }
     }
 
diff --git a/subprojects/logging/src/main/java/org/gradle/internal/logging/progress/DefaultProgressLoggerFactory.java b/subprojects/logging/src/main/java/org/gradle/internal/logging/progress/DefaultProgressLoggerFactory.java
index 27ff2b3..5994f8a 100644
--- a/subprojects/logging/src/main/java/org/gradle/internal/logging/progress/DefaultProgressLoggerFactory.java
+++ b/subprojects/logging/src/main/java/org/gradle/internal/logging/progress/DefaultProgressLoggerFactory.java
@@ -53,9 +53,7 @@ public ProgressLogger newOperation(Class<?> loggerCategory) {
     public ProgressLogger newOperation(Class<?> loggerCategory, BuildOperationDescriptor buildOperationDescriptor) {
         String category = ProgressStartEvent.BUILD_OP_CATEGORY;
         BuildOperationMetadata metadata = buildOperationDescriptor.getMetadata();
-        BuildOperationCategory buildOperationCategory = metadata == BuildOperationMetadata.NONE
-            ? BuildOperationCategory.UNCATEGORIZED
-            : (BuildOperationCategory) metadata;
+        BuildOperationCategory buildOperationCategory = BuildOperationCategory.toCategory(metadata);
         if (buildOperationCategory == BuildOperationCategory.TASK) {
             // This is a legacy quirk.
             // Scans use this to determine that progress logging is indicating start/finish of tasks.
diff --git a/subprojects/logging/src/main/java/org/gradle/internal/logging/sink/OutputEventRenderer.java b/subprojects/logging/src/main/java/org/gradle/internal/logging/sink/OutputEventRenderer.java
index a6b770c..64ad045 100644
--- a/subprojects/logging/src/main/java/org/gradle/internal/logging/sink/OutputEventRenderer.java
+++ b/subprojects/logging/src/main/java/org/gradle/internal/logging/sink/OutputEventRenderer.java
@@ -66,7 +66,7 @@ public class OutputEventRenderer implements OutputEventListener, LoggingRouter {
     private final AtomicReference<LogLevel> logLevel = new AtomicReference<LogLevel>(LogLevel.LIFECYCLE);
     private final Clock clock;
     private final ListenerBroadcast<OutputEventListener> formatters = new ListenerBroadcast<OutputEventListener>(OutputEventListener.class);
-    private final OutputEventTransformer transformer = new OutputEventTransformer(formatters.getSource());
+    private final OutputEventTransformer transformer = new OutputEventTransformer(formatters.getSource(), lock);
 
     private ColorMap colourMap;
     private OutputStream originalStdOut;
@@ -427,9 +427,7 @@ public void onOutput(OutputEvent event) {
             }
             this.logLevel.set(newLogLevel);
         }
-        synchronized (lock) {
-            transformer.onOutput(event);
-        }
+        transformer.onOutput(event);
     }
 
     private boolean isProgressEvent(OutputEvent event) {
diff --git a/subprojects/logging/src/main/java/org/gradle/internal/logging/sink/OutputEventTransformer.java b/subprojects/logging/src/main/java/org/gradle/internal/logging/sink/OutputEventTransformer.java
index 42fce84..fadeb3c 100644
--- a/subprojects/logging/src/main/java/org/gradle/internal/logging/sink/OutputEventTransformer.java
+++ b/subprojects/logging/src/main/java/org/gradle/internal/logging/sink/OutputEventTransformer.java
@@ -26,24 +26,26 @@
 import org.gradle.internal.operations.OperationIdentifier;
 import org.gradle.util.internal.GUtil;
 
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Transforms the stream of output events to discard progress operations that are not interesting to the logging subsystem. This reduces the amount of work that downstream consumers have to do to process the stream. For example, these discarded events don't need to be written to the daemon client.
  */
 public class OutputEventTransformer implements OutputEventListener {
     // A map from progress operation id seen in event -> progress operation id that should be forwarded
-    private final Map<OperationIdentifier, OperationIdentifier> effectiveProgressOperation = new HashMap<OperationIdentifier, OperationIdentifier>();
+    private final Map<OperationIdentifier, OperationIdentifier> effectiveProgressOperation = new ConcurrentHashMap<OperationIdentifier, OperationIdentifier>();
     // A set of progress operations that have been forwarded
-    private final Set<OperationIdentifier> forwarded = new HashSet<OperationIdentifier>();
+    private final Set<OperationIdentifier> forwarded = Collections.newSetFromMap(new ConcurrentHashMap<OperationIdentifier, Boolean>());
 
     private final OutputEventListener listener;
+    private final Object lock;
 
-    public OutputEventTransformer(OutputEventListener listener) {
+    public OutputEventTransformer(OutputEventListener listener, Object lock) {
         this.listener = listener;
+        this.lock = lock;
     }
 
     @Override
@@ -59,7 +61,7 @@ public void onOutput(OutputEvent event) {
                         startEvent = startEvent.withParentProgressOperation(mappedId);
                     }
                 }
-                listener.onOutput(startEvent);
+                invokeListener(startEvent);
                 return;
             }
 
@@ -72,7 +74,7 @@ public void onOutput(OutputEvent event) {
                         startEvent = startEvent.withParentProgressOperation(mappedId);
                     }
                 }
-                listener.onOutput(startEvent);
+                invokeListener(startEvent);
             } else {
                 // Ignore this progress operation, and map any reference to it to its parent (or whatever its parent is mapped to)
                 OperationIdentifier mappedParent = effectiveProgressOperation.get(startEvent.getParentProgressOperationId());
@@ -85,12 +87,12 @@ public void onOutput(OutputEvent event) {
             ProgressCompleteEvent completeEvent = (ProgressCompleteEvent) event;
             OperationIdentifier mappedEvent = effectiveProgressOperation.remove(completeEvent.getProgressOperationId());
             if (mappedEvent == null && forwarded.remove(completeEvent.getProgressOperationId())) {
-                listener.onOutput(event);
+                invokeListener(event);
             }
         } else if (event instanceof ProgressEvent) {
             ProgressEvent progressEvent = (ProgressEvent) event;
             if (forwarded.contains(progressEvent.getProgressOperationId())) {
-                listener.onOutput(event);
+                invokeListener(event);
             }
         } else if (event instanceof RenderableOutputEvent) {
             RenderableOutputEvent outputEvent = (RenderableOutputEvent) event;
@@ -101,9 +103,15 @@ public void onOutput(OutputEvent event) {
                     outputEvent = outputEvent.withBuildOperationId(mappedId);
                 }
             }
-            listener.onOutput(outputEvent);
+            invokeListener(outputEvent);
         } else {
+            invokeListener(event);
+        }
+    }
+    private void invokeListener(OutputEvent event) {
+        synchronized (lock) {
             listener.onOutput(event);
         }
     }
+
 }
diff --git a/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecatedFeatureUsageTest.groovy b/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecatedFeatureUsageTest.groovy
index c33f972..9c2c9ff 100644
--- a/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecatedFeatureUsageTest.groovy
+++ b/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecatedFeatureUsageTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.internal.deprecation
 
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.util.GradleVersion
 import spock.lang.Specification
 
@@ -33,7 +34,7 @@
         "summary" | "removalDetails" | null     | null               | Documentation.NO_DOCUMENTATION         | "summary removalDetails"
         "summary" | "removalDetails" | "advice" | null               | Documentation.NO_DOCUMENTATION         | "summary removalDetails advice"
         "summary" | "removalDetails" | "advice" | "contextualAdvice" | Documentation.NO_DOCUMENTATION         | "summary removalDetails contextualAdvice advice"
-        "summary" | "removalDetails" | "advice" | "contextualAdvice" | Documentation.userManual("foo", "bar") | "summary removalDetails contextualAdvice advice See https://docs.gradle.org/${GradleVersion.current().version}/userguide/foo.html#bar for more details."
+        "summary" | "removalDetails" | "advice" | "contextualAdvice" | Documentation.userManual("userguide", "bar") | "summary removalDetails contextualAdvice advice ${new DocumentationRegistry().getDocumentationRecommendationFor("information", "userguide", "bar")}"
     }
 
     def "returns documentation url"() {
diff --git a/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecationLoggerTest.groovy b/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecationLoggerTest.groovy
index e0ca3e4..81be384 100644
--- a/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecationLoggerTest.groovy
+++ b/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecationLoggerTest.groovy
@@ -149,8 +149,7 @@
 
     def "reports suppressed deprecation messages with --warning-mode summary"() {
         given:
-        def documentation = new DocumentationRegistry()
-        def documentationReference = documentation.getDocumentationFor("command_line_interface", "sec:command_line_warnings")
+        def documentationReference = new DocumentationRegistry().getDocumentationRecommendationFor("on this", "command_line_interface", "sec:command_line_warnings")
         DeprecationLogger.init(Mock(UsageLocationReporter), WarningMode.Summary, Mock(BuildOperationProgressEventEmitter))
         DeprecationLogger.deprecate("nag").willBeRemovedInGradle9().undocumented().nagUser()
 
@@ -165,7 +164,7 @@
 
 You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
 
-See ${documentationReference}"""
+${documentationReference}"""
     }
 
 }
diff --git a/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecationMessagesTest.groovy b/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecationMessagesTest.groovy
index 24b75b1..492f808 100644
--- a/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecationMessagesTest.groovy
+++ b/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DeprecationMessagesTest.groovy
@@ -27,6 +27,8 @@
 import org.junit.Rule
 import spock.lang.Specification
 
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
+
 class DeprecationMessagesTest extends Specification {
 
     private static final String NEXT_GRADLE_VERSION = "9.0"
@@ -324,8 +326,8 @@
             .nagUser()
 
         then:
-        def expectedDocumentationUrl = DOCUMENTATION_REGISTRY.getDocumentationFor("viewing_debugging_dependencies", "sub:resolving-unsafe-configuration-resolution-errors")
-        expectMessage "Some behavior. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle ${NEXT_GRADLE_VERSION}. See ${expectedDocumentationUrl} for more details."
+        def expectedDocumentationUrl = DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("information","viewing_debugging_dependencies", "sub:resolving-unsafe-configuration-resolution-errors")
+        expectMessage "Some behavior. This behavior has been deprecated. This behavior is scheduled to be removed in Gradle ${NEXT_GRADLE_VERSION}. ${expectedDocumentationUrl}"
     }
 
     def "logs DSL property documentation reference"() {
@@ -337,7 +339,10 @@
 
         then:
         def dslReference = DOCUMENTATION_REGISTRY.getDslRefForProperty(AbstractArchiveTask, "bar")
-        expectMessage "The DeprecationLogger.archiveName property has been deprecated. This is scheduled to be removed in Gradle ${NEXT_GRADLE_VERSION}. Please use the archiveFileName property instead. See ${dslReference} for more details."
+        expectMessage "The DeprecationLogger.archiveName property has been deprecated. " +
+            "This is scheduled to be removed in Gradle ${NEXT_GRADLE_VERSION}. " +
+            "Please use the archiveFileName property instead. " +
+            String.format(RECOMMENDATION, "information", dslReference)
     }
 
     def "logs DSL documentation reference for deprecated property implicitly"() {
@@ -349,7 +354,10 @@
 
         then:
         def dslReference = DOCUMENTATION_REGISTRY.getDslRefForProperty(AbstractArchiveTask, "archiveName")
-        expectMessage "The AbstractArchiveTask.archiveName property has been deprecated. This is scheduled to be removed in Gradle ${NEXT_GRADLE_VERSION}. Please use the archiveFileName property instead. See ${dslReference} for more details."
+        expectMessage "The AbstractArchiveTask.archiveName property has been deprecated. " +
+            "This is scheduled to be removed in Gradle ${NEXT_GRADLE_VERSION}. " +
+            "Please use the archiveFileName property instead. " +
+            String.format(RECOMMENDATION, "information", dslReference)
     }
 
     private void expectMessage(String expectedMessage) {
diff --git a/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DocumentationTest.groovy b/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DocumentationTest.groovy
index 20c3a51..6ff3395 100644
--- a/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DocumentationTest.groovy
+++ b/subprojects/logging/src/test/groovy/org/gradle/internal/deprecation/DocumentationTest.groovy
@@ -37,13 +37,12 @@
         def documentationReference = Documentation.userManual(documentationId, documentationSection)
 
         expect:
-        documentationReference.documentationUrl() == expectedUrl
-        documentationReference.consultDocumentationMessage() == "See ${expectedUrl} for more details."
+        documentationReference.consultDocumentationMessage() == expectedUrl
 
         where:
         documentationId | documentationSection | expectedUrl
-        "foo"           | "bar"                | DOCUMENTATION_REGISTRY.getDocumentationFor("foo", "bar")
-        "foo"           | null                 | DOCUMENTATION_REGISTRY.getDocumentationFor("foo")
+        "foo"           | "bar"                | DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("information", "foo", "bar")
+        "foo"           | null                 | DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("information", "foo")
     }
 
     def "creates upgrade guide reference"() {
@@ -63,7 +62,7 @@
         then:
         def expectedUrl = DOCUMENTATION_REGISTRY.getDslRefForProperty(Documentation, "property")
         documentationReference.documentationUrl() == expectedUrl
-        documentationReference.consultDocumentationMessage() == "See ${expectedUrl} for more details."
+        documentationReference.consultDocumentationMessage() == "For more information, please refer to ${expectedUrl} in the Gradle documentation."
     }
 
 }
diff --git a/subprojects/logging/src/test/groovy/org/gradle/logging/ConsoleRendererTest.groovy b/subprojects/logging/src/test/groovy/org/gradle/logging/ConsoleRendererTest.groovy
index 8d49b89..4ebe831 100644
--- a/subprojects/logging/src/test/groovy/org/gradle/logging/ConsoleRendererTest.groovy
+++ b/subprojects/logging/src/test/groovy/org/gradle/logging/ConsoleRendererTest.groovy
@@ -17,20 +17,20 @@
 package org.gradle.logging
 
 import org.gradle.internal.logging.ConsoleRenderer
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Specification
 
 class ConsoleRendererTest extends Specification {
     ConsoleRenderer renderer = new ConsoleRenderer()
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "produces triple-slash file URLs"() {
         expect:
         renderer.asClickableFileUrl(new File("/foo/bar/baz")) == "file:///foo/bar/baz"
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "produces triple-slash file URLs on Windows"() {
         expect:
         renderer.asClickableFileUrl(new File("C:\\foo\\bar\\baz")) == "file:///C:/foo/bar/baz"
diff --git a/subprojects/maven/build.gradle.kts b/subprojects/maven/build.gradle.kts
index e49a0fd..73eb8c3 100644
--- a/subprojects/maven/build.gradle.kts
+++ b/subprojects/maven/build.gradle.kts
@@ -46,6 +46,7 @@
     testFixturesApi(project(":base-services")) {
         because("Test fixtures export the Action class")
     }
+    testFixturesImplementation(project(":logging"))
     testFixturesImplementation(project(":core-api"))
     testFixturesImplementation(project(":internal-integ-testing"))
     testFixturesImplementation(project(":dependency-management"))
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishArtifactCustomizationIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishArtifactCustomizationIntegTest.groovy
index ae6c778..4c83873 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishArtifactCustomizationIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishArtifactCustomizationIntegTest.groovy
@@ -557,7 +557,7 @@
 
             configurations.create("srcLicense") {
                 canBeResolved = false
-                canBeConsumed = true
+                assert canBeConsumed
             }
 
             def srcLicenseDir = tasks.register("srcLicenseDir", Sync) {
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishFeaturesJavaIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishFeaturesJavaIntegTest.groovy
index 3e9f1dc..46c4b3b 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishFeaturesJavaIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishFeaturesJavaIntegTest.groovy
@@ -30,7 +30,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, org.gradle.api.internal.artifacts.JavaEcosystemSupport.DEPRECATED_JAVA_RUNTIME_JARS))
                     }
@@ -104,7 +104,7 @@
                 optionalFeature1RuntimeElements {
                     extendsFrom optionalFeature1Implementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, "java-runtime-jars"))
                     }
@@ -116,7 +116,7 @@
                 optionalFeature2RuntimeElements {
                     extendsFrom optionalFeature2Implementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, "java-runtime-jars"))
                     }
@@ -190,7 +190,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, "java-runtime-jars"))
                     }
@@ -301,7 +301,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, "java-runtime-jars"))
                     }
@@ -386,7 +386,7 @@
                 optionalFeatureRuntimeElements {
                     extendsFrom optionalFeatureImplementation
                     canBeResolved = false
-                    canBeConsumed = true
+                    assert canBeConsumed
                     attributes {
                         attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME))
                     }
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpIntegTest.groovy
index 57e15f9..5894946 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishHttpIntegTest.groovy
@@ -18,7 +18,7 @@
 
 import org.gradle.api.credentials.Credentials
 import org.gradle.api.credentials.PasswordCredentials
-import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
 import org.gradle.internal.credentials.DefaultPasswordCredentials
 import org.gradle.test.fixtures.server.http.AuthScheme
@@ -325,7 +325,6 @@
     /**
      * @see org.gradle.configurationcache.ConfigurationCacheMavenPublishIntegrationTest
      */
-    @UnsupportedWithConfigurationCache(because="Unsafe/inline credentials not supported with CC")
     def "cannot publish to authenticated repository using credentials Provider with inferred identity if repo has incompatible name"() {
         given:
         buildFile << publicationBuild(version, group, mavenRemoteRepo.uri, "incompatible_repo_name")
@@ -335,11 +334,14 @@
         fails 'publish'
 
         then:
-        failure.assertHasDescription("Execution failed for task ':publishMavenPublicationToIncompatible_repo_nameRepository'.")
+        if (GradleContextualExecuter.isConfigCache()) {
+            failure.assertHasDescription("Configuration cache state could not be cached:")
+        } else {
+            failure.assertHasDescription("Execution failed for task ':publishMavenPublicationToIncompatible_repo_nameRepository'.")
+        }
         failure.assertHasCause("Identity may contain only letters and digits, received: incompatible_repo_name")
     }
 
-    @UnsupportedWithConfigurationCache(because="Unsafe/inline credentials not supported with CC")
     def "can publish to authenticated repository using inlined credentials"() {
         given:
         PasswordCredentials credentials = new DefaultPasswordCredentials('username', 'password')
@@ -357,9 +359,11 @@
 
         then:
         module.assertPublishedAsJavaModule()
+        if (GradleContextualExecuter.isConfigCache()) {
+            postBuildOutputContains("Configuration cache entry discarded")
+        }
     }
 
-    @UnsupportedWithConfigurationCache(because="Unsafe/inline credentials not supported with CC")
     def "can publish to authenticated repository with name not valid as identity as long as one uses inlined credentials "() {
         given:
         PasswordCredentials credentials = new DefaultPasswordCredentials('username', 'password')
@@ -380,6 +384,9 @@
 
         then:
         module.assertPublishedAsJavaModule()
+        if (GradleContextualExecuter.isConfigCache()) {
+            postBuildOutputContains("Configuration cache entry discarded")
+        }
     }
 
     def "fails at configuration time with helpful error message when username and password provider has no value"() {
diff --git a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishJavaIntegTest.groovy b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishJavaIntegTest.groovy
index 6f9b085..cff4343 100644
--- a/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishJavaIntegTest.groovy
+++ b/subprojects/maven/src/integTest/groovy/org/gradle/api/publish/maven/MavenPublishJavaIntegTest.groovy
@@ -149,7 +149,7 @@
             ${mavenCentralRepository()}
 
             def testConf = configurations.create('testConf') {
-                canBeResolved = true
+                assert canBeResolved
                 attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.VERIFICATION))
             }
 
@@ -167,9 +167,15 @@
             }
         """)
 
+
         expect:
         fails('publish')
-        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testConf' that contains a 'org.gradle.category' attribute with a value of 'verification'.  This attribute is reserved for test verification output and is not publishable.  See: ")
+        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testConf' that contains a 'org.gradle.category' attribute with a value of 'verification'.  " +
+            "This attribute is reserved for test verification output and is not publishable.  " + variantAttributesLink())
+    }
+
+    def variantAttributesLink() {
+        documentationRegistry.getDocumentationRecommendationFor("on this", "variant_attributes", "sec:verification_category")
     }
 
     def "can not publish variant with attribute specifying category = verification if defining new attribute with string"() {
@@ -179,7 +185,7 @@
             ${mavenCentralRepository()}
 
             def testConf = configurations.create('testConf') {
-                canBeResolved = true
+                assert canBeResolved
                 attributes.attribute(Attribute.of('org.gradle.category', String), 'verification')
             }
 
@@ -199,7 +205,8 @@
 
         expect:
         fails('publish')
-        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testConf' that contains a 'org.gradle.category' attribute with a value of 'verification'.  This attribute is reserved for test verification output and is not publishable.  See: ")
+        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testConf' that contains a 'org.gradle.category' attribute with a value of 'verification'.  " +
+            "This attribute is reserved for test verification output and is not publishable.  " + variantAttributesLink())
     }
 
     def "can not publish test results from java test suite"() {
@@ -244,7 +251,8 @@
 
         expect:
         fails('test', 'publish')
-        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testResultsElementsForTest' that contains a 'org.gradle.category' attribute with a value of 'verification'.  This attribute is reserved for test verification output and is not publishable.  See: ")
+        failure.assertHasCause("Cannot publish module metadata for component 'java' which would include a variant 'testResultsElementsForTest' that contains a 'org.gradle.category' attribute with a value of 'verification'.  " +
+            "This attribute is reserved for test verification output and is not publishable.  " + variantAttributesLink())
     }
 
     def "can publish variants with attribute specifying category if not verification"() {
@@ -254,7 +262,7 @@
             ${mavenCentralRepository()}
 
             def testConf = configurations.create('testConf') {
-                canBeResolved = true
+                assert canBeResolved
                 attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, 'not verification'))
             }
 
diff --git a/subprojects/maven/src/main/java/org/gradle/api/publish/maven/MavenPublication.java b/subprojects/maven/src/main/java/org/gradle/api/publish/maven/MavenPublication.java
index d35c6f2..6d94293 100644
--- a/subprojects/maven/src/main/java/org/gradle/api/publish/maven/MavenPublication.java
+++ b/subprojects/maven/src/main/java/org/gradle/api/publish/maven/MavenPublication.java
@@ -25,7 +25,7 @@
 /**
  * A {@code MavenPublication} is the representation/configuration of how Gradle should publish something in Maven format.
  *
- * You directly add a named Maven Publication the project's {@code publishing.publications} container by providing {@link MavenPublication} as the type.
+ * You directly add a named Maven publication the project's {@code publishing.publications} container by providing {@link MavenPublication} as the type.
  * <pre>
  * publishing {
  *   publications {
diff --git a/subprojects/maven/src/main/java/org/gradle/api/publish/maven/plugins/MavenPublishPlugin.java b/subprojects/maven/src/main/java/org/gradle/api/publish/maven/plugins/MavenPublishPlugin.java
index de2721b..e9baeb2 100644
--- a/subprojects/maven/src/main/java/org/gradle/api/publish/maven/plugins/MavenPublishPlugin.java
+++ b/subprojects/maven/src/main/java/org/gradle/api/publish/maven/plugins/MavenPublishPlugin.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.publish.maven.plugins;
 
+import org.apache.commons.lang.builder.EqualsBuilder;
 import org.gradle.api.NamedDomainObjectFactory;
 import org.gradle.api.NamedDomainObjectList;
 import org.gradle.api.NamedDomainObjectSet;
@@ -24,13 +25,18 @@
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
 import org.gradle.api.attributes.Usage;
+import org.gradle.api.credentials.Credentials;
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.internal.artifacts.Module;
 import org.gradle.api.internal.artifacts.configurations.DependencyMetaDataProvider;
+import org.gradle.api.internal.artifacts.repositories.DefaultMavenArtifactRepository;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.provider.MissingValueException;
 import org.gradle.api.internal.tasks.TaskDependencyFactory;
 import org.gradle.api.model.ObjectFactory;
+import org.gradle.api.provider.Provider;
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.publish.PublishingExtension;
 import org.gradle.api.publish.internal.versionmapping.DefaultVersionMappingStrategy;
@@ -126,7 +132,7 @@ private void realizePublishingTasksLater(final Project project, final Publishing
             createGenerateMetadataTask(tasks, publication, mavenPublications, buildDirectory);
             createGeneratePomTask(tasks, publication, buildDirectory, project);
             createLocalInstallTask(tasks, publishLocalLifecycleTask, publication);
-            createPublishTasksForEachMavenRepo(tasks, publishLifecycleTask, publication, repositories);
+            createPublishTasksForEachMavenRepo(project, tasks, publishLifecycleTask, publication, repositories);
         });
     }
 
@@ -134,7 +140,7 @@ private String publishAllToSingleRepoTaskName(MavenArtifactRepository repository
         return "publishAllPublicationsTo" + capitalize(repository.getName()) + "Repository";
     }
 
-    private void createPublishTasksForEachMavenRepo(final TaskContainer tasks, final TaskProvider<Task> publishLifecycleTask, final MavenPublicationInternal publication, final NamedDomainObjectList<MavenArtifactRepository> repositories) {
+    private void createPublishTasksForEachMavenRepo(final Project project, final TaskContainer tasks, final TaskProvider<Task> publishLifecycleTask, final MavenPublicationInternal publication, final NamedDomainObjectList<MavenArtifactRepository> repositories) {
         final String publicationName = publication.getName();
         repositories.all(repository -> {
             final String repositoryName = repository.getName();
@@ -144,12 +150,51 @@ private void createPublishTasksForEachMavenRepo(final TaskContainer tasks, final
                 publishTask.setRepository(repository);
                 publishTask.setGroup(PublishingPlugin.PUBLISH_TASK_GROUP);
                 publishTask.setDescription("Publishes Maven publication '" + publicationName + "' to Maven repository '" + repositoryName + "'.");
+                project.getGradle().getTaskGraph().whenReady(graph -> {
+                    if (graph.hasTask(publishTask)) {
+                        validateCredentialsSetup(project, publishTask);
+                    }
+                });
             });
+
             publishLifecycleTask.configure(task -> task.dependsOn(publishTaskName));
             tasks.named(publishAllToSingleRepoTaskName(repository), publish -> publish.dependsOn(publishTaskName));
         });
     }
 
+    private static void validateCredentialsSetup(Project project, PublishToMavenRepository publishToMavenRepository) {
+        DefaultMavenArtifactRepository repository = (DefaultMavenArtifactRepository) publishToMavenRepository.getRepository();
+        Credentials creds;
+        try {
+            creds = repository.getConfiguredCredentials().getOrNull();
+        } catch (Exception e) {
+            // In case of exception, we assume compatibility as this will fail later as well
+            creds = null;
+        }
+        if (creds != null && !isUsingCredentialsProvider((ProjectInternal) project, repository.getName(), creds)) {
+            publishToMavenRepository.notCompatibleWithConfigurationCache("Publishing to a repository without a credentials provider is not yet supported for the configuration cache");
+        }
+    }
+
+    private static boolean isUsingCredentialsProvider(ProjectInternal project, String identity, Credentials toCheck) {
+        ProviderFactory providerFactory = project.getServices().get(ProviderFactory.class);
+        Credentials referenceCredentials;
+        try {
+            Provider<? extends Credentials> credentialsProvider;
+            try {
+                credentialsProvider = providerFactory.credentials(toCheck.getClass(), identity);
+            } catch (IllegalArgumentException e) {
+                // some possibilities are invalid repository names and invalid credential types
+                // either way, this is not the place to validate that
+                return false;
+            }
+            referenceCredentials = credentialsProvider.get();
+        } catch (MissingValueException e) {
+            return false;
+        }
+        return EqualsBuilder.reflectionEquals(toCheck, referenceCredentials);
+    }
+
     private void createLocalInstallTask(TaskContainer tasks, final TaskProvider<Task> publishLocalLifecycleTask, final MavenPublicationInternal publication) {
         final String publicationName = publication.getName();
         final String installTaskName = "publish" + capitalize(publicationName) + "PublicationToMavenLocal";
diff --git a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Int2ObjectMapSerializer.java b/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Int2ObjectMapSerializer.java
deleted file mode 100644
index 95ae31d..0000000
--- a/subprojects/messaging/src/main/java/org/gradle/internal/serialize/Int2ObjectMapSerializer.java
+++ /dev/null
@@ -1,70 +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.serialize;
-
-import com.google.common.base.Objects;
-import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
-
-import java.util.Map;
-
-public class Int2ObjectMapSerializer<V> extends AbstractSerializer<Map<Integer, ? extends V>> {
-
-    private final Serializer<Integer> keySerializer;
-    private final Serializer<V> valueSerializer;
-
-    public Int2ObjectMapSerializer(Serializer<V> valueSerializer) {
-        this.keySerializer = new DefaultSerializer<Integer>();
-        this.valueSerializer = valueSerializer;
-    }
-
-    @Override
-    public Map<Integer, V> read(Decoder decoder) throws Exception {
-        int size = decoder.readInt();
-        Map<Integer, V> valueMap = new Int2ObjectOpenHashMap<V>(size);
-        for (int i = 0; i < size; i++) {
-            Integer key = keySerializer.read(decoder);
-            V value = valueSerializer.read(decoder);
-            valueMap.put(key, value);
-        }
-        return valueMap;
-    }
-
-    @Override
-    public void write(Encoder encoder, Map<Integer, ? extends V> value) throws Exception {
-        encoder.writeInt(value.size());
-        for (Map.Entry<Integer, ? extends V> entry : value.entrySet()) {
-            keySerializer.write(encoder, entry.getKey());
-            valueSerializer.write(encoder, entry.getValue());
-        }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!super.equals(obj)) {
-            return false;
-        }
-
-        Int2ObjectMapSerializer<?> rhs = (Int2ObjectMapSerializer<?>) obj;
-        return Objects.equal(valueSerializer, rhs.valueSerializer);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(super.hashCode(), keySerializer, valueSerializer);
-    }
-
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/remote/internal/inet/InetAddressesTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/remote/internal/inet/InetAddressesTest.groovy
index b5d89ad..5557b82 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/internal/remote/internal/inet/InetAddressesTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/remote/internal/inet/InetAddressesTest.groovy
@@ -16,8 +16,8 @@
 
 package org.gradle.internal.remote.internal.inet
 
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Specification
 
 class InetAddressesTest extends Specification{
@@ -28,7 +28,7 @@
         !addresses.loopback.empty
     }
 
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     def "always contains at least one remote address"() {
         expect:
         !addresses.remote.empty
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/remote/internal/inet/TcpConnectorTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/remote/internal/inet/TcpConnectorTest.groovy
index 627c6c2..d98264d 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/internal/remote/internal/inet/TcpConnectorTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/remote/internal/inet/TcpConnectorTest.groovy
@@ -26,8 +26,8 @@
 import org.gradle.internal.serialize.Serializer
 import org.gradle.internal.serialize.Serializers
 import org.gradle.test.fixtures.concurrent.ConcurrentSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.ports.ReleasingPortAllocator
 import org.junit.Rule
 import spock.lang.Issue
@@ -281,7 +281,7 @@
     }
 
     @Issue("GRADLE-2316")
-    @Requires(TestPrecondition.NOT_MAC_OS_X) // https://github.com/gradle/gradle-private/issues/2832
+    @Requires(UnitTestPreconditions.NotMacOs) // https://github.com/gradle/gradle-private/issues/2832
     def "detects self connect when outgoing connection binds to same port"() {
         given:
         def socketChannel = SocketChannel.open()
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/Int2ObjectMapSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/Int2ObjectMapSerializerTest.groovy
deleted file mode 100644
index 8531236..0000000
--- a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/Int2ObjectMapSerializerTest.groovy
+++ /dev/null
@@ -1,57 +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.serialize
-
-import it.unimi.dsi.fastutil.ints.Int2ObjectMap
-
-class Int2ObjectMapSerializerTest extends SerializerSpec {
-
-    def stringSerializer = new NullSafeStringSerializer()
-
-    def "serialize map"() {
-        when:
-        def serializer = new Int2ObjectMapSerializer(stringSerializer)
-
-        then:
-        serialize([1: "one", 2: "two", 3: "three"], serializer) == [1: "one", 2: "two", 3: "three"]
-    }
-
-    def "serialize null value"() {
-        when:
-        def serializer = new Int2ObjectMapSerializer(stringSerializer)
-
-        then:
-        serialize([1: "one", 2: null], serializer) == [1: "one", 2: null]
-    }
-
-    def "uses efficient serialization value"() {
-        when:
-        def serializer = new Int2ObjectMapSerializer(stringSerializer)
-
-        then:
-        usesEfficientSerialization([1: "one", 2: null], serializer)
-    }
-
-    def "deserialize bytes to Int2ObjectMap"() {
-        when:
-        def serializer = new Int2ObjectMapSerializer(stringSerializer)
-
-        then:
-        serialize([1: "one", 2: null], serializer) instanceof Int2ObjectMap
-    }
-
-}
diff --git a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/SetSerializerTest.groovy b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/SetSerializerTest.groovy
index 835f1fc..e5e4f36 100644
--- a/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/SetSerializerTest.groovy
+++ b/subprojects/messaging/src/test/groovy/org/gradle/internal/serialize/SetSerializerTest.groovy
@@ -51,4 +51,4 @@
         then:
         serialize(['1', '2'] as Set, serializer) instanceof HashSet
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/api/provider/ProcessOutputProviderIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/api/provider/ProcessOutputProviderIntegrationTest.groovy
index 0002b28..33c4045 100644
--- a/subprojects/model-core/src/integTest/groovy/org/gradle/api/provider/ProcessOutputProviderIntegrationTest.groovy
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/api/provider/ProcessOutputProviderIntegrationTest.groovy
@@ -89,10 +89,12 @@
                 ${cmdToExecConfig(*testScript.commandLine)}
             }
 
-            println("exit value = " + execProvider.result.get().exitValue)
-            println(execProvider.standardOutput.asText.get())
-
-            task empty() {}
+            task empty() {
+                doLast {
+                    println("exit value = " + execProvider.result.get().exitValue)
+                    println(execProvider.standardOutput.asText.get())
+                }
+            }
         """
 
         when:
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/api/provider/PropertyAssignmentIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/api/provider/PropertyAssignmentIntegrationTest.groovy
index 570dbc0..12c3813 100644
--- a/subprojects/model-core/src/integTest/groovy/org/gradle/api/provider/PropertyAssignmentIntegrationTest.groovy
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/api/provider/PropertyAssignmentIntegrationTest.groovy
@@ -66,7 +66,6 @@
     }
 
     def "test Kotlin eager object types assignment for #description"() {
-        file("gradle.properties") << "\nsystemProp.org.gradle.unsafe.kotlin.assignment=true\n"
         def inputDeclaration = "var input: $inputType? = null"
         kotlinBuildFile(inputDeclaration, inputValue, "=")
 
@@ -87,7 +86,6 @@
     }
 
     def "test Kotlin lazy object types assignment for #description"() {
-        file("gradle.properties") << "\nsystemProp.org.gradle.unsafe.kotlin.assignment=true\n"
         def inputDeclaration = "abstract val input: $inputType"
         kotlinBuildFile(inputDeclaration, inputValue, "=")
 
@@ -169,7 +167,6 @@
     }
 
     def "test Kotlin eager collection types assignment for #description"() {
-        file("gradle.properties") << "\nsystemProp.org.gradle.unsafe.kotlin.assignment=true\n"
         def initValue = inputType.contains("Map<") ? "mutableMapOf<String, MyObject>()" : "mutableListOf<MyObject>()"
         def inputDeclaration = "var input: $inputType = $initValue"
         kotlinBuildFile(inputDeclaration, inputValue, operation)
@@ -196,7 +193,6 @@
     }
 
     def "test Kotlin lazy collection types assignment for #description"() {
-        file("gradle.properties") << "\nsystemProp.org.gradle.unsafe.kotlin.assignment=true\n"
         def inputDeclaration = "abstract val input: $inputType"
         kotlinBuildFile(inputDeclaration, inputValue, operation)
 
@@ -266,7 +262,6 @@
     }
 
     def "test Kotlin eager FileCollection types assignment for #description"() {
-        file("gradle.properties") << "\nsystemProp.org.gradle.unsafe.kotlin.assignment=true\n"
         def inputDeclaration = "var input: $inputType = project.files()"
         kotlinBuildFile(inputDeclaration, inputValue, operation)
 
@@ -289,7 +284,6 @@
     }
 
     def "test Kotlin lazy FileCollection types assignment for #description"() {
-        file("gradle.properties") << "\nsystemProp.org.gradle.unsafe.kotlin.assignment=true\n"
         def inputDeclaration = "abstract val input: $inputType"
         kotlinBuildFile(inputDeclaration, inputValue, operation)
 
diff --git a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InterfaceBackedManagedTypeIntegrationTest.groovy b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InterfaceBackedManagedTypeIntegrationTest.groovy
index 5d9bafe..b9c39e9 100644
--- a/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InterfaceBackedManagedTypeIntegrationTest.groovy
+++ b/subprojects/model-core/src/integTest/groovy/org/gradle/model/managed/InterfaceBackedManagedTypeIntegrationTest.groovy
@@ -18,13 +18,13 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 // Caused by: java.lang.IncompatibleClassChangeError: Method Person.getName()Ljava/lang/String; must be InterfaceMethodref constant
 // Fail since build 125
-@Requires(TestPrecondition.JDK8_OR_EARLIER)
+@Requires(UnitTestPreconditions.Jdk8OrEarlier)
 @Issue('https://github.com/gradle/gradle/issues/721')
 @UnsupportedWithConfigurationCache(because = "software model")
 class InterfaceBackedManagedTypeIntegrationTest extends AbstractIntegrationSpec {
diff --git a/subprojects/model-core/src/main/java/org/gradle/api/internal/HasConvention.java b/subprojects/model-core/src/main/java/org/gradle/api/internal/HasConvention.java
index 3e7a100..3819085 100644
--- a/subprojects/model-core/src/main/java/org/gradle/api/internal/HasConvention.java
+++ b/subprojects/model-core/src/main/java/org/gradle/api/internal/HasConvention.java
@@ -16,18 +16,15 @@
 
 package org.gradle.api.internal;
 
-import org.gradle.api.plugins.Convention;
-
 /**
  * Demarcates objects that expose a convention.
  *
  * Convention objects aren't going to be around forever, so this is a temporary interface.
  *
- * @deprecated Use extensions instead. This interface is scheduled for removal in Gradle 8.
+ * @deprecated Use extensions instead. This interface is scheduled for removal in Gradle 9.
  * @see org.gradle.api.plugins.ExtensionAware
  */
 @Deprecated
 public interface HasConvention {
-    @Deprecated
-    Convention getConvention();
+    org.gradle.api.plugins.Convention getConvention();
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/api/internal/plugins/DslObject.java b/subprojects/model-core/src/main/java/org/gradle/api/internal/plugins/DslObject.java
index 7d37121..ea7155b 100644
--- a/subprojects/model-core/src/main/java/org/gradle/api/internal/plugins/DslObject.java
+++ b/subprojects/model-core/src/main/java/org/gradle/api/internal/plugins/DslObject.java
@@ -20,11 +20,11 @@
 import org.gradle.api.internal.DynamicObjectAware;
 import org.gradle.api.internal.GeneratedSubclasses;
 import org.gradle.api.internal.IConventionAware;
-import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtensionAware;
 import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.reflect.HasPublicType;
 import org.gradle.api.reflect.TypeOf;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.metaobject.DynamicObject;
 
 import static org.gradle.internal.Cast.uncheckedCast;
@@ -44,7 +44,7 @@ public class DslObject implements DynamicObjectAware, ExtensionAware, IConventio
     private DynamicObject dynamicObject;
     private ExtensionContainer extensionContainer;
     private ConventionMapping conventionMapping;
-    private Convention convention;
+    private org.gradle.api.plugins.Convention convention;
 
     private final Object object;
 
@@ -62,9 +62,16 @@ public DynamicObject getAsDynamicObject() {
 
     @Override
     @Deprecated
-    public Convention getConvention() {
+    public org.gradle.api.plugins.Convention getConvention() {
+// TODO nag once KGP doesn't register conventions anymore
+//        DeprecationLogger.deprecateType(org.gradle.api.internal.HasConvention.class)
+//            .willBeRemovedInGradle9()
+//            .withUpgradeGuideSection(8, "deprecated_access_to_conventions")
+//            .nagUser();
         if (convention == null) {
-            this.convention = toType(object, org.gradle.api.internal.HasConvention.class).getConvention();
+            this.convention = DeprecationLogger.whileDisabled(() ->
+                toType(object, org.gradle.api.internal.HasConvention.class).getConvention()
+            );
         }
         return convention;
     }
@@ -80,7 +87,9 @@ public ExtensionContainer getExtensions() {
     @Override
     public ConventionMapping getConventionMapping() {
         if (conventionMapping == null) {
-            this.conventionMapping = toType(object, IConventionAware.class).getConventionMapping();
+            this.conventionMapping = DeprecationLogger.whileDisabled(() ->
+                toType(object, IConventionAware.class).getConventionMapping()
+            );
         }
         return conventionMapping;
     }
@@ -105,8 +114,8 @@ private static <T> T toType(Object delegate, Class<T> type) {
             return type.cast(delegate);
         } else {
             throw new IllegalStateException(
-                    String.format("Cannot create DslObject for '%s' (class: %s) as it does not implement '%s' (it is not a DSL object)",
-                            delegate, delegate.getClass().getSimpleName(), type.getName())
+                String.format("Cannot create DslObject for '%s' (class: %s) as it does not implement '%s' (it is not a DSL object)",
+                    delegate, delegate.getClass().getSimpleName(), type.getName())
             );
         }
     }
diff --git a/subprojects/model-core/src/main/java/org/gradle/api/internal/provider/sources/process/DelegatingJavaExecSpec.java b/subprojects/model-core/src/main/java/org/gradle/api/internal/provider/sources/process/DelegatingJavaExecSpec.java
index a64cc2e..0e43d93 100644
--- a/subprojects/model-core/src/main/java/org/gradle/api/internal/provider/sources/process/DelegatingJavaExecSpec.java
+++ b/subprojects/model-core/src/main/java/org/gradle/api/internal/provider/sources/process/DelegatingJavaExecSpec.java
@@ -19,6 +19,7 @@
 import org.gradle.api.Action;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.jvm.ModularitySpec;
+import org.gradle.api.provider.ListProperty;
 import org.gradle.api.provider.Property;
 import org.gradle.process.CommandLineArgumentProvider;
 import org.gradle.process.JavaDebugOptions;
@@ -247,6 +248,11 @@ default void setAllJvmArgs(Iterable<?> arguments) {
     }
 
     @Override
+    default ListProperty<String> getJvmArguments() {
+        return getDelegate().getJvmArguments();
+    }
+
+    @Override
     default JavaForkOptions copyTo(JavaForkOptions options) {
         getDelegate().copyTo(options);
         return this;
diff --git a/subprojects/model-core/src/main/java/org/gradle/api/internal/tasks/AbstractTaskDependency.java b/subprojects/model-core/src/main/java/org/gradle/api/internal/tasks/AbstractTaskDependency.java
index 0c61573..2487fb4 100644
--- a/subprojects/model-core/src/main/java/org/gradle/api/internal/tasks/AbstractTaskDependency.java
+++ b/subprojects/model-core/src/main/java/org/gradle/api/internal/tasks/AbstractTaskDependency.java
@@ -19,7 +19,7 @@
 import org.gradle.api.Action;
 import org.gradle.api.NonNullApi;
 import org.gradle.api.Task;
-import org.gradle.api.internal.artifacts.transform.TransformationDependency;
+import org.gradle.api.internal.artifacts.transform.TransformNodeDependency;
 
 import javax.annotation.Nullable;
 import java.util.Set;
@@ -42,7 +42,7 @@ public AbstractTaskDependency(@Nullable TaskDependencyUsageTracker dependencyUsa
         @Override
         public boolean resolve(Task task, Object node, Action<? super Task> resolveAction) {
             // Ignore artifact transforms
-            return node instanceof TransformationDependency || node instanceof WorkNodeAction;
+            return node instanceof TransformNodeDependency || node instanceof WorkNodeAction;
         }
     };
 
diff --git a/subprojects/model-core/src/main/java/org/gradle/api/internal/tasks/TaskDependencyResolveContext.java b/subprojects/model-core/src/main/java/org/gradle/api/internal/tasks/TaskDependencyResolveContext.java
index 0d1d435..7668af8 100644
--- a/subprojects/model-core/src/main/java/org/gradle/api/internal/tasks/TaskDependencyResolveContext.java
+++ b/subprojects/model-core/src/main/java/org/gradle/api/internal/tasks/TaskDependencyResolveContext.java
@@ -18,7 +18,7 @@
 
 import org.gradle.api.Action;
 import org.gradle.api.Task;
-import org.gradle.api.internal.artifacts.transform.TransformationDependency;
+import org.gradle.api.internal.artifacts.transform.TransformNodeDependency;
 
 import javax.annotation.Nullable;
 
@@ -41,7 +41,7 @@ default void execute(Task task) {
      *
      * <li>{@link org.gradle.api.Buildable}</li>
      *
-     * <li>{@link TransformationDependency}</li>
+     * <li>{@link TransformNodeDependency}</li>
      *
      * <li>{@link WorkNodeAction}</li>
      *
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/ConventionAwareHelper.java b/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/ConventionAwareHelper.java
index 3b4524a..bc92394 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/ConventionAwareHelper.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/ConventionAwareHelper.java
@@ -21,8 +21,8 @@
 import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.ConventionMapping;
 import org.gradle.api.internal.IConventionAware;
-import org.gradle.api.plugins.Convention;
 import org.gradle.internal.Cast;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.deprecation.DocumentedFailure;
 import org.gradle.internal.reflect.JavaPropertyReflectionUtil;
 
@@ -38,7 +38,7 @@
 @SuppressWarnings("deprecation")
 public class ConventionAwareHelper implements ConventionMapping, org.gradle.api.internal.HasConvention {
     //prefix internal fields with _ so that they don't get into the way of propertyMissing()
-    private final Convention _convention;
+    private final org.gradle.api.plugins.Convention _convention;
     private final IConventionAware _source;
     // These are properties that could have convention mapping applied to them
     private final Set<String> _propertyNames;
@@ -47,7 +47,7 @@ public class ConventionAwareHelper implements ConventionMapping, org.gradle.api.
 
     private final Map<String, MappedPropertyImpl> _mappings = new HashMap<String, MappedPropertyImpl>();
 
-    public ConventionAwareHelper(IConventionAware source, Convention convention) {
+    public ConventionAwareHelper(IConventionAware source, org.gradle.api.plugins.Convention convention) {
         this._source = source;
         this._convention = convention;
         this._propertyNames = JavaPropertyReflectionUtil.propertyNames(source);
@@ -75,7 +75,7 @@ private MappedProperty map(String propertyName, MappedPropertyImpl mapping) {
     public MappedProperty map(String propertyName, final Closure<?> value) {
         return map(propertyName, new MappedPropertyImpl() {
             @Override
-            public Object doGetValue(Convention convention, IConventionAware conventionAwareObject) {
+            public Object doGetValue(org.gradle.api.plugins.Convention convention, IConventionAware conventionAwareObject) {
                 switch (value.getMaximumNumberOfParameters()) {
                     case 0:
                         return value.call();
@@ -92,7 +92,7 @@ public Object doGetValue(Convention convention, IConventionAware conventionAware
     public MappedProperty map(String propertyName, final Callable<?> value) {
         return map(propertyName, new MappedPropertyImpl() {
             @Override
-            public Object doGetValue(Convention convention, IConventionAware conventionAwareObject) {
+            public Object doGetValue(org.gradle.api.plugins.Convention convention, IConventionAware conventionAwareObject) {
                 return uncheckedCall(value);
             }
         });
@@ -132,8 +132,13 @@ public <T> T getConventionValue(T actualValue, String propertyName, boolean isEx
         return returnValue;
     }
 
+    @Deprecated
     @Override
-    public Convention getConvention() {
+    public org.gradle.api.plugins.Convention getConvention() {
+        DeprecationLogger.deprecateType(org.gradle.api.internal.HasConvention.class)
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "deprecated_access_to_conventions")
+            .nagUser();
         return _convention;
     }
 
@@ -142,7 +147,7 @@ private static abstract class MappedPropertyImpl implements MappedProperty {
         private boolean cache;
         private Object cachedValue;
 
-        public Object getValue(Convention convention, IConventionAware conventionAwareObject) {
+        public Object getValue(org.gradle.api.plugins.Convention convention, IConventionAware conventionAwareObject) {
             if (!cache) {
                 return doGetValue(convention, conventionAwareObject);
             }
@@ -159,6 +164,6 @@ public void cache() {
             cachedValue = null;
         }
 
-        abstract Object doGetValue(Convention convention, IConventionAware conventionAwareObject);
+        abstract Object doGetValue(org.gradle.api.plugins.Convention convention, IConventionAware conventionAwareObject);
     }
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/DefaultConvention.java b/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/DefaultConvention.java
index 3f6c1e6..065894c 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/DefaultConvention.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/DefaultConvention.java
@@ -21,29 +21,36 @@
 import org.gradle.api.Action;
 import org.gradle.api.internal.plugins.DslObject;
 import org.gradle.api.internal.plugins.ExtensionContainerInternal;
-import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtensionsSchema;
 import org.gradle.api.plugins.ExtraPropertiesExtension;
 import org.gradle.api.reflect.TypeOf;
 import org.gradle.internal.Cast;
 import org.gradle.internal.Describables;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.instantiation.InstanceGenerator;
 import org.gradle.internal.metaobject.AbstractDynamicObject;
 import org.gradle.internal.metaobject.BeanDynamicObject;
 import org.gradle.internal.metaobject.DynamicInvokeResult;
 import org.gradle.internal.metaobject.DynamicObject;
 import org.gradle.util.internal.ConfigureUtil;
+import com.google.common.collect.ForwardingMap;
 
+import javax.annotation.Nullable;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 
 import static java.lang.String.format;
 import static org.gradle.api.reflect.TypeOf.typeOf;
 
-public class DefaultConvention implements Convention, ExtensionContainerInternal {
+@Deprecated
+public class DefaultConvention implements org.gradle.api.plugins.Convention, ExtensionContainerInternal {
     private static final TypeOf<ExtraPropertiesExtension> EXTRA_PROPERTIES_EXTENSION_TYPE = typeOf(ExtraPropertiesExtension.class);
     private final DefaultConvention.ExtensionsDynamicObject extensionsDynamicObject = new ExtensionsDynamicObject();
     private final ExtensionsStorage extensionsStorage = new ExtensionsStorage();
@@ -58,19 +65,25 @@ public DefaultConvention(InstanceGenerator instanceGenerator) {
         add(EXTRA_PROPERTIES_EXTENSION_TYPE, ExtraPropertiesExtension.EXTENSION_NAME, extraProperties);
     }
 
+    @Deprecated
     @Override
     public Map<String, Object> getPlugins() {
         if (plugins == null) {
             plugins = Maps.newLinkedHashMap();
         }
-        return plugins;
+        return new ConventionPluginsMap(plugins);
     }
 
     @Override
     public DynamicObject getExtensionsAsDynamicObject() {
+        // This implementation of Convention doesn't log a deprecation warning
+        // because it mixes both extensions and conventions.
+        // Instead, the returned object logs a deprecation warning when
+        // a convention is actually accessed.
         return extensionsDynamicObject;
     }
 
+    @Deprecated
     @Override
     public <T> T getPlugin(Class<T> type) {
         T value = findPlugin(type);
@@ -81,8 +94,10 @@ public <T> T getPlugin(Class<T> type) {
         return value;
     }
 
+    @Deprecated
     @Override
     public <T> T findPlugin(Class<T> type) throws IllegalStateException {
+        logConventionDeprecation();
         if (plugins == null) {
             return null;
         }
@@ -233,6 +248,7 @@ public boolean hasProperty(String name) {
             }
             for (Object object : plugins.values()) {
                 if (asDynamicObject(object).hasProperty(name)) {
+                    logConventionDeprecation();
                     return true;
                 }
             }
@@ -266,6 +282,7 @@ public DynamicInvokeResult tryGetProperty(String name) {
                 DynamicObject dynamicObject = asDynamicObject(object).withNotImplementsMissing();
                 DynamicInvokeResult result = dynamicObject.tryGetProperty(name);
                 if (result.isFound()) {
+                    logConventionDeprecation();
                     return result;
                 }
             }
@@ -286,6 +303,7 @@ public DynamicInvokeResult trySetProperty(String name, Object value) {
                 BeanDynamicObject dynamicObject = asDynamicObject(object).withNotImplementsMissing();
                 DynamicInvokeResult result = dynamicObject.trySetProperty(name, value);
                 if (result.isFound()) {
+                    logConventionDeprecation();
                     return result;
                 }
             }
@@ -308,6 +326,7 @@ public DynamicInvokeResult tryInvokeMethod(String name, Object... args) {
                 BeanDynamicObject dynamicObject = asDynamicObject(object).withNotImplementsMissing();
                 DynamicInvokeResult result = dynamicObject.tryInvokeMethod(name, args);
                 if (result.isFound()) {
+                    logConventionDeprecation();
                     return result;
                 }
             }
@@ -329,6 +348,7 @@ public boolean hasMethod(String name, Object... args) {
             for (Object object : plugins.values()) {
                 BeanDynamicObject dynamicObject = asDynamicObject(object);
                 if (dynamicObject.hasMethod(name, args)) {
+                    logConventionDeprecation();
                     return true;
                 }
             }
@@ -370,4 +390,106 @@ private Object configureExtension(String name, Object[] args) {
         }
         return extensionsStorage.configureExtension(name, action);
     }
+
+    private static void logConventionDeprecation() {
+        DeprecationLogger.deprecateType(org.gradle.api.plugins.Convention.class)
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "deprecated_access_to_conventions")
+            .nagUser();
+    }
+
+    private static class ConventionPluginsMap extends ForwardingMap<String, Object> {
+
+        private final Map<String, Object> delegate;
+
+        private ConventionPluginsMap(Map<String, Object> delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        protected Map<String, Object> delegate() {
+            return delegate;
+        }
+
+        @Nullable
+        @Override
+        public Object get(@Nullable Object key) {
+            logConventionDeprecation();
+            return super.get(key);
+        }
+
+        @Override
+        public Object getOrDefault(Object key, Object defaultValue) {
+            logConventionDeprecation();
+            return super.getOrDefault(key, defaultValue);
+        }
+
+        @Nullable
+        @Override
+        public Object remove(@Nullable Object key) {
+            logConventionDeprecation();
+            return super.remove(key);
+        }
+
+        @Override
+        public boolean remove(Object key, Object value) {
+            logConventionDeprecation();
+            return super.remove(key, value);
+        }
+
+        @Override
+        public void forEach(BiConsumer<? super String, ? super Object> action) {
+            logConventionDeprecation();
+            super.forEach(action);
+        }
+
+        @Nullable
+        @Override
+        public Object replace(String key, Object value) {
+            logConventionDeprecation();
+            return super.replace(key, value);
+        }
+
+        @Override
+        public void replaceAll(BiFunction<? super String, ? super Object, ?> function) {
+            logConventionDeprecation();
+            super.replaceAll(function);
+        }
+
+        @Override
+        public void clear() {
+            logConventionDeprecation();
+            super.clear();
+        }
+
+        @Override
+        public boolean containsKey(@Nullable Object key) {
+            logConventionDeprecation();
+            return super.containsKey(key);
+        }
+
+        @Override
+        public boolean containsValue(@Nullable Object value) {
+            logConventionDeprecation();
+            return super.containsValue(value);
+        }
+
+        @Override
+        public Set<String> keySet() {
+            logConventionDeprecation();
+            return super.keySet();
+        }
+
+        @Override
+        public Set<Entry<String, Object>> entrySet() {
+            logConventionDeprecation();
+            return super.entrySet();
+        }
+
+        @Override
+        public Collection<Object> values() {
+            logConventionDeprecation();
+            return super.values();
+        }
+    }
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/ExtensibleDynamicObject.java b/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/ExtensibleDynamicObject.java
index 3850b35..7493f65 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/ExtensibleDynamicObject.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/extensibility/ExtensibleDynamicObject.java
@@ -17,7 +17,6 @@
 
 import groovy.lang.MissingMethodException;
 import groovy.lang.MissingPropertyException;
-import org.gradle.api.plugins.Convention;
 import org.gradle.api.plugins.ExtraPropertiesExtension;
 import org.gradle.internal.instantiation.InstanceGenerator;
 import org.gradle.internal.metaobject.AbstractDynamicObject;
@@ -47,7 +46,7 @@ public enum Location {
 
     private final AbstractDynamicObject dynamicDelegate;
     private DynamicObject parent;
-    private final Convention convention;
+    private final org.gradle.api.plugins.Convention convention;
     private DynamicObject beforeConvention;
     private DynamicObject afterConvention;
     private final DynamicObject extraPropertiesDynamicObject;
@@ -60,7 +59,7 @@ public ExtensibleDynamicObject(Object delegate, AbstractDynamicObject dynamicDel
         this(delegate, dynamicDelegate, new DefaultConvention(instanceGenerator));
     }
 
-    public ExtensibleDynamicObject(Object delegate, AbstractDynamicObject dynamicDelegate, Convention convention) {
+    public ExtensibleDynamicObject(Object delegate, AbstractDynamicObject dynamicDelegate, org.gradle.api.plugins.Convention convention) {
         this.dynamicDelegate = dynamicDelegate;
         this.convention = convention;
         this.extraPropertiesDynamicObject = new ExtraPropertiesDynamicObjectAdapter(delegate.getClass(), convention.getExtraProperties());
@@ -140,7 +139,12 @@ public void setParent(DynamicObject parent) {
 
     @Override
     @Deprecated
-    public Convention getConvention() {
+    public org.gradle.api.plugins.Convention getConvention() {
+// TODO nag once KGP doesn't register conventions anymore
+//        DeprecationLogger.deprecateType(org.gradle.api.internal.HasConvention.class)
+//            .willBeRemovedInGradle9()
+//            .withUpgradeGuideSection(8, "deprecated_access_to_conventions")
+//            .nagUser();
         return convention;
     }
 
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AbstractClassGenerator.java b/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AbstractClassGenerator.java
index b44d139..3c472ae 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AbstractClassGenerator.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AbstractClassGenerator.java
@@ -32,6 +32,7 @@
 import org.gradle.api.Action;
 import org.gradle.api.Describable;
 import org.gradle.api.DomainObjectSet;
+import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
 import org.gradle.api.NamedDomainObjectContainer;
 import org.gradle.api.NonExtensible;
 import org.gradle.api.file.ConfigurableFileCollection;
@@ -102,6 +103,12 @@
  * </ul>
  */
 abstract class AbstractClassGenerator implements ClassGenerator {
+    /**
+     * Types that are allowed to be instantiated directly by Gradle when exposed as a getter on a type.
+     *
+     * @implNote Keep in sync with subprojects/docs/src/docs/userguide/extending-gradle/custom_gradle_types.adoc
+     * @see ManagedObjectFactory#newInstance
+     */
     private static final ImmutableSet<Class<?>> MANAGED_PROPERTY_TYPES = ImmutableSet.of(
         ConfigurableFileCollection.class,
         ConfigurableFileTree.class,
@@ -112,6 +119,7 @@ abstract class AbstractClassGenerator implements ClassGenerator {
         DirectoryProperty.class,
         Property.class,
         NamedDomainObjectContainer.class,
+        ExtensiblePolymorphicDomainObjectContainer.class,
         DomainObjectSet.class
     );
     private static final Object[] NO_PARAMS = new Object[0];
@@ -993,10 +1001,15 @@ void applyTo(ClassInspectionVisitor visitor) {
 
         @Override
         void applyTo(ClassGenerationVisitor visitor) {
-            if (extensible && !hasExtensionAwareImplementation) {
+            boolean addExtensionProperty = extensible && !hasExtensionAwareImplementation;
+            boolean mixInConventionAware = conventionAware && !IConventionAware.class.isAssignableFrom(type);
+            if (addExtensionProperty || mixInConventionAware) {
+                visitor.addNoDeprecationConventionPrivateGetter();
+            }
+            if (addExtensionProperty) {
                 visitor.addExtensionsProperty();
             }
-            if (conventionAware && !IConventionAware.class.isAssignableFrom(type)) {
+            if (mixInConventionAware) {
                 visitor.mixInConventionAware();
             }
             for (PropertyMetadata property : conventionProperties) {
@@ -1441,6 +1454,8 @@ default void addConstructor(Constructor<?> constructor) {
 
         void mixInDynamicAware();
 
+        void addNoDeprecationConventionPrivateGetter();
+
         void mixInConventionAware();
 
         void mixInGroovyObject();
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AsmBackedClassGenerator.java b/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AsmBackedClassGenerator.java
index 78dd895..49d2372 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AsmBackedClassGenerator.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/AsmBackedClassGenerator.java
@@ -24,21 +24,23 @@
 import groovy.lang.MetaClassRegistry;
 import org.gradle.api.Action;
 import org.gradle.api.Describable;
+import org.gradle.api.NonNullApi;
+import org.gradle.api.Project;
 import org.gradle.api.Task;
-import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.ConventionMapping;
 import org.gradle.api.internal.DynamicObjectAware;
 import org.gradle.api.internal.GeneratedSubclass;
 import org.gradle.api.internal.IConventionAware;
 import org.gradle.api.internal.provider.DefaultProperty;
 import org.gradle.api.internal.provider.support.LazyGroovySupport;
-import org.gradle.api.plugins.Convention;
+import org.gradle.api.invocation.Gradle;
 import org.gradle.api.plugins.ExtensionAware;
 import org.gradle.api.plugins.ExtensionContainer;
 import org.gradle.api.services.ServiceReference;
 import org.gradle.cache.internal.CrossBuildInMemoryCache;
 import org.gradle.cache.internal.CrossBuildInMemoryCacheFactory;
 import org.gradle.internal.DisplayName;
+import org.gradle.internal.Factory;
 import org.gradle.internal.UncheckedException;
 import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.extensibility.ConventionAwareHelper;
@@ -106,6 +108,7 @@
 import static org.objectweb.asm.Opcodes.ACC_TRANSIENT;
 import static org.objectweb.asm.Opcodes.H_INVOKESPECIAL;
 import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
 import static org.objectweb.asm.Opcodes.V1_8;
 import static org.objectweb.asm.Type.BOOLEAN_TYPE;
 import static org.objectweb.asm.Type.INT_TYPE;
@@ -383,6 +386,7 @@ public ClassGenerationVisitor builder() {
         }
     }
 
+    @NonNullApi
     private static class ClassBuilderImpl extends ClassVisitorScope implements ClassGenerationVisitor {
         public static final int PV_FINAL_STATIC = ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC;
         private static final Set<? extends Class<?>> PRIMITIVE_TYPES = ImmutableSet.of(Byte.TYPE, Boolean.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE);
@@ -414,7 +418,8 @@ private static class ClassBuilderImpl extends ClassVisitorScope implements Class
         private final static Type DYNAMIC_OBJECT_TYPE = getType(DynamicObject.class);
         private final static Type CONVENTION_MAPPING_TYPE = getType(ConventionMapping.class);
         private final static Type GROOVY_OBJECT_TYPE = getType(GroovyObject.class);
-        private final static Type CONVENTION_TYPE = getType(Convention.class);
+        @SuppressWarnings("deprecation")
+        private final static Type CONVENTION_TYPE = getType(org.gradle.api.plugins.Convention.class);
         private final static Type ASM_BACKED_CLASS_GENERATOR_TYPE = getType(AsmBackedClassGenerator.class);
         private final static Type ABSTRACT_DYNAMIC_OBJECT_TYPE = getType(AbstractDynamicObject.class);
         private final static Type EXTENSIBLE_DYNAMIC_OBJECT_HELPER_TYPE = getType(MixInExtensibleDynamicObject.class);
@@ -427,6 +432,17 @@ private static class ClassBuilderImpl extends ClassVisitorScope implements Class
         private static final Type MANAGED_OBJECT_FACTORY_TYPE = getType(ManagedObjectFactory.class);
         private static final Type DEFAULT_PROPERTY_TYPE = getType(DefaultProperty.class);
         private static final Type BUILD_SERVICE_PROVIDER_TYPE = getType("Lorg/gradle/api/services/internal/BuildServiceProvider;");
+        private static final Type INSTRUMENTED_EXECUTION_ACCESS_TYPE = getType("Lorg/gradle/internal/classpath/InstrumentedExecutionAccess;");
+
+        // This set is unlikely to change often, so instead of introducing an additional level of indirection,
+        // we are storing it here despite its relationship to Configuration Cache logic.
+        // The full prohibited hierarchy is stored because there is no efficient way to check the class hierarchy via `org.objectweb.asm.Type`.
+        private static final Set<Type> DISALLOWED_AT_EXECUTION_INJECTED_SERVICES_TYPES = ImmutableSet.of(
+            getType(Project.class),
+            getType("Lorg/gradle/api/internal/project/ProjectInternal;"),
+            getType(Gradle.class),
+            getType("Lorg/gradle/api/internal/GradleInternal;")
+        );
         private static final Type JAVA_LANG_REFLECT_TYPE = getType(java.lang.reflect.Type.class);
         private static final Type OBJECT_TYPE = getType(Object.class);
         private static final Type CLASS_TYPE = getType(Class.class);
@@ -438,13 +454,13 @@ private static class ClassBuilderImpl extends ClassVisitorScope implements Class
         private static final Type OBJECT_ARRAY_TYPE = getType(Object[].class);
         private static final Type ACTION_TYPE = getType(Action.class);
         private static final Type LAZY_GROOVY_SUPPORT_TYPE = getType(LazyGroovySupport.class);
-        private static final Type FILE_COLLECTION_TYPE = getType(FileCollection.class);
         private static final Type MANAGED_TYPE = getType(Managed.class);
         private static final Type EXTENSION_CONTAINER_TYPE = getType(ExtensionContainer.class);
         private static final Type DESCRIBABLE_TYPE = getType(Describable.class);
         private static final Type DISPLAY_NAME_TYPE = getType(DisplayName.class);
         private static final Type INJECT_TYPE = getType(Inject.class);
         private static final Type RUNNABLE_TYPE = getType(Runnable.class);
+        private static final Type FACTORY_TYPE = getType(Factory.class);
         private static final Type LAMBDA_METAFACTORY_TYPE = getType(LambdaMetafactory.class);
         private static final Type METHOD_HANDLES_TYPE = getType(MethodHandles.class);
         private static final Type METHOD_HANDLES_LOOKUP_TYPE = getType(MethodHandles.Lookup.class);
@@ -453,14 +469,12 @@ private static class ClassBuilderImpl extends ClassVisitorScope implements Class
         private static final String RETURN_STRING = getMethodDescriptor(STRING_TYPE);
         private static final String RETURN_DESCRIBABLE = getMethodDescriptor(DESCRIBABLE_TYPE);
         private static final String RETURN_VOID_FROM_OBJECT = getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE);
-        private static final String RETURN_VOID_FROM_OBJECT_ARRAY = getMethodDescriptor(Type.VOID_TYPE, OBJECT_ARRAY_TYPE);
         private static final String RETURN_VOID_FROM_OBJECT_CLASS_DYNAMIC_OBJECT_SERVICE_LOOKUP = getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE, CLASS_TYPE, DYNAMIC_OBJECT_TYPE, SERVICE_LOOKUP_TYPE);
         private static final String RETURN_OBJECT_FROM_STRING_OBJECT_BOOLEAN = getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, STRING_TYPE, BOOLEAN_TYPE);
         private static final String RETURN_CLASS = getMethodDescriptor(CLASS_TYPE);
         private static final String RETURN_BOOLEAN = getMethodDescriptor(BOOLEAN_TYPE);
         private static final String RETURN_VOID = getMethodDescriptor(Type.VOID_TYPE);
         private static final String RETURN_VOID_FROM_CONVENTION_AWARE_CONVENTION = getMethodDescriptor(Type.VOID_TYPE, CONVENTION_AWARE_TYPE, CONVENTION_TYPE);
-        private static final String RETURN_VOID_FROM_FILE_COLLECTION = getMethodDescriptor(Type.VOID_TYPE, FILE_COLLECTION_TYPE);
         private static final String RETURN_CONVENTION = getMethodDescriptor(CONVENTION_TYPE);
         private static final String RETURN_CONVENTION_MAPPING = getMethodDescriptor(CONVENTION_MAPPING_TYPE);
         private static final String RETURN_OBJECT = getMethodDescriptor(OBJECT_TYPE);
@@ -488,9 +502,12 @@ private static class ClassBuilderImpl extends ClassVisitorScope implements Class
         private static final String RETURN_OBJECT_FROM_MODEL_OBJECT_STRING_CLASS_CLASS_CLASS = getMethodDescriptor(OBJECT_TYPE, MODEL_OBJECT_TYPE, STRING_TYPE, CLASS_TYPE, CLASS_TYPE, CLASS_TYPE);
         private static final String RETURN_VOID_FROM_STRING = getMethodDescriptor(VOID_TYPE, STRING_TYPE);
         private static final String LAMBDA_METAFACTORY_METHOD = getMethodDescriptor(getType(CallSite.class), METHOD_HANDLES_LOOKUP_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, METHOD_TYPE_TYPE, getType(MethodHandle.class), METHOD_TYPE_TYPE);
-        private static final String DEPRECATION_LOGGER_WHILE_DISABLED_METHOD = getMethodDescriptor(Type.VOID_TYPE, RUNNABLE_TYPE);
+        private static final String DEPRECATION_LOGGER_WHILE_DISABLED_RUNNABLE_METHOD = getMethodDescriptor(Type.VOID_TYPE, RUNNABLE_TYPE);
+        private static final String DEPRECATION_LOGGER_WHILE_DISABLED_FACTORY_METHOD = getMethodDescriptor(OBJECT_TYPE, FACTORY_TYPE);
         private static final Handle LAMBDA_BOOTSTRAP_HANDLE = new Handle(H_INVOKESTATIC, LAMBDA_METAFACTORY_TYPE.getInternalName(), "metafactory", LAMBDA_METAFACTORY_METHOD, false);
         private static final Type RETURN_VOID_METHOD_TYPE = Type.getMethodType(RETURN_VOID);
+        private static final Type RETURN_OBJECT_METHOD_TYPE = Type.getMethodType(RETURN_OBJECT);
+        private static final Type RETURN_CONVENTION_METHOD_TYPE = Type.getMethodType(RETURN_CONVENTION);
 
         private static final String[] EMPTY_STRINGS = new String[0];
         private static final Type[] EMPTY_TYPES = new Type[0];
@@ -672,7 +689,7 @@ public void addConstructor(Constructor<?> constructor, boolean addNameParameter)
         }
 
         @Nonnull
-        private List<Type> paramTypesOf(Constructor<?> constructor, boolean addNameParameter) {
+        private static List<Type> paramTypesOf(Constructor<?> constructor, boolean addNameParameter) {
             Class<?>[] parameterTypes = constructor.getParameterTypes();
             List<Type> paramTypes = new ArrayList<>(parameterTypes.length + (addNameParameter ? 1 : 0));
             for (Class<?> paramType : parameterTypes) {
@@ -681,7 +698,7 @@ private List<Type> paramTypesOf(Constructor<?> constructor, boolean addNameParam
             return paramTypes;
         }
 
-        private void visitDeclaredAnnotationsOf(Constructor<?> constructor, MethodVisitor methodVisitor) {
+        private static void visitDeclaredAnnotationsOf(Constructor<?> constructor, MethodVisitor methodVisitor) {
             for (Annotation annotation : constructor.getDeclaredAnnotations()) {
                 Class<? extends Annotation> annotationType = annotation.annotationType();
                 if (annotationType.getAnnotation(Inherited.class) != null) {
@@ -710,7 +727,7 @@ private void generateInitMethod() {
                     new Handle(H_INVOKESPECIAL, generatedType.getInternalName(), INIT_WORK_METHOD, RETURN_VOID, false),
                     RETURN_VOID_METHOD_TYPE
                 ));
-                _INVOKESTATIC(DEPRECATION_LOGGER_TYPE, "whileDisabled", DEPRECATION_LOGGER_WHILE_DISABLED_METHOD);
+                _INVOKESTATIC(DEPRECATION_LOGGER_TYPE, "whileDisabled", DEPRECATION_LOGGER_WHILE_DISABLED_RUNNABLE_METHOD);
                 _RETURN();
             }});
 
@@ -774,12 +791,30 @@ private void generateInitMethod() {
         }
 
         @Override
-        public void addExtensionsProperty() {
-            // GENERATE public ExtensionContainer getExtensions() { return getConvention(); }
-            addGetter("getExtensions", EXTENSION_CONTAINER_TYPE, RETURN_EXTENSION_CONTAINER, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
-                // GENERATE getConvention()
+        public void addNoDeprecationConventionPrivateGetter() {
+            // GENERATE private Convention getConventionWhileDisabledDeprecationLogger() {
+            //     return DeprecationLogger.whileDisabled { getConvention() }
+            // }
+            privateSyntheticMethod("getConventionWhileDisabledDeprecationLogger", RETURN_CONVENTION, methodVisitor -> new LocalMethodVisitorScope(methodVisitor) {{
                 _ALOAD(0);
-                _INVOKEVIRTUAL(generatedType, "getConvention", RETURN_CONVENTION);
+                _INVOKEDYNAMIC("create", getMethodDescriptor(FACTORY_TYPE, generatedType), LAMBDA_BOOTSTRAP_HANDLE, Arrays.asList(
+                    RETURN_OBJECT_METHOD_TYPE,
+                    new Handle(H_INVOKEVIRTUAL, generatedType.getInternalName(), "getConvention", RETURN_CONVENTION, false),
+                    RETURN_CONVENTION_METHOD_TYPE
+                ));
+                _INVOKESTATIC(DEPRECATION_LOGGER_TYPE, "whileDisabled", DEPRECATION_LOGGER_WHILE_DISABLED_FACTORY_METHOD);
+                _CHECKCAST(CONVENTION_TYPE);
+                _ARETURN();
+            }});
+        }
+
+        @Override
+        public void addExtensionsProperty() {
+            // GENERATE public ExtensionContainer getExtensions() { return getConventionWhileDisabledDeprecationLogger(); }
+            addGetter("getExtensions", EXTENSION_CONTAINER_TYPE, RETURN_EXTENSION_CONTAINER, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
+                // GENERATE getConventionWhileDisabledDeprecationLogger()
+                _ALOAD(0);
+                _INVOKEVIRTUAL(generatedType, "getConventionWhileDisabledDeprecationLogger", RETURN_CONVENTION);
             }});
         }
 
@@ -812,7 +847,7 @@ public void mixInDynamicAware() {
             //      }
             //      return dynamicObjectHelper;
             // }
-            addLazyGetter("getAsDynamicObject", DYNAMIC_OBJECT_TYPE, RETURN_DYNAMIC_OBJECT, null, DYNAMIC_OBJECT_HELPER_FIELD, ABSTRACT_DYNAMIC_OBJECT_TYPE, methodVisitor -> new LocalMethodVisitorScope(methodVisitor) {{
+            addLazyGetter("getAsDynamicObject", DYNAMIC_OBJECT_TYPE, RETURN_DYNAMIC_OBJECT, DYNAMIC_OBJECT_HELPER_FIELD, ABSTRACT_DYNAMIC_OBJECT_TYPE, methodVisitor -> new LocalMethodVisitorScope(methodVisitor) {{
                 if (extensible) {
                     // GENERATE new MixInExtensibleDynamicObject(this, getClass().getSuperClass(), super.getAsDynamicObject(), this.services())
                     _NEW(EXTENSIBLE_DYNAMIC_OBJECT_HELPER_TYPE);
@@ -857,26 +892,27 @@ public void mixInConventionAware() {
 
             // GENERATE public ConventionMapping getConventionMapping() {
             //     if (mapping == null) {
-            //         mapping = new ConventionAwareHelper(this, getConvention());
+            //         mapping = new ConventionAwareHelper(this, getConventionWhileDisabledDeprecationLogger());
+            //         ineligibleProperties.forEach { mapping.ineligible(it.name) }
             //     }
             //     return mapping;
             // }
-            addLazyGetter("getConventionMapping", CONVENTION_MAPPING_TYPE, RETURN_CONVENTION_MAPPING, null, MAPPING_FIELD, CONVENTION_MAPPING_TYPE, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
-                // GENERATE new ConventionAwareHelper(this, getConvention())
+            addLazyGetter("getConventionMapping", CONVENTION_MAPPING_TYPE, RETURN_CONVENTION_MAPPING, MAPPING_FIELD, CONVENTION_MAPPING_TYPE, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
+                // GENERATE new ConventionAwareHelper(this, getConventionWhileDisabledDeprecationLogger())
                 _NEW(CONVENTION_AWARE_HELPER_TYPE);
                 _DUP();
                 _ALOAD(0);
 
                 // GENERATE getConvention()
                 _ALOAD(0);
-                _INVOKEVIRTUAL(generatedType, "getConvention", RETURN_CONVENTION);
+                _INVOKEVIRTUAL(generatedType, "getConventionWhileDisabledDeprecationLogger", RETURN_CONVENTION);
                 // END
 
                 _INVOKESPECIAL(CONVENTION_AWARE_HELPER_TYPE, "<init>", RETURN_VOID_FROM_CONVENTION_AWARE_CONVENTION);
                 // END
 
                 for (PropertyMetadata property : ineligibleProperties) {
-                    // GENERATE convention.ineligible(__property.getName()__)
+                    // GENERATE mapping.ineligible(__property.getName()__)
                     _DUP();
                     _LDC(property.getName());
                     _INVOKEINTERFACE(CONVENTION_MAPPING_TYPE, "ineligible", RETURN_VOID_FROM_STRING);
@@ -900,7 +936,7 @@ public void mixInGroovyObject() {
             //     }
             //     return metaClass;
             // }
-            addLazyGetter("getMetaClass", META_CLASS_TYPE, RETURN_META_CLASS, null, META_CLASS_FIELD, META_CLASS_TYPE, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
+            addLazyGetter("getMetaClass", META_CLASS_TYPE, RETURN_META_CLASS, META_CLASS_FIELD, META_CLASS_TYPE, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
                 // GroovySystem.getMetaClassRegistry()
                 _INVOKESTATIC(GROOVY_SYSTEM_TYPE, "getMetaClassRegistry", RETURN_META_CLASS_REGISTRY);
 
@@ -925,7 +961,7 @@ private void addSetter(String methodName, String methodDescriptor, BytecodeFragm
             addSetter(methodName, methodDescriptor, null, body);
         }
 
-        private void addSetter(String methodName, String methodDescriptor, String signature, BytecodeFragment body) {
+        private void addSetter(String methodName, String methodDescriptor, @Nullable String signature, BytecodeFragment body) {
             publicMethod(methodName, methodDescriptor, signature, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
                 emit(body);
                 _RETURN();
@@ -957,11 +993,23 @@ private void addLazyGetter(
             String methodName,
             Type returnType,
             String methodDescriptor,
-            @Nullable String signature,
             final String fieldName,
             final Type fieldType,
             final BytecodeFragment initializer
         ) {
+            addLazyGetter(methodName, returnType, methodDescriptor, null, fieldName, fieldType, initializer, BytecodeFragment.NO_OP);
+        }
+
+        private void addLazyGetter(
+            String methodName,
+            Type returnType,
+            String methodDescriptor,
+            @Nullable String signature,
+            final String fieldName,
+            final Type fieldType,
+            final BytecodeFragment initializer,
+            final BytecodeFragment epilogue
+        ) {
             addGetter(methodName, returnType, methodDescriptor, signature, methodVisitor -> new MethodVisitorScope(methodVisitor) {{
                 // var = this.<field>
                 _ALOAD(0);
@@ -978,6 +1026,7 @@ private void addLazyGetter(
                 _PUTFIELD(generatedType, fieldName, fieldType);
                 // return var
                 visitLabel(returnValue);
+                emit(epilogue);
                 _ALOAD(1);
             }});
         }
@@ -1088,7 +1137,7 @@ public void applyServiceInjectionToGetter(PropertyMetadata property, @Nullable f
             String propFieldName = propFieldName(property);
             String signature = getterSignature(getter.getGenericReturnType());
 
-            addLazyGetter(getterName, returnType, methodDescriptor, signature, propFieldName, serviceType, methodVisitor -> new LocalMethodVisitorScope(methodVisitor) {{
+            BytecodeFragment getterInitializer = methodVisitor -> new LocalMethodVisitorScope(methodVisitor) {{
 
                 putServiceRegistryOnStack();
 
@@ -1112,7 +1161,25 @@ public void applyServiceInjectionToGetter(PropertyMetadata property, @Nullable f
 
                 // (<type>)<service>
                 _CHECKCAST(serviceType);
-            }});
+            }};
+
+            BytecodeFragment getterEpilogue = getInjectedServiceGetterEpilogue(serviceType, getterName);
+
+            addLazyGetter(getterName, returnType, methodDescriptor, signature, propFieldName, serviceType, getterInitializer, getterEpilogue);
+        }
+
+        private BytecodeFragment getInjectedServiceGetterEpilogue(Type serviceType, String getterName) {
+            if (DISALLOWED_AT_EXECUTION_INJECTED_SERVICES_TYPES.contains(serviceType)) {
+                return methodVisitor -> new LocalMethodVisitorScope(methodVisitor) {{
+                    // InstrumentedExecutionAccess.disallowedAtExecutionInjectedServiceAccessed(<service-type>,<getter-name>,<this-type-name>)
+                    _LDC(serviceType);
+                    _LDC(getterName);
+                    _LDC(type.getName());
+                    _INVOKESTATIC(INSTRUMENTED_EXECUTION_ACCESS_TYPE, "disallowedAtExecutionInjectedServiceAccessed", getMethodDescriptor(VOID_TYPE, CLASS_TYPE, STRING_TYPE, STRING_TYPE));
+                }};
+            } else {
+                return BytecodeFragment.NO_OP;
+            }
         }
 
         @Override
@@ -1153,7 +1220,7 @@ public void applyReadOnlyManagedStateToGetter(PropertyMetadata property, Method
             Type returnType = getType(getter.getReturnType());
             String descriptor = getMethodDescriptor(returnType);
             String fieldName = propFieldName(property);
-            addLazyGetter(getter.getName(), returnType, descriptor, null, fieldName, propType, methodVisitor -> new LocalMethodVisitorScope(methodVisitor) {{
+            addLazyGetter(getter.getName(), returnType, descriptor, fieldName, propType, methodVisitor -> new LocalMethodVisitorScope(methodVisitor) {{
 
                 // GENERATE factory = getFactory()
                 _ALOAD(0);
@@ -1570,7 +1637,6 @@ public void addActionMethod(Method method) {
 
             Type returnType = getType(method.getReturnType());
 
-            @SuppressWarnings("NullableProblems")
             Type[] originalParameterTypes = collectArray(method.getParameterTypes(), Type.class, Type::getType);
             int numParams = originalParameterTypes.length;
             Type[] closurisedParameterTypes = new Type[numParams];
@@ -1718,7 +1784,7 @@ private void visitArrayElements(AnnotationVisitor arrayVisitor, Class<?> arrayEl
             }
         }
 
-        private Object getAnnotationParameterValue(Annotation annotation, Method method) {
+        private static Object getAnnotationParameterValue(Annotation annotation, Method method) {
             try {
                 return method.invoke(annotation);
             } catch (IllegalAccessException | InvocationTargetException e) {
@@ -1726,7 +1792,7 @@ private Object getAnnotationParameterValue(Annotation annotation, Method method)
             }
         }
 
-        private void attachFactoryIdToImplType(Class<?> implClass, int id) {
+        private static void attachFactoryIdToImplType(Class<?> implClass, int id) {
             try {
                 Field factoryField = implClass.getDeclaredField(FACTORY_ID_FIELD);
                 factoryField.setAccessible(true);
@@ -1883,6 +1949,10 @@ public void mixInDynamicAware() {
         }
 
         @Override
+        public void addNoDeprecationConventionPrivateGetter() {
+        }
+
+        @Override
         public void mixInConventionAware() {
         }
 
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/ManagedObjectFactory.java b/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/ManagedObjectFactory.java
index a8112a3..9d7e1cd 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/ManagedObjectFactory.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/instantiation/generator/ManagedObjectFactory.java
@@ -19,6 +19,7 @@
 import org.apache.commons.lang.StringUtils;
 import org.gradle.api.Describable;
 import org.gradle.api.DomainObjectSet;
+import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
 import org.gradle.api.NamedDomainObjectContainer;
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.ConfigurableFileTree;
@@ -92,11 +93,14 @@ public Object newInstance(ModelObject owner, String propertyName, Class<?> type,
         if (type.isAssignableFrom(SetProperty.class)) {
             return attachOwner(getObjectFactory().setProperty(paramType), owner, propertyName);
         }
+        if (type.isAssignableFrom(DomainObjectSet.class)) {
+            return attachOwner(getObjectFactory().domainObjectSet(paramType), owner, propertyName);
+        }
         if (type.isAssignableFrom(NamedDomainObjectContainer.class)) {
             return attachOwner(getObjectFactory().domainObjectContainer(paramType), owner, propertyName);
         }
-        if (type.isAssignableFrom(DomainObjectSet.class)) {
-            return attachOwner(getObjectFactory().domainObjectSet(paramType), owner, propertyName);
+        if (type.isAssignableFrom(ExtensiblePolymorphicDomainObjectContainer.class)) {
+            return attachOwner(getObjectFactory().polymorphicDomainObjectContainer(paramType), owner, propertyName);
         }
         throw new IllegalArgumentException("Don't know how to create an instance of type " + type.getName());
     }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/properties/PropertyVisitor.java b/subprojects/model-core/src/main/java/org/gradle/internal/properties/PropertyVisitor.java
index 6416c0f..1b04e7d 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/properties/PropertyVisitor.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/properties/PropertyVisitor.java
@@ -16,6 +16,7 @@
 
 package org.gradle.internal.properties;
 
+import org.gradle.api.services.BuildService;
 import org.gradle.internal.fingerprint.DirectorySensitivity;
 import org.gradle.internal.fingerprint.FileNormalizer;
 import org.gradle.internal.fingerprint.LineEndingSensitivity;
@@ -39,5 +40,5 @@ default void visitLocalStateProperty(Object value) {}
     /**
      * Visits a service reference. Service references may or may not be declared with a name.
      */
-    default void visitServiceReference(String propertyName, boolean optional, PropertyValue value, @Nullable String serviceName) {}
+    default void visitServiceReference(String propertyName, boolean optional, PropertyValue value, @Nullable String serviceName, Class<? extends BuildService<?>> buildServiceType) {}
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/AbstractTypeMetadataWalker.java b/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/AbstractTypeMetadataWalker.java
index 686b313..26b697d 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/AbstractTypeMetadataWalker.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/AbstractTypeMetadataWalker.java
@@ -19,10 +19,10 @@
 import com.google.common.reflect.TypeToken;
 import org.gradle.api.Named;
 import org.gradle.api.provider.Provider;
+import org.gradle.internal.reflect.JavaReflectionUtil;
 
 import javax.annotation.Nullable;
 import java.lang.annotation.Annotation;
-import java.lang.reflect.ParameterizedType;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.Map;
@@ -115,11 +115,6 @@ private static String getQualifiedName(@Nullable String parentPropertyName, Stri
             : parentPropertyName + "." + childPropertyName;
     }
 
-    private static <T> TypeToken<?> extractNestedType(TypeToken<T> beanType, Class<? super T> parameterizedSuperClass, int typeParameterIndex) {
-        ParameterizedType type = (ParameterizedType) beanType.getSupertype(parameterizedSuperClass).getType();
-        return TypeToken.of(type.getActualTypeArguments()[typeParameterIndex]);
-    }
-
     static class InstanceTypeMetadataWalker extends AbstractTypeMetadataWalker<Object, InstanceMetadataVisitor> implements InstanceMetadataWalker {
 
         public InstanceTypeMetadataWalker(TypeMetadataStore typeMetadataStore, Class<? extends Annotation> nestedAnnotation) {
@@ -148,13 +143,13 @@ protected void walkNestedProvider(Object node, String qualifiedName, PropertyMet
             );
         }
 
-        @SuppressWarnings("unchecked")
         @Override
         protected void walkNestedMap(Object node, String qualifiedName, BiConsumer<String, Object> handler) {
-            ((Map<String, Object>) node).forEach((key, value) -> {
+            ((Map<?, ?>) node).forEach((key, value) -> {
                 checkNotNull(key, "Null keys in nested map '%s' are not allowed.", qualifiedName);
-                checkNotNullNestedCollectionValue(qualifiedName, key, value);
-                handler.accept(key, value);
+                String stringKey = key.toString();
+                checkNotNullNestedCollectionValue(qualifiedName, stringKey, value);
+                handler.accept(stringKey, value);
             });
         }
 
@@ -210,7 +205,7 @@ protected void walkLeaf(Object node, @Nullable String parentQualifiedName, Insta
         @SuppressWarnings("unchecked")
         private static TypeToken<?> unpackType(TypeToken<?> type) {
             while (Provider.class.isAssignableFrom(type.getRawType())) {
-                type = extractNestedType((TypeToken<Provider<?>>) type, Provider.class, 0);
+                type = JavaReflectionUtil.extractNestedType((TypeToken<Provider<?>>) type, Provider.class, 0);
             }
             return type;
         }
@@ -249,7 +244,7 @@ protected void onNestedNodeCycle(@Nullable String firstOccurrenceQualifiedName,
         @SuppressWarnings("unchecked")
         @Override
         protected void walkNestedProvider(TypeToken<?> node, String qualifiedName, PropertyMetadata propertyMetadata, StaticMetadataVisitor visitor, boolean isElementOfCollection, Consumer<TypeToken<?>> handler) {
-            handler.accept(extractNestedType((TypeToken<Provider<?>>) node, Provider.class, 0));
+            handler.accept(JavaReflectionUtil.extractNestedType((TypeToken<Provider<?>>) node, Provider.class, 0));
         }
 
         @SuppressWarnings("unchecked")
@@ -257,13 +252,13 @@ protected void walkNestedProvider(TypeToken<?> node, String qualifiedName, Prope
         protected void walkNestedMap(TypeToken<?> node, String qualifiedName, BiConsumer<String, TypeToken<?>> handler) {
             handler.accept(
                 "<key>",
-                extractNestedType((TypeToken<Map<?, ?>>) node, Map.class, 1));
+                JavaReflectionUtil.extractNestedType((TypeToken<Map<?, ?>>) node, Map.class, 1));
         }
 
         @SuppressWarnings("unchecked")
         @Override
         protected void walkNestedIterable(TypeToken<?> node, String qualifiedName, BiConsumer<String, TypeToken<?>> handler) {
-            TypeToken<?> nestedType = extractNestedType((TypeToken<? extends Iterable<?>>) node, Iterable.class, 0);
+            TypeToken<?> nestedType = JavaReflectionUtil.extractNestedType((TypeToken<? extends Iterable<?>>) node, Iterable.class, 0);
             handler.accept(determinePropertyName(nestedType), nestedType);
         }
 
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/NestedBeanAnnotationHandler.java b/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/NestedBeanAnnotationHandler.java
index 2dbf29c..e038a84 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/NestedBeanAnnotationHandler.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/NestedBeanAnnotationHandler.java
@@ -17,12 +17,20 @@
 package org.gradle.internal.properties.annotations;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.TypeToken;
 import org.gradle.api.tasks.Nested;
 import org.gradle.internal.properties.PropertyValue;
 import org.gradle.internal.properties.PropertyVisitor;
+import org.gradle.internal.reflect.JavaReflectionUtil;
+import org.gradle.internal.reflect.problems.ValidationProblemId;
+import org.gradle.internal.reflect.validation.TypeValidationContext;
 
 import java.lang.annotation.Annotation;
 import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.gradle.internal.reflect.validation.Severity.WARNING;
 
 public class NestedBeanAnnotationHandler extends AbstractPropertyAnnotationHandler {
     public NestedBeanAnnotationHandler(Collection<Class<? extends Annotation>> allowedModifiers) {
@@ -35,6 +43,35 @@ public boolean isPropertyRelevant() {
     }
 
     @Override
+    @SuppressWarnings("unchecked")
+    public void validatePropertyMetadata(PropertyMetadata propertyMetadata, TypeValidationContext validationContext) {
+        if (Map.class.isAssignableFrom(propertyMetadata.getDeclaredType().getRawType())) {
+            Class<?> keyType = JavaReflectionUtil.extractNestedType((TypeToken<Map<?, ?>>) propertyMetadata.getDeclaredType(), Map.class, 0).getRawType();
+            validateKeyType(propertyMetadata, validationContext, keyType);
+        }
+    }
+
+    @Override
     public void visitPropertyValue(String propertyName, PropertyValue value, PropertyMetadata propertyMetadata, PropertyVisitor visitor) {
     }
+
+    private static final ImmutableSet<Class<?>> SUPPORTED_KEY_TYPES = ImmutableSet.of(Enum.class, Integer.class, String.class);
+
+    private static String getSupportedKeyTypes() {
+        return SUPPORTED_KEY_TYPES.stream().map(cls -> "'" + cls.getSimpleName() + "'").collect(Collectors.joining(", "));
+    }
+
+    private static void validateKeyType(PropertyMetadata propertyMetadata, TypeValidationContext validationContext, Class<?> keyType) {
+        if (!SUPPORTED_KEY_TYPES.contains(keyType)) {
+            validationContext.visitPropertyProblem(problem ->
+                problem.withId(ValidationProblemId.NESTED_MAP_UNSUPPORTED_KEY_TYPE)
+                    .reportAs(WARNING)
+                    .forProperty(propertyMetadata.getPropertyName())
+                    .withDescription(() -> "where key of nested map is of type '" + keyType.getName() + "'")
+                    .happensBecause("Key of nested map must be one of the following types: " + getSupportedKeyTypes())
+                    .addPossibleSolution("Change type of key to one of the following types: " + getSupportedKeyTypes())
+                    .documentedAt("validation_problems", "unsupported_key_type_of_nested_map")
+            );
+        }
+    }
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/TypeMetadataWalker.java b/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/TypeMetadataWalker.java
index 166ac06..727a0aa 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/TypeMetadataWalker.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/properties/annotations/TypeMetadataWalker.java
@@ -22,7 +22,7 @@
 import javax.annotation.Nullable;
 import java.lang.annotation.Annotation;
 
-/***
+/**
  * A generalized type metadata walker for traversing annotated types and instances using their {@link TypeMetadata}.
  *
  * During the walk we first visit the root (the type or instance passed to {@link #walk(Object, TypeMetadataVisitor)},
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/DefaultTypeValidationContext.java b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/DefaultTypeValidationContext.java
index 449edfe..b1eb5b9 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/DefaultTypeValidationContext.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/DefaultTypeValidationContext.java
@@ -17,13 +17,17 @@
 package org.gradle.internal.reflect;
 
 import com.google.common.collect.ImmutableMap;
+import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.internal.DocumentationRegistry;
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
 import org.gradle.internal.reflect.validation.Severity;
 import org.gradle.internal.reflect.validation.TypeValidationProblem;
 import org.gradle.internal.reflect.validation.TypeValidationProblemRenderer;
+import org.gradle.model.internal.type.ModelType;
 
 import javax.annotation.Nullable;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 public class DefaultTypeValidationContext extends ProblemRecordingTypeValidationContext {
     private final boolean reportCacheabilityProblems;
@@ -54,4 +58,20 @@ protected void recordProblem(TypeValidationProblem problem) {
     public ImmutableMap<String, Severity> getProblems() {
         return problems.build();
     }
+
+    public static void throwOnProblemsOf(Class<?> implementation, ImmutableMap<String, Severity> validationMessages) {
+        if (!validationMessages.isEmpty()) {
+            String formatString = validationMessages.size() == 1
+                ? "A problem was found with the configuration of %s."
+                : "Some problems were found with the configuration of %s.";
+            throw new DefaultMultiCauseException(
+                String.format(formatString, ModelType.of(implementation).getDisplayName()),
+                validationMessages.keySet().stream()
+                    .sorted()
+                    .map(InvalidUserDataException::new)
+                    .collect(Collectors.toList())
+            );
+        }
+    }
+
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/problems/ValidationProblemId.java b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/problems/ValidationProblemId.java
index ebbbe30..61e092b 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/problems/ValidationProblemId.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/problems/ValidationProblemId.java
@@ -48,4 +48,5 @@ public enum ValidationProblemId {
     NOT_CACHEABLE_WITHOUT_REASON,
     UNSUPPORTED_VALUE_TYPE,
     SERVICE_REFERENCE_MUST_BE_A_BUILD_SERVICE,
+    NESTED_MAP_UNSUPPORTED_KEY_TYPE,
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/DefaultTypeValidationProblemBuilder.java b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/DefaultTypeValidationProblemBuilder.java
index 6de342e..43561b2 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/DefaultTypeValidationProblemBuilder.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/DefaultTypeValidationProblemBuilder.java
@@ -47,7 +47,7 @@ public TypeValidationProblem build() {
         return new TypeValidationProblem(
             problemId,
             severity,
-            typeIrrelevantInErrorMessage ? TypeValidationProblemLocation.irrelevant() :  TypeValidationProblemLocation.inType(type, pluginId),
+            typeIrrelevantInErrorMessage ? TypeValidationProblemLocation.irrelevant() : TypeValidationProblemLocation.inType(type, pluginId),
             shortProblemDescription,
             longDescription,
             reason,
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/ReplayingTypeValidationContext.java b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/ReplayingTypeValidationContext.java
index dc2c12b..ceb04b4 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/ReplayingTypeValidationContext.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/ReplayingTypeValidationContext.java
@@ -34,7 +34,7 @@ public void visitTypeProblem(Action<? super TypeProblemBuilder> problemSpec) {
     public void visitPropertyProblem(Action<? super PropertyProblemBuilder> problemSpec) {
         problems.add((ownerProperty, validationContext) -> validationContext.visitPropertyProblem(builder -> {
             problemSpec.execute(builder);
-            ((PropertyProblemBuilderInternal)builder).forOwner(ownerProperty);
+            ((PropertyProblemBuilderInternal) builder).forOwner(ownerProperty);
         }));
     }
 
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/TypeValidationContext.java b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/TypeValidationContext.java
index 95a3e70..b8bbd03 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/TypeValidationContext.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/TypeValidationContext.java
@@ -22,16 +22,18 @@ public interface TypeValidationContext {
 
     /**
      * Visits a validation problem associated with the given type.
-     * Callers are encourages to provide as much information as they can on
+     * Callers are encouraged to provide as much information as they can on
      * the problem following the problem builder instructions.
+     *
      * @param problemSpec the problem builder
      */
     void visitTypeProblem(Action<? super TypeProblemBuilder> problemSpec);
 
     /**
      * Visits a validation problem associated with the given property.
-     * Callers are encourages to provide as much information as they can on
+     * Callers are encouraged to provide as much information as they can on
      * the problem following the problem builder instructions.
+     *
      * @param problemSpec the problem builder
      */
     void visitPropertyProblem(Action<? super PropertyProblemBuilder> problemSpec);
@@ -41,7 +43,7 @@ public interface TypeValidationContext {
         public void visitTypeProblem(Action<? super TypeProblemBuilder> problemSpec) {}
 
         @Override
-        public void visitPropertyProblem(Action<? super PropertyProblemBuilder> problemSpec) { }
+        public void visitPropertyProblem(Action<? super PropertyProblemBuilder> problemSpec) {}
     };
 
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/TypeValidationProblemRenderer.java b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/TypeValidationProblemRenderer.java
index 8c254ed..b40fc07 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/TypeValidationProblemRenderer.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/TypeValidationProblemRenderer.java
@@ -15,7 +15,6 @@
  */
 package org.gradle.internal.reflect.validation;
 
-import org.apache.commons.lang.StringUtils;
 import org.gradle.internal.logging.text.TreeFormatter;
 import org.gradle.model.internal.type.ModelType;
 import org.gradle.plugin.use.PluginId;
@@ -23,6 +22,9 @@
 
 import java.util.List;
 
+import static org.apache.commons.lang.StringUtils.capitalize;
+import static org.gradle.util.internal.TextUtil.endLineWithDot;
+
 public class TypeValidationProblemRenderer {
 
     // We should get rid of this method quickly, as the validation message
@@ -40,37 +42,40 @@ public static String renderMinimalInformationAbout(TypeValidationProblem problem
 
     public static String renderMinimalInformationAbout(TypeValidationProblem problem, boolean renderDocLink, boolean renderSolutions) {
         TreeFormatter formatter = new TreeFormatter();
-        formatter.node(introductionFor(problem.getWhere()) + maybeAppendDot(problem.getShortDescription()));
+        formatter.node(introductionFor(problem.getWhere()) + endLineWithDot(problem.getShortDescription()));
         problem.getWhy().ifPresent(reason -> {
             formatter.blankLine();
-            formatter.node("Reason: " + StringUtils.capitalize(maybeAppendDot(reason)));
+            formatter.node("Reason: " + capitalize(endLineWithDot(reason)));
         });
         if (renderSolutions) {
-            List<Solution> possibleSolutions = problem.getPossibleSolutions();
-            int solutionCount = possibleSolutions.size();
-            if (solutionCount > 0) {
-                formatter.blankLine();
-                if (solutionCount == 1) {
-                    formatter.node("Possible solution: " + StringUtils.capitalize(maybeAppendDot(possibleSolutions.get(0).getShortDescription())));
-                } else {
-                    formatter.node("Possible solutions");
-                    formatter.startNumberedChildren();
-                    possibleSolutions.forEach(solution ->
-                        formatter.node(StringUtils.capitalize(maybeAppendDot(solution.getShortDescription())))
-                    );
-                    formatter.endChildren();
-                }
-            }
+            renderSolutions(formatter, problem.getPossibleSolutions());
         }
         if (renderDocLink) {
             problem.getDocumentationLink().ifPresent(docLink -> {
                 formatter.blankLine();
-                formatter.node("Please refer to ").append(docLink).append(" for more details about this problem.");
+                formatter.node(docLink);
             });
         }
         return formatter.toString();
     }
 
+    public static void renderSolutions(TreeFormatter formatter, List<Solution> possibleSolutions) {
+        int solutionCount = possibleSolutions.size();
+        if (solutionCount > 0) {
+            formatter.blankLine();
+            if (solutionCount == 1) {
+                formatter.node("Possible solution: " + capitalize(endLineWithDot(possibleSolutions.get(0).getShortDescription())));
+            } else {
+                formatter.node("Possible solutions");
+                formatter.startNumberedChildren();
+                possibleSolutions.forEach(solution ->
+                    formatter.node(capitalize(endLineWithDot(solution.getShortDescription())))
+                );
+                formatter.endChildren();
+            }
+        }
+    }
+
     /**
      * This is an adhoc reformatting tool which should go away as soon as we have
      * a better way to display multiline deprecation warnings
@@ -94,8 +99,8 @@ private static String introductionFor(TypeValidationProblemLocation location) {
             } else {
                 builder.append("Type '");
             }
-            builder.append(ModelType.of(rootType).getName());
-            builder.append("' ");
+            builder.append(ModelType.of(rootType).getName())
+                .append("' ");
         }
         String property = location.getPropertyName().orElse(null);
         if (property != null) {
@@ -112,8 +117,8 @@ private static String introductionFor(TypeValidationProblemLocation location) {
                 builder.append(parentProperty);
                 builder.append('.');
             });
-            builder.append(property);
-            builder.append("' ");
+            builder.append(property)
+                .append("' ");
         }
         return builder.toString();
     }
@@ -125,12 +130,4 @@ private static String introductionFor(TypeValidationProblemLocation location) {
     private static boolean shouldRenderType(Class<?> clazz) {
         return !("org.gradle.api.DefaultTask".equals(clazz.getName()));
     }
-
-    private static String maybeAppendDot(String txt) {
-        if (txt.endsWith(".") || txt.endsWith("\n")) {
-            return txt;
-        }
-        return txt + ".";
-    }
-
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/UserManualReference.java b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/UserManualReference.java
index 5fc8cc3..35965dd 100644
--- a/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/UserManualReference.java
+++ b/subprojects/model-core/src/main/java/org/gradle/internal/reflect/validation/UserManualReference.java
@@ -37,6 +37,6 @@ public String getSection() {
     }
 
     public String toDocumentationLink() {
-        return documentationRegistry.getDocumentationFor(id, section);
+        return documentationRegistry.getDocumentationRecommendationFor("information", id, section);
     }
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/model/internal/asm/BytecodeFragment.java b/subprojects/model-core/src/main/java/org/gradle/model/internal/asm/BytecodeFragment.java
index bffb34f..1e6c140 100644
--- a/subprojects/model-core/src/main/java/org/gradle/model/internal/asm/BytecodeFragment.java
+++ b/subprojects/model-core/src/main/java/org/gradle/model/internal/asm/BytecodeFragment.java
@@ -19,5 +19,8 @@
 import org.objectweb.asm.MethodVisitor;
 
 public interface BytecodeFragment {
+
+    BytecodeFragment NO_OP = visitor -> {};
+
     void emit(MethodVisitor visitor);
 }
diff --git a/subprojects/model-core/src/main/java/org/gradle/util/ConfigureUtil.java b/subprojects/model-core/src/main/java/org/gradle/util/ConfigureUtil.java
index b2cb257..3449afc 100644
--- a/subprojects/model-core/src/main/java/org/gradle/util/ConfigureUtil.java
+++ b/subprojects/model-core/src/main/java/org/gradle/util/ConfigureUtil.java
@@ -19,12 +19,12 @@
 import groovy.lang.Closure;
 import org.codehaus.groovy.runtime.GeneratedClosure;
 import org.gradle.api.Action;
-import org.gradle.internal.deprecation.DeprecationLogger;
-import org.gradle.internal.metaobject.DynamicObjectUtil;
 import org.gradle.internal.Actions;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.metaobject.ConfigureDelegate;
 import org.gradle.internal.metaobject.DynamicInvokeResult;
 import org.gradle.internal.metaobject.DynamicObject;
+import org.gradle.internal.metaobject.DynamicObjectUtil;
 import org.gradle.util.internal.ClosureBackedAction;
 
 import javax.annotation.Nullable;
@@ -73,7 +73,7 @@
 public class ConfigureUtil {
 
     public static <T> T configureByMap(Map<?, ?> properties, T delegate) {
-        logDeprecation(8);
+        logDeprecation();
         return configureByMapInternal(properties, delegate);
     }
 
@@ -102,6 +102,7 @@ private static <T> T configureByMapInternal(Map<?, ?> properties, T delegate) {
     }
 
     public static <T> T configureByMap(Map<?, ?> properties, T delegate, Collection<?> mandatoryKeys) {
+        logDeprecation();
         if (!mandatoryKeys.isEmpty()) {
             Collection<String> missingKeys = toStringList(mandatoryKeys);
             missingKeys.removeAll(toStringList(properties.keySet()));
@@ -109,7 +110,6 @@ public static <T> T configureByMap(Map<?, ?> properties, T delegate, Collection<
                 throw new IncompleteInputException("Input configuration map does not contain following mandatory keys: " + missingKeys, missingKeys);
             }
         }
-        logDeprecation(7);
         return configureByMapInternal(properties, delegate);
     }
 
@@ -123,7 +123,7 @@ public static class IncompleteInputException extends RuntimeException {
         public IncompleteInputException(String message, Collection missingKeys) {
             super(message);
             this.missingKeys = missingKeys;
-            logDeprecation(7);
+            logDeprecation();
         }
 
         public Collection getMissingKeys() {
@@ -145,7 +145,7 @@ public Collection getMissingKeys() {
      * @return The delegate param
      */
     public static <T> T configure(@Nullable Closure configureClosure, T target) {
-        // TODO log deprecation once the shadow plugin is fixed
+        logDeprecation();
         if (configureClosure == null) {
             return target;
         }
@@ -163,7 +163,7 @@ public static <T> T configure(@Nullable Closure configureClosure, T target) {
      * Creates an action that uses the given closure to configure objects of type T.
      */
     public static <T> Action<T> configureUsing(@Nullable final Closure configureClosure) {
-        logDeprecation(7);
+        logDeprecation();
         if (configureClosure == null) {
             return Actions.doNothing();
         }
@@ -175,7 +175,7 @@ public static <T> Action<T> configureUsing(@Nullable final Closure configureClos
      * Called from an object's {@link Configurable#configure} method.
      */
     public static <T> T configureSelf(@Nullable Closure configureClosure, T target) {
-        logDeprecation(7);
+        logDeprecation();
         if (configureClosure == null) {
             return target;
         }
@@ -188,7 +188,7 @@ public static <T> T configureSelf(@Nullable Closure configureClosure, T target)
      * Called from an object's {@link Configurable#configure} method.
      */
     public static <T> T configureSelf(@Nullable Closure configureClosure, T target, ConfigureDelegate closureDelegate) {
-        logDeprecation(7);
+        logDeprecation();
         if (configureClosure == null) {
             return target;
         }
@@ -208,10 +208,10 @@ private static <T> void configureTarget(Closure configureClosure, T target, Conf
         new ClosureBackedAction<T>(withNewOwner, Closure.OWNER_ONLY, false).execute(target);
     }
 
-    private static void logDeprecation(int majorVersion) {
+    private static void logDeprecation() {
         DeprecationLogger.deprecateType(ConfigureUtil.class)
             .willBeRemovedInGradle9()
-            .withUpgradeGuideSection(majorVersion, "org_gradle_util_reports_deprecations")
+            .withUpgradeGuideSection(8, "org_gradle_util_reports_deprecations")
             .nagUser();
     }
 
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/internal/extensibility/ExtensibleDynamicObjectTestHelper.groovy b/subprojects/model-core/src/test/groovy/org/gradle/internal/extensibility/ExtensibleDynamicObjectTestHelper.groovy
index 20d1ff7..0e7aadd 100644
--- a/subprojects/model-core/src/test/groovy/org/gradle/internal/extensibility/ExtensibleDynamicObjectTestHelper.groovy
+++ b/subprojects/model-core/src/test/groovy/org/gradle/internal/extensibility/ExtensibleDynamicObjectTestHelper.groovy
@@ -114,4 +114,4 @@
             throw new groovy.lang.MissingMethodException(name, getClass(), args)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/internal/reflect/validation/ValidationMessageCheckerTest.groovy b/subprojects/model-core/src/test/groovy/org/gradle/internal/reflect/validation/ValidationMessageCheckerTest.groovy
index ed2dd7c..12aa3d7 100644
--- a/subprojects/model-core/src/test/groovy/org/gradle/internal/reflect/validation/ValidationMessageCheckerTest.groovy
+++ b/subprojects/model-core/src/test/groovy/org/gradle/internal/reflect/validation/ValidationMessageCheckerTest.groovy
@@ -17,6 +17,7 @@
 package org.gradle.internal.reflect.validation
 
 import groovy.transform.CompileStatic
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.api.services.BuildService
 import org.gradle.api.services.ServiceReference
 import org.gradle.internal.reflect.problems.ValidationProblemId
@@ -71,7 +72,7 @@
   1. Assign a value to 'someProperty'.
   2. Mark property 'someProperty' as optional.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#value_not_set for more details about this problem.
+${validationMessage("value_not_set")}
 """
     }
 
@@ -88,6 +89,7 @@
             includeLink()
         }
 
+
         then:
         outputEquals """
 Type 'MyTask' method 'setFoo()' should not be annotated with: @SomeAnnotation.
@@ -98,10 +100,14 @@
   1. Remove the annotations.
   2. Rename the method.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#ignored_annotations_on_method for more details about this problem.
+${validationMessage("ignored_annotations_on_method")}
 """
     }
 
+    private validationMessage(String ignoredAnnotationsOnMethod) {
+        new DocumentationRegistry().getDocumentationRecommendationFor("information", "validation_problems", ignoredAnnotationsOnMethod)
+    }
+
     @ValidationTestFor(
         ValidationProblemId.PRIVATE_GETTER_MUST_NOT_BE_ANNOTATED
     )
@@ -123,7 +129,7 @@
   1. Make the getter public.
   2. Annotate the public version of the getter.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#private_getter_must_not_be_annotated for more details about this problem.
+${validationMessage("private_getter_must_not_be_annotated")}
 """
     }
 
@@ -149,7 +155,7 @@
   1. Remove the input annotations.
   2. Remove the @Internal annotation.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#ignored_property_must_not_be_annotated for more details about this problem.
+${validationMessage("ignored_property_must_not_be_annotated")}
 """
     }
 
@@ -172,7 +178,7 @@
 
 Possible solution: Choose between one of the conflicting annotations.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#conflicting_annotations for more details about this problem.
+${validationMessage("conflicting_annotations")}
 """
     }
 
@@ -198,7 +204,7 @@
   1. Remove the property.
   2. Use a different annotation, e.g one of @Inject, @InputArtifact or @InputArtifactDependencies.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#annotation_invalid_in_context for more details about this problem.
+${validationMessage("annotation_invalid_in_context")}
 """
         when:
         render annotationInvalidInContext {
@@ -259,7 +265,7 @@
   1. Remove the annotation.
   2. Use a different annotation, e.g one of @Classpath, @CompileClasspath or @PathSensitive.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#annotation_invalid_in_context for more details about this problem.
+${validationMessage("annotation_invalid_in_context")}
 """
     }
 
@@ -284,7 +290,7 @@
   1. Add something cool.
   2. Mark it as @Internal.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#missing_annotation for more details about this problem.
+${validationMessage("missing_annotation")}
 """
     }
 
@@ -309,7 +315,7 @@
   1. Add a getter for field 'claws'.
   2. Remove the annotations on 'claws'.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#ignored_annotations_on_field for more details about this problem.
+${validationMessage("ignored_annotations_on_field")}
 """
     }
 
@@ -333,7 +339,7 @@
 
 Possible solution: Remove the '@Boring' annotation.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#incompatible_annotations for more details about this problem.
+${validationMessage("incompatible_annotations")}
 """
     }
 
@@ -359,7 +365,7 @@
   2. Annotate with @InputFiles for collections of files.
   3. If you want to track the path, return File.absolutePath as a String and keep @Input.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#incorrect_use_of_input_annotation for more details about this problem.
+${validationMessage("incorrect_use_of_input_annotation")}
 """
     }
 
@@ -386,7 +392,7 @@
   1. Make 'mypackage.FooBar' implement '${BuildService.class.name}'.
   2. Replace the @ServiceReference annotation on 'someService' with @Internal and assign a value of type 'mypackage.FooBar' explicitly.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#service_reference_must_be_a_build_service for more details about this problem.
+${validationMessage("service_reference_must_be_a_build_service")}
 """
     }
 
@@ -409,7 +415,7 @@
 
 Possible solution: Declare the normalization strategy by annotating the property with either @PathSensitive, @Classpath or @CompileClasspath.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#missing_normalization_annotation for more details about this problem.
+${validationMessage("missing_normalization_annotation")}
 """
     }
 
@@ -437,7 +443,7 @@
   2. Declare an explicit dependency on 'producer' from 'consumer' using Task#dependsOn.
   3. Declare an explicit dependency on 'producer' from 'consumer' using Task#mustRunAfter.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#implicit_dependency for more details about this problem.
+${validationMessage("implicit_dependency")}
 """
     }
 
@@ -463,7 +469,7 @@
   1. Make sure the file exists before the task is called.
   2. Make sure that the task which produces the file is declared as an input.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#input_file_does_not_exist for more details about this problem.
+${validationMessage("input_file_does_not_exist")}
 """
 
         render inputDoesNotExist {
@@ -482,7 +488,7 @@
   1. Make sure the directory exists before the task is called.
   2. Make sure that the task which produces the directory is declared as an input.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#input_file_does_not_exist for more details about this problem.
+${validationMessage("input_file_does_not_exist")}
 """
 
     }
@@ -511,7 +517,7 @@
   1. Use a file as an input.
   2. Declare the input as a directory instead.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#unexpected_input_file_type for more details about this problem.
+${validationMessage("unexpected_input_file_type")}
 """
     }
 
@@ -538,7 +544,7 @@
 
 Possible solution: Make sure that the 'output' is configured to a directory.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#cannot_write_output for more details about this problem.
+${validationMessage("cannot_write_output")}
 """
 
         when:
@@ -557,7 +563,7 @@
 
 Possible solution: Make sure that the 'output' is configured to a directory.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#cannot_write_output for more details about this problem.
+${validationMessage("cannot_write_output")}
 """
     }
 
@@ -586,7 +592,7 @@
   1. Configure 'output' to point to a file, not a directory.
   2. Annotate 'output' with @OutputDirectory instead of @OutputFiles.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#cannot_write_output for more details about this problem.
+${validationMessage("cannot_write_output")}
 """
 
         when:
@@ -605,7 +611,7 @@
 
 Possible solution: Configure 'output' to point to the correct location.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#cannot_write_output for more details about this problem.
+${validationMessage("cannot_write_output")}
 """
     }
 
@@ -630,7 +636,7 @@
 
 Possible solution: Select a different output location.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#cannot_write_to_reserved_location for more details about this problem.
+${validationMessage("cannot_write_to_reserved_location")}
 """
     }
 
@@ -655,7 +661,7 @@
 
 Possible solution: Make sure that the root of the file tree 'output' is configured to a directory.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#cannot_write_output for more details about this problem.
+${validationMessage("cannot_write_output")}
 """
     }
 
@@ -682,7 +688,7 @@
   1. Use a mask.
   2. Use a vaccine.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#unsupported_notation for more details about this problem.
+${validationMessage("unsupported_notation")}
 """
     }
 
@@ -706,7 +712,7 @@
 
 Possible solution: Remove the annotation.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#invalid_use_of_cacheable_annotation for more details about this problem.
+${validationMessage("invalid_use_of_cacheable_annotation")}
 """
     }
 
@@ -731,7 +737,7 @@
   1. Remove the @Optional annotation.
   2. Use the java.lang.Integer type instead.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#cannot_use_optional_on_primitive_types for more details about this problem.
+${validationMessage("cannot_use_optional_on_primitive_types")}
 """
     }
 
@@ -755,7 +761,7 @@
   1. Remove one of the getters.
   2. Annotate one of the getters with @Internal.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#redundant_getters for more details about this problem.
+${validationMessage("redundant_getters")}
 """
     }
 
@@ -778,7 +784,7 @@
 
 Possible solution: Remove the 'setSomeProperty' method.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#mutable_type_with_setter for more details about this problem.
+${validationMessage("mutable_type_with_setter")}
 """
     }
 
@@ -800,7 +806,7 @@
 
 Possible solution: Use a different normalization strategy via @PathSensitive, @Classpath or @CompileClasspath.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#cacheable_transform_cant_use_absolute_sensitivity for more details about this problem.
+${validationMessage("cacheable_transform_cant_use_absolute_sensitivity")}
 """
     }
 
@@ -823,7 +829,8 @@
 
 Possible solution: Use an (anonymous inner) class instead.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#implementation_unknown for more details about this problem."""
+${validationMessage("implementation_unknown")}
+"""
     }
 
     @ValidationTestFor(
@@ -845,7 +852,7 @@
 
 Possible solution: Use an (anonymous inner) class instead.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#implementation_unknown for more details about this problem."""
+${validationMessage("implementation_unknown")}"""
     }
 
     @ValidationTestFor(
@@ -867,7 +874,7 @@
 
 Possible solution: Load your class by using one of Gradle's built-in ways.
 
-Please refer to https://docs.gradle.org/current/userguide/validation_problems.html#implementation_unknown for more details about this problem."""
+${validationMessage("implementation_unknown")}"""
     }
 
     @ValidationTestFor(
diff --git a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/HasStrings.java b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/HasStrings.java
index 3819d65..a262a99 100644
--- a/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/HasStrings.java
+++ b/subprojects/model-core/src/test/groovy/org/gradle/model/internal/inspect/HasStrings.java
@@ -20,4 +20,4 @@
 
 interface HasStrings<T> {
     List<T> strings();
-}
\ No newline at end of file
+}
diff --git a/subprojects/model-core/src/testFixtures/groovy/org/gradle/internal/reflect/validation/ValidationMessageChecker.groovy b/subprojects/model-core/src/testFixtures/groovy/org/gradle/internal/reflect/validation/ValidationMessageChecker.groovy
index 77a4380..7a89c87 100644
--- a/subprojects/model-core/src/testFixtures/groovy/org/gradle/internal/reflect/validation/ValidationMessageChecker.groovy
+++ b/subprojects/model-core/src/testFixtures/groovy/org/gradle/internal/reflect/validation/ValidationMessageChecker.groovy
@@ -35,12 +35,9 @@
         messageIndent = indent
     }
 
-    String userguideLink(String id, String section) {
-        documentationRegistry.getDocumentationFor(id, section)
-    }
 
     String learnAt(String id, String section) {
-        "Please refer to ${userguideLink(id, section)} for more details about this problem"
+        documentationRegistry.getDocumentationRecommendationFor("information", id, section)
     }
 
     @ValidationTestFor(
@@ -424,6 +421,17 @@
         config.render()
     }
 
+    @ValidationTestFor(
+        ValidationProblemId.NESTED_MAP_UNSUPPORTED_KEY_TYPE
+    )
+    String nestedMapUnsupportedKeyType(@DelegatesTo(value = NestedMapUnsupportedKeyType, strategy = Closure.DELEGATE_FIRST) Closure<?> spec) {
+        def config = display(NestedMapUnsupportedKeyType, "unsupported_key_type_of_nested_map", spec)
+        config.description("where key of nested map is of type '${config.keyType}'.")
+            .reason("Key of nested map must be one of the following types: 'Enum', 'Integer', 'String'")
+            .solution("Change type of key to one of the following types: 'Enum', 'Integer', 'String'")
+            .render()
+    }
+
     void expectThatExecutionOptimizationDisabledWarningIsDisplayed(GradleExecuter executer,
                                                                    String message,
                                                                    String docId = "incremental_build",
@@ -433,7 +441,7 @@
             "This behavior has been deprecated. " +
             "This behavior is scheduled to be removed in Gradle 9.0. " +
             "Execution optimizations are disabled to ensure correctness. " +
-            "See https://docs.gradle.org/current/userguide/${docId}.html#${section} for more details."
+            documentationRegistry.getDocumentationRecommendationFor("information", docId, section)
         executer.expectDocumentedDeprecationWarning(deprecationMessage)
     }
 
@@ -952,6 +960,20 @@
         }
     }
 
+    static class NestedMapUnsupportedKeyType extends ValidationMessageDisplayConfiguration<NestedMapUnsupportedKeyType> {
+
+        String keyType
+
+        NestedMapUnsupportedKeyType(ValidationMessageChecker checker) {
+            super(checker)
+        }
+
+        NestedMapUnsupportedKeyType keyType(String keyType) {
+            this.keyType = keyType
+            this
+        }
+    }
+
     static class UnsupportedServiceReferenceType extends ValidationMessageDisplayConfiguration<UnsupportedServiceReferenceType> {
 
         UnsupportedServiceReferenceType(ValidationMessageChecker checker) {
diff --git a/subprojects/model-core/src/testFixtures/groovy/org/gradle/internal/reflect/validation/ValidationMessageDisplayConfiguration.groovy b/subprojects/model-core/src/testFixtures/groovy/org/gradle/internal/reflect/validation/ValidationMessageDisplayConfiguration.groovy
index 6e28958..6250a1f 100644
--- a/subprojects/model-core/src/testFixtures/groovy/org/gradle/internal/reflect/validation/ValidationMessageDisplayConfiguration.groovy
+++ b/subprojects/model-core/src/testFixtures/groovy/org/gradle/internal/reflect/validation/ValidationMessageDisplayConfiguration.groovy
@@ -16,6 +16,9 @@
 
 package org.gradle.internal.reflect.validation
 
+
+import static org.gradle.util.internal.TextUtil.endLineWithDot
+
 class ValidationMessageDisplayConfiguration<T extends ValidationMessageDisplayConfiguration<T>> {
     private final ValidationMessageChecker checker
     String pluginId
@@ -95,35 +98,28 @@
     }
 
     private String getIntro() {
-        if (hasIntro) {
-            String intro = typeName ? "Type '$typeName' ${property ? "${propertyIntro} '${property}' " : ''}" : (property ? "${propertyIntro.capitalize()} '${property}' " : "")
-            if (pluginId) {
-                return "In plugin '${pluginId}' ${intro.uncapitalize()}"
-            } else {
-                return intro
-            }
-        } else {
-            ''
+        if (!hasIntro) {
+            return ''
         }
+        String intro = typeName ? "Type '$typeName' ${property ? "${propertyIntro} '${property}' " : ''}" : (property ? "${propertyIntro.capitalize()} '${property}' " : "")
+        if (pluginId) {
+            return "In plugin '${pluginId}' ${intro.uncapitalize()}"
+        }
+        return intro
     }
 
     private String getOutro() {
-        includeLink ? "${checker.learnAt(documentationId, section)}." : ""
+        checker.learnAt(documentationId, section)
     }
 
     static String formatEntry(String entry) {
-        if (entry.endsWith(".")) {
-            entry.capitalize()
-        } else {
-            "${entry.capitalize()}."
-        }
+        endLineWithDot(entry.capitalize())
     }
 
     String render(boolean renderSolutions = true) {
         def newLine = "\n${checker.messageIndent}"
         StringBuilder sb = new StringBuilder(intro)
-        sb.append(description)
-            .append(description.endsWith(".") ? '' : '.')
+        sb.append(endLineWithDot(description))
             .append(newLine)
             .append(newLine)
         if (reason) {
@@ -140,7 +136,7 @@
             }
             sb.append(newLine)
         }
-        if (outro) {
+        if (includeLink) {
             sb.append(outro)
         }
         sb.toString().trim()
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetectorTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetectorTest.groovy
index 616233b..4e01412 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetectorTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/console/NativePlatformConsoleDetectorTest.groovy
@@ -19,9 +19,9 @@
 import net.rubygrapefruit.platform.NativeException
 import net.rubygrapefruit.platform.terminal.Terminals
 import org.gradle.internal.nativeintegration.ProcessEnvironment
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.Specification
 
 class NativePlatformConsoleDetectorTest extends Specification {
@@ -52,7 +52,7 @@
         detector.console == null
     }
 
-    @Requires(TestPrecondition.SMART_TERMINAL)
+    @Requires(UnitTestPreconditions.SmartTerminalAvailable)
     def "returns metadata when stdout and stderr are attached to console"() {
         given:
         terminals.isTerminal(Terminals.Output.Stdout) >> true
@@ -64,7 +64,7 @@
         detector.console.stdErr
     }
 
-    @Requires(TestPrecondition.SMART_TERMINAL)
+    @Requires(UnitTestPreconditions.SmartTerminalAvailable)
     def "returns metadata when only stdout is attached to console"() {
         given:
         terminals.isTerminal(Terminals.Output.Stdout) >> true
@@ -76,7 +76,7 @@
         !detector.console.stdErr
     }
 
-    @Requires(TestPrecondition.SMART_TERMINAL)
+    @Requires(UnitTestPreconditions.SmartTerminalAvailable)
     def "returns metadata when only stderr is attached to console"() {
         given:
         terminals.isTerminal(Terminals.Output.Stdout) >> false
@@ -88,7 +88,7 @@
         detector.console.stdErr
     }
 
-    @Requires(TestPrecondition.UNIX)
+    @Requires(UnitTestPreconditions.Unix)
     def "returns null when TERM is not set"() {
         given:
         env.removeEnvironmentVariable('TERM')
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/CommonFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/CommonFileSystemTest.groovy
index 950550c..8f485bd 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/CommonFileSystemTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/CommonFileSystemTest.groovy
@@ -21,9 +21,9 @@
 import org.gradle.internal.file.FileType
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -58,7 +58,7 @@
         e.message == "Could not set file mode 644 on '$file'."
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "unix permissions on files can be changed and read"() {
         def f = tmpDir.createFile("someFile\u03B1.txt")
 
@@ -73,7 +73,7 @@
         mode << [0644, 0600, 0751]
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "unix permissions on directories can be changed and read"() {
         def d = tmpDir.createDir("someDir\u03B1")
 
@@ -88,20 +88,20 @@
         mode << [0755, 0700, 0722]
     }
 
-    @Requires(TestPrecondition.NO_FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.NoFilePermissions)
     def "unix permissions have default values on unsupported platforms"() {
         expect:
         fs.getUnixMode(tmpDir.createFile("someFile")) == FileSystem.DEFAULT_FILE_MODE
         fs.getUnixMode(tmpDir.createDir("someDir")) == FileSystem.DEFAULT_DIR_MODE
     }
 
-    @Requires(TestPrecondition.NO_FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.NoFilePermissions)
     def "setting unix permissions does nothing on unsupported platforms"() {
         expect:
         fs.chmod(tmpDir.createFile("someFile"), 0644)
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "can create symlink on platforms that support symlinks"() {
         def target = tmpDir.createFile("target.txt")
         def link = tmpDir.file("link.txt")
@@ -114,7 +114,7 @@
         link.readLink() == target.absolutePath
     }
 
-    @Requires(TestPrecondition.NO_SYMLINKS)
+    @Requires(UnitTestPreconditions.NoSymlinks)
     def "cannot create symlinks on platforms that do not support symlinks"() {
         def target = tmpDir.createFile("target.txt")
         def link = tmpDir.file("link.txt")
@@ -157,7 +157,7 @@
         stat.length == 0
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "stats symlink"() {
         def file = tmpDir.file("file")
         file.text = "123"
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/LinuxFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/LinuxFileSystemTest.groovy
index 19200dd..4dffca5 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/LinuxFileSystemTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/LinuxFileSystemTest.groovy
@@ -15,12 +15,12 @@
  */
 package org.gradle.internal.nativeintegration.filesystem
 
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.Specification
 
-@Requires(TestPrecondition.LINUX)
+@Requires(UnitTestPreconditions.Linux)
 class LinuxFileSystemTest extends Specification {
     def fs = NativeServicesTestFixture.instance.get(FileSystem)
 
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/MacOsFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/MacOsFileSystemTest.groovy
index 7ab7f0e..b800c10 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/MacOsFileSystemTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/MacOsFileSystemTest.groovy
@@ -15,12 +15,12 @@
  */
 package org.gradle.internal.nativeintegration.filesystem
 
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.Specification
 
-@Requires(TestPrecondition.MAC_OS_X)
+@Requires(UnitTestPreconditions.MacOs)
 class MacOsFileSystemTest extends Specification {
     def fs = NativeServicesTestFixture.instance.get(FileSystem)
 
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/WindowsFileSystemTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/WindowsFileSystemTest.groovy
index 85ab7aa..de47717 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/WindowsFileSystemTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/WindowsFileSystemTest.groovy
@@ -16,12 +16,12 @@
 
 package org.gradle.internal.nativeintegration.filesystem
 
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.Specification
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class WindowsFileSystemTest extends Specification {
     def fs = NativeServicesTestFixture.instance.get(FileSystem)
 
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/Jdk7SymlinkTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/Jdk7SymlinkTest.groovy
index 68d9984..f0d81a7 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/Jdk7SymlinkTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/Jdk7SymlinkTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.internal.SystemProperties
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -33,13 +33,13 @@
     @Rule
     TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider(getClass())
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def 'on symlink supporting system, it will return true for supported symlink'() {
         expect:
         new Jdk7Symlink(TestFiles.tmpDirTemporaryFileProvider(temporaryFolder.getRoot())).isSymlinkCreationSupported()
     }
 
-    @Requires(TestPrecondition.NO_SYMLINKS)
+    @Requires(UnitTestPreconditions.NoSymlinks)
     def 'on non symlink supporting system, it will return false for supported symlink'() {
         expect:
         !new WindowsJdk7Symlink().isSymlinkCreationSupported()
@@ -55,7 +55,7 @@
         create << [{ it -> new Jdk7Symlink(TestFiles.tmpDirTemporaryFileProvider(it)) }, { new WindowsJdk7Symlink() }]
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def 'can create and detect symlinks'() {
         def symlink = new Jdk7Symlink(TestFiles.tmpDirTemporaryFileProvider(temporaryFolder.getRoot()))
         def testDirectory = temporaryFolder.getTestDirectory().createDir()
@@ -73,7 +73,7 @@
         symlink.isSymlink(new File(testDirectory, 'testDir'))
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def 'can detect Windows symbolic links as symbolic links'() {
         def symlink = new WindowsJdk7Symlink()
         def testDirectory = temporaryFolder.getTestDirectory().createDir()
@@ -91,7 +91,7 @@
         symlink.isSymlink(new File(testDirectory, 'testDir'))
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def 'does not detect Windows hard links as symbolic links'() {
         def symlink = new WindowsJdk7Symlink()
         def testDirectory = temporaryFolder.getTestDirectory().createDir()
@@ -103,7 +103,7 @@
         !symlink.isSymlink(new File(testDirectory, 'testFile'))
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def 'can detect Windows junction point as symbolic links'() {
         def symlink = new WindowsJdk7Symlink()
         def testDirectory = temporaryFolder.getTestDirectory().createDir()
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy
index 32f8c3d..3b48d71 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/jdk7/PosixJdk7FilePermissionHandlerTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.internal.nativeintegration.filesystem.jdk7
 
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class PosixJdk7FilePermissionHandlerTest extends Specification {
     @Rule TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider(getClass())
 
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/AbstractFileMetadataAccessorTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/AbstractFileMetadataAccessorTest.groovy
index 6fa6285..9f40dd2 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/AbstractFileMetadataAccessorTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/filesystem/services/AbstractFileMetadataAccessorTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.internal.file.FileType
 import org.gradle.internal.nativeintegration.filesystem.FileMetadataAccessor
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
@@ -77,7 +77,7 @@
         assertSameAccessType(stat, DIRECT)
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "stats symlink"() {
         def file = tmpDir.file("file")
         file.text = "123"
@@ -92,7 +92,7 @@
         assertSameAccessType(stat, VIA_SYMLINK)
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "stats symlink to directory"() {
         def dir = tmpDir.createDir("dir")
         def link = tmpDir.file("link")
@@ -106,7 +106,7 @@
         assertSameAccessType(stat, VIA_SYMLINK)
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "stats broken symlink"() {
         def file = tmpDir.file("file")
         def link = tmpDir.file("link")
@@ -120,7 +120,7 @@
         assertSameAccessType(stat, VIA_SYMLINK)
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "stats symlink pointing to symlink pointing to file"() {
         def file = tmpDir.file("file")
         file.text = "123"
@@ -137,7 +137,7 @@
         assertSameAccessType(stat, VIA_SYMLINK)
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "stats symlink pointing to broken symlink"() {
         def file = tmpDir.file("file")
         def link = tmpDir.file("link")
@@ -153,7 +153,7 @@
         assertSameAccessType(stat, VIA_SYMLINK)
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "stats a symlink cycle"() {
         def first = tmpDir.file("first")
         def second = tmpDir.file("second")
@@ -170,7 +170,7 @@
         assertSameAccessType(stat, VIA_SYMLINK)
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "stats named pipes"() {
         def pipe = tmpDir.file("testPipe").createNamedPipe()
 
@@ -180,7 +180,7 @@
         thrown(UncheckedIOException)
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "stat a file in an unreadable directory"() {
         def unreadableDir = tmpDir.createDir("unreadableDir")
         def fileInDir = unreadableDir.createFile("inDir")
@@ -197,7 +197,7 @@
         unreadableDir.makeReadable()
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "stat an unreadable file"() {
         def unreadableFile = tmpDir.createFile("unreadable")
         unreadableFile.makeUnreadable()
@@ -213,7 +213,7 @@
         unreadableFile.makeReadable()
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "stat an unreadable directory"() {
         def unreadableDir = tmpDir.createDir("unreadable")
         unreadableDir.makeUnreadable()
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/jansi/JansiLibraryFactoryIntegrationTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/jansi/JansiLibraryFactoryIntegrationTest.groovy
index a272f43..405a7aa 100644
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/jansi/JansiLibraryFactoryIntegrationTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/jansi/JansiLibraryFactoryIntegrationTest.groovy
@@ -16,17 +16,19 @@
 
 package org.gradle.internal.nativeintegration.jansi
 
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Specification
 
-import static org.gradle.internal.nativeintegration.jansi.JansiLibraryFactory.*
-import static org.gradle.util.TestPrecondition.*
+import static org.gradle.internal.nativeintegration.jansi.JansiLibraryFactory.LINUX_LIB_FILENAME
+import static org.gradle.internal.nativeintegration.jansi.JansiLibraryFactory.MAC_OSX_LIB_FILENAME
+import static org.gradle.internal.nativeintegration.jansi.JansiLibraryFactory.WINDOWS_LIB_FILENAME
 
 class JansiLibraryFactoryIntegrationTest extends Specification {
 
     def factory = new JansiLibraryFactory()
 
-    @Requires(MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     def "jansi library can be created for MacOSX"() {
         when:
         JansiLibrary jansiLibrary = factory.create()
@@ -37,7 +39,7 @@
         jansiLibrary.resourcePath ==  "/META-INF/native/" + jansiLibrary.path
     }
 
-    @Requires(LINUX)
+    @Requires(UnitTestPreconditions.Linux)
     def "jansi library can be created for Linux"() {
         when:
         JansiLibrary jansiLibrary = factory.create()
@@ -48,7 +50,7 @@
         jansiLibrary.resourcePath ==  "/META-INF/native/" + jansiLibrary.path
     }
 
-    @Requires(WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "jansi library can be created for Windows"() {
         when:
         JansiLibrary jansiLibrary = factory.create()
diff --git a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/processenvironment/ProcessEnvironmentTest.groovy b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/processenvironment/ProcessEnvironmentTest.groovy
index 67664ac..54984a5 100755
--- a/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/processenvironment/ProcessEnvironmentTest.groovy
+++ b/subprojects/native/src/test/groovy/org/gradle/internal/nativeintegration/processenvironment/ProcessEnvironmentTest.groovy
@@ -17,10 +17,10 @@
 
 import org.gradle.internal.nativeintegration.ProcessEnvironment
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
 import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -47,13 +47,13 @@
         System.getenv("TEST_ENV_2") == null
     }
 
-    @Requires(TestPrecondition.WORKING_DIR)
+    @Requires(UnitTestPreconditions.WorkingDir)
     def "can get working directory of current process"() {
         expect:
         env.processDir.canonicalFile == new File('.').canonicalFile
     }
 
-    @Requires(TestPrecondition.WORKING_DIR)
+    @Requires(UnitTestPreconditions.WorkingDir)
     def "can get set working directory of current process"() {
         File originalDir = new File(System.getProperty("user.dir"))
 
diff --git a/subprojects/normalization-java/src/main/java/org/gradle/api/internal/changedetection/state/ZipHasher.java b/subprojects/normalization-java/src/main/java/org/gradle/api/internal/changedetection/state/ZipHasher.java
index 477d64a..1aaa490 100644
--- a/subprojects/normalization-java/src/main/java/org/gradle/api/internal/changedetection/state/ZipHasher.java
+++ b/subprojects/normalization-java/src/main/java/org/gradle/api/internal/changedetection/state/ZipHasher.java
@@ -57,17 +57,20 @@ public static boolean isZipFile(final String name) {
     }
 
     private final ResourceHasher resourceHasher;
+    private final ZipHasher fallbackZipHasher;
     private final HashingExceptionReporter hashingExceptionReporter;
 
     public ZipHasher(ResourceHasher resourceHasher) {
         this(
             resourceHasher,
+            null,
             (s, e) -> LOGGER.debug("Malformed archive '{}'. Falling back to full content hash instead of entry hashing.", s.getName(), e)
         );
     }
 
-    public ZipHasher(ResourceHasher resourceHasher, HashingExceptionReporter hashingExceptionReporter) {
+    public ZipHasher(ResourceHasher resourceHasher, @Nullable ZipHasher fallbackZipHasher, HashingExceptionReporter hashingExceptionReporter) {
         this.resourceHasher = resourceHasher;
+        this.fallbackZipHasher = fallbackZipHasher;
         this.hashingExceptionReporter = hashingExceptionReporter;
     }
 
@@ -95,6 +98,9 @@ private HashCode hashZipContents(RegularFileSnapshot zipFileSnapshot) {
             return hasher.hash();
         } catch (Exception e) {
             hashingExceptionReporter.report(zipFileSnapshot, e);
+            if (fallbackZipHasher != null) {
+                return fallbackZipHasher.hashZipContents(zipFileSnapshot);
+            }
             return zipFileSnapshot.getHash();
         }
     }
diff --git a/subprojects/normalization-java/src/main/java/org/gradle/internal/fingerprint/classpath/impl/ClasspathFingerprintingStrategy.java b/subprojects/normalization-java/src/main/java/org/gradle/internal/fingerprint/classpath/impl/ClasspathFingerprintingStrategy.java
index a8bc488..0c6f41a 100644
--- a/subprojects/normalization-java/src/main/java/org/gradle/internal/fingerprint/classpath/impl/ClasspathFingerprintingStrategy.java
+++ b/subprojects/normalization-java/src/main/java/org/gradle/internal/fingerprint/classpath/impl/ClasspathFingerprintingStrategy.java
@@ -109,10 +109,7 @@ public static ClasspathFingerprintingStrategy runtimeClasspath(
         Interner<String> stringInterner,
         LineEndingSensitivity lineEndingSensitivity
     ) {
-        ResourceHasher resourceHasher = LineEndingNormalizingResourceHasher.wrap(runtimeClasspathResourceHasher, lineEndingSensitivity);
-        resourceHasher = propertiesFileHasher(resourceHasher, propertiesFileFilters);
-        resourceHasher = metaInfAwareClasspathResourceHasher(resourceHasher, manifestAttributeResourceEntryFilter);
-        resourceHasher = ignoringResourceHasher(resourceHasher, classpathResourceFilter);
+        ResourceHasher resourceHasher = runtimeClasspathResourceHasher(runtimeClasspathResourceHasher, lineEndingSensitivity, propertiesFileFilters, manifestAttributeResourceEntryFilter, classpathResourceFilter);
         ZipHasher zipHasher = new ZipHasher(resourceHasher);
         return new ClasspathFingerprintingStrategy(CLASSPATH_IDENTIFIER, USE_FILE_HASH, resourceHasher, zipHasher, cacheService, stringInterner);
     }
@@ -122,11 +119,31 @@ public static ClasspathFingerprintingStrategy compileClasspath(ResourceHasher cl
         return new ClasspathFingerprintingStrategy(COMPILE_CLASSPATH_IDENTIFIER, IGNORE, classpathResourceHasher, zipHasher, cacheService, stringInterner);
     }
 
-    public static ClasspathFingerprintingStrategy compileClasspath(ResourceHasher classpathResourceHasher, ResourceSnapshotterCacheService cacheService, Interner<String> stringInterner, ZipHasher.HashingExceptionReporter hashingExceptionReporter) {
-        ZipHasher zipHasher = new ZipHasher(classpathResourceHasher, hashingExceptionReporter);
+    public static ClasspathFingerprintingStrategy compileClasspathFallbackToRuntimeClasspath(
+        ResourceHasher classpathResourceHasher,
+        ResourceHasher runtimeClasspathResourceHasher,
+        ResourceSnapshotterCacheService cacheService,
+        Interner<String> stringInterner,
+        ZipHasher.HashingExceptionReporter hashingExceptionReporter
+    ) {
+        ZipHasher fallbackZipHasher = new ZipHasher(runtimeClasspathResourceHasher);
+        ZipHasher zipHasher = new ZipHasher(classpathResourceHasher, fallbackZipHasher, hashingExceptionReporter);
         return new ClasspathFingerprintingStrategy(COMPILE_CLASSPATH_IDENTIFIER, IGNORE, classpathResourceHasher, zipHasher, cacheService, stringInterner);
     }
 
+    public static ResourceHasher runtimeClasspathResourceHasher(
+        RuntimeClasspathResourceHasher runtimeClasspathResourceHasher,
+        LineEndingSensitivity lineEndingSensitivity,
+        Map<String, ResourceEntryFilter> propertiesFileFilters,
+        ResourceEntryFilter manifestAttributeResourceEntryFilter,
+        ResourceFilter classpathResourceFilter
+    ) {
+        ResourceHasher resourceHasher = LineEndingNormalizingResourceHasher.wrap(runtimeClasspathResourceHasher, lineEndingSensitivity);
+        resourceHasher = propertiesFileHasher(resourceHasher, propertiesFileFilters);
+        resourceHasher = metaInfAwareClasspathResourceHasher(resourceHasher, manifestAttributeResourceEntryFilter);
+        return ignoringResourceHasher(resourceHasher, classpathResourceFilter);
+    }
+
     private static ResourceHasher ignoringResourceHasher(ResourceHasher delegate, ResourceFilter resourceFilter) {
         return new IgnoringResourceHasher(delegate, resourceFilter);
     }
diff --git a/subprojects/normalization-java/src/test/groovy/org/gradle/internal/normalization/java/ApiClassExtractorTest.groovy b/subprojects/normalization-java/src/test/groovy/org/gradle/internal/normalization/java/ApiClassExtractorTest.groovy
index 9212a0a..f7fe90b 100644
--- a/subprojects/normalization-java/src/test/groovy/org/gradle/internal/normalization/java/ApiClassExtractorTest.groovy
+++ b/subprojects/normalization-java/src/test/groovy/org/gradle/internal/normalization/java/ApiClassExtractorTest.groovy
@@ -16,6 +16,8 @@
 
 package org.gradle.internal.normalization.java
 
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Assume
 import org.objectweb.asm.ClassReader
 import org.objectweb.asm.ClassVisitor
@@ -273,7 +275,8 @@
     }
 
     void "target binary compatibility is maintained"() {
-        Assume.assumeFalse(target == "1.6" && !org.gradle.util.TestPrecondition.SUPPORTS_TARGETING_JAVA6.fulfilled)
+        Assume.assumeFalse(target == "1.6" && !canTargetJava6())
+        Assume.assumeFalse(target == "1.7" && !canTargetJava7())
 
         given:
         def api = toApi(target, [A: 'public class A {}'])
@@ -298,6 +301,14 @@
         '1.8'  | 52
     }
 
+    private boolean canTargetJava6() {
+        TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk12OrEarlier)
+    }
+
+    private boolean canTargetJava7() {
+        TestPrecondition.doSatisfies(UnitTestPreconditions.Jdk19OrEarlier)
+    }
+
     def "should not remove public field"() {
         given:
         def api = toApi 'A': '''
diff --git a/subprojects/normalization-java/src/test/groovy/org/gradle/internal/normalization/java/ApiClassExtractorTestSupport.groovy b/subprojects/normalization-java/src/test/groovy/org/gradle/internal/normalization/java/ApiClassExtractorTestSupport.groovy
index 875f36a..1562138 100644
--- a/subprojects/normalization-java/src/test/groovy/org/gradle/internal/normalization/java/ApiClassExtractorTestSupport.groovy
+++ b/subprojects/normalization-java/src/test/groovy/org/gradle/internal/normalization/java/ApiClassExtractorTestSupport.groovy
@@ -106,8 +106,12 @@
     @Rule
     public final TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider(getClass())
 
+    // The default target version can be updated to a new version when necessary, as long as
+    // you also update `ApiClassExtractorTest#target binary compatibility is maintained` with new assumptions.
+    private static final String DEFAULT_TARGET_VERSION = '8'
+
     protected ApiContainer toApi(Map<String, String> sources) {
-        toApi('1.7', [], sources)
+        toApi(DEFAULT_TARGET_VERSION, [], sources)
     }
 
     protected ApiContainer toApi(String targetVersion, Map<String, String> sources) {
@@ -115,7 +119,7 @@
     }
 
     protected ApiContainer toApi(List<String> packages, Map<String, String> sources) {
-        toApi('1.7', packages, sources)
+        toApi(DEFAULT_TARGET_VERSION, packages, sources)
     }
 
     protected ApiContainer toApi(String targetVersion, List<String> packages,  Map<String, String> sources) {
diff --git a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/AndroidIncrementalExecutionPerformanceTest.groovy b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/AndroidIncrementalExecutionPerformanceTest.groovy
index 85257c3..4986c06 100644
--- a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/AndroidIncrementalExecutionPerformanceTest.groovy
+++ b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/AndroidIncrementalExecutionPerformanceTest.groovy
@@ -42,15 +42,13 @@
 
     def setup() {
         testProject = AndroidTestProject.findProjectFor(runner.testProject) as IncrementalAndroidTestProject
-        AndroidTestProject.useLatestAgpVersion(runner)
+        AndroidTestProject.useAgpLatestStableOrRcVersion(runner)
+        AndroidTestProject.useKotlinLatestStableOrRcVersion(runner)
         runner.args.add('-Dorg.gradle.parallel=true')
         runner.args.addAll(["--no-build-cache", "--no-scan"])
-        runner.args.add("-D${StartParameterBuildOptions.ConfigurationCacheProblemsOption.PROPERTY_NAME}=warn")
-        AndroidTestProject.useLatestKotlinVersion(runner)
-        // AGP 7.3 requires Gradle 7.4
-        runner.minimumBaseVersion = "7.4"
+        // use the deprecated property so it works with previous versions
+        runner.args.add("-D${StartParameterBuildOptions.ConfigurationCacheProblemsOption.DEPRECATED_PROPERTY_NAME}=warn")
         applyEnterprisePlugin()
-        configureProjectJavaHomeToJdk11()
     }
 
     def "abi change#configurationCaching"() {
diff --git a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/AndroidPerformanceTestFixture.groovy b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/AndroidPerformanceTestFixture.groovy
index e9a0a6e..866f068 100644
--- a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/AndroidPerformanceTestFixture.groovy
+++ b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/AndroidPerformanceTestFixture.groovy
@@ -17,12 +17,8 @@
 package org.gradle.performance.regression.android
 
 import groovy.transform.SelfType
-import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.performance.AbstractCrossVersionPerformanceTest
 import org.gradle.performance.fixture.AndroidTestProject
-import org.gradle.profiler.BuildMutator
-import org.gradle.profiler.InvocationSettings
-import org.gradle.profiler.ScenarioContext
 
 @SelfType(AbstractCrossVersionPerformanceTest)
 trait AndroidPerformanceTestFixture {
@@ -33,25 +29,4 @@
         runner.assumeShouldRun()
         AndroidTestProject.projectFor(runner.testProject)
     }
-
-    void configureProjectJavaHomeToJdk11() {
-        def buildJavaHome = AvailableJavaHomes.jdk11.javaHome
-        runner.addBuildMutator { invocation -> new JavaHomeMutator(invocation, buildJavaHome) }
-    }
-
-    static class JavaHomeMutator implements BuildMutator {
-        private final File buildJavaHome
-        private final InvocationSettings invocation
-
-        JavaHomeMutator(InvocationSettings invocation, File buildJavaHome) {
-            this.invocation = invocation
-            this.buildJavaHome = buildJavaHome
-        }
-
-        @Override
-        void beforeScenario(ScenarioContext context) {
-            def gradleProps = new File(invocation.projectDir, "gradle.properties")
-            gradleProps << "\norg.gradle.java.home=${buildJavaHome.absolutePath.replace("\\", "/")}\n"
-        }
-    }
 }
diff --git a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/RealLifeAndroidBuildPerformanceTest.groovy b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/RealLifeAndroidBuildPerformanceTest.groovy
index 52382a3..6513066 100644
--- a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/RealLifeAndroidBuildPerformanceTest.groovy
+++ b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/RealLifeAndroidBuildPerformanceTest.groovy
@@ -34,11 +34,8 @@
 
     def setup() {
         runner.args = [AndroidGradlePluginVersions.OVERRIDE_VERSION_CHECK]
-        AndroidTestProject.useStableAgpVersion(runner)
-        AndroidTestProject.useStableKotlinVersion(runner)
-        // AGP 7.3 requires Gradle 7.4
-        runner.minimumBaseVersion = "7.4"
-        configureProjectJavaHomeToJdk11()
+        AndroidTestProject.useAgpLatestStableOrRcVersion(runner)
+        AndroidTestProject.useKotlinLatestStableOrRcVersion(runner)
     }
 
     @RunFor([
diff --git a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/RealLifeAndroidStudioPerformanceTest.groovy b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/RealLifeAndroidStudioPerformanceTest.groovy
index 1717b64..1698952 100644
--- a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/RealLifeAndroidStudioPerformanceTest.groovy
+++ b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/android/RealLifeAndroidStudioPerformanceTest.groovy
@@ -16,11 +16,15 @@
 
 package org.gradle.performance.regression.android
 
+import org.apache.commons.io.FilenameUtils
 import org.gradle.integtests.fixtures.versions.AndroidGradlePluginVersions
 import org.gradle.performance.AbstractCrossVersionPerformanceTest
 import org.gradle.performance.annotations.RunFor
 import org.gradle.performance.annotations.Scenario
 import org.gradle.performance.fixture.AndroidTestProject
+import org.gradle.profiler.BuildMutator
+import org.gradle.profiler.InvocationSettings
+import org.gradle.profiler.ScenarioContext
 
 import static org.gradle.performance.annotations.ScenarioType.PER_COMMIT
 import static org.gradle.performance.results.OperatingSystem.LINUX
@@ -31,22 +35,25 @@
 class RealLifeAndroidStudioPerformanceTest extends AbstractCrossVersionPerformanceTest implements AndroidPerformanceTestFixture {
 
     /**
-     * To run this test locally you should have Android Studio installed in /Applications/Android Studio.*.app folder
-     * or you should set "studioHome" system property with the Android Studio installation path.
+     * To run this test locally you should have Android Studio installed in /Applications/Android Studio.*.app folder,
+     * or you should set "studioHome" system property with the Android Studio installation path,
+     * or you should enable automatic download of Android Studio with the -PautoDownloadAndroidStudio=true.
+     *
+     * Additionally, you should also have ANDROID_SDK_ROOT env. variable set with Android SDK (normally on MacOS it's installed in "$HOME/Library/Android/sdk").
+     *
+     * To enable headless mode run with -PrunAndroidStudioInHeadlessMode=true.
      */
     def "run Android Studio sync"() {
         given:
         runner.args = [AndroidGradlePluginVersions.OVERRIDE_VERSION_CHECK]
         def testProject = AndroidTestProject.projectFor(runner.testProject)
         testProject.configure(runner)
-        AndroidTestProject.useStableAgpVersion(runner)
-        AndroidTestProject.useStableKotlinVersion(runner)
+        AndroidTestProject.useAgpLatestStableOrRcVersion(runner)
+        AndroidTestProject.useKotlinLatestStableOrRcVersion(runner)
         runner.warmUpRuns = 20
         runner.runs = 20
-        // AGP 7.3 requires Gradle 7.4
-        runner.minimumBaseVersion = "7.4"
         runner.setupAndroidStudioSync()
-        configureProjectJavaHomeToJdk11()
+        configureLocalProperties()
 
         when:
         def result = runner.run()
@@ -54,4 +61,26 @@
         then:
         result.assertCurrentVersionHasNotRegressed()
     }
+
+    void configureLocalProperties() {
+        assert System.getenv("ANDROID_SDK_ROOT") != null
+        String androidSdkRootPath = System.getenv("ANDROID_SDK_ROOT")
+        runner.addBuildMutator { invocation -> new LocalPropertiesMutator(invocation, FilenameUtils.separatorsToUnix(androidSdkRootPath)) }
+    }
+
+    static class LocalPropertiesMutator implements BuildMutator {
+        private final String androidSdkRootPath
+        private final InvocationSettings invocation
+
+        LocalPropertiesMutator(InvocationSettings invocation, String androidSdkRootPath) {
+            this.invocation = invocation
+            this.androidSdkRootPath = androidSdkRootPath
+        }
+
+        @Override
+        void beforeScenario(ScenarioContext context) {
+            def localProperties = new File(invocation.projectDir, "local.properties")
+            localProperties << "\nsdk.dir=$androidSdkRootPath\n"
+        }
+    }
 }
diff --git a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/corefeature/AbstractIncrementalExecutionPerformanceTest.groovy b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/corefeature/AbstractIncrementalExecutionPerformanceTest.groovy
index 80ed4dd..f56f9b4 100644
--- a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/corefeature/AbstractIncrementalExecutionPerformanceTest.groovy
+++ b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/corefeature/AbstractIncrementalExecutionPerformanceTest.groovy
@@ -35,7 +35,8 @@
     }
 
     protected boolean enableConfigurationCaching(boolean configurationCachingEnabled) {
-        runner.args.add("-D${StartParameterBuildOptions.ConfigurationCacheOption.PROPERTY_NAME}=${configurationCachingEnabled}")
+        // use the deprecated property so it works with previous versions
+        runner.args.add("-D${StartParameterBuildOptions.ConfigurationCacheOption.DEPRECATED_PROPERTY_NAME}=${configurationCachingEnabled}")
     }
 
     protected static configurationCachingMessage(boolean configurationCachingEnabled) {
diff --git a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/java/JavaConfigurationCachePerformanceTest.groovy b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/java/JavaConfigurationCachePerformanceTest.groovy
index cf97642..d8f1739 100644
--- a/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/java/JavaConfigurationCachePerformanceTest.groovy
+++ b/subprojects/performance/src/performanceTest/groovy/org/gradle/performance/regression/java/JavaConfigurationCachePerformanceTest.groovy
@@ -49,7 +49,13 @@
     def "assemble #action configuration cache state with #daemon daemon"() {
         given:
         runner.tasksToRun = ["assemble"]
-        runner.args = ["-D${ConfigurationCacheOption.PROPERTY_NAME}=true"]
+        // use the deprecated property so it works with previous versions
+        runner.args = ["-D${ConfigurationCacheOption.DEPRECATED_PROPERTY_NAME}=true"]
+
+        // Workaround to make that test work on Java 17
+        // Unable to make field private final java.lang.Object[] java.lang.invoke.SerializedLambda.capturedArgs accessible
+        // module java.base does not "opens java.lang.invoke" to unnamed module
+        runner.gradleOpts += ["--add-opens=java.base/java.lang.invoke=ALL-UNNAMED"]
 
         and:
         runner.useDaemon = daemon == hot
diff --git a/subprojects/performance/src/templates/native-component/build.gradle b/subprojects/performance/src/templates/native-component/build.gradle
index 3030328..80ea014 100644
--- a/subprojects/performance/src/templates/native-component/build.gradle
+++ b/subprojects/performance/src/templates/native-component/build.gradle
@@ -6,4 +6,4 @@
         lib${it+1}(NativeLibrarySpec)
         <% } %>
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/performance/src/templates/native-monolithic/build.gradle b/subprojects/performance/src/templates/native-monolithic/build.gradle
index 9e6d2a3..1030491 100644
--- a/subprojects/performance/src/templates/native-monolithic/build.gradle
+++ b/subprojects/performance/src/templates/native-monolithic/build.gradle
@@ -4,4 +4,4 @@
 }
 
 apply from: file("common.gradle")
-apply from: file("prebuilt.gradle")
\ No newline at end of file
+apply from: file("prebuilt.gradle")
diff --git a/subprojects/performance/src/templates/native-pch-component/build.gradle b/subprojects/performance/src/templates/native-pch-component/build.gradle
index f98a8c0..eff0b5d 100644
--- a/subprojects/performance/src/templates/native-pch-component/build.gradle
+++ b/subprojects/performance/src/templates/native-pch-component/build.gradle
@@ -37,4 +37,4 @@
         }
         <% } %>
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/performance/src/templates/project-with-source/Test.groovy b/subprojects/performance/src/templates/project-with-source/Test.groovy
index aeffc36..b7d847d 100644
--- a/subprojects/performance/src/templates/project-with-source/Test.groovy
+++ b/subprojects/performance/src/templates/project-with-source/Test.groovy
@@ -10,4 +10,4 @@
     public void test() {
         assertEquals(production.getProperty(), "value");
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/performance/src/templates/project-with-source/Test.java b/subprojects/performance/src/templates/project-with-source/Test.java
index 1918fb7..b1ccb01 100644
--- a/subprojects/performance/src/templates/project-with-source/Test.java
+++ b/subprojects/performance/src/templates/project-with-source/Test.java
@@ -9,4 +9,4 @@ public class ${testClassName} {
     public void test() {
         assertEquals(production.getProperty(), "value");
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/persistent-cache/src/integTest/groovy/org/gradle/cache/internal/DefaultFileLockManagerContentionIntegrationTest.groovy b/subprojects/persistent-cache/src/integTest/groovy/org/gradle/cache/internal/DefaultFileLockManagerContentionIntegrationTest.groovy
index f946d3f..a96ee5d 100644
--- a/subprojects/persistent-cache/src/integTest/groovy/org/gradle/cache/internal/DefaultFileLockManagerContentionIntegrationTest.groovy
+++ b/subprojects/persistent-cache/src/integTest/groovy/org/gradle/cache/internal/DefaultFileLockManagerContentionIntegrationTest.groovy
@@ -25,6 +25,7 @@
 import org.gradle.cache.internal.locklistener.FileLockContentionHandler
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleHandle
+import org.gradle.internal.agents.AgentStatus
 import org.gradle.internal.concurrent.DefaultExecutorFactory
 import org.gradle.internal.remote.internal.inet.InetAddressFactory
 import org.gradle.internal.service.ServiceRegistry
@@ -235,6 +236,7 @@
             import org.gradle.internal.service.scopes.GlobalScopeServices
             import org.gradle.workers.WorkParameters
             import ${GradleUserHomeScopeServices.name}
+            import ${AgentStatus.name}
 
             task doWorkInWorker(type: WorkerTask)
 
@@ -274,7 +276,7 @@
                     super(NativeServices.getInstance());
 
                     add(OutputEventListener.class, OutputEventListener.NO_OP);
-                    addProvider(new GlobalScopeServices(true));
+                    addProvider(new GlobalScopeServices(true, AgentStatus.disabled()));
                 }
 
                 public static ServiceRegistry getInstance(File gradleUserHome) {
diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/FileLockManager.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/FileLockManager.java
index 6d1c795..5836334 100644
--- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/FileLockManager.java
+++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/FileLockManager.java
@@ -16,10 +16,13 @@
 package org.gradle.cache;
 
 import org.gradle.api.Action;
+import org.gradle.internal.service.scopes.Scope;
+import org.gradle.internal.service.scopes.ServiceScope;
 
 import javax.annotation.Nullable;
 import java.io.File;
 
+@ServiceScope(Scope.Global.class)
 public interface FileLockManager {
     /**
      * Creates a lock for the given file with the given mode. Acquires a lock with the given mode, which is held until the lock is
@@ -60,7 +63,7 @@ public interface FileLockManager {
 
     enum LockMode {
         /**
-         * No synchronisation is done.
+         * Support processes asking for access.
          */
         OnDemand,
         /**
@@ -72,7 +75,7 @@ enum LockMode {
          */
         Exclusive,
         /**
-         * No locking whatsoever
+         * No locking whatsoever.
          */
         None
     }
diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/CacheInitializationAction.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/CacheInitializationAction.java
index f13bea9..5f1c6d7 100644
--- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/CacheInitializationAction.java
+++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/CacheInitializationAction.java
@@ -29,4 +29,15 @@ public interface CacheInitializationAction {
      * The lock is not released between calling {@link #requiresInitialization(FileLock)} and this method.
      */
     void initialize(FileLock fileLock);
+
+    CacheInitializationAction NO_INIT_REQUIRED = new CacheInitializationAction() {
+        @Override
+        public boolean requiresInitialization(FileLock fileLock) {
+            return false;
+        }
+
+        @Override
+        public void initialize(FileLock fileLock) {
+        }
+    };
 }
diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java
index 4d3778c..3d1aabe 100644
--- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java
+++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultDecompressionCache.java
@@ -39,7 +39,7 @@ public class DefaultDecompressionCache implements DecompressionCache {
     public DefaultDecompressionCache(ScopedCacheBuilderFactory cacheBuilderFactory) {
         this.cache = cacheBuilderFactory.createCrossVersionCacheBuilder(EXPANSION_CACHE_KEY)
                 .withDisplayName(EXPANSION_CACHE_NAME)
-                .withLockOptions(mode(FileLockManager.LockMode.OnDemand))
+                .withLockOptions(mode(FileLockManager.LockMode.OnDemand)) //TODO: the documentation above says this is a cross-version cache, not it's not! should it be?
                 .open();
     }
 
diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultPersistentDirectoryStore.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultPersistentDirectoryStore.java
index bffbdf7..364419e 100644
--- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultPersistentDirectoryStore.java
+++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/DefaultPersistentDirectoryStore.java
@@ -18,7 +18,6 @@
 import org.gradle.cache.CacheBuilder;
 import org.gradle.cache.CacheCleanupStrategy;
 import org.gradle.cache.CacheOpenException;
-import org.gradle.cache.FileLock;
 import org.gradle.cache.FileLockManager;
 import org.gradle.cache.IndexedCache;
 import org.gradle.cache.IndexedCacheParameters;
@@ -40,6 +39,8 @@
 import java.util.Collection;
 import java.util.concurrent.TimeUnit;
 
+import static org.gradle.cache.internal.CacheInitializationAction.NO_INIT_REQUIRED;
+
 public class DefaultPersistentDirectoryStore implements ReferencablePersistentCache {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPersistentDirectoryStore.class);
@@ -111,17 +112,7 @@ private File getLockTarget() {
     }
 
     protected CacheInitializationAction getInitAction() {
-        return new CacheInitializationAction() {
-            @Override
-            public boolean requiresInitialization(FileLock fileLock) {
-                return false;
-            }
-
-            @Override
-            public void initialize(FileLock fileLock) {
-                throw new UnsupportedOperationException();
-            }
-        };
+        return NO_INIT_REQUIRED;
     }
 
     protected CacheCleanupExecutor getCleanupExecutor() {
diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/ExclusiveCacheAccessingWorker.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/ExclusiveCacheAccessingWorker.java
index 4a09c7e..7165df5 100644
--- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/ExclusiveCacheAccessingWorker.java
+++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/ExclusiveCacheAccessingWorker.java
@@ -35,7 +35,7 @@
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 
-class ExclusiveCacheAccessingWorker implements Runnable, Stoppable, AsyncCacheAccess {
+public class ExclusiveCacheAccessingWorker implements Runnable, Stoppable, AsyncCacheAccess {
     private final BlockingQueue<Runnable> workQueue;
     private final String displayName;
     private final ExclusiveCacheAccessCoordinator cacheAccess;
@@ -47,7 +47,7 @@ class ExclusiveCacheAccessingWorker implements Runnable, Stoppable, AsyncCacheAc
     private final CountDownLatch doneSignal = new CountDownLatch(1);
     private final ExecutorPolicy.CatchAndRecordFailures failureHandler = new ExecutorPolicy.CatchAndRecordFailures();
 
-    ExclusiveCacheAccessingWorker(String displayName, ExclusiveCacheAccessCoordinator cacheAccess) {
+    public ExclusiveCacheAccessingWorker(String displayName, ExclusiveCacheAccessCoordinator cacheAccess) {
         this.displayName = displayName;
         this.cacheAccess = cacheAccess;
         this.batchWindowMillis = 200;
diff --git a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/LockOnDemandCrossProcessCacheAccess.java b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/LockOnDemandCrossProcessCacheAccess.java
index 3906659..3096171 100644
--- a/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/LockOnDemandCrossProcessCacheAccess.java
+++ b/subprojects/persistent-cache/src/main/java/org/gradle/cache/internal/LockOnDemandCrossProcessCacheAccess.java
@@ -29,7 +29,7 @@
 import java.io.File;
 import java.util.concurrent.locks.Lock;
 
-class LockOnDemandCrossProcessCacheAccess extends AbstractCrossProcessCacheAccess {
+public class LockOnDemandCrossProcessCacheAccess extends AbstractCrossProcessCacheAccess {
     private static final Logger LOGGER = LoggerFactory.getLogger(LockOnDemandCrossProcessCacheAccess.class);
     private final String cacheDisplayName;
     private final File lockTarget;
diff --git a/subprojects/persistent-cache/src/test/groovy/org/gradle/cache/internal/AbstractFileLockManagerTest.groovy b/subprojects/persistent-cache/src/test/groovy/org/gradle/cache/internal/AbstractFileLockManagerTest.groovy
index 78be0c4..f6f015d 100644
--- a/subprojects/persistent-cache/src/test/groovy/org/gradle/cache/internal/AbstractFileLockManagerTest.groovy
+++ b/subprojects/persistent-cache/src/test/groovy/org/gradle/cache/internal/AbstractFileLockManagerTest.groovy
@@ -29,8 +29,8 @@
 import org.gradle.internal.id.IdGenerator
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -407,7 +407,7 @@
         mode << [Shared, Exclusive]
     }
 
-    @Requires(TestPrecondition.NO_FILE_LOCK_ON_OPEN)
+    @Requires(UnitTestPreconditions.NoMandatoryFileLockOnOpen)
     def "writes lock file with info region while exclusive lock is open"() {
         expect:
         def lock = createLock(Exclusive)
@@ -419,7 +419,7 @@
         lock?.close()
     }
 
-    @Requires(TestPrecondition.NO_FILE_LOCK_ON_OPEN)
+    @Requires(UnitTestPreconditions.NoMandatoryFileLockOnOpen)
     def "writes dirty lock file with info region while updating file"() {
         given:
         writeFile()
@@ -441,7 +441,7 @@
         lock?.close()
     }
 
-    @Requires(TestPrecondition.NO_FILE_LOCK_ON_OPEN)
+    @Requires(UnitTestPreconditions.NoMandatoryFileLockOnOpen)
     def "long descriptor strings are trimmed when written to information region"() {
         setup:
         def customMetaDataProvider = Mock(ProcessMetaDataProvider)
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/InternalViewsSampleIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/InternalViewsSampleIntegrationTest.groovy
index 3cf5bf6..65f405d 100644
--- a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/InternalViewsSampleIntegrationTest.groovy
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/InternalViewsSampleIntegrationTest.groovy
@@ -20,11 +20,11 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 @UnsupportedWithConfigurationCache(because = "software model")
 class InternalViewsSampleIntegrationTest extends AbstractIntegrationSpec {
     @Rule
diff --git a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeSampleIntegrationTest.groovy b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeSampleIntegrationTest.groovy
index 5eb4ad6..cc126fc 100644
--- a/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeSampleIntegrationTest.groovy
+++ b/subprojects/platform-base/src/integTest/groovy/org/gradle/language/base/LanguageTypeSampleIntegrationTest.groovy
@@ -19,11 +19,11 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 @UnsupportedWithConfigurationCache(because = "software model")
 class LanguageTypeSampleIntegrationTest extends AbstractIntegrationSpec {
     @Rule
diff --git a/subprojects/platform-base/src/main/java/org/gradle/api/internal/plugins/NaggingBasePluginConvention.java b/subprojects/platform-base/src/main/java/org/gradle/api/internal/plugins/NaggingBasePluginConvention.java
new file mode 100644
index 0000000..34c00e3
--- /dev/null
+++ b/subprojects/platform-base/src/main/java/org/gradle/api/internal/plugins/NaggingBasePluginConvention.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 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.api.internal.plugins;
+
+import org.gradle.api.NonNullApi;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.internal.deprecation.DeprecationLogger;
+
+@SuppressWarnings("deprecation")
+@NonNullApi
+public class NaggingBasePluginConvention extends org.gradle.api.plugins.BasePluginConvention {
+
+    private final org.gradle.api.plugins.BasePluginConvention delegate;
+
+    public NaggingBasePluginConvention(org.gradle.api.plugins.BasePluginConvention delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public DirectoryProperty getDistsDirectory() {
+        logDeprecation();
+        return delegate.getDistsDirectory();
+    }
+
+    @Override
+    public DirectoryProperty getLibsDirectory() {
+        logDeprecation();
+        return delegate.getLibsDirectory();
+    }
+
+    @Override
+    public String getDistsDirName() {
+        logDeprecation();
+        return delegate.getDistsDirName();
+    }
+
+    @Override
+    public void setDistsDirName(String distsDirName) {
+        logDeprecation();
+        delegate.setDistsDirName(distsDirName);
+    }
+
+    @Override
+    public String getLibsDirName() {
+        logDeprecation();
+        return delegate.getLibsDirName();
+    }
+
+    @Override
+    public void setLibsDirName(String libsDirName) {
+        logDeprecation();
+        delegate.setLibsDirName(libsDirName);
+    }
+
+    @Override
+    public String getArchivesBaseName() {
+        logDeprecation();
+        return delegate.getArchivesBaseName();
+    }
+
+    @Override
+    public void setArchivesBaseName(String archivesBaseName) {
+        logDeprecation();
+        delegate.setArchivesBaseName(archivesBaseName);
+    }
+
+    private static void logDeprecation() {
+        DeprecationLogger.deprecateType(org.gradle.api.plugins.BasePluginConvention.class)
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "base_convention_deprecation")
+            .nagUser();
+    }
+}
diff --git a/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePlugin.java b/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePlugin.java
index a31f8ad..ce69a06 100644
--- a/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePlugin.java
+++ b/subprojects/platform-base/src/main/java/org/gradle/api/plugins/BasePlugin.java
@@ -24,9 +24,11 @@
 import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.plugins.BuildConfigurationRule;
 import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet;
+import org.gradle.api.internal.plugins.NaggingBasePluginConvention;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.plugins.internal.DefaultBasePluginExtension;
 import org.gradle.api.tasks.bundling.AbstractArchiveTask;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.language.base.plugins.LifecycleBasePlugin;
 
 /**
@@ -45,12 +47,7 @@ public void apply(final Project project) {
 
         BasePluginExtension baseExtension = project.getExtensions().create(BasePluginExtension.class, "base", DefaultBasePluginExtension.class, project);
 
-        @SuppressWarnings("deprecation")
-        BasePluginConvention convention = project.getObjects().newInstance(org.gradle.api.plugins.internal.DefaultBasePluginConvention.class, baseExtension);
-        @SuppressWarnings("deprecation")
-        Convention projectConvention = project.getConvention();
-        projectConvention.getPlugins().put("base", convention);
-
+        addConvention(project, baseExtension);
         configureExtension(project, baseExtension);
         configureBuildConfigurationRule(project);
         configureArchiveDefaults(project, baseExtension);
@@ -58,6 +55,15 @@ public void apply(final Project project) {
         configureAssemble((ProjectInternal) project);
     }
 
+
+    @SuppressWarnings("deprecation")
+    private void addConvention(Project project, BasePluginExtension baseExtension) {
+        BasePluginConvention convention = project.getObjects().newInstance(org.gradle.api.plugins.internal.DefaultBasePluginConvention.class, baseExtension);
+        DeprecationLogger.whileDisabled(() -> {
+            project.getConvention().getPlugins().put("base", new NaggingBasePluginConvention(convention));
+        });
+    }
+
     private void configureExtension(Project project, BasePluginExtension extension) {
         extension.getArchivesName().convention(project.getName());
         extension.getLibsDirectory().convention(project.getLayout().getBuildDirectory().dir("libs"));
@@ -81,12 +87,12 @@ private void configureBuildConfigurationRule(Project project) {
 
     private void configureConfigurations(final Project project) {
         RoleBasedConfigurationContainerInternal configurations = (RoleBasedConfigurationContainerInternal) project.getConfigurations();
-        ((ProjectInternal)project).getInternalStatus().convention("integration");
+        ((ProjectInternal) project).getInternalStatus().convention("integration");
 
-        final Configuration archivesConfiguration = configurations.maybeCreateWithRole(Dependency.ARCHIVES_CONFIGURATION, ConfigurationRoles.INTENDED_CONSUMABLE, false, false).
+        final Configuration archivesConfiguration = configurations.maybeCreateWithRole(Dependency.ARCHIVES_CONFIGURATION, ConfigurationRoles.CONSUMABLE, false, false).
             setDescription("Configuration for archive artifacts.");
 
-        final Configuration defaultConfiguration =  configurations.maybeCreateWithRole(Dependency.DEFAULT_CONFIGURATION, ConfigurationRoles.INTENDED_CONSUMABLE, false, false).
+        final Configuration defaultConfiguration = configurations.maybeCreateWithRole(Dependency.DEFAULT_CONFIGURATION, ConfigurationRoles.CONSUMABLE, false, false).
             setDescription("Configuration for default artifacts.");
 
         final DefaultArtifactPublicationSet defaultArtifacts = project.getExtensions().create(
diff --git a/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/package-info.java b/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/package-info.java
index 2b3361a..2a8ff2a 100644
--- a/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/package-info.java
+++ b/subprojects/platform-base/src/main/java/org/gradle/language/base/artifact/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Classes representing artifacts relevant to languages in general.
  */
-package org.gradle.language.base.artifact;
\ No newline at end of file
+package org.gradle.language.base.artifact;
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryTasksCollectionWrapper.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryTasksCollectionWrapper.java
index f07cff1..f5260b8 100644
--- a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryTasksCollectionWrapper.java
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/BinaryTasksCollectionWrapper.java
@@ -16,25 +16,17 @@
 
 package org.gradle.platform.base.internal;
 
-import groovy.lang.Closure;
 import org.gradle.api.Action;
-import org.gradle.api.DomainObjectCollection;
 import org.gradle.api.DomainObjectSet;
 import org.gradle.api.Task;
 import org.gradle.api.UnknownDomainObjectException;
-import org.gradle.api.provider.Provider;
-import org.gradle.api.specs.Spec;
+import org.gradle.api.internal.DelegatingDomainObjectSet;
 import org.gradle.platform.base.BinaryTasksCollection;
 
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Set;
-
-public class BinaryTasksCollectionWrapper implements BinaryTasksCollection {
-    private final BinaryTasksCollection delegate;
+public class BinaryTasksCollectionWrapper extends DelegatingDomainObjectSet<Task> implements BinaryTasksCollection {
 
     public BinaryTasksCollectionWrapper(BinaryTasksCollection delegate) {
-        this.delegate = delegate;
+        super(delegate);
     }
 
     public <T extends Task> T findSingleTaskWithType(Class<T> type) {
@@ -49,184 +41,33 @@ public <T extends Task> T findSingleTaskWithType(Class<T> type) {
     }
 
     @Override
+    protected BinaryTasksCollection getDelegate() {
+        return (BinaryTasksCollection) super.getDelegate();
+    }
+
+    @Override
     public String taskName(String verb) {
-        return delegate.taskName(verb);
+        return getDelegate().taskName(verb);
     }
 
     @Override
     public String taskName(String verb, String object) {
-        return delegate.taskName(verb, object);
+        return getDelegate().taskName(verb, object);
     }
 
     @Override
     public Task getBuild() {
-        return delegate.getBuild();
+        return getDelegate().getBuild();
     }
 
     @Override
     public Task getCheck() {
-        return delegate.getCheck();
+        return getDelegate().getCheck();
     }
 
     @Override
     public <T extends Task> void create(String name, Class<T> type, Action<? super T> config) {
-        delegate.create(name, type, config);
-    }
-
-    @Override
-    public <S extends Task> DomainObjectSet<S> withType(Class<S> type) {
-        return delegate.withType(type);
-    }
-
-    @Override
-    public DomainObjectSet<Task> matching(Spec<? super Task> spec) {
-        return delegate.matching(spec);
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Override
-    public DomainObjectSet<Task> matching(Closure spec) {
-        return delegate.matching(spec);
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Override
-    public Set<Task> findAll(Closure spec) {
-        return delegate.findAll(spec);
-    }
-
-    @Override
-    public <S extends Task> DomainObjectCollection<S> withType(Class<S> type, Action<? super S> configureAction) {
-        return delegate.withType(type, configureAction);
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Override
-    public <S extends Task> DomainObjectCollection<S> withType(Class<S> type, Closure configureClosure) {
-        return delegate.withType(type, configureClosure);
-    }
-
-    @Override
-    public Action<? super Task> whenObjectAdded(Action<? super Task> action) {
-        return delegate.whenObjectAdded(action);
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Override
-    public void whenObjectAdded(Closure action) {
-        delegate.whenObjectAdded(action);
-    }
-
-    @Override
-    public Action<? super Task> whenObjectRemoved(Action<? super Task> action) {
-        return delegate.whenObjectRemoved(action);
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Override
-    public void whenObjectRemoved(Closure action) {
-        delegate.whenObjectRemoved(action);
-    }
-
-    @Override
-    public void all(Action<? super Task> action) {
-        delegate.all(action);
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Override
-    public void all(Closure action) {
-        delegate.all(action);
-    }
-
-    @Override
-    public void configureEach(Action<? super Task> action) {
-        delegate.configureEach(action);
-    }
-
-    @Override
-    public int size() {
-        return delegate.size();
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return delegate.isEmpty();
-    }
-
-    @Override
-    public boolean contains(Object o) {
-        return delegate.contains(o);
-    }
-
-    @Override
-    public Iterator<Task> iterator() {
-        return delegate.iterator();
-    }
-
-    @Override
-    public Object[] toArray() {
-        return delegate.toArray();
-    }
-
-    @Override
-    public <T> T[] toArray(T[] a) {
-        return delegate.toArray(a);
-    }
-
-    @Override
-    public boolean add(Task task) {
-        return delegate.add(task);
-    }
-
-    @Override
-    public void addLater(Provider<? extends Task> provider) {
-        delegate.addLater(provider);
-    }
-
-    @Override
-    public void addAllLater(Provider<? extends Iterable<Task>> provider) {
-        delegate.addAllLater(provider);
-    }
-
-    @Override
-    public boolean remove(Object o) {
-        return delegate.remove(o);
-    }
-
-    @Override
-    public boolean containsAll(Collection<?> c) {
-        return delegate.containsAll(c);
-    }
-
-    @Override
-    public boolean addAll(Collection<? extends Task> c) {
-        return delegate.addAll(c);
-    }
-
-    @Override
-    public boolean removeAll(Collection<?> c) {
-        return delegate.removeAll(c);
-    }
-
-    @Override
-    public boolean retainAll(Collection<?> c) {
-        return delegate.retainAll(c);
-    }
-
-    @Override
-    public void clear() {
-        delegate.clear();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        return delegate.equals(o);
-    }
-
-    @Override
-    public int hashCode() {
-        return delegate.hashCode();
+        getDelegate().create(name, type, config);
     }
 
 }
diff --git a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/FixedBuildAbility.java b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/FixedBuildAbility.java
index 4aec6dc..7018f77 100644
--- a/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/FixedBuildAbility.java
+++ b/subprojects/platform-base/src/main/java/org/gradle/platform/base/internal/FixedBuildAbility.java
@@ -36,4 +36,4 @@ public void explain(DiagnosticsVisitor visitor) {
             visitor.node("Disabled by user");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadIntegrationTest.groovy
index a054a5b..2acf2f0 100644
--- a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadIntegrationTest.groovy
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadIntegrationTest.groovy
@@ -16,10 +16,19 @@
 
 package org.gradle.jvm.toolchain
 
+import net.rubygrapefruit.platform.internal.DefaultSystemInfo
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
+import org.gradle.integtests.fixtures.executer.DocumentationUtils
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.platform.internal.DefaultBuildPlatform
 import org.gradle.test.fixtures.file.TestFile
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+
 class JavaToolchainDownloadIntegrationTest extends AbstractIntegrationSpec {
 
     @ToBeFixedForConfigurationCache(because = "Fails the build with an additional error")
@@ -52,9 +61,15 @@
         failure.assertHasDescription("Execution failed for task ':compileJava'.")
             .assertHasCause("Error while evaluating property 'javaCompiler' of task ':compileJava'.")
             .assertHasCause("Failed to calculate the value of task ':compileJava' property 'javaCompiler'")
-            .assertHasCause("No matching toolchains found for requested specification: {languageVersion=14, vendor=ADOPTIUM, implementation=J9}.")
-            .assertHasDocumentedCause("No locally installed toolchains match (see https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection) " +
-                    "and the configured toolchain download repositories aren't able to provide a match either (see https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories).")
+            .assertHasCause("No matching toolchains found for requested specification: {languageVersion=14, vendor=ADOPTIUM, implementation=J9} ${getFailureMessageBuildPlatform()}.")
+            .assertHasCause("No locally installed toolchains match and the configured toolchain download repositories aren't able to provide a match either.")
+            .assertHasResolutions(
+                DocumentationUtils.normalizeDocumentationLink("Learn more about toolchain auto-detection at https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection."),
+                DocumentationUtils.normalizeDocumentationLink("Learn more about toolchain repositories at https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories."),
+                STACKTRACE_MESSAGE,
+                INFO_DEBUG,
+                SCAN,
+                GET_HELP)
     }
 
     @ToBeFixedForConfigurationCache(because = "Fails the build with an additional error")
@@ -87,9 +102,14 @@
         failure.assertHasDescription("Execution failed for task ':compileJava'.")
             .assertHasCause("Error while evaluating property 'javaCompiler' of task ':compileJava'.")
             .assertHasCause("Failed to calculate the value of task ':compileJava' property 'javaCompiler'")
-            .assertHasCause("No matching toolchains found for requested specification: {languageVersion=14, vendor=any, implementation=vendor-specific}.")
-            .assertHasDocumentedCause("No locally installed toolchains match (see https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection) " +
-                    "and toolchain auto-provisioning is not enabled (see https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection).")
+            .assertHasCause("No matching toolchains found for requested specification: {languageVersion=14, vendor=any, implementation=vendor-specific} ${getFailureMessageBuildPlatform()}.")
+            .assertHasCause("No locally installed toolchains match and toolchain auto-provisioning is not enabled.")
+            .assertHasResolutions(
+                DocumentationUtils.normalizeDocumentationLink("Learn more about toolchain auto-detection at https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection."),
+                STACKTRACE_MESSAGE,
+                INFO_DEBUG,
+                SCAN,
+                GET_HELP)
     }
 
     @ToBeFixedForConfigurationCache(because = "Fails the build with an additional error")
@@ -136,16 +156,16 @@
             public abstract class CustomToolchainResolverPlugin implements Plugin<Settings> {
                 @Inject
                 protected abstract JavaToolchainResolverRegistry getToolchainResolverRegistry();
-            
+
                 void apply(Settings settings) {
                     settings.getPlugins().apply("jvm-toolchain-management");
-                
+
                     JavaToolchainResolverRegistry registry = getToolchainResolverRegistry();
                     registry.register(CustomToolchainResolver.class);
                 }
             }
-            
-            
+
+
             import java.util.Optional;
             import org.gradle.platform.BuildPlatform;
 
@@ -156,10 +176,10 @@
                     return Optional.of(JavaToolchainDownload.fromUri(uri));
                 }
             }
-            
+
 
             apply plugin: CustomToolchainResolverPlugin
-                       
+
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -172,4 +192,9 @@
         """
     }
 
+    private def getFailureMessageBuildPlatform() {
+        def buildPlatform = new DefaultBuildPlatform(new DefaultSystemInfo(), OperatingSystem.current())
+        return "for ${buildPlatform.operatingSystem} on ${buildPlatform.architecture.toString().toLowerCase()}"
+    }
+
 }
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiAuthenticationIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiAuthenticationIntegrationTest.groovy
index 2703f45..f821cc5 100644
--- a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiAuthenticationIntegrationTest.groovy
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiAuthenticationIntegrationTest.groovy
@@ -154,4 +154,4 @@
             }
             """
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiIntegrationTest.groovy
index 57d9894..955d3dc 100644
--- a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiIntegrationTest.groovy
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiIntegrationTest.groovy
@@ -18,15 +18,21 @@
 
 import net.rubygrapefruit.platform.SystemInfo
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
+import org.gradle.integtests.fixtures.executer.DocumentationUtils
 import org.gradle.internal.nativeintegration.services.NativeServices
 import org.gradle.internal.os.OperatingSystem
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+
 class JavaToolchainDownloadSpiIntegrationTest extends AbstractJavaToolchainDownloadSpiIntegrationTest {
 
     @ToBeFixedForConfigurationCache(because = "Fails the build with an additional error")
     def "can inject custom toolchain registry via settings plugin"() {
         settingsFile << """
-            ${applyToolchainResolverPlugin("CustomToolchainResolver", customToolchainResolverCode())}               
+            ${applyToolchainResolverPlugin("CustomToolchainResolver", customToolchainResolverCode())}
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -68,7 +74,7 @@
     @ToBeFixedForConfigurationCache(because = "Fails the build with an additional error")
     def "downloaded JDK is checked against the spec"() {
         settingsFile << """
-            ${applyToolchainResolverPlugin("BrokenToolchainResolver", brokenToolchainResolverCode())}               
+            ${applyToolchainResolverPlugin("BrokenToolchainResolver", brokenToolchainResolverCode())}
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -111,7 +117,7 @@
     def "custom toolchain registries are consulted in order"() {
         settingsFile << """
             ${applyToolchainResolverPlugin("CustomToolchainResolver", customToolchainResolverCode())}
-            ${applyToolchainResolverPlugin("UselessToolchainResolver", uselessToolchainResolverCode("UselessToolchainResolver"))}            
+            ${applyToolchainResolverPlugin("UselessToolchainResolver", uselessToolchainResolverCode("UselessToolchainResolver"))}
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -197,7 +203,7 @@
 
     def "fails on implementation class collision"() {
         settingsFile << """
-            ${applyToolchainResolverPlugin("UselessToolchainResolver", uselessToolchainResolverCode("UselessToolchainResolver"))}            
+            ${applyToolchainResolverPlugin("UselessToolchainResolver", uselessToolchainResolverCode("UselessToolchainResolver"))}
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -232,13 +238,13 @@
                 .runWithFailure()
 
         then:
-        failure.assertHasCause("Duplicate configuration for repository implementation 'UselessToolchainResolver'.")
+        failure.assertHasDescription("Duplicate configuration for repository implementation 'UselessToolchainResolver'.")
     }
 
     def "fails on repository name collision"() {
         settingsFile << """
-            ${applyToolchainResolverPlugin("UselessToolchainResolver1", uselessToolchainResolverCode("UselessToolchainResolver1"))}            
-            ${applyToolchainResolverPlugin("UselessToolchainResolver2", uselessToolchainResolverCode("UselessToolchainResolver2"))}            
+            ${applyToolchainResolverPlugin("UselessToolchainResolver1", uselessToolchainResolverCode("UselessToolchainResolver1"))}
+            ${applyToolchainResolverPlugin("UselessToolchainResolver2", uselessToolchainResolverCode("UselessToolchainResolver2"))}
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -278,9 +284,9 @@
 
     def "list of requested repositories can be queried"() {
         settingsFile << """
-            ${applyToolchainResolverPlugin("UselessToolchainResolver1", uselessToolchainResolverCode("UselessToolchainResolver1"))}            
-            ${applyToolchainResolverPlugin("UselessToolchainResolver2", uselessToolchainResolverCode("UselessToolchainResolver2"))}            
-            ${applyToolchainResolverPlugin("UselessToolchainResolver3", uselessToolchainResolverCode("UselessToolchainResolver3"))}            
+            ${applyToolchainResolverPlugin("UselessToolchainResolver1", uselessToolchainResolverCode("UselessToolchainResolver1"))}
+            ${applyToolchainResolverPlugin("UselessToolchainResolver2", uselessToolchainResolverCode("UselessToolchainResolver2"))}
+            ${applyToolchainResolverPlugin("UselessToolchainResolver3", uselessToolchainResolverCode("UselessToolchainResolver3"))}
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -293,7 +299,7 @@
                     }
                 }
             }
-            
+
             println(\"\"\"Explicitly requested toolchains: \${toolchainManagement.jvm.getJavaRepositories().getAsList().collect { it.getName() }}.\"\"\")
         """
 
@@ -322,9 +328,9 @@
 
     def "created repository can be removed"() {
         settingsFile << """
-            ${applyToolchainResolverPlugin("UselessToolchainResolver1", uselessToolchainResolverCode("UselessToolchainResolver1"))}            
-            ${applyToolchainResolverPlugin("UselessToolchainResolver2", uselessToolchainResolverCode("UselessToolchainResolver2"))}            
-            ${applyToolchainResolverPlugin("UselessToolchainResolver3", uselessToolchainResolverCode("UselessToolchainResolver3"))}            
+            ${applyToolchainResolverPlugin("UselessToolchainResolver1", uselessToolchainResolverCode("UselessToolchainResolver1"))}
+            ${applyToolchainResolverPlugin("UselessToolchainResolver2", uselessToolchainResolverCode("UselessToolchainResolver2"))}
+            ${applyToolchainResolverPlugin("UselessToolchainResolver3", uselessToolchainResolverCode("UselessToolchainResolver3"))}
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -340,9 +346,9 @@
                     }
                 }
             }
-            
+
             toolchainManagement.jvm.javaRepositories.remove('useless2')
-            
+
             println(\"\"\"Explicitly requested toolchains: \${toolchainManagement.jvm.getJavaRepositories().getAsList().collect { it.getName() }}.\"\"\")
         """
 
@@ -371,7 +377,7 @@
 
     def "cannot mutate repository rules after settings have been evaluated"() {
         settingsFile << """
-            ${applyToolchainResolverPlugin("UselessToolchainResolver", uselessToolchainResolverCode("UselessToolchainResolver"))}            
+            ${applyToolchainResolverPlugin("UselessToolchainResolver", uselessToolchainResolverCode("UselessToolchainResolver"))}
             toolchainManagement {
                 jvm {
                     javaRepositories {
@@ -397,7 +403,7 @@
     @ToBeFixedForConfigurationCache(because = "Fails the build with an additional error")
     def "throws informative error on repositories not being configured"() {
         settingsFile << """
-            ${applyToolchainResolverPlugin("CustomToolchainResolver", customToolchainResolverCode())} 
+            ${applyToolchainResolverPlugin("CustomToolchainResolver", customToolchainResolverCode())}
         """
 
         buildFile << """
@@ -423,8 +429,14 @@
         failure.assertHasDescription("Execution failed for task ':compileJava'.")
                 .assertHasCause("Error while evaluating property 'javaCompiler' of task ':compileJava'.")
                 .assertHasCause("Failed to calculate the value of task ':compileJava' property 'javaCompiler'.")
-                .assertHasDocumentedCause("No locally installed toolchains match (see https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection) " +
-                        "and toolchain download repositories have not been configured (see https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories).")
+                .assertHasCause("No locally installed toolchains match and toolchain download repositories have not been configured.")
+                .assertHasResolutions(
+                    DocumentationUtils.normalizeDocumentationLink("Learn more about toolchain auto-detection at https://docs.gradle.org/current/userguide/toolchains.html#sec:auto_detection."),
+                    DocumentationUtils.normalizeDocumentationLink("Learn more about toolchain repositories at https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories."),
+                    STACKTRACE_MESSAGE,
+                    INFO_DEBUG,
+                    SCAN,
+                    GET_HELP)
     }
 
     private static String customToolchainResolverCode() {
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiKotlinIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiKotlinIntegrationTest.groovy
index a99e422..8e6edcd 100644
--- a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiKotlinIntegrationTest.groovy
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/JavaToolchainDownloadSpiKotlinIntegrationTest.groovy
@@ -100,4 +100,4 @@
             """
     }
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/ShowToolchainsIntegrationTest.groovy b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/ShowToolchainsIntegrationTest.groovy
index 680181e..4fc0fcd 100644
--- a/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/ShowToolchainsIntegrationTest.groovy
+++ b/subprojects/platform-jvm/src/integTest/groovy/org/gradle/jvm/toolchain/ShowToolchainsIntegrationTest.groovy
@@ -26,8 +26,7 @@
         settingsFile << """
                 include 'a', 'b', 'c'
             """
-        buildFile << """
-"""
+        buildFile << ""
         then:
         run("javaToolchains")
 
@@ -43,4 +42,16 @@
         outputContains("javaToolchains - Displays the detected java toolchains.")
     }
 
+    def "toolchains log contains progress info about installation suppliers"() {
+        when:
+        settingsFile << """
+                include 'a'
+            """
+        buildFile << ""
+        then:
+        run("javaToolchains", "--debug")
+
+        outputContains(":javaToolchains")
+        outputContains("Discovering toolchains provided via Maven Toolchains")
+    }
 }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/api/internal/tasks/DefaultSourceSet.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/internal/tasks/DefaultSourceSet.java
index 79b8f8b..085f65e 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/api/internal/tasks/DefaultSourceSet.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/api/internal/tasks/DefaultSourceSet.java
@@ -141,7 +141,13 @@ private String getTaskBaseName() {
         return baseName;
     }
 
-    private String configurationNameOf(String baseName) {
+    /**
+     * Determines the name of a configuration owned by this source set, with the given {@code baseName}.
+     *
+     * <p>If this is the main source set, returns the uncapitalized {@code baseName}, otherwise, returns the
+     * base name prefixed with this source set's name.</p>
+     */
+    public String configurationNameOf(String baseName) {
         return StringUtils.uncapitalize(getTaskBaseName() + StringUtils.capitalize(baseName));
     }
 
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestException.java b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestException.java
index d757f4e..2265c4e 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestException.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/api/java/archives/ManifestException.java
@@ -29,4 +29,4 @@ public ManifestException(String message) {
     public ManifestException(String message, Throwable cause) {
         super(message, cause);
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/services/PlatformJvmServices.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/services/PlatformJvmServices.java
index cac5402..57a8662 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/services/PlatformJvmServices.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/internal/services/PlatformJvmServices.java
@@ -26,6 +26,7 @@
 import org.gradle.internal.authentication.AuthenticationSchemeRegistry;
 import org.gradle.internal.jvm.inspection.ConditionalInvalidation;
 import org.gradle.internal.jvm.inspection.InvalidJvmInstallationCacheInvalidator;
+import org.gradle.internal.jvm.inspection.JavaInstallationRegistry;
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
 import org.gradle.internal.jvm.inspection.JvmMetadataDetector;
 import org.gradle.internal.operations.BuildOperationExecutor;
@@ -44,8 +45,6 @@
 import org.gradle.jvm.toolchain.internal.InstallationSupplier;
 import org.gradle.jvm.toolchain.internal.IntellijInstallationSupplier;
 import org.gradle.jvm.toolchain.internal.JabbaInstallationSupplier;
-import org.gradle.jvm.toolchain.internal.JavaInstallationRegistry;
-import org.gradle.jvm.toolchain.internal.JavaToolchainFactory;
 import org.gradle.jvm.toolchain.internal.JavaToolchainQueryService;
 import org.gradle.jvm.toolchain.internal.LinuxInstallationSupplier;
 import org.gradle.jvm.toolchain.internal.LocationListInstallationSupplier;
@@ -115,7 +114,6 @@ private void registerJavaInstallationSuppliers(ServiceRegistration registration)
 
     @Override
     public void registerProjectServices(ServiceRegistration registration) {
-        registration.add(JavaToolchainFactory.class);
         registration.add(DefaultJavaToolchainProvisioningService.class);
         registration.add(SecureFileDownloader.class);
         registration.add(JavaToolchainQueryService.class);
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/AutoInstalledInstallationSupplier.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/AutoInstalledInstallationSupplier.java
index 99465ba..a87fdea 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/AutoInstalledInstallationSupplier.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/AutoInstalledInstallationSupplier.java
@@ -37,6 +37,11 @@ public AutoInstalledInstallationSupplier(ProviderFactory factory, JdkCacheDirect
     }
 
     @Override
+    public String getSourceName() {
+        return "Auto-provisioned by Gradle";
+    }
+
+    @Override
     protected Set<InstallationLocation> findCandidates() {
         return cacheDirProvider.listJavaHomes().stream()
             .map(this::asInstallation)
@@ -44,7 +49,7 @@ protected Set<InstallationLocation> findCandidates() {
     }
 
     private InstallationLocation asInstallation(File javaHome) {
-        return new InstallationLocation(javaHome, "Auto-provisioned by Gradle", true);
+        return new InstallationLocation(javaHome, getSourceName(), true);
     }
 
     @Override
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolchainResolverRegistry.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolchainResolverRegistry.java
index a153a02..4c25cde 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolchainResolverRegistry.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolchainResolverRegistry.java
@@ -87,6 +87,13 @@ public List<RealizedJavaToolchainRepository> requestedRepositories() {
         return realizedRepositories;
     }
 
+    @Override
+    public void preventFromFurtherMutation() {
+        repositoryHandler.preventFromFurtherMutation();
+        // This makes sure all configured elements have been transformed in their internal representation for later use
+        realizeRepositories();
+    }
+
     private void realizeRepositories() {
         realizedRepositories.clear();
 
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolchainService.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolchainService.java
index b0a44a1..ed6665d 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolchainService.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJavaToolchainService.java
@@ -19,6 +19,7 @@
 import org.gradle.api.Action;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.Provider;
+import org.gradle.internal.operations.BuildOperationProgressEventEmitter;
 import org.gradle.jvm.toolchain.JavaCompiler;
 import org.gradle.jvm.toolchain.JavaLauncher;
 import org.gradle.jvm.toolchain.JavaToolchainService;
@@ -32,11 +33,23 @@ public class DefaultJavaToolchainService implements JavaToolchainService {
 
     private final JavaToolchainQueryService queryService;
     private final ObjectFactory objectFactory;
+    private final JavaCompilerFactory compilerFactory;
+    private final ToolchainToolFactory toolFactory;
+    private final BuildOperationProgressEventEmitter eventEmitter;
 
     @Inject
-    public DefaultJavaToolchainService(JavaToolchainQueryService queryService, ObjectFactory objectFactory) {
+    public DefaultJavaToolchainService(
+        JavaToolchainQueryService queryService,
+        ObjectFactory objectFactory,
+        JavaCompilerFactory compilerFactory,
+        ToolchainToolFactory toolFactory,
+        BuildOperationProgressEventEmitter eventEmitter
+    ) {
         this.queryService = queryService;
         this.objectFactory = objectFactory;
+        this.compilerFactory = compilerFactory;
+        this.toolFactory = toolFactory;
+        this.eventEmitter = eventEmitter;
     }
 
     @Override
@@ -46,7 +59,9 @@ public Provider<JavaCompiler> compilerFor(Action<? super JavaToolchainSpec> conf
 
     @Override
     public Provider<JavaCompiler> compilerFor(JavaToolchainSpec spec) {
-        return queryService.toolFor(spec, JavaToolchain::getJavaCompiler, JavaTool.COMPILER);
+        return queryService.findMatchingToolchain(spec)
+            .withSideEffect(toolchain -> emitEvent(toolchain, JavaTool.COMPILER))
+            .map(javaToolchain -> new DefaultToolchainJavaCompiler(javaToolchain, compilerFactory));
     }
 
     @Override
@@ -56,7 +71,9 @@ public Provider<JavaLauncher> launcherFor(Action<? super JavaToolchainSpec> conf
 
     @Override
     public Provider<JavaLauncher> launcherFor(JavaToolchainSpec spec) {
-        return queryService.toolFor(spec, JavaToolchain::getJavaLauncher, JavaTool.LAUNCHER);
+        return queryService.findMatchingToolchain(spec)
+            .withSideEffect(toolchain -> emitEvent(toolchain, JavaTool.LAUNCHER))
+            .map(DefaultToolchainJavaLauncher::new);
     }
 
     @Override
@@ -66,7 +83,9 @@ public Provider<JavadocTool> javadocToolFor(Action<? super JavaToolchainSpec> co
 
     @Override
     public Provider<JavadocTool> javadocToolFor(JavaToolchainSpec spec) {
-        return queryService.toolFor(spec, JavaToolchain::getJavadocTool, JavaTool.JAVADOC);
+        return queryService.findMatchingToolchain(spec)
+            .withSideEffect(toolchain -> emitEvent(toolchain, JavaTool.JAVADOC))
+            .map(javaToolchain -> toolFactory.create(JavadocTool.class, javaToolchain));
     }
 
     private DefaultToolchainSpec configureToolchainSpec(Action<? super JavaToolchainSpec> config) {
@@ -74,4 +93,9 @@ private DefaultToolchainSpec configureToolchainSpec(Action<? super JavaToolchain
         config.execute(toolchainSpec);
         return toolchainSpec;
     }
+
+    private void emitEvent(JavaToolchain toolchain, JavaTool toolName) {
+        eventEmitter.emitNowForCurrent(new DefaultJavaToolchainUsageProgressDetails(toolName, toolchain.getMetadata()));
+    }
+
 }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJvmToolchainManagement.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJvmToolchainManagement.java
index 783b570..7231c08 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJvmToolchainManagement.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/DefaultJvmToolchainManagement.java
@@ -44,6 +44,6 @@ public void javaRepositories(Action<? super JavaToolchainRepositoryHandler> conf
 
     @Override
     public void preventFromFurtherMutation() {
-        registry.getRepositories().preventFromFurtherMutation();
+        registry.preventFromFurtherMutation();
     }
 }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaExecutableUtils.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaExecutableUtils.java
index a0ca762..6368f9c 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaExecutableUtils.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaExecutableUtils.java
@@ -36,35 +36,46 @@ public static File resolveExecutable(String executable) {
                     .withContext("Resolving relative file paths might yield unexpected results, there is no single clear location it would make sense to resolve against.")
                     .withAdvice("Configure an absolute path to a Java executable instead.")
                     .willBecomeAnErrorInGradle9()
-                    .undocumented()
+                    .withUpgradeGuideSection(8, "no_relative_paths_for_java_executables")
                     .nagUser();
         }
-        if (!executableFile.getAbsoluteFile().exists()) {
+        File executableAbsoluteFile = executableFile.getAbsoluteFile();
+        if (!executableAbsoluteFile.exists()) {
             throw new InvalidUserDataException("The configured executable does not exist (" + executableFile.getAbsolutePath() + ")");
         }
-        if (executableFile.getAbsoluteFile().isDirectory()) {
+        if (executableAbsoluteFile.isDirectory()) {
             throw new InvalidUserDataException("The configured executable is a directory (" + executableFile.getAbsolutePath() + ")");
         }
-        return executableFile;
+        return executableAbsoluteFile;
     }
 
+    public static File resolveJavaHomeOfExecutable(String executable) {
+        // Relying on the layout of the toolchain distribution: <JAVA HOME>/bin/<executable>
+        return resolveExecutable(executable).getParentFile().getParentFile();
+    }
+
+
     public static void validateExecutable(@Nullable String executable, String executableDescription, File referenceFile, String referenceDescription) {
         if (executable == null) {
             return;
         }
 
         File executableFile = resolveExecutable(executable);
-        if (executableFile.equals(referenceFile)) {
+        validateMatchingFiles(executableFile, executableDescription, referenceFile, referenceDescription);
+    }
+
+    public static void validateMatchingFiles(File customFile, String customDescription, File referenceFile, String referenceDescription) {
+        if (customFile.equals(referenceFile)) {
             return;
         }
 
-        File canonicalExecutableFile = canonicalFile(executableFile);
+        File canonicalCustomFile = canonicalFile(customFile);
         File canonicalReferenceFile = canonicalFile(referenceFile);
-        if (canonicalExecutableFile.equals(canonicalReferenceFile)) {
+        if (canonicalCustomFile.equals(canonicalReferenceFile)) {
             return;
         }
 
-        throw new IllegalStateException(executableDescription + " does not match " + referenceDescription + ".");
+        throw new IllegalStateException(customDescription + " does not match " + referenceDescription + ".");
     }
 
     private static File canonicalFile(File file) {
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchain.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchain.java
index 80f7a5c..b6282a8 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchain.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchain.java
@@ -24,16 +24,9 @@
 import org.gradle.api.tasks.Nested;
 import org.gradle.internal.jvm.Jvm;
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
-import org.gradle.internal.operations.BuildOperationProgressEventEmitter;
 import org.gradle.internal.os.OperatingSystem;
-import org.gradle.jvm.toolchain.JavaCompiler;
 import org.gradle.jvm.toolchain.JavaInstallationMetadata;
 import org.gradle.jvm.toolchain.JavaLanguageVersion;
-import org.gradle.jvm.toolchain.JavaLauncher;
-import org.gradle.jvm.toolchain.JavadocTool;
-import org.gradle.util.internal.VersionNumber;
-
-import java.nio.file.Path;
 
 public class JavaToolchain implements Describable, JavaInstallationMetadata {
 
@@ -41,34 +34,23 @@ static JavaLanguageVersion getJavaLanguageVersion(JvmInstallationMetadata metada
         return JavaLanguageVersion.of(metadata.getLanguageVersion().getMajorVersion());
     }
 
-    private final JavaCompilerFactory compilerFactory;
-    private final ToolchainToolFactory toolFactory;
     private final Directory javaHome;
-    private final VersionNumber toolchainVersion;
     private final JavaLanguageVersion javaVersion;
     private final JvmInstallationMetadata metadata;
     private final JavaToolchainInput input;
     private final boolean isFallbackToolchain;
-    private final BuildOperationProgressEventEmitter eventEmitter;
 
     public JavaToolchain(
         JvmInstallationMetadata metadata,
-        JavaCompilerFactory compilerFactory,
-        ToolchainToolFactory toolFactory,
         FileFactory fileFactory,
         JavaToolchainInput input,
-        boolean isFallbackToolchain,
-        BuildOperationProgressEventEmitter eventEmitter
+        boolean isFallbackToolchain
     ) {
-        this.isFallbackToolchain = isFallbackToolchain;
-        this.javaHome = fileFactory.dir(computeEnclosingJavaHome(metadata.getJavaHome()).toFile());
+        this.javaHome = fileFactory.dir(metadata.getJavaHome().toFile());
         this.javaVersion = getJavaLanguageVersion(metadata);
-        this.compilerFactory = compilerFactory;
-        this.toolFactory = toolFactory;
-        this.toolchainVersion = VersionNumber.withPatchNumber().parse(metadata.getJavaVersion());
         this.metadata = metadata;
         this.input = input;
-        this.eventEmitter = eventEmitter;
+        this.isFallbackToolchain = isFallbackToolchain;
     }
 
     @Nested
@@ -76,21 +58,6 @@ protected JavaToolchainInput getTaskInputs() {
         return input;
     }
 
-    @Internal
-    public JavaCompiler getJavaCompiler() {
-        return new DefaultToolchainJavaCompiler(this, compilerFactory);
-    }
-
-    @Internal
-    public JavaLauncher getJavaLauncher() {
-        return new DefaultToolchainJavaLauncher(this);
-    }
-
-    @Internal
-    public JavadocTool getJavadocTool() {
-        return toolFactory.create(JavadocTool.class, this);
-    }
-
     @Override
     public JavaLanguageVersion getLanguageVersion() {
         return javaVersion;
@@ -108,21 +75,11 @@ public String getJvmVersion() {
     }
 
     @Internal
-    public VersionNumber getToolchainVersion() {
-        return toolchainVersion;
-    }
-
-    @Internal
     public Directory getInstallationPath() {
         return javaHome;
     }
 
     @Internal
-    public boolean isJdk() {
-        return metadata.hasCapability(JvmInstallationMetadata.JavaInstallationCapability.JAVA_COMPILER);
-    }
-
-    @Internal
     @Override
     public boolean isCurrentJvm() {
         return javaHome.getAsFile().equals(Jvm.current().getJavaHome());
@@ -153,24 +110,11 @@ public RegularFile findExecutable(String toolName) {
         return getInstallationPath().file(getBinaryPath(toolName));
     }
 
-    public void emitUsageEvent(DefaultJavaToolchainUsageProgressDetails.JavaTool javaTool) {
-        eventEmitter.emitNowForCurrent(new DefaultJavaToolchainUsageProgressDetails(javaTool, metadata));
-    }
-
     @Override
     public String toString() {
         return "JavaToolchain(javaHome=" + getDisplayName() + ")";
     }
 
-    private Path computeEnclosingJavaHome(Path home) {
-        final Path parentPath = home.getParent();
-        final boolean isEmbeddedJre = home.getFileName().toString().equalsIgnoreCase("jre");
-        if (isEmbeddedJre && parentPath.resolve(getBinaryPath("java")).toFile().exists()) {
-            return parentPath;
-        }
-        return home;
-    }
-
     private String getBinaryPath(String java) {
         return "bin/" + OperatingSystem.current().getExecutableName(java);
     }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainComparator.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainComparator.java
deleted file mode 100644
index 9a229a3..0000000
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainComparator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020 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.jvm.toolchain.internal;
-
-import org.gradle.internal.jvm.inspection.JvmVendor;
-
-import java.io.File;
-import java.util.Comparator;
-
-public class JavaToolchainComparator implements Comparator<JavaToolchain> {
-
-    public static Comparator<? super JavaToolchainInstantiationResult> forInstantiationResult() {
-        return Comparator.comparing(r -> r.getToolchain().get(), new JavaToolchainComparator());
-    }
-
-    @Override
-    public int compare(JavaToolchain o1, JavaToolchain o2) {
-        return Comparator
-            .comparing(JavaToolchain::isCurrentJvm)
-            .thenComparing(JavaToolchain::isJdk)
-            .thenComparing(this::extractVendor, Comparator.reverseOrder())
-            .thenComparing(JavaToolchain::getToolchainVersion)
-            // It is possible for different JDK builds to have exact same version. The input order
-            // may change so the installation path breaks ties to keep sorted output consistent
-            // between runs.
-            .thenComparing(this::extractInstallationPathAsFile)
-            .reversed()
-            .compare(o1, o2);
-    }
-
-    private JvmVendor.KnownJvmVendor extractVendor(JavaToolchain toolchain) {
-        return toolchain.getMetadata().getVendor().getKnownVendor();
-    }
-
-    private File extractInstallationPathAsFile(JavaToolchain javaToolchain) {
-        return javaToolchain.getInstallationPath().getAsFile();
-    }
-}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainFactory.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainFactory.java
deleted file mode 100644
index 851a57d..0000000
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainFactory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2020 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.jvm.toolchain.internal;
-
-import org.gradle.api.internal.file.FileFactory;
-import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
-import org.gradle.internal.jvm.inspection.JvmMetadataDetector;
-import org.gradle.internal.operations.BuildOperationProgressEventEmitter;
-
-import javax.inject.Inject;
-
-public class JavaToolchainFactory {
-
-    private final JavaCompilerFactory compilerFactory;
-    private final ToolchainToolFactory toolFactory;
-    private final FileFactory fileFactory;
-    private final JvmMetadataDetector detector;
-    private final BuildOperationProgressEventEmitter eventEmitter;
-
-    @Inject
-    public JavaToolchainFactory(
-        JvmMetadataDetector detector,
-        JavaCompilerFactory compilerFactory,
-        ToolchainToolFactory toolFactory,
-        FileFactory fileFactory,
-        BuildOperationProgressEventEmitter eventEmitter
-    ) {
-        this.detector = detector;
-        this.compilerFactory = compilerFactory;
-        this.toolFactory = toolFactory;
-        this.fileFactory = fileFactory;
-        this.eventEmitter = eventEmitter;
-    }
-
-    public JavaToolchainInstantiationResult newInstance(InstallationLocation javaHome, JavaToolchainInput input, boolean isFallbackToolchain) {
-        final JvmInstallationMetadata metadata = detector.getMetadata(javaHome);
-        if (metadata.isValidInstallation()) {
-            final JavaToolchain toolchain = new JavaToolchain(metadata, compilerFactory, toolFactory, fileFactory, input, isFallbackToolchain, eventEmitter);
-            return new JavaToolchainInstantiationResult(javaHome, metadata, toolchain);
-        }
-        return new JavaToolchainInstantiationResult(javaHome, metadata);
-    }
-
-}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainInstantiationResult.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainInstantiationResult.java
deleted file mode 100644
index cb21347..0000000
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainInstantiationResult.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2022 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.jvm.toolchain.internal;
-
-import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
-
-import javax.annotation.Nullable;
-import java.util.Optional;
-
-public class JavaToolchainInstantiationResult {
-
-    private final InstallationLocation javaHome;
-
-    private final JvmInstallationMetadata metadata;
-
-    private final Optional<JavaToolchain> toolchain;
-
-    public JavaToolchainInstantiationResult(InstallationLocation javaHome, JvmInstallationMetadata metadata) {
-        this(javaHome, metadata, null);
-    }
-
-    public JavaToolchainInstantiationResult(InstallationLocation javaHome, JvmInstallationMetadata metadata, @Nullable JavaToolchain toolchain) {
-        this.javaHome = javaHome;
-        this.metadata = metadata;
-        this.toolchain = Optional.ofNullable(toolchain);
-    }
-
-    public InstallationLocation getJavaHome() {
-        return javaHome;
-    }
-
-    public JvmInstallationMetadata getMetadata() {
-        return metadata;
-    }
-
-    public Optional<JavaToolchain> getToolchain() {
-        return toolchain;
-    }
-}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainMatcher.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainMatcher.java
deleted file mode 100644
index 3e11189..0000000
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainMatcher.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2020 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.jvm.toolchain.internal;
-
-import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
-import org.gradle.internal.jvm.inspection.JvmVendor;
-import org.gradle.jvm.toolchain.JavaLanguageVersion;
-import org.gradle.jvm.toolchain.JavaToolchainSpec;
-import org.gradle.jvm.toolchain.JvmImplementation;
-
-import java.util.Optional;
-import java.util.function.Predicate;
-
-public class JavaToolchainMatcher implements Predicate<JavaToolchain> {
-
-    public static Predicate<? super JavaToolchainInstantiationResult> forInstantiationResult(JavaToolchainSpec spec) {
-        return r -> {
-            Optional<JavaToolchain> toolchain = r.getToolchain();
-            return toolchain.isPresent() && new JavaToolchainMatcher(spec).test(toolchain.get());
-        };
-    }
-
-    private final JavaToolchainSpec spec;
-
-    public JavaToolchainMatcher(JavaToolchainSpec spec) {
-        this.spec = spec;
-    }
-
-    @Override
-    public boolean test(JavaToolchain toolchain) {
-        return test(toolchain.getMetadata());
-    }
-
-    public boolean test(JvmInstallationMetadata metadata) {
-        Predicate<JvmInstallationMetadata> predicate = languagePredicate().and(vendorPredicate()).and(implementationPredicate());
-        return predicate.test(metadata);
-    }
-
-    private Predicate<JvmInstallationMetadata> languagePredicate() {
-        return metadata -> {
-            JavaLanguageVersion actualVersion = JavaToolchain.getJavaLanguageVersion(metadata);
-            JavaLanguageVersion expectedVersion = spec.getLanguageVersion().get();
-            return actualVersion.equals(expectedVersion);
-        };
-    }
-
-    private Predicate<? super JvmInstallationMetadata> implementationPredicate() {
-        return metadata -> {
-            if (spec.getImplementation().get() == JvmImplementation.VENDOR_SPECIFIC) {
-                return true;
-            }
-
-            final boolean j9Requested = isJ9ExplicitlyRequested(spec) || isJ9RequestedViaVendor(spec);
-            final boolean isJ9Vm = metadata.hasCapability(JvmInstallationMetadata.JavaInstallationCapability.J9_VIRTUAL_MACHINE);
-            return j9Requested == isJ9Vm;
-        };
-    }
-
-    private static boolean isJ9ExplicitlyRequested(JavaToolchainSpec spec) {
-        return spec.getImplementation().get() == JvmImplementation.J9;
-    }
-
-    private static boolean isJ9RequestedViaVendor(JavaToolchainSpec spec) {
-        DefaultJvmVendorSpec vendorSpec = (DefaultJvmVendorSpec) spec.getVendor().get();
-        return vendorSpec != DefaultJvmVendorSpec.any() && vendorSpec.test(JvmVendor.KnownJvmVendor.IBM.asJvmVendor());
-    }
-
-    private Predicate<JvmInstallationMetadata> vendorPredicate() {
-        return (DefaultJvmVendorSpec) spec.getVendor().get();
-    }
-
-}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainQueryService.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainQueryService.java
index b264ff1..06b05b4 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainQueryService.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainQueryService.java
@@ -18,29 +18,34 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import org.gradle.api.GradleException;
-import org.gradle.api.Transformer;
+import org.gradle.api.internal.file.FileFactory;
 import org.gradle.api.internal.provider.DefaultProvider;
 import org.gradle.api.internal.provider.ProviderInternal;
 import org.gradle.api.model.ObjectFactory;
-import org.gradle.api.provider.Provider;
 import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.deprecation.Documentation;
 import org.gradle.internal.deprecation.DocumentedFailure;
 import org.gradle.internal.jvm.Jvm;
+import org.gradle.internal.jvm.inspection.JavaInstallationRegistry;
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
+import org.gradle.internal.jvm.inspection.JvmInstallationMetadataComparator;
+import org.gradle.internal.jvm.inspection.JvmMetadataDetector;
 import org.gradle.internal.service.scopes.Scopes;
 import org.gradle.internal.service.scopes.ServiceScope;
 import org.gradle.jvm.toolchain.JavaToolchainSpec;
 import org.gradle.jvm.toolchain.internal.install.JavaToolchainProvisioningService;
+import org.gradle.platform.BuildPlatform;
 
 import javax.inject.Inject;
 import java.io.File;
+import java.util.Comparator;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.function.Predicate;
 
-@ServiceScope(Scopes.Project.class) //TODO: should be much higher scoped, as many other toolchain related services, but is bogged down by the scope of services it depends on
+@ServiceScope(Scopes.Project.class) //TODO #24353: should be much higher scoped, as many other toolchain related services, but is bogged down by the scope of services it depends on
 public class JavaToolchainQueryService {
 
     // A key that matches only the fallback toolchain
@@ -52,38 +57,48 @@ public String toString() {
     };
 
     private final JavaInstallationRegistry registry;
-    private final JavaToolchainFactory toolchainFactory;
+    private final FileFactory fileFactory;
+    private final JvmMetadataDetector detector;
     private final JavaToolchainProvisioningService installService;
     // Map values are either `JavaToolchain` or `Exception`
     private final ConcurrentMap<JavaToolchainSpecInternal.Key, Object> matchingToolchains;
     private final CurrentJvmToolchainSpec fallbackToolchainSpec;
+    private final File currentJavaHome;
+    private final BuildPlatform buildPlatform;
 
     @Inject
     public JavaToolchainQueryService(
         JavaInstallationRegistry registry,
-        JavaToolchainFactory toolchainFactory,
+        JvmMetadataDetector detector,
+        FileFactory fileFactory,
         JavaToolchainProvisioningService provisioningService,
-        ObjectFactory objectFactory
+        ObjectFactory objectFactory,
+        BuildPlatform buildPlatform
     ) {
-        this.registry = registry;
-        this.toolchainFactory = toolchainFactory;
-        this.installService = provisioningService;
-        this.matchingToolchains = new ConcurrentHashMap<>();
-        this.fallbackToolchainSpec = objectFactory.newInstance(CurrentJvmToolchainSpec.class);
-    }
-
-    <T> Provider<T> toolFor(
-        JavaToolchainSpec spec,
-        Transformer<T, JavaToolchain> toolFunction,
-        DefaultJavaToolchainUsageProgressDetails.JavaTool requestedTool
-    ) {
-        return findMatchingToolchain(spec)
-            .withSideEffect(toolchain -> toolchain.emitUsageEvent(requestedTool))
-            .map(toolFunction);
+        this(registry, detector, fileFactory, provisioningService, objectFactory, Jvm.current().getJavaHome(), buildPlatform);
     }
 
     @VisibleForTesting
-    ProviderInternal<JavaToolchain> findMatchingToolchain(JavaToolchainSpec filter) {
+    JavaToolchainQueryService(
+        JavaInstallationRegistry registry,
+        JvmMetadataDetector detector,
+        FileFactory fileFactory,
+        JavaToolchainProvisioningService provisioningService,
+        ObjectFactory objectFactory,
+        File currentJavaHome,
+        BuildPlatform buildPlatform
+    ) {
+        this.registry = registry;
+        this.detector = detector;
+        this.fileFactory = fileFactory;
+        this.installService = provisioningService;
+        this.matchingToolchains = new ConcurrentHashMap<>();
+        this.fallbackToolchainSpec = objectFactory.newInstance(CurrentJvmToolchainSpec.class);
+        this.currentJavaHome = currentJavaHome;
+        this.buildPlatform = buildPlatform;
+    }
+
+    public ProviderInternal<JavaToolchain> findMatchingToolchain(JavaToolchainSpec filter) {
         JavaToolchainSpecInternal filterInternal = (JavaToolchainSpecInternal) Objects.requireNonNull(filter);
         return new DefaultProvider<>(() -> resolveToolchain(filterInternal));
     }
@@ -121,33 +136,31 @@ private JavaToolchain resolveToolchain(JavaToolchainSpecInternal requestedSpec)
 
     private JavaToolchain query(JavaToolchainSpec spec, boolean isFallback) {
         if (spec instanceof CurrentJvmToolchainSpec) {
-            // TODO (#22023) Look into checking if this optional is present and throwing an exception
-            return asToolchain(new InstallationLocation(Jvm.current().getJavaHome(), "current JVM"), spec, isFallback).getToolchain().get();
+            return asToolchainOrThrow(new InstallationLocation(currentJavaHome, "current JVM"), spec, isFallback);
         }
 
         if (spec instanceof SpecificInstallationToolchainSpec) {
-            final InstallationLocation installation = new InstallationLocation(((SpecificInstallationToolchainSpec) spec).getJavaHome(), "specific installation");
-            return asToolchainOrThrow(installation, spec);
+            return asToolchainOrThrow(new InstallationLocation(((SpecificInstallationToolchainSpec) spec).getJavaHome(), "specific installation"), spec, false);
         }
 
-        Optional<JavaToolchainInstantiationResult> detectedToolchain = registry.listInstallations().stream()
-                .map(javaHome -> asToolchain(javaHome, spec, false))
-                .filter(JavaToolchainMatcher.forInstantiationResult(spec))
-                .min(JavaToolchainComparator.forInstantiationResult());
-
-        if (detectedToolchain.isPresent()) {
-            warnIfAutoProvisionedToolchainUsedWithoutRepositoryDefinitions(detectedToolchain.get());
-            return detectedToolchain.get().getToolchain().get();
-        }
-
-        InstallationLocation downloadedInstallation = downloadToolchain(spec);
-        JavaToolchain downloadedToolchain = asToolchainOrThrow(downloadedInstallation, spec);
-        registry.addInstallation(downloadedInstallation);
-        return downloadedToolchain;
+        return findInstalledToolchain(spec).orElseGet(() -> downloadToolchain(spec));
     }
 
-    private void warnIfAutoProvisionedToolchainUsedWithoutRepositoryDefinitions(JavaToolchainInstantiationResult detectedToolchain) {
-        boolean autoDetectedToolchain = detectedToolchain.getJavaHome().isAutoProvisioned();
+    private Optional<JavaToolchain> findInstalledToolchain(JavaToolchainSpec spec) {
+        Predicate<JvmInstallationMetadata> matcher = new JvmInstallationMetadataMatcher(spec);
+
+        return registry.toolchains().stream()
+            .filter(result -> result.metadata.isValidInstallation())
+            .filter(result -> matcher.test(result.metadata))
+            .min(Comparator.comparing(result -> result.metadata, new JvmInstallationMetadataComparator(currentJavaHome)))
+            .map(result -> {
+                warnIfAutoProvisionedToolchainUsedWithoutRepositoryDefinitions(result.location);
+                return new JavaToolchain(result.metadata, fileFactory, new JavaToolchainInput(spec), false);
+            });
+    }
+
+    private void warnIfAutoProvisionedToolchainUsedWithoutRepositoryDefinitions(InstallationLocation javaHome) {
+        boolean autoDetectedToolchain = javaHome.isAutoProvisioned();
         if (autoDetectedToolchain && installService.isAutoDownloadEnabled() && !installService.hasConfiguredToolchainRepositories()) {
             DeprecationLogger.warnOfChangedBehaviour(
                 "Using a toolchain installed via auto-provisioning, but having no toolchain repositories configured",
@@ -159,26 +172,27 @@ private void warnIfAutoProvisionedToolchainUsedWithoutRepositoryDefinitions(Java
         }
     }
 
-    private InstallationLocation downloadToolchain(JavaToolchainSpec spec) {
+    private JavaToolchain downloadToolchain(JavaToolchainSpec spec) {
+        File installation;
         try {
-            File installation = installService.tryInstall(spec);
-            return new InstallationLocation(installation, "provisioned toolchain", true);
+            installation = installService.tryInstall(spec);
         } catch (ToolchainDownloadFailedException e) {
-            throw new NoToolchainAvailableException(spec, e);
+            throw new NoToolchainAvailableException(spec, buildPlatform, e);
         }
+
+        InstallationLocation downloadedInstallation = new InstallationLocation(installation, "provisioned toolchain", true);
+        JavaToolchain downloadedToolchain = asToolchainOrThrow(downloadedInstallation, spec, false);
+        registry.addInstallation(downloadedInstallation);
+        return downloadedToolchain;
     }
 
-    private JavaToolchain asToolchainOrThrow(InstallationLocation javaHome, JavaToolchainSpec spec) {
-        JavaToolchainInstantiationResult result = asToolchain(javaHome, spec, false);
-        Optional<JavaToolchain> toolchain = result.getToolchain();
-        if (!toolchain.isPresent()) {
-            JvmInstallationMetadata metadata = result.getMetadata();
+    private JavaToolchain asToolchainOrThrow(InstallationLocation javaHome, JavaToolchainSpec spec, boolean isFallback) {
+        final JvmInstallationMetadata metadata = detector.getMetadata(javaHome);
+
+        if (metadata.isValidInstallation()) {
+            return new JavaToolchain(metadata, fileFactory, new JavaToolchainInput(spec), isFallback);
+        } else {
             throw new GradleException("Toolchain installation '" + javaHome.getLocation() + "' could not be probed: " + metadata.getErrorMessage(), metadata.getErrorCause());
         }
-        return toolchain.get();
-    }
-
-    private JavaToolchainInstantiationResult asToolchain(InstallationLocation javaHome, JavaToolchainSpec spec, boolean isFallback) {
-        return toolchainFactory.newInstance(javaHome, new JavaToolchainInput(spec), isFallback);
     }
 }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainResolverRegistryInternal.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainResolverRegistryInternal.java
index 8448afe..6b0e417 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainResolverRegistryInternal.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JavaToolchainResolverRegistryInternal.java
@@ -25,4 +25,6 @@ public interface JavaToolchainResolverRegistryInternal extends JavaToolchainReso
     JavaToolchainRepositoryHandlerInternal getRepositories();
 
     List<? extends RealizedJavaToolchainRepository> requestedRepositories();
+
+    void preventFromFurtherMutation();
 }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataMatcher.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataMatcher.java
new file mode 100644
index 0000000..42912ed
--- /dev/null
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataMatcher.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 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.jvm.toolchain.internal;
+
+import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
+import org.gradle.internal.jvm.inspection.JvmVendor;
+import org.gradle.jvm.toolchain.JavaLanguageVersion;
+import org.gradle.jvm.toolchain.JavaToolchainSpec;
+import org.gradle.jvm.toolchain.JvmImplementation;
+
+import java.util.function.Predicate;
+
+public class JvmInstallationMetadataMatcher implements Predicate<JvmInstallationMetadata> {
+
+    private final JavaToolchainSpec spec;
+
+    public JvmInstallationMetadataMatcher(JavaToolchainSpec spec) {
+        this.spec = spec;
+    }
+
+    public boolean test(JvmInstallationMetadata metadata) {
+        Predicate<JvmInstallationMetadata> predicate = languagePredicate().and(vendorPredicate()).and(implementationPredicate());
+        return predicate.test(metadata);
+    }
+
+    private Predicate<JvmInstallationMetadata> languagePredicate() {
+        return metadata -> {
+            JavaLanguageVersion actualVersion = JavaToolchain.getJavaLanguageVersion(metadata);
+            JavaLanguageVersion expectedVersion = spec.getLanguageVersion().get();
+            return actualVersion.equals(expectedVersion);
+        };
+    }
+
+    private Predicate<? super JvmInstallationMetadata> implementationPredicate() {
+        return metadata -> {
+            if (spec.getImplementation().get() == JvmImplementation.VENDOR_SPECIFIC) {
+                return true;
+            }
+
+            final boolean j9Requested = isJ9ExplicitlyRequested(spec) || isJ9RequestedViaVendor(spec);
+            final boolean isJ9Vm = metadata.hasCapability(JvmInstallationMetadata.JavaInstallationCapability.J9_VIRTUAL_MACHINE);
+            return j9Requested == isJ9Vm;
+        };
+    }
+
+    private static boolean isJ9ExplicitlyRequested(JavaToolchainSpec spec) {
+        return spec.getImplementation().get() == JvmImplementation.J9;
+    }
+
+    private static boolean isJ9RequestedViaVendor(JavaToolchainSpec spec) {
+        DefaultJvmVendorSpec vendorSpec = (DefaultJvmVendorSpec) spec.getVendor().get();
+        return vendorSpec != DefaultJvmVendorSpec.any() && vendorSpec.test(JvmVendor.KnownJvmVendor.IBM.asJvmVendor());
+    }
+
+    private Predicate<JvmInstallationMetadata> vendorPredicate() {
+        return (DefaultJvmVendorSpec) spec.getVendor().get();
+    }
+
+}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/NoToolchainAvailableException.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/NoToolchainAvailableException.java
index fbab5e6..99e1ac8 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/NoToolchainAvailableException.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/NoToolchainAvailableException.java
@@ -19,11 +19,24 @@
 import org.gradle.api.GradleException;
 import org.gradle.internal.exceptions.Contextual;
 import org.gradle.jvm.toolchain.JavaToolchainSpec;
+import org.gradle.platform.BuildPlatform;
 
 @Contextual
 public class NoToolchainAvailableException extends GradleException {
 
-    public NoToolchainAvailableException(JavaToolchainSpec specification, ToolchainDownloadFailedException cause) {
-        super("No matching toolchains found for requested specification: " + specification.getDisplayName() + ".", cause);
+    public NoToolchainAvailableException(
+        JavaToolchainSpec specification,
+        BuildPlatform buildPlatform,
+        ToolchainDownloadFailedException cause
+    ) {
+        super(
+            String.format(
+                "No matching toolchains found for requested specification: %s for %s on %s.",
+                specification.getDisplayName(),
+                buildPlatform.getOperatingSystem(),
+                buildPlatform.getArchitecture().toString().toLowerCase()
+            ),
+            cause
+        );
     }
 }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/SpecificInstallationToolchainSpec.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/SpecificInstallationToolchainSpec.java
index 9472b25..6bd4367 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/SpecificInstallationToolchainSpec.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/SpecificInstallationToolchainSpec.java
@@ -74,10 +74,7 @@ public static SpecificInstallationToolchainSpec fromJavaHome(ObjectFactory objec
     }
 
     public static SpecificInstallationToolchainSpec fromJavaExecutable(ObjectFactory objectFactory, String executable) {
-        File executableFile = JavaExecutableUtils.resolveExecutable(executable);
-        // Relying on the layout of the toolchain distribution: <JAVA HOME>/bin/<executable>
-        final File parentJavaHome = executableFile.getParentFile().getParentFile();
-        return new SpecificInstallationToolchainSpec(objectFactory, parentJavaHome);
+        return new SpecificInstallationToolchainSpec(objectFactory, JavaExecutableUtils.resolveJavaHomeOfExecutable(executable));
     }
 
     @Override
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/ToolchainDownloadFailedException.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/ToolchainDownloadFailedException.java
index c04d900..35fd1cd 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/ToolchainDownloadFailedException.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/ToolchainDownloadFailedException.java
@@ -17,10 +17,22 @@
 package org.gradle.jvm.toolchain.internal;
 
 import org.gradle.api.GradleException;
+import org.gradle.internal.exceptions.ResolutionProvider;
 
-public class ToolchainDownloadFailedException extends GradleException {
+import java.util.Arrays;
+import java.util.List;
 
-    public ToolchainDownloadFailedException(String message) {
+public class ToolchainDownloadFailedException extends GradleException implements ResolutionProvider {
+
+    private final List<String> resolutions;
+
+    public ToolchainDownloadFailedException(String message, String... resolutions) {
         super(message);
+        this.resolutions = Arrays.asList(resolutions);
+    }
+
+    @Override
+    public List<String> getResolutions() {
+        return resolutions;
     }
 }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/install/DefaultJavaToolchainProvisioningService.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/install/DefaultJavaToolchainProvisioningService.java
index 3bf299d..30c3321 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/install/DefaultJavaToolchainProvisioningService.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/install/DefaultJavaToolchainProvisioningService.java
@@ -99,18 +99,15 @@ public boolean hasConfiguredToolchainRepositories() {
 
     public File tryInstall(JavaToolchainSpec spec) {
         if (!isAutoDownloadEnabled()) {
-            throw new ToolchainDownloadFailedException("No locally installed toolchains match (see " +
-                    Documentation.userManual("toolchains", "sec:auto_detection").documentationUrl() +
-                    ") and toolchain auto-provisioning is not enabled (see " +
-                    Documentation.userManual("toolchains", "sec:auto_detection").documentationUrl() + ").");
+            throw new ToolchainDownloadFailedException("No locally installed toolchains match and toolchain auto-provisioning is not enabled.",
+                "Learn more about toolchain auto-detection at " + Documentation.userManual("toolchains", "sec:auto_detection").documentationUrl() + ".");
         }
 
         List<? extends RealizedJavaToolchainRepository> repositories = toolchainResolverRegistry.requestedRepositories();
         if (repositories.isEmpty()) {
-            throw new ToolchainDownloadFailedException("No locally installed toolchains match (see " +
-                    Documentation.userManual("toolchains", "sec:auto_detection").documentationUrl() +
-                    ") and toolchain download repositories have not been configured (see " +
-                    Documentation.userManual("toolchains", "sub:download_repositories").documentationUrl() + ").");
+            throw new ToolchainDownloadFailedException("No locally installed toolchains match and toolchain download repositories have not been configured.",
+                "Learn more about toolchain auto-detection at " + Documentation.userManual("toolchains", "sec:auto_detection").documentationUrl() + ".",
+                "Learn more about toolchain repositories at " + Documentation.userManual("toolchains", "sub:download_repositories").documentationUrl() + ".");
         }
 
         for (RealizedJavaToolchainRepository request : repositories) {
@@ -121,10 +118,9 @@ public File tryInstall(JavaToolchainSpec spec) {
             }
         }
 
-        throw new ToolchainDownloadFailedException("No locally installed toolchains match (see " +
-                Documentation.userManual("toolchains", "sec:auto_detection").documentationUrl() +
-                ") and the configured toolchain download repositories aren't able to provide a match either (see " +
-                Documentation.userManual("toolchains", "sub:download_repositories").documentationUrl() + ").");
+        throw new ToolchainDownloadFailedException("No locally installed toolchains match and the configured toolchain download repositories aren't able to provide a match either.",
+            "Learn more about toolchain auto-detection at " + Documentation.userManual("toolchains", "sec:auto_detection").documentationUrl() + ".",
+            "Learn more about toolchain repositories at " + Documentation.userManual("toolchains", "sub:download_repositories").documentationUrl() + ".");
     }
 
     private File provisionInstallation(JavaToolchainSpec spec, URI uri, Collection<Authentication> authentications) {
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/install/JdkCacheDirectory.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/install/JdkCacheDirectory.java
index e397fc4..19f645f 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/install/JdkCacheDirectory.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/install/JdkCacheDirectory.java
@@ -32,7 +32,7 @@
 import org.gradle.internal.os.OperatingSystem;
 import org.gradle.jvm.toolchain.JavaToolchainSpec;
 import org.gradle.jvm.toolchain.internal.InstallationLocation;
-import org.gradle.jvm.toolchain.internal.JavaToolchainMatcher;
+import org.gradle.jvm.toolchain.internal.JvmInstallationMetadataMatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -208,7 +208,7 @@ private File markLocationInsideFolder(File unpackedInstallationFolder) {
     }
 
     private static void validateMetadataMatchesSpec(JavaToolchainSpec spec, URI uri, JvmInstallationMetadata metadata) {
-        if (!new JavaToolchainMatcher(spec).test(metadata)) {
+        if (!new JvmInstallationMetadataMatcher(spec).test(metadata)) {
             throw new GradleException("Toolchain provisioned from '" + uri + "' doesn't satisfy the specification: " + spec.getDisplayName() + ".");
         }
     }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ReportableToolchain.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ReportableToolchain.java
deleted file mode 100644
index 31c0a95..0000000
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ReportableToolchain.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2020 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.jvm.toolchain.internal.task;
-
-import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
-import org.gradle.jvm.toolchain.internal.InstallationLocation;
-
-public class ReportableToolchain {
-
-    JvmInstallationMetadata metadata;
-
-    InstallationLocation location;
-
-    public ReportableToolchain(JvmInstallationMetadata metadata, InstallationLocation location) {
-        this.metadata = metadata;
-        this.location = location;
-    }
-
-}
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ShowToolchainsTask.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ShowToolchainsTask.java
index 7d83b3e..fb9c659 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ShowToolchainsTask.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ShowToolchainsTask.java
@@ -21,16 +21,15 @@
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.tasks.TaskAction;
 import org.gradle.api.tasks.UntrackedTask;
-import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
-import org.gradle.internal.jvm.inspection.JvmMetadataDetector;
+import org.gradle.internal.jvm.inspection.JavaInstallationRegistry;
+import org.gradle.internal.jvm.inspection.JvmToolchainMetadata;
 import org.gradle.internal.logging.text.StyledTextOutput;
 import org.gradle.internal.logging.text.StyledTextOutputFactory;
-import org.gradle.jvm.toolchain.internal.install.DefaultJavaToolchainProvisioningService;
 import org.gradle.jvm.toolchain.internal.AutoDetectingInstallationSupplier;
-import org.gradle.jvm.toolchain.internal.InstallationLocation;
-import org.gradle.jvm.toolchain.internal.JavaInstallationRegistry;
+import org.gradle.jvm.toolchain.internal.install.DefaultJavaToolchainProvisioningService;
 
 import javax.inject.Inject;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
 import java.util.function.Predicate;
@@ -44,8 +43,8 @@
 @UntrackedTask(because = "Produces only non-cacheable console output")
 public abstract class ShowToolchainsTask extends DefaultTask {
 
-    private static final Comparator<ReportableToolchain> TOOLCHAIN_COMPARATOR = Comparator
-        .<ReportableToolchain, String>comparing(t -> t.metadata.getDisplayName())
+    private static final Comparator<JvmToolchainMetadata> TOOLCHAIN_COMPARATOR = Comparator
+        .<JvmToolchainMetadata, String>comparing(t -> t.metadata.getDisplayName())
         .thenComparing(t -> t.metadata.getLanguageVersion());
 
     private final ToolchainReportRenderer toolchainRenderer = new ToolchainReportRenderer();
@@ -59,9 +58,9 @@ public void showToolchains() {
         StyledTextOutput output = getTextOutputFactory().create(getClass());
         toolchainRenderer.setOutput(output);
         output.println();
-        List<ReportableToolchain> toolchains = allReportableToolchains();
-        List<ReportableToolchain> validToolchains = validToolchains(toolchains);
-        List<ReportableToolchain> invalidToolchains = invalidToolchains(toolchains);
+        List<JvmToolchainMetadata> toolchains = allReportableToolchains();
+        List<JvmToolchainMetadata> validToolchains = validToolchains(toolchains);
+        List<JvmToolchainMetadata> invalidToolchains = invalidToolchains(toolchains);
         printOptions(output);
         validToolchains.forEach(toolchainRenderer::printToolchain);
         toolchainRenderer.printInvalidToolchains(invalidToolchains);
@@ -82,27 +81,21 @@ private Boolean getBooleanProperty(String propertyKey) {
         return getProviderFactory().gradleProperty(propertyKey).map(Boolean::parseBoolean).getOrElse(true);
     }
 
-    private List<ReportableToolchain> invalidToolchains(List<ReportableToolchain> toolchains) {
+    private List<JvmToolchainMetadata> invalidToolchains(List<JvmToolchainMetadata> toolchains) {
         return toolchains.stream().filter(t -> !isValidToolchain().test(t)).collect(Collectors.toList());
     }
 
-    private List<ReportableToolchain> validToolchains(List<ReportableToolchain> toolchains) {
+    private List<JvmToolchainMetadata> validToolchains(Collection<JvmToolchainMetadata> toolchains) {
         return toolchains.stream().filter(isValidToolchain()).sorted(TOOLCHAIN_COMPARATOR).collect(Collectors.toList());
     }
 
-    private Predicate<? super ReportableToolchain> isValidToolchain() {
+    private Predicate<? super JvmToolchainMetadata> isValidToolchain() {
         return t -> t.metadata.isValidInstallation();
     }
 
-    private List<ReportableToolchain> allReportableToolchains() {
-        return getInstallationRegistry().listInstallations().parallelStream()
-            .map(this::asReportableToolchain)
-            .collect(Collectors.toList());
-    }
-
-    private ReportableToolchain asReportableToolchain(InstallationLocation location) {
-        JvmInstallationMetadata metadata = getMetadataDetector().getMetadata(location);
-        return new ReportableToolchain(metadata, location);
+    private List<JvmToolchainMetadata> allReportableToolchains() {
+        return getInstallationRegistry()
+            .toolchains();
     }
 
     @Inject
@@ -111,11 +104,6 @@ protected JavaInstallationRegistry getInstallationRegistry() {
     }
 
     @Inject
-    protected JvmMetadataDetector getMetadataDetector() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Inject
     protected StyledTextOutputFactory getTextOutputFactory() {
         throw new UnsupportedOperationException();
     }
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ToolchainReportRenderer.java b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ToolchainReportRenderer.java
index dc1c9d7..28e32ff 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ToolchainReportRenderer.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/jvm/toolchain/internal/task/ToolchainReportRenderer.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Strings;
 import org.gradle.api.tasks.diagnostics.internal.TextReportRenderer;
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata;
+import org.gradle.internal.jvm.inspection.JvmToolchainMetadata;
 import org.gradle.internal.logging.text.StyledTextOutput;
 
 import java.util.List;
@@ -30,7 +31,7 @@
 
 public class ToolchainReportRenderer extends TextReportRenderer {
 
-    public void printToolchain(ReportableToolchain toolchain) {
+    public void printToolchain(JvmToolchainMetadata toolchain) {
         StyledTextOutput output = getTextOutput();
         JvmInstallationMetadata metadata = toolchain.metadata;
         String displayName = metadata.getDisplayName();
@@ -50,11 +51,11 @@ private void printAttribute(String key, String value) {
         getTextOutput().withStyle(Description).println(value);
     }
 
-    public void printInvalidToolchains(List<ReportableToolchain> invalidToolchains) {
-        if(!invalidToolchains.isEmpty()) {
+    public void printInvalidToolchains(List<JvmToolchainMetadata> invalidToolchains) {
+        if (!invalidToolchains.isEmpty()) {
             StyledTextOutput output = getTextOutput();
             output.withStyle(Identifier).println(" + Invalid toolchains");
-            for (ReportableToolchain toolchain : invalidToolchains) {
+            for (JvmToolchainMetadata toolchain : invalidToolchains) {
                 JvmInstallationMetadata metadata = toolchain.metadata;
                 output.withStyle(Identifier).println("     + " + metadata.getJavaHome());
                 printInvalidToolchainErrorLines(toolchain);
@@ -63,7 +64,7 @@ public void printInvalidToolchains(List<ReportableToolchain> invalidToolchains)
         }
     }
 
-    private void printInvalidToolchainErrorLines(ReportableToolchain invalidToolchain) {
+    private void printInvalidToolchainErrorLines(JvmToolchainMetadata invalidToolchain) {
         getTextOutput().withStyle(Normal).format("       | %s", Strings.padEnd("Error:", 20, ' '));
         getTextOutput().withStyle(Description).println(invalidToolchain.metadata.getErrorMessage());
 
diff --git a/subprojects/platform-jvm/src/main/java/org/gradle/platform/package-info.java b/subprojects/platform-jvm/src/main/java/org/gradle/platform/package-info.java
index d20e5ac..fef8875 100644
--- a/subprojects/platform-jvm/src/main/java/org/gradle/platform/package-info.java
+++ b/subprojects/platform-jvm/src/main/java/org/gradle/platform/package-info.java
@@ -18,4 +18,4 @@
  * Types to define build environment.
  */
 @org.gradle.api.Incubating
-package org.gradle.platform;
\ No newline at end of file
+package org.gradle.platform;
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainComparatorTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainComparatorTest.groovy
deleted file mode 100644
index 6f17d73..0000000
--- a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainComparatorTest.groovy
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2020 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.jvm.toolchain.internal
-
-import org.gradle.api.internal.file.TestFiles
-import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
-import org.gradle.util.internal.VersionNumber
-import spock.lang.Issue
-import spock.lang.Specification
-
-import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor
-import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.ADOPTOPENJDK
-import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.AMAZON
-import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.BELLSOFT
-import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.ORACLE
-import static org.gradle.internal.jvm.inspection.JvmVendor.KnownJvmVendor.UNKNOWN
-import static org.gradle.internal.jvm.inspection.JvmVendor.fromString
-
-class JavaToolchainComparatorTest extends Specification {
-
-    def "prefers higher major versions"() {
-        given:
-        def toolchains = [
-            mockToolchain("6.0"),
-            mockToolchain("8.0"),
-            mockToolchain("11.0"),
-            mockToolchain("5.1")
-        ]
-
-        when:
-        toolchains.sort(new JavaToolchainComparator())
-
-        then:
-        assertOrder(toolchains, "11.0.0", "8.0.0", "6.0.0", "5.1.0")
-    }
-
-    def "deterministically matches vendor over vendor-specific tool version"() {
-        given:
-        def toolchains = [
-            mockToolchain("8.2", true, AMAZON),
-            mockToolchain("8.3", true, AMAZON),
-            mockToolchain("8.1", true, AMAZON),
-            mockToolchain("8.8", true, BELLSOFT),
-            mockToolchain("8.7", true, ORACLE),
-            mockToolchain("8.4", true, UNKNOWN),
-        ]
-
-        when:
-        toolchains.sort(new JavaToolchainComparator())
-
-        then:
-        assertOrder(toolchains, "8.3.0", "8.2.0", "8.1.0", "8.8.0", "8.7.0", "8.4.0")
-    }
-
-    def "prefers higher minor versions"() {
-        given:
-        def toolchains = [
-            mockToolchain("8.0.1"),
-            mockToolchain("8.0.123"),
-            mockToolchain("8.0.1234"),
-        ]
-
-        when:
-        toolchains.sort(new JavaToolchainComparator())
-
-        then:
-        assertOrder(toolchains, "8.0.1234", "8.0.123", "8.0.1")
-    }
-
-    def "prefers jdk over jre"() {
-        def jdk = Mock(JavaToolchain) {
-            getToolchainVersion() >> VersionNumber.parse("8.0.1")
-            isJdk() >> true
-        }
-        def jre = Mock(JavaToolchain) {
-            getToolchainVersion() >> VersionNumber.parse("8.0.1")
-            isJdk() >> false
-        }
-        given:
-        def toolchains = [jre, jdk]
-
-        when:
-        toolchains.sort(new JavaToolchainComparator())
-
-        then:
-        toolchains == [jdk, jre]
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/17195")
-    def "compares installation paths as a last resort"() {
-        given:
-        def prevJdk = mockToolchain("8.0.1", true, ADOPTOPENJDK, "/jdks/openjdk-8.0.1")
-        def nextJdk = mockToolchain("8.0.1", true, ADOPTOPENJDK, "/jdks/openjdk-8.0.1.1")
-        def toolchains = [prevJdk, nextJdk]
-
-        when:
-        toolchains.sort(new JavaToolchainComparator())
-
-        then:
-        toolchains == [nextJdk, prevJdk]
-    }
-
-    def "current JVM is picked above all else"() {
-        given:
-        def toolchains = [
-            mockToolchain("8.2", true, AMAZON),
-            mockToolchain("8.3", true, AMAZON),
-            mockToolchain("8.1", true, AMAZON,),
-            mockToolchain("8.8", true, BELLSOFT),
-            mockToolchain("8.7", true, ORACLE),
-            mockToolchain("8.0", false, ORACLE, null, true),
-            mockToolchain("8.4", true, UNKNOWN),
-        ]
-
-        when:
-        toolchains.sort(new JavaToolchainComparator())
-
-        then:
-        assertOrder(toolchains,  "8.0.0", "8.3.0", "8.2.0", "8.1.0", "8.8.0", "8.7.0", "8.4.0")
-    }
-
-    static void assertOrder(List<JavaToolchain> list, String[] expectedOrder) {
-        assert list*.toolchainVersion.toString() == expectedOrder.toString()
-    }
-
-    JavaToolchain mockToolchain(String implementationVersion, boolean isJdk = false, KnownJvmVendor jvmVendor = ADOPTOPENJDK, String installPath = null, boolean isCurrentJvm = false) {
-        def javaHome = new File(installPath != null ? installPath : "/" + isJdk ? "jdk" : "jre" + "s/" + implementationVersion).absoluteFile
-        Mock(JavaToolchain) {
-            it.getToolchainVersion() >> VersionNumber.parse(implementationVersion)
-            it.isJdk() >> isJdk
-            it.isCurrentJvm() >> isCurrentJvm
-            it.getInstallationPath() >> TestFiles.fileFactory().dir(javaHome)
-            it.getMetadata() >> Mock(JvmInstallationMetadata) {
-                it.getVendor() >> fromString(jvmVendor.name())
-            }
-        }
-    }
-}
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainMatcherTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainMatcherTest.groovy
deleted file mode 100644
index a5a896d..0000000
--- a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainMatcherTest.groovy
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2022 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.jvm.toolchain.internal
-
-import org.gradle.api.JavaVersion
-import org.gradle.api.internal.file.TestFiles
-import org.gradle.internal.jvm.inspection.DefaultJvmMetadataDetector
-import org.gradle.internal.jvm.inspection.MetadataProbe
-import org.gradle.internal.jvm.inspection.ProbedSystemProperty
-import org.gradle.jvm.toolchain.JavaLanguageVersion
-import org.gradle.jvm.toolchain.JvmImplementation
-import org.gradle.jvm.toolchain.JvmVendorSpec
-import org.gradle.process.ExecResult
-import org.gradle.process.internal.ExecHandle
-import org.gradle.process.internal.ExecHandleBuilder
-import org.gradle.process.internal.ExecHandleFactory
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.TestUtil
-import spock.lang.Specification
-import spock.lang.TempDir
-
-class JavaToolchainMatcherTest extends Specification {
-
-    @TempDir
-    File temporaryFolder
-
-    TestFile tmpDir
-    def setup() {
-        tmpDir = new TestFile(new File(temporaryFolder, "tmp").tap { mkdirs() })
-    }
-
-    def "ibm vendors match semeru runtime metadata (java version: #javaVersion, vendor: #vendor, implementation: #implementation)"() {
-        given:
-        def execHandleFactory = createExecHandleFactory(systemProperties)
-        def detector = createDefaultJvmMetadataDetector(execHandleFactory)
-        def javaHome = new File(temporaryFolder, jdk).tap { mkdirs() }
-        def metadata = detector.getMetadata(testLocation(javaHome))
-
-        when:
-        def spec = new DefaultToolchainSpec(TestUtil.objectFactory())
-        spec.getLanguageVersion().set(JavaLanguageVersion.of(javaVersion.getMajorVersion()))
-        spec.getVendor().set(vendor)
-        spec.getImplementation().set(implementation)
-
-        then:
-        new JavaToolchainMatcher(spec).test(metadata)
-
-        where:
-        jdk              | systemProperties         | javaVersion             | vendor                    | implementation
-        'semeru11'       | semeruJvm11()            | JavaVersion.VERSION_11  | JvmVendorSpec.IBM         | JvmImplementation.VENDOR_SPECIFIC
-        'semeru16'       | semeruJvm16()            | JavaVersion.VERSION_16  | JvmVendorSpec.IBM         | JvmImplementation.VENDOR_SPECIFIC
-        'semeru17'       | semeruJvm17()            | JavaVersion.VERSION_17  | JvmVendorSpec.IBM         | JvmImplementation.VENDOR_SPECIFIC
-
-        'semeru11'       | semeruJvm11()            | JavaVersion.VERSION_11  | JvmVendorSpec.IBM         | JvmImplementation.J9
-        'semeru16'       | semeruJvm16()            | JavaVersion.VERSION_16  | JvmVendorSpec.IBM         | JvmImplementation.J9
-        'semeru17'       | semeruJvm17()            | JavaVersion.VERSION_17  | JvmVendorSpec.IBM         | JvmImplementation.J9
-
-        'semeru11'       | semeruJvm11()            | JavaVersion.VERSION_11  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.VENDOR_SPECIFIC
-        'semeru16'       | semeruJvm16()            | JavaVersion.VERSION_16  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.VENDOR_SPECIFIC
-        'semeru17'       | semeruJvm17()            | JavaVersion.VERSION_17  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.VENDOR_SPECIFIC
-
-        'semeru11'       | semeruJvm11()            | JavaVersion.VERSION_11  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.J9
-        'semeru16'       | semeruJvm16()            | JavaVersion.VERSION_16  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.J9
-        'semeru17'       | semeruJvm17()            | JavaVersion.VERSION_17  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.J9
-    }
-
-    def createExecHandleFactory(Map<String, String> actualProperties) {
-        def probedSystemProperties = ProbedSystemProperty.values().findAll { it != ProbedSystemProperty.Z_ERROR }
-        if (!actualProperties.isEmpty()) {
-            assert actualProperties.keySet() == probedSystemProperties.collect { it.systemPropertyKey }.toSet()
-        }
-
-        def execHandleFactory = Mock(ExecHandleFactory)
-        def exec = Mock(ExecHandleBuilder)
-        execHandleFactory.newExec() >> exec
-        PrintStream output
-        exec.setStandardOutput(_ as OutputStream) >> { OutputStream outputStream ->
-            output = new PrintStream(outputStream)
-            null
-        }
-        def handle = Mock(ExecHandle)
-        handle.start() >> handle
-        handle.waitForFinish() >> {
-            // important to output in the order of the enum members as parsing uses enum ordinals
-            probedSystemProperties.each {
-                def actualValue = actualProperties[it.systemPropertyKey]
-                // write conditionally to simulate wrong number of outputs
-                if (actualValue != null) {
-                    output.println(MetadataProbe.MARKER_PREFIX + actualValue)
-                }
-            }
-            Mock(ExecResult)
-        }
-        exec.build() >> handle
-        execHandleFactory
-    }
-
-    private DefaultJvmMetadataDetector createDefaultJvmMetadataDetector(ExecHandleFactory execHandleFactory) {
-        return new DefaultJvmMetadataDetector(
-                execHandleFactory,
-                TestFiles.tmpDirTemporaryFileProvider(tmpDir)
-        )
-    }
-
-    private InstallationLocation testLocation(File javaHome) {
-        new InstallationLocation(javaHome, "test")
-    }
-
-    private static Map<String, String> semeruJvm11() {
-        ['java.home': "java-home",
-         'java.version': "11.0.17",
-         'java.vendor': "IBM Corporation",
-         'os.arch': "x86_64",
-         'java.vm.name': "Eclipse OpenJ9 VM",
-         'java.vm.version': "openj9-0.35.0",
-         'java.vm.vendor': "Eclipse OpenJ9",
-         'java.runtime.name': "IBM Semeru Runtime Open Edition",
-         'java.runtime.version': "11.0.17+8"
-        ]
-    }
-
-    private static Map<String, String> semeruJvm16() {
-        ['java.home': "java-home",
-         'java.version': "16.0.2",
-         'java.vendor': "International Business Machines Corporation",
-         'os.arch': "x86_64",
-         'java.vm.name': "Eclipse OpenJ9 VM",
-         'java.vm.version': "openj9-0.27.0",
-         'java.vm.vendor': "Eclipse OpenJ9",
-         'java.runtime.name': "IBM Semeru Runtime Open Edition",
-         'java.runtime.version': "16.0.2+7"
-        ]
-    }
-
-    private static Map<String, String> semeruJvm17() {
-        ['java.home': "java-home",
-         'java.version': "17.0.5",
-         'java.vendor': "IBM Corporation",
-         'os.arch': "x86_64",
-         'java.vm.name': "Eclipse OpenJ9 VM",
-         'java.vm.version': "openj9-0.35.0",
-         'java.vm.vendor': "Eclipse OpenJ9",
-         'java.runtime.name': "IBM Semeru Runtime Open Edition",
-         'java.runtime.version': "17.0.5+8"
-        ]
-    }
-
-}
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainQueryServiceTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainQueryServiceTest.groovy
index f0d66a5..1abfea8 100644
--- a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainQueryServiceTest.groovy
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainQueryServiceTest.groovy
@@ -20,23 +20,25 @@
 import org.gradle.api.JavaVersion
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.internal.jvm.Jvm
+import org.gradle.internal.jvm.inspection.JavaInstallationRegistry
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
 import org.gradle.internal.jvm.inspection.JvmMetadataDetector
 import org.gradle.internal.jvm.inspection.JvmVendor
-import org.gradle.internal.operations.BuildOperationProgressEventEmitter
 import org.gradle.internal.operations.TestBuildOperationExecutor
 import org.gradle.internal.os.OperatingSystem
+import org.gradle.internal.progress.NoOpProgressLoggerFactory
 import org.gradle.jvm.toolchain.JavaLanguageVersion
 import org.gradle.jvm.toolchain.JavaToolchainSpec
 import org.gradle.jvm.toolchain.JvmImplementation
 import org.gradle.jvm.toolchain.JvmVendorSpec
 import org.gradle.jvm.toolchain.internal.install.JavaToolchainProvisioningService
+import org.gradle.platform.Architecture
+import org.gradle.platform.BuildPlatform
 import org.gradle.util.TestUtil
 import spock.lang.Issue
 import spock.lang.Specification
 
 import java.util.function.Function
-import java.util.function.Predicate
 
 import static org.gradle.api.internal.file.TestFiles.systemSpecificAbsolutePath
 import static org.gradle.internal.jvm.inspection.JvmInstallationMetadata.JavaInstallationCapability.J9_VIRTUAL_MACHINE
@@ -45,9 +47,7 @@
 
     def "can query for matching toolchain using version #versionToFind"() {
         given:
-        def registry = createInstallationRegistry(versionRange(8, 12))
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(versionRange(8, 12))
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -66,9 +66,7 @@
 
     def "uses most recent version of multiple matches for version #versionToFind"() {
         given:
-        def registry = createInstallationRegistry(["8.0", "8.0.242.hs-adpt", "7.9", "7.7", "14.0.2+12", "8.0.zzz.foo"])
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(["8.0", "8.0.242.hs-adpt", "7.9", "7.7", "14.0.2+12", "8.0.zzz.foo"])
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -89,9 +87,7 @@
     @Issue("https://github.com/gradle/gradle/issues/17195")
     def "uses most recent version of multiple matches if version has a legacy format"() {
         given:
-        def registry = createDeterministicInstallationRegistry(["1.8.0_282", "1.8.0_292"])
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(["1.8.0_282", "1.8.0_292"])
         def versionToFind = JavaLanguageVersion.of(8)
 
         when:
@@ -106,9 +102,7 @@
 
     def "uses j9 toolchain if requested"() {
         given:
-        def registry = createInstallationRegistry(["8.0", "8.0.242.hs-adpt", "7.9", "7.7", "14.0.2+12", "8.0.1.j9"])
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(["8.0", "8.0.242.hs-adpt", "7.9", "7.7", "14.0.2+12", "8.0.1.j9"])
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -123,9 +117,7 @@
 
     def "no preferred implementation if vendor-specific is requested"() {
         given:
-        def registry = createInstallationRegistry(["8.0.2.j9", "8.0.1.hs"])
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(["8.0.2.j9", "8.0.1.hs"])
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -139,21 +131,12 @@
 
     def "matches J9 toolchain via vendor"() {
         given:
-        def registry = createInstallationRegistry(["8.hs-amazon", "8.j9-international business machines corporation"])
-        def compilerFactory = Mock(JavaCompilerFactory)
-        def toolFactory = Mock(ToolchainToolFactory)
-        def eventEmitter = Stub(BuildOperationProgressEventEmitter)
-        def toolchainFactory = new JavaToolchainFactory(Mock(JvmMetadataDetector), compilerFactory, toolFactory, TestFiles.fileFactory(), eventEmitter) {
-            @Override
-            JavaToolchainInstantiationResult newInstance(InstallationLocation javaHome, JavaToolchainInput input, boolean isFallbackToolchain) {
-                String locationName = javaHome.location.name
-                def vendor = locationName.substring(5)
-                def metadata = newMetadata(new InstallationLocation(new File("/path/" + locationName), javaHome.source), "8", vendor)
-                return new JavaToolchainInstantiationResult(javaHome, metadata,
-                    new JavaToolchain(metadata, compilerFactory, toolFactory, TestFiles.fileFactory(), input, isFallbackToolchain, eventEmitter))
-            }
-        }
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(
+            ["8.hs-amazon", "8.j9-international business machines corporation"],
+            null,
+            version -> version.split("\\.")[0],
+            version -> version.split("-")[1]
+        )
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -169,9 +152,7 @@
 
     def "ignores invalid toolchains when finding a matching one"() {
         given:
-        def registry = createInstallationRegistry(["8.0", "8.0.242.hs-adpt", "8.0.broken"])
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(["8.0", "8.0.242.hs-adpt", "8.0.broken"])
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -185,9 +166,7 @@
 
     def "returns failing provider if no toolchain matches"() {
         given:
-        def registry = createInstallationRegistry(["8", "9", "10"])
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(["8", "9", "10"])
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -197,15 +176,14 @@
 
         then:
         def e = thrown(NoToolchainAvailableException)
-        e.message == "No matching toolchains found for requested specification: {languageVersion=12, vendor=any, implementation=vendor-specific}."
+        e.message == "No matching toolchains found for requested specification: {languageVersion=12, vendor=any, implementation=vendor-specific} for LINUX on x86_64."
         e.cause.message == "Configured toolchain download repositories can't match requested specification"
     }
 
     def "returns current JVM toolchain if requested"() {
         given:
-        def registry = createInstallationRegistry(versionRange(8, 19))
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def currentJvm = locationFor("17")
+        def queryService = setupInstallations(versionRange(8, 19), currentJvm)
 
         when:
         def filter = new CurrentJvmToolchainSpec(TestUtil.objectFactory())
@@ -214,15 +192,14 @@
         then:
         toolchain.isPresent()
         !toolchain.get().isFallbackToolchain()
-        toolchain.get().languageVersion == JavaLanguageVersion.of(Jvm.current().javaVersion.majorVersion)
-        toolchain.get().getInstallationPath().toString() == Jvm.current().javaHome.absolutePath
+        toolchain.get().languageVersion == JavaLanguageVersion.of(17)
+        toolchain.get().getInstallationPath().toString() == currentJvm.location.absolutePath
     }
 
     def "returns fallback toolchain if filter is not configured"() {
         given:
-        def registry = createInstallationRegistry(versionRange(8, 19))
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def currentJvm = locationFor("17")
+        def queryService = setupInstallations(versionRange(8, 19), currentJvm)
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -231,15 +208,14 @@
         then:
         toolchain.isPresent()
         toolchain.get().isFallbackToolchain()
-        toolchain.get().languageVersion == JavaLanguageVersion.of(Jvm.current().javaVersion.majorVersion)
-        toolchain.get().getInstallationPath().toString() == Jvm.current().javaHome.absolutePath
+        toolchain.get().languageVersion == JavaLanguageVersion.of(17)
+        toolchain.get().getInstallationPath().toString() == currentJvm.location.absolutePath
     }
 
     def "returns non-fallback current JVM toolchain for matching filter"() {
         given:
-        def registry = createInstallationRegistry(versionRange(8, 19))
-        def toolchainFactory = newToolchainFactory(javaHome -> javaHome.name, javaHome -> javaHome.name == "17")
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def currentJvm = locationFor("17")
+        def queryService = setupInstallations(versionRange(8, 19), currentJvm)
         def versionToFind = JavaLanguageVersion.of(17)
 
         when:
@@ -259,9 +235,8 @@
      */
     def "returns fallback toolchain if filter is not configured even after returning current JVM"() {
         given:
-        def registry = createInstallationRegistry(versionRange(8, 19))
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def currentJvm = locationFor("17")
+        def queryService = setupInstallations(versionRange(8, 19), currentJvm)
 
         when:
         def currentJvmFilter = new CurrentJvmToolchainSpec(TestUtil.objectFactory())
@@ -283,9 +258,8 @@
      */
     def "returns non-fallback current JVM toolchain if requested even after returning fallback toolchain"() {
         given:
-        def registry = createInstallationRegistry(versionRange(8, 19))
-        def toolchainFactory = newToolchainFactory()
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def currentJvm = locationFor("17")
+        def queryService = setupInstallations(versionRange(8, 19), currentJvm)
 
         when:
         def fallbackFilter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -303,20 +277,12 @@
 
     def "returns toolchain matching vendor"() {
         given:
-        def registry = createInstallationRegistry(["8-amazon", "8-bellsoft", "8-ibm", "8-zulu"])
-        def compilerFactory = Mock(JavaCompilerFactory)
-        def toolFactory = Mock(ToolchainToolFactory)
-        def eventEmitter = Stub(BuildOperationProgressEventEmitter)
-        def toolchainFactory = new JavaToolchainFactory(Mock(JvmMetadataDetector), compilerFactory, toolFactory, TestFiles.fileFactory(), eventEmitter) {
-            @Override
-            JavaToolchainInstantiationResult newInstance(InstallationLocation javaHome, JavaToolchainInput input, boolean isFallbackToolchain) {
-                def vendor = javaHome.location.name.substring(2)
-                def metadata = newMetadata(new InstallationLocation(new File("/path/8"), javaHome.source), "8", vendor)
-                return new JavaToolchainInstantiationResult(javaHome, metadata,
-                        new JavaToolchain(metadata, compilerFactory, toolFactory, TestFiles.fileFactory(), input, isFallbackToolchain, eventEmitter))
-            }
-        }
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(
+            ["8-amazon", "8-bellsoft", "8-ibm", "8-zulu"],
+            null,
+            version -> version.split("-")[0],
+            version -> version.split("-")[1]
+        )
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -333,7 +299,6 @@
     def "install toolchain if no matching toolchain found"() {
         given:
         def registry = createInstallationRegistry([])
-        def toolchainFactory = newToolchainFactory()
         def installed = false
         def provisionService = new JavaToolchainProvisioningService() {
             @Override
@@ -351,7 +316,7 @@
                 new File("/path/12")
             }
         }
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, provisionService, TestUtil.objectFactory())
+        def queryService = createQueryService(registry, newJvmMetadataDetector(), provisionService)
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -366,7 +331,6 @@
     def "handles broken provisioned toolchain"() {
         given:
         def registry = createInstallationRegistry([])
-        def toolchainFactory = newToolchainFactory()
         def installed = false
         def provisionService = new JavaToolchainProvisioningService() {
             @Override
@@ -384,7 +348,7 @@
                 new File("/path/12.broken")
             }
         }
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, provisionService, TestUtil.objectFactory())
+        def queryService = createQueryService(registry, newJvmMetadataDetector(), provisionService)
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -400,7 +364,6 @@
     def "provisioned toolchain is cached no re-request"() {
         given:
         def registry = createInstallationRegistry([])
-        def toolchainFactory = newToolchainFactory()
         int installed = 0
         def provisionService = new JavaToolchainProvisioningService() {
             @Override
@@ -418,7 +381,7 @@
                 new File("/path/12")
             }
         }
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, provisionService, TestUtil.objectFactory())
+        def queryService = createQueryService(registry, newJvmMetadataDetector(), provisionService)
 
         when:
         def filter = new DefaultToolchainSpec(TestUtil.objectFactory())
@@ -434,9 +397,7 @@
 
     def "prefer version Gradle is running on as long as it is a match"() {
         given:
-        def registry = createDeterministicInstallationRegistry(["1.8.1", "1.8.2", "1.8.3"])
-        def toolchainFactory = newToolchainFactory(javaHome -> javaHome.name, javaHome -> javaHome.name == "1.8.2")
-        def queryService = new JavaToolchainQueryService(registry, toolchainFactory, createProvisioningService(), TestUtil.objectFactory())
+        def queryService = setupInstallations(["1.8.1", "1.8.2", "1.8.3"], locationFor("1.8.2"))
         def versionToFind = JavaLanguageVersion.of(8)
 
         when:
@@ -449,40 +410,38 @@
         toolchain.getInstallationPath().toString() == systemSpecificAbsolutePath("/path/1.8.2")
     }
 
+    private JavaToolchainQueryService setupInstallations(
+        Collection<String> installations,
+        InstallationLocation currentJvm = null,
+        Function<String, String> getVersion = { it },
+        Function<String, String> getVendor = { "" }
+    ) {
+        def detector = newJvmMetadataDetector(getVersion, getVendor)
+        def registry = createInstallationRegistry(installations, detector)
+        def currentJvmPath = currentJvm?.location ?: Jvm.current().javaHome
+
+        def queryService = createQueryService(registry, detector, createProvisioningService(), currentJvmPath)
+        return queryService
+    }
+
     private JavaToolchainProvisioningService createProvisioningService() {
         def provisioningService = Mock(JavaToolchainProvisioningService)
         provisioningService.tryInstall(_ as JavaToolchainSpec) >> { throw new ToolchainDownloadFailedException("Configured toolchain download repositories can't match requested specification") }
         provisioningService
     }
 
-    private JavaToolchainFactory newToolchainFactory() {
-        Predicate<File> isCurrentJvm = { it -> Jvm.current().javaHome.absoluteFile == it.absoluteFile }
-        def getVersion = { File javaHome -> isCurrentJvm(javaHome) ? Jvm.current().javaVersion.toString() : javaHome.name }
-        newToolchainFactory(getVersion, isCurrentJvm)
-    }
-
-    private JavaToolchainFactory newToolchainFactory(Function<File, String> getVersion, Predicate<File> isCurrentJvm) {
-        def compilerFactory = Mock(JavaCompilerFactory)
-        def toolFactory = Mock(ToolchainToolFactory)
-        def eventEmitter = Stub(BuildOperationProgressEventEmitter)
-        def toolchainFactory = new JavaToolchainFactory(Mock(JvmMetadataDetector), compilerFactory, toolFactory, TestFiles.fileFactory(), eventEmitter) {
+    private def newJvmMetadataDetector(
+        Function<String, String> getVersion = { it },
+        Function<String, String> getVendor = { "" }
+    ) {
+        return new JvmMetadataDetector() {
             @Override
-            JavaToolchainInstantiationResult newInstance(InstallationLocation javaHome, JavaToolchainInput input, boolean isFallbackToolchain) {
-                def languageVersion = Jvm.current().javaHome == javaHome.location ? Jvm.current().javaVersion.toString() : getVersion.apply(javaHome.location)
-                def metadata = newMetadata(javaHome, languageVersion)
-                if (metadata.isValidInstallation()) {
-                    def toolchain = new JavaToolchain(metadata, compilerFactory, toolFactory, TestFiles.fileFactory(), input, isFallbackToolchain, eventEmitter) {
-                        @Override
-                        boolean isCurrentJvm() {
-                            return isCurrentJvm.test(javaHome.location)
-                        }
-                    }
-                    return new JavaToolchainInstantiationResult(javaHome, metadata, toolchain)
-                }
-                return new JavaToolchainInstantiationResult(javaHome, metadata)
+            JvmInstallationMetadata getMetadata(InstallationLocation javaInstallationLocation) {
+                def languageVersion = getVersion.apply(javaInstallationLocation.location.name)
+                def vendor = getVendor.apply(javaInstallationLocation.location.name)
+                newMetadata(javaInstallationLocation, languageVersion, vendor)
             }
         }
-        toolchainFactory
     }
 
     def newMetadata(InstallationLocation javaHome, String languageVersion, String vendor = "") {
@@ -507,17 +466,28 @@
         }
     }
 
-    static def createInstallationRegistry(List<String> installations) {
+    private def createInstallationRegistry(
+        Collection<String> installations,
+        JvmMetadataDetector detector = newJvmMetadataDetector()
+    ) {
         def supplier = new InstallationSupplier() {
             @Override
+            String getSourceName() {
+                "test"
+            }
+
+            @Override
             Set<InstallationLocation> get() {
-                installations.collect { new InstallationLocation(new File("/path/${it}").absoluteFile, "test") } as Set
+                installations.collect{ locationFor(it) } as Set<InstallationLocation>
             }
         }
-        def registry = new JavaInstallationRegistry([supplier], new TestBuildOperationExecutor(), OperatingSystem.current()) {
+        def registry = new JavaInstallationRegistry([supplier], detector, new TestBuildOperationExecutor(), OperatingSystem.current(), new NoOpProgressLoggerFactory()) {
+            @Override
             boolean installationExists(InstallationLocation installationLocation) {
                 return true
             }
+
+            @Override
             boolean installationHasExecutable(InstallationLocation installationLocation) {
                 return true
             }
@@ -529,11 +499,27 @@
         return (begin..end).collect { it.toString() }
     }
 
-    def createDeterministicInstallationRegistry(List<String> installations) {
-        def installationLocations = installations.collect { new InstallationLocation(new File("/path/${it}").absoluteFile, "test") } as LinkedHashSet
-        Mock(JavaInstallationRegistry) {
-            listInstallations() >> installationLocations
-            installationExists(_ as InstallationLocation) >> true
+    private InstallationLocation locationFor(String version){
+        return new InstallationLocation(new File("/path/${version}").absoluteFile, "test")
+    }
+
+    private JavaToolchainQueryService createQueryService(
+        JavaInstallationRegistry registry,
+        JvmMetadataDetector detector,
+        JavaToolchainProvisioningService provisioningService,
+        File currentJavaHome = Jvm.current().getJavaHome()
+    ) {
+        def buildPlatform = new BuildPlatform() {
+            @Override
+            org.gradle.platform.OperatingSystem getOperatingSystem() {
+                return org.gradle.platform.OperatingSystem.LINUX
+            }
+
+            @Override
+            Architecture getArchitecture() {
+                return Architecture.X86_64
+            }
         }
+        new JavaToolchainQueryService(registry, detector, TestFiles.fileFactory(), provisioningService, TestUtil.objectFactory(), currentJavaHome, buildPlatform)
     }
 }
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainTest.groovy
index e2cc41e..119875c 100644
--- a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainTest.groovy
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JavaToolchainTest.groovy
@@ -19,7 +19,6 @@
 import org.gradle.api.internal.file.TestFiles
 import org.gradle.internal.jvm.Jvm
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
-import org.gradle.internal.operations.BuildOperationProgressEventEmitter
 import org.gradle.jvm.toolchain.JavaInstallationMetadata
 import org.gradle.jvm.toolchain.JavaLanguageVersion
 import org.gradle.jvm.toolchain.JvmImplementation
@@ -32,15 +31,13 @@
         given:
         def javaHome = new File("/jvm/$javaVersion").absoluteFile
         def metadata = JvmInstallationMetadata.from(javaHome, javaVersion, "vendor", "runtimeName", runtimeVersion, "jvmName", jvmVersion, "jvmVendor", "archName")
-        def compilerFactory = Mock(JavaCompilerFactory)
-        def toolFactory = Mock(ToolchainToolFactory)
 
         when:
-        def javaToolchain = new JavaToolchain(metadata, compilerFactory, toolFactory, TestFiles.fileFactory(), Mock(JavaToolchainInput) {
+        def javaToolchain = new JavaToolchain(metadata, TestFiles.fileFactory(), Mock(JavaToolchainInput) {
             getLanguageVersion() >> JavaLanguageVersion.of(languageVersion)
             getVendor() >> DefaultJvmVendorSpec.any().toString()
             getImplementation() >> JvmImplementation.VENDOR_SPECIFIC.toString()
-        }, false, Stub(BuildOperationProgressEventEmitter))
+        }, false)
         then:
         javaToolchain.languageVersion.asInt() == languageVersion
         javaToolchain.javaRuntimeVersion == runtimeVersion
@@ -61,7 +58,7 @@
         }
 
         when:
-        def javaToolchain = new JavaToolchain(metadata, Stub(JavaCompilerFactory), Stub(ToolchainToolFactory), TestFiles.fileFactory(), Stub(JavaToolchainInput), false, Stub(BuildOperationProgressEventEmitter))
+        def javaToolchain = new JavaToolchain(metadata, TestFiles.fileFactory(), Stub(JavaToolchainInput), false)
         def installationMetadata = javaToolchain as JavaInstallationMetadata
 
         then:
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataMatcherTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataMatcherTest.groovy
new file mode 100644
index 0000000..c216458
--- /dev/null
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/JvmInstallationMetadataMatcherTest.groovy
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2022 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.jvm.toolchain.internal
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.internal.file.TestFiles
+import org.gradle.internal.jvm.inspection.DefaultJvmMetadataDetector
+import org.gradle.internal.jvm.inspection.MetadataProbe
+import org.gradle.internal.jvm.inspection.ProbedSystemProperty
+import org.gradle.jvm.toolchain.JavaLanguageVersion
+import org.gradle.jvm.toolchain.JvmImplementation
+import org.gradle.jvm.toolchain.JvmVendorSpec
+import org.gradle.process.ExecResult
+import org.gradle.process.internal.ExecHandle
+import org.gradle.process.internal.ExecHandleBuilder
+import org.gradle.process.internal.ExecHandleFactory
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.util.TestUtil
+import spock.lang.Specification
+import spock.lang.TempDir
+
+class JvmInstallationMetadataMatcherTest extends Specification {
+
+    @TempDir
+    File temporaryFolder
+
+    TestFile tmpDir
+    def setup() {
+        tmpDir = new TestFile(new File(temporaryFolder, "tmp").tap { mkdirs() })
+    }
+
+    def "ibm vendors match semeru runtime metadata (java version: #javaVersion, vendor: #vendor, implementation: #implementation)"() {
+        given:
+        def execHandleFactory = createExecHandleFactory(systemProperties)
+        def detector = createDefaultJvmMetadataDetector(execHandleFactory)
+        def javaHome = new File(temporaryFolder, jdk).tap { mkdirs() }
+        def metadata = detector.getMetadata(testLocation(javaHome))
+
+        when:
+        def spec = new DefaultToolchainSpec(TestUtil.objectFactory())
+        spec.getLanguageVersion().set(JavaLanguageVersion.of(javaVersion.getMajorVersion()))
+        spec.getVendor().set(vendor)
+        spec.getImplementation().set(implementation)
+
+        then:
+        new JvmInstallationMetadataMatcher(spec).test(metadata)
+
+        where:
+        jdk              | systemProperties         | javaVersion             | vendor                    | implementation
+        'semeru11'       | semeruJvm11()            | JavaVersion.VERSION_11  | JvmVendorSpec.IBM         | JvmImplementation.VENDOR_SPECIFIC
+        'semeru16'       | semeruJvm16()            | JavaVersion.VERSION_16  | JvmVendorSpec.IBM         | JvmImplementation.VENDOR_SPECIFIC
+        'semeru17'       | semeruJvm17()            | JavaVersion.VERSION_17  | JvmVendorSpec.IBM         | JvmImplementation.VENDOR_SPECIFIC
+
+        'semeru11'       | semeruJvm11()            | JavaVersion.VERSION_11  | JvmVendorSpec.IBM         | JvmImplementation.J9
+        'semeru16'       | semeruJvm16()            | JavaVersion.VERSION_16  | JvmVendorSpec.IBM         | JvmImplementation.J9
+        'semeru17'       | semeruJvm17()            | JavaVersion.VERSION_17  | JvmVendorSpec.IBM         | JvmImplementation.J9
+
+        'semeru11'       | semeruJvm11()            | JavaVersion.VERSION_11  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.VENDOR_SPECIFIC
+        'semeru16'       | semeruJvm16()            | JavaVersion.VERSION_16  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.VENDOR_SPECIFIC
+        'semeru17'       | semeruJvm17()            | JavaVersion.VERSION_17  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.VENDOR_SPECIFIC
+
+        'semeru11'       | semeruJvm11()            | JavaVersion.VERSION_11  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.J9
+        'semeru16'       | semeruJvm16()            | JavaVersion.VERSION_16  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.J9
+        'semeru17'       | semeruJvm17()            | JavaVersion.VERSION_17  | JvmVendorSpec.IBM_SEMERU  | JvmImplementation.J9
+    }
+
+    def createExecHandleFactory(Map<String, String> actualProperties) {
+        def probedSystemProperties = ProbedSystemProperty.values().findAll { it != ProbedSystemProperty.Z_ERROR }
+        if (!actualProperties.isEmpty()) {
+            assert actualProperties.keySet() == probedSystemProperties.collect { it.systemPropertyKey }.toSet()
+        }
+
+        def execHandleFactory = Mock(ExecHandleFactory)
+        def exec = Mock(ExecHandleBuilder)
+        execHandleFactory.newExec() >> exec
+        PrintStream output
+        exec.setStandardOutput(_ as OutputStream) >> { OutputStream outputStream ->
+            output = new PrintStream(outputStream)
+            null
+        }
+        def handle = Mock(ExecHandle)
+        handle.start() >> handle
+        handle.waitForFinish() >> {
+            // important to output in the order of the enum members as parsing uses enum ordinals
+            probedSystemProperties.each {
+                def actualValue = actualProperties[it.systemPropertyKey]
+                // write conditionally to simulate wrong number of outputs
+                if (actualValue != null) {
+                    output.println(MetadataProbe.MARKER_PREFIX + actualValue)
+                }
+            }
+            Mock(ExecResult)
+        }
+        exec.build() >> handle
+        execHandleFactory
+    }
+
+    private DefaultJvmMetadataDetector createDefaultJvmMetadataDetector(ExecHandleFactory execHandleFactory) {
+        return new DefaultJvmMetadataDetector(
+                execHandleFactory,
+                TestFiles.tmpDirTemporaryFileProvider(tmpDir)
+        )
+    }
+
+    private InstallationLocation testLocation(File javaHome) {
+        new InstallationLocation(javaHome, "test")
+    }
+
+    private static Map<String, String> semeruJvm11() {
+        ['java.home': "java-home",
+         'java.version': "11.0.17",
+         'java.vendor': "IBM Corporation",
+         'os.arch': "x86_64",
+         'java.vm.name': "Eclipse OpenJ9 VM",
+         'java.vm.version': "openj9-0.35.0",
+         'java.vm.vendor': "Eclipse OpenJ9",
+         'java.runtime.name': "IBM Semeru Runtime Open Edition",
+         'java.runtime.version': "11.0.17+8"
+        ]
+    }
+
+    private static Map<String, String> semeruJvm16() {
+        ['java.home': "java-home",
+         'java.version': "16.0.2",
+         'java.vendor': "International Business Machines Corporation",
+         'os.arch': "x86_64",
+         'java.vm.name': "Eclipse OpenJ9 VM",
+         'java.vm.version': "openj9-0.27.0",
+         'java.vm.vendor': "Eclipse OpenJ9",
+         'java.runtime.name': "IBM Semeru Runtime Open Edition",
+         'java.runtime.version': "16.0.2+7"
+        ]
+    }
+
+    private static Map<String, String> semeruJvm17() {
+        ['java.home': "java-home",
+         'java.version': "17.0.5",
+         'java.vendor': "IBM Corporation",
+         'os.arch': "x86_64",
+         'java.vm.name': "Eclipse OpenJ9 VM",
+         'java.vm.version': "openj9-0.35.0",
+         'java.vm.vendor': "Eclipse OpenJ9",
+         'java.runtime.name': "IBM Semeru Runtime Open Edition",
+         'java.runtime.version': "17.0.5+8"
+        ]
+    }
+
+}
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/ShowToolchainsTaskTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/ShowToolchainsTaskTest.groovy
index 02c9f05..04ac249 100644
--- a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/ShowToolchainsTaskTest.groovy
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/ShowToolchainsTaskTest.groovy
@@ -19,10 +19,12 @@
 
 import org.gradle.api.internal.provider.Providers
 import org.gradle.api.provider.ProviderFactory
+import org.gradle.internal.jvm.inspection.JavaInstallationRegistry
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
 import org.gradle.internal.jvm.inspection.JvmMetadataDetector
 import org.gradle.internal.logging.text.StyledTextOutputFactory
 import org.gradle.internal.logging.text.TestStyledTextOutput
+import org.gradle.internal.progress.NoOpProgressLoggerFactory
 import org.gradle.jvm.toolchain.internal.task.ShowToolchainsTask
 import org.gradle.test.fixtures.AbstractProjectBuilderSpec
 
@@ -37,7 +39,6 @@
         detector = Mock(JvmMetadataDetector)
         output = new TestStyledTextOutput()
 
-        task.installationRegistry = Mock(JavaInstallationRegistry)
         task.metadataDetector = detector
         task.providerFactory = createProviderFactory(true, true)
         task.outputFactory = Mock(StyledTextOutputFactory) {
@@ -53,7 +54,7 @@
         def jdk82 = testLocation("1.8.0_404")
 
         given:
-        task.installationRegistry.listInstallations() >> [jdk14, jdk15, jdk9, jdk8, jdk82]
+        task.installationRegistry = createRegistry([jdk14, jdk15, jdk9, jdk8, jdk82])
         detector.getMetadata(jdk14) >> metadata("14", "+2")
         detector.getMetadata(jdk15) >> metadata("15-ea", "+2")
         detector.getMetadata(jdk9) >> metadata("9", "+2")
@@ -118,7 +119,7 @@
         def noSuchDirectory = testLocation("noSuchDirectory")
 
         given:
-        task.installationRegistry.listInstallations() >> [jdk14, invalid, noSuchDirectory]
+        task.installationRegistry = createRegistry([jdk14, invalid, noSuchDirectory])
         detector.getMetadata(jdk14) >> metadata("14", "+1")
         detector.getMetadata(invalid) >> newInvalidMetadata()
         detector.getMetadata(noSuchDirectory) >> newInvalidMetadata()
@@ -159,7 +160,7 @@
         }
 
         given:
-        task.installationRegistry.listInstallations() >> [path]
+        task.installationRegistry = createRegistry([path])
         detector.getMetadata(path) >> JvmInstallationMetadata.failure(path.location, createFailureWithNCauses(nCauses))
 
         when:
@@ -218,7 +219,7 @@
     def "reports download and detection options"() {
         given:
         task.providerFactory = createProviderFactory(false, false)
-        task.installationRegistry.listInstallations() >> []
+        task.installationRegistry = createRegistry([])
 
         when:
         task.showToolchains()
@@ -237,7 +238,7 @@
         def noSuchDirectory = testLocation("noSuchDirectory")
 
         given:
-        task.installationRegistry.listInstallations() >> [invalid, noSuchDirectory]
+        task.installationRegistry = createRegistry([invalid, noSuchDirectory])
         detector.getMetadata(invalid) >> newInvalidMetadata()
         detector.getMetadata(noSuchDirectory) >> newInvalidMetadata()
 
@@ -276,6 +277,15 @@
         providerFactory
     }
 
+    private JavaInstallationRegistry createRegistry(List<InstallationLocation> locations) {
+        return new JavaInstallationRegistry(null, detector, null, null, new NoOpProgressLoggerFactory()) {
+            @Override
+            protected Set<InstallationLocation> listInstallations() {
+                return locations;
+            }
+        }
+    }
+
     private static InstallationLocation testLocation(String javaHomePath) {
         return new InstallationLocation(new File(javaHomePath), "TestSource");
     }
@@ -292,11 +302,6 @@
         }
 
         @Override
-        protected JvmMetadataDetector getMetadataDetector() {
-            return metadataDetector
-        }
-
-        @Override
         protected StyledTextOutputFactory getTextOutputFactory() {
             outputFactory
         }
diff --git a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/ToolchainReportRendererTest.groovy b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/ToolchainReportRendererTest.groovy
index df8613c..02b80e3 100644
--- a/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/ToolchainReportRendererTest.groovy
+++ b/subprojects/platform-jvm/src/test/groovy/org/gradle/jvm/toolchain/internal/ToolchainReportRendererTest.groovy
@@ -18,9 +18,9 @@
 
 
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
+import org.gradle.internal.jvm.inspection.JvmToolchainMetadata
 import org.gradle.internal.logging.text.TestStyledTextOutput
 import org.gradle.internal.os.OperatingSystem
-import org.gradle.jvm.toolchain.internal.task.ReportableToolchain
 import org.gradle.jvm.toolchain.internal.task.ToolchainReportRenderer
 import spock.lang.Specification
 import spock.lang.TempDir
@@ -89,7 +89,7 @@
         def renderer = new ToolchainReportRenderer()
         def output = new TestStyledTextOutput()
         renderer.output = output
-        renderer.printToolchain(new ReportableToolchain(metadata, installation))
+        renderer.printToolchain(new JvmToolchainMetadata(metadata, installation))
         assert output.value == expectedOutput
     }
 }
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryBuildTypesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryBuildTypesIntegrationTest.groovy
index 166d7d0..93ba9a5 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryBuildTypesIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryBuildTypesIntegrationTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.nativeplatform.fixtures.NativePlatformsTestFixture
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class BinaryBuildTypesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     def helloWorldApp = new CppHelloWorldApp()
@@ -122,7 +122,7 @@
         executable("build/exe/main/main").exec().out == helloWorldApp.frenchOutput
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "executable with build type depends on library with matching build type"() {
         when:
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryConfigurationIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryConfigurationIntegrationTest.groovy
index 3890c60..fb726ac 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryConfigurationIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryConfigurationIntegrationTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.hamcrest.CoreMatchers
 import spock.lang.IgnoreIf
 import spock.lang.Issue
@@ -111,7 +111,7 @@
         // TODO - need to verify that the debug info ended up in the binary
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "can configure the binaries of a C++ library"() {
         given:
@@ -288,7 +288,7 @@
 
     @Issue("https://github.com/gradle/gradle-native/issues/368")
     @RequiresInstalledToolChain(VISUALCPP)
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "can configure output file for shared library on MSVC"() {
         given:
@@ -328,7 +328,7 @@
         file("build/libs/hello/shared/runtime/new_output/_hello.dll").assertExists()
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "can link to #linkage library binary with custom output file"() {
         given:
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryFlavorsIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryFlavorsIntegrationTest.groovy
index be6f24e..f1427b9 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryFlavorsIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/BinaryFlavorsIntegrationTest.groovy
@@ -20,10 +20,13 @@
 import org.gradle.nativeplatform.fixtures.NativePlatformsTestFixture
 import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.HelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class BinaryFlavorsIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     static final DEFAULT = HelloWorldApp.HELLO_WORLD
     static final FRENCH = HelloWorldApp.HELLO_WORLD_FRENCH
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryApiDependenciesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryApiDependenciesIntegrationTest.groovy
index dfce6d6..01da4a7 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryApiDependenciesIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryApiDependenciesIntegrationTest.groovy
@@ -19,10 +19,13 @@
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class LibraryApiDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     def "setup"() {
         settingsFile << "rootProject.name = 'test'"
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryBinariesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryBinariesIntegrationTest.groovy
index 9eeb57a3..7fd28e8 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryBinariesIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryBinariesIntegrationTest.groovy
@@ -18,11 +18,14 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class LibraryBinariesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     def "setup"() {
         settingsFile << "rootProject.name = 'test'"
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryDependenciesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryDependenciesIntegrationTest.groovy
index 4336ba8..ed181e2 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryDependenciesIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/LibraryDependenciesIntegrationTest.groovy
@@ -20,10 +20,13 @@
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ExeWithDiamondDependencyHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.ExeWithLibraryUsingLibraryHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class LibraryDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     def "setup"() {
         settingsFile << "rootProject.name = 'test'"
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeBinariesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeBinariesIntegrationTest.groovy
index 1aba2bd..2f4f033 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeBinariesIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeBinariesIntegrationTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.nativeplatform.fixtures.NativePlatformsTestFixture
 import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
 import org.gradle.nativeplatform.fixtures.app.CppCallingCHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.util.Matchers.containsText
 
@@ -310,7 +310,7 @@
         failure.assertThatCause(containsText("Static library archiver failed while archiving ${libName}"))
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @ToBeFixedForConfigurationCache
     def "installed executable receives command-line parameters"() {
         buildFile << """
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeDependentComponentsReportIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeDependentComponentsReportIntegrationTest.groovy
index f504d92..55ab429 100644
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeDependentComponentsReportIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/NativeDependentComponentsReportIntegrationTest.groovy
@@ -28,7 +28,6 @@
         settingsFile << "rootProject.name = 'test'"
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "displays dependents report for all components of the task's project"() {
         given:
         buildScript simpleCppBuild()
@@ -42,7 +41,6 @@
         outputContains simpleCppMainDependents()
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "displays dependents of targeted '#component' component"() {
         given:
         buildScript simpleCppBuild()
@@ -60,7 +58,6 @@
         'main'    | simpleCppMainDependents()
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "fails when targeted component is not found"() {
         given:
         buildScript simpleCppBuild()
@@ -72,7 +69,6 @@
         failure.assertHasCause "Component 'unknown' not found."
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "fails when some of the targeted components are not found"() {
         given:
         buildScript simpleBuildWithTestSuites()
@@ -84,7 +80,6 @@
         failure.assertHasCause "Components 'unknown', 'anonymous' and 'whatever' not found."
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "displays dependent of multiple targeted components"() {
         given:
         buildScript simpleCppBuild()
@@ -98,7 +93,6 @@
         !output.contains(simpleCppUtilDependents())
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "hide non-buildable dependents by default #nonBuildables"() {
         given:
         buildScript simpleCppBuild()
@@ -136,7 +130,6 @@
         ['util', 'lib', 'main'] | _
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "displays non-buildable dependents when using #option"() {
         given:
         buildScript simpleCppBuild() + '''
@@ -179,7 +172,6 @@
         '--non-buildable' | _
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "consider components with no buildable binaries as non-buildables"() {
         given:
         buildScript simpleCppBuild()
@@ -203,7 +195,6 @@
         output.contains('Some non-buildable components were not shown, use --non-buildable or --all to show them.')
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "displays dependents across projects in a build"() {
         given:
         settingsFile.text = multiProjectSettings()
@@ -229,7 +220,6 @@
     }
 
     @IgnoreIf({ GradleContextualExecuter.isParallel() })
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "can show dependent components in parallel"() {
         given: 'a multiproject build'
         settingsFile.text = multiProjectSettings()
@@ -277,7 +267,6 @@
             '''.stripIndent()
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "don't fail with prebuilt libraries"() {
         given:
         buildScript simpleBuildWithPrebuiltLibrary()
@@ -286,7 +275,6 @@
         succeeds 'dependentComponents'
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "hide test suites by default"() {
         given:
         buildScript simpleBuildWithTestSuites()
@@ -301,7 +289,6 @@
         output.contains 'Some test suites were not shown, use --test-suites or --all to show them.'
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "displays dependent test suites when using #option"() {
         given:
         buildScript simpleBuildWithTestSuites()
@@ -346,7 +333,6 @@
         '--test-suites' | _
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "direct circular dependencies are handled gracefully"() {
         buildScript simpleCppBuild()
         buildFile << '''
@@ -376,7 +362,6 @@
             '''.stripIndent().trim()
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "indirect circular dependencies are handled gracefully"() {
         buildScript simpleCppBuild()
         buildFile << '''
@@ -412,7 +397,6 @@
             '''.stripIndent().trim()
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "circular dependencies across projects are handled gracefully"() {
         given:
         settingsFile.text = multiProjectSettings()
@@ -447,7 +431,6 @@
 
     }
 
-    @ToBeFixedForConfigurationCache(because = ":dependentComponents")
     def "report renders variant binaries"() {
         buildFile << """
             apply plugin: 'cpp'
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 1bc277f..73f88f7 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
@@ -21,8 +21,8 @@
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
@@ -30,7 +30,10 @@
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.SUPPORTS_32_AND_64
 import static org.junit.Assume.assumeTrue
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class NativePlatformSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     @Rule public final Sample cppLib = sample(testDirectoryProvider, 'cpp-lib')
     @Rule public final Sample cppExe = sample(testDirectoryProvider, 'cpp-exe')
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/PrebuiltLibrariesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/PrebuiltLibrariesIntegrationTest.groovy
index e4b5541..c19e524 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/PrebuiltLibrariesIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/PrebuiltLibrariesIntegrationTest.groovy
@@ -19,10 +19,13 @@
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.NativePlatformsTestFixture
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class PrebuiltLibrariesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     final app = new CppHelloWorldApp()
 
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/SharedLibrarySoNameIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/SharedLibrarySoNameIntegrationTest.groovy
index a4e6ff6..15453cd 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/SharedLibrarySoNameIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/SharedLibrarySoNameIntegrationTest.groovy
@@ -19,10 +19,13 @@
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires([TestPrecondition.NOT_WINDOWS, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.NotWindows,
+    UnitTestPreconditions.NotMacOs
+])
 class SharedLibrarySoNameIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     def "setup"() {
         settingsFile << "rootProject.name = 'test'"
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/platform/InstallExecutableIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/platform/InstallExecutableIntegrationTest.groovy
index 15c8d4d..b005b0e 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/platform/InstallExecutableIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/platform/InstallExecutableIntegrationTest.groovy
@@ -21,12 +21,12 @@
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.PlatformDetectingTestApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 
-@Requires(TestPrecondition.UNIX)
+@Requires(UnitTestPreconditions.Unix)
 @RequiresInstalledToolChain(GCC_COMPATIBLE)
 class InstallExecutableIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     def testApp = new PlatformDetectingTestApp()
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetLinkDependenciesIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetLinkDependenciesIntegrationTest.groovy
index bb8f6e2..803942e 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetLinkDependenciesIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/sourceset/SourceSetLinkDependenciesIntegrationTest.groovy
@@ -18,10 +18,13 @@
 
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.nativeplatform.fixtures.AbstractInstalledToolChainIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires([TestPrecondition.CAN_INSTALL_EXECUTABLE, TestPrecondition.NOT_MAC_OS_X])
+@Requires([
+    UnitTestPreconditions.CanInstallExecutable,
+    UnitTestPreconditions.NotMacOs
+])
 class SourceSetLinkDependenciesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     def "setup"() {
         settingsFile << "rootProject.name = 'test'"
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCustomisationIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCustomisationIntegrationTest.groovy
index 43d181f..925cac4 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCustomisationIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/GccToolChainCustomisationIntegrationTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.GCC_COMPATIBLE
 import static org.gradle.nativeplatform.fixtures.ToolChainRequirement.SUPPORTS_32
@@ -109,7 +109,7 @@
         executable("build/exe/main/sparc/main").exec().out == helloWorldApp.englishOutput
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @ToBeFixedForConfigurationCache
     def "can configure tool executables"() {
         def binDir = testDirectory.createDir("bin")
@@ -138,7 +138,7 @@
         executable("build/exe/main/main").exec().out == helloWorldApp.frenchOutput
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @ToBeFixedForConfigurationCache
     def "can configure platform specific executables"() {
         def binDir = testDirectory.createDir("bin")
@@ -208,7 +208,7 @@
         executable("build/exe/execTest/alwaysFrench/execTest").exec().out == "C compiler used"
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     @ToBeFixedForConfigurationCache
     def "can configure setTargets with alternate toolchain"() {
         def binDir = testDirectory.createDir("bin")
diff --git a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/MultipleNativeToolChainIntegrationTest.groovy b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/MultipleNativeToolChainIntegrationTest.groovy
index 6c1765d..8a1c9ec 100755
--- a/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/MultipleNativeToolChainIntegrationTest.groovy
+++ b/subprojects/platform-native/src/integTest/groovy/org/gradle/nativeplatform/toolchain/MultipleNativeToolChainIntegrationTest.groovy
@@ -23,8 +23,8 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.fixtures.app.CppCompilerDetectingTestApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Assume
 
 @RequiresInstalledToolChain
@@ -39,7 +39,7 @@
         helloWorld.writeSources(file("src/main"))
     }
 
-    @Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+    @Requires(UnitTestPreconditions.CanInstallExecutable)
     @RequiresInstalledToolChain(ToolChainRequirement.GCC)
     @ToBeFixedForConfigurationCache
     def "can build with multiple tool chains"() {
diff --git a/subprojects/platform-native/src/main/java/org/gradle/language/nativeplatform/HeaderExportingSourceSet.java b/subprojects/platform-native/src/main/java/org/gradle/language/nativeplatform/HeaderExportingSourceSet.java
index 7ee732d..6105795 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/language/nativeplatform/HeaderExportingSourceSet.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/language/nativeplatform/HeaderExportingSourceSet.java
@@ -33,4 +33,4 @@ public interface HeaderExportingSourceSet extends LanguageSourceSet {
      * The headers that are private to this source set and implicitly available. These are not explicitly made available for compilation.
      */
     SourceDirectorySet getImplicitHeaders();
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeDependencySet.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeDependencySet.java
index 0017c7a..de23055 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeDependencySet.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeDependencySet.java
@@ -37,4 +37,4 @@ public interface NativeDependencySet {
      * Returns the files to use at runtime.
      */
     FileCollection getRuntimeFiles();
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutable.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutable.java
index 8772e9f..2bc08ea 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutable.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutable.java
@@ -24,4 +24,4 @@
 @Incubating
 public interface NativeExecutable extends Application {
     
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutableBinary.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutableBinary.java
index 7597070..7c28e71 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutableBinary.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutableBinary.java
@@ -28,4 +28,4 @@ public interface NativeExecutableBinary extends NativeBinary {
      * The executable file.
      */
     File getExecutableFile();
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutableSpec.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutableSpec.java
index d3011a9..5c12a79 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutableSpec.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeExecutableSpec.java
@@ -24,4 +24,4 @@
 @Incubating
 public interface NativeExecutableSpec extends ApplicationSpec, NativeComponentSpec, TargetedNativeComponent {
     
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeLibrary.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeLibrary.java
index 3781302..4c33af9 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeLibrary.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeLibrary.java
@@ -23,4 +23,4 @@
  */
 @Incubating
 public interface NativeLibrary extends Library {
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeLibrarySpec.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeLibrarySpec.java
index de0c034..36bd806 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeLibrarySpec.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/NativeLibrarySpec.java
@@ -37,4 +37,4 @@ public interface NativeLibrarySpec extends LibrarySpec, NativeComponentSpec, Tar
      * Converts this library to a native library requirement that uses the api library linkage.
      */
     NativeLibraryRequirement getApi();
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/SharedLibraryBinary.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/SharedLibraryBinary.java
index 788f57d..dc7aa8c 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/SharedLibraryBinary.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/SharedLibraryBinary.java
@@ -34,4 +34,4 @@ public interface SharedLibraryBinary extends NativeLibraryBinary {
      * The shared library link file.
      */
     File getSharedLibraryLinkFile();
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/StaticLibraryBinary.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/StaticLibraryBinary.java
index 3b926cf..20906a6 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/StaticLibraryBinary.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/StaticLibraryBinary.java
@@ -31,4 +31,4 @@ public interface StaticLibraryBinary extends NativeLibraryBinary {
      * The static library file.
      */
     File getStaticLibraryFile();
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpec.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpec.java
index 9ea1113..4e1d41b 100755
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpec.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/internal/DefaultNativeExecutableSpec.java
@@ -22,4 +22,4 @@ public class DefaultNativeExecutableSpec extends AbstractTargetedNativeComponent
     protected String getTypeName() {
         return "native executable";
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/NativeToolChainRegistry.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/NativeToolChainRegistry.java
index 068c012..88f361f 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/NativeToolChainRegistry.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/NativeToolChainRegistry.java
@@ -27,4 +27,4 @@
 @Incubating
 @HasInternalProtocol
 public interface NativeToolChainRegistry extends ExtensiblePolymorphicDomainObjectContainer<NativeToolChain>, ToolChainRegistry<NativePlatform, NativeToolChain> {
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/ArgsTransformer.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/ArgsTransformer.java
index f59d2c1..6034da1 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/ArgsTransformer.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/ArgsTransformer.java
@@ -22,4 +22,4 @@
 
 public interface ArgsTransformer<T extends BinaryToolSpec> extends Transformer<List<String>, T> {
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/swift/SwiftDepsHandler.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/swift/SwiftDepsHandler.java
index 2b90f58..4dea532 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/swift/SwiftDepsHandler.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/swift/SwiftDepsHandler.java
@@ -20,6 +20,7 @@
 import org.gradle.internal.IoActions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.LoaderOptions;
 import org.yaml.snakeyaml.Yaml;
 import org.yaml.snakeyaml.constructor.Constructor;
 
@@ -61,7 +62,7 @@ class SwiftDepsHandler {
 
     SwiftDeps parse(File moduleSwiftDeps) throws FileNotFoundException {
         return IoActions.withResource(new FileInputStream(moduleSwiftDeps), fileInputStream -> {
-            Yaml yaml = new Yaml(new Constructor(SwiftDeps.class));
+            Yaml yaml = new Yaml(new Constructor(SwiftDeps.class, new LoaderOptions()));
             return yaml.loadAs(fileInputStream, SwiftDeps.class);
         });
     }
diff --git a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolSearchResult.java b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolSearchResult.java
index bf1081f..7b6c355 100644
--- a/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolSearchResult.java
+++ b/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/tools/CommandLineToolSearchResult.java
@@ -22,4 +22,4 @@
 
 public interface CommandLineToolSearchResult extends ToolSearchResult {
     File getTool();
-}
\ No newline at end of file
+}
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy
index ea85210..03082cc 100644
--- a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChainTest.groovy
@@ -44,8 +44,8 @@
 import org.gradle.platform.base.internal.toolchain.SearchResult
 import org.gradle.platform.base.internal.toolchain.ToolSearchResult
 import org.gradle.process.internal.ExecActionFactory
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.TestUtil
 import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
@@ -258,7 +258,7 @@
         assert platformActionApplied == 2
     }
 
-    @Requires(TestPrecondition.NOT_MAC_OS_X)
+    @Requires(UnitTestPreconditions.NotMacOs)
     def "supplies no additional arguments to target native binary for tool chain default"() {
         def action = Mock(Action)
 
@@ -288,7 +288,7 @@
         ["-m64"]  | ["-m64"]
     }
 
-    @Requires(TestPrecondition.NOT_MAC_OS_X)
+    @Requires(UnitTestPreconditions.NotMacOs)
     def "supplies args for supported architecture for non-macOS platforms"() {
         def action = Mock(Action)
 
@@ -319,7 +319,7 @@
         "x86_64" | ["-m64"]  | ["-m64"]
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     def "supplies args for supported architecture for macOS platforms"() {
         def action = Mock(Action)
 
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/metadata/GccMetadataProviderTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/metadata/GccMetadataProviderTest.groovy
index f1c43d6..ac56bec 100644
--- a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/metadata/GccMetadataProviderTest.groovy
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/gcc/metadata/GccMetadataProviderTest.groovy
@@ -25,8 +25,8 @@
 import org.gradle.process.internal.ExecActionFactory
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.UsesNativeServices
 import org.gradle.util.internal.VersionNumber
 import org.junit.Rule
@@ -301,7 +301,7 @@
         1 * visitor.node("g++ appears to be GCC rather than Clang. Treating it as GCC.")
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "parses gcc system includes"() {
         def includes = correctPathSeparators(['/usr/local', '/usr/some/dir'])
         expect:
@@ -309,7 +309,7 @@
         result.component.systemIncludes*.path == includes
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "parses clang system includes"() {
         def includes = correctPathSeparators([
             '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include',
@@ -322,7 +322,7 @@
         result.component.systemIncludes*.path == includes
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "ignores Framework directories for GCC"() {
         def includes = [
             '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include',
@@ -335,7 +335,7 @@
         result.component.systemIncludes*.path == includes
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "parses gcc cygwin system includes and maps to windows paths"() {
         def includes = [
             '/usr/include',
diff --git a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/swift/SwiftDepsHandlerTest.groovy b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/swift/SwiftDepsHandlerTest.groovy
index d369ec5..3b4cbcf 100644
--- a/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/swift/SwiftDepsHandlerTest.groovy
+++ b/subprojects/platform-native/src/test/groovy/org/gradle/nativeplatform/toolchain/internal/swift/SwiftDepsHandlerTest.groovy
@@ -17,13 +17,13 @@
 package org.gradle.nativeplatform.toolchain.internal.swift
 
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 import spock.lang.Subject
 
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class SwiftDepsHandlerTest extends Specification {
     @Rule
     TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider(getClass())
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
index da22aac..ed85c13 100755
--- a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/AbstractInstalledToolChainIntegrationSpec.groovy
@@ -25,15 +25,15 @@
 import org.gradle.nativeplatform.internal.CompilerOutputFileNamingSchemeFactory
 import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 /**
  * Runs a test separately for each installed tool chain.
  */
 @NativeToolchainTest
 @MultiVersionTestCategory
-@Requires(TestPrecondition.NOT_MAC_OS_X)
+@Requires(UnitTestPreconditions.NotMacOs)
 abstract class AbstractInstalledToolChainIntegrationSpec extends AbstractIntegrationSpec implements HostPlatform {
     static AvailableToolChains.InstalledToolChain toolChain
     File initScript
diff --git a/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/SwiftXCTestWithDepAndCustomXCTestSuite.groovy b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/SwiftXCTestWithDepAndCustomXCTestSuite.groovy
new file mode 100644
index 0000000..4cb1eed
--- /dev/null
+++ b/subprojects/platform-native/src/testFixtures/groovy/org/gradle/nativeplatform/fixtures/app/SwiftXCTestWithDepAndCustomXCTestSuite.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 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.nativeplatform.fixtures.app
+
+class SwiftXCTestWithDepAndCustomXCTestSuite extends SwiftXCTest {
+    String testSuiteName
+    String methodName
+    String assertion
+    String[] imports
+    String[] importsTestable
+
+    SwiftXCTestWithDepAndCustomXCTestSuite(
+        String projectName,
+        String classToTest,
+        String assertion,
+        String[] imports,
+        String[] importsTestable
+    ) {
+        super(projectName)
+        this.testSuiteName = classToTest + "Test"
+        this.methodName = "test" + classToTest
+        this.assertion = assertion
+        this.imports = imports
+        this.importsTestable = importsTestable
+    }
+
+    @Override
+    List<XCTestSourceFileElement> getTestSuites() {
+        def xcTestSourceFileElement = new XCTestSourceFileElement(testSuiteName) {
+            @Override
+            List<XCTestCaseElement> getTestCases() {
+                return [testCase(methodName, assertion)]
+            }
+        }
+        imports.each { item ->
+            xcTestSourceFileElement.withImport(item)
+        }
+        importsTestable.each { item ->
+            xcTestSourceFileElement.withTestableImport(item)
+        }
+        return [xcTestSourceFileElement]
+    }
+}
diff --git a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/impldeps/GradleImplDepsConcurrencyIntegrationTest.groovy b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/impldeps/GradleImplDepsConcurrencyIntegrationTest.groovy
index 420a194..bc80857 100644
--- a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/impldeps/GradleImplDepsConcurrencyIntegrationTest.groovy
+++ b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/impldeps/GradleImplDepsConcurrencyIntegrationTest.groovy
@@ -17,10 +17,10 @@
 package org.gradle.plugin.devel.impldeps
 
 import org.gradle.api.Plugin
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.HIGH_PERFORMANCE)
+@Requires(UnitTestPreconditions.HighPerformance)
 class GradleImplDepsConcurrencyIntegrationTest extends BaseGradleImplDepsIntegrationTest {
 
     private static final int CONCURRENT_BUILDS_PROJECT_COUNT = 4
diff --git a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginPublishingIntegrationTest.groovy b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginPublishingIntegrationTest.groovy
index 5beb65a..e5031d2 100644
--- a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginPublishingIntegrationTest.groovy
+++ b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/JavaGradlePluginPluginPublishingIntegrationTest.groovy
@@ -17,7 +17,7 @@
 package org.gradle.plugin.devel.plugins
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
+import spock.lang.Issue
 
 import static org.gradle.plugin.use.resolve.internal.ArtifactRepositoriesPluginResolver.PLUGIN_MARKER_SUFFIX
 
@@ -34,7 +34,6 @@
         """
     }
 
-    @ToBeFixedForConfigurationCache(because = "publishing")
     def "Publishes main plugin artifact to Ivy"() {
         given:
         plugin('foo', 'com.example.foo')
@@ -61,7 +60,6 @@
         mavenRepo.module('com.example', 'plugins', '1.0').assertPublished()
     }
 
-    @ToBeFixedForConfigurationCache(because = "publishing")
     def "Publishes one Ivy marker for every plugin"() {
         given:
         plugin('foo', 'com.example.foo', 'The Foo Plugin', 'The greatest Foo plugin of all time.')
@@ -108,7 +106,6 @@
         }
     }
 
-    @ToBeFixedForConfigurationCache(because = "publishing")
     def "Can publish to Maven and Ivy at the same time"() {
         given:
         plugin('foo', 'com.example.foo')
@@ -127,7 +124,6 @@
         ivyRepo.module('com.example.foo', 'com.example.foo' + PLUGIN_MARKER_SUFFIX, '1.0').assertPublished()
     }
 
-    @ToBeFixedForConfigurationCache(because = "publishing")
     def "Can publish supplementary artifacts to both Maven and Ivy"() {
 
         given:
@@ -170,7 +166,79 @@
         ivyRepo.module('com.example.foo', 'com.example.foo' + PLUGIN_MARKER_SUFFIX, '1.0').assertPublished()
     }
 
-    @ToBeFixedForConfigurationCache(because = "publishing")
+    @Issue("https://github.com/gradle/gradle/issues/23551")
+    def "Can publish maven with changed artifactId"() {
+
+        given:
+        plugin('foo', 'com.example.foo')
+        publishToMaven()
+
+        and:
+        buildFile << """
+            publishing {
+                afterEvaluate {
+                    publications {
+                        getByName("pluginMaven") {
+                            configure {
+                                artifactId = "foo-new"
+                                groupId = "com.example.foo.new"
+                                version = "1.2.3"
+                            }
+                        }
+                    }
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds 'publish'
+
+
+        then:
+        mavenRepo.module('com.example.foo.new', 'foo-new', '1.2.3').assertPublished()
+
+        def module = mavenRepo.module('com.example.foo', 'com.example.foo' + PLUGIN_MARKER_SUFFIX, '1.0')
+        module.assertPublished()
+        module.getPomFile().text.contains('foo-new')
+        module.getPomFile().text.contains('com.example.foo.new')
+        module.getPomFile().text.contains('1.2.3')
+    }
+
+    def "Can publish ivy with changed artifactId"() {
+
+        given:
+        plugin('foo', 'com.example.foo')
+        publishToIvy()
+
+        and:
+        buildFile << """
+            publishing {
+                afterEvaluate {
+                    publications {
+                        getByName("pluginIvy") {
+                            configure {
+                                module = "foo-new"
+                                organisation = "com.example.foo.new"
+                                revision = "1.2.3"
+                            }
+                        }
+                    }
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds 'publish'
+
+
+        then:
+        ivyRepo.module('com.example.foo.new', 'foo-new', '1.2.3').assertPublished()
+
+        def module = ivyRepo.module('com.example.foo', 'com.example.foo' + PLUGIN_MARKER_SUFFIX, '1.0')
+        module.assertPublished()
+        module.parsedIvy.dependencies["com.example.foo.new:foo-new:1.2.3"] != null
+    }
+
     def "Can handle unspecified version"() {
         given:
         buildFile << """
diff --git a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/PrecompiledGroovyPluginsIntegrationTest.groovy b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/PrecompiledGroovyPluginsIntegrationTest.groovy
index dbe015e..f3301d2 100644
--- a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/PrecompiledGroovyPluginsIntegrationTest.groovy
+++ b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/plugins/PrecompiledGroovyPluginsIntegrationTest.groovy
@@ -19,7 +19,6 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.GradleVersion
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 
@@ -962,40 +961,36 @@
     }
 
     def "should not allow precompiled plugin to conflict with core plugin"() {
-        given:
+        when:
         enablePrecompiledPluginsInBuildSrc()
-
         file("buildSrc/src/main/groovy/plugins/java.gradle") << ""
 
-        when:
-        def failure = fails "help"
-
         then:
-        failure.assertHasCause("The precompiled plugin (${'src/main/groovy/plugins/java.gradle'.replace("/", File.separator)}) conflicts with the core plugin 'java'. Rename your plugin.\n\n"
-            + "See https://docs.gradle.org/${GradleVersion.current().version}/userguide/custom_plugins.html#sec:precompiled_plugins for more details.")
+        fails("help")
+            .assertHasCause("The precompiled plugin (${'src/main/groovy/plugins/java.gradle'.replace("/", File.separator)}) conflicts with the core plugin 'java'. Rename your plugin.")
+            .assertHasResolution(getDocLinkMessage())
+    }
+
+    private getDocLinkMessage() {
+        documentationRegistry.getDocumentationRecommendationFor("information", "custom_plugins", "sec:precompiled_plugins")
     }
 
     def "should not allow precompiled plugin to have org.gradle prefix"() {
-        given:
+        when:
         enablePrecompiledPluginsInBuildSrc()
-
         file("buildSrc/src/main/groovy/plugins/${pluginName}.gradle") << ""
 
-        when:
-        fails "help"
-
         then:
-        failure.assertHasCause("The precompiled plugin (${"src/main/groovy/plugins/${pluginName}.gradle".replace("/", File.separator)}) cannot start with 'org.gradle'.\n\n"
-            + "See https://docs.gradle.org/${GradleVersion.current().version}/userguide/custom_plugins.html#sec:precompiled_plugins for more details.")
+        fails("help")
+            .assertHasCause("The precompiled plugin (${"src/main/groovy/plugins/${pluginName}.gradle".replace("/", File.separator)}) cannot start with 'org.gradle'.")
+            .assertHasResolution(getDocLinkMessage())
 
         where:
         pluginName << ["org.gradle.my-plugin", "org.gradle"]
     }
 
     private String packagePrecompiledPlugin(String pluginFile, String pluginContent = REGISTER_SAMPLE_TASK) {
-        Map<String, String> plugins = [:]
-        plugins.putAt(pluginFile, pluginContent)
-        return packagePrecompiledPlugins(plugins)
+        return packagePrecompiledPlugins(Collections.singletonMap(pluginFile, pluginContent))
     }
 
     private String packagePrecompiledPlugins(Map<String, String> pluginToContent) {
diff --git a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/tasks/TaskFromPluginValidationIntegrationTest.groovy b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/tasks/TaskFromPluginValidationIntegrationTest.groovy
index 1d2346a..584f7e9 100644
--- a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/tasks/TaskFromPluginValidationIntegrationTest.groovy
+++ b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/tasks/TaskFromPluginValidationIntegrationTest.groovy
@@ -18,14 +18,14 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.GroovyBuildScriptLanguage
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.reflect.problems.ValidationProblemId
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.internal.reflect.validation.ValidationTestFor
 import org.gradle.test.fixtures.file.TestFile
-import spock.lang.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
-@Requires({ GradleContextualExecuter.embedded })
+@Requires(IntegTestPreconditions.IsEmbeddedExecutor)
 // this test only works in embedded mode because of the use of validation test fixtures
 class TaskFromPluginValidationIntegrationTest extends AbstractIntegrationSpec implements ValidationMessageChecker {
 
diff --git a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/tasks/ValidatePluginsIntegrationTest.groovy b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/tasks/ValidatePluginsIntegrationTest.groovy
index 7723f22..8faa00e 100644
--- a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/tasks/ValidatePluginsIntegrationTest.groovy
+++ b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/tasks/ValidatePluginsIntegrationTest.groovy
@@ -23,7 +23,9 @@
 import org.gradle.internal.reflect.problems.ValidationProblemId
 import org.gradle.internal.reflect.validation.ValidationTestFor
 import org.gradle.test.fixtures.file.TestFile
+import spock.lang.Issue
 
+import static org.gradle.util.internal.TextUtil.getPluralEnding
 import static org.hamcrest.Matchers.containsString
 import static org.junit.Assume.assumeNotNull
 
@@ -59,12 +61,12 @@
         report.verify(messages.collectEntries {
             def fullMessage = it.message
             if (!it.defaultDocLink) {
-                fullMessage = "${fullMessage}\n${learnAt(it.id, it.section)}."
+                fullMessage = "${fullMessage}\n${learnAt(it.id, it.section)}"
             }
             [(fullMessage): it.severity]
         })
 
-        failure.assertHasCause "Plugin validation failed with ${messages.size()} problem${messages.size() > 1 ? 's' : ''}"
+        failure.assertHasCause "Plugin validation failed with ${messages.size()} problem${getPluralEnding(messages)}"
         messages.forEach { problem ->
             String indentedMessage = problem.message.replaceAll('\n', '\n    ').trim()
             failure.assertThatCause(containsString("$problem.severity: $indentedMessage"))
@@ -798,6 +800,106 @@
         ])
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/23045")
+    @ValidationTestFor(
+        ValidationProblemId.NESTED_MAP_UNSUPPORTED_KEY_TYPE
+    )
+    def "nested map with #supportedType key is validated without deprecation warning"() {
+        def gStringValue = "foo"
+        javaTaskSource << """
+            import org.gradle.api.*;
+            import org.gradle.api.tasks.*;
+            import org.gradle.work.*;
+            import java.util.*;
+
+            @DisableCachingByDefault(because = "test task")
+            public class MyTask extends DefaultTask {
+                @Nested
+                public Options getOptions() {
+                    return new Options();
+                }
+
+                @Nested
+                public Map<String, Options> getMapWithGStringKey() {
+                    return Collections.singletonMap("$gStringValue", new Options());
+                }
+
+                @Nested
+                public Map<$supportedType, Options> getMapWithSupportedKey() {
+                    return Collections.singletonMap($value, new Options());
+                }
+
+                @Nested
+                public Map<$supportedType, Options> getMapEmpty() {
+                    return Collections.emptyMap();
+                }
+
+                public static class Options {
+                    @Input
+                    public String getGood() {
+                        return "good";
+                    }
+                }
+
+                @TaskAction
+                public void doStuff() { }
+            }
+
+            enum Letter { A, B, C }
+        """
+
+        expect:
+        assertValidationSucceeds()
+
+        where:
+        supportedType | value
+        'Integer'     | 'Integer.valueOf(0)'
+        'String'      | '"foo"'
+        'Enum'        | 'Letter.A'
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/23045")
+    @ValidationTestFor(
+        ValidationProblemId.NESTED_MAP_UNSUPPORTED_KEY_TYPE
+    )
+    def "nested map with unsupported key type is validated with deprecation warning"() {
+        javaTaskSource << """
+            import org.gradle.api.*;
+            import org.gradle.api.tasks.*;
+            import org.gradle.work.*;
+            import java.util.*;
+
+            @DisableCachingByDefault(because = "test task")
+            public class MyTask extends DefaultTask {
+                @Nested
+                public Options getOptions() {
+                    return new Options();
+                }
+
+                @Nested
+                public Map<Boolean, Options> getMapWithUnsupportedKey() {
+                    return Collections.singletonMap(true, new Options());
+                }
+
+                public static class Options {
+                    @Input
+                    public String getGood() {
+                        return "good";
+                    }
+                }
+
+                @TaskAction
+                public void doStuff() { }
+            }
+        """
+
+        expect:
+        executer.withArgument("-Dorg.gradle.internal.max.validation.errors=1")
+        assertValidationFailsWith([
+            warning(nestedMapUnsupportedKeyType { type('MyTask').property("mapWithUnsupportedKey").keyType("java.lang.Boolean") }, 'validation_problems', 'unsupported_key_type_of_nested_map'),
+        ])
+    }
+
     def "honors configured Java Toolchain to avoid compiled by a more recent version failure"() {
         def currentJdk = Jvm.current()
         def newerJdk = AvailableJavaHomes.getDifferentVersion { it.languageVersion > currentJdk.javaVersion }
diff --git a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/variants/GradlePluginWithVariantsPublicationIntegrationTest.groovy b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/variants/GradlePluginWithVariantsPublicationIntegrationTest.groovy
index 1b72e9b..fb2dc83 100644
--- a/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/variants/GradlePluginWithVariantsPublicationIntegrationTest.groovy
+++ b/subprojects/plugin-development/src/integTest/groovy/org/gradle/plugin/devel/variants/GradlePluginWithVariantsPublicationIntegrationTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.plugin.devel.variants
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class GradlePluginWithVariantsPublicationIntegrationTest extends AbstractIntegrationSpec {
 
-    @Requires(TestPrecondition.JDK15_OR_EARLIER) // older Gradle version 6.7.1 is used in test
+    @Requires(UnitTestPreconditions.Jdk15OrEarlier) // older Gradle version 6.7.1 is used in test
     def "can publish and use Gradle plugin with multiple variants"() {
         given:
         def producer = file('producer')
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/internal/precompiled/PrecompiledGroovyPluginsPlugin.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/internal/precompiled/PrecompiledGroovyPluginsPlugin.java
index 562928a..11f78e0 100644
--- a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/internal/precompiled/PrecompiledGroovyPluginsPlugin.java
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/internal/precompiled/PrecompiledGroovyPluginsPlugin.java
@@ -16,7 +16,6 @@
 
 package org.gradle.plugin.devel.internal.precompiled;
 
-import org.gradle.api.GradleException;
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.file.DirectoryProperty;
@@ -100,13 +99,15 @@ private void exposeScriptsAsPlugins(Project project) {
 
     private void validateScriptPlugin(Project project, PrecompiledGroovyScript scriptPlugin) {
         if (scriptPlugin.getId().equals(CORE_PLUGIN_NAMESPACE) || scriptPlugin.getId().startsWith(CORE_PLUGIN_PREFIX)) {
-            throw new GradleException(String.format("The precompiled plugin (%s) cannot start with '%s'.\n\n%s.",
-                project.relativePath(scriptPlugin.getFileName()), CORE_PLUGIN_NAMESPACE, PRECOMPILED_SCRIPT_MANUAL.consultDocumentationMessage()));
+            throw new PrecompiledScriptException(
+                String.format("The precompiled plugin (%s) cannot start with '%s'.", project.relativePath(scriptPlugin.getFileName()), CORE_PLUGIN_NAMESPACE),
+                PRECOMPILED_SCRIPT_MANUAL.consultDocumentationMessage());
         }
         Plugin<?> existingPlugin = project.getPlugins().findPlugin(scriptPlugin.getId());
         if (existingPlugin != null && existingPlugin.getClass().getPackage().getName().startsWith(CORE_PLUGIN_PREFIX)) {
-            throw new GradleException(String.format("The precompiled plugin (%s) conflicts with the core plugin '%s'. Rename your plugin.\n\n%s", project.relativePath(scriptPlugin.getFileName()), scriptPlugin.getId(),
-                PRECOMPILED_SCRIPT_MANUAL.consultDocumentationMessage()));
+            throw new PrecompiledScriptException(
+                String.format("The precompiled plugin (%s) conflicts with the core plugin '%s'. Rename your plugin.", project.relativePath(scriptPlugin.getFileName()), scriptPlugin.getId()),
+                PRECOMPILED_SCRIPT_MANUAL.consultDocumentationMessage());
         }
     }
 
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/internal/precompiled/PrecompiledScriptException.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/internal/precompiled/PrecompiledScriptException.java
new file mode 100644
index 0000000..c57d884
--- /dev/null
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/internal/precompiled/PrecompiledScriptException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 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.plugin.devel.internal.precompiled;
+
+import org.gradle.api.GradleException;
+import org.gradle.internal.exceptions.ResolutionProvider;
+
+import java.util.Collections;
+import java.util.List;
+
+public class PrecompiledScriptException extends GradleException implements ResolutionProvider {
+    private final List<String> resolutions;
+
+    public PrecompiledScriptException(String message, String resolution) {
+        super(message);
+        this.resolutions = Collections.singletonList(resolution);
+    }
+
+    @Override
+    public List<String> getResolutions() {
+        return resolutions;
+    }
+}
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/package-info.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/package-info.java
index 7758d5f..8c7d6723 100644
--- a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/package-info.java
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Classes for assisting with plugin development.
  */
-package org.gradle.plugin.devel;
\ No newline at end of file
+package org.gradle.plugin.devel;
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/IvyPluginPublishingPlugin.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/IvyPluginPublishingPlugin.java
index f11a818..40ba3e2 100644
--- a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/IvyPluginPublishingPlugin.java
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/IvyPluginPublishingPlugin.java
@@ -21,6 +21,8 @@
 import org.gradle.api.Project;
 import org.gradle.api.XmlProvider;
 import org.gradle.api.component.SoftwareComponent;
+import org.gradle.api.provider.Provider;
+import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.publish.PublicationContainer;
 import org.gradle.api.publish.PublishingExtension;
 import org.gradle.api.publish.ivy.IvyModuleDescriptorDescription;
@@ -41,6 +43,9 @@
 abstract class IvyPluginPublishingPlugin implements Plugin<Project> {
 
     @Inject
+    protected abstract ProviderFactory getProviderFactory();
+
+    @Inject
     public IvyPluginPublishingPlugin() {
         // This class is not visible outside of this package.
         // To instantiate this plugin, we need a public constructor.
@@ -89,6 +94,11 @@ private void createIvyMarkerPublication(final PluginDeclaration declaration, fin
         publication.setAlias(true);
         publication.setOrganisation(pluginId);
         publication.setModule(pluginId + PLUGIN_MARKER_SUFFIX);
+
+        Provider<String> organisation = getProviderFactory().provider(mainPublication::getOrganisation);
+        Provider<String> module = getProviderFactory().provider(mainPublication::getModule);
+        Provider<String> revision = getProviderFactory().provider(mainPublication::getRevision);
+
         publication.descriptor(new Action<IvyModuleDescriptorSpec>() {
             @Override
             public void execute(IvyModuleDescriptorSpec descriptor) {
@@ -106,13 +116,13 @@ public void execute(XmlProvider xmlProvider) {
                         Node dependencies = root.getElementsByTagName("dependencies").item(0);
                         Node dependency = dependencies.appendChild(document.createElement("dependency"));
                         Attr org = document.createAttribute("org");
-                        org.setValue(mainPublication.getOrganisation());
+                        org.setValue(organisation.get());
                         dependency.getAttributes().setNamedItem(org);
                         Attr name = document.createAttribute("name");
-                        name.setValue(mainPublication.getModule());
+                        name.setValue(module.get());
                         dependency.getAttributes().setNamedItem(name);
                         Attr rev = document.createAttribute("rev");
-                        rev.setValue(mainPublication.getRevision());
+                        rev.setValue(revision.get());
                         dependency.getAttributes().setNamedItem(rev);
                     }
                 });
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java
index 8ad4806..1fd1ca7 100644
--- a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java
@@ -178,7 +178,7 @@ private void configureJarTask(Project project, GradlePluginDevelopmentExtension
     }
 
     private GradlePluginDevelopmentExtension createExtension(Project project) {
-        SourceSet defaultPluginSourceSet = JavaPluginHelper.getJavaComponent(project).getSourceSet();
+        SourceSet defaultPluginSourceSet = JavaPluginHelper.getJavaComponent(project).getMainFeature().getSourceSet();
         SourceSet defaultTestSourceSet = JavaPluginHelper.getDefaultTestSuite(project).getSources();
         return project.getExtensions().create(EXTENSION_NAME, GradlePluginDevelopmentExtension.class, project, defaultPluginSourceSet, defaultTestSourceSet);
     }
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/MavenPluginPublishPlugin.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/MavenPluginPublishPlugin.java
index 395bd00..335b297 100644
--- a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/MavenPluginPublishPlugin.java
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/MavenPluginPublishPlugin.java
@@ -21,6 +21,8 @@
 import org.gradle.api.Project;
 import org.gradle.api.XmlProvider;
 import org.gradle.api.component.SoftwareComponent;
+import org.gradle.api.provider.Provider;
+import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.publish.PublicationContainer;
 import org.gradle.api.publish.PublishingExtension;
 import org.gradle.api.publish.maven.MavenPublication;
@@ -38,6 +40,9 @@
 abstract class MavenPluginPublishPlugin implements Plugin<Project> {
 
     @Inject
+    protected abstract ProviderFactory getProviderFactory();
+
+    @Inject
     public MavenPluginPublishPlugin() {
         // This class is not visible outside of this package.
         // To instantiate this plugin, we need a protected constructor.
@@ -82,13 +87,14 @@ private void addMarkerPublications(MavenPublication mainPublication, PublishingE
 
     private void createMavenMarkerPublication(PluginDeclaration declaration, final MavenPublication coordinates, PublicationContainer publications) {
         String pluginId = declaration.getId();
-        String pluginGroupId = coordinates.getGroupId();
-        String pluginArtifactId = coordinates.getArtifactId();
-        String pluginVersion = coordinates.getVersion();
         MavenPublicationInternal publication = (MavenPublicationInternal) publications.create(declaration.getName() + "PluginMarkerMaven", MavenPublication.class);
         publication.setAlias(true);
         publication.setArtifactId(pluginId + PLUGIN_MARKER_SUFFIX);
         publication.setGroupId(pluginId);
+
+        Provider<String> groupProvider = getProviderFactory().provider(coordinates::getGroupId);
+        Provider<String> artifactIdProvider = getProviderFactory().provider(coordinates::getArtifactId);
+        Provider<String> versionProvider = getProviderFactory().provider(coordinates::getVersion);
         publication.getPom().withXml(new Action<XmlProvider>() {
             @Override
             public void execute(XmlProvider xmlProvider) {
@@ -97,11 +103,11 @@ public void execute(XmlProvider xmlProvider) {
                 Node dependencies = root.appendChild(document.createElement("dependencies"));
                 Node dependency = dependencies.appendChild(document.createElement("dependency"));
                 Node groupId = dependency.appendChild(document.createElement("groupId"));
-                groupId.setTextContent(pluginGroupId);
+                groupId.setTextContent(groupProvider.get());
                 Node artifactId = dependency.appendChild(document.createElement("artifactId"));
-                artifactId.setTextContent(pluginArtifactId);
+                artifactId.setTextContent(artifactIdProvider.get());
                 Node version = dependency.appendChild(document.createElement("version"));
-                version.setTextContent(pluginVersion);
+                version.setTextContent(versionProvider.get());
             }
         });
         publication.getPom().getName().set(declaration.getDisplayName());
diff --git a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/tasks/ValidatePlugins.java b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/tasks/ValidatePlugins.java
index 7fd6bb6..9b23ab9 100644
--- a/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/tasks/ValidatePlugins.java
+++ b/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/tasks/ValidatePlugins.java
@@ -106,14 +106,13 @@ public void validateTaskClasses() throws IOException {
         } else {
             if (getFailOnWarning().get() || problemMessages.stream().anyMatch(line -> line.startsWith("Error:"))) {
                 if (getIgnoreFailures().get()) {
-                    getLogger().warn("Plugin validation finished with errors. See {} for more information on how to annotate task properties.{}",
-                        getDocumentationRegistry().getDocumentationFor("incremental_build", "sec:task_input_output_annotations"),
+                    getLogger().warn("Plugin validation finished with errors. {} {}",
+                        annotateTaskPropertiesDoc(),
                         toMessageList(problemMessages));
                 } else {
                     throw WorkValidationException.forProblems(problemMessages)
                         .withSummaryForPlugin()
-                        .getWithExplanation(String.format("See %s for more information on how to annotate task properties.",
-                            getDocumentationRegistry().getDocumentationFor("incremental_build", "sec:task_input_output_annotations")));
+                        .getWithExplanation(annotateTaskPropertiesDoc());
                 }
             } else {
                 getLogger().warn("Plugin validation finished with warnings:{}",
@@ -122,6 +121,10 @@ public void validateTaskClasses() throws IOException {
         }
     }
 
+    private String annotateTaskPropertiesDoc() {
+        return getDocumentationRegistry().getDocumentationRecommendationFor("on how to annotate task properties", "incremental_build", "sec:task_input_output_annotations");
+    }
+
     private static List<String> parseMessageList(List<String> lines) {
         List<String> list = Lists.newArrayList();
         StringBuilder sb = new StringBuilder();
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/repository/ResolvingFromMultipleCustomPluginRepositorySpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/repository/ResolvingFromMultipleCustomPluginRepositorySpec.groovy
index cf35a6f..b699d2f 100644
--- a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/repository/ResolvingFromMultipleCustomPluginRepositorySpec.groovy
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/repository/ResolvingFromMultipleCustomPluginRepositorySpec.groovy
@@ -23,8 +23,8 @@
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.gradle.test.fixtures.maven.MavenModule
 import org.gradle.test.fixtures.plugin.PluginBuilder
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 import spock.lang.Shared
 
@@ -202,7 +202,7 @@
         repoType << [IVY, MAVEN]
     }
 
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     def "Can opt-in to plugin portal"() {
         given:
         publishPlugins(MAVEN)
@@ -228,7 +228,7 @@
     }
 
     @Issue("GRADLE-3502")
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     def "Plugin Portal provides transitive dependencies for other plugins"() {
         given:
         publishPlugins(MAVEN)
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/repository/ResolvingFromSingleCustomPluginRepositorySpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/repository/ResolvingFromSingleCustomPluginRepositorySpec.groovy
index ee0fb71..0d0be54 100644
--- a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/repository/ResolvingFromSingleCustomPluginRepositorySpec.groovy
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/repository/ResolvingFromSingleCustomPluginRepositorySpec.groovy
@@ -23,8 +23,8 @@
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.gradle.test.fixtures.maven.MavenRepository
 import org.gradle.test.fixtures.plugin.PluginBuilder
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 @LeaksFileHandles
 class ResolvingFromSingleCustomPluginRepositorySpec extends AbstractDependencyResolutionTest {
@@ -162,7 +162,7 @@
         repoType << [IVY, MAVEN]
     }
 
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     def "Fails gracefully if a plugin is not found in #repoType repo"() {
         given:
         publishTestPlugin(repoType)
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/DeployedPortalIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/DeployedPortalIntegrationSpec.groovy
index 2e566fa..9e90c6c 100644
--- a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/DeployedPortalIntegrationSpec.groovy
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/DeployedPortalIntegrationSpec.groovy
@@ -18,13 +18,13 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.LeaksFileHandles
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.hamcrest.CoreMatchers.startsWith
 
 //These tests depend on https://plugins.gradle.org
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 @LeaksFileHandles
 class DeployedPortalIntegrationSpec extends AbstractIntegrationSpec {
 
diff --git a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseDslIntegrationSpec.groovy b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseDslIntegrationSpec.groovy
index 65c87b8..a91f9a8 100644
--- a/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseDslIntegrationSpec.groovy
+++ b/subprojects/plugin-use/src/integTest/groovy/org/gradle/plugin/use/PluginUseDslIntegrationSpec.groovy
@@ -200,6 +200,7 @@
         2          | "apply false"                          | BASE_MESSAGE
         2          | "id 'foo' apply"                       | BASE_MESSAGE
         2          | "id 'foo' apply('foo')"                | NEED_SINGLE_BOOLEAN
+        2          | "apply plugin: 'java'"                 | NEED_SINGLE_BOOLEAN
         2          | "id null"                              | NEED_LITERAL_STRING
         2          | "id 'foo' version null"                | NEED_INTERPOLATED_STRING
         2          | "file('foo')" /* script api */         | BASE_MESSAGE
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginArtifactRepositories.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginArtifactRepositories.java
index 5a8f111..a424f41 100644
--- a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginArtifactRepositories.java
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/internal/DefaultPluginArtifactRepositories.java
@@ -56,7 +56,7 @@ public PluginResolver getPluginResolver() {
     public void applyRepositoriesTo(RepositoryHandler repositories) {
         if (isExclusiveContentInUse() && !repositories.isEmpty()) {
             throw new InvalidUserCodeException("When using exclusive repository content in 'settings.pluginManagement.repositories', you cannot add repositories to 'buildscript.repositories'.\n" +
-                "See the documentation in " + new DocumentationRegistry().getDocumentationFor("declaring_repositories", "declaring_content_exclusively_found_in_one_repository") + ".");
+                new DocumentationRegistry().getDocumentationRecommendationFor("information", "declaring_repositories", "declaring_content_exclusively_found_in_one_repository"));
         }
         repositories.addAll(dependencyResolutionServices.getResolveRepositoryHandler());
     }
diff --git a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CorePluginResolver.java b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CorePluginResolver.java
index be7c479..16cb615 100644
--- a/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CorePluginResolver.java
+++ b/subprojects/plugin-use/src/main/java/org/gradle/plugin/use/resolve/internal/CorePluginResolver.java
@@ -46,7 +46,7 @@ public void resolve(PluginRequestInternal pluginRequest, PluginResolutionResult
 
         PluginImplementation<?> plugin = pluginRegistry.lookup(id);
         if (plugin == null) {
-            result.notFound(getDescription(), format("not a core plugin, please see %s for available core plugins", documentationRegistry.getDocumentationFor("plugin_reference")));
+            result.notFound(getDescription(), "not a core plugin. " + documentationRegistry.getDocumentationRecommendationFor("available plugins", "plugin_reference"));
             return;
         }
 
@@ -54,28 +54,32 @@ public void resolve(PluginRequestInternal pluginRequest, PluginResolutionResult
         result.found(getDescription(), new SimplePluginResolution(plugin));
     }
 
-    private void validate(PluginRequestInternal pluginRequest) {
+    private static void validate(PluginRequestInternal pluginRequest) {
         if (pluginRequest.getVersion() != null) {
             throw new InvalidPluginRequestException(pluginRequest,
-                "Plugin '" + pluginRequest.getId() + "' is a core Gradle plugin, which cannot be specified with a version number. "
+                getCorePluginClarification(pluginRequest) + "which cannot be specified with a version number. "
                     + "Such plugins are versioned as part of Gradle. Please remove the version number from the declaration."
             );
         }
         if (pluginRequest.getModule() != null) {
             throw new InvalidPluginRequestException(pluginRequest,
-                "Plugin '" + pluginRequest.getId() + "' is a core Gradle plugin, which cannot be specified with a custom implementation artifact. "
+                getCorePluginClarification(pluginRequest) + "which cannot be specified with a custom implementation artifact. "
                     + "Such plugins are versioned as part of Gradle. Please remove the custom artifact from the request."
             );
         }
         if (!pluginRequest.isApply()) {
             throw new InvalidPluginRequestException(pluginRequest,
-                "Plugin '" + pluginRequest.getId() + "' is a core Gradle plugin, which is already on the classpath. "
+                getCorePluginClarification(pluginRequest) + "which is already on the classpath. "
                     + "Requesting it with the 'apply false' option is a no-op."
             );
         }
     }
 
-    private boolean isCorePluginRequest(PluginId id) {
+    private static String getCorePluginClarification(PluginRequestInternal pluginRequest) {
+        return "Plugin '" + pluginRequest.getId() + "' is a core Gradle plugin, ";
+    }
+
+    private static  boolean isCorePluginRequest(PluginId id) {
         String namespace = id.getNamespace();
         return namespace == null || namespace.equals(CORE_PLUGIN_NAMESPACE);
     }
diff --git a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/ArtifactRepositoriesPluginResolverTest.groovy b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/ArtifactRepositoriesPluginResolverTest.groovy
index a9b9f70..95a4320 100644
--- a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/ArtifactRepositoriesPluginResolverTest.groovy
+++ b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/ArtifactRepositoriesPluginResolverTest.groovy
@@ -17,10 +17,10 @@
 package org.gradle.plugin.use.resolve.internal
 
 import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
 import org.gradle.api.artifacts.ResolvedConfiguration
 import org.gradle.api.artifacts.dsl.RepositoryHandler
 import org.gradle.api.internal.artifacts.DependencyResolutionServices
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal
 import org.gradle.api.internal.artifacts.repositories.ArtifactRepositoryInternal
 import org.gradle.api.internal.attributes.AttributesSchemaInternal
 import org.gradle.groovy.scripts.TextResourceScriptSource
@@ -46,7 +46,7 @@
         getResolvedConfiguration() >> resolvedConfiguration
         setTransitive(false) >> {}
     }
-    def configurations = Mock(ConfigurationContainer) {
+    def configurations = Mock(RoleBasedConfigurationContainerInternal) {
         detachedConfiguration(_) >> configuration
     }
 
diff --git a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/CorePluginResolverTest.groovy b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/CorePluginResolverTest.groovy
index 3b3a955..2a2a8e1 100644
--- a/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/CorePluginResolverTest.groovy
+++ b/subprojects/plugin-use/src/test/groovy/org/gradle/plugin/use/resolve/internal/CorePluginResolverTest.groovy
@@ -37,9 +37,7 @@
         }
     }
 
-    def docRegistry = Mock(DocumentationRegistry) {
-        it.getDocumentationFor(_ as String) >> { String page -> "https://docs.gradle.org/current/userguide/${page}.html"}
-    }
+    def docRegistry = new DocumentationRegistry()
     def pluginRegistry = Mock(PluginRegistry)
     def result = Mock(PluginResolutionResult)
 
@@ -104,7 +102,7 @@
         then:
         1 * pluginRegistry.lookup(DefaultPluginId.of("org.gradle.foo")) >> null
         1 * result.notFound(resolver.getDescription(), {
-            it.contains("not a core plugin, please see https://docs.gradle.org/current/userguide/plugin_reference.html for available core plugins")
+            it.contains("not a core plugin. " + new DocumentationRegistry().getDocumentationRecommendationFor("available plugins","plugin_reference"))
         })
     }
 
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginConfigurationIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginConfigurationIntegrationTest.groovy
index 3509818..288fab4 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginConfigurationIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginConfigurationIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.ScriptExecuter
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 
 class ApplicationPluginConfigurationIntegrationTest extends AbstractIntegrationSpec {
@@ -61,7 +61,7 @@
         out.toString() == TextUtil.toPlatformLineSeparators("all good\n")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "can configure using project extension for main class and main module"() {
         settingsFile << """
             rootProject.name = 'test'
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginIntegrationTest.groovy
index b9b36bd..2af4414 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginIntegrationTest.groovy
@@ -15,16 +15,14 @@
  */
 package org.gradle.api.plugins
 
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
 import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.jvm.Jvm
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.IgnoreIf
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 class ApplicationPluginIntegrationTest extends WellBehavedPluginTest {
@@ -46,7 +44,7 @@
         assertGeneratedWindowsStartScript()
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "can generate start scripts with module path"() {
         given:
         configureMainModule()
@@ -67,9 +65,11 @@
     def "can generate starts script generation with custom user configuration"() {
         given:
         buildFile << """
-applicationName = 'myApp'
-applicationDefaultJvmArgs = ["-Dgreeting.language=en", "-DappId=\${project.name - ':'}"]
-"""
+        application {
+            applicationName = 'myApp'
+            applicationDefaultJvmArgs = ["-Dgreeting.language=en", "-DappId=\${project.name - ':'}"]
+        }
+        """
 
         when:
         succeeds('startScripts')
@@ -83,7 +83,6 @@
         given:
         file('customUnixStartScript.txt') << '${applicationName} start up script for UN*X'
         file('customWindowsStartScript.txt') << '${applicationName} start up script for Windows'
-
         buildFile << """
 startScripts {
     applicationName = 'myApp'
@@ -91,6 +90,7 @@
     windowsStartScriptGenerator.template = resources.text.fromFile(file('customWindowsStartScript.txt'))
 }
 """
+
         when:
         succeeds('startScripts')
 
@@ -122,6 +122,7 @@
     }
 }
 '''
+
         when:
         succeeds('startScripts')
 
@@ -132,7 +133,7 @@
         windowsStartScript.text == 'myApp start up script for Windows'
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "can execute generated Unix start script"() {
         when:
         succeeds('installDist')
@@ -147,7 +148,7 @@
         outputContains('Hello World!')
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "can execute generated Unix start script using JAVA_HOME with spaces"() {
         given:
         def testJavaHome = file("javahome/java home with spaces")
@@ -169,7 +170,7 @@
         testJavaHome.usingNativeTools().deleteDir() //remove symlink
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "java PID equals script PID"() {
         given:
         succeeds('installDist')
@@ -189,7 +190,7 @@
         assert pids[0] == pids[1]
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "can execute generated Windows start script"() {
         when:
         succeeds('installDist')
@@ -257,6 +258,7 @@
     compileOnly 'org.gradle.test:compileOnly:1.0'
 }
 """
+
         when:
         run "installDist"
 
@@ -267,7 +269,7 @@
     def "executables can be placed at the root of the distribution"() {
         given:
         buildFile << """
-executableDir = ''
+application.executableDir = ''
 """
         when:
         run "installDist"
@@ -278,6 +280,7 @@
 
         when:
         runViaStartScript(file('build/install/sample'))
+
         then:
         outputContains("Hello World")
     }
@@ -285,7 +288,7 @@
     def "executables can be placed in a custom directory"() {
         given:
         buildFile << """
-executableDir = 'foo/bar'
+application.executableDir = 'foo/bar'
 """
         when:
         run "installDist"
@@ -296,14 +299,14 @@
 
         when:
         runViaStartScript(file('build/install/sample/foo/bar'))
+
         then:
         outputContains("Hello World")
     }
 
     def "includes transitive implementation dependencies in distribution"() {
-        mavenRepo.module('org.gradle.test', 'implementation', '1.0').publish()
-
         given:
+        mavenRepo.module('org.gradle.test', 'implementation', '1.0').publish()
         buildFile << """
             allprojects {
                 repositories {
@@ -311,7 +314,6 @@
                 }
             }
         """
-
         file('settings.gradle') << "include 'utils', 'core'"
         buildFile << '''
             apply plugin: 'java'
@@ -348,9 +350,8 @@
     }
 
     def "includes transitive runtime dependencies in runtime classpath"() {
-        mavenRepo.module('org.gradle.test', 'implementation', '1.0').publish()
-
         given:
+        mavenRepo.module('org.gradle.test', 'implementation', '1.0').publish()
         buildFile << """
         allprojects {
             repositories {
@@ -402,9 +403,8 @@
     }
 
     def "includes transitive implementation dependencies in test runtime classpath"() {
-        mavenRepo.module('org.gradle.test', 'implementation', '1.0').publish()
-
         given:
+        mavenRepo.module('org.gradle.test', 'implementation', '1.0').publish()
         buildFile << """
         allprojects {
             repositories {
@@ -413,7 +413,6 @@
             apply plugin: 'java'
         }
         """
-
         file('settings.gradle') << "include 'utils', 'core', 'foo', 'bar'"
         buildFile << '''
             apply plugin: 'java'
@@ -465,12 +464,11 @@
         (lines.find { it.startsWith 'set CLASSPATH=' } - 'set CLASSPATH=').split(';').collect([] as Set) { it - '%APP_HOME%\\lib\\' }
     }
 
-    @ToBeFixedForConfigurationCache
     @Issue("https://github.com/gradle/gradle/issues/21505")
     def "run task honors applicationDefaultJvmArgs"() {
         given:
         buildFile """
-            applicationDefaultJvmArgs = ['-DFOO=42']
+            application.applicationDefaultJvmArgs = ['-DFOO=42']
         """
         when:
         succeeds 'run'
@@ -482,7 +480,7 @@
     def "can use APP_HOME in DEFAULT_JVM_OPTS with custom start script"() {
         given:
         buildFile << """
-applicationDefaultJvmArgs = ["-DappHomeSystemProp=REPLACE_THIS_WITH_APP_HOME"]
+application.applicationDefaultJvmArgs = ["-DappHomeSystemProp=REPLACE_THIS_WITH_APP_HOME"]
 
 startScripts {
     doLast {
@@ -491,8 +489,10 @@
     }
 }
 """
+
         when:
         succeeds('installDist')
+
         and:
         runViaStartScript()
 
@@ -504,7 +504,7 @@
         OperatingSystem.current().isWindows() ? runViaWindowsStartScript(startScriptDir) : runViaUnixStartScript(startScriptDir)
     }
 
-    @IgnoreIf({ TestPrecondition.WINDOWS.fulfilled })
+    @Requires(UnitTestPreconditions.NotWindows)
     // This test already fails silently on Windows, but adding an explicit check for the existence of xargs made it fail explicitly.
     def "can run under posix sh environment"() {
         buildFile << """
@@ -520,8 +520,7 @@
     }
 
     // Paths to cygpath are only available when running under the embedded executor
-    @Requires(TestPrecondition.WINDOWS)
-    @IgnoreIf({ !GradleContextualExecuter.embedded })
+    @Requires([UnitTestPreconditions.Windows, IntegTestPreconditions.IsEmbeddedExecutor])
     def "can pass absolute Unix-like paths to script on Windows"() {
         file("run.sh") << '''#!/bin/sh
 # convert paths into absolute Unix-like paths
@@ -537,8 +536,10 @@
 """
         when:
         succeeds('installDist')
+
         then:
         succeeds("execStartScript")
+
         and:
         // confirm that the arguments were converted back to Windows-like paths (using forward slashes instead of backslashes)
         outputContains("Args: [${buildFile.absolutePath.replace('\\', '/')}, ${file("src").absolutePath.replace('\\', '/')}]")
@@ -627,6 +628,7 @@
             // This should cause us to regenerated the script
             version = "3.0"
         """
+
         and:
         succeeds("startScripts")
 
@@ -649,7 +651,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/4627")
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "distribution not in root directory has correct permissions set"() {
         given:
         buildFile << """
@@ -678,7 +680,7 @@
         executed(':compileJava', ':processResources', ':classes', ':run')
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "runs the jar for modular applications"() {
         given:
         configureMainModule()
@@ -691,7 +693,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle-private/issues/3386")
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "does not execute code in user-set environment variable"() {
         when:
         succeeds('installDist')
@@ -720,7 +722,7 @@
         envVar << ["JAVA_OPTS", "SAMPLE_OPTS"]
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "environment variables can have spaces in their values"() {
         when:
         succeeds('installDist')
@@ -745,7 +747,7 @@
         envVar << ["JAVA_OPTS", "SAMPLE_OPTS"]
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "environment variables can have spaces in their values that should be treated as separate tokens"() {
         when:
         succeeds('installDist')
@@ -770,7 +772,7 @@
         envVar << ["JAVA_OPTS", "SAMPLE_OPTS"]
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "environment variables that do not have spaces in their values that should be treated as separate tokens"() {
         when:
         succeeds('installDist')
@@ -795,7 +797,7 @@
         envVar << ["JAVA_OPTS", "SAMPLE_OPTS"]
     }
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     def "environment variables that do not have spaces in their values that should be treated as one token"() {
         when:
         succeeds('installDist')
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginUnixShellsIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginUnixShellsIntegrationTest.groovy
index 03c4d60..f6df566 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginUnixShellsIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/ApplicationPluginUnixShellsIntegrationTest.groovy
@@ -18,8 +18,9 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.executer.ExecutionResult
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.PluginTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 class ApplicationPluginUnixShellsIntegrationTest extends AbstractIntegrationSpec {
     def setup() {
@@ -32,20 +33,7 @@
         }
     }
 
-    static boolean shellAvailable(String shellCommand) {
-        return TestPrecondition.UNIX_DERIVATIVE.isFulfilled() && locate(shellCommand)
-    }
-
-    static File locate(String shellCommand) {
-        return [
-                new File("/bin/$shellCommand"),
-                new File("/usr/bin/$shellCommand"),
-                new File("/usr/local/bin/$shellCommand"),
-                new File("/opt/local/bin/$shellCommand")
-        ].find { it.exists() }
-    }
-
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("bash") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.BashAvailable])
     def "can execute generated Unix start script in Bash"() {
         given:
         succeeds('installDist')
@@ -57,7 +45,7 @@
         outputContains('Hello World!')
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("dash") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.DashAvailable])
     def "can execute generated Unix start script in Dash"() {
         given:
         succeeds('installDist')
@@ -69,7 +57,7 @@
         outputContains('Hello World!')
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("static-sh") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.StaticShAvailable])
     def "can execute generated Unix start script in BusyBox"() {
         given:
         succeeds('installDist')
@@ -81,7 +69,7 @@
         outputContains('Hello World!')
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("bash") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.BashAvailable])
     def "can use APP_HOME in DEFAULT_JVM_OPTS with custom start script in Bash"() {
         given:
         extendBuildFileWithAppHomeProperty()
@@ -94,7 +82,7 @@
         outputContains("App Home: ${file('build/install/sample').absolutePath}")
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("dash") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.DashAvailable])
     def "can use APP_HOME in DEFAULT_JVM_OPTS with custom start script in Dash"() {
         given:
         extendBuildFileWithAppHomeProperty()
@@ -107,7 +95,7 @@
         outputContains("App Home: ${file('build/install/sample').absolutePath}")
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("static-sh") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.StaticShAvailable])
     def "can use APP_HOME in DEFAULT_JVM_OPTS with custom start script in BusyBox"() {
         given:
         extendBuildFileWithAppHomeProperty()
@@ -120,7 +108,7 @@
         outputContains("App Home: ${file('build/install/sample').absolutePath}")
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("bash") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.BashAvailable])
     def "can pass argument to App with custom start script in Bash"() {
         given:
         succeeds('installDist')
@@ -135,7 +123,7 @@
         outputContains('Arg: -DGOO=\'car < caz\'')
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("dash") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.DashAvailable])
     def "can pass argument to App with custom start script in Dash"() {
         given:
         succeeds('installDist')
@@ -150,7 +138,7 @@
         outputContains('Arg: -DGOO=\'car < caz\'')
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("static-sh") })
+    @Requires([UnitTestPreconditions.UnixDerivative, PluginTestPreconditions.StaticShAvailable])
     def "can pass argument to App with custom start script in BusyBox"() {
         given:
         succeeds('installDist')
@@ -165,7 +153,7 @@
         outputContains('Arg: -DGOO=\'car < caz\'')
     }
 
-    @Requires(adhoc = { TestPrecondition.JDK9_OR_LATER.fulfilled && ApplicationPluginUnixShellsIntegrationTest.shellAvailable("bash") })
+    @Requires([UnitTestPreconditions.Jdk9OrLater, PluginTestPreconditions.BashAvailable])
     def "can execute generated Unix start script for Java module in Bash"() {
         given:
         turnSampleProjectIntoModule()
@@ -178,7 +166,7 @@
         outputContains('Hello World!')
     }
 
-    @Requires(adhoc = { TestPrecondition.JDK9_OR_LATER.fulfilled && ApplicationPluginUnixShellsIntegrationTest.shellAvailable("dash") })
+    @Requires([UnitTestPreconditions.Jdk9OrLater, PluginTestPreconditions.DashAvailable])
     def "can execute generated Unix start script for Java module in Dash"() {
         given:
         turnSampleProjectIntoModule()
@@ -191,7 +179,7 @@
         outputContains('Hello World!')
     }
 
-    @Requires(adhoc = { TestPrecondition.JDK9_OR_LATER.fulfilled && ApplicationPluginUnixShellsIntegrationTest.shellAvailable("static-sh") })
+    @Requires([UnitTestPreconditions.Jdk9OrLater, PluginTestPreconditions.StaticShAvailable])
     def "can execute generated Unix start script for Java module in BusyBox"() {
         given:
         turnSampleProjectIntoModule()
@@ -204,7 +192,7 @@
         outputContains('Hello World!')
     }
 
-    @Requires(adhoc = { ApplicationPluginUnixShellsIntegrationTest.shellAvailable("shellcheck") })
+    @Requires(PluginTestPreconditions.ShellcheckAvailable)
     def "generate start script passes shellcheck"() {
         given:
         succeeds('installDist')
@@ -220,7 +208,7 @@
         buildFile << """
 task execStartScript(type: Exec) {
     workingDir '$startScriptDir.canonicalPath'
-    commandLine '${locate(shCommand).absolutePath}'
+    commandLine '${PluginTestPreconditions.locate(shCommand).absolutePath}'
     args "./sample"
 }
 """
@@ -249,7 +237,7 @@
 
     private void extendBuildFileWithAppHomeProperty() {
         buildFile << """
-applicationDefaultJvmArgs = ["-DappHomeSystemProp=REPLACE_THIS_WITH_APP_HOME"]
+application.applicationDefaultJvmArgs = ["-DappHomeSystemProp=REPLACE_THIS_WITH_APP_HOME"]
 
 startScripts {
     doLast {
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BasePluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BasePluginIntegrationTest.groovy
index 8797e4a..2839338 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BasePluginIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/BasePluginIntegrationTest.groovy
@@ -16,14 +16,14 @@
 package org.gradle.api.plugins
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.hamcrest.CoreMatchers.containsString
 
 class BasePluginIntegrationTest extends AbstractIntegrationSpec {
 
-    @Requires(TestPrecondition.MANDATORY_FILE_LOCKING)
+    @Requires(UnitTestPreconditions.MandatoryFileLockOnOpen)
     def "clean failure message indicates file"() {
         given:
         buildFile << """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaPluginIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaPluginIntegrationTest.groovy
index b115e59..6052f0f 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaPluginIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/plugins/JavaPluginIntegrationTest.groovy
@@ -109,7 +109,7 @@
             // A resolvable configuration to collect source data
             def sourceElementsConfig = configurations.create("sourceElements") {
                 visible = true
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
                 attributes {
                     attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.class, Category.VERIFICATION))
@@ -199,7 +199,7 @@
             // A resolvable configuration to collect JaCoCo coverage data
             def sourceElementsConfig = configurations.create("sourceElements") {
                 visible = true
-                canBeResolved = true
+                assert canBeResolved
                 canBeConsumed = false
                 extendsFrom(configurations.implementation)
                 attributes {
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecDebugIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecDebugIntegrationTest.groovy
index 5b8e6af..8df6731 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecDebugIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecDebugIntegrationTest.groovy
@@ -20,14 +20,12 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.jvm.JDWPUtil
 import org.gradle.test.fixtures.ConcurrentTestUtil
-import org.gradle.test.fixtures.Flaky
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Assume
 import org.junit.Rule
 import spock.lang.Issue
 
-@Flaky(because = "https://github.com/gradle/gradle-private/issues/3612")
 class JavaExecDebugIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule
@@ -93,7 +91,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/20644")
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "can debug Java exec with socket server debugger (server = true) on explicitly any host with task :#taskName"() {
         def jdwpHost = nonLoopbackAddress()
         Assume.assumeNotNull(jdwpHost)
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecIntegrationTest.groovy
index ff2d41b..4ce701f 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecIntegrationTest.groovy
@@ -79,10 +79,11 @@
         """
 
         when:
-        executer.expectDeprecationWarning("Configuring a Java executable via a relative path. " +
-                "This behavior has been deprecated. This will fail with an error in Gradle 9.0. " +
-                "Resolving relative file paths might yield unexpected results, there is no single clear location it would make sense to resolve against. " +
-                "Configure an absolute path to a Java executable instead.")
+        executer.expectDocumentedDeprecationWarning("Configuring a Java executable via a relative path. " +
+            "This behavior has been deprecated. This will fail with an error in Gradle 9.0. " +
+            "Resolving relative file paths might yield unexpected results, there is no single clear location it would make sense to resolve against. " +
+            "Configure an absolute path to a Java executable instead. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#no_relative_paths_for_java_executables")
         run "run"
 
         then:
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecMainClassIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecMainClassIntegrationTest.groovy
index e254e16..08bdc21 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecMainClassIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecMainClassIntegrationTest.groovy
@@ -73,10 +73,10 @@
             def resolveMainClassName = tasks.register('resolveMainClassName', ResolveMainClassName) {
                 classpath.from(compileJava)
                 mainClassFromBootExtension.set(
-                    project.convention.findByType(BootExtension.class)?.mainClassName
+                    project.extensions.findByType(BootExtension.class)?.mainClassName
                 )
                 mainClassFromJavaApplication.set(
-                    project.convention.findByType(JavaApplication.class)?.mainClass
+                    project.extensions.findByType(JavaApplication.class)?.mainClass
                 )
                 mainClassFile = layout.buildDirectory.file('mainClass.txt')
             }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecSubclassingIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecSubclassingIntegrationTest.groovy
new file mode 100644
index 0000000..7daaa94
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/JavaExecSubclassingIntegrationTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 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.api.tasks
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
+
+class JavaExecSubclassingIntegrationTest extends AbstractIntegrationSpec {
+
+    @Issue("https://github.com/gradle/gradle/issues/24403")
+    def "can use jvmArgs before super.exec()"() {
+        given:
+        file('src/main/java/Main.java') << """
+            public class Main {
+                public static void main(String[] args) {
+                    System.out.println("Run app!");
+                }
+            }
+        """
+
+        buildFile << """
+            apply plugin: 'java'
+
+            class MyJavaExec extends JavaExec {
+
+                @Override
+                void exec() {
+                    jvmArgs = jvmArgs
+                    super.exec()
+                }
+            }
+
+            task run(type: MyJavaExec) {
+                classpath = sourceSets.main.runtimeClasspath
+                mainClass = "Main"
+            }
+        """
+
+        when:
+        run("run")
+
+        then:
+        outputContains("Run app!")
+        outputDoesNotContain("Cannot resolve which method to invoke for [null] due to overlapping prototypes between")
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/UploadTaskIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/UploadTaskIntegrationTest.groovy
index b792ac22..e684b05 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/UploadTaskIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/UploadTaskIntegrationTest.groovy
@@ -53,6 +53,7 @@
         and:
         executer.withBuildJvmOpts("-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true")
         fails 'upload'
-        result.assertHasErrorOutput "The legacy `Upload` task was removed in Gradle 8. Please use the `maven-publish` or `ivy-publish` plugin instead. See https://docs.gradle.org/${GradleVersion.current().version}/userguide/publishing_maven.html#publishing_maven or https://docs.gradle.org/${GradleVersion.current().version}/userguide/publishing_ivy.html#publishing_ivy for details"
+        result.assertHasErrorOutput "The legacy `Upload` task was removed in Gradle 8. Please use the `maven-publish` or `ivy-publish` plugin instead. " +
+            "For more on publishing on maven repositories, please refer to https://docs.gradle.org/${GradleVersion.current().version}/userguide/publishing_maven.html#publishing_maven in the Gradle documentation."
     }
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/bundling/WarTaskIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/bundling/WarTaskIntegrationTest.groovy
index 0606824..ab293c0 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/bundling/WarTaskIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/api/tasks/bundling/WarTaskIntegrationTest.groovy
@@ -359,4 +359,28 @@
         then:
         skipped ":war"
     }
+
+    def "emits deprecation message when war convention is accessed"() {
+        setup:
+        buildFile << '''
+            plugins {
+                id 'war'
+            }
+
+            tasks.register('custom') {
+                println webAppDir
+            }
+        '''
+
+        expect:
+        executer
+            .expectDocumentedDeprecationWarning('The org.gradle.api.plugins.Convention type has been deprecated. ' +
+                'This is scheduled to be removed in Gradle 9.0. ' +
+                'Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#deprecated_access_to_conventions')
+            .expectDocumentedDeprecationWarning('The org.gradle.api.plugins.WarPluginConvention type has been deprecated. ' +
+                'This is scheduled to be removed in Gradle 9.0. ' +
+                'Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#war_convention_deprecation')
+
+        succeeds 'custom'
+    }
 }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec.groovy
index e949d7d..891650c 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec.groovy
@@ -27,9 +27,9 @@
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.internal.reflect.validation.ValidationTestFor
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testing.fixture.GroovyCoverage
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.junit.Assume
 import org.junit.Rule
 import spock.lang.Ignore
@@ -447,7 +447,10 @@
 
     // JavaFx was removed in JDK 10
     // We don't have Oracle Java 8 on Windows any more
-    @Requires([TestPrecondition.JDK9_OR_EARLIER, TestPrecondition.NOT_WINDOWS])
+    @Requires([
+        UnitTestPreconditions.Jdk9OrEarlier,
+        UnitTestPreconditions.NotWindows
+    ])
     def "compileJavaFx8Code"() {
         Assume.assumeFalse("Setup invalid with toolchains", getClass().name.contains('Toolchain') && !getClass().name.contains('SameToolchain'))
 
@@ -475,7 +478,7 @@
 
     @Ignore
     @Issue("https://issues.gradle.org/browse/GRADLE-3377")
-    @Requires(TestPrecondition.ONLINE)
+    @Requires(UnitTestPreconditions.Online)
     def "can compile with Groovy library resolved by classifier"() {
         def gradleBaseServicesClass = Action
         buildScript """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/GroovyCompileToolchainIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/GroovyCompileToolchainIntegrationTest.groovy
index 5aa0103..3f63c80 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/GroovyCompileToolchainIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/compile/GroovyCompileToolchainIntegrationTest.groovy
@@ -27,7 +27,6 @@
 import org.gradle.util.internal.TextUtil
 import org.gradle.util.internal.VersionNumber
 import org.junit.Assume
-import spock.lang.Ignore
 
 import static org.gradle.util.internal.GroovyDependencyUtil.groovyModuleDependency
 
@@ -197,8 +196,8 @@
         'none' | 'none' | '11'      | '11'
     }
 
-    @Ignore("https://github.com/gradle/gradle-private/issues/3729")
     def "can compile source and run tests using Java #javaVersion for Groovy "() {
+        Assume.assumeTrue(GroovyCoverage.supportsJavaVersion("$versionNumber", javaVersion))
         def jdk = AvailableJavaHomes.getJdk(javaVersion)
         Assume.assumeTrue(jdk != null)
 
@@ -209,9 +208,7 @@
                 testImplementation "org.spockframework:spock-core:${getSpockVersion(versionNumber)}"
             }
 
-            test {
-                useJUnitPlatform()
-            }
+            testing.suites.test.useJUnitJupiter()
         """
 
         file("src/test/groovy/GroovySpec.groovy") << """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/environment/JreJavaHomeGroovyIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/environment/JreJavaHomeGroovyIntegrationTest.groovy
index 10298b9..236241b 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/environment/JreJavaHomeGroovyIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/groovy/environment/JreJavaHomeGroovyIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.IgnoreIf
 
 class JreJavaHomeGroovyIntegrationTest extends AbstractIntegrationSpec {
@@ -50,7 +50,7 @@
         forkMode << [true, false]
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "groovy compiler works when gradle is started with no JAVA_HOME defined in forking mode = #forkMode"() {
         given:
         writeJavaTestSource("src/main/groovy")
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/integtests/resolve/consistency/JavaProjectResolutionConsistencyIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/integtests/resolve/consistency/JavaProjectResolutionConsistencyIntegrationTest.groovy
index f23fb19..347fb51 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/integtests/resolve/consistency/JavaProjectResolutionConsistencyIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/integtests/resolve/consistency/JavaProjectResolutionConsistencyIntegrationTest.groovy
@@ -72,12 +72,15 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 module('org:foo:1.0') {
-                    module("org:transitive:1.0")
+                    byConsistentResolution('compileClasspath')
+                    module("org:transitive:1.0") {
+                        notRequested()
+                        byConsistentResolution('compileClasspath')
+                        byAncestor()
+                    }
                 }
                 module('org:bar:1.0') {
-                    edge("org:transitive:1.1", "org:transitive:1.0") {
-                        byConsistentResolution('compileClasspath')
-                    }
+                    edge("org:transitive:1.1", "org:transitive:1.0")
                 }
                 constraint("org:foo:{strictly 1.0}", "org:foo:1.0")
                 constraint("org:transitive:{strictly 1.0}", "org:transitive:1.0")
@@ -94,14 +97,18 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 module('org:foo:1.0') {
+                    byConsistentResolution('testCompileClasspath')
                     module("org:transitive:1.0")
                 }
                 module('org:bar:1.0') {
                     edge("org:transitive:1.1", "org:transitive:1.0") {
+                        notRequested()
                         byConsistentResolution('testCompileClasspath')
+                        byAncestor()
                     }
                 }
                 module('org:baz:1.0') {
+                    byConsistentResolution('testCompileClasspath')
                     edge("org:transitive:1.2", "org:transitive:1.0") {
                         byConsistentResolution('testCompileClasspath')
                     }
@@ -146,7 +153,10 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 module('org:foo:1.0') {
+                    byConsistentResolution('runtimeClasspath')
                     edge("org:transitive:1.0", "org:transitive:1.1") {
+                        notRequested()
+                        byAncestor()
                         byConsistentResolution('runtimeClasspath')
                     }
                 }
@@ -191,12 +201,15 @@
         resolve.expectGraph {
             root(':', ':test:') {
                 module('org:foo:1.0') {
-                    module("org:transitive:1.0")
+                    byConsistentResolution('customCompileClasspath')
+                    module("org:transitive:1.0") {
+                        notRequested()
+                        byConsistentResolution('customCompileClasspath')
+                        byAncestor()
+                    }
                 }
                 module('org:bar:1.0') {
-                    edge("org:transitive:1.1", "org:transitive:1.0") {
-                        byConsistentResolution('customCompileClasspath')
-                    }
+                    edge("org:transitive:1.1", "org:transitive:1.0")
                 }
                 constraint("org:foo:{strictly 1.0}", "org:foo:1.0")
                 constraint("org:transitive:{strictly 1.0}", "org:transitive:1.0")
@@ -249,7 +262,9 @@
         then:
         resolve.expectGraph {
             root(':', ':test:') {
-                module('org:foo:1.0')
+                module('org:foo:1.0') {
+                    byConsistentResolution("runtimeClasspath")
+                }
                 constraint("org:foo:{strictly 1.0}", "org:foo:1.0")
             }
         }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/internal/deployment/JavaApplicationDeploymentIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/internal/deployment/JavaApplicationDeploymentIntegrationTest.groovy
index e519abd..b0917bb 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/internal/deployment/JavaApplicationDeploymentIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/internal/deployment/JavaApplicationDeploymentIntegrationTest.groovy
@@ -16,10 +16,12 @@
 
 package org.gradle.internal.deployment
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
 import org.gradle.integtests.fixtures.AbstractContinuousIntegrationTest
 import org.gradle.test.fixtures.ConcurrentTestUtil
 import org.gradle.test.fixtures.file.TestFile
 
+@LocalOnly(because = "https://github.com/gradle/gradle-private/issues/3499")
 class JavaApplicationDeploymentIntegrationTest extends AbstractContinuousIntegrationTest {
     TestFile messageSrc
 
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaCrossCompilationIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaCrossCompilationIntegrationTest.groovy
index e76e656..db1d4c3 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaCrossCompilationIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaCrossCompilationIntegrationTest.groovy
@@ -95,7 +95,9 @@
         given:
         withJavaProjectUsingToolchainsForJavaVersion(version)
         buildFile << """
-            dependencies { testImplementation 'org.testng:testng:6.8.8' }
+            testing.suites.test {
+                useTestNG('6.8.8')
+            }
         """
 
         file("src/test/java/ThingTest.java") << """
@@ -104,7 +106,7 @@
             public class ThingTest {
                 @Test
                 public void verify() {
-                    assert System.getProperty("java.version").startsWith("${version}.");
+                    assert System.getProperty("java.version").startsWith("${version}");
                 }
             }
         """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryConsumptionIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryConsumptionIntegrationTest.groovy
index e0f4726..4d2c556 100755
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryConsumptionIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryConsumptionIntegrationTest.groovy
@@ -60,7 +60,7 @@
 
             configurations {
                 consumer {
-                    canBeResolved = true
+                    assert canBeResolved
                     canBeConsumed = false
                     attributes {
                         // intentionally not complete
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryCrossProjectTargetJvmVersionIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryCrossProjectTargetJvmVersionIntegrationTest.groovy
index f50f497..0fb3144 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryCrossProjectTargetJvmVersionIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryCrossProjectTargetJvmVersionIntegrationTest.groovy
@@ -92,7 +92,7 @@
             [6, 7, 9].each { v ->
                 configurations {
                     "apiElementsJdk\${v}" {
-                        canBeConsumed = true
+                        assert canBeConsumed
                         canBeResolved = false
                         attributes {
                             attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, 'java-api'))
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryDocumentationIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryDocumentationIntegrationTest.groovy
index df34e51..73d141a 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryDocumentationIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryDocumentationIntegrationTest.groovy
@@ -31,14 +31,16 @@
             'build.gradle'('''
                 configurations {
                     javadoc {
-                        canBeResolved = true; canBeConsumed = false
+                        assert canBeResolved
+                        canBeConsumed = false
                         attributes {
                             attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
                             attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, DocsType.JAVADOC))
                         }
                     }
                     sources {
-                        canBeResolved = true; canBeConsumed = false
+                        assert canBeResolved
+                        canBeConsumed = false
                         attributes {
                             attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
                             attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, DocsType.SOURCES))
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryFeatureCompilationIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryFeatureCompilationIntegrationTest.groovy
index 37d7913..e558f4c 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryFeatureCompilationIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryFeatureCompilationIntegrationTest.groovy
@@ -540,6 +540,68 @@
         executedAndNotSkipped ':compileMain211Java', ':compileMain212Java'
     }
 
+    def "creates configurations when using main source set and java-library is not applied" () {
+        given:
+        buildFile << """
+            plugins {
+                id('java-base')
+            }
+
+            sourceSets {
+               main
+            }
+
+            configurations {
+                testCompileClasspath
+                testRuntimeClasspath
+            }
+
+            java {
+               registerFeature('feature') {
+                  usingSourceSet(sourceSets.main)
+               }
+            }
+        """
+
+        when:
+        succeeds 'dependencies'
+
+        then:
+        outputContains("featureRuntimeOnly")
+        outputContains("featureCompileOnly")
+        outputContains("featureImplementation")
+        outputContains("featureApi")
+        outputContains("featureCompileOnlyApi")
+        outputContains("featureRuntimeElements")
+        outputContains("featureApiElements")
+    }
+
+    def "creates configurations when using main source set and main feature name"() {
+        buildFile << """
+            plugins {
+                id('java-library')
+            }
+
+            java {
+                registerFeature('main') {
+                   usingSourceSet(sourceSets.main)
+                }
+            }
+        """
+
+        when:
+        run 'dependencies'
+
+        then:
+        outputContains("mainRuntimeOnly")
+        outputContains("mainCompileOnly")
+        outputContains("mainImplementation")
+        outputContains("mainApi")
+        outputContains("mainCompileOnlyApi")
+        outputContains("mainRuntimeElements")
+        outputContains("mainApiElements")
+    }
+
     def "elements configurations have the correct roles"() {
         given:
         buildFile << """
@@ -564,11 +626,11 @@
                 [apiElements, runtimeElements].each {
                     assert it.canBeConsumed == true
                     assert it.canBeResolved == false
-                    assert it.canBeDeclaredAgainst == true
+                    assert it.canBeDeclared == true
 
-                    assert it.declarationAlternatives == null
-                    assert it.resolutionAlternatives == null
-                    assert it.consumptionDeprecation == null
+                    assert it.deprecatedForDeclarationAgainst == true
+                    assert it.deprecatedForResolution == false
+                    assert it.deprecatedForConsumption == false
                 }
             }
         """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryFeaturePublishingIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryFeaturePublishingIntegrationTest.groovy
new file mode 100644
index 0000000..f4a0825
--- /dev/null
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryFeaturePublishingIntegrationTest.groovy
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2023 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.java
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.test.fixtures.maven.AbstractMavenModule
+import org.gradle.test.fixtures.maven.MavenFileRepository
+import spock.lang.Unroll
+
+class JavaLibraryFeaturePublishingIntegrationTest extends AbstractIntegrationSpec {
+    MavenFileRepository publishRepo
+    AbstractMavenModule module
+
+    def setup() {
+        settingsFile << """
+            rootProject.name = 'mylib'
+        """
+        buildFile << """
+            plugins {
+                id 'java-library'
+                id 'java-test-fixtures'
+                id 'maven-publish'
+            }
+
+            group = 'com.acme'
+            version = '1.4'
+
+            ${mavenCentralRepository()}
+
+            testing.suites {
+                integTest(JvmTestSuite) { }
+            }
+
+            publishing {
+                repositories {
+                    maven { url "\${buildDir}/repo" }
+                }
+                publications {
+                    maven(MavenPublication) {
+                        from components.java
+                    }
+                }
+            }
+        """
+
+        publishRepo = new MavenFileRepository(
+            testDirectory.file("build/repo")
+        )
+        module = publishRepo.module('com.acme', 'mylib', '1.4')
+    }
+
+    /**
+     * This test exercises the creation of a secondary feature within the java component,
+     * similar to "test fixtures", but where the fixtures are consumed by the integration tests
+     */
+    @Unroll("can register integration test fixtures (published=#published, javadocs=#javadocs, sources=#sources)")
+    def "can register integration test fixtures"() {
+        buildFile << """
+            sourceSets {
+                integTestFixtures
+            }
+
+            java {
+                registerFeature("integTestFixtures") {
+                    usingSourceSet sourceSets.integTestFixtures
+                    if ($javadocs) { withJavadocJar() }
+                    if ($sources) { withSourcesJar() }
+                    if (!$published) { disablePublication() }
+                }
+            }
+
+            dependencies {
+                integTestImplementation(project(":")) {
+                    capabilities {
+                        requireCapability 'com.acme:mylib-integ-test-fixtures'
+                    }
+                }
+            }
+        """
+
+        file("src/integTest/java/MyTest.java") << """
+            public class MyTest {
+                @org.junit.jupiter.api.Test
+                public void testSomething() {
+                    assert MyFixture.answer() == 42;
+                }
+            }
+        """
+
+        file("src/integTestFixtures/java/MyFixture.java") << """
+            public class MyFixture {
+                public static int answer() { return 42; }
+            }
+        """
+
+        when:
+        succeeds ':compileIntegTestJava'
+
+        then:
+        executedAndNotSkipped ":compileIntegTestFixturesJava", ":compileIntegTestJava"
+        notExecuted ":integTestFixturesJar"
+
+        when:
+        succeeds ":integTest"
+
+        then:
+        executedAndNotSkipped ":integTestFixturesJar"
+
+        when:
+        succeeds ':publishMavenPublicationToMavenRepository'
+
+        then:
+        module.assertPublished()
+        def expectedArtifacts = [
+            'mylib-1.4.pom',
+            'mylib-1.4.module',
+            'mylib-1.4.jar',
+            'mylib-1.4-test-fixtures.jar'
+        ]
+        if (published) {
+            expectedArtifacts.add('mylib-1.4-integ-test-fixtures.jar')
+        }
+        if (javadocs) {
+            expectedArtifacts.add('mylib-1.4-integ-test-fixtures-javadoc.jar')
+        }
+        if (sources) {
+            expectedArtifacts.add('mylib-1.4-integ-test-fixtures-sources.jar')
+        }
+        module.assertArtifactsPublished(expectedArtifacts)
+
+        and:
+        def md = module.parsedModuleMetadata
+        if (published) {
+            md.variant('integTestFixturesRuntimeElements') {
+                capability('com.acme', 'mylib-integ-test-fixtures', '1.4')
+                file("mylib-1.4-integ-test-fixtures.jar")
+                noMoreDependencies()
+            }
+            md.variant('integTestFixturesApiElements') {
+                capability('com.acme', 'mylib-integ-test-fixtures', '1.4')
+                file("mylib-1.4-integ-test-fixtures.jar")
+                noMoreDependencies()
+            }
+            if (javadocs) {
+                md.variant('integTestFixturesJavadocElements') {
+                    capability('com.acme', 'mylib-integ-test-fixtures', '1.4')
+                    file("mylib-1.4-integ-test-fixtures-javadoc.jar")
+                    noMoreDependencies()
+                }
+            }
+            if (sources) {
+                md.variant('integTestFixturesSourcesElements') {
+                    capability('com.acme', 'mylib-integ-test-fixtures', '1.4')
+                    file("mylib-1.4-integ-test-fixtures-sources.jar")
+                    noMoreDependencies()
+                }
+            }
+        }
+
+        where:
+        published | javadocs | sources
+        false     | false    | false
+        true      | false    | false
+        true      | true     | false
+        true      | false    | true
+        true      | true     | true
+    }
+}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryPublishedTargetJvmEnvironmentIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryPublishedTargetJvmEnvironmentIntegrationTest.groovy
index 1317f4e..0ebc8bf 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryPublishedTargetJvmEnvironmentIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/JavaLibraryPublishedTargetJvmEnvironmentIntegrationTest.groovy
@@ -168,6 +168,7 @@
                         'org.gradle.dependency.bundling': 'external',
                         'org.gradle.status': 'release'
                     ])
+                    byConstraint()
                     artifact(classifier: 'jre')
                 }
             }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/ParallelTestTaskIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/ParallelTestTaskIntegrationTest.groovy
index 5bcf0a5..40d23ff 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/ParallelTestTaskIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/ParallelTestTaskIntegrationTest.groovy
@@ -53,8 +53,8 @@
 def latch = new CountDownLatch(${subprojects.size()})
 subprojects {
     apply plugin: 'java'
-    sourceCompatibility = ${version}
-    targetCompatibility = ${version}
+    java.sourceCompatibility = ${version}
+    java.targetCompatibility = ${version}
 
     ${mavenCentralRepository()}
     dependencies { testImplementation 'junit:junit:4.13' }
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AbstractIncrementalCompileIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AbstractIncrementalCompileIntegrationTest.groovy
index 360d40e..c62992d 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AbstractIncrementalCompileIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AbstractIncrementalCompileIntegrationTest.groovy
@@ -34,7 +34,7 @@
         file("src/main/${language.name}/Test.${language.name}") << 'public class Test{}'
         buildFile << """
             apply plugin: '${language.name}'
-            sourceCompatibility = 1.7
+            java.sourceCompatibility = '1.7'
             ${language.compileTaskName}.options.debug = true
             ${language.compileTaskName}.options.incremental = true
             ${language.projectGroovyDependencies()}
@@ -47,7 +47,7 @@
         executedAndNotSkipped ":${language.compileTaskName}"
 
         when:
-        buildFile << 'sourceCompatibility = 1.8\n'
+        buildFile << 'java.sourceCompatibility = 1.8\n'
         succeeds ":${language.compileTaskName}"
 
         then:
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AbstractJavaCompileAvoidanceIntegrationSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AbstractJavaCompileAvoidanceIntegrationSpec.groovy
index 76386d3..babbe6c 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AbstractJavaCompileAvoidanceIntegrationSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/AbstractJavaCompileAvoidanceIntegrationSpec.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.CompiledLanguage
 import org.gradle.language.fixtures.HelperProcessorFixture
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 abstract class AbstractJavaCompileAvoidanceIntegrationSpec extends AbstractJavaGroovyCompileAvoidanceIntegrationSpec {
@@ -336,7 +336,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/20394")
-    @Requires(TestPrecondition.JDK16_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk16OrLater)
     def "doesn't recompile when record implementation changes"() {
         given:
         buildFile << """
@@ -375,7 +375,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/20394")
-    @Requires(TestPrecondition.JDK16_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk16OrLater)
     def "recompiles when record components change"() {
         given:
         buildFile << """
@@ -414,7 +414,7 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/20394")
-    @Requires(TestPrecondition.JDK17_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk17OrLater)
     def "recompiles when sealed modifier is changed"() {
         given:
         buildFile << """
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/BasicJavaCompilerIntegrationSpec.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/BasicJavaCompilerIntegrationSpec.groovy
index 53c0150..0c7f1d8 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/BasicJavaCompilerIntegrationSpec.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/BasicJavaCompilerIntegrationSpec.groovy
@@ -18,13 +18,12 @@
 package org.gradle.java.compile
 
 import org.gradle.api.Action
-import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.internal.classanalysis.JavaClassUtil
 import org.gradle.test.fixtures.file.ClassFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 abstract class BasicJavaCompilerIntegrationSpec extends AbstractIntegrationSpec {
 
@@ -48,7 +47,7 @@
         javaClassFile("compile/test/Person.class").exists()
     }
 
-    def compileBadCodeBreaksTheBuild() {
+    def "compile bad code breaks the build and compilation error doesn't show link to help.gradle.org"() {
         given:
         badCode()
 
@@ -56,6 +55,7 @@
         fails("compileJava")
         output.contains(logStatement())
         failure.assertHasErrorOutput("';' expected")
+        failure.assertNotOutput("https://help.gradle.org")
         javaClassFile("").assertHasDescendants()
     }
 
@@ -133,7 +133,10 @@
 
     // JavaFx was removed in JDK 10
     // We don't have Oracle Java 8 on Windows any more
-    @Requires([TestPrecondition.JDK9_OR_EARLIER, TestPrecondition.NOT_WINDOWS])
+    @Requires([
+        UnitTestPreconditions.Jdk9OrEarlier,
+        UnitTestPreconditions.NotWindows
+    ])
     def "compileJavaFx8Code"() {
         given:
         file("src/main/java/compile/test/FxApp.java") << '''
@@ -150,7 +153,7 @@
         succeeds("compileJava")
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdk(JavaVersion.VERSION_11) != null })
+    @Requires(IntegTestPreconditions.Java11HomeAvailable)
     def 'ignores project level compatibility when using toolchain'() {
         given:
         goodCode()
@@ -172,7 +175,7 @@
 """
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdk(JavaVersion.VERSION_11) != null })
+    @Requires(IntegTestPreconditions.Java11HomeAvailable)
     def 'honors task level compatibility when using toolchain'() {
         given:
         goodCode()
@@ -195,7 +198,7 @@
 """
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdk(JavaVersion.VERSION_14) != null })
+    @Requires(IntegTestPreconditions.Java14HomeAvailable)
     def 'computes target jvm version when using toolchain'() {
         given:
         goodCode()
@@ -215,7 +218,7 @@
 """
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "compile with release flag using #notation notation"() {
         given:
         goodCode()
@@ -251,7 +254,7 @@
         ]
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "compile with release property set"() {
         given:
         goodCode()
@@ -281,7 +284,7 @@
         bytecodeVersion() == 52
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "fails to compile with release property and flag set"() {
         given:
         goodCode()
@@ -295,7 +298,7 @@
         failureHasCause('Cannot specify --release via `CompileOptions.compilerArgs` when using `JavaCompile.release`.')
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "compile with release property and autoTargetJvmDisabled"() {
         given:
         goodCode()
@@ -382,7 +385,7 @@
         bytecodeVersion() == 52
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "compile fails when using newer API with release option"() {
         given:
         file("src/main/java/compile/test/FailsOnJava8.java") << '''
@@ -409,7 +412,7 @@
         failure.assertHasErrorOutput("method takeWhile")
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "compile fails when using newer API with release property"() {
         given:
         file("src/main/java/compile/test/FailsOnJava8.java") << '''
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/daemon/DaemonJavaCompilerIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/daemon/DaemonJavaCompilerIntegrationTest.groovy
index 2c99194..29f00f7 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/daemon/DaemonJavaCompilerIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/compile/daemon/DaemonJavaCompilerIntegrationTest.groovy
@@ -17,8 +17,8 @@
 
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.java.compile.JavaCompilerIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import spock.lang.Issue
 
@@ -81,7 +81,10 @@
     }
 
     @Issue("https://github.com/gradle/gradle/issues/3098")
-    @Requires([TestPrecondition.JDK8_OR_EARLIER, TestPrecondition.JDK_ORACLE])
+    @Requires([
+        UnitTestPreconditions.Jdk8OrEarlier,
+        UnitTestPreconditions.JdkOracle
+    ])
     def "handles -bootclasspath being specified"() {
         def jre = AvailableJavaHomes.getBestJre()
         def bootClasspath = TextUtil.escapeString(jre.absolutePath) + "/lib/rt.jar"
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/java/environment/JreJavaHomeJavaIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/java/environment/JreJavaHomeJavaIntegrationTest.groovy
index 54aab2f..41d15df 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/java/environment/JreJavaHomeJavaIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/java/environment/JreJavaHomeJavaIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.IgnoreIf
 
 class JreJavaHomeJavaIntegrationTest extends AbstractIntegrationSpec {
@@ -45,7 +45,7 @@
         forkMode << [true, false]
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "java compilation works in forking mode = #forkMode when gradle is started with no JAVA_HOME defined"() {
         given:
         writeJavaTestSource("src/main/java");
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/JavaLibraryOutgoingElementsBuilderIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/JavaLibraryOutgoingElementsBuilderIntegrationTest.groovy
deleted file mode 100644
index bfb0f13..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/JavaLibraryOutgoingElementsBuilderIntegrationTest.groovy
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright 2020 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.jvm
-
-import org.gradle.api.JavaVersion
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-
-class JavaLibraryOutgoingElementsBuilderIntegrationTest extends AbstractIntegrationSpec {
-    def setup() {
-        settingsFile << """
-            rootProject.name = 'mylib'
-        """
-        buildFile << """
-            plugins {
-                id 'java-library'
-                id 'maven-publish'
-            }
-            def jvm = project.services.get(org.gradle.api.plugins.jvm.internal.JvmModelingServices)
-
-            group = 'com.acme'
-            version = '1.4'
-
-            publishing {
-                repositories {
-                    maven {
-                        url "\${buildDir}/repo"
-                    }
-                }
-                publications {
-                    maven(MavenPublication) {
-                        from components.java
-                    }
-                }
-            }
-        """
-    }
-
-    def "configures an additional outgoing variant (#scenario, #capability)"() {
-        buildFile << """
-            def shadowJar = tasks.register("shadowJar", Jar) {
-                archiveClassifier = 'all'
-                from(sourceSets.main.output)
-            }
-
-            def shadowElements = jvm.createOutgoingElements("shadowElements") {
-                withDescription 'A fat jar'
-                if ($runtime) {
-                    providesRuntime()
-                } else {
-                    providesApi()
-                }
-                artifact(shadowJar)
-                if ($published) {
-                    published()
-                }
-                if ($cgroup) {
-                    capability($cgroup, $cname, $cversion)
-                }
-            }
-        """
-
-        when:
-        run 'outgoingVariants'
-
-        then:
-        outputContains """Variant shadowElements
---------------------------------------------------
-A fat jar
-
-Capabilities
-    - $capability
-Attributes
-    - org.gradle.category            = library
-    - org.gradle.dependency.bundling = external
-    - org.gradle.libraryelements     = jar
-    - org.gradle.usage               = java-${runtime ? 'runtime' : 'api'}
-Artifacts
-    - build${File.separator}libs${File.separator}mylib-1.4-all.jar (artifactType = jar, classifier = all)
-"""
-        when:
-        run 'publish'
-
-        then:
-        if (published) {
-            executed ':shadowJar'
-        } else {
-            notExecuted ':shadowJar'
-        }
-
-        where:
-        scenario                   | published | runtime | cgroup  | cname     | cversion
-        "with publishing"          | true      | true    | null    | null      | null
-        "without publishing"       | false     | true    | null    | null      | null
-        "non published API"        | false     | false   | null    | null      | null
-        "with explicit capability" | false     | false   | "'com'" | "'other'" | "'1.2'"
-
-        capability = cgroup == null ? 'com.acme:mylib:1.4 (default capability)' : "${cgroup}:${cname}:${cversion}".replaceAll(/'/, '')
-    }
-
-    def "can configure an additional outgoing variant from a source set (with classes dir=#classesDir)"() {
-        buildFile << """
-            sourceSets {
-                integTest
-            }
-
-            jvm.createOutgoingElements("integTestElements") {
-                fromSourceSet(sourceSets.integTest)
-                providesApi()
-                if ($classesDir) {
-                    withClassDirectoryVariant()
-                }
-            }
-
-            // Need to ensure the new variant has distinct attributes, without this it would duplicate apiElements
-            configurations.integTestElements.attributes {
-                attribute(TestSuiteType.TEST_SUITE_TYPE_ATTRIBUTE, objects.named(TestSuiteType.class, TestSuiteType.INTEGRATION_TEST))
-            }
-        """
-
-        when:
-        run 'outgoingVariants'
-
-        then:
-        outputContains """Variant integTestElements (i)
---------------------------------------------------
-
-Capabilities
-    - com.acme:mylib:1.4 (default capability)
-Attributes
-    - org.gradle.category            = library
-    - org.gradle.dependency.bundling = external
-    - org.gradle.jvm.version         = ${JavaVersion.current().majorVersion}
-    - org.gradle.libraryelements     = jar
-    - org.gradle.testsuite.type      = integration-test
-    - org.gradle.usage               = java-api
-"""
-        if (classesDir) {
-            outputContains """Variant integTestElements (i)
---------------------------------------------------
-
-Capabilities
-    - com.acme:mylib:1.4 (default capability)
-Attributes
-    - org.gradle.category            = library
-    - org.gradle.dependency.bundling = external
-    - org.gradle.jvm.version         = ${JavaVersion.current().majorVersion}
-    - org.gradle.libraryelements     = jar
-    - org.gradle.testsuite.type      = integration-test
-    - org.gradle.usage               = java-api
-
-Secondary Variants (*)
-
-    --------------------------------------------------
-    Secondary Variant classes (i)
-    --------------------------------------------------
-    Directories containing compiled class files for integTest.
-
-    Attributes
-        - org.gradle.category            = library
-        - org.gradle.dependency.bundling = external
-        - org.gradle.jvm.version         = ${JavaVersion.current().majorVersion}
-        - org.gradle.libraryelements     = classes
-        - org.gradle.testsuite.type      = integration-test
-        - org.gradle.usage               = java-api
-    Artifacts
-        - build${File.separator}classes${File.separator}java${File.separator}integTest (artifactType = java-classes-directory)"""
-        }
-
-        where:
-        classesDir << [false, true]
-    }
-
-    def "can configure an outgoing elements configuration for documentation"() {
-        buildFile << """
-            def userguide = tasks.register('userguide') {
-                outputs.file('userguide.zip')
-            }
-
-            jvm.createOutgoingElements('userguide') {
-                attributes {
-                    documentation('userguide')
-                }
-                artifact(userguide)
-            }
-        """
-
-        when:
-        succeeds 'outgoingVariants'
-
-        then:
-        outputContains """Variant userguide
---------------------------------------------------
-
-Capabilities
-    - com.acme:mylib:1.4 (default capability)
-Attributes
-    - org.gradle.category            = documentation
-    - org.gradle.dependency.bundling = external
-    - org.gradle.docstype            = userguide
-    - org.gradle.usage               = java-runtime
-Artifacts
-    - userguide.zip (artifactType = zip)"""
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/JvmVariantBuilderIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/JvmVariantBuilderIntegrationTest.groovy
deleted file mode 100644
index f5ff1b9..0000000
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/JvmVariantBuilderIntegrationTest.groovy
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2020 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.jvm
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.test.fixtures.maven.AbstractMavenModule
-import org.gradle.test.fixtures.maven.MavenFileRepository
-import spock.lang.Unroll
-
-class JvmVariantBuilderIntegrationTest extends AbstractIntegrationSpec {
-    MavenFileRepository publishRepo
-    AbstractMavenModule module
-
-    def setup() {
-        settingsFile << """
-            rootProject.name = 'mylib'
-        """
-        buildFile << """
-            plugins {
-                id 'java-library'
-                id 'java-test-fixtures'
-                id 'maven-publish'
-            }
-            def jvm = project.services.get(org.gradle.api.plugins.jvm.internal.JvmModelingServices)
-
-            group = 'com.acme'
-            version = '1.4'
-
-            ${mavenCentralRepository()}
-
-            jvm.createJvmVariant("integTest") {
-            }
-
-            configurations {
-                integTestImplementation.extendsFrom(testImplementation)
-                integTestRuntimeOnly.extendsFrom(testRuntimeOnly)
-            }
-
-            dependencies {
-                testImplementation 'junit:junit:4.13'
-            }
-
-            tasks.register("integTest", Test) {
-                description = "Runs the integration tests."
-                group = "verification"
-                testClassesDirs = sourceSets.integTest.output.classesDirs
-                classpath = sourceSets.integTest.runtimeClasspath
-            }
-
-            publishing {
-                repositories {
-                    maven { url "\${buildDir}/repo" }
-                }
-                publications {
-                    maven(MavenPublication) {
-                        from components.java
-                    }
-                }
-            }
-        """
-
-        publishRepo = new MavenFileRepository(
-            testDirectory.file("build/repo")
-        )
-        module = publishRepo.module('com.acme', 'mylib', '1.4')
-    }
-
-    /**
-     * This test exercises the creation of a secondary component
-     * similar to "test fixtures" but where the fixtures are only
-     * available to integration tests
-     */
-    @Unroll("can register integration test fixtures (exposeApi=#exposeApi, published=#published, javadocs=#javadocs, sources=#sources)")
-    def "can register integration test fixtures"() {
-        buildFile << """
-            sourceSets {
-                integTestFixtures
-            }
-            jvm.createJvmVariant("integTestFixtures") {
-                distinctCapability()
-                withDisplayName("Integration test fixtures")
-                if ($exposeApi) { exposesApi() }
-                if ($published) { published() }
-                if ($javadocs) { withJavadocJar() }
-                if ($sources) { withSourcesJar() }
-            }
-
-            dependencies {
-                integTestImplementation(project(":")) {
-                    capabilities {
-                        requireCapability 'com.acme:mylib-integ-test-fixtures'
-                    }
-                }
-            }
-        """
-
-
-        file("src/integTest/java/MyTest.java") << """
-            import org.junit.Test;
-            import static org.junit.Assert.*;
-
-            public class MyTest {
-                @Test
-                public void testSomething() {
-                    int answer = MyFixture.answer();
-                    assertEquals(answer, 42);
-                }
-            }
-        """
-
-        file("src/integTestFixtures/java/MyFixture.java") << """
-            public class MyFixture {
-                public static int answer() { return 42; }
-            }
-        """
-
-        when:
-        succeeds ':compileIntegTestJava'
-
-        then:
-        executedAndNotSkipped ":compileIntegTestFixturesJava", ":compileIntegTestJava"
-        if (exposeApi) {
-            notExecuted ":integTestFixturesJar"
-        } else {
-            executedAndNotSkipped ":integTestFixturesJar"
-        }
-
-        when:
-        succeeds ":integTest"
-
-        then:
-        if (exposeApi) {
-            executedAndNotSkipped ":integTestFixturesJar"
-        } else {
-            skipped ":integTestFixturesJar"
-        }
-
-        when:
-        succeeds ':publishMavenPublicationToMavenRepository'
-
-        then:
-        module.assertPublished()
-        def expectedArtifacts = [
-            'mylib-1.4.pom',
-            'mylib-1.4.module',
-            'mylib-1.4.jar',
-            'mylib-1.4-test-fixtures.jar'
-        ]
-        if (published) {
-            expectedArtifacts.add('mylib-1.4-integ-test-fixtures.jar')
-        }
-        if (javadocs) {
-            expectedArtifacts.add('mylib-1.4-integ-test-fixtures-javadoc.jar')
-        }
-        if (sources) {
-            expectedArtifacts.add('mylib-1.4-integ-test-fixtures-sources.jar')
-        }
-        module.assertArtifactsPublished(expectedArtifacts)
-
-        and:
-        def md = module.parsedModuleMetadata
-        if (published) {
-            md.variant('integTestFixturesRuntimeElements') {
-                capability('com.acme', 'mylib-integ-test-fixtures', '1.4')
-                file("mylib-1.4-integ-test-fixtures.jar")
-                noMoreDependencies()
-            }
-            if (exposeApi) {
-                md.variant('integTestFixturesApiElements') {
-                    capability('com.acme', 'mylib-integ-test-fixtures', '1.4')
-                    file("mylib-1.4-integ-test-fixtures.jar")
-                    noMoreDependencies()
-                }
-            }
-            if (javadocs) {
-                md.variant('integTestFixturesJavadocElements') {
-                    capability('com.acme', 'mylib-integ-test-fixtures', '1.4')
-                    file("mylib-1.4-integ-test-fixtures-javadoc.jar")
-                    noMoreDependencies()
-                }
-            }
-            if (sources) {
-                md.variant('integTestFixturesSourcesElements') {
-                    capability('com.acme', 'mylib-integ-test-fixtures', '1.4')
-                    file("mylib-1.4-integ-test-fixtures-sources.jar")
-                    noMoreDependencies()
-                }
-            }
-        }
-
-        where:
-        exposeApi | published | javadocs | sources
-        false     | false     | false    | false
-        true      | false     | false    | false
-        false     | true      | false    | false
-        true      | true      | false    | false
-        false     | true      | true     | false
-        false     | true      | false    | true
-        false     | true      | true     | true
-
-    }
-}
diff --git a/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponentIntegrationTest.groovy b/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponentIntegrationTest.groovy
index 1699d60..42da426 100644
--- a/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponentIntegrationTest.groovy
+++ b/subprojects/plugins/src/integTest/groovy/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponentIntegrationTest.groovy
@@ -155,7 +155,7 @@
 
             components {
                 java {
-                    enableJavadocJarVariant()
+                    withJavadocJar()
                 }
             }
 
@@ -183,7 +183,7 @@
 
             components {
                 named<JvmSoftwareComponentInternal>("java") {
-                    enableJavadocJarVariant()
+                    withJavadocJar()
                 }
             }
 
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/ApiGroovyCompilerIntegrationSpec/canUseCustomFileExtensions/src/test/groovy/PersonTest.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/ApiGroovyCompilerIntegrationSpec/canUseCustomFileExtensions/src/test/groovy/PersonTest.groovy
index c96f935..2e6a5b9 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/ApiGroovyCompilerIntegrationSpec/canUseCustomFileExtensions/src/test/groovy/PersonTest.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/ApiGroovyCompilerIntegrationSpec/canUseCustomFileExtensions/src/test/groovy/PersonTest.groovy
@@ -1,4 +1,4 @@
 class PersonTest {
     @org.junit.Test
     void testMe() {}
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileBadCode/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileBadCode/src/main/groovy/BrokenClass.groovy
index 5745292..19752b5 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileBadCode/src/main/groovy/BrokenClass.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileBadCode/src/main/groovy/BrokenClass.groovy
@@ -1,4 +1,4 @@
 class BrokenClass {
     Unknown1 field1
     Unknown2 field2
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileBadJavaCode/src/main/groovy/OkClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileBadJavaCode/src/main/groovy/OkClass.groovy
index 4e4a0b2..fdf12f6 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileBadJavaCode/src/main/groovy/OkClass.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileBadJavaCode/src/main/groovy/OkClass.groovy
@@ -1,4 +1,4 @@
 
 class OkClass {
     String field1
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileGoodCode/src/main/groovy/Person.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileGoodCode/src/main/groovy/Person.groovy
index 01ac9cc..e6222d0 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileGoodCode/src/main/groovy/Person.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/compileGoodCode/src/main/groovy/Person.groovy
@@ -6,4 +6,4 @@
     void sing() {
         println "tra-la-la"
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/build.gradle
index fc20e82..edb49c2 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/build.gradle
@@ -4,4 +4,4 @@
     mavenCentral()
 }
 
-compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
\ No newline at end of file
+compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/groovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/groovycompilerconfig.groovy
index 2ffc5b6..0a7947e 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/groovycompilerconfig.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/groovycompilerconfig.groovy
@@ -20,4 +20,4 @@
 
 withConfig(configuration) {
     ast(TypeChecked)
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/src/main/groovy/BrokenClass.groovy
index 1c41eda..6e03d3f 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/src/main/groovy/BrokenClass.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/configurationScriptNotSupported/src/main/groovy/BrokenClass.groovy
@@ -2,4 +2,4 @@
     void doesNotTypeCheck() {
         "foo".bar()
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/build.gradle
index fc20e82..edb49c2 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/build.gradle
@@ -4,4 +4,4 @@
     mavenCentral()
 }
 
-compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
\ No newline at end of file
+compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/groovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/groovycompilerconfig.groovy
index 5f9beaa..299290c 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/groovycompilerconfig.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/groovycompilerconfig.groovy
@@ -1,3 +1,3 @@
 withConfig(configuration) {
     ast(TypeChecked) // invalid config file because of a missing import
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/src/main/groovy/BrokenClass.groovy
index 1c41eda..6e03d3f 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/src/main/groovy/BrokenClass.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfInvalidConfigFile/src/main/groovy/BrokenClass.groovy
@@ -2,4 +2,4 @@
     void doesNotTypeCheck() {
         "foo".bar()
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/build.gradle
index fc20e82..edb49c2 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/build.gradle
@@ -4,4 +4,4 @@
     mavenCentral()
 }
 
-compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
\ No newline at end of file
+compileGroovy.groovyOptions.configurationScript = file('groovycompilerconfig.groovy')
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/src/main/groovy/BrokenClass.groovy
index 1c41eda..6e03d3f 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/src/main/groovy/BrokenClass.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/failsBecauseOfMissingConfigFile/src/main/groovy/BrokenClass.groovy
@@ -2,4 +2,4 @@
     void doesNotTypeCheck() {
         "foo".bar()
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/groovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/groovycompilerconfig.groovy
index 0d91ba0..2421c9f 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/groovycompilerconfig.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/groovycompilerconfig.groovy
@@ -2,4 +2,4 @@
 
 withConfig(configuration) {
     ast(TypeChecked)
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/src/main/groovy/BrokenClass.groovy
index 1c41eda..6e03d3f 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/src/main/groovy/BrokenClass.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/BasicGroovyCompilerIntegrationSpec/useConfigurationScript/src/main/groovy/BrokenClass.groovy
@@ -2,4 +2,4 @@
     void doesNotTypeCheck() {
         "foo".bar()
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canJointCompileWithJavaCompilerExecutable/src/main/groovy/GroovyCode.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canJointCompileWithJavaCompilerExecutable/src/main/groovy/GroovyCode.groovy
index d2378ad..225be3c 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canJointCompileWithJavaCompilerExecutable/src/main/groovy/GroovyCode.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canJointCompileWithJavaCompilerExecutable/src/main/groovy/GroovyCode.groovy
@@ -1 +1 @@
-class GroovyCode {}
\ No newline at end of file
+class GroovyCode {}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformWrittenInGroovy/src/main/groovy/GroovyMagicFieldTransform.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformWrittenInGroovy/src/main/groovy/GroovyMagicFieldTransform.groovy
index d10ea94..a2c95d5 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformWrittenInGroovy/src/main/groovy/GroovyMagicFieldTransform.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseAstTransformWrittenInGroovy/src/main/groovy/GroovyMagicFieldTransform.groovy
@@ -14,4 +14,4 @@
         def clazz = (ClassNode) nodes[1]
         clazz.addField("magicField", Modifier.PUBLIC, ClassHelper.STRING_TYPE, new ConstantExpression("magicValue"))
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/src/main/java/MagicFieldTransform.java b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/src/main/java/MagicFieldTransform.java
index 6cc1177..4213694 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/src/main/java/MagicFieldTransform.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/src/main/java/MagicFieldTransform.java
@@ -14,4 +14,4 @@ public void visit(ASTNode[] nodes, SourceUnit source) {
         ClassNode clazz = (ClassNode) nodes[1];
         clazz.addField("magicField", Modifier.PUBLIC, ClassHelper.STRING_TYPE, new ConstantExpression("magicValue"));
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/src/main/java/MagicInterfaceTransform.java b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/src/main/java/MagicInterfaceTransform.java
index 1b1370f..a444cca 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/src/main/java/MagicInterfaceTransform.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/canUseThirdPartyAstTransform/src/main/java/MagicInterfaceTransform.java
@@ -10,4 +10,4 @@ public void visit(ASTNode[] nodes, SourceUnit source) {
         ClassNode clazz = (ClassNode) nodes[1];
         clazz.addInterface(new ClassNode(Marker.class));
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/build.gradle b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/build.gradle
index 4d7caf8..af5670d 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/build.gradle
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/build.gradle
@@ -8,3 +8,7 @@
     implementation 'javax.servlet:servlet-api:2.4'
     implementation 'javax.servlet:jsp-api:2.0'
 }
+
+testing.suites.test {
+    useJUnitJupiter()
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/test/groovy/DummyFileForCompilation.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/test/groovy/DummyFileForCompilation.groovy
index 6daa349..92eea35 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/test/groovy/DummyFileForCompilation.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/GroovyCompilerIntegrationSpec/gradle3235/src/test/groovy/DummyFileForCompilation.groovy
@@ -16,7 +16,11 @@
 
 import groovy.transform.Canonical
 import groovy.transform.CompileStatic
+import org.junit.jupiter.api.Test
 
 @Canonical
 @CompileStatic
-class DummyFileForCompilation {}
\ No newline at end of file
+class DummyFileForCompilation {
+    @Test
+    void dummyTest() {}
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/groovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/groovycompilerconfig.groovy
index fa35f59..d1a083b 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/groovycompilerconfig.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/groovycompilerconfig.groovy
@@ -1,4 +1,4 @@
 import groovy.transform.TypeChecked
 
 withConfig(configuration) {
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newgroovycompilerconfig.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newgroovycompilerconfig.groovy
index 0d91ba0..2421c9f 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newgroovycompilerconfig.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/newgroovycompilerconfig.groovy
@@ -2,4 +2,4 @@
 
 withConfig(configuration) {
     ast(TypeChecked)
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/src/main/groovy/BrokenClass.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/src/main/groovy/BrokenClass.groovy
index 1c41eda..6e03d3f 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/src/main/groovy/BrokenClass.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/failsCompilationWhenConfigScriptIsUpdated/src/main/groovy/BrokenClass.groovy
@@ -2,4 +2,4 @@
     void doesNotTypeCheck() {
         "foo".bar()
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/NewIPerson.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/NewIPerson.groovy
index e57e80b..2e7b9e7 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/NewIPerson.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/NewIPerson.groovy
@@ -1,4 +1,4 @@
 interface IPerson {
     String getName()
     String getAddress()
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/IPerson.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/IPerson.groovy
index 7900cbf..016b5ff 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/IPerson.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/IPerson.groovy
@@ -1,3 +1,3 @@
 interface IPerson {
     String getName()
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/Person.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/Person.groovy
index cb9c12a..40cdfd7 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/Person.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesDependentClasses/src/main/groovy/Person.groovy
@@ -1,3 +1,3 @@
 class Person implements IPerson {
     def String name
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/Person.java b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/Person.java
index f86b6d1..dabe134 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/Person.java
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/Person.java
@@ -1,3 +1,3 @@
 interface Person {
     String getName();
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/PersonImpl.groovy b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/PersonImpl.groovy
index 9dec906..26e1be3 100644
--- a/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/PersonImpl.groovy
+++ b/subprojects/plugins/src/integTest/resources/org/gradle/groovy/compile/IncrementalGroovyCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/groovy/PersonImpl.groovy
@@ -1,3 +1,3 @@
 class PersonImpl implements Person {
     def String name
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java
index 5f80efe..e2d1356 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java
@@ -31,6 +31,7 @@
 import org.gradle.api.plugins.internal.DefaultApplicationPluginConvention;
 import org.gradle.api.plugins.internal.DefaultJavaApplication;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.tasks.JavaExec;
@@ -41,7 +42,7 @@
 import org.gradle.api.tasks.bundling.Jar;
 import org.gradle.api.tasks.compile.JavaCompile;
 import org.gradle.api.tasks.internal.JavaExecExecutableUtils;
-import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.jvm.toolchain.JavaToolchainService;
 import org.gradle.jvm.toolchain.JavaToolchainSpec;
 
@@ -73,17 +74,17 @@ public void apply(final Project project) {
         project.getPluginManager().apply(JavaPlugin.class);
         project.getPluginManager().apply(DistributionPlugin.class);
 
-        JvmSoftwareComponentInternal component = JavaPluginHelper.getJavaComponent(project);
+        JvmFeatureInternal mainFeature = JavaPluginHelper.getJavaComponent(project).getMainFeature();
 
         JavaApplication extension = addExtension(project);
-        addRunTask(project, component, extension);
-        addCreateScriptsTask(project, component, extension);
-        configureJavaCompileTask(component.getMainCompileJavaTask(), extension);
+        addRunTask(project, mainFeature, extension);
+        addCreateScriptsTask(project, mainFeature, extension);
+        configureJavaCompileTask(mainFeature.getCompileJavaTask(), extension);
         configureInstallTask(project.getProviders(), tasks.named(TASK_INSTALL_NAME, Sync.class), extension);
 
         DistributionContainer distributions = project.getExtensions().getByType(DistributionContainer.class);
         Distribution mainDistribution = distributions.getByName(DistributionPlugin.MAIN_DISTRIBUTION_NAME);
-        configureDistribution(project, component, mainDistribution, extension);
+        configureDistribution(project, mainFeature, mainDistribution, extension);
     }
 
     private void configureJavaCompileTask(TaskProvider<JavaCompile> javaCompile, JavaApplication pluginExtension) {
@@ -135,28 +136,27 @@ public void execute(Task task) {
 
     private JavaApplication addExtension(Project project) {
         ApplicationPluginConvention pluginConvention = project.getObjects().newInstance(DefaultApplicationPluginConvention.class, project);
-        pluginConvention.setApplicationName(project.getName());
-        project.getConvention().getPlugins().put("application", pluginConvention);
-
+        DeprecationLogger.whileDisabled(() -> pluginConvention.setApplicationName(project.getName()));
+        DeprecationLogger.whileDisabled(() -> project.getConvention().getPlugins().put("application", pluginConvention));
         return project.getExtensions().create(JavaApplication.class, "application", DefaultJavaApplication.class, pluginConvention);
     }
 
-    private void addRunTask(Project project, JvmSoftwareComponentInternal component, JavaApplication pluginExtension) {
+    private void addRunTask(Project project, JvmFeatureInternal mainFeature, JavaApplication pluginExtension) {
         project.getTasks().register(TASK_RUN_NAME, JavaExec.class, run -> {
             run.setDescription("Runs this project as a JVM application");
             run.setGroup(APPLICATION_GROUP);
 
             FileCollection runtimeClasspath = project.files().from((Callable<FileCollection>) () -> {
                 if (run.getMainModule().isPresent()) {
-                    return jarsOnlyRuntimeClasspath(component);
+                    return jarsOnlyRuntimeClasspath(mainFeature);
                 } else {
-                    return runtimeClasspath(component);
+                    return runtimeClasspath(mainFeature);
                 }
             });
             run.setClasspath(runtimeClasspath);
             run.getMainModule().set(pluginExtension.getMainModule());
             run.getMainClass().set(pluginExtension.getMainClass());
-            run.getConventionMapping().map("jvmArgs", pluginExtension::getApplicationDefaultJvmArgs);
+            run.getJvmArguments().convention(project.provider(pluginExtension::getApplicationDefaultJvmArgs));
 
             JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
             run.getModularity().getInferModulePath().convention(javaPluginExtension.getModularity().getInferModulePath());
@@ -179,10 +179,10 @@ private <T> Provider<T> getToolchainTool(
     }
 
     // @Todo: refactor this task configuration to extend a copy task and use replace tokens
-    private void addCreateScriptsTask(Project project, JvmSoftwareComponentInternal component, JavaApplication pluginExtension) {
+    private void addCreateScriptsTask(Project project, JvmFeatureInternal mainFeature, JavaApplication pluginExtension) {
         project.getTasks().register(TASK_START_SCRIPTS_NAME, CreateStartScripts.class, startScripts -> {
             startScripts.setDescription("Creates OS specific scripts to run the project as a JVM application.");
-            startScripts.setClasspath(jarsOnlyRuntimeClasspath(component));
+            startScripts.setClasspath(jarsOnlyRuntimeClasspath(mainFeature));
 
             startScripts.getMainModule().set(pluginExtension.getMainModule());
             startScripts.getMainClass().set(pluginExtension.getMainClass());
@@ -200,25 +200,25 @@ private void addCreateScriptsTask(Project project, JvmSoftwareComponentInternal
         });
     }
 
-    private FileCollection runtimeClasspath(JvmSoftwareComponentInternal component) {
-        return component.getSourceSet().getRuntimeClasspath();
+    private FileCollection runtimeClasspath(JvmFeatureInternal mainFeature) {
+        return mainFeature.getSourceSet().getRuntimeClasspath();
     }
 
-    private FileCollection jarsOnlyRuntimeClasspath(JvmSoftwareComponentInternal component) {
-        return component.getMainJarTask().get().getOutputs().getFiles().plus(component.getRuntimeClasspathConfiguration());
+    private FileCollection jarsOnlyRuntimeClasspath(JvmFeatureInternal mainFeature) {
+        return mainFeature.getJarTask().get().getOutputs().getFiles().plus(mainFeature.getRuntimeClasspathConfiguration());
     }
 
-    private CopySpec configureDistribution(Project project, JvmSoftwareComponentInternal component, Distribution mainDistribution, JavaApplication pluginExtension) {
+    private CopySpec configureDistribution(Project project, JvmFeatureInternal mainFeature, Distribution mainDistribution, JavaApplication pluginExtension) {
         mainDistribution.getDistributionBaseName().convention(project.provider(pluginExtension::getApplicationName));
         CopySpec distSpec = mainDistribution.getContents();
 
-        TaskProvider<Jar> jar = component.getMainJarTask();
+        TaskProvider<Jar> jar = mainFeature.getJarTask();
         TaskProvider<Task> startScripts = project.getTasks().named(TASK_START_SCRIPTS_NAME);
 
         CopySpec libChildSpec = project.copySpec();
         libChildSpec.into("lib");
         libChildSpec.from(jar);
-        libChildSpec.from(component.getRuntimeClasspathConfiguration());
+        libChildSpec.from(mainFeature.getRuntimeClasspathConfiguration());
 
         CopySpec binChildSpec = project.copySpec();
 
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/ApplicationPluginConvention.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/ApplicationPluginConvention.java
index 073d6d3..ed07772 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/ApplicationPluginConvention.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/ApplicationPluginConvention.java
@@ -79,8 +79,10 @@ public abstract class ApplicationPluginConvention {
      *     id 'application'
      * }
      *
-     * applicationDistribution.from("some/dir") {
-     *   include "*.txt"
+     * application {
+     *     applicationDistribution.from("some/dir") {
+     *       include "*.txt"
+     *     }
      * }
      * </pre>
      * <p>
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/GroovyBasePlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/GroovyBasePlugin.java
index ec32f4a..9972bf1 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/GroovyBasePlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/GroovyBasePlugin.java
@@ -39,6 +39,7 @@
 import org.gradle.api.tasks.compile.GroovyCompile;
 import org.gradle.api.tasks.javadoc.Groovydoc;
 import org.gradle.api.tasks.javadoc.GroovydocAccess;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.jvm.toolchain.JavaLauncher;
 import org.gradle.jvm.toolchain.JavaToolchainService;
 
@@ -120,7 +121,9 @@ private void configureSourceSetDefaults(Project project) {
     @SuppressWarnings("deprecation")
     private GroovySourceDirectorySet getGroovySourceDirectorySet(SourceSet sourceSet) {
         final org.gradle.api.internal.tasks.DefaultGroovySourceSet groovySourceSet = objectFactory.newInstance(org.gradle.api.internal.tasks.DefaultGroovySourceSet.class, "groovy", ((DefaultSourceSet) sourceSet).getDisplayName(), objectFactory);
-        new DslObject(sourceSet).getConvention().getPlugins().put("groovy", groovySourceSet);
+        DeprecationLogger.whileDisabled(() ->
+            new DslObject(sourceSet).getConvention().getPlugins().put("groovy", groovySourceSet)
+        );
         return groovySourceSet.getGroovy();
     }
 
@@ -157,6 +160,7 @@ private TaskProvider<GroovyCompile> createGroovyCompileTask(Project project, Sou
 
         return compileTask;
     }
+
     private void configureGroovydoc(Project project, GroovyRuntime groovyRuntime) {
         project.getTasks().withType(Groovydoc.class).configureEach(groovydoc -> {
             groovydoc.getConventionMapping().map("groovyClasspath", () -> {
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/GroovyPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/GroovyPlugin.java
index dd8f9aa..69e3c6d 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/GroovyPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/GroovyPlugin.java
@@ -20,9 +20,9 @@
 import org.gradle.api.Project;
 import org.gradle.api.file.SourceDirectorySet;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.tasks.GroovySourceDirectorySet;
 import org.gradle.api.tasks.javadoc.Groovydoc;
-import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
 
 /**
  * <p>A {@link Plugin} which extends the {@link JavaPlugin} to provide support for compiling and documenting Groovy
@@ -45,10 +45,10 @@ private void configureGroovydoc(final Project project) {
             groovyDoc.setDescription("Generates Groovydoc API documentation for the main source code.");
             groovyDoc.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
 
-            JvmSoftwareComponentInternal component = JavaPluginHelper.getJavaComponent(project);
-            groovyDoc.setClasspath(component.getMainOutput().plus(component.getSourceSet().getCompileClasspath()));
+            JvmFeatureInternal mainFeature = JavaPluginHelper.getJavaComponent(project).getMainFeature();
+            groovyDoc.setClasspath(mainFeature.getSourceSet().getOutput().plus(mainFeature.getSourceSet().getCompileClasspath()));
 
-            SourceDirectorySet groovySourceSet = component.getSourceSet().getExtensions().getByType(GroovySourceDirectorySet.class);
+            SourceDirectorySet groovySourceSet = mainFeature.getSourceSet().getExtensions().getByType(GroovySourceDirectorySet.class);
             groovyDoc.setSource(groovySourceSet);
         });
     }
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaApplication.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaApplication.java
index b39e564..f7d608a 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaApplication.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaApplication.java
@@ -91,8 +91,10 @@ public interface JavaApplication {
      *     id 'application'
      * }
      *
-     * applicationDistribution.from("some/dir") {
-     *   include "*.txt"
+     * application {
+     *     applicationDistribution.from("some/dir") {
+     *         include "*.txt"
+     *     }
      * }
      * </pre>
      * <p>
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaBasePlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaBasePlugin.java
index f6bd1c1..fd99227 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaBasePlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaBasePlugin.java
@@ -43,6 +43,7 @@
 import org.gradle.api.plugins.internal.DefaultJavaPluginExtension;
 import org.gradle.api.plugins.internal.JavaConfigurationVariantMapping;
 import org.gradle.api.plugins.internal.JvmPluginsHelper;
+import org.gradle.api.plugins.internal.NaggingJavaPluginConvention;
 import org.gradle.api.plugins.jvm.internal.JvmEcosystemUtilities;
 import org.gradle.api.plugins.jvm.internal.JvmPluginServices;
 import org.gradle.api.provider.Provider;
@@ -146,8 +147,9 @@ public void apply(final Project project) {
     private DefaultJavaPluginExtension addExtensions(final Project project) {
         DefaultToolchainSpec toolchainSpec = objectFactory.newInstance(DefaultToolchainSpec.class);
         SourceSetContainer sourceSets = (SourceSetContainer) project.getExtensions().getByName("sourceSets");
-        DefaultJavaPluginExtension javaPluginExtension = (DefaultJavaPluginExtension) project.getExtensions().create(JavaPluginExtension.class, "java", DefaultJavaPluginExtension.class, project, sourceSets, toolchainSpec, jvmPluginServices);
-        project.getConvention().getPlugins().put("java", objectFactory.newInstance(DefaultJavaPluginConvention.class, project, javaPluginExtension));
+        DefaultJavaPluginExtension javaPluginExtension = (DefaultJavaPluginExtension) project.getExtensions().create(JavaPluginExtension.class, "java", DefaultJavaPluginExtension.class, project, sourceSets, toolchainSpec);
+        DeprecationLogger.whileDisabled(() ->
+            project.getConvention().getPlugins().put("java", new NaggingJavaPluginConvention(objectFactory.newInstance(DefaultJavaPluginConvention.class, project, javaPluginExtension))));
         return javaPluginExtension;
     }
 
@@ -170,6 +172,7 @@ private void configureSourceSetDefaults(Project project, final JavaPluginExtensi
 
     private void configureLibraryElements(TaskProvider<JavaCompile> compileTaskProvider, SourceSet sourceSet, ConfigurationContainer configurations, ObjectFactory objectFactory) {
         ConfigurationInternal compileClasspath = (ConfigurationInternal) configurations.getByName(sourceSet.getCompileClasspathConfigurationName());
+        // TODO:configuration-cache this is a callback that affects configuration attributes #23732
         compileClasspath.beforeLocking(conf -> {
             AttributeContainerInternal attributes = conf.getAttributes();
             if (!attributes.contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE)) {
@@ -257,37 +260,37 @@ private void defineConfigurationsForSourceSet(SourceSet sourceSet, RoleBasedConf
         String sourceSetName = sourceSet.toString();
 
         warnIfConfigurationAlreadyExists(configurations, implementationConfigurationName);
-        Configuration implementationConfiguration = configurations.maybeCreateWithRole(implementationConfigurationName, ConfigurationRoles.INTENDED_BUCKET, false, false);
+        Configuration implementationConfiguration = configurations.maybeCreateWithRole(implementationConfigurationName, ConfigurationRoles.BUCKET, false, false);
         implementationConfiguration.setVisible(false);
         implementationConfiguration.setDescription("Implementation only dependencies for " + sourceSetName + ".");
 
         warnIfConfigurationAlreadyExists(configurations, compileOnlyConfigurationName);
-        Configuration compileOnlyConfiguration = configurations.maybeCreateWithRole(compileOnlyConfigurationName, ConfigurationRoles.INTENDED_BUCKET, false, false);
+        Configuration compileOnlyConfiguration = configurations.maybeCreateWithRole(compileOnlyConfigurationName, ConfigurationRoles.BUCKET, false, false);
         compileOnlyConfiguration.setVisible(false);
         compileOnlyConfiguration.setDescription("Compile only dependencies for " + sourceSetName + ".");
 
         warnIfConfigurationAlreadyExists(configurations, compileClasspathConfigurationName);
-        ConfigurationInternal compileClasspathConfiguration = configurations.maybeCreateWithRole(compileClasspathConfigurationName, ConfigurationRoles.INTENDED_RESOLVABLE, false, false);
+        Configuration compileClasspathConfiguration = configurations.maybeCreateWithRole(compileClasspathConfigurationName, ConfigurationRoles.RESOLVABLE, false, false);
         compileClasspathConfiguration.setVisible(false);
         compileClasspathConfiguration.extendsFrom(compileOnlyConfiguration, implementationConfiguration);
         compileClasspathConfiguration.setDescription("Compile classpath for " + sourceSetName + ".");
         jvmPluginServices.configureAsCompileClasspath(compileClasspathConfiguration);
 
         warnIfConfigurationAlreadyExists(configurations, annotationProcessorConfigurationName);
-        ConfigurationInternal annotationProcessorConfiguration = configurations.maybeCreateWithRole(annotationProcessorConfigurationName, ConfigurationRoles.INTENDED_RESOLVABLE_BUCKET, false, false);
+        Configuration annotationProcessorConfiguration = configurations.maybeCreateWithRole(annotationProcessorConfigurationName, ConfigurationRoles.RESOLVABLE_BUCKET, false, false);
         annotationProcessorConfiguration.setVisible(false);
         annotationProcessorConfiguration.setDescription("Annotation processors and their dependencies for " + sourceSetName + ".");
         jvmPluginServices.configureAsRuntimeClasspath(annotationProcessorConfiguration);
 
         warnIfConfigurationAlreadyExists(configurations, runtimeOnlyConfigurationName);
-        Configuration runtimeOnlyConfiguration = configurations.maybeCreateWithRole(runtimeOnlyConfigurationName, ConfigurationRoles.INTENDED_BUCKET, false, false);
+        Configuration runtimeOnlyConfiguration = configurations.maybeCreateWithRole(runtimeOnlyConfigurationName, ConfigurationRoles.BUCKET, false, false);
         runtimeOnlyConfiguration.setVisible(false);
         runtimeOnlyConfiguration.setDescription("Runtime only dependencies for " + sourceSetName + ".");
 
         // TODO: The jmh plugin prevents asserting this role upon creation; if it has been applied, then it will pre-create a runtime classpath configuration
-        // for the jmg sourceset, which is both resolvable and declarable against, so this would fail if we assert the role's usage here.
+        // for the jmg sourceset, which is both resolvable and declarable, so this would fail if we assert the role's usage here.
         warnIfConfigurationAlreadyExists(configurations, runtimeClasspathConfigurationName);
-        Configuration runtimeClasspathConfiguration = configurations.maybeCreateWithRole(runtimeClasspathConfigurationName, ConfigurationRoles.INTENDED_RESOLVABLE, false, false);
+        Configuration runtimeClasspathConfiguration = configurations.maybeCreateWithRole(runtimeClasspathConfigurationName, ConfigurationRoles.RESOLVABLE, false, false);
         runtimeClasspathConfiguration.setVisible(false);
         runtimeClasspathConfiguration.setDescription("Runtime classpath of " + sourceSetName + ".");
         runtimeClasspathConfiguration.extendsFrom(runtimeOnlyConfiguration, implementationConfiguration);
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaLibraryDistributionPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaLibraryDistributionPlugin.java
index c237944..6daed47 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaLibraryDistributionPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaLibraryDistributionPlugin.java
@@ -22,7 +22,7 @@
 import org.gradle.api.distribution.plugins.DistributionPlugin;
 import org.gradle.api.file.CopySpec;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
-import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 
 /**
  * A {@link Plugin} which package a Java project as a distribution including the JAR and runtime dependencies.
@@ -37,14 +37,14 @@ public void apply(final Project project) {
 
         DistributionContainer distributionContainer = (DistributionContainer)project.getExtensions().getByName("distributions");
         distributionContainer.named(DistributionPlugin.MAIN_DISTRIBUTION_NAME).configure(dist -> {
-            JvmSoftwareComponentInternal component = JavaPluginHelper.getJavaComponent(project);
+            JvmFeatureInternal mainFeature = JavaPluginHelper.getJavaComponent(project).getMainFeature();
             CopySpec childSpec = project.copySpec();
-            childSpec.from(component.getMainJarTask());
+            childSpec.from(mainFeature.getJarTask());
             childSpec.from(project.file("src/dist"));
 
             CopySpec libSpec = project.copySpec();
             libSpec.into("lib");
-            libSpec.from(component.getRuntimeClasspathConfiguration());
+            libSpec.from(mainFeature.getRuntimeClasspathConfiguration());
 
             childSpec.with(libSpec);
             dist.getContents().with(childSpec);
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaLibraryPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaLibraryPlugin.java
index f6e4d81..e47756f 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaLibraryPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaLibraryPlugin.java
@@ -17,13 +17,8 @@
 
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles;
-import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
 import org.gradle.api.plugins.jvm.JvmTestSuite;
-import org.gradle.api.plugins.jvm.internal.JvmEcosystemUtilities;
-import org.gradle.api.tasks.SourceSet;
 import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
 
 import javax.inject.Inject;
@@ -37,39 +32,20 @@
  */
 public abstract class JavaLibraryPlugin implements Plugin<Project> {
 
-    private final JvmEcosystemUtilities jvmEcosystemUtilities;
-
     @Inject
-    public JavaLibraryPlugin(JvmEcosystemUtilities jvmEcosystemUtilities) {
-        this.jvmEcosystemUtilities = jvmEcosystemUtilities;
-    }
+    public JavaLibraryPlugin() { }
 
     @Override
     public void apply(Project project) {
         project.getPluginManager().apply(JavaPlugin.class);
 
         JvmSoftwareComponentInternal component = JavaPluginHelper.getJavaComponent(project);
-        SourceSet sourceSet = component.getSourceSet();
-
-        // TODO: Why do we not do this in createApiElements?
-        jvmEcosystemUtilities.configureClassesDirectoryVariant(component.getApiElementsConfiguration(), sourceSet);
-
-        RoleBasedConfigurationContainerInternal configurations = (RoleBasedConfigurationContainerInternal) project.getConfigurations();
-
-        Configuration api = configurations.maybeCreateWithRole(sourceSet.getApiConfigurationName(), ConfigurationRoles.INTENDED_BUCKET, false, false);
-        api.setDescription("API dependencies for " + sourceSet + ".");
-        api.setVisible(false);
-
-        Configuration compileOnlyApi = configurations.maybeCreateWithRole(sourceSet.getCompileOnlyApiConfigurationName(), ConfigurationRoles.INTENDED_BUCKET, false, false);
-        compileOnlyApi.setDescription("Compile only API dependencies for " + sourceSet + ".");
-        compileOnlyApi.setVisible(false);
-
-        component.getApiElementsConfiguration().extendsFrom(api, compileOnlyApi);
-        component.getImplementationConfiguration().extendsFrom(api);
-        component.getCompileOnlyConfiguration().extendsFrom(compileOnlyApi);
+        component.getMainFeature().withApi();
 
         // Make compileOnlyApi visible to tests.
         JvmTestSuite defaultTestSuite = JavaPluginHelper.getDefaultTestSuite(project);
-        configurations.getByName(defaultTestSuite.getSources().getCompileOnlyConfigurationName()).extendsFrom(compileOnlyApi);
+        project.getConfigurations()
+            .getByName(defaultTestSuite.getSources().getCompileOnlyConfigurationName())
+            .extendsFrom(component.getMainFeature().getCompileOnlyApiConfiguration());
     }
 }
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlatformPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlatformPlugin.java
index f86e7fd..9cb4ee4 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlatformPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlatformPlugin.java
@@ -16,19 +16,20 @@
 package org.gradle.api.plugins;
 
 import com.google.common.collect.Sets;
-import org.gradle.api.Action;
 import org.gradle.api.InvalidUserCodeException;
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.attributes.Category;
 import org.gradle.api.attributes.LibraryElements;
 import org.gradle.api.attributes.Usage;
 import org.gradle.api.capabilities.Capability;
 import org.gradle.api.component.AdhocComponentWithVariants;
 import org.gradle.api.component.SoftwareComponentFactory;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.java.DefaultJavaPlatformExtension;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.plugins.internal.JavaConfigurationVariantMapping;
 import org.gradle.api.publish.PublishingExtension;
@@ -38,8 +39,8 @@
 import org.gradle.api.publish.maven.MavenPublication;
 import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal;
 import org.gradle.api.publish.plugins.PublishingPlugin;
-import org.gradle.internal.component.external.model.ShadowedImmutableCapability;
 import org.gradle.internal.component.external.model.ProjectDerivedCapability;
+import org.gradle.internal.component.external.model.ShadowedImmutableCapability;
 
 import javax.inject.Inject;
 import java.util.Set;
@@ -66,20 +67,6 @@ public abstract class JavaPlatformPlugin implements Plugin<Project> {
     // Resolvable configurations
     public static final String CLASSPATH_CONFIGURATION_NAME = "classpath";
 
-    private static final Action<Configuration> AS_CONSUMABLE_CONFIGURATION = conf -> {
-        conf.setCanBeResolved(false);
-        conf.setCanBeConsumed(true);
-    };
-
-    private static final Action<Configuration> AS_BUCKET = conf -> {
-        conf.setCanBeResolved(false);
-        conf.setCanBeConsumed(false);
-    };
-
-    private static final Action<Configuration> AS_RESOLVABLE_CONFIGURATION = conf -> {
-        conf.setCanBeResolved(true);
-        conf.setCanBeConsumed(false);
-    };
     private static final String DISALLOW_DEPENDENCIES = "Adding dependencies to platforms is not allowed by default.\n" +
         "Most likely you want to add constraints instead.\n" +
         "If you did this intentionally, you need to configure the platform extension to allow dependencies:\n    javaPlatform.allowDependencies()\n" +
@@ -103,7 +90,7 @@ public void apply(Project project) {
         }
         project.getPluginManager().apply(BasePlugin.class);
         project.getPluginManager().apply(JvmEcosystemPlugin.class);
-        createConfigurations(project);
+        createConfigurations((ProjectInternal) project);
         configureExtension(project);
         configurePublishing(project);
     }
@@ -115,39 +102,39 @@ private void createSoftwareComponent(Project project, Configuration apiElements,
         component.addVariantsFromConfiguration(runtimeElements, new JavaConfigurationVariantMapping("runtime", false));
     }
 
-    private void createConfigurations(Project project) {
-        ConfigurationContainer configurations = project.getConfigurations();
+    private void createConfigurations(ProjectInternal project) {
+        RoleBasedConfigurationContainerInternal configurations = project.getConfigurations();
         Capability enforcedCapability = new ShadowedImmutableCapability(new ProjectDerivedCapability(project), "-derived-enforced-platform");
 
-        Configuration api = configurations.create(API_CONFIGURATION_NAME, AS_BUCKET);
-        Configuration apiElements = createConsumableApi(project, configurations, api, API_ELEMENTS_CONFIGURATION_NAME, Category.REGULAR_PLATFORM);
-        Configuration enforcedApiElements = createConsumableApi(project, configurations, api, ENFORCED_API_ELEMENTS_CONFIGURATION_NAME, Category.ENFORCED_PLATFORM);
+        Configuration api = configurations.bucket(API_CONFIGURATION_NAME);
+        Configuration apiElements = createConsumableApi(project, api, API_ELEMENTS_CONFIGURATION_NAME, Category.REGULAR_PLATFORM);
+        Configuration enforcedApiElements = createConsumableApi(project, api, ENFORCED_API_ELEMENTS_CONFIGURATION_NAME, Category.ENFORCED_PLATFORM);
         enforcedApiElements.getOutgoing().capability(enforcedCapability);
 
-        Configuration runtime = project.getConfigurations().create(RUNTIME_CONFIGURATION_NAME, AS_BUCKET);
+        Configuration runtime = project.getConfigurations().bucket(RUNTIME_CONFIGURATION_NAME);
         runtime.extendsFrom(api);
 
         Configuration runtimeElements = createConsumableRuntime(project, runtime, RUNTIME_ELEMENTS_CONFIGURATION_NAME, Category.REGULAR_PLATFORM);
         Configuration enforcedRuntimeElements = createConsumableRuntime(project, runtime, ENFORCED_RUNTIME_ELEMENTS_CONFIGURATION_NAME, Category.ENFORCED_PLATFORM);
         enforcedRuntimeElements.getOutgoing().capability(enforcedCapability);
 
-        Configuration classpath = configurations.create(CLASSPATH_CONFIGURATION_NAME, AS_RESOLVABLE_CONFIGURATION);
+        Configuration classpath = configurations.createWithRole(CLASSPATH_CONFIGURATION_NAME, ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE);
         classpath.extendsFrom(runtimeElements);
         declareConfigurationUsage(project.getObjects(), classpath, Usage.JAVA_RUNTIME, LibraryElements.JAR);
 
         createSoftwareComponent(project, apiElements, runtimeElements);
     }
 
-    private Configuration createConsumableRuntime(Project project, Configuration apiElements, String name, String platformKind) {
-        Configuration runtimeElements = project.getConfigurations().create(name, AS_CONSUMABLE_CONFIGURATION);
+    private Configuration createConsumableRuntime(ProjectInternal project, Configuration apiElements, String name, String platformKind) {
+        Configuration runtimeElements = project.getConfigurations().createWithRole(name, ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
         runtimeElements.extendsFrom(apiElements);
         declareConfigurationUsage(project.getObjects(), runtimeElements, Usage.JAVA_RUNTIME);
         declareConfigurationCategory(project.getObjects(), runtimeElements, platformKind);
         return runtimeElements;
     }
 
-    private Configuration createConsumableApi(Project project, ConfigurationContainer configurations, Configuration api, String name, String platformKind) {
-        Configuration apiElements = configurations.create(name, AS_CONSUMABLE_CONFIGURATION);
+    private Configuration createConsumableApi(ProjectInternal project, Configuration api, String name, String platformKind) {
+        Configuration apiElements = project.getConfigurations().createWithRole(name, ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
         apiElements.extendsFrom(api);
         declareConfigurationUsage(project.getObjects(), apiElements, Usage.JAVA_API);
         declareConfigurationCategory(project.getObjects(), apiElements, platformKind);
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java
index f19f2b7..da75fdc 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java
@@ -31,6 +31,9 @@
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.plugins.jvm.JvmTestSuite;
 import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.TaskCollection;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.api.tasks.bundling.Jar;
 import org.gradle.api.tasks.diagnostics.DependencyInsightReportTask;
 import org.gradle.api.tasks.testing.Test;
 import org.gradle.internal.execution.BuildOutputCleanupRegistry;
@@ -253,14 +256,15 @@ public void apply(final Project project) {
 
         // Set the 'java' component as the project's default.
         Configuration defaultConfiguration = project.getConfigurations().getByName(Dependency.DEFAULT_CONFIGURATION);
-        defaultConfiguration.extendsFrom(component.getRuntimeElementsConfiguration());
+        defaultConfiguration.extendsFrom(component.getMainFeature().getRuntimeElementsConfiguration());
         ((SoftwareComponentContainerInternal) project.getComponents()).getMainComponent().convention(component);
 
         JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class);
         BuildOutputCleanupRegistry buildOutputCleanupRegistry = projectInternal.getServices().get(BuildOutputCleanupRegistry.class);
-
-        configureBuiltInTest(project, component);
         configureSourceSets(javaExtension, buildOutputCleanupRegistry);
+
+        configureTestTaskOrdering(project.getTasks());
+        configureBuiltInTest(project, component);
         configureDiagnostics(project, component);
         configureBuild(project);
     }
@@ -270,6 +274,19 @@ private static void configureSourceSets(JavaPluginExtension pluginExtension, fin
         pluginExtension.getSourceSets().all(sourceSet -> buildOutputCleanupRegistry.registerOutputs(sourceSet.getOutput()));
     }
 
+    /**
+     * Unless there are other concerns, we'd prefer to run jar tasks prior to test tasks, as this might offer a small performance improvement
+     * for common usage.  In practice, running test tasks tends to take longer than building a jar; especially as a project matures. If tasks
+     * in downstream projects require the jar from this project, and the jar and test tasks in this project are available to be run in either order,
+     * running jar first so that other projects can continue executing tasks in parallel while this project runs its tests could be an improvement.
+     * However, while we want to prioritize cross-project dependencies to maximize parallelism if possible, we don't want to add an explicit
+     * dependsOn() relationship between the jar task and the test task, so that any projects which need to run test tasks first will not need modification.
+     */
+    private static void configureTestTaskOrdering(TaskContainer tasks) {
+        TaskCollection<Jar> jarTasks = tasks.withType(Jar.class);
+        tasks.withType(Test.class).configureEach(test -> test.shouldRunAfter(jarTasks));
+    }
+
     private static void configureBuiltInTest(Project project, JvmSoftwareComponentInternal component) {
         TestingExtension testing = project.getExtensions().getByType(TestingExtension.class);
         final NamedDomainObjectProvider<JvmTestSuite> testSuite = testing.getSuites().register(DEFAULT_TEST_SUITE_NAME, JvmTestSuite.class, suite -> {
@@ -285,7 +302,7 @@ private static void configureBuiltInTest(Project project, JvmSoftwareComponentIn
             // relies on the main source set being created before the tests. So, this code here cannot live in the
             // JvmTestSuitePlugin and must live here, so that we can ensure we register this test suite after we've
             // created the main source set.
-            final SourceSet mainSourceSet = component.getSourceSet();
+            final SourceSet mainSourceSet = component.getMainFeature().getSourceSet();
             final FileCollection mainSourceSetOutput = mainSourceSet.getOutput();
             final FileCollection testSourceSetOutput = testSourceSet.getOutput();
             testSourceSet.setCompileClasspath(project.getObjects().fileCollection().from(mainSourceSetOutput, testCompileClasspathConfiguration));
@@ -303,7 +320,7 @@ private static void configureBuiltInTest(Project project, JvmSoftwareComponentIn
 
     private static void configureDiagnostics(Project project, JvmSoftwareComponentInternal component) {
         project.getTasks().withType(DependencyInsightReportTask.class).configureEach(task -> {
-            new DslObject(task).getConventionMapping().map("configuration", component::getCompileClasspathConfiguration);
+            new DslObject(task).getConventionMapping().map("configuration", component.getMainFeature()::getCompileClasspathConfiguration);
         });
     }
 
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaTestFixturesPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaTestFixturesPlugin.java
index 592bc69..24b43b6 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaTestFixturesPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaTestFixturesPlugin.java
@@ -20,12 +20,20 @@
 import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ProjectDependency;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRole;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.plugins.internal.JavaConfigurationVariantMapping;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
-import org.gradle.api.plugins.jvm.internal.JvmModelingServices;
+import org.gradle.api.plugins.jvm.internal.DefaultJvmFeature;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.tasks.SourceSet;
+import org.gradle.internal.component.external.model.ProjectDerivedCapability;
 import org.gradle.internal.component.external.model.ProjectTestFixtures;
+import org.gradle.jvm.component.internal.DefaultJvmSoftwareComponent;
 
 import javax.inject.Inject;
+import java.util.Collections;
 
 import static org.gradle.internal.component.external.model.TestFixturesSupport.TEST_FIXTURES_API;
 import static org.gradle.internal.component.external.model.TestFixturesSupport.TEST_FIXTURES_FEATURE_NAME;
@@ -44,21 +52,33 @@
  */
 public abstract class JavaTestFixturesPlugin implements Plugin<Project> {
 
-    private final JvmModelingServices jvmEcosystemUtilities;
-
     @Inject
-    public JavaTestFixturesPlugin(JvmModelingServices jvmModelingServices) {
-        this.jvmEcosystemUtilities = jvmModelingServices;
-    }
+    public JavaTestFixturesPlugin() { }
 
     @Override
     public void apply(Project project) {
         project.getPluginManager().withPlugin("java", plugin -> {
-            jvmEcosystemUtilities.createJvmVariant(TEST_FIXTURES_FEATURE_NAME, builder ->
-                builder
-                    .exposesApi()
-                    .published()
+            JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class);
+            SourceSet testFixturesSourceSet = extension.getSourceSets().maybeCreate(TEST_FIXTURES_FEATURE_NAME);
+
+            @SuppressWarnings("deprecation")
+            ConfigurationRole role = ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE;
+
+            JvmFeatureInternal feature = new DefaultJvmFeature(
+                TEST_FIXTURES_FEATURE_NAME,
+                testFixturesSourceSet,
+                Collections.singletonList(new ProjectDerivedCapability(project, TEST_FIXTURES_FEATURE_NAME)),
+                (ProjectInternal) project,
+                role,
+                false
             );
+
+            feature.withApi();
+
+            DefaultJvmSoftwareComponent component = (DefaultJvmSoftwareComponent) JavaPluginHelper.getJavaComponent(project);
+            component.addVariantsFromConfiguration(feature.getApiElementsConfiguration(), new JavaConfigurationVariantMapping("compile", true));
+            component.addVariantsFromConfiguration(feature.getRuntimeElementsConfiguration(), new JavaConfigurationVariantMapping("runtime", true));
+
             createImplicitTestFixturesDependencies(project);
         });
     }
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmEcosystemPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmEcosystemPlugin.java
index 5e778cd..73862e1 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmEcosystemPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmEcosystemPlugin.java
@@ -57,7 +57,7 @@ public void apply(Project project) {
         project.getExtensions().add(SourceSetContainer.class, "sourceSets", sourceSets);
         configureVariantDerivationStrategy(p);
         configureSchema(p);
-        jvmPluginServices.inject(p, sourceSets);
+        jvmPluginServices.inject(p);
     }
 
     private void configureVariantDerivationStrategy(ProjectInternal project) {
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmTestSuitePlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmTestSuitePlugin.java
index 271595a..b07d831 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmTestSuitePlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmTestSuitePlugin.java
@@ -29,6 +29,7 @@
 import org.gradle.api.attributes.TestSuiteTargetName;
 import org.gradle.api.attributes.TestSuiteType;
 import org.gradle.api.attributes.VerificationType;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
 import org.gradle.api.plugins.jvm.JvmTestSuite;
@@ -79,14 +80,14 @@ public void apply(Project project) {
             // So defer looking up the java extension and sourceSet until the convention mapping is resolved.
             // See https://github.com/gradle/gradle/issues/18622
             test.getConventionMapping().map("testClassesDirs", () -> {
-                DeprecationLogger.deprecate("Configuring the test classes dirs by default for standalone Test tasks")
+                DeprecationLogger.deprecate("Relying on the convention for Test.testClassesDirs in custom Test tasks")
                     .willBeRemovedInGradle9()
                     .withUpgradeGuideSection(8, "test_task_default_classpath")
                     .nagUser();
                 return JavaPluginHelper.getDefaultTestSuite(project).getSources().getOutput().getClassesDirs();
             });
             test.getConventionMapping().map("classpath", () -> {
-                DeprecationLogger.deprecate("Configuring the test classpath by default for standalone Test tasks")
+                DeprecationLogger.deprecate("Relying on the convention for Test.classpath in custom Test tasks")
                     .willBeRemovedInGradle9()
                     .withUpgradeGuideSection(8, "test_task_default_classpath")
                     .nagUser();
@@ -105,14 +106,14 @@ public void apply(Project project) {
             });
         });
 
-        configureTestDataElementsVariants(project);
+        configureTestDataElementsVariants((ProjectInternal) project);
     }
 
     private String getDefaultTestType(JvmTestSuite testSuite) {
         return DEFAULT_TEST_SUITE_NAME.equals(testSuite.getName()) ? TestSuiteType.UNIT_TEST : TextUtil.camelToKebabCase(testSuite.getName());
     }
 
-    private void configureTestDataElementsVariants(Project project) {
+    private void configureTestDataElementsVariants(ProjectInternal project) {
         final TestingExtension testing = project.getExtensions().getByType(TestingExtension.class);
         final ExtensiblePolymorphicDomainObjectContainer<TestSuite> testSuites = testing.getSuites();
 
@@ -123,12 +124,10 @@ private void configureTestDataElementsVariants(Project project) {
         });
     }
 
-    private void addTestResultsVariant(Project project, JvmTestSuite suite, JvmTestSuiteTarget target) {
-        final Configuration variant = project.getConfigurations().create(TEST_RESULTS_ELEMENTS_VARIANT_PREFIX + StringUtils.capitalize(target.getName()));
+    private void addTestResultsVariant(ProjectInternal project, JvmTestSuite suite, JvmTestSuiteTarget target) {
+        final Configuration variant = project.getConfigurations().consumable(TEST_RESULTS_ELEMENTS_VARIANT_PREFIX + StringUtils.capitalize(target.getName()));
         variant.setDescription("Directory containing binary results of running tests for the " + suite.getName() + " Test Suite's " + target.getName() + " target.");
         variant.setVisible(false);
-        variant.setCanBeResolved(false);
-        variant.setCanBeConsumed(true);
 
         final ObjectFactory objects = project.getObjects();
         variant.attributes(attributes -> {
@@ -147,7 +146,7 @@ private void addTestResultsVariant(Project project, JvmTestSuite suite, JvmTestS
 
     private TestSuiteType createNamedTestTypeAndVerifyUniqueness(Project project, TestSuite suite, String tt) {
         final TestSuite other = testTypesInUse.putIfAbsent(tt, suite);
-        if (null != other) {
+        if (null != other && other != suite) {
             throw new BuildException("Could not configure suite: '" + suite.getName() + "'. Another test suite: '" + other.getName() + "' uses the type: '" + tt + "' and has already been configured in project: '" + project.getName() + "'.");
         }
         return project.getObjects().named(TestSuiteType.class, tt);
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmToolchainManagementPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmToolchainManagementPlugin.java
index d99f8bf..32eb10b 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmToolchainManagementPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/JvmToolchainManagementPlugin.java
@@ -42,4 +42,4 @@ public void apply(Settings settings) {
         toolchainManagement.getExtensions()
                 .add(JvmToolchainManagement.class, "jvm", getDefaultJvmToolchainManagement());
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/TestReportAggregationPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/TestReportAggregationPlugin.java
index 105dd47..6d3aad7 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/TestReportAggregationPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/TestReportAggregationPlugin.java
@@ -27,6 +27,8 @@
 import org.gradle.api.attributes.VerificationType;
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.testing.DefaultAggregateTestReport;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.plugins.jvm.JvmTestSuite;
@@ -59,11 +61,10 @@ public abstract class TestReportAggregationPlugin implements Plugin<Project> {
     public void apply(Project project) {
         project.getPluginManager().apply("org.gradle.reporting-base");
 
-        final Configuration testAggregation = project.getConfigurations().create(TEST_REPORT_AGGREGATION_CONFIGURATION_NAME);
+        RoleBasedConfigurationContainerInternal configurations = ((ProjectInternal) project).getConfigurations();
+        final Configuration testAggregation = configurations.bucket(TEST_REPORT_AGGREGATION_CONFIGURATION_NAME);
         testAggregation.setDescription("A configuration to collect test execution results");
         testAggregation.setVisible(false);
-        testAggregation.setCanBeConsumed(false);
-        testAggregation.setCanBeResolved(false);
 
         ReportingExtension reporting = project.getExtensions().getByType(ReportingExtension.class);
         reporting.getReports().registerBinding(AggregateTestReport.class, DefaultAggregateTestReport.class);
@@ -78,12 +79,10 @@ public void apply(Project project) {
         });
 
         // A resolvable configuration to collect test results
-        Configuration testResultsConf = project.getConfigurations().create("aggregateTestReportResults");
+        Configuration testResultsConf = configurations.resolvable("aggregateTestReportResults");
         testResultsConf.extendsFrom(testAggregation);
         testResultsConf.setDescription("Graph needed for the aggregated test results report.");
         testResultsConf.setVisible(false);
-        testResultsConf.setCanBeConsumed(false);
-        testResultsConf.setCanBeResolved(true);
 
         // Iterate and configure each user-specified report.
         reporting.getReports().withType(AggregateTestReport.class).all(report -> {
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/WarPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/WarPlugin.java
index 1af0ef5..04ef936 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/WarPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/WarPlugin.java
@@ -23,6 +23,7 @@
 import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.attributes.Usage;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact;
 import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
 import org.gradle.api.internal.java.WebApplication;
@@ -32,10 +33,10 @@
 import org.gradle.api.plugins.internal.DefaultWarPluginConvention;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
 import org.gradle.api.plugins.jvm.JvmTestSuite;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.tasks.TaskProvider;
 import org.gradle.api.tasks.bundling.War;
 import org.gradle.internal.deprecation.DeprecationLogger;
-import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
 
 import javax.inject.Inject;
 import java.util.concurrent.Callable;
@@ -63,7 +64,7 @@ public abstract class WarPlugin implements Plugin<Project> {
     private final ImmutableAttributesFactory attributesFactory;
 
     private Project project;
-    private JvmSoftwareComponentInternal component;
+    private JvmFeatureInternal mainFeature;
 
     @Inject
     public WarPlugin(ObjectFactory objectFactory, ImmutableAttributesFactory attributesFactory) {
@@ -75,18 +76,18 @@ public WarPlugin(ObjectFactory objectFactory, ImmutableAttributesFactory attribu
     public void apply(final Project project) {
         project.getPluginManager().apply(JavaPlugin.class);
         this.project = project;
-        this.component = JavaPluginHelper.getJavaComponent(project);
+        this.mainFeature = JavaPluginHelper.getJavaComponent(project).getMainFeature();
 
         final WarPluginConvention pluginConvention = objectFactory.newInstance(DefaultWarPluginConvention.class, project);
-        project.getConvention().getPlugins().put("war", pluginConvention);
+        DeprecationLogger.whileDisabled(() -> project.getConvention().getPlugins().put("war", pluginConvention));
 
         project.getTasks().withType(War.class).configureEach(task -> {
-            task.getWebAppDirectory().convention(project.getLayout().dir(project.provider(() -> pluginConvention.getWebAppDir())));
+            task.getWebAppDirectory().convention(project.getLayout().dir(project.provider(() -> DeprecationLogger.whileDisabled(() -> pluginConvention.getWebAppDir()))));
             task.from(task.getWebAppDirectory());
-            task.dependsOn((Callable) () -> component.getSourceSet().getRuntimeClasspath());
+            task.dependsOn((Callable) () -> mainFeature.getSourceSet().getRuntimeClasspath());
             task.classpath((Callable) () -> {
                 Configuration providedRuntime = project.getConfigurations().getByName(PROVIDED_RUNTIME_CONFIGURATION_NAME);
-                return component.getSourceSet().getRuntimeClasspath().minus(providedRuntime);
+                return mainFeature.getSourceSet().getRuntimeClasspath().minus(providedRuntime);
             });
         });
 
@@ -97,7 +98,7 @@ public void apply(final Project project) {
 
         PublishArtifact warArtifact = new LazyPublishArtifact(war, ((ProjectInternal) project).getFileResolver(), ((ProjectInternal) project).getTaskDependencyFactory());
         project.getExtensions().getByType(DefaultArtifactPublicationSet.class).addCandidate(warArtifact);
-        configureConfigurations(project, project.getConfigurations(), component);
+        configureConfigurations(((ProjectInternal) project).getConfigurations(), mainFeature);
         configureComponent(project, warArtifact);
     }
 
@@ -113,22 +114,20 @@ public void configureConfigurations(ConfigurationContainer configurationContaine
             .withUpgradeGuideSection(8, "war_plugin_configure_configurations")
             .nagUser();
 
-        configureConfigurations(project, configurationContainer, component);
+        configureConfigurations((RoleBasedConfigurationContainerInternal) configurationContainer, mainFeature);
     }
 
-    private void configureConfigurations(Project project, ConfigurationContainer configurationContainer, JvmSoftwareComponentInternal component) {
-        Configuration providedCompileConfiguration = configurationContainer.create(PROVIDED_COMPILE_CONFIGURATION_NAME).setVisible(false).
+    private void configureConfigurations(RoleBasedConfigurationContainerInternal configurationContainer, JvmFeatureInternal mainFeature) {
+        Configuration providedCompileConfiguration = configurationContainer.resolvableBucket(PROVIDED_COMPILE_CONFIGURATION_NAME).setVisible(false).
             setDescription("Additional compile classpath for libraries that should not be part of the WAR archive.");
-        providedCompileConfiguration.setCanBeConsumed(false);
 
-        Configuration providedRuntimeConfiguration = configurationContainer.create(PROVIDED_RUNTIME_CONFIGURATION_NAME).setVisible(false).
+        Configuration providedRuntimeConfiguration = configurationContainer.resolvableBucket(PROVIDED_RUNTIME_CONFIGURATION_NAME).setVisible(false).
             extendsFrom(providedCompileConfiguration).
             setDescription("Additional runtime classpath for libraries that should not be part of the WAR archive.");
-        providedRuntimeConfiguration.setCanBeConsumed(false);
 
-        component.getImplementationConfiguration().extendsFrom(providedCompileConfiguration);
-        component.getRuntimeClasspathConfiguration().extendsFrom(providedRuntimeConfiguration);
-        component.getRuntimeElementsConfiguration().extendsFrom(providedRuntimeConfiguration);
+        mainFeature.getImplementationConfiguration().extendsFrom(providedCompileConfiguration);
+        mainFeature.getRuntimeClasspathConfiguration().extendsFrom(providedRuntimeConfiguration);
+        mainFeature.getRuntimeElementsConfiguration().extendsFrom(providedRuntimeConfiguration);
 
         JvmTestSuite defaultTestSuite = JavaPluginHelper.getDefaultTestSuite(project);
         configurationContainer.getByName(defaultTestSuite.getSources().getRuntimeClasspathConfigurationName()).extendsFrom(providedRuntimeConfiguration);
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/catalog/VersionCatalogPlugin.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/catalog/VersionCatalogPlugin.java
index 2c870ce..17de20b 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/catalog/VersionCatalogPlugin.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/catalog/VersionCatalogPlugin.java
@@ -22,6 +22,9 @@
 import org.gradle.api.attributes.Usage;
 import org.gradle.api.component.AdhocComponentWithVariants;
 import org.gradle.api.component.SoftwareComponentFactory;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.plugins.BasePlugin;
 import org.gradle.api.plugins.catalog.internal.DefaultVersionCatalogPluginExtension;
 import org.gradle.api.plugins.catalog.internal.CatalogExtensionInternal;
@@ -51,17 +54,15 @@ public VersionCatalogPlugin(SoftwareComponentFactory softwareComponentFactory) {
 
     @Override
     public void apply(Project project) {
-        Configuration dependenciesConfiguration = createDependenciesConfiguration(project);
+        Configuration dependenciesConfiguration = createDependenciesConfiguration((ProjectInternal) project);
         CatalogExtensionInternal extension = createExtension(project, dependenciesConfiguration);
         TaskProvider<TomlFileGenerator> generator = createGenerator(project, extension);
-        createPublication(project, generator);
+        createPublication((ProjectInternal) project, generator);
     }
 
-    private void createPublication(Project project, TaskProvider<TomlFileGenerator> generator) {
-        Configuration exported = project.getConfigurations().create(VERSION_CATALOG_ELEMENTS, cnf -> {
+    private void createPublication(ProjectInternal project, TaskProvider<TomlFileGenerator> generator) {
+        @SuppressWarnings("deprecation") Configuration exported = project.getConfigurations().createWithRole(VERSION_CATALOG_ELEMENTS, ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE, cnf -> {
             cnf.setDescription("Artifacts for the version catalog");
-            cnf.setCanBeConsumed(true);
-            cnf.setCanBeResolved(false);
             cnf.getOutgoing().artifact(generator);
             cnf.attributes(attrs -> {
                 attrs.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.REGULAR_PLATFORM));
@@ -73,11 +74,9 @@ private void createPublication(Project project, TaskProvider<TomlFileGenerator>
         versionCatalog.addVariantsFromConfiguration(exported, new JavaConfigurationVariantMapping("compile", true));
     }
 
-    private Configuration createDependenciesConfiguration(Project project) {
-        return project.getConfigurations().create(GRADLE_PLATFORM_DEPENDENCIES, cnf -> {
+    private Configuration createDependenciesConfiguration(ProjectInternal project) {
+        return project.getConfigurations().createWithRole(GRADLE_PLATFORM_DEPENDENCIES, ConfigurationRoles.BUCKET, cnf -> {
             cnf.setVisible(false);
-            cnf.setCanBeConsumed(false);
-            cnf.setCanBeResolved(false);
         });
     }
 
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultAdhocSoftwareComponent.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultAdhocSoftwareComponent.java
index 96a8638..0a68acb 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultAdhocSoftwareComponent.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultAdhocSoftwareComponent.java
@@ -73,6 +73,10 @@ public Set<? extends UsageContext> getUsages() {
         return builder.build();
     }
 
+    protected boolean isRegisteredAsLegacyVariant(Configuration outgoingConfiguration) {
+        return variants.containsKey(outgoingConfiguration);
+    }
+
     @Override
     public void finalizeValue() {
         finalized = true;
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultApplicationPluginConvention.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultApplicationPluginConvention.java
index 1d75134..061038b 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultApplicationPluginConvention.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultApplicationPluginConvention.java
@@ -21,6 +21,7 @@
 import org.gradle.api.plugins.ApplicationPluginConvention;
 import org.gradle.api.reflect.HasPublicType;
 import org.gradle.api.reflect.TypeOf;
+import org.gradle.internal.deprecation.DeprecationLogger;
 
 import javax.inject.Inject;
 import java.util.ArrayList;
@@ -49,56 +50,74 @@ public TypeOf<?> getPublicType() {
 
     @Override
     public String getApplicationName() {
+        logDeprecation();
         return applicationName;
     }
 
     @Override
     public void setApplicationName(String applicationName) {
+        logDeprecation();
         this.applicationName = applicationName;
     }
 
     @Override
     public String getMainClassName() {
+        logDeprecation();
         return mainClassName;
     }
 
     @Override
     public void setMainClassName(String mainClassName) {
+        logDeprecation();
         this.mainClassName = mainClassName;
     }
 
     @Override
     public Iterable<String> getApplicationDefaultJvmArgs() {
+        logDeprecation();
         return applicationDefaultJvmArgs;
     }
 
     @Override
     public void setApplicationDefaultJvmArgs(Iterable<String> applicationDefaultJvmArgs) {
+        logDeprecation();
         this.applicationDefaultJvmArgs = applicationDefaultJvmArgs;
     }
 
     @Override
     public String getExecutableDir() {
+        logDeprecation();
         return executableDir;
     }
 
     @Override
     public void setExecutableDir(String executableDir) {
+        logDeprecation();
         this.executableDir = executableDir;
     }
 
     @Override
     public CopySpec getApplicationDistribution() {
+        logDeprecation();
         return applicationDistribution;
     }
 
     @Override
     public void setApplicationDistribution(CopySpec applicationDistribution) {
+        logDeprecation();
         this.applicationDistribution = applicationDistribution;
     }
 
     @Override
     public Project getProject() {
+        logDeprecation();
         return project;
     }
+
+    private static void logDeprecation() {
+        DeprecationLogger.deprecateType(ApplicationPluginConvention.class)
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "application_convention_deprecation")
+            .nagUser();
+    }
 }
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaApplication.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaApplication.java
index db26cf4..440fd63 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaApplication.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaApplication.java
@@ -22,6 +22,7 @@
 import org.gradle.api.plugins.JavaApplication;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.ProviderFactory;
+import org.gradle.internal.deprecation.DeprecationLogger;
 
 public class DefaultJavaApplication implements JavaApplication {
     private final ApplicationPluginConvention convention;
@@ -31,17 +32,17 @@ public class DefaultJavaApplication implements JavaApplication {
     public DefaultJavaApplication(ApplicationPluginConvention convention, ObjectFactory objectFactory, ProviderFactory providerFactory) {
         this.convention = convention;
         this.mainModule = objectFactory.property(String.class);
-        this.mainClass = objectFactory.property(String.class).convention(providerFactory.provider(convention::getMainClassName));
+        this.mainClass = objectFactory.property(String.class).convention(providerFactory.provider(() -> DeprecationLogger.whileDisabled(convention::getMainClassName)));
     }
 
     @Override
     public String getApplicationName() {
-        return convention.getApplicationName();
+        return DeprecationLogger.whileDisabled(convention::getApplicationName);
     }
 
     @Override
     public void setApplicationName(String applicationName) {
-        convention.setApplicationName(applicationName);
+        DeprecationLogger.whileDisabled(() -> convention.setApplicationName(applicationName));
     }
 
     @Override
@@ -56,31 +57,31 @@ public Property<String> getMainClass() {
 
     @Override
     public Iterable<String> getApplicationDefaultJvmArgs() {
-        return convention.getApplicationDefaultJvmArgs();
+        return DeprecationLogger.whileDisabled(convention::getApplicationDefaultJvmArgs);
     }
 
     @Override
     public void setApplicationDefaultJvmArgs(Iterable<String> applicationDefaultJvmArgs) {
-        convention.setApplicationDefaultJvmArgs(applicationDefaultJvmArgs);
+        DeprecationLogger.whileDisabled(() -> convention.setApplicationDefaultJvmArgs(applicationDefaultJvmArgs));
     }
 
     @Override
     public String getExecutableDir() {
-        return convention.getExecutableDir();
+        return DeprecationLogger.whileDisabled(convention::getExecutableDir);
     }
 
     @Override
     public void setExecutableDir(String executableDir) {
-        convention.setExecutableDir(executableDir);
+        DeprecationLogger.whileDisabled(() -> convention.setExecutableDir(executableDir));
     }
 
     @Override
     public CopySpec getApplicationDistribution() {
-        return convention.getApplicationDistribution();
+        return DeprecationLogger.whileDisabled(convention::getApplicationDistribution);
     }
 
     @Override
     public void setApplicationDistribution(CopySpec applicationDistribution) {
-        convention.setApplicationDistribution(applicationDistribution);
+        DeprecationLogger.whileDisabled(() -> convention.setApplicationDistribution(applicationDistribution));
     }
 }
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaFeatureSpec.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaFeatureSpec.java
index 0652257..861c576 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaFeatureSpec.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaFeatureSpec.java
@@ -15,33 +15,37 @@
  */
 package org.gradle.api.plugins.internal;
 
-import com.google.common.collect.Lists;
 import org.gradle.api.InvalidUserCodeException;
+import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.capabilities.Capability;
-import org.gradle.api.plugins.jvm.internal.JvmModelingServices;
-import org.gradle.api.plugins.jvm.internal.JvmVariantBuilderInternal;
+import org.gradle.api.component.AdhocComponentWithVariants;
+import org.gradle.api.component.SoftwareComponent;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRole;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.plugins.jvm.internal.DefaultJvmFeature;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.internal.component.external.model.DefaultImmutableCapability;
+import org.gradle.internal.component.external.model.ProjectDerivedCapability;
 
+import javax.annotation.Nullable;
+import java.util.ArrayList;
 import java.util.List;
 
 public class DefaultJavaFeatureSpec implements FeatureSpecInternal {
     private final String name;
-    private final List<Capability> capabilities = Lists.newArrayListWithExpectedSize(2);
-    private final JvmModelingServices jvmEcosystemUtilities;
+    private final List<Capability> capabilities = new ArrayList<>(1);
+    private final ProjectInternal project;
 
-    private boolean overrideDefaultCapability = true;
     private SourceSet sourceSet;
     private boolean withJavadocJar = false;
     private boolean withSourcesJar = false;
     private boolean allowPublication = true;
 
-    public DefaultJavaFeatureSpec(String name,
-                                  Capability defaultCapability,
-                                  JvmModelingServices jvmModelingServices) {
+    public DefaultJavaFeatureSpec(String name, ProjectInternal project) {
         this.name = name;
-        this.jvmEcosystemUtilities = jvmModelingServices;
-        this.capabilities.add(defaultCapability);
+        this.project = project;
     }
 
     @Override
@@ -51,10 +55,6 @@ public void usingSourceSet(SourceSet sourceSet) {
 
     @Override
     public void capability(String group, String name, String version) {
-        if (overrideDefaultCapability) {
-            capabilities.clear();
-            overrideDefaultCapability = false;
-        }
         capabilities.add(new DefaultImmutableCapability(group, name, version));
     }
 
@@ -83,24 +83,45 @@ private void setupConfigurations(SourceSet sourceSet) {
             throw new InvalidUserCodeException("You must specify which source set to use for feature '" + name + "'");
         }
 
-        jvmEcosystemUtilities.createJvmVariant(name, builder -> {
-            builder.usingSourceSet(sourceSet)
-                .withDisplayName("feature " + name)
-                .exposesApi();
-            if (withJavadocJar) {
-                builder.withJavadocJar();
-            }
-            if (withSourcesJar) {
-                builder.withSourcesJar();
-            }
-            if (allowPublication) {
-                builder.published();
-            }
-            for (Capability capability : capabilities) {
-                ((JvmVariantBuilderInternal)builder).capability(capability);
-            }
-        });
+        if (capabilities.isEmpty()) {
+            capabilities.add(new ProjectDerivedCapability(project, name));
+        }
 
+        @SuppressWarnings("deprecation")
+        ConfigurationRole role = ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE;
+        JvmFeatureInternal feature = new DefaultJvmFeature(name, sourceSet, capabilities, project, role, SourceSet.isMain(sourceSet));
+        feature.withApi();
+
+        AdhocComponentWithVariants component = findJavaComponent();
+        if (withJavadocJar && component != null) {
+            feature.withJavadocJar();
+            Configuration javadocElements = feature.getJavadocElementsConfiguration();
+            component.addVariantsFromConfiguration(javadocElements, new JavaConfigurationVariantMapping("runtime", true));
+        }
+        if (withSourcesJar && component != null) {
+            feature.withSourcesJar();
+            Configuration sourcesElements = feature.getSourcesElementsConfiguration();
+            component.addVariantsFromConfiguration(sourcesElements, new JavaConfigurationVariantMapping("runtime", true));
+        }
+
+        if (allowPublication && component != null) {
+            component.addVariantsFromConfiguration(feature.getApiElementsConfiguration(), new JavaConfigurationVariantMapping("compile", true));
+            component.addVariantsFromConfiguration(feature.getRuntimeElementsConfiguration(), new JavaConfigurationVariantMapping("runtime", true));
+        }
+    }
+
+    // TODO: #23495 Investigate the implications of using this class without
+    //       the java plugin applied, and thus no java component present.
+    //       In the long run, all domain objects created by this feature should be
+    //       owned by a component. If we do not add them to the default java component,
+    //       we should be adding them to a user-provided or new component instead.
+    @Nullable
+    public AdhocComponentWithVariants findJavaComponent() {
+        SoftwareComponent component = project.getComponents().findByName("java");
+        if (component instanceof AdhocComponentWithVariants) {
+            return (AdhocComponentWithVariants) component;
+        }
+        return null;
     }
 
 }
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaPluginExtension.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaPluginExtension.java
index 15e63ab..b54600e 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaPluginExtension.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultJavaPluginExtension.java
@@ -24,7 +24,6 @@
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.capabilities.Capability;
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.java.archives.Manifest;
@@ -36,15 +35,13 @@
 import org.gradle.api.plugins.JavaPluginExtension;
 import org.gradle.api.plugins.JavaResolutionConsistency;
 import org.gradle.api.plugins.jvm.JvmTestSuite;
-import org.gradle.api.plugins.jvm.internal.JvmPluginServices;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.reporting.ReportingExtension;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.SourceSetContainer;
 import org.gradle.internal.Actions;
-import org.gradle.internal.component.external.model.ProjectDerivedCapability;
 import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.jvm.DefaultModularitySpec;
-import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
 import org.gradle.jvm.toolchain.JavaToolchainSpec;
 import org.gradle.jvm.toolchain.internal.DefaultToolchainSpec;
 import org.gradle.jvm.toolchain.internal.JavaToolchainSpecInternal;
@@ -64,7 +61,6 @@ public class DefaultJavaPluginExtension implements JavaPluginExtension {
     private final JavaToolchainSpecInternal toolchainSpec;
     private final ObjectFactory objectFactory;
     private final ModularitySpec modularity;
-    private final JvmPluginServices jvmPluginServices;
     private final JavaToolchainSpec toolchain;
     private final ProjectInternal project;
 
@@ -76,7 +72,7 @@ public class DefaultJavaPluginExtension implements JavaPluginExtension {
     private boolean autoTargetJvm = true;
 
     @Inject
-    public DefaultJavaPluginExtension(ProjectInternal project, SourceSetContainer sourceSets, DefaultToolchainSpec toolchainSpec, JvmPluginServices jvmPluginServices) {
+    public DefaultJavaPluginExtension(ProjectInternal project, SourceSetContainer sourceSets, DefaultToolchainSpec toolchainSpec) {
         this.docsDir = project.getObjects().directoryProperty();
         this.testResultsDir = project.getObjects().directoryProperty();
         this.testReportDir = project.getObjects().directoryProperty(); //TestingBasePlugin.TESTS_DIR_NAME;
@@ -85,7 +81,6 @@ public DefaultJavaPluginExtension(ProjectInternal project, SourceSetContainer so
         this.toolchainSpec = toolchainSpec;
         this.objectFactory = project.getObjects();
         this.modularity = objectFactory.newInstance(DefaultModularitySpec.class);
-        this.jvmPluginServices = jvmPluginServices;
         this.toolchain = toolchainSpec;
         configureDefaults();
     }
@@ -198,11 +193,7 @@ public boolean getAutoTargetJvmDisabled() {
 
     @Override
     public void registerFeature(String name, Action<? super FeatureSpec> configureAction) {
-        Capability defaultCapability = new ProjectDerivedCapability(project, name);
-        DefaultJavaFeatureSpec spec = new DefaultJavaFeatureSpec(
-            validateFeatureName(name),
-            defaultCapability,
-            jvmPluginServices);
+        DefaultJavaFeatureSpec spec = new DefaultJavaFeatureSpec(validateFeatureName(name), project);
         configureAction.execute(spec);
         spec.create();
     }
@@ -212,7 +203,7 @@ public void withJavadocJar() {
         maybeEmitMissingJavaComponentDeprecation("withJavadocJar()");
 
         if (project.getPlugins().hasPlugin(JavaPlugin.class)) {
-            JavaPluginHelper.getJavaComponent(project).enableJavadocJarVariant();
+            JavaPluginHelper.getJavaComponent(project).withJavadocJar();
         } else {
             SourceSet main = getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
             JvmPluginsHelper.createDocumentationVariantWithArtifact(
@@ -232,7 +223,7 @@ public void withSourcesJar() {
         maybeEmitMissingJavaComponentDeprecation("withSourcesJar()");
 
         if (project.getPlugins().hasPlugin(JavaPlugin.class)) {
-            JavaPluginHelper.getJavaComponent(project).enableSourcesJarVariant();
+            JavaPluginHelper.getJavaComponent(project).withSourcesJar();
         } else {
             SourceSet main = getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
             JvmPluginsHelper.createDocumentationVariantWithArtifact(
@@ -303,11 +294,11 @@ public DefaultJavaResolutionConsistency(Project project, SourceSetContainer sour
             this.configurations = configurations;
 
             if (project.getPlugins().hasPlugin(JavaPlugin.class)) {
-                JvmSoftwareComponentInternal component = JavaPluginHelper.getJavaComponent(project);
+                JvmFeatureInternal mainFeature = JavaPluginHelper.getJavaComponent(project).getMainFeature();
                 JvmTestSuite defaultTestSuite = JavaPluginHelper.getDefaultTestSuite(project);
 
-                mainCompileClasspath = component.getCompileClasspathConfiguration();
-                mainRuntimeClasspath = component.getRuntimeClasspathConfiguration();
+                mainCompileClasspath = mainFeature.getCompileClasspathConfiguration();
+                mainRuntimeClasspath = mainFeature.getRuntimeClasspathConfiguration();
                 testCompileClasspath = findConfiguration(defaultTestSuite.getSources().getCompileClasspathConfigurationName());
                 testRuntimeClasspath = findConfiguration(defaultTestSuite.getSources().getRuntimeClasspathConfigurationName());
             } else {
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultWarPluginConvention.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultWarPluginConvention.java
index ec55598..b08d3e0 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultWarPluginConvention.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/DefaultWarPluginConvention.java
@@ -20,6 +20,7 @@
 import org.gradle.api.plugins.WarPluginConvention;
 import org.gradle.api.reflect.HasPublicType;
 import org.gradle.api.reflect.TypeOf;
+import org.gradle.internal.deprecation.DeprecationLogger;
 
 import javax.inject.Inject;
 import java.io.File;
@@ -43,21 +44,32 @@ public TypeOf<?> getPublicType() {
 
     @Override
     public File getWebAppDir() {
+        logDeprecation();
         return project.file(webAppDirName);
     }
 
     @Override
     public String getWebAppDirName() {
+        logDeprecation();
         return webAppDirName;
     }
 
     @Override
     public void setWebAppDirName(String webAppDirName) {
+       logDeprecation();
         this.webAppDirName = webAppDirName;
     }
 
     @Override
     public Project getProject() {
+       logDeprecation();
         return project;
     }
+
+    private static void logDeprecation() {
+        DeprecationLogger.deprecateType(WarPluginConvention.class)
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "war_convention_deprecation")
+            .nagUser();
+    }
 }
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/JvmPluginsHelper.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/JvmPluginsHelper.java
index 69b083c..717aab9 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/JvmPluginsHelper.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/JvmPluginsHelper.java
@@ -26,6 +26,7 @@
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.SourceDirectorySet;
 import org.gradle.api.internal.ConventionMapping;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
 import org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact;
 import org.gradle.api.internal.plugins.DslObject;
 import org.gradle.api.internal.project.ProjectInternal;
@@ -57,7 +58,7 @@
  */
 public class JvmPluginsHelper {
 
-    /***
+    /**
      * For compatibility with <a href="https://plugins.gradle.org/plugin/io.freefair.aspectj">AspectJ Plugin</a>
      */
     @Deprecated
@@ -90,7 +91,7 @@ public static void configureAnnotationProcessorPath(final SourceSet sourceSet, S
         options.getGeneratedSourceOutputDirectory().convention(target.getLayout().getBuildDirectory().dir(annotationProcessorGeneratedSourcesChildPath));
     }
 
-    /***
+    /**
      * For compatibility with <a href="https://plugins.gradle.org/plugin/io.freefair.aspectj">AspectJ Plugin</a>
      */
     @Deprecated
@@ -109,11 +110,11 @@ public static void configureOutputDirectoryForSourceSet(final SourceSet sourceSe
         sourceDirectorySet.compiledBy(compileTask, AbstractCompile::getDestinationDirectory);
     }
 
-    public static void configureJavaDocTask(@Nullable String featureName, SourceSet sourceSet, TaskContainer tasks, @Nullable JavaPluginExtension javaPluginExtension) {
+    public static void configureJavaDocTask(String displayName, SourceSet sourceSet, TaskContainer tasks, @Nullable JavaPluginExtension javaPluginExtension) {
         String javadocTaskName = sourceSet.getJavadocTaskName();
         if (!tasks.getNames().contains(javadocTaskName)) {
             tasks.register(javadocTaskName, Javadoc.class, javadoc -> {
-                javadoc.setDescription("Generates Javadoc API documentation for the " + (featureName == null ? "main source code." : "'" + featureName + "' feature."));
+                javadoc.setDescription("Generates Javadoc API documentation for the " + displayName + ".");
                 javadoc.setGroup(JvmConstants.DOCUMENTATION_GROUP);
                 javadoc.setClasspath(sourceSet.getOutput().plus(sourceSet.getCompileClasspath()));
                 javadoc.setSource(sourceSet.getAllJava());
@@ -134,10 +135,8 @@ public static Configuration createDocumentationVariantWithArtifact(
         Object artifactSource,
         ProjectInternal project
     ) {
-        Configuration variant = project.getConfigurations().maybeCreate(variantName);
+        @SuppressWarnings("deprecation") Configuration variant = project.getConfigurations().maybeCreateWithRole(variantName, ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
         variant.setVisible(false);
-        variant.setCanBeResolved(false);
-        variant.setCanBeConsumed(true);
         variant.setDescription(docsType + " elements for " + (featureName == null ? "main" : featureName) + ".");
 
         ObjectFactory objectFactory = project.getObjects();
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/NaggingJavaPluginConvention.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/NaggingJavaPluginConvention.java
new file mode 100644
index 0000000..79277d2
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/NaggingJavaPluginConvention.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2022 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.api.plugins.internal;
+
+import groovy.lang.Closure;
+import org.gradle.api.Action;
+import org.gradle.api.JavaVersion;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.java.archives.Manifest;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.SourceSetContainer;
+import org.gradle.internal.deprecation.DeprecationLogger;
+
+import java.io.File;
+
+@org.gradle.api.NonNullApi
+public class NaggingJavaPluginConvention extends JavaPluginConvention {
+    private final JavaPluginConvention delegate;
+
+    public NaggingJavaPluginConvention(JavaPluginConvention delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Object sourceSets(Closure closure) {
+        logDeprecation();
+        return delegate.sourceSets(closure);
+    }
+
+    @Override
+    public File getDocsDir() {
+        logDeprecation();
+        return delegate.getDocsDir();
+    }
+
+    @Override
+    public File getTestResultsDir() {
+        logDeprecation();
+        return delegate.getTestResultsDir();
+    }
+
+    @Override
+    public File getTestReportDir() {
+        logDeprecation();
+        return delegate.getTestReportDir();
+    }
+
+    @Override
+    public JavaVersion getSourceCompatibility() {
+        logDeprecation();
+        return delegate.getSourceCompatibility();
+    }
+
+    @Override
+    public void setSourceCompatibility(Object value) {
+        logDeprecation();
+        delegate.setSourceCompatibility(value);
+    }
+
+    @Override
+    public void setSourceCompatibility(JavaVersion value) {
+        logDeprecation();
+        delegate.setSourceCompatibility(value);
+    }
+
+    @Override
+    public JavaVersion getTargetCompatibility() {
+        logDeprecation();
+        return delegate.getTargetCompatibility();
+    }
+
+    @Override
+    public void setTargetCompatibility(Object value) {
+        logDeprecation();
+        delegate.setTargetCompatibility(value);
+    }
+
+    @Override
+    public void setTargetCompatibility(JavaVersion value) {
+        logDeprecation();
+        delegate.setTargetCompatibility(value);
+    }
+
+    @Override
+    public Manifest manifest() {
+        logDeprecation();
+        return delegate.manifest();
+    }
+
+    @Override
+    public Manifest manifest(Closure closure) {
+        logDeprecation();
+        return delegate.manifest(closure);
+    }
+
+    @Override
+    public Manifest manifest(Action<? super Manifest> action) {
+        logDeprecation();
+        return delegate.manifest(action);
+    }
+
+    @Override
+    public String getDocsDirName() {
+        logDeprecation();
+        return delegate.getDocsDirName();
+    }
+
+    @Override
+    public void setDocsDirName(String docsDirName) {
+        logDeprecation();
+        delegate.setDocsDirName(docsDirName);
+    }
+
+    @Override
+    public String getTestResultsDirName() {
+        logDeprecation();
+        return delegate.getTestResultsDirName();
+    }
+
+    @Override
+    public void setTestResultsDirName(String testResultsDirName) {
+        logDeprecation();
+        delegate.setTestResultsDirName(testResultsDirName);
+    }
+
+    @Override
+    public String getTestReportDirName() {
+        logDeprecation();
+        return delegate.getTestReportDirName();
+    }
+
+    @Override
+    public void setTestReportDirName(String testReportDirName) {
+        logDeprecation();
+        delegate.setTestReportDirName(testReportDirName);
+    }
+
+    @Override
+    public SourceSetContainer getSourceSets() {
+        logDeprecation();
+        return delegate.getSourceSets();
+    }
+
+    @Override
+    public ProjectInternal getProject() {
+        logDeprecation();
+        return delegate.getProject();
+    }
+
+    @Override
+    public void disableAutoTargetJvm() {
+        logDeprecation();
+        delegate.disableAutoTargetJvm();
+    }
+
+    @Override
+    public boolean getAutoTargetJvmDisabled() {
+        logDeprecation();
+        return delegate.getAutoTargetJvmDisabled();
+    }
+
+    private static void logDeprecation() {
+        DeprecationLogger.deprecateType(JavaPluginConvention.class)
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "java_convention_deprecation")
+            .nagUser();
+    }
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/PluginAuthorServices.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/PluginAuthorServices.java
index 4b9d7b0..d223b56 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/PluginAuthorServices.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/internal/PluginAuthorServices.java
@@ -15,8 +15,6 @@
  */
 package org.gradle.api.plugins.internal;
 
-import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.component.SoftwareComponentContainer;
 import org.gradle.api.component.SoftwareComponentFactory;
 import org.gradle.api.internal.tasks.DefaultSourceSetContainer;
 import org.gradle.api.model.ObjectFactory;
@@ -24,7 +22,6 @@
 import org.gradle.api.plugins.jvm.internal.JvmPluginServices;
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.tasks.SourceSetContainer;
-import org.gradle.api.tasks.TaskContainer;
 import org.gradle.internal.Describables;
 import org.gradle.internal.instantiation.InstanceGenerator;
 import org.gradle.internal.instantiation.InstantiatorFactory;
@@ -53,21 +50,16 @@ SoftwareComponentFactory createSoftwareComponentFactory(Instantiator instantiato
     }
 
     private static class ProjectScopeServices {
-        JvmPluginServices createJvmPluginServices(ConfigurationContainer configurations,
-                                                  ObjectFactory objectFactory,
+        JvmPluginServices createJvmPluginServices(ObjectFactory objectFactory,
                                                   ProviderFactory providerFactory,
-                                                  TaskContainer tasks,
-                                                  SoftwareComponentContainer components,
                                                   InstantiatorFactory instantiatorFactory) {
             InstanceGenerator instantiator = instantiatorFactory.decorateScheme().instantiator();
             return instantiator.newInstanceWithDisplayName(DefaultJvmPluginServices.class,
                 Describables.of("JVM Plugin Services"),
-                configurations,
                 objectFactory,
                 providerFactory,
-                tasks,
-                components,
-                instantiator);
+                instantiator
+            );
         }
 
         SourceSetContainer createSourceSetContainer(ObjectFactory objectFactory) {
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/AbstractConfigurationBuilder.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/AbstractConfigurationBuilder.java
deleted file mode 100644
index afd31f7..0000000
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/AbstractConfigurationBuilder.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2020 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.api.plugins.jvm.internal;
-
-import com.google.common.collect.Lists;
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
-import org.gradle.api.provider.Provider;
-import org.gradle.internal.Cast;
-
-import javax.annotation.Nullable;
-import java.util.Collections;
-import java.util.List;
-
-abstract class AbstractConfigurationBuilder<T extends AbstractConfigurationBuilder<T>> {
-    final String name;
-    final JvmPluginServices jvmEcosystemUtilities;
-    final ConfigurationContainerInternal configurations;
-    String description;
-    List<Object> extendsFrom;
-    Action<? super JvmEcosystemAttributesDetails> attributesRefiner;
-
-    public AbstractConfigurationBuilder(String name,
-                                        JvmPluginServices jvmEcosystemUtilities,
-                                        ConfigurationContainerInternal configurations) {
-        this.name = name;
-        this.jvmEcosystemUtilities = jvmEcosystemUtilities;
-        this.configurations = configurations;
-    }
-
-    abstract Configuration build();
-
-    @Nullable
-    protected Configuration[] buildExtendsFrom() {
-        if (extendsFrom == null) {
-            return null;
-        }
-        Configuration[] out = new Configuration[extendsFrom.size()];
-        int i=0;
-        for (Object obj : extendsFrom) {
-            if (obj instanceof Configuration) {
-                out[i] = (Configuration) obj;
-            } else if (obj instanceof Provider) {
-                out[i] = Cast.<Provider<Configuration>>uncheckedCast(obj).get();
-            } else {
-                throw new IllegalStateException("Unexpected configuration provider type: " + obj);
-            }
-            i++;
-        }
-        return out;
-    }
-
-    public T withDescription(String description) {
-        this.description = description;
-        return Cast.uncheckedCast(this);
-    }
-
-    public T extendsFrom(Configuration... parentConfigurations) {
-        if (extendsFrom == null) {
-            extendsFrom = Lists.newArrayListWithExpectedSize(parentConfigurations.length);
-        }
-        Collections.addAll(extendsFrom, parentConfigurations);
-        return Cast.uncheckedCast(this);
-    }
-
-    public final T extendsFrom(List<Provider<Configuration>> parentConfigurations) {
-        if (extendsFrom == null) {
-            extendsFrom = Lists.newArrayListWithExpectedSize(parentConfigurations.size());
-        }
-        extendsFrom.addAll(parentConfigurations);
-        return Cast.uncheckedCast(this);
-    }
-
-    protected T attributes(Action<? super JvmEcosystemAttributesDetails> refiner) {
-        this.attributesRefiner = refiner;
-        return Cast.uncheckedCast(this);
-    }
-}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmFeature.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmFeature.java
new file mode 100644
index 0000000..17a3ece
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmFeature.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2020 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.api.plugins.jvm.internal;
+
+import org.apache.commons.lang.StringUtils;
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.ConfigurationPublications;
+import org.gradle.api.artifacts.PublishArtifact;
+import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
+import org.gradle.api.capabilities.CapabilitiesMetadata;
+import org.gradle.api.capabilities.Capability;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRole;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
+import org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.tasks.DefaultSourceSet;
+import org.gradle.api.internal.tasks.JvmConstants;
+import org.gradle.api.plugins.BasePlugin;
+import org.gradle.api.plugins.JavaPluginExtension;
+import org.gradle.api.plugins.internal.JvmPluginsHelper;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.api.tasks.TaskProvider;
+import org.gradle.api.tasks.bundling.Jar;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.internal.component.external.model.ImmutableCapabilities;
+import org.gradle.util.internal.TextUtil;
+
+import java.util.List;
+
+import static org.gradle.api.attributes.DocsType.JAVADOC;
+import static org.gradle.api.attributes.DocsType.SOURCES;
+
+/**
+ * Represents a generic "Java feature", using the specified source set and it's corresponding
+ * configurations, compile task, and jar task. This feature creates a jar task and javadoc task, and
+ * can optionally also create consumable javadoc and sources jar variants.
+ *
+ * <p>This can be used to create production libraries, applications, test suites, test fixtures,
+ * or any other consumable JVM feature.</p>
+ *
+ * <p>This feature can conditionally be configured to instead "extend" the production code. In that case, this
+ * feature creates additional dependency configurations which live adjacent to the main source set's buckets,
+ * which allow users to declare optional dependencies that the production code will compile and test against.
+ * These extra dependencies are not published as part of the production variants, but as separate apiElements
+ * and runtimeElements variants as defined by this feature. Then, users can declare a dependency on this
+ * feature to get access to the optional dependencies.</p>
+ *
+ * <p>This "extending" functionality is fragile, in that it allows the production code to be compiled and
+ * tested against dependencies which will not necessarily be present at runtime. For this reason, we are
+ * planning to deprecate the "extending" functionality. For more information, see {@link #doExtendProductionCode}.</p>
+ *
+ * <p>For backwards compatibility reasons, when this feature is operating in the "extending" mode,
+ * this feature is able to operate without the presence of the main feature, as long as the user
+ * explicitly configures the project by manually creating a main and test source set themselves.
+ * In that case, this feature will additionally create the jar and javadoc tasks which the main
+ * source set would normally create. Additionally, this extension feature is able to create the
+ * sources and javadoc variants that the main feature would also conditionally create.</p>
+ */
+public class DefaultJvmFeature implements JvmFeatureInternal {
+
+    private final String name;
+    private final SourceSet sourceSet;
+    private final List<Capability> capabilities;
+    private final boolean extendProductionCode;
+
+    // Services
+    private final ProjectInternal project;
+    private final JvmPluginServices jvmPluginServices;
+
+    // Tasks
+    private final TaskProvider<Jar> jar;
+    private final TaskProvider<JavaCompile> compileJava;
+
+    // Dependency configurations
+    private final Configuration implementation;
+    private final Configuration runtimeOnly;
+    private final Configuration compileOnly;
+
+    // Configurable dependency configurations
+    private Configuration compileOnlyApi;
+    private Configuration api;
+
+    // Resolvable configurations
+    private final Configuration runtimeClasspath;
+    private final Configuration compileClasspath;
+
+    // Outgoing variants
+    private final Configuration apiElements;
+    private final Configuration runtimeElements;
+
+    // Configurable outgoing variants
+    private Configuration javadocElements;
+    private Configuration sourcesElements;
+
+    public DefaultJvmFeature(
+        String name,
+        SourceSet sourceSet,
+        List<Capability> capabilities,
+        ProjectInternal project,
+        // The elements configurations' roles should always be consumable only, but
+        // some users of this class are still migrating towards that. In 9.0, we can remove this
+        // parameter and hard-code the elements configurations' roles to consumable only.
+        ConfigurationRole elementsConfigurationRole,
+        boolean extendProductionCode
+    ) {
+        this.name = name;
+        this.sourceSet = sourceSet;
+        this.capabilities = capabilities;
+        this.project = project;
+        this.extendProductionCode = extendProductionCode;
+
+        // TODO: Deprecate allowing user to extend main feature.
+        if (extendProductionCode && !SourceSet.isMain(sourceSet)) {
+            throw new GradleException("Cannot extend main feature if source set is not also main.");
+        }
+
+        this.jvmPluginServices = project.getServices().get(JvmPluginServices.class);
+        RoleBasedConfigurationContainerInternal configurations = project.getConfigurations();
+        TaskContainer tasks = project.getTasks();
+
+        this.compileJava = tasks.named(sourceSet.getCompileJavaTaskName(), JavaCompile.class);
+        this.jar = registerOrGetJarTask(sourceSet, tasks);
+
+        // If extendProductionCode=false, the source set has already created these configurations.
+        // If extendProductionCode=true, then we create new buckets and later update the main and
+        // test source sets to extend from these buckets.
+        this.implementation = bucket("Implementation", JvmConstants.IMPLEMENTATION_CONFIGURATION_NAME);
+        this.compileOnly = bucket("Compile-only", JvmConstants.COMPILE_ONLY_CONFIGURATION_NAME);
+        this.runtimeOnly = bucket("Runtime-only", JvmConstants.RUNTIME_ONLY_CONFIGURATION_NAME);
+
+        this.runtimeClasspath = configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName());
+        this.compileClasspath = configurations.getByName(sourceSet.getCompileClasspathConfigurationName());
+
+        PublishArtifact jarArtifact = new LazyPublishArtifact(jar, project.getFileResolver(), project.getTaskDependencyFactory());
+        this.apiElements = createApiElements(configurations, jarArtifact, compileJava, elementsConfigurationRole);
+        this.runtimeElements = createRuntimeElements(configurations, jarArtifact, compileJava, elementsConfigurationRole);
+
+        if (extendProductionCode) {
+            doExtendProductionCode();
+        }
+
+        JavaPluginExtension javaPluginExtension = project.getExtensions().findByType(JavaPluginExtension.class);
+        JvmPluginsHelper.configureJavaDocTask("'" + name + "' feature", sourceSet, tasks, javaPluginExtension);
+    }
+
+    void doExtendProductionCode() {
+        // This method is one of the primary reasons that we want to deprecate the "extending" behavior. It updates
+        // the main source set and test source set to "extend" this feature. That means any dependencies declared on
+        // this feature's dependency configurations will be available locally, during compilation and runtime, to the main
+        // production code and default test suite. However, when publishing the production code, these dependencies will
+        // not be included in its consumable variants. Therefore, the main code is compiled _and tested_ against
+        // dependencies which will not necessarily be available at runtime when it is consumed from other projects
+        // or in its published form.
+        //
+        // This leads to a case where, in order for the production code to not throw NoClassDefFoundErrors during runtime,
+        // it must detect the presence of the dependencies added by this feature, and then conditionally enable and disable
+        // certain optional behavior. We do not want to promote this pattern.
+        //
+        // A much safer pattern would be to create normal features as opposed to an "extending" feature. Then, the normal
+        // feature would have a project dependency on the main feature. It would provide an extra jar with any additional code,
+        // and also bring along any extra dependencies that code requires. The main feature would then be able to detect the
+        // presence of the feature through some {@code ServiceLoader} mechanism, as opposed to detecting the existence of
+        // dependencies directly.
+        //
+        // This pattern is also more flexible than the "extending" pattern in that it allows features to extend arbitrary
+        // features as opposed to just the main feature.
+
+        ConfigurationContainer configurations = project.getConfigurations();
+        SourceSet mainSourceSet = project.getExtensions().findByType(JavaPluginExtension.class)
+            .getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
+
+        // Update the main feature's source set to extend our "extension" feature's buckets.
+        configurations.getByName(mainSourceSet.getCompileClasspathConfigurationName()).extendsFrom(implementation, compileOnly);
+        configurations.getByName(mainSourceSet.getRuntimeClasspathConfigurationName()).extendsFrom(implementation, runtimeOnly);
+        // Update the default test suite's source set to extend our "extension" feature's buckets.
+        configurations.getByName(JvmConstants.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(implementation);
+        configurations.getByName(JvmConstants.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(implementation, runtimeOnly);
+    }
+
+    /**
+     * Hack to allow us to create configurations for normal and "extending" features. This should go away.
+     */
+    private String getConfigurationName(String suffix) {
+        if (extendProductionCode) {
+            return name + StringUtils.capitalize(suffix);
+        } else {
+            return ((DefaultSourceSet) sourceSet).configurationNameOf(suffix);
+        }
+    }
+
+    private static void addJarArtifactToConfiguration(Configuration configuration, PublishArtifact jarArtifact) {
+        ConfigurationPublications publications = configuration.getOutgoing();
+
+        // Configure an implicit variant
+        publications.getArtifacts().add(jarArtifact);
+        publications.getAttributes().attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE);
+    }
+
+    private Configuration createApiElements(
+        RoleBasedConfigurationContainerInternal configurations,
+        PublishArtifact jarArtifact,
+        TaskProvider<JavaCompile> compileJava,
+        ConfigurationRole elementsRole
+    ) {
+        String configName = getConfigurationName(JvmConstants.API_ELEMENTS_CONFIGURATION_NAME);
+        Configuration apiElements = configurations.maybeCreateWithRole(configName, elementsRole, false, false);
+
+        apiElements.setVisible(false);
+        jvmPluginServices.useDefaultTargetPlatformInference(apiElements, compileJava);
+        jvmPluginServices.configureAsApiElements(apiElements);
+        capabilities.forEach(apiElements.getOutgoing()::capability);
+        apiElements.setDescription("API elements for the '" + name + "' feature.");
+
+        // Configure variants
+        addJarArtifactToConfiguration(apiElements, jarArtifact);
+
+        return apiElements;
+    }
+
+    private Configuration createRuntimeElements(
+        RoleBasedConfigurationContainerInternal configurations,
+        PublishArtifact jarArtifact,
+        TaskProvider<JavaCompile> compileJava,
+        ConfigurationRole elementsRole
+    ) {
+        String configName = getConfigurationName(JvmConstants.RUNTIME_ELEMENTS_CONFIGURATION_NAME);
+        Configuration runtimeElements = configurations.maybeCreateWithRole(configName, elementsRole, false, false);
+
+        runtimeElements.setVisible(false);
+        jvmPluginServices.useDefaultTargetPlatformInference(runtimeElements, compileJava);
+        jvmPluginServices.configureAsRuntimeElements(runtimeElements);
+        capabilities.forEach(runtimeElements.getOutgoing()::capability);
+        runtimeElements.setDescription("Runtime elements for the '" + name + "' feature.");
+
+        runtimeElements.extendsFrom(implementation, runtimeOnly);
+
+        // Configure variants
+        addJarArtifactToConfiguration(runtimeElements, jarArtifact);
+        jvmPluginServices.configureClassesDirectoryVariant(runtimeElements, sourceSet);
+        jvmPluginServices.configureResourcesDirectoryVariant(runtimeElements, sourceSet);
+
+        return runtimeElements;
+    }
+
+    @Override
+    public void withApi() {
+        this.api = bucket("API", JvmConstants.API_CONFIGURATION_NAME);
+        this.compileOnlyApi = bucket("Compile-only API", JvmConstants.COMPILE_ONLY_API_CONFIGURATION_NAME);
+
+        this.apiElements.extendsFrom(api, compileOnlyApi);
+        this.implementation.extendsFrom(api);
+        this.compileOnly.extendsFrom(compileOnlyApi);
+
+        // TODO: Why do we not always do this? Why only when we have an API?
+        jvmPluginServices.configureClassesDirectoryVariant(apiElements, sourceSet);
+
+        if (extendProductionCode) {
+            project.getConfigurations().getByName(JvmConstants.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(compileOnlyApi);
+        }
+    }
+
+    @Override
+    public void withJavadocJar() {
+        if (javadocElements != null) {
+            return;
+        }
+        this.javadocElements = JvmPluginsHelper.createDocumentationVariantWithArtifact(
+            sourceSet.getJavadocElementsConfigurationName(),
+            SourceSet.isMain(sourceSet) ? null : name,
+            JAVADOC,
+            capabilities,
+            sourceSet.getJavadocJarTaskName(),
+            project.getTasks().named(sourceSet.getJavadocTaskName()),
+            project
+        );
+    }
+
+    @Override
+    public void withSourcesJar() {
+        if (sourcesElements != null) {
+            return;
+        }
+        this.sourcesElements = JvmPluginsHelper.createDocumentationVariantWithArtifact(
+            sourceSet.getSourcesElementsConfigurationName(),
+            SourceSet.isMain(sourceSet) ? null : name,
+            SOURCES,
+            capabilities,
+            sourceSet.getSourcesJarTaskName(),
+            sourceSet.getAllSource(),
+            project
+        );
+    }
+
+    private Configuration bucket(String kind, String suffix) {
+        String configName = getConfigurationName(suffix);
+        Configuration configuration = project.getConfigurations().maybeCreateWithRole(configName, ConfigurationRoles.BUCKET, false, false);
+        configuration.setDescription(kind + " dependencies for the '" + name + "' feature.");
+        configuration.setVisible(false);
+        return configuration;
+    }
+
+    private TaskProvider<Jar> registerOrGetJarTask(SourceSet sourceSet, TaskContainer tasks) {
+        String jarTaskName = sourceSet.getJarTaskName();
+        if (!tasks.getNames().contains(jarTaskName)) {
+            return tasks.register(jarTaskName, Jar.class, jar -> {
+                jar.setDescription("Assembles a jar archive containing the classes of the '" + name + "' feature.");
+                jar.setGroup(BasePlugin.BUILD_GROUP);
+                jar.from(sourceSet.getOutput());
+                if (!capabilities.isEmpty()) {
+                    jar.getArchiveClassifier().set(TextUtil.camelToKebabCase(name));
+                }
+            });
+        }
+        return tasks.named(jarTaskName, Jar.class);
+    }
+
+    @Override
+    public CapabilitiesMetadata getCapabilities() {
+        return ImmutableCapabilities.of(capabilities);
+    }
+
+    @Override
+    public TaskProvider<Jar> getJarTask() {
+        return jar;
+    }
+
+    @Override
+    public TaskProvider<JavaCompile> getCompileJavaTask() {
+        return compileJava;
+    }
+
+    @Override
+    public SourceSet getSourceSet() {
+        return sourceSet;
+    }
+
+    @Override
+    public Configuration getImplementationConfiguration() {
+        return implementation;
+    }
+
+    @Override
+    public Configuration getRuntimeOnlyConfiguration() {
+        return runtimeOnly;
+    }
+
+    @Override
+    public Configuration getCompileOnlyConfiguration() {
+        return compileOnly;
+    }
+
+    @Override
+    public Configuration getApiConfiguration() {
+        return api;
+    }
+
+    @Override
+    public Configuration getCompileOnlyApiConfiguration() {
+        return compileOnlyApi;
+    }
+
+    @Override
+    public Configuration getRuntimeClasspathConfiguration() {
+        return runtimeClasspath;
+    }
+
+    @Override
+    public Configuration getCompileClasspathConfiguration() {
+        return compileClasspath;
+    }
+
+    @Override
+    public Configuration getApiElementsConfiguration() {
+        return apiElements;
+    }
+
+    @Override
+    public Configuration getRuntimeElementsConfiguration() {
+        return runtimeElements;
+    }
+
+    @Override
+    public Configuration getJavadocElementsConfiguration() {
+        return javadocElements;
+    }
+
+    @Override
+    public Configuration getSourcesElementsConfiguration() {
+        return sourcesElements;
+    }
+
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServices.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServices.java
index eeb9384..89fbab5 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServices.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServices.java
@@ -15,29 +15,18 @@
  */
 package org.gradle.api.plugins.jvm.internal;
 
-import com.google.common.collect.Lists;
 import org.gradle.api.Action;
-import org.gradle.api.InvalidUserDataException;
 import org.gradle.api.JavaVersion;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ConfigurationPublications;
 import org.gradle.api.artifacts.ConfigurationVariant;
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
-import org.gradle.api.attributes.Category;
 import org.gradle.api.attributes.HasConfigurableAttributes;
 import org.gradle.api.attributes.LibraryElements;
-import org.gradle.api.capabilities.Capability;
-import org.gradle.api.component.AdhocComponentWithVariants;
-import org.gradle.api.component.ConfigurationVariantDetails;
-import org.gradle.api.component.SoftwareComponent;
-import org.gradle.api.component.SoftwareComponentContainer;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.artifacts.ConfigurationVariantInternal;
 import org.gradle.api.internal.artifacts.JavaEcosystemSupport;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationContainerInternal;
 import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.artifacts.publish.AbstractPublishArtifact;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
 import org.gradle.api.internal.project.ProjectInternal;
@@ -49,14 +38,9 @@
 import org.gradle.api.provider.Provider;
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.tasks.SourceSet;
-import org.gradle.api.tasks.SourceSetContainer;
-import org.gradle.api.tasks.TaskContainer;
 import org.gradle.api.tasks.TaskProvider;
 import org.gradle.api.tasks.compile.AbstractCompile;
-import org.gradle.api.tasks.compile.JavaCompile;
 import org.gradle.internal.Cast;
-import org.gradle.internal.component.external.model.DefaultImmutableCapability;
-import org.gradle.internal.component.external.model.ProjectDerivedCapability;
 import org.gradle.internal.instantiation.InstanceGenerator;
 
 import javax.annotation.Nullable;
@@ -71,46 +55,38 @@
 import java.util.stream.Collectors;
 
 public class DefaultJvmPluginServices implements JvmPluginServices {
-    private final ConfigurationContainer configurations;
     private final ObjectFactory objectFactory;
     private final ProviderFactory providerFactory;
-    private final TaskContainer tasks;
-    private final SoftwareComponentContainer components;
     private final InstanceGenerator instanceGenerator;
     private final Map<ConfigurationInternal, Set<TaskProvider<?>>> configurationToCompileTasks; // ? is really AbstractCompile & HasCompileOptions
 
-    private SourceSetContainer sourceSets;
     private ProjectInternal project; // would be great to avoid this but for lazy capabilities it's hard to avoid!
 
     @Inject
-    public DefaultJvmPluginServices(ConfigurationContainer configurations,
-                                    ObjectFactory objectFactory,
+    public DefaultJvmPluginServices(ObjectFactory objectFactory,
                                     ProviderFactory providerFactory,
-                                    TaskContainer tasks,
-                                    SoftwareComponentContainer components,
                                     InstanceGenerator instanceGenerator) {
-        this.configurations = configurations;
         this.objectFactory = objectFactory;
         this.providerFactory = providerFactory;
-        this.tasks = tasks;
-        this.components = components;
         this.instanceGenerator = instanceGenerator;
         configurationToCompileTasks = new HashMap<>(5);
     }
 
     @Override
-    public void inject(ProjectInternal project, SourceSetContainer sourceSets) {
+    public void inject(ProjectInternal project) {
         this.project = project;
-        this.sourceSets = sourceSets;
     }
 
     @Override
-    public <T> void configureAsCompileClasspath(HasConfigurableAttributes<T> configuration) {
-        configureAttributes(configuration, details -> details.library().apiUsage().withExternalDependencies().preferStandardJVM());
+    public void configureAsCompileClasspath(HasConfigurableAttributes<?> configuration) {
+        configureAttributes(
+            configuration,
+            details -> details.library().apiUsage().withExternalDependencies().preferStandardJVM()
+        );
     }
 
     @Override
-    public <T> void configureAsRuntimeClasspath(HasConfigurableAttributes<T> configuration) {
+    public void configureAsRuntimeClasspath(HasConfigurableAttributes<?> configuration) {
         configureAttributes(
             configuration,
             details -> details.library().runtimeUsage().asJar().withExternalDependencies().preferStandardJVM()
@@ -118,6 +94,22 @@ public <T> void configureAsRuntimeClasspath(HasConfigurableAttributes<T> configu
     }
 
     @Override
+    public void configureAsApiElements(HasConfigurableAttributes<?> configuration) {
+        configureAttributes(
+            configuration,
+            details -> details.library().apiUsage().asJar().withExternalDependencies()
+        );
+    }
+
+    @Override
+    public void configureAsRuntimeElements(HasConfigurableAttributes<?> configuration) {
+        configureAttributes(
+            configuration,
+            details -> details.library().runtimeUsage().asJar().withExternalDependencies()
+        );
+    }
+
+    @Override
     public <T> void configureAttributes(HasConfigurableAttributes<T> configurable, Action<? super JvmEcosystemAttributesDetails> configuration) {
         AttributeContainerInternal attributes = (AttributeContainerInternal) configurable.getAttributes();
         DefaultJvmEcosystemAttributesDetails details = instanceGenerator.newInstance(DefaultJvmEcosystemAttributesDetails.class, objectFactory, attributes);
@@ -235,33 +227,6 @@ private <COMPILE extends AbstractCompile & HasCompileOptions> Action<Configurati
         };
     }
 
-    @Override
-    public Configuration createOutgoingElements(String name, Action<? super OutgoingElementsBuilder> configuration) {
-        DefaultElementsConfigurationBuilder builder = instanceGenerator.newInstance(DefaultElementsConfigurationBuilder.class,
-            name,
-            this,
-            configurations,
-            components,
-            tasks);
-        configuration.execute(builder);
-        return builder.build();
-    }
-
-    @Override
-    public void createJvmVariant(String name, Action<? super JvmVariantBuilder> configuration) {
-        DefaultJvmVariantBuilder builder = instanceGenerator.newInstance(DefaultJvmVariantBuilder.class,
-            name,
-            new ProjectDerivedCapability(project, name),
-            this,
-            sourceSets,
-            configurations,
-            tasks,
-            components,
-            project);
-        configuration.execute(builder);
-        builder.build();
-    }
-
     private static int getReleaseOption(List<String> compilerArgs) {
         int flagIndex = compilerArgs.indexOf("--release");
         if (flagIndex != -1 && flagIndex + 1 < compilerArgs.size()) {
@@ -270,159 +235,6 @@ private static int getReleaseOption(List<String> compilerArgs) {
         return 0;
     }
 
-    public static class DefaultElementsConfigurationBuilder extends AbstractConfigurationBuilder<DefaultElementsConfigurationBuilder> implements OutgoingElementsBuilder {
-        final SoftwareComponentContainer components;
-        private final TaskContainer tasks;
-        boolean api;
-        SourceSet sourceSet;
-        List<Object> artifactProducers;
-        List<Capability> capabilities;
-        boolean classDirectory;
-        private boolean published;
-
-        @Inject
-        public DefaultElementsConfigurationBuilder(String name, JvmPluginServices jvmEcosystemUtilities, ConfigurationContainerInternal configurations, SoftwareComponentContainer components, TaskContainer tasks) {
-            super(name, jvmEcosystemUtilities, (RoleBasedConfigurationContainerInternal) configurations);
-            this.components = components;
-            this.tasks = tasks;
-        }
-
-        @Override
-        Configuration build() {
-            Configuration cnf = configurations.maybeCreate(name);
-            if (description != null) {
-                cnf.setDescription(description);
-            }
-            cnf.setVisible(false);
-            cnf.setCanBeConsumed(true);
-            cnf.setCanBeResolved(false);
-            Configuration[] extendsFrom = buildExtendsFrom();
-            if (extendsFrom != null) {
-                cnf.extendsFrom(extendsFrom);
-            }
-            jvmEcosystemUtilities.configureAttributes(cnf, details -> {
-                    details.library()
-                        .withExternalDependencies();
-                    if (api) {
-                        details.apiUsage();
-                    } else {
-                        details.runtimeUsage();
-                    }
-                    if (attributesRefiner != null) {
-                        attributesRefiner.execute(details);
-                    }
-                    if (Category.LIBRARY.equals(cnf.getAttributes().getAttribute(Category.CATEGORY_ATTRIBUTE).getName())) {
-                        if (!cnf.getAttributes().contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE)) {
-                            details.asJar();
-                        }
-                    }
-                }
-            );
-            if (sourceSet != null) {
-                jvmEcosystemUtilities.useDefaultTargetPlatformInference(cnf, tasks.named(sourceSet.getCompileJavaTaskName(), JavaCompile.class));
-            }
-            ConfigurationPublications outgoing = cnf.getOutgoing();
-            if (artifactProducers != null) {
-                for (Object provider : artifactProducers) {
-                    outgoing.artifact(provider);
-                }
-            }
-            if (capabilities != null) {
-                for (Capability capability : capabilities) {
-                    outgoing.capability(capability);
-                }
-            }
-            if (classDirectory) {
-                if (!api) {
-                    throw new IllegalStateException("Cannot add a class directory variant for a runtime outgoing variant");
-                }
-                if (sourceSet == null) {
-                    throw new IllegalStateException("Cannot add a class directory variant without specifying the source set");
-                }
-                jvmEcosystemUtilities.configureClassesDirectoryVariant(cnf, sourceSet);
-            }
-            if (published) {
-                AdhocComponentWithVariants component = findJavaComponent();
-                if (component != null) {
-                    component.addVariantsFromConfiguration(cnf, ConfigurationVariantDetails::mapToOptional);
-                }
-            }
-            return cnf;
-        }
-
-        @Override
-        public OutgoingElementsBuilder providesApi() {
-            this.api = true;
-            return this;
-        }
-
-        @Override
-        public OutgoingElementsBuilder providesRuntime() {
-            this.api = false;
-            return this;
-        }
-
-        @Override
-        public OutgoingElementsBuilder fromSourceSet(SourceSet sourceSet) {
-            this.sourceSet = sourceSet;
-            return this;
-        }
-
-        @Override
-        public OutgoingElementsBuilder artifact(Object producer) {
-            if (artifactProducers == null) {
-                artifactProducers = Lists.newArrayList();
-            }
-            artifactProducers.add(producer);
-            return this;
-        }
-
-        @Override
-        public OutgoingElementsBuilder providesAttributes(Action<? super JvmEcosystemAttributesDetails> refiner) {
-            return attributes(refiner);
-        }
-
-        @Override
-        public OutgoingElementsBuilder withCapabilities(List<Capability> capabilities) {
-            this.capabilities = capabilities;
-            return this;
-        }
-
-        @Override
-        public OutgoingElementsBuilder capability(String group, String name, String version) {
-            if (capabilities == null) {
-                capabilities = Lists.newArrayList();
-            }
-            DefaultImmutableCapability capability = new DefaultImmutableCapability(group, name, version);
-            if (capability.getVersion() == null) {
-                throw new InvalidUserDataException("Capabilities declared on outgoing variants must have a version");
-            }
-            capabilities.add(capability);
-            return this;
-        }
-
-        @Override
-        public OutgoingElementsBuilder withClassDirectoryVariant() {
-            this.classDirectory = true;
-            return this;
-        }
-
-        @Override
-        public OutgoingElementsBuilder published() {
-            this.published = true;
-            return this;
-        }
-
-        @Nullable
-        public AdhocComponentWithVariants findJavaComponent() {
-            SoftwareComponent component = components.findByName("java");
-            if (component instanceof AdhocComponentWithVariants) {
-                return (AdhocComponentWithVariants) component;
-            }
-            return null;
-        }
-    }
-
     /**
      * A custom artifact type which allows the getFile call to be done lazily only when the
      * artifact is actually needed.
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmTestSuite.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmTestSuite.java
index 6b56ac2..da3aaa0 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmTestSuite.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmTestSuite.java
@@ -20,6 +20,7 @@
 import groovy.lang.GroovySystem;
 import org.gradle.api.Action;
 import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer;
+import org.gradle.api.Transformer;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.ExternalModuleDependency;
@@ -44,6 +45,7 @@
 import org.gradle.api.tasks.SourceSetContainer;
 import org.gradle.api.tasks.TaskDependency;
 import org.gradle.api.tasks.testing.Test;
+import org.gradle.internal.Pair;
 import org.gradle.util.internal.VersionNumber;
 
 import javax.inject.Inject;
@@ -67,7 +69,7 @@ public enum TestingFramework {
                 // spock-core references junit-jupiter's BOM, which in turn specifies the platform version
                 "org.junit.platform:junit-platform-launcher"
         )),
-        KOTLIN_TEST("org.jetbrains.kotlin", "kotlin-test-junit5", "1.8.10", Collections.singletonList(
+        KOTLIN_TEST("org.jetbrains.kotlin", "kotlin-test-junit5", "1.8.21", Collections.singletonList(
                 // kotlin-test-junit5 depends on junit-jupiter, which in turn specifies the platform version
                 "org.junit.platform:junit-platform-launcher"
         )),
@@ -138,6 +140,25 @@ public List<String> getRuntimeOnlyDependencies() {
         }
     }
 
+    private static class CachingTestFrameworkTransformer implements Transformer<TestFramework, VersionedTestingFramework> {
+        private final Transformer<TestFramework, TestingFramework> mapToTestFramework;
+        // This is the current mapping from TestingFramework to TestFramework (i.e. right -> left)
+        private Pair<TestFramework, TestingFramework> currentMapping;
+
+        public CachingTestFrameworkTransformer(Transformer<TestFramework, TestingFramework> mapToTestFramework) {
+            this.mapToTestFramework = mapToTestFramework;
+        }
+
+        @Override
+        public TestFramework transform(VersionedTestingFramework vtf) {
+            if (currentMapping == null || currentMapping.right() != vtf.getFramework()) {
+                currentMapping = Pair.of(mapToTestFramework.transform(vtf.getFramework()), vtf.getFramework());
+            }
+
+            return currentMapping.left();
+        }
+    }
+
     private final ExtensiblePolymorphicDomainObjectContainer<JvmTestSuiteTarget> targets;
     private final SourceSet sourceSet;
     private final String name;
@@ -172,7 +193,6 @@ public DefaultJvmTestSuite(String name, SourceSetContainer sourceSets, Configura
             // and add dependencies automatically.
             getTestSuiteTestingFramework().convention(new VersionedTestingFramework(TestingFramework.JUNIT_JUPITER, TestingFramework.JUNIT_JUPITER.getDefaultVersion()));
         }
-        getTestSuiteTestingFramework().finalizeValueOnRead(); // The framework set on the SUITE is finalized upon read, even for the default suite
 
         addDefaultTestTarget();
 
@@ -196,34 +216,31 @@ public DefaultJvmTestSuite(String name, SourceSetContainer sourceSets, Configura
     }
 
     private void initializeTestFramework(String name, Test task) {
-        Provider<TestFramework> mapTestingFrameworkToTestFramework = getTestSuiteTestingFramework().map(vtf -> {
-            switch (vtf.getFramework()) {
-                case JUNIT4:
-                    return new JUnitTestFramework(task, (DefaultTestFilter) task.getFilter(), false);
-                case KOTLIN_TEST: // fall-through
-                case JUNIT_JUPITER: // fall-through
-                case SPOCK:
-                    return new JUnitPlatformTestFramework((DefaultTestFilter) task.getFilter(), false);
-                case TESTNG:
-                    return new TestNGTestFramework(task, (DefaultTestFilter) task.getFilter(), getObjectFactory());
-                default:
-                    throw new IllegalStateException("do not know how to handle " + vtf);
-            }
-        });
+        Provider<TestFramework> mapTestingFrameworkToTestFramework = getTestSuiteTestingFramework().map(
+            new CachingTestFrameworkTransformer(framework -> {
+                switch (framework) {
+                    case JUNIT4:
+                        return new JUnitTestFramework(task, (DefaultTestFilter) task.getFilter(), false);
+                    case KOTLIN_TEST: // fall-through
+                    case JUNIT_JUPITER: // fall-through
+                    case SPOCK:
+                        return new JUnitPlatformTestFramework((DefaultTestFilter) task.getFilter(), false);
+                    case TESTNG:
+                        return new TestNGTestFramework(task, (DefaultTestFilter) task.getFilter(), getObjectFactory());
+                    default:
+                        throw new IllegalStateException("do not know how to handle " + framework);
+                }
+            })
+        );
 
         if (name.equals(JvmTestSuitePlugin.DEFAULT_TEST_SUITE_NAME)) {
             // In order to maintain compatibility for the default test suite, we need to load JUnit4 from the Gradle distribution
             // instead of including it in testImplementation.
-            task.getTestFrameworkProperty().convention(mapTestingFrameworkToTestFramework.orElse(getProviderFactory().provider(() -> new JUnitTestFramework(task, (DefaultTestFilter) task.getFilter(), true))));
-            // We can't disallow changes to the test framework yet because we need to allow the test task to be configured without the test suite
-            task.getTestFrameworkProperty().finalizeValueOnRead();
+            TestFramework defaultFramework = new JUnitTestFramework(task, (DefaultTestFilter) task.getFilter(), true);
+            task.getTestFrameworkProperty().convention(mapTestingFrameworkToTestFramework.orElse(getProviderFactory().provider(() -> defaultFramework)));
         } else {
             // The Test task's testing framework is derived from the test suite's testing framework
             task.getTestFrameworkProperty().convention(mapTestingFrameworkToTestFramework);
-            // The Test task cannot override the testing framework chosen by the test suite
-            task.getTestFrameworkProperty().disallowChanges();
-            // The Test task's testing framework is locked in as soon as its needed
-            task.getTestFrameworkProperty().finalizeValueOnRead();
         }
     }
 
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmVariantBuilder.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmVariantBuilder.java
deleted file mode 100644
index b634e61..0000000
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmVariantBuilder.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright 2020 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.api.plugins.jvm.internal;
-
-import com.google.common.collect.Lists;
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Task;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.capabilities.Capability;
-import org.gradle.api.component.AdhocComponentWithVariants;
-import org.gradle.api.component.SoftwareComponent;
-import org.gradle.api.component.SoftwareComponentContainer;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles;
-import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
-import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.internal.tasks.JvmConstants;
-import org.gradle.api.plugins.BasePlugin;
-import org.gradle.api.plugins.JavaPluginExtension;
-import org.gradle.api.plugins.internal.JavaConfigurationVariantMapping;
-import org.gradle.api.plugins.internal.JvmPluginsHelper;
-import org.gradle.api.tasks.SourceSet;
-import org.gradle.api.tasks.SourceSetContainer;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.api.tasks.TaskProvider;
-import org.gradle.api.tasks.bundling.Jar;
-import org.gradle.internal.component.external.model.DefaultImmutableCapability;
-import org.gradle.internal.component.external.model.ProjectDerivedCapability;
-import org.gradle.util.internal.TextUtil;
-
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import java.util.List;
-
-import static org.gradle.api.attributes.DocsType.JAVADOC;
-import static org.gradle.api.attributes.DocsType.SOURCES;
-import static org.gradle.api.plugins.internal.JvmPluginsHelper.configureJavaDocTask;
-
-public class DefaultJvmVariantBuilder implements JvmVariantBuilderInternal {
-    private final String name;
-    private final JvmPluginServices jvmPluginServices;
-    private final SourceSetContainer sourceSets;
-    private final RoleBasedConfigurationContainerInternal configurations;
-    private final TaskContainer tasks;
-    private final SoftwareComponentContainer components;
-    private final ProjectInternal project;
-    private String displayName;
-    private SourceSet sourceSet;
-    private boolean exposeApi;
-    private boolean javadocJar;
-    private boolean sourcesJar;
-    private boolean published;
-    private boolean overrideDefaultCapability = true;
-    private final List<Capability> capabilities = Lists.newArrayListWithExpectedSize(2);
-
-    @Inject
-    public DefaultJvmVariantBuilder(String name,
-                                    Capability defaultCapability,
-                                    JvmPluginServices jvmPluginServices,
-                                    SourceSetContainer sourceSets,
-                                    ConfigurationContainer configurations,
-                                    TaskContainer tasks,
-                                    SoftwareComponentContainer components,
-                                    // Ideally we should use project but more specific components
-                                    // like the previous parameters. However for project derived
-                                    // capabilities we need a handle on the project coordinates
-                                    // which can't be externalized. So we use Project but must
-                                    // reduce the scope to this usage only so that we can replace
-                                    // it later
-                                    ProjectInternal project) {
-        this.name = name;
-        this.jvmPluginServices = jvmPluginServices;
-        this.sourceSets = sourceSets;
-        this.configurations = (RoleBasedConfigurationContainerInternal) configurations;
-        this.tasks = tasks;
-        this.components = components;
-        this.project = project;
-        this.capabilities.add(defaultCapability);
-    }
-
-    @Override
-    public JvmVariantBuilder withDisplayName(String displayName) {
-        this.displayName = displayName;
-        return this;
-    }
-
-    @Override
-    public JvmVariantBuilder exposesApi() {
-        exposeApi = true;
-        return this;
-    }
-
-    @Override
-    public JvmVariantBuilder withJavadocJar() {
-        javadocJar = true;
-        return this;
-    }
-
-    @Override
-    public JvmVariantBuilder withSourcesJar() {
-        sourcesJar = true;
-        return this;
-    }
-
-    @Override
-    public JvmVariantBuilder usingSourceSet(SourceSet sourceSet) {
-        this.sourceSet = sourceSet;
-        return this;
-    }
-
-    @Override
-    public JvmVariantBuilder capability(Capability capability) {
-        if (capability.getVersion() == null) {
-            throw new InvalidUserDataException("Capabilities declared on outgoing variants must have a version");
-        }
-        if (overrideDefaultCapability) {
-            capabilities.clear();
-            overrideDefaultCapability = false;
-        }
-        capabilities.add(capability);
-        return this;
-    }
-
-    @Override
-    public JvmVariantBuilder capability(String group, String name, String version) {
-        return capability(new DefaultImmutableCapability(group, name, version));
-    }
-
-    @Override
-    public JvmVariantBuilder distinctCapability() {
-        return capability(new ProjectDerivedCapability(project, name));
-    }
-
-    @Override
-    public JvmVariantBuilder published() {
-        published = true;
-        return this;
-    }
-
-    void build() {
-        SourceSet sourceSet = this.sourceSet == null ? sourceSets.maybeCreate(name) : this.sourceSet;
-        boolean mainSourceSet = SourceSet.isMain(sourceSet);
-        String apiConfigurationName;
-        String implementationConfigurationName;
-        String apiElementsConfigurationName;
-        String runtimeElementsConfigurationName;
-        String compileOnlyConfigurationName;
-        String compileOnlyApiConfigurationName;
-        String runtimeOnlyConfigurationName;
-        if (mainSourceSet) {
-            apiConfigurationName = name + "Api";
-            implementationConfigurationName = name + "Implementation";
-            apiElementsConfigurationName = apiConfigurationName + "Elements";
-            runtimeElementsConfigurationName = name + "RuntimeElements";
-            compileOnlyConfigurationName = name + "CompileOnly";
-            compileOnlyApiConfigurationName = name + "CompileOnlyApi";
-            runtimeOnlyConfigurationName = name + "RuntimeOnly";
-        } else {
-            apiConfigurationName = sourceSet.getApiConfigurationName();
-            implementationConfigurationName = sourceSet.getImplementationConfigurationName();
-            apiElementsConfigurationName = sourceSet.getApiElementsConfigurationName();
-            runtimeElementsConfigurationName = sourceSet.getRuntimeElementsConfigurationName();
-            compileOnlyConfigurationName = sourceSet.getCompileOnlyConfigurationName();
-            compileOnlyApiConfigurationName = sourceSet.getCompileOnlyApiConfigurationName();
-            runtimeOnlyConfigurationName = sourceSet.getRuntimeOnlyConfigurationName();
-        }
-
-        String displayName = this.displayName == null ? name : this.displayName;
-        // In the general case, the following configurations are already created
-        // but if we're using the "main" source set, it means that the component we're creating shares
-        // the same source set (main) but declares its dependencies in its own buckets, so we need
-        // to create them
-        Configuration implementation = bucket("Implementation", implementationConfigurationName, displayName);
-        Configuration compileOnly = bucket("Compile-Only", compileOnlyConfigurationName, displayName);
-        Configuration compileOnlyApi = bucket("Compile-Only API", compileOnlyApiConfigurationName, displayName);
-        Configuration runtimeOnly = bucket("Runtime-Only", runtimeOnlyConfigurationName, displayName);
-
-        TaskProvider<Task> jarTask = registerOrGetJarTask(sourceSet, displayName);
-        Configuration api = exposeApi ? bucket("API", apiConfigurationName, displayName) : null;
-        Configuration apiElements = exposeApi ? jvmPluginServices.createOutgoingElements(apiElementsConfigurationName, builder -> {
-            builder.fromSourceSet(sourceSet)
-                .providesApi()
-                .withDescription("API elements for " + displayName)
-                .extendsFrom(api, compileOnlyApi)
-                .withCapabilities(capabilities)
-                .withClassDirectoryVariant()
-                .artifact(jarTask);
-        }) : null;
-        if (exposeApi) {
-            implementation.extendsFrom(api);
-        }
-
-        Configuration runtimeElements = jvmPluginServices.createOutgoingElements(runtimeElementsConfigurationName, builder -> {
-            builder.fromSourceSet(sourceSet)
-                .providesRuntime()
-                .withDescription("Runtime elements for " + displayName)
-                .extendsFrom(implementation, runtimeOnly)
-                .withCapabilities(capabilities)
-                .artifact(jarTask);
-        });
-        if (mainSourceSet) {
-            // we need to wire the compile only and runtime only to the classpath configurations
-            configurations.getByName(sourceSet.getCompileClasspathConfigurationName()).extendsFrom(implementation, compileOnly);
-            configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName()).extendsFrom(implementation, runtimeOnly);
-            // and we also want the feature dependencies to be available on the test classpath
-            configurations.getByName(JvmConstants.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME).extendsFrom(implementation, compileOnlyApi);
-            configurations.getByName(JvmConstants.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME).extendsFrom(implementation, runtimeOnly);
-        }
-
-        // TODO: #23495 Investigate the implications of using this class without
-        //       the java plugin applied, and thus no java component present.
-        //       In the long run, all of these variants and domain objects should be
-        //       owned by a component. If we do not add them to the default java component,
-        //       we should be adding them to a user-provided or new component instead.
-        final AdhocComponentWithVariants component = findJavaComponent();
-
-        JavaPluginExtension javaPluginExtension = project.getExtensions().findByType(JavaPluginExtension.class);
-        configureJavaDocTask(name, sourceSet, tasks, javaPluginExtension);
-        if (javadocJar) {
-            Configuration javadocVariant = JvmPluginsHelper.createDocumentationVariantWithArtifact(
-                sourceSet.getJavadocElementsConfigurationName(),
-                mainSourceSet ? null : name,
-                JAVADOC,
-                capabilities,
-                sourceSet.getJavadocJarTaskName(),
-                tasks.named(sourceSet.getJavadocTaskName()),
-                project
-            );
-
-            if (component != null) {
-                component.addVariantsFromConfiguration(javadocVariant, new JavaConfigurationVariantMapping("runtime", true));
-            }
-        }
-        if (sourcesJar) {
-            Configuration sourcesVariant = JvmPluginsHelper.createDocumentationVariantWithArtifact(
-                sourceSet.getSourcesElementsConfigurationName(),
-                mainSourceSet ? null : name,
-                SOURCES,
-                capabilities,
-                sourceSet.getSourcesJarTaskName(),
-                sourceSet.getAllSource(),
-                project
-            );
-            if (component != null) {
-                component.addVariantsFromConfiguration(sourcesVariant, new JavaConfigurationVariantMapping("runtime", true));
-            }
-        }
-
-        if (published && component != null) {
-            if (apiElements != null) {
-                component.addVariantsFromConfiguration(apiElements, new JavaConfigurationVariantMapping("compile", true));
-            }
-            component.addVariantsFromConfiguration(runtimeElements, new JavaConfigurationVariantMapping("runtime", true));
-        }
-    }
-
-    private Configuration bucket(String kind, String configName, String displayName) {
-        Configuration configuration = configurations.maybeCreateWithRole(configName, ConfigurationRoles.INTENDED_BUCKET, false, false);
-        configuration.setDescription(kind + " dependencies for " + displayName);
-        configuration.setVisible(false);
-        return configuration;
-    }
-
-    private TaskProvider<Task> registerOrGetJarTask(SourceSet sourceSet, String displayName) {
-        String jarTaskName = sourceSet.getJarTaskName();
-        if (!tasks.getNames().contains(jarTaskName)) {
-            tasks.register(jarTaskName, Jar.class, jar -> {
-                jar.setDescription("Assembles a jar archive containing the classes of the '" + displayName + "'.");
-                jar.setGroup(BasePlugin.BUILD_GROUP);
-                jar.from(sourceSet.getOutput());
-                jar.getArchiveClassifier().set(TextUtil.camelToKebabCase(name));
-            });
-        }
-        return tasks.named(jarTaskName);
-    }
-
-    @Nullable
-    public AdhocComponentWithVariants findJavaComponent() {
-        SoftwareComponent component = components.findByName("java");
-        if (component instanceof AdhocComponentWithVariants) {
-            return (AdhocComponentWithVariants) component;
-        }
-        return null;
-    }
-
-}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmEcosystemUtilities.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmEcosystemUtilities.java
index ead3389..e51f3c2 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmEcosystemUtilities.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmEcosystemUtilities.java
@@ -56,14 +56,28 @@ public interface JvmEcosystemUtilities {
      *
      * @param configuration the configuration to be configured
      */
-    <T> void configureAsCompileClasspath(HasConfigurableAttributes<T> configuration);
+    void configureAsCompileClasspath(HasConfigurableAttributes<?> configuration);
 
     /**
      * Configures a configuration with reasonable defaults to be resolved as a runtime classpath.
      *
      * @param configuration the configuration to be configured
      */
-    <T> void configureAsRuntimeClasspath(HasConfigurableAttributes<T> configuration);
+    void configureAsRuntimeClasspath(HasConfigurableAttributes<?> configuration);
+
+    /**
+     * Configures a consumable configuration to provide an API compile classpath.
+     *
+     * @param configuration the configuration to be configured
+     */
+    void configureAsApiElements(HasConfigurableAttributes<?> configuration);
+
+    /**
+     * Configures a consumable configuration to provide a runtime classpath.
+     *
+     * @param configuration the configuration to be configured
+     */
+    void configureAsRuntimeElements(HasConfigurableAttributes<?> configuration);
 
     <T> void configureAttributes(HasConfigurableAttributes<T> configurableAttributes, Action<? super JvmEcosystemAttributesDetails> details);
 
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmFeatureInternal.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmFeatureInternal.java
new file mode 100644
index 0000000..708db66
--- /dev/null
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmFeatureInternal.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2020 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.api.plugins.jvm.internal;
+
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.capabilities.CapabilitiesMetadata;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.TaskProvider;
+import org.gradle.api.tasks.bundling.Jar;
+import org.gradle.api.tasks.compile.JavaCompile;
+
+import javax.annotation.Nullable;
+
+/**
+ * A Jvm Feature wraps a source set to encapsulate the logic and domain objects required to
+ * implement a feature of a JVM component. Features are used to model constructs like
+ * production libraries, test suites, test fixtures, applications, etc. While features are not
+ * individually consumable themselves for publication or through dependency-management,
+ * they can be exposed to consumers via an owning component.
+ *
+ * <p>Features are classified by their capabilities. Each variant of a feature provides at least
+ * the same set of capabilities as the feature itself. Since all variants of a feature are derived
+ * from the same sources, they all expose the same API or "content" and thus provide the same
+ * capabilities. Some variants may expose additional capabilities than those of its owning feature,
+ * for example with fat jars.</p>
+ *
+ * <p>TODO: The current API is written as if this were a single-target feature. Before we make this API
+ * public, we should make this API multi-target aware. Alternatively, we could implement a
+ * SingleTargetJvmFeature now and in the future implement a MultiTargetJvmFeature when we're ready.
+ * This would allow us to use the JvmFeature interface as a common parent interface.</p>
+ */
+public interface JvmFeatureInternal {
+
+    /**
+     * Get the capabilities of this feature. All variants exposed by this feature must provide at least
+     * the same capabilities as this feature.
+     */
+    CapabilitiesMetadata getCapabilities();
+
+    // TODO: Are sources and javadoc also target-specific? Some targets, for example java 8 vs 11, may use
+    // separate sources which compile to version-specific APIs. Same for javadoc. They are built from the sources
+    // used for compilation. Also each version of java comes with a separate javadoc tool.
+
+    // TODO: Should Javadoc even live on a generic JVM target? May be can call it withDocumentationJar?
+    // Scala and groovy have Groovydoc and Scaladoc. Kotlin has KDoc.
+
+    /**
+     * Configures this feature to publish a javadoc jar alongside the primary artifacts. As a result,
+     * this method also configures the necessary configurations and tasks required to produce
+     * the javadoc artifact.
+     */
+    void withJavadocJar();
+
+    /**
+     * Configures this feature to publish a sources jar alongside the primary artifacts. As a result,
+     * this method also configures the necessary configurations and tasks required to produce
+     * the sources artifact.
+     */
+    void withSourcesJar();
+
+    /**
+     * Adds the {@code api} and {@code compileOnlyApi} dependency configurations to this feature.
+     *
+     * TODO: Should this live on the "base" JVM feature? Should all JVM features know how to add
+     * an API? Or should we have subclasses which have APIs and others, which support
+     * application features and test suites, which do not have APIs?
+     */
+    void withApi();
+
+    /**
+     * Gets the consumable configuration created by {@link #withJavadocJar()}.
+     *
+     * @return null if {@link #withJavadocJar()} has not been called.
+     */
+    @Nullable
+    Configuration getJavadocElementsConfiguration();
+
+    /**
+     * Gets the consumable configuration created by {@link #withSourcesJar()}.
+     *
+     * @return null if {@link #withSourcesJar()} has not been called.
+     */
+    @Nullable
+    Configuration getSourcesElementsConfiguration();
+
+    // TODO: Many of the methods below probably belong on a JvmTarget. Features may have many targets
+    // and thus many configurations, jar tasks, compile tasks, etc.
+
+    /**
+     * Get the {@link Jar} task which assembles the resources and compilation outputs into
+     * a single artifact.
+     *
+     * @return A provider which supplies the feature's {@link Jar} task.
+     */
+    TaskProvider<Jar> getJarTask();
+
+    /**
+     * Get the {@link JavaCompile} task which compiles the Java source files into classes.
+     *
+     * @return A provider which supplies the feature's {@link JavaCompile} task.
+     */
+    TaskProvider<JavaCompile> getCompileJavaTask();
+
+    /**
+     * Get this feature's backing source set.
+     * <p>
+     * {@link SourceSet#getOutput()} and the classpath-returning methods on the returned
+     * source set should ideally be avoided in favor of the similarly-named methods on
+     * this feature. The concept of source sets having a single set of outputs is only
+     * relevant for single-target features.
+     *
+     * @return This feature's source set.
+     */
+    SourceSet getSourceSet();
+
+    /**
+     * Gets the dependency configuration for which to declare dependencies internal to the feature.
+     * Dependencies declared on this configuration are present during compilation and runtime, but are not
+     * exposed as part of the feature's API variant.
+     *
+     * @return The {@code implementation} configuration.
+     */
+    Configuration getImplementationConfiguration();
+
+    /**
+     * Gets the dependency configuration for which to declare runtime-only dependencies.
+     * Dependencies declared on this configuration are present only during runtime, are not
+     * present during compilation, and are not exposed as part of the feature's API variant.
+     *
+     * @return The {@code runtimeOnly} configuration.
+     */
+    Configuration getRuntimeOnlyConfiguration();
+
+    /**
+     * Gets the dependency configuration for which to declare compile-only dependencies.
+     * Dependencies declared on this configuration are present only during compilation, are not
+     * present during runtime, and are not exposed as part of the feature's API variant.
+     *
+     * @return The {@code compileOnly} configuration.
+     */
+    Configuration getCompileOnlyConfiguration();
+
+    /**
+     * Gets the dependency configuration for which to declare API dependencies.
+     * Dependencies declared on this configuration are present during compilation
+     * and runtime, and are exposed as part of the feature's API variant.
+     *
+     * @return null if {@link #withApi()} has not been called.
+     */
+    @Nullable
+    Configuration getApiConfiguration();
+
+    /**
+     * Gets the dependency configuration for which to declare compile-only API dependencies.
+     * Dependencies declared on this configuration are present during compilation
+     * but not runtime, and are exposed as part of the feature's API variant.
+     *
+     * @return null if {@link #withApi()} has not been called.
+     */
+    @Nullable
+    Configuration getCompileOnlyApiConfiguration();
+
+    /**
+     * Get the resolvable configuration containing the resolved runtime dependencies
+     * for this feature. This configuration does not contain the artifacts from the
+     * feature's compilation itself.
+     *
+     * @return The {@code runtimeClasspath} configuration.
+     */
+    Configuration getRuntimeClasspathConfiguration();
+
+    /**
+     * Get the resolvable configuration containing the resolved compile dependencies
+     * for this feature.
+     *
+     * @return The {@code compileClasspath} configuration.
+     */
+    Configuration getCompileClasspathConfiguration();
+
+    /**
+     * Get the consumable configuration which produces the {@code apiElements} variant of this feature.
+     * This configuration includes all API compilation dependencies as well as the feature's
+     * compilation outputs, but does not include {@code implementation}, {@code compileOnly},
+     * or {@code runtimeOnly} dependencies.
+     *
+     * @return The {@code apiElements} configuration.
+     */
+    Configuration getApiElementsConfiguration();
+
+    /**
+     * Get the consumable configuration which produces the {@code runtimeElements} variant of this feature.
+     * This configuration includes all runtime dependencies as well as the feature's
+     * compilation outputs, but does not include {@code compileOnly} dependencies.
+     *
+     * @return The {@code runtimeElements} configuration.
+     */
+    Configuration getRuntimeElementsConfiguration();
+
+}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmModelingServices.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmModelingServices.java
deleted file mode 100644
index 269697d..0000000
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmModelingServices.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2020 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.api.plugins.jvm.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.NonNullApi;
-import org.gradle.api.artifacts.Configuration;
-
-/**
- * Provides helpers to model JVM components, cross-project JVM publications
- * or resolvable configurations.
- */
-@NonNullApi
-@SuppressWarnings("UnusedReturnValue")
-public interface JvmModelingServices {
-    /**
-     * Creates an outgoing configuration and configures it with reasonable defaults.
-     * @param name the name of the outgoing configurtion
-     * @param configuration the configuration builder, used to describe what the configuration is used for
-     * @return an outgoing (consumable) configuration
-     */
-    Configuration createOutgoingElements(String name, Action<? super OutgoingElementsBuilder> configuration);
-
-    /**
-     * Creates a generic "java component", which implies creation of an underlying source set,
-     * compile tasks, jar tasks, possibly javadocs and sources jars and allows configuring if such
-     * a component has to be published externally.
-     *
-     * This can be used to create new tests, test fixtures, or any other Java component which
-     * needs to live within the same project as the main component.
-     *
-     * @param name the name of the component to create
-     * @param configuration the configuration for the component to be created
-     */
-    void createJvmVariant(String name, Action<? super JvmVariantBuilder> configuration);
-}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmPluginServices.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmPluginServices.java
index 4518613..31ddc6d 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmPluginServices.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmPluginServices.java
@@ -16,8 +16,7 @@
 package org.gradle.api.plugins.jvm.internal;
 
 import org.gradle.api.internal.project.ProjectInternal;
-import org.gradle.api.tasks.SourceSetContainer;
 
-public interface JvmPluginServices extends JvmEcosystemUtilities, JvmModelingServices {
-    void inject(ProjectInternal project, SourceSetContainer sourceSets);
+public interface JvmPluginServices extends JvmEcosystemUtilities {
+    void inject(ProjectInternal project);
 }
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmVariantBuilder.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmVariantBuilder.java
deleted file mode 100644
index 5f225f1..0000000
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmVariantBuilder.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2020 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.api.plugins.jvm.internal;
-
-import org.gradle.api.tasks.SourceSet;
-import org.gradle.internal.HasInternalProtocol;
-
-/**
- * A Java component builder, allowing the automatic creation of a number of configurations,
- * tasks, ...
- */
-@HasInternalProtocol
-public interface JvmVariantBuilder {
-    /**
-     * If this method is called, this allows using an existing source set instead
-     * of relying on automatic creation of a source set
-     * @param sourceSet the existing source set to use
-     */
-    JvmVariantBuilder usingSourceSet(SourceSet sourceSet);
-
-    /**
-     * Sets a display name for this component
-     * @param displayName the display name
-     */
-    JvmVariantBuilder withDisplayName(String displayName);
-
-    /**
-     * Tells that this component exposes an API, in which case a configuration
-     * to declare API dependencies will be automatically created.
-     */
-    JvmVariantBuilder exposesApi();
-
-    /**
-     * Tells that this component should build a javadoc jar too
-     */
-    JvmVariantBuilder withJavadocJar();
-
-    /**
-     * Tells that this component should build a sources jar too
-     */
-    JvmVariantBuilder withSourcesJar();
-
-    /**
-     * Explicitly declares a capability provided by this component
-     * @param group the capability group
-     * @param name the capability name
-     * @param version the capability version
-     */
-    JvmVariantBuilder capability(String group, String name, String version);
-
-    /**
-     * Tells that this component is not the main component and corresponds to a different "thing"
-     * than the main one. For example, test fixtures are different than the component method.
-     * It will implicitly declare a capability which name is derived from the project name and
-     * this component name. For example, for project "lib" and a component named "languageSupport",
-     * the capability name for this component will be "lib-language-support"
-     */
-    JvmVariantBuilder distinctCapability();
-
-    /**
-     * If this method is called, then this component will automatically be
-     * published externally if a publishing plugin is applied.
-     */
-    JvmVariantBuilder published();
-}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmVariantBuilderInternal.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmVariantBuilderInternal.java
deleted file mode 100644
index 9e144ed..0000000
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/JvmVariantBuilderInternal.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2020 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.api.plugins.jvm.internal;
-
-import org.gradle.api.capabilities.Capability;
-
-public interface JvmVariantBuilderInternal extends JvmVariantBuilder {
-    /**
-     * Explicitly declares a capability provided by this component
-     * @param capability the capability this component provides
-     */
-    JvmVariantBuilder capability(Capability capability);
-}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/OutgoingElementsBuilder.java b/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/OutgoingElementsBuilder.java
deleted file mode 100644
index bee76c4..0000000
--- a/subprojects/plugins/src/main/java/org/gradle/api/plugins/jvm/internal/OutgoingElementsBuilder.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2020 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.api.plugins.jvm.internal;
-
-import org.gradle.api.Action;
-import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.capabilities.Capability;
-import org.gradle.api.provider.Provider;
-import org.gradle.api.tasks.SourceSet;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A builder to construct an "outgoing elements" configuration, that is to say something
- * which is consumable by other components (other projects or external projects)
- */
-@SuppressWarnings("UnusedReturnValue")
-public interface OutgoingElementsBuilder {
-    /**
-     * Sets the description for this outgoing elements
-     * @param description the description
-     */
-    OutgoingElementsBuilder withDescription(String description);
-
-    /**
-     * Tells that this elements configuration provides an API
-     */
-    OutgoingElementsBuilder providesApi();
-
-    /**
-     * Tells that this elements configuration provides a runtime
-     */
-    OutgoingElementsBuilder providesRuntime();
-
-    /**
-     * Allows setting the configurations this outgoing elements will inherit from.
-     * Those configurations are typically buckets of dependencies
-     * @param parentConfigurations the parent configurations
-     */
-    OutgoingElementsBuilder extendsFrom(Configuration... parentConfigurations);
-
-    /**
-     * Allows setting the configurations this outgoing elements will inherit from.
-     * Those configurations are typically buckets of dependencies
-     * @param parentConfigurations the parent configurations
-     */
-    OutgoingElementsBuilder extendsFrom(List<Provider<Configuration>> parentConfigurations);
-
-    /**
-     * Adds this configuration as a parent configuration
-     * @param configuration the parent configuration
-     */
-    default OutgoingElementsBuilder extendsFrom(Provider<Configuration> configuration) {
-        return extendsFrom(Collections.singletonList(configuration));
-    }
-
-    /**
-     * If this method is called, the outgoing elements configuration will be automatically
-     * configured to export the output of the source set.
-     * @param sourceSet the source set which consistutes an output to share with this configuration
-     */
-    OutgoingElementsBuilder fromSourceSet(SourceSet sourceSet);
-
-    /**
-     * Registers an artifact to be attached to this configuration. Supports anything supported
-     * by the {@link org.gradle.api.artifacts.ConfigurationPublications#artifact(Object)} method,
-     * which includes task providers, or files.
-     *
-     * @param producer the producer
-     */
-    OutgoingElementsBuilder artifact(Object producer);
-
-    /**
-     * Allows refining the attributes of this configuration in case the defaults are not
-     * sufficient. The refiner will be called after the default attributes are set.
-     * @param refiner the attributes refiner configuration
-     */
-    OutgoingElementsBuilder providesAttributes(Action<? super JvmEcosystemAttributesDetails> refiner);
-
-    /**
-     * Allows declaring the capabilities this outgoing configuration provides
-     * @param capabilities the capabilities
-     */
-    OutgoingElementsBuilder withCapabilities(List<Capability> capabilities);
-
-    /**
-     * Explicitly declares a capability provided by this outgoing configuration
-     * @param group the capability group
-     * @param name the capability name
-     * @param version the capability version
-     */
-    OutgoingElementsBuilder capability(String group, String name, String version);
-
-    /**
-     * Configures this outgoing configuration to provides a "classes directory" variant, which
-     * is useful for intra and inter-project optimization, avoiding the creation of jar tasks
-     * when the only thing which is required is the API of a component.
-     * This should only be called in association with {@link #fromSourceSet(SourceSet)}
-     */
-    OutgoingElementsBuilder withClassDirectoryVariant();
-
-    /**
-     * Configures this outgoing variant for publication. A published outgoing variant
-     * configured this way will be mapped to the "optional" scope, meaning that its
-     * dependencies will appear as optional in the generated POM file.
-     */
-    OutgoingElementsBuilder published();
-}
diff --git a/subprojects/plugins/src/main/java/org/gradle/api/tasks/Upload.java b/subprojects/plugins/src/main/java/org/gradle/api/tasks/Upload.java
index f63412f..7d8a356 100644
--- a/subprojects/plugins/src/main/java/org/gradle/api/tasks/Upload.java
+++ b/subprojects/plugins/src/main/java/org/gradle/api/tasks/Upload.java
@@ -50,10 +50,11 @@ public abstract class Upload extends ConventionTask {
 
     @TaskAction
     protected void upload() {
+        DocumentationRegistry docRegistry = getDocumentationRegistry();
         throw new InvalidUserCodeException(
-                "The legacy `Upload` task was removed in Gradle 8. Please use the `maven-publish` or `ivy-publish` plugin instead. See " +
-                        getDocumentationRegistry().getDocumentationFor("publishing_maven", "publishing_maven") + " or " +
-                        getDocumentationRegistry().getDocumentationFor("publishing_ivy", "publishing_ivy") + " for details");
+                "The legacy `Upload` task was removed in Gradle 8. Please use the `maven-publish` or `ivy-publish` plugin instead. " +
+                        docRegistry.getDocumentationRecommendationFor("on publishing on maven repositories", "publishing_maven", "publishing_maven") + "\n" +
+                        docRegistry.getDocumentationRecommendationFor("on publishing on ivy repositories", "publishing_ivy", "publishing_ivy"));
     }
 
     /**
diff --git a/subprojects/plugins/src/main/java/org/gradle/jvm/application/tasks/package-info.java b/subprojects/plugins/src/main/java/org/gradle/jvm/application/tasks/package-info.java
index 9889d1e..dc17410 100644
--- a/subprojects/plugins/src/main/java/org/gradle/jvm/application/tasks/package-info.java
+++ b/subprojects/plugins/src/main/java/org/gradle/jvm/application/tasks/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Tasks for the JVM application plugin.
  */
-package org.gradle.jvm.application.tasks;
\ No newline at end of file
+package org.gradle.jvm.application.tasks;
diff --git a/subprojects/plugins/src/main/java/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponent.java b/subprojects/plugins/src/main/java/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponent.java
index be9a670..32e6e32 100644
--- a/subprojects/plugins/src/main/java/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponent.java
+++ b/subprojects/plugins/src/main/java/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponent.java
@@ -16,20 +16,17 @@
 
 package org.gradle.jvm.component.internal;
 
-import com.google.common.collect.ImmutableList;
 import org.gradle.api.GradleException;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
-import org.gradle.api.artifacts.ConfigurationPublications;
-import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
 import org.gradle.api.attributes.Bundling;
 import org.gradle.api.attributes.Category;
 import org.gradle.api.attributes.Usage;
 import org.gradle.api.attributes.VerificationType;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
-import org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRoles;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet;
 import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.model.ObjectFactory;
@@ -39,8 +36,8 @@
 import org.gradle.api.plugins.PluginContainer;
 import org.gradle.api.plugins.internal.DefaultAdhocSoftwareComponent;
 import org.gradle.api.plugins.internal.JavaConfigurationVariantMapping;
-import org.gradle.api.plugins.internal.JvmPluginsHelper;
-import org.gradle.api.plugins.jvm.internal.JvmPluginServices;
+import org.gradle.api.plugins.jvm.internal.DefaultJvmFeature;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.provider.ProviderFactory;
 import org.gradle.api.publish.PublishingExtension;
 import org.gradle.api.publish.internal.versionmapping.VersionMappingStrategyInternal;
@@ -51,91 +48,57 @@
 import org.gradle.api.publish.plugins.PublishingPlugin;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.SourceSetContainer;
-import org.gradle.api.tasks.SourceSetOutput;
-import org.gradle.api.tasks.TaskContainer;
-import org.gradle.api.tasks.TaskProvider;
-import org.gradle.api.tasks.bundling.Jar;
-import org.gradle.api.tasks.compile.JavaCompile;
-import org.gradle.api.tasks.testing.Test;
 import org.gradle.internal.reflect.Instantiator;
 
 import javax.inject.Inject;
-
-import static org.gradle.api.attributes.DocsType.JAVADOC;
-import static org.gradle.api.attributes.DocsType.SOURCES;
+import java.util.Collections;
 
 /**
- * The software component created by the Java plugin. This component owns the consumable configurations which contribute to
- * this component's variants. Additionally, this component also owns its base {@link SourceSet} and transitively any domain
- * objects which are created by the {@link BasePlugin} on the source set's behalf. This includes the source set's resolvable
- * configurations and buckets, as well as any associated tasks.
+ * The software component created by the Java plugin. This component owns the main {@link JvmFeatureInternal} which itself
+ * is responsible for compiling and packaging the main production jar. Therefore, this component transitively owns the
+ * corresponding source set and any domain objects which are created by the {@link BasePlugin} on the source set's behalf.
+ * This includes the source set's resolvable configurations and buckets, as well as any associated tasks.
  */
 public class DefaultJvmSoftwareComponent extends DefaultAdhocSoftwareComponent implements JvmSoftwareComponentInternal {
 
     private static final String SOURCE_ELEMENTS_VARIANT_NAME_SUFFIX = "SourceElements";
 
-    private final Project project;
-
-    private final JvmPluginServices jvmPluginServices;
-
-    private final SourceSet sourceSet;
-    private final Configuration implementation;
-    private final Configuration runtimeOnly;
-    private final Configuration compileOnly;
-
-    private final Configuration runtimeClasspath;
-    private final Configuration compileClasspath;
-
-    private final Configuration runtimeElements;
-    private final Configuration apiElements;
-
-    private final TaskProvider<JavaCompile> compileJava;
-    private final TaskProvider<Jar> jar;
+    private final JvmFeatureInternal mainFeature;
 
     @Inject
     public DefaultJvmSoftwareComponent(
         String componentName,
         String sourceSetName,
         Project project,
-        JvmPluginServices jvmPluginServices,
         ObjectFactory objectFactory,
         ProviderFactory providerFactory,
         Instantiator instantiator
     ) {
         super(componentName, instantiator);
-        this.project = project;
 
-        this.jvmPluginServices = jvmPluginServices;
-
-        TaskContainer tasks = project.getTasks();
-        ConfigurationContainer configurations = project.getConfigurations();
+        RoleBasedConfigurationContainerInternal configurations = ((ProjectInternal) project).getConfigurations();
         PluginContainer plugins = project.getPlugins();
         ExtensionContainer extensions = project.getExtensions();
 
         JavaPluginExtension javaExtension = getJavaPluginExtension(extensions);
-        this.sourceSet = createSourceSet(sourceSetName, javaExtension.getSourceSets());
+        SourceSet sourceSet = createSourceSet(sourceSetName, javaExtension.getSourceSets());
 
-        this.compileJava = tasks.named(sourceSet.getCompileJavaTaskName(), JavaCompile.class);
-        this.jar = registerJarTask(tasks, sourceSet);
+        this.mainFeature = new DefaultJvmFeature(
+            sourceSetName, sourceSet, Collections.emptyList(),
+            (ProjectInternal) project, ConfigurationRoles.CONSUMABLE, false);
 
-        this.implementation = configurations.getByName(sourceSet.getImplementationConfigurationName());
-        this.compileOnly = configurations.getByName(sourceSet.getCompileOnlyConfigurationName());
-        this.runtimeOnly = configurations.getByName(sourceSet.getRuntimeOnlyConfigurationName());
+        // TODO: Should all features also have this variant? Why just the main feature?
+        createSourceElements(configurations, providerFactory, objectFactory, mainFeature);
 
-        this.runtimeClasspath = configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName());
-        this.compileClasspath = configurations.getByName(sourceSet.getCompileClasspathConfigurationName());
+        // Build the main jar when running `assemble`.
+        extensions.getByType(DefaultArtifactPublicationSet.class)
+            .addCandidate(mainFeature.getRuntimeElementsConfiguration().getArtifacts().iterator().next());
 
-        PublishArtifact jarArtifact = configureArchives(project, jar, tasks, extensions);
-        this.runtimeElements = createRuntimeElements(sourceSet, jarArtifact);
-        this.apiElements = createApiElements(sourceSet, jarArtifact);
-        createSourceElements(configurations, providerFactory, objectFactory, sourceSet);
-
-        JvmPluginsHelper.configureJavaDocTask(null, sourceSet, tasks, javaExtension);
         configurePublishing(plugins, extensions, sourceSet);
 
         // Register the consumable configurations as providing variants for consumption.
-        addVariantsFromConfiguration(apiElements, new JavaConfigurationVariantMapping("compile", false));
-        addVariantsFromConfiguration(runtimeElements, new JavaConfigurationVariantMapping("runtime", false));
+        addVariantsFromConfiguration(mainFeature.getApiElementsConfiguration(), new JavaConfigurationVariantMapping("compile", false));
+        addVariantsFromConfiguration(mainFeature.getRuntimeElementsConfiguration(), new JavaConfigurationVariantMapping("runtime", false));
     }
 
     private static JavaPluginExtension getJavaPluginExtension(ExtensionContainer extensions) {
@@ -154,84 +117,18 @@ private static SourceSet createSourceSet(String name, SourceSetContainer sourceS
         return sourceSets.create(name);
     }
 
-    private static TaskProvider<Jar> registerJarTask(TaskContainer tasks, SourceSet sourceSet) {
-        return tasks.register(sourceSet.getJarTaskName(), Jar.class, jar -> {
-            jar.setDescription("Assembles a jar archive containing the main classes.");
-            jar.setGroup(BasePlugin.BUILD_GROUP);
-            jar.from(sourceSet.getOutput());
-        });
-    }
-
-    private static PublishArtifact configureArchives(Project project, TaskProvider<Jar> jarTaskProvider, TaskContainer tasks, ExtensionContainer extensions) {
-        /*
-         * Unless there are other concerns, we'd prefer to run jar tasks prior to test tasks, as this might offer a small performance improvement
-         * for common usage.  In practice, running test tasks tends to take longer than building a jar; especially as a project matures. If tasks
-         * in downstream projects require the jar from this project, and the jar and test tasks in this project are available to be run in either order,
-         * running jar first so that other projects can continue executing tasks in parallel while this project runs its tests could be an improvement.
-         * However, while we want to prioritize cross-project dependencies to maximize parallelism if possible, we don't want to add an explicit
-         * dependsOn() relationship between the jar task and the test task, so that any projects which need to run test tasks first will not need modification.
-         */
-        tasks.withType(Test.class).configureEach(test -> {
-            // Attempt to avoid configuring jar task if possible, it will likely be configured anyway the by apiElements variant
-            test.shouldRunAfter(tasks.withType(Jar.class));
-        });
-
-        PublishArtifact jarArtifact = new LazyPublishArtifact(jarTaskProvider, ((ProjectInternal) project).getFileResolver(), ((ProjectInternal) project).getTaskDependencyFactory());
-        extensions.getByType(DefaultArtifactPublicationSet.class).addCandidate(jarArtifact);
-        return jarArtifact;
-    }
-
-    private static void addJarArtifactToConfiguration(Configuration configuration, PublishArtifact jarArtifact) {
-        ConfigurationPublications publications = configuration.getOutgoing();
-
-        // Configure an implicit variant
-        publications.getArtifacts().add(jarArtifact);
-        publications.getAttributes().attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE);
-    }
-
-    private Configuration createRuntimeElements(SourceSet sourceSet, PublishArtifact jarArtifact) {
-        Configuration runtimeElementsConfiguration = jvmPluginServices.createOutgoingElements(sourceSet.getRuntimeElementsConfigurationName(),
-            builder -> builder.fromSourceSet(sourceSet)
-                .providesRuntime()
-                .withDescription("Elements of runtime for main.")
-                .extendsFrom(implementation, runtimeOnly));
-        ((ConfigurationInternal) runtimeElementsConfiguration).setCanBeDeclaredAgainst(false);
-
-        // Configure variants
-        addJarArtifactToConfiguration(runtimeElementsConfiguration, jarArtifact);
-        jvmPluginServices.configureClassesDirectoryVariant(runtimeElementsConfiguration, sourceSet);
-        jvmPluginServices.configureResourcesDirectoryVariant(runtimeElementsConfiguration, sourceSet);
-
-        return runtimeElementsConfiguration;
-    }
-
-    private Configuration createApiElements(SourceSet sourceSet, PublishArtifact jarArtifact) {
-        Configuration apiElementsConfiguration = jvmPluginServices.createOutgoingElements(sourceSet.getApiElementsConfigurationName(),
-            builder -> builder.fromSourceSet(sourceSet)
-                .providesApi()
-                .withDescription("API elements for main."));
-        ((ConfigurationInternal) apiElementsConfiguration).setCanBeDeclaredAgainst(false);
-
-        // Configure variants
-        addJarArtifactToConfiguration(apiElementsConfiguration, jarArtifact);
-
-        return apiElementsConfiguration;
-    }
-
-    private Configuration createSourceElements(ConfigurationContainer configurations, ProviderFactory providerFactory, ObjectFactory objectFactory, SourceSet sourceSet) {
+    private Configuration createSourceElements(RoleBasedConfigurationContainerInternal configurations, ProviderFactory providerFactory, ObjectFactory objectFactory, JvmFeatureInternal feature) {
 
         // TODO: Why are we using this non-standard name? For the `java` component, this
         // equates to `mainSourceElements` instead of `sourceElements` as one would expect.
         // Can we change this name without breaking compatibility? Is the variant name part
         // of the component's API?
-        String variantName = sourceSet.getName() + SOURCE_ELEMENTS_VARIANT_NAME_SUFFIX;
+        String variantName = feature.getSourceSet().getName() + SOURCE_ELEMENTS_VARIANT_NAME_SUFFIX;
 
-        Configuration variant = configurations.create(variantName);
+        @SuppressWarnings("deprecation") Configuration variant = configurations.createWithRole(variantName, ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
         variant.setDescription("List of source directories contained in the Main SourceSet.");
         variant.setVisible(false);
-        variant.setCanBeResolved(false);
-        variant.setCanBeConsumed(true);
-        variant.extendsFrom(implementation);
+        variant.extendsFrom(mainFeature.getImplementationConfiguration());
 
         variant.attributes(attributes -> {
             attributes.attribute(Bundling.BUNDLING_ATTRIBUTE, objectFactory.named(Bundling.class, Bundling.EXTERNAL));
@@ -240,13 +137,15 @@ private Configuration createSourceElements(ConfigurationContainer configurations
         });
 
         variant.getOutgoing().artifacts(
-            sourceSet.getAllSource().getSourceDirectories().getElements().flatMap(e -> providerFactory.provider(() -> e)),
+            feature.getSourceSet().getAllSource().getSourceDirectories().getElements().flatMap(e -> providerFactory.provider(() -> e)),
             artifact -> artifact.setType(ArtifactTypeDefinition.DIRECTORY_TYPE)
         );
 
         return variant;
     }
 
+    // TODO: This approach is not necessarily correct for non-main features. All publications will attempt to use the main feature's
+    // compile and runtime classpaths for version mapping, even if a non-main feature is being published.
     private static void configurePublishing(PluginContainer plugins, ExtensionContainer extensions, SourceSet sourceSet) {
         plugins.withType(PublishingPlugin.class, plugin -> {
             PublishingExtension publishing = extensions.getByType(PublishingExtension.class);
@@ -265,92 +164,33 @@ private static void configurePublishing(PluginContainer plugins, ExtensionContai
         });
     }
 
+    // TODO: The component itself should not be concerned with configuring the sources and javadoc jars
+    // of its features. It should lazily react to the variants of the feature being added and configure
+    // itself to in turn advertise those variants. However, this requires a more complete variant API,
+    // which is still being designed. For now, we'll add the variants manually.
+
     @Override
-    public void enableJavadocJarVariant() {
-        if (project.getConfigurations().findByName(sourceSet.getJavadocElementsConfigurationName()) != null) {
-            return;
+    public void withJavadocJar() {
+        mainFeature.withJavadocJar();
+
+        Configuration javadocElements = mainFeature.getJavadocElementsConfiguration();
+        if (!isRegisteredAsLegacyVariant(javadocElements)) {
+            addVariantsFromConfiguration(javadocElements, new JavaConfigurationVariantMapping("runtime", true));
         }
-        Configuration javadocVariant = JvmPluginsHelper.createDocumentationVariantWithArtifact(
-            sourceSet.getJavadocElementsConfigurationName(),
-            null,
-            JAVADOC,
-            ImmutableList.of(),
-            sourceSet.getJavadocJarTaskName(),
-            project.getTasks().named(sourceSet.getJavadocTaskName()),
-            (ProjectInternal) project
-        );
-        addVariantsFromConfiguration(javadocVariant, new JavaConfigurationVariantMapping("runtime", true));
     }
 
     @Override
-    public void enableSourcesJarVariant() {
-        if (project.getConfigurations().findByName(sourceSet.getSourcesElementsConfigurationName()) != null) {
-            return;
+    public void withSourcesJar() {
+        mainFeature.withSourcesJar();
+
+        Configuration sourcesElements = mainFeature.getSourcesElementsConfiguration();
+        if (!isRegisteredAsLegacyVariant(sourcesElements)) {
+            addVariantsFromConfiguration(sourcesElements, new JavaConfigurationVariantMapping("runtime", true));
         }
-        Configuration sourcesVariant = JvmPluginsHelper.createDocumentationVariantWithArtifact(
-            sourceSet.getSourcesElementsConfigurationName(),
-            null,
-            SOURCES,
-            ImmutableList.of(),
-            sourceSet.getSourcesJarTaskName(),
-            sourceSet.getAllSource(),
-            (ProjectInternal) project
-        );
-        addVariantsFromConfiguration(sourcesVariant, new JavaConfigurationVariantMapping("runtime", true));
     }
 
     @Override
-    public TaskProvider<Jar> getMainJarTask() {
-        return jar;
-    }
-
-    @Override
-    public TaskProvider<JavaCompile> getMainCompileJavaTask() {
-        return compileJava;
-    }
-
-    @Override
-    public SourceSetOutput getMainOutput() {
-        return sourceSet.getOutput();
-    }
-
-    @Override
-    public SourceSet getSourceSet() {
-        return sourceSet;
-    }
-
-    @Override
-    public Configuration getImplementationConfiguration() {
-        return implementation;
-    }
-
-    @Override
-    public Configuration getRuntimeOnlyConfiguration() {
-        return runtimeOnly;
-    }
-
-    @Override
-    public Configuration getCompileOnlyConfiguration() {
-        return compileOnly;
-    }
-
-    @Override
-    public Configuration getRuntimeClasspathConfiguration() {
-        return runtimeClasspath;
-    }
-
-    @Override
-    public Configuration getCompileClasspathConfiguration() {
-        return compileClasspath;
-    }
-
-    @Override
-    public Configuration getRuntimeElementsConfiguration() {
-        return runtimeElements;
-    }
-
-    @Override
-    public Configuration getApiElementsConfiguration() {
-        return apiElements;
+    public JvmFeatureInternal getMainFeature() {
+        return mainFeature;
     }
 }
diff --git a/subprojects/plugins/src/main/java/org/gradle/jvm/component/internal/JvmSoftwareComponentInternal.java b/subprojects/plugins/src/main/java/org/gradle/jvm/component/internal/JvmSoftwareComponentInternal.java
index 1a4abbd..f195738 100644
--- a/subprojects/plugins/src/main/java/org/gradle/jvm/component/internal/JvmSoftwareComponentInternal.java
+++ b/subprojects/plugins/src/main/java/org/gradle/jvm/component/internal/JvmSoftwareComponentInternal.java
@@ -16,145 +16,48 @@
 
 package org.gradle.jvm.component.internal;
 
-import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.component.SoftwareComponent;
-import org.gradle.api.tasks.SourceSet;
-import org.gradle.api.tasks.SourceSetOutput;
-import org.gradle.api.tasks.TaskProvider;
-import org.gradle.api.tasks.bundling.Jar;
-import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 
 /**
  * A {@link SoftwareComponent} which produces variants intended for use within the JVM ecosystem.
- * <p>
- * TODO: There is currently no public interface for this type of component, as the JVM component
+ *
+ * <p>TODO: There is currently no public interface for this type of component, as the JVM component
  * infrastructure is still under construction. The main blocker for publicizing this component
- * is the lack of multi-target functionality. This interface currently exposes the
- * {@link #getMainJarTask()}, {@link #getSourceSet()}, {@link #getImplementationConfiguration()},
- * etc. methods, which imply that this component only supports a single compilation target. A
- * multi-target component would not expose these methods at the top-level, but would instead
- * encapsulate them within some kind of {@code targets} or {@code pipelines} container.
- * <p>
- * TODO: Before publicizing this component, we also need to consider how component extensibility works.
+ * is the lack of a proper variant API and support for dynamically adding Features to a component.</p>
+ *
+ * <p>TODO: Before publicizing this component, we also need to consider how component extensibility works.
  * For example, for the java-library plugin we have the additional {@code api} and {@code compileOnlyApi}
- * dependency configurations. Do we expect that plugin to write a new component interface and extend this in
- * order to add the proper getter methods? What about the concrete component class which implements that new
- * interface? Does it extend the default implementation class? Is there a way we can avoid Java inheritance?
+ * dependency configurations. Do we expect that plugin to write a new feature interface and extend a base
+ * interface in order to add the proper getter methods? What about the concrete feature class which implements
+ * that new interface? Does it extend the default implementation class? Is there a way we can avoid
+ * Java inheritance?</p>
  */
 public interface JvmSoftwareComponentInternal extends SoftwareComponent {
 
+    // TODO: These with* names are not ideally named. Traditionally, "withers" create a new
+    // instance with the value changed, but these mutate the component. However, other names
+    // like like "enableJavadocJar" or "addJavadocJar" are also not great since their names
+    // are not declarative.
     /**
      * Configures this component to publish a javadoc jar alongside the primary artifacts. As a result,
      * this method also configures the necessary configurations and tasks required to produce
      * the javadoc artifact.
      */
-    void enableJavadocJarVariant();
+    void withJavadocJar();
 
     /**
      * Configures this component to publish a sources jar alongside the primary artifacts. As a result,
      * this method also configures the necessary configurations and tasks required to produce
      * the sources artifact.
      */
-    void enableSourcesJarVariant();
+    void withSourcesJar();
+
+    // TODO: Future iterations of this component should support dynamically adding new features.
 
     /**
-     * Get the {@link Jar} task which assembles the resources and compilation outputs into
-     * a single artifact.
-     *
-     * @return A provider which supplies the component's {@link Jar} task.
+     * Get the feature which encapsulates all logic and domain objects for building the production software product.
      */
-    TaskProvider<Jar> getMainJarTask();
-
-    /**
-     * Get the {@link JavaCompile} task which compiles the Java source files into classes.
-     *
-     * @return A provider which supplies the component's {@link JavaCompile} task.
-     */
-    TaskProvider<JavaCompile> getMainCompileJavaTask();
-
-    // TODO: This could use a better name. "output" can be confused with a
-    // component's "real" outputs, which are its variants.
-    /**
-     * Get the resources and compilation outputs for this component which are used as
-     * inputs for the {@link #getMainJarTask() main jar} task.
-     *
-     * @return The source set outputs.
-     */
-    SourceSetOutput getMainOutput();
-
-    /**
-     * Get this component's backing source set.
-     * <p>
-     * {@link SourceSet#getOutput()} and the classpath-returning methods on the returned
-     * source set should ideally be avoided in favor of the similarly-named methods on
-     * this component. The concept of source sets having a single set of outputs is only
-     * relevant for single-target components.
-     *
-     * @return This component's source set.
-     */
-    SourceSet getSourceSet();
-
-    /**
-     * Gets the dependency configuration for which to declare dependencies internal to the component.
-     * Dependencies declared on this configuration are present during compilation and runtime, but are not
-     * exposed as part of the component's API variant.
-     *
-     * @return The {@code implementation} configuration.
-     */
-    Configuration getImplementationConfiguration();
-
-    /**
-     * Gets the dependency configuration for which to declare runtime-only dependencies.
-     * Dependencies declared on this configuration are present only during runtime, are not
-     * present during compilation, and are not exposed as part of the component's API variant.
-     *
-     * @return The {@code runtimeOnly} configuration.
-     */
-    Configuration getRuntimeOnlyConfiguration();
-
-    /**
-     * Gets the dependency configuration for which to declare compile-only dependencies.
-     * Dependencies declared on this configuration are present only during compilation, are not
-     * present during runtime, and are not exposed as part of the component's API variant.
-     *
-     * @return The {@code compileOnly} configuration.
-     */
-    Configuration getCompileOnlyConfiguration();
-
-    /**
-     * Get the resolvable configuration containing the resolved runtime dependencies
-     * for this component. This configuration does not contain the artifacts from the
-     * component's compilation itself.
-     *
-     * @return The {@code runtimeClasspath} configuration.
-     */
-    Configuration getRuntimeClasspathConfiguration();
-
-    /**
-     * Get the resolvable configuration containing the resolved compile dependencies
-     * for this component.
-     *
-     * @return The {@code compileClasspath} configuration.
-     */
-    Configuration getCompileClasspathConfiguration();
-
-    /**
-     * Get the consumable configuration which produces the {@code runtimeElements} variant of this component.
-     * This configuration includes all runtime dependencies as well as the component's
-     * compilation outputs, but does not include {@code compileOnly} dependencies.
-     *
-     * @return The {@code runtimeElements} configuration.
-     */
-    Configuration getRuntimeElementsConfiguration();
-
-    /**
-     * Get the consumable configuration which produces the {@code apiElements} variant of this component.
-     * This configuration includes all API compilation dependencies as well as the component's
-     * compilation outputs, but does not include {@code implementation}, {@code compileOnly},
-     * or {@code runtimeOnly} dependencies.
-     *
-     * @return The {@code apiElements} configuration.
-     */
-    Configuration getApiElementsConfiguration();
+    JvmFeatureInternal getMainFeature();
 
 }
diff --git a/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt b/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
index 7c5bcf6..077e527 100755
--- a/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+++ b/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
@@ -118,9 +118,6 @@
 APP_BASE_NAME=\${0##*/}
 APP_HOME=\$( cd "\${APP_HOME:-./}${appHomeRelativePath}" && pwd -P ) || exit
 
-# Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script.
-DEFAULT_JVM_OPTS=${defaultJvmOpts}
-
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
 
@@ -168,10 +165,13 @@
     fi
 else
     JAVACMD=java
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
+    fi
 fi
 
 # Increase the maximum file descriptors if we can.
@@ -232,6 +232,13 @@
     done
 fi
 
+<% /*
+# The DEFAULT_JVM_OPTS variable is intentionally defined here to allow using cygwin-processed APP_HOME.
+# So far the only way to inject APP_HOME reference into DEFAULT_JVM_OPTS is to post-process the start script; the declaration is a good anchor to do that.
+*/ %>
+# Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script.
+DEFAULT_JVM_OPTS=${defaultJvmOpts}
+
 # Collect all arguments for the java command;
 #   * \$DEFAULT_JVM_OPTS, \$JAVA_OPTS, and \$${optsEnvironmentVar} can contain fragments of
 #     shell script including quotes and variable substitutions, so put them in
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/DefaultJavaPluginConventionTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/DefaultJavaPluginConventionTest.groovy
index 252fccb..92854d3 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/DefaultJavaPluginConventionTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/DefaultJavaPluginConventionTest.groovy
@@ -23,7 +23,6 @@
 import org.gradle.api.java.archives.internal.DefaultManifest
 import org.gradle.api.plugins.internal.DefaultJavaPluginConvention
 import org.gradle.api.plugins.internal.DefaultJavaPluginExtension
-import org.gradle.api.plugins.jvm.internal.JvmPluginServices
 import org.gradle.api.tasks.SourceSetContainer
 import org.gradle.jvm.toolchain.JavaLanguageVersion
 import org.gradle.jvm.toolchain.internal.DefaultToolchainSpec
@@ -43,7 +42,7 @@
 
     def setup() {
         project.pluginManager.apply(ReportingBasePlugin)
-        extension = TestUtil.newInstance(DefaultJavaPluginExtension.class, project, sourceSets, toolchainSpec, Stub(JvmPluginServices))
+        extension = TestUtil.newInstance(DefaultJavaPluginExtension.class, project, sourceSets, toolchainSpec)
         convention = TestUtil.newInstance(DefaultJavaPluginConvention.class, project, extension)
     }
 
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
index 29031b0..c10fda2 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaBasePluginTest.groovy
@@ -296,7 +296,7 @@
         when:
         project.pluginManager.apply(JavaBasePlugin)
         project.sourceSets.create('custom')
-        def compileJava = project.tasks['compileCustomJava']
+        def compileJava = project.tasks['compileCustomJava'] as JavaCompile
         compileJava.options.annotationProcessorGeneratedSourcesDirectory = generatedSourcesDir
 
         then:
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
index de334a0..887c0a4 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/JavaPluginTest.groovy
@@ -284,10 +284,9 @@
         task.destinationDir == project.sourceSets.main.output.resourcesDir
 
         when:
-        task = project.tasks[JvmConstants.COMPILE_JAVA_TASK_NAME]
+        task = project.tasks[JvmConstants.COMPILE_JAVA_TASK_NAME] as JavaCompile
 
         then:
-        task instanceof JavaCompile
         task dependsOn()
         task.classpath.is(project.sourceSets.main.compileClasspath)
         task.options.annotationProcessorPath.is(project.sourceSets.main.annotationProcessorPath)
@@ -314,10 +313,9 @@
         task.destinationDir == project.sourceSets.test.output.resourcesDir
 
         when:
-        task = project.tasks[JvmConstants.COMPILE_TEST_JAVA_TASK_NAME]
+        task = project.tasks[JvmConstants.COMPILE_TEST_JAVA_TASK_NAME] as JavaCompile
 
         then:
-        task instanceof JavaCompile
         task dependsOn(JvmConstants.CLASSES_TASK_NAME, JvmConstants.COMPILE_JAVA_TASK_NAME)
         task.classpath.is(project.sourceSets.test.compileClasspath)
         task.options.annotationProcessorPath.is(project.sourceSets.test.annotationProcessorPath)
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/catalog/internal/TomlWriterTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/catalog/internal/TomlWriterTest.groovy
index 00f5e55..5163b24 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/catalog/internal/TomlWriterTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/catalog/internal/TomlWriterTest.groovy
@@ -18,29 +18,30 @@
 
 import com.google.common.collect.Interners
 import groovy.transform.Canonical
+import org.gradle.api.InvalidUserDataException
 import org.gradle.api.internal.catalog.DefaultVersionCatalog
 import org.gradle.api.internal.catalog.DefaultVersionCatalogBuilder
 import org.gradle.api.internal.catalog.parser.TomlCatalogFileParser
 import org.gradle.util.TestUtil
 import spock.lang.Specification
-import spock.lang.Subject
+import spock.lang.TempDir
 
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
 import java.util.function.Supplier
 
 class TomlWriterTest extends Specification {
 
-    private StringWriter output = new StringWriter()
-    private Model sourceModel
-    private Model outputModel
-
-    @Subject
-    private TomlWriter writer = new TomlWriter(output)
+    @TempDir
+    File tempTomlDir
 
     def "generates an equivalent file from an input (#file)"() {
-        parse(file)
+        given:
+        def sourceModel = parse("/org/gradle/api/plugins/catalog/internal/${file}.toml")
 
         when:
-        generateFromModel()
+        def outputModel = generateFromModel(sourceModel)
 
         then:
         outputModel == sourceModel
@@ -54,6 +55,10 @@
     }
 
     def "generated file contains model version"() {
+        given:
+        def output = new StringWriter()
+        def writer = new TomlWriter(output)
+
         when:
         writer.generate(Stub(DefaultVersionCatalog))
 
@@ -66,24 +71,43 @@
 """
     }
 
-    private void generateFromModel() {
-        writer.generate(sourceModel.deps)
-        outputModel = parse(new ByteArrayInputStream(output.toString().getBytes("utf-8")))
+    def "error contains absolute path"() {
+        when:
+        parse("/org/gradle/api/plugins/catalog/internal/wrong.toml")
+
+        then:
+        def exception = thrown(InvalidUserDataException.class)
+        exception.message.contains("In file '")
+        exception.message.contains("wrong.toml'")
     }
 
-    private Model parse(String fileName) {
-        sourceModel = parse(this.class.getResourceAsStream("${fileName}.toml"))
+    private Model generateFromModel(Model sourceModel) {
+        def tomlFile = Files.createTempFile(tempTomlDir.toPath(), "test-", ".toml")
+
+        def writer = Files.newBufferedWriter(tomlFile)
+        writer.withCloseable {
+            def tomlWriter = new TomlWriter(it)
+            tomlWriter.generate(sourceModel.deps)
+        }
+
+        return parse(tomlFile)
     }
 
-    private Model parse(InputStream ins) {
+    private Model parse(String resourceName) {
+        def resourceUri = this.class.getResource(resourceName).toURI()
+        def resourcePath = Paths.get(resourceUri)
+
+        return parse(resourcePath)
+    }
+
+    private Model parse(Path path) {
         def builder = new DefaultVersionCatalogBuilder("libs",
             Interners.newStrongInterner(),
             Interners.newStrongInterner(),
             TestUtil.objectFactory(),
             Stub(Supplier))
-        ins.withCloseable {
-            TomlCatalogFileParser.parse(it, builder)
-        }
+
+        TomlCatalogFileParser.parse(path, builder)
         return new Model(builder.build())
     }
 
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/jvm/internal/AbstractJvmPluginServicesTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/jvm/internal/AbstractJvmPluginServicesTest.groovy
index 36a521d..99d4a8d 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/jvm/internal/AbstractJvmPluginServicesTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/jvm/internal/AbstractJvmPluginServicesTest.groovy
@@ -17,13 +17,11 @@
 package org.gradle.api.plugins.jvm.internal
 
 import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.component.SoftwareComponentContainer
 import org.gradle.api.file.ProjectLayout
 import org.gradle.api.internal.plugins.ExtensionContainerInternal
 import org.gradle.api.internal.project.ProjectInternal
 import org.gradle.api.internal.tasks.TaskContainerInternal
 import org.gradle.api.plugins.Convention
-import org.gradle.api.tasks.SourceSetContainer
 import org.gradle.util.TestUtil
 import spock.lang.Specification
 import spock.lang.Subject
@@ -33,8 +31,6 @@
 abstract class AbstractJvmPluginServicesTest extends Specification {
     def configurations = Mock(ConfigurationContainer)
     def tasks = Mock(TaskContainerInternal)
-    def softwareComponents = Mock(SoftwareComponentContainer)
-    def sourceSets = Mock(SourceSetContainer)
     def project = Stub(ProjectInternal) {
         getObjects() >> TestUtil.objectFactory()
         getProviders() >> TestUtil.providerFactory()
@@ -60,18 +56,12 @@
 
     @Subject
     DefaultJvmPluginServices services = new DefaultJvmPluginServices(
-        configurations,
         TestUtil.objectFactory(),
         TestUtil.providerFactory(),
-        tasks,
-        softwareComponents,
         TestUtil.instantiatorFactory().decorateScheme().instantiator()
     )
 
     def setup() {
-        services.inject(
-            project,
-            sourceSets
-        )
+        services.inject(project)
     }
 }
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServicesTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServicesTest.groovy
index 0360a9c..27d5c2b 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServicesTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/plugins/jvm/internal/DefaultJvmPluginServicesTest.groovy
@@ -18,6 +18,7 @@
 
 import org.gradle.api.Action
 import org.gradle.api.NamedDomainObjectContainer
+import org.gradle.api.artifacts.Configuration
 import org.gradle.api.artifacts.ConfigurationPublications
 import org.gradle.api.artifacts.PublishArtifact
 import org.gradle.api.artifacts.PublishArtifactSet
@@ -47,6 +48,7 @@
 import static org.gradle.api.attributes.Category.REGULAR_PLATFORM
 import static org.gradle.api.attributes.DocsType.DOCS_TYPE_ATTRIBUTE
 import static org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE
+import static org.gradle.api.attributes.Usage.JAVA_API
 import static org.gradle.api.attributes.Usage.JAVA_RUNTIME
 import static org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
 import static org.gradle.api.attributes.java.TargetJvmEnvironment.STANDARD_JVM
@@ -56,7 +58,7 @@
 
 class DefaultJvmPluginServicesTest extends AbstractJvmPluginServicesTest {
 
-    def configuresCompileClasspath() {
+    def "configures compileClasspath"() {
         def mutable = AttributeTestUtil.attributesFactory().mutable()
         def attrs = Mock(HasConfigurableAttributes)
         Action[] action = new Action[1]
@@ -97,7 +99,7 @@
         ]
     }
 
-    def configuresRuntimeClasspath() {
+    def "configures runtimeClasspath"() {
         def mutable = AttributeTestUtil.attributesFactory().mutable()
         def attrs = Mock(HasConfigurableAttributes)
 
@@ -116,6 +118,43 @@
         ]
     }
 
+    def "configures apiElements"() {
+        def mutable = AttributeTestUtil.attributesFactory().mutable()
+        def attrs = Mock(Configuration)
+
+        when:
+        services.configureAsApiElements(attrs)
+
+        then:
+        1 * attrs.getAttributes() >> mutable
+        0 * _
+        mutable.asMap() == [
+            (CATEGORY_ATTRIBUTE): named(Category, LIBRARY),
+            (USAGE_ATTRIBUTE): named(Usage, JAVA_API),
+            (BUNDLING_ATTRIBUTE): named(Bundling, EXTERNAL),
+            (LIBRARY_ELEMENTS_ATTRIBUTE): named(LibraryElements, LibraryElements.JAR)
+        ]
+
+    }
+
+    def "configures runtimeElements"() {
+        def mutable = AttributeTestUtil.attributesFactory().mutable()
+        def attrs = Mock(Configuration)
+
+        when:
+        services.configureAsRuntimeElements(attrs)
+
+        then:
+        1 * attrs.getAttributes() >> mutable
+        0 * _
+        mutable.asMap() == [
+            (CATEGORY_ATTRIBUTE): named(Category, LIBRARY),
+            (USAGE_ATTRIBUTE): named(Usage, JAVA_RUNTIME),
+            (BUNDLING_ATTRIBUTE): named(Bundling, EXTERNAL),
+            (LIBRARY_ELEMENTS_ATTRIBUTE): named(LibraryElements, LibraryElements.JAR)
+        ]
+    }
+
     def "can replace the artifacts of an outgoing configuration"() {
         def outgoing = Mock(ConfigurationPublications)
         def artifacts = Mock(PublishArtifactSet)
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.groovy
index ece0861..364df60 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/api/tasks/testing/TestTest.groovy
@@ -35,12 +35,9 @@
 import org.gradle.api.tasks.util.PatternSet
 import org.gradle.internal.jvm.Jvm
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
-import org.gradle.internal.operations.BuildOperationProgressEventEmitter
 import org.gradle.jvm.toolchain.internal.DefaultToolchainJavaLauncher
-import org.gradle.jvm.toolchain.internal.JavaCompilerFactory
 import org.gradle.jvm.toolchain.internal.JavaToolchain
 import org.gradle.jvm.toolchain.internal.JavaToolchainInput
-import org.gradle.jvm.toolchain.internal.ToolchainToolFactory
 import org.gradle.process.CommandLineArgumentProvider
 
 import static org.gradle.util.internal.WrapUtil.toLinkedSet
@@ -236,7 +233,7 @@
         metadata.getLanguageVersion() >> Jvm.current().javaVersion
         metadata.getCapabilities() >> Collections.emptySet()
         metadata.getJavaHome() >> Jvm.current().javaHome.toPath()
-        def toolchain = new JavaToolchain(metadata, Mock(JavaCompilerFactory), Mock(ToolchainToolFactory), TestFiles.fileFactory(), Mock(JavaToolchainInput), false, Stub(BuildOperationProgressEventEmitter))
+        def toolchain = new JavaToolchain(metadata, TestFiles.fileFactory(), Mock(JavaToolchainInput), false)
         def launcher = new DefaultToolchainJavaLauncher(toolchain)
 
         when:
diff --git a/subprojects/plugins/src/test/groovy/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponentTest.groovy b/subprojects/plugins/src/test/groovy/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponentTest.groovy
index bc8dfe4..166dc88 100644
--- a/subprojects/plugins/src/test/groovy/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponentTest.groovy
+++ b/subprojects/plugins/src/test/groovy/org/gradle/jvm/component/internal/DefaultJvmSoftwareComponentTest.groovy
@@ -23,12 +23,18 @@
 import org.gradle.api.internal.tasks.JvmConstants
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.api.plugins.jvm.internal.DefaultJvmFeature
 import org.gradle.api.reflect.ObjectInstantiationException
 import org.gradle.api.tasks.SourceSet
 import org.gradle.test.fixtures.AbstractProjectBuilderSpec
 
 /**
  * Tests {@link DefaultJvmSoftwareComponent}.
+ *
+ * TODO: This tests a lot of the functionality of the {@link DefaultJvmFeature}. We should probably
+ *       move some of the testing to another feature-specific test class. However, given that the
+ *       DefaultJvmFeature itself is probably going to change heavily when adding multi-target support,
+ *       we should wait to avoid rework.
  */
 class DefaultJvmSoftwareComponentTest extends AbstractProjectBuilderSpec {
 
@@ -59,19 +65,19 @@
         def component = project.objects.newInstance(DefaultJvmSoftwareComponent, "name", "main")
 
         then:
-        component.sourceSet == ext.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
-        component.mainOutput == component.getSourceSet().getOutput()
-        component.runtimeClasspathConfiguration == project.configurations.getByName(JvmConstants.RUNTIME_CLASSPATH_CONFIGURATION_NAME)
-        component.compileClasspathConfiguration == project.configurations.getByName(JvmConstants.COMPILE_CLASSPATH_CONFIGURATION_NAME)
-        component.runtimeElementsConfiguration == project.configurations.getByName(JvmConstants.RUNTIME_ELEMENTS_CONFIGURATION_NAME)
-        component.apiElementsConfiguration == project.configurations.getByName(JvmConstants.API_ELEMENTS_CONFIGURATION_NAME)
+        component.mainFeature instanceof DefaultJvmFeature
+        component.mainFeature.sourceSet == ext.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
+        component.mainFeature.runtimeClasspathConfiguration == project.configurations.getByName(JvmConstants.RUNTIME_CLASSPATH_CONFIGURATION_NAME)
+        component.mainFeature.compileClasspathConfiguration == project.configurations.getByName(JvmConstants.COMPILE_CLASSPATH_CONFIGURATION_NAME)
+        component.mainFeature.runtimeElementsConfiguration == project.configurations.getByName(JvmConstants.RUNTIME_ELEMENTS_CONFIGURATION_NAME)
+        component.mainFeature.apiElementsConfiguration == project.configurations.getByName(JvmConstants.API_ELEMENTS_CONFIGURATION_NAME)
         project.configurations.getByName('mainSourceElements')
-        component.implementationConfiguration == project.configurations.getByName(JvmConstants.IMPLEMENTATION_CONFIGURATION_NAME)
-        component.runtimeOnlyConfiguration == project.configurations.getByName(JvmConstants.RUNTIME_ONLY_CONFIGURATION_NAME)
-        component.compileOnlyConfiguration == project.configurations.getByName(JvmConstants.COMPILE_ONLY_CONFIGURATION_NAME)
+        component.mainFeature.implementationConfiguration == project.configurations.getByName(JvmConstants.IMPLEMENTATION_CONFIGURATION_NAME)
+        component.mainFeature.runtimeOnlyConfiguration == project.configurations.getByName(JvmConstants.RUNTIME_ONLY_CONFIGURATION_NAME)
+        component.mainFeature.compileOnlyConfiguration == project.configurations.getByName(JvmConstants.COMPILE_ONLY_CONFIGURATION_NAME)
         project.configurations.getByName(JvmConstants.ANNOTATION_PROCESSOR_CONFIGURATION_NAME)
-        component.mainCompileJavaTask.get() == project.tasks.getByName(JvmConstants.COMPILE_JAVA_TASK_NAME)
-        component.mainJarTask.get() == project.tasks.getByName(JvmConstants.JAR_TASK_NAME)
+        component.mainFeature.compileJavaTask.get() == project.tasks.getByName(JvmConstants.COMPILE_JAVA_TASK_NAME)
+        component.mainFeature.jarTask.get() == project.tasks.getByName(JvmConstants.JAR_TASK_NAME)
         project.tasks.getByName(JvmConstants.JAVADOC_TASK_NAME)
         project.tasks.getByName(JvmConstants.PROCESS_RESOURCES_TASK_NAME)
     }
@@ -103,19 +109,19 @@
         def component = project.objects.newInstance(DefaultJvmSoftwareComponent, "name", "feature")
 
         then:
-        component.sourceSet == ext.sourceSets.getByName('feature')
-        component.mainOutput == component.getSourceSet().getOutput()
-        component.runtimeClasspathConfiguration == project.configurations.getByName('featureRuntimeClasspath')
-        component.compileClasspathConfiguration == project.configurations.getByName('featureCompileClasspath')
-        component.runtimeElementsConfiguration == project.configurations.getByName('featureRuntimeElements')
-        component.apiElementsConfiguration == project.configurations.getByName('featureApiElements')
+        component.mainFeature instanceof DefaultJvmFeature
+        component.mainFeature.sourceSet == ext.sourceSets.getByName('feature')
+        component.mainFeature.runtimeClasspathConfiguration == project.configurations.getByName('featureRuntimeClasspath')
+        component.mainFeature.compileClasspathConfiguration == project.configurations.getByName('featureCompileClasspath')
+        component.mainFeature.runtimeElementsConfiguration == project.configurations.getByName('featureRuntimeElements')
+        component.mainFeature.apiElementsConfiguration == project.configurations.getByName('featureApiElements')
         project.configurations.getByName('featureSourceElements')
-        component.implementationConfiguration == project.configurations.getByName('featureImplementation')
-        component.runtimeOnlyConfiguration == project.configurations.getByName('featureRuntimeOnly')
-        component.compileOnlyConfiguration == project.configurations.getByName('featureCompileOnly')
+        component.mainFeature.implementationConfiguration == project.configurations.getByName('featureImplementation')
+        component.mainFeature.runtimeOnlyConfiguration == project.configurations.getByName('featureRuntimeOnly')
+        component.mainFeature.compileOnlyConfiguration == project.configurations.getByName('featureCompileOnly')
         project.configurations.getByName('featureAnnotationProcessor')
-        component.mainCompileJavaTask.get() == project.tasks.getByName('compileFeatureJava')
-        component.mainJarTask.get() == project.tasks.getByName('featureJar')
+        component.mainFeature.compileJavaTask.get() == project.tasks.getByName('compileFeatureJava')
+        component.mainFeature.jarTask.get() == project.tasks.getByName('featureJar')
         project.tasks.getByName('featureJavadoc')
         project.tasks.getByName('processFeatureResources')
     }
@@ -163,7 +169,7 @@
 
         when:
         def component = project.objects.newInstance(DefaultJvmSoftwareComponent, "name", "main")
-        component.enableJavadocJarVariant()
+        component.withJavadocJar()
 
         then:
         def configuration = project.configurations.getByName(JvmConstants.JAVADOC_ELEMENTS_CONFIGURATION_NAME)
@@ -175,7 +181,7 @@
         (configuration.attributes.getAttribute(Category.CATEGORY_ATTRIBUTE) as Category).name == Category.DOCUMENTATION
         (configuration.attributes.getAttribute(Bundling.BUNDLING_ATTRIBUTE) as Bundling).name == Bundling.EXTERNAL
         (configuration.attributes.getAttribute(DocsType.DOCS_TYPE_ATTRIBUTE) as DocsType).name == DocsType.JAVADOC
-        project.tasks.getByName(component.sourceSet.javadocJarTaskName)
+        project.tasks.getByName(component.mainFeature.sourceSet.javadocJarTaskName)
         component.usages.find { it.name == JvmConstants.JAVADOC_ELEMENTS_CONFIGURATION_NAME}
     }
 
@@ -192,7 +198,7 @@
 
         when:
         def component = project.objects.newInstance(DefaultJvmSoftwareComponent, "name", "main")
-        component.enableSourcesJarVariant()
+        component.withSourcesJar()
 
         then:
         def configuration = project.configurations.getByName(JvmConstants.SOURCES_ELEMENTS_CONFIGURATION_NAME)
@@ -204,7 +210,7 @@
         (configuration.attributes.getAttribute(Category.CATEGORY_ATTRIBUTE) as Category).name == Category.DOCUMENTATION
         (configuration.attributes.getAttribute(Bundling.BUNDLING_ATTRIBUTE) as Bundling).name == Bundling.EXTERNAL
         (configuration.attributes.getAttribute(DocsType.DOCS_TYPE_ATTRIBUTE) as DocsType).name == DocsType.SOURCES
-        project.tasks.getByName(component.sourceSet.sourcesJarTaskName)
+        project.tasks.getByName(component.mainFeature.sourceSet.sourcesJarTaskName)
         component.usages.find { it.name == JvmConstants.SOURCES_ELEMENTS_CONFIGURATION_NAME}
     }
 
diff --git a/subprojects/plugins/src/test/resources/org/gradle/api/plugins/catalog/internal/wrong.toml b/subprojects/plugins/src/test/resources/org/gradle/api/plugins/catalog/internal/wrong.toml
new file mode 100644
index 0000000..883cece
--- /dev/null
+++ b/subprojects/plugins/src/test/resources/org/gradle/api/plugins/catalog/internal/wrong.toml
@@ -0,0 +1,13 @@
+[versions]
+# Note the missing equals operator
+groovy "2.5.6"
+
+[libraries]
+groovy = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
+groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
+
+guava = "com.google.guava:guava:17.0-jre"
+
+[bundles]
+groovy = ["groovy", "groovy-json"]
+
diff --git a/subprojects/plugins/src/testFixtures/groovy/org/gradle/test/preconditions/PluginTestPreconditions.groovy b/subprojects/plugins/src/testFixtures/groovy/org/gradle/test/preconditions/PluginTestPreconditions.groovy
new file mode 100644
index 0000000..8ad042e
--- /dev/null
+++ b/subprojects/plugins/src/testFixtures/groovy/org/gradle/test/preconditions/PluginTestPreconditions.groovy
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 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.test.preconditions
+
+import org.gradle.test.precondition.TestPrecondition
+
+class PluginTestPreconditions {
+    static File locate(String shellCommand) {
+        return [
+            new File("/bin/$shellCommand"),
+            new File("/usr/bin/$shellCommand"),
+            new File("/usr/local/bin/$shellCommand"),
+            new File("/opt/local/bin/$shellCommand")
+        ].find { it.exists() }
+    }
+
+    static class BashAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return doSatisfies(UnitTestPreconditions.UnixDerivative) && locate("bash") != null
+        }
+    }
+
+    static class DashAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return doSatisfies(UnitTestPreconditions.UnixDerivative) && locate("dash") != null
+        }
+    }
+
+    static class StaticShAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return doSatisfies(UnitTestPreconditions.UnixDerivative) && locate("static-sh") != null
+        }
+    }
+
+    static class ShellcheckAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return doSatisfies(UnitTestPreconditions.UnixDerivative) && locate("shellcheck") != null
+        }
+    }
+}
diff --git a/subprojects/problems/src/main/java/org/gradle/problems/internal/RenderingUtils.java b/subprojects/problems/src/main/java/org/gradle/problems/internal/RenderingUtils.java
index f6a0b67..8cb7309 100644
--- a/subprojects/problems/src/main/java/org/gradle/problems/internal/RenderingUtils.java
+++ b/subprojects/problems/src/main/java/org/gradle/problems/internal/RenderingUtils.java
@@ -19,6 +19,8 @@
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 
+import static java.lang.String.join;
+
 public abstract class RenderingUtils {
     public static String oxfordListOf(Collection<String> values, String conjunction) {
         return values.stream()
@@ -29,14 +31,17 @@ public static String oxfordListOf(Collection<String> values, String conjunction)
 
     public static Collector<? super String, ?, String> oxfordJoin(String conjunction) {
         return Collectors.collectingAndThen(Collectors.toList(), stringList -> {
-            if (stringList.isEmpty()) {
-                return "";
+            switch (stringList.size()) {
+                case 0:
+                    return "";
+                case 1:
+                    return stringList.get(0);
+                case 2:
+                    return join(" " + conjunction + " ", stringList);
+                default:
+                    int bound = stringList.size() - 1;
+                    return join(", ", stringList.subList(0, bound)) + ", " + conjunction + " " + stringList.get(bound);
             }
-            if (stringList.size() == 1) {
-                return stringList.get(0);
-            }
-            int bound = stringList.size() - 1;
-            return String.join(", ", stringList.subList(0, bound)) + " " + conjunction + " " + stringList.get(bound);
         });
     }
 }
diff --git a/subprojects/problems/src/test/groovy/org/gradle/problems/internal/RenderingUtilsTest.groovy b/subprojects/problems/src/test/groovy/org/gradle/problems/internal/RenderingUtilsTest.groovy
new file mode 100644
index 0000000..3b3a306
--- /dev/null
+++ b/subprojects/problems/src/test/groovy/org/gradle/problems/internal/RenderingUtilsTest.groovy
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 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.problems.internal
+
+import spock.lang.Specification
+
+class RenderingUtilsTest extends Specification {
+    def "oxford comma"() {
+        when:
+        def render = RenderingUtils.oxfordListOf(items, "and")
+
+        then:
+        render == result
+
+        where:
+        items               | result
+        ["i1", "i2", "i3"] | "'i1', 'i2', and 'i3'"
+        ["i1", "i2"]       | "'i1' and 'i2'"
+        ["i1"]             | "'i1'"
+        []                 | ""
+    }
+}
diff --git a/subprojects/process-services/src/integTest/groovy/org/gradle/process/internal/health/memory/DefaultOsMemoryInfoIntegrationTest.groovy b/subprojects/process-services/src/integTest/groovy/org/gradle/process/internal/health/memory/DefaultOsMemoryInfoIntegrationTest.groovy
index a1547a2..bf3bbd5 100644
--- a/subprojects/process-services/src/integTest/groovy/org/gradle/process/internal/health/memory/DefaultOsMemoryInfoIntegrationTest.groovy
+++ b/subprojects/process-services/src/integTest/groovy/org/gradle/process/internal/health/memory/DefaultOsMemoryInfoIntegrationTest.groovy
@@ -16,15 +16,15 @@
 
 package org.gradle.process.internal.health.memory
 
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.UsesNativeServices
 import spock.lang.Specification
 
 @UsesNativeServices
 class DefaultOsMemoryInfoIntegrationTest extends Specification {
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "gets OS total memory on a Windows system"() {
         when:
         new DefaultOsMemoryInfo().getOsSnapshot().getTotalPhysicalMemory()
@@ -33,7 +33,7 @@
         notThrown UnsupportedOperationException
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "gets OS free memory on a Windows system"() {
         when:
         new DefaultOsMemoryInfo().getOsSnapshot().getFreePhysicalMemory()
@@ -42,7 +42,7 @@
         notThrown UnsupportedOperationException
     }
 
-    @Requires(TestPrecondition.LINUX)
+    @Requires(UnitTestPreconditions.Linux)
     def "gets OS total memory on a Linux system"() {
         when:
         new DefaultOsMemoryInfo().getOsSnapshot().getTotalPhysicalMemory()
@@ -51,7 +51,7 @@
         notThrown UnsupportedOperationException
     }
 
-    @Requires(TestPrecondition.LINUX)
+    @Requires(UnitTestPreconditions.Linux)
     def "gets OS free memory on a Linux system"() {
         when:
         new DefaultOsMemoryInfo().getOsSnapshot().getFreePhysicalMemory()
@@ -60,7 +60,7 @@
         notThrown UnsupportedOperationException
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     def "gets OS total memory on a MacOS system"() {
         when:
         new DefaultOsMemoryInfo().getOsSnapshot().getTotalPhysicalMemory()
@@ -69,7 +69,7 @@
         notThrown UnsupportedOperationException
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     def "gets OS free memory on a MacOS system"() {
         when:
         new DefaultOsMemoryInfo().getOsSnapshot().getFreePhysicalMemory()
diff --git a/subprojects/process-services/src/test/groovy/org/gradle/process/internal/health/memory/DefaultOsMemoryInfoTest.groovy b/subprojects/process-services/src/test/groovy/org/gradle/process/internal/health/memory/DefaultOsMemoryInfoTest.groovy
index 3be08be..71953dd 100644
--- a/subprojects/process-services/src/test/groovy/org/gradle/process/internal/health/memory/DefaultOsMemoryInfoTest.groovy
+++ b/subprojects/process-services/src/test/groovy/org/gradle/process/internal/health/memory/DefaultOsMemoryInfoTest.groovy
@@ -18,8 +18,8 @@
 package org.gradle.process.internal.health.memory
 
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
@@ -34,7 +34,7 @@
     }
 
     // We only use MX Bean methods on Windows
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "getFreePhysicalMemory only throws when memory management methods are unavailable"() {
         expect:
         memTest({ new DefaultOsMemoryInfo().getOsSnapshot().getFreePhysicalMemory() })
diff --git a/subprojects/publish/src/main/java/org/gradle/api/publish/internal/metadata/InvalidPublicationChecker.java b/subprojects/publish/src/main/java/org/gradle/api/publish/internal/metadata/InvalidPublicationChecker.java
index afda5d2..6cb2be3 100644
--- a/subprojects/publish/src/main/java/org/gradle/api/publish/internal/metadata/InvalidPublicationChecker.java
+++ b/subprojects/publish/src/main/java/org/gradle/api/publish/internal/metadata/InvalidPublicationChecker.java
@@ -19,8 +19,6 @@
 import com.google.common.base.Objects;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
 import org.gradle.api.InvalidUserCodeException;
 import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.capabilities.Capability;
@@ -32,6 +30,9 @@
 import java.util.List;
 import java.util.Set;
 
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+
 @NotThreadSafe
 class InvalidPublicationChecker {
 
@@ -40,8 +41,8 @@ class InvalidPublicationChecker {
     private final String publicationName;
     private final String taskPath;
     private final BiMap<String, VariantIdentity> variants = HashBiMap.create();
-    private List<String> errors;
-    private Set<String> explanations;
+    private final List<String> errors = newArrayList();
+    private final Set<String> explanations = newLinkedHashSet();
     private boolean publicationHasVersion = false;
     private boolean publicationHasDependencyOrConstraint = false;
 
@@ -70,9 +71,9 @@ public void registerVariant(String name, AttributeContainer attributes, Set<? ex
     private void checkVariantDependencyVersions() {
         if (publicationHasDependencyOrConstraint && !publicationHasVersion) {
             // Previous variant did not declare any version
-            failWith("Publication only contains dependencies and/or constraints without a version. You need to add minimal version information, publish resolved versions ("
-                + DOCUMENTATION_REGISTRY.getDocumentationFor("publishing_maven", "publishing_maven:resolved_dependencies") + ") or reference a platform ("
-                + DOCUMENTATION_REGISTRY.getDocumentationFor("platforms") + ")");
+            failWith("Publication only contains dependencies and/or constraints without a version. " +
+                "You need to add minimal version information, publish resolved versions (" + DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("on this", "publishing_maven", "publishing_maven:resolved_dependencies") + ") or " +
+                "reference a platform (" + DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("platforms", "platforms") + ")");
         }
     }
 
@@ -81,7 +82,7 @@ public void validate() {
             failWith("This publication must publish at least one variant");
         }
         checkVariantDependencyVersions();
-        if (errors != null) {
+        if (!errors.isEmpty()) {
             TreeFormatter formatter = new TreeFormatter();
             formatter.node("Invalid publication '" + publicationName + "'");
             formatter.startChildren();
@@ -89,10 +90,8 @@ public void validate() {
                 formatter.node(error);
             }
             formatter.endChildren();
-            if (explanations != null) {
-                for (String explanation : explanations) {
-                    formatter.node(explanation);
-                }
+            for (String explanation : explanations) {
+                formatter.node(explanation);
             }
             throw new InvalidUserCodeException(formatter.toString());
         }
@@ -103,14 +102,8 @@ private void failWith(String message) {
     }
 
     private void failWith(String message, @Nullable String explanation) {
-        if (errors == null) {
-            errors = Lists.newArrayList();
-        }
         errors.add(message);
         if (explanation != null) {
-            if (explanations == null) {
-                explanations = Sets.newLinkedHashSet();
-            }
             explanations.add(explanation);
         }
     }
@@ -130,8 +123,8 @@ public void addDependencyValidationError(String variant, String errorMessage, St
     }
 
     private String explainHowToSuppress(String suppressor) {
-        return " If you did this intentionally you can disable this check by adding '" + suppressor + "' to the suppressed validations of the " + taskPath + " task." +
-            DOCUMENTATION_REGISTRY.getDocumentationFor("publishing_setup", "sec:suppressing_validation_errors");
+        return " If you did this intentionally you can disable this check by adding '" + suppressor + "' to the suppressed validations of the " + taskPath + " task. " +
+            DOCUMENTATION_REGISTRY.getDocumentationRecommendationFor("on suppressing validations", "publishing_setup", "sec:suppressing_validation_errors");
     }
 
     private static final class VariantIdentity {
diff --git a/subprojects/publish/src/main/java/org/gradle/api/publish/internal/validation/PublicationErrorChecker.java b/subprojects/publish/src/main/java/org/gradle/api/publish/internal/validation/PublicationErrorChecker.java
index 6e892a0..775bb87 100644
--- a/subprojects/publish/src/main/java/org/gradle/api/publish/internal/validation/PublicationErrorChecker.java
+++ b/subprojects/publish/src/main/java/org/gradle/api/publish/internal/validation/PublicationErrorChecker.java
@@ -17,14 +17,11 @@
 package org.gradle.api.publish.internal.validation;
 
 import org.gradle.api.artifacts.PublishException;
-import org.gradle.api.attributes.Attribute;
 import org.gradle.api.attributes.Category;
 import org.gradle.api.component.SoftwareComponentVariant;
 import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.api.internal.component.SoftwareComponentInternal;
 
-import java.util.Optional;
-
 /**
  * Static util class containing publication checks agnostic of the publication type.
  */
@@ -37,17 +34,18 @@ public abstract class PublicationErrorChecker {
      * @throws PublishException if the component uses attributes invalid for publication
      */
     public static void checkForUnpublishableAttributes(SoftwareComponentInternal component, DocumentationRegistry documentationRegistry) {
-        for (final SoftwareComponentVariant variant : component.getUsages()) {
-            Optional<Attribute<?>> category = variant.getAttributes().keySet().stream()
-                .filter(a -> Category.CATEGORY_ATTRIBUTE.getName().equals(a.getName()))
-                .findFirst();
-
-            category.ifPresent(c -> {
-                Object value = variant.getAttributes().getAttribute(c);
-                if (value != null && Category.VERIFICATION.equals(value.toString())) {
-                    throw new PublishException("Cannot publish module metadata for component '" + component.getName() + "' which would include a variant '" + variant.getName() + "' that contains a '" + Category.CATEGORY_ATTRIBUTE.getName() + "' attribute with a value of '" + Category.VERIFICATION + "'.  This attribute is reserved for test verification output and is not publishable.  See: " + documentationRegistry.getDocumentationFor("variant_attributes.html", "sec:verification_category"));
-                }
-            });
+        for (SoftwareComponentVariant variant : component.getUsages()) {
+            variant.getAttributes().keySet().stream()
+                .filter(attribute -> Category.CATEGORY_ATTRIBUTE.getName().equals(attribute.getName()))
+                .findFirst()
+                .ifPresent(attribute -> {
+                    Object value = variant.getAttributes().getAttribute(attribute);
+                    if (value != null && Category.VERIFICATION.equals(value.toString())) {
+                        throw new PublishException("Cannot publish module metadata for component '" + component.getName() + "' which would include a variant '" + variant.getName() + "' that contains a '" +
+                            Category.CATEGORY_ATTRIBUTE.getName() + "' attribute with a value of '" + Category.VERIFICATION + "'.  This attribute is reserved for test verification output and is not publishable.  " +
+                            documentationRegistry.getDocumentationRecommendationFor("on this", "variant_attributes", "sec:verification_category"));
+                    }
+                });
         }
     }
 }
diff --git a/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/plugins/BuildDashboardPluginIntegrationTest.groovy b/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/plugins/BuildDashboardPluginIntegrationTest.groovy
index ac7d911..ee45ebc 100644
--- a/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/plugins/BuildDashboardPluginIntegrationTest.groovy
+++ b/subprojects/reporting/src/integTest/groovy/org/gradle/api/reporting/plugins/BuildDashboardPluginIntegrationTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.WellBehavedPluginTest
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.jsoup.Jsoup
 import org.jsoup.nodes.Document
 
@@ -277,7 +277,7 @@
     }
 
     @ToBeFixedForConfigurationCache(because = ":buildDashboard")
-    @Requires(TestPrecondition.STABLE_GROOVY) // FIXME KM temporarily disabling while CodeNarc runs in Worker API with multiple Groovy runtimes
+    @Requires(UnitTestPreconditions.StableGroovy) // FIXME KM temporarily disabling while CodeNarc runs in Worker API with multiple Groovy runtimes
     void 'enabling an additional report renders buildDashboard out-of-date'() {
         given:
         goodCode()
@@ -379,7 +379,7 @@
     }
 
     @ToBeFixedForConfigurationCache(because = ":buildDashboard")
-    @Requires(TestPrecondition.STABLE_GROOVY) // FIXME KM temporarily disabling while CodeNarc runs in Worker API with multiple Groovy runtimes
+    @Requires(UnitTestPreconditions.StableGroovy) // FIXME KM temporarily disabling while CodeNarc runs in Worker API with multiple Groovy runtimes
     void 'dashboard includes CodeNarc reports'() {
         given:
         goodCode()
diff --git a/subprojects/reporting/src/main/java/org/gradle/api/reporting/Reporting.java b/subprojects/reporting/src/main/java/org/gradle/api/reporting/Reporting.java
index 5eb7fc4..bf6d434 100644
--- a/subprojects/reporting/src/main/java/org/gradle/api/reporting/Reporting.java
+++ b/subprojects/reporting/src/main/java/org/gradle/api/reporting/Reporting.java
@@ -45,8 +45,8 @@
  * <pre>
  * task my(type: MyTask) {
  *     reports {
- *         html.enabled = true
- *         csv.enabled = false
+ *         html.required = true
+ *         csv.required = false
  *     }
  * }
  * </pre>
@@ -74,9 +74,9 @@ public interface Reporting<T extends ReportContainer> {
      * <pre>
      * reports {
      *   html {
-     *     enabled false
+     *     required false
      *   }
-     *   xml.destination "build/reports/myReport.xml"
+     *   xml.outputLocation = "build/reports/myReport.xml"
      * }
      * </pre>
      *
@@ -91,9 +91,9 @@ public interface Reporting<T extends ReportContainer> {
      * <pre>
      * reports {
      *   html {
-     *     enabled false
+     *     required false
      *   }
-     *   xml.destination "build/reports/myReport.xml"
+     *   xml.outputLocation = "build/reports/myReport.xml"
      * }
      * </pre>
      * @param configureAction The configuration
diff --git a/subprojects/reporting/src/main/java/org/gradle/api/reporting/plugins/package-info.java b/subprojects/reporting/src/main/java/org/gradle/api/reporting/plugins/package-info.java
index e55cb16..27c18be 100644
--- a/subprojects/reporting/src/main/java/org/gradle/api/reporting/plugins/package-info.java
+++ b/subprojects/reporting/src/main/java/org/gradle/api/reporting/plugins/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Plugins for reporting
  */
-package org.gradle.api.reporting.plugins;
\ No newline at end of file
+package org.gradle.api.reporting.plugins;
diff --git a/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/maven/MavenGcsRepoErrorsIntegrationTest.groovy b/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/maven/MavenGcsRepoErrorsIntegrationTest.groovy
index f8ce7f2..ae0fdfd 100644
--- a/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/maven/MavenGcsRepoErrorsIntegrationTest.groovy
+++ b/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/maven/MavenGcsRepoErrorsIntegrationTest.groovy
@@ -20,6 +20,12 @@
 import org.gradle.integtests.resource.gcs.AbstractGcsDependencyResolutionTest
 import org.gradle.integtests.resource.gcs.fixtures.MavenGcsModule
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 class MavenGcsRepoErrorsIntegrationTest extends AbstractGcsDependencyResolutionTest {
 
     String artifactVersion = "1.85"
@@ -101,9 +107,13 @@
             """Could not find org.gradle:test:1.85.
 Searched in the following locations:
   - ${module.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
 """)
+        failure.assertHasResolutions(repositoryHint("Maven POM"),
+            STACKTRACE_MESSAGE,
+            DEBUG,
+            SCAN,
+            GET_HELP)
     }
 
     @ToBeFixedForConfigurationCache(skip = ToBeFixedForConfigurationCache.Skip.FAILS_TO_CLEANUP)
diff --git a/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/maven/MavenPublishGcsIntegrationTest.groovy b/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/maven/MavenPublishGcsIntegrationTest.groovy
index ceb0fb2..94f1f15 100644
--- a/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/maven/MavenPublishGcsIntegrationTest.groovy
+++ b/subprojects/resources-gcs/src/integTest/groovy/org/gradle/integtests/resource/gcs/maven/MavenPublishGcsIntegrationTest.groovy
@@ -21,15 +21,15 @@
 import org.gradle.integtests.resource.gcs.fixtures.GcsArtifact
 import org.gradle.integtests.resource.gcs.fixtures.GcsServer
 import org.gradle.integtests.resource.gcs.fixtures.MavenGcsRepository
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
 import static org.gradle.internal.resource.transport.gcp.gcs.GcsConnectionProperties.GCS_DISABLE_AUTH_PROPERTY
 import static org.gradle.internal.resource.transport.gcp.gcs.GcsConnectionProperties.GCS_ENDPOINT_PROPERTY
 import static org.gradle.internal.resource.transport.gcp.gcs.GcsConnectionProperties.GCS_SERVICE_PATH_PROPERTY
 
-@Requires(TestPrecondition.NOT_WINDOWS)
+@Requires(UnitTestPreconditions.NotWindows)
 class MavenPublishGcsIntegrationTest extends AbstractMavenPublishIntegTest {
     @Rule
     public GcsServer server = new GcsServer(temporaryFolder)
diff --git a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientHelper.java b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientHelper.java
index 4924ab3..a5784ff 100644
--- a/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientHelper.java
+++ b/subprojects/resources-http/src/main/java/org/gradle/internal/resource/transport/http/HttpClientHelper.java
@@ -45,6 +45,7 @@
 import java.util.List;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
+import static java.lang.String.join;
 import static org.apache.http.client.protocol.HttpClientContext.REDIRECT_LOCATIONS;
 
 /**
@@ -117,32 +118,31 @@ private static HttpRequestException createHttpRequestException(String method, Th
     }
 
     private Exception createCause(IOException e) {
-        Exception cause = e;
-        if (e instanceof SSLHandshakeException) {
-            SSLHandshakeException sslException = (SSLHandshakeException) e;
-            String message = String.format(
-                "%s support the client's requested TLS protocol versions: (%s). " +
-                    "You may need to configure the client to allow other protocols to be used. " +
-                    "See: %s",
-                getConfidenceNote(sslException),
-                String.join(", ", HttpClientConfigurer.supportedTlsVersions()),
-                documentationRegistry.getDocumentationFor("build_environment", "sec:gradle_system_properties")
-            );
-            return new HttpRequestException(message, cause);
+        if (!(e instanceof SSLHandshakeException)) {
+            return e;
         }
-        return cause;
+
+        SSLHandshakeException sslException = (SSLHandshakeException) e;
+        String message = String.format(
+            "The server %s not support the client's requested TLS protocol versions: (%s). " +
+                "You may need to configure the client to allow other protocols to be used. " +
+                "%s",
+            getConfidenceNote(sslException),
+            join(", ", HttpClientConfigurer.supportedTlsVersions()),
+            documentationRegistry.getDocumentationRecommendationFor("on this", "build_environment", "sec:gradle_system_properties")
+        );
+        return new HttpRequestException(message, e);
     }
 
     @Nonnull
     private static String getConfidenceNote(SSLHandshakeException sslException) {
-        final String confidence;
         if (sslException.getMessage() != null && sslException.getMessage().contains("protocol_version")) {
             // If we're handling an SSLHandshakeException with the error of 'protocol_version' we know that the server doesn't support this protocol.
-            return "The server does not";
+            return "does";
         }
         // Sometimes the SSLHandshakeException doesn't include the 'protocol_version', even though this is the cause of the error.
         // Tell the user this but with less confidence.
-        return "The server may not";
+        return "may";
     }
 
     protected HttpClientResponse executeGetOrHead(HttpRequestBase method) throws IOException {
diff --git a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/DeprecatedTLSVersionTest.groovy b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/DeprecatedTLSVersionTest.groovy
index dff9e5b..00065e1 100644
--- a/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/DeprecatedTLSVersionTest.groovy
+++ b/subprojects/resources-http/src/test/groovy/org/gradle/internal/resource/transport/http/DeprecatedTLSVersionTest.groovy
@@ -59,8 +59,8 @@
         and:
         HttpRequestException humanReadableException = exception.cause as HttpRequestException
         humanReadableException.message.startsWith(
-            "The server may not support the client's requested TLS protocol versions: ($SUPPORTED_TLS_VERSION_STRING). You may need to configure the client to allow other protocols to be used. See: "
-        )
+            "The server may not support the client's requested TLS protocol versions: ($SUPPORTED_TLS_VERSION_STRING). You may need to configure the client to allow other protocols to be used. "
+                + new DocumentationRegistry().getDocumentationRecommendationFor("on this", "build_environment", "sec:gradle_system_properties"))
         and:
         humanReadableException.cause instanceof SSLHandshakeException
         cleanup:
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/S3ClientIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/S3ClientIntegrationTest.groovy
index 5dbce33..60f964a 100644
--- a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/S3ClientIntegrationTest.groovy
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/S3ClientIntegrationTest.groovy
@@ -34,8 +34,8 @@
 import org.gradle.internal.resource.transport.aws.s3.S3ConnectionProperties
 import org.gradle.internal.resource.transport.aws.s3.S3RegionalResource
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Ignore
 import spock.lang.Shared
@@ -62,7 +62,7 @@
         awsCredentials.setSecretKey(secret)
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     def "should perform #authenticationType put get and list on an S3 bucket"() {
         setup:
         def fileContents = 'This is only a test'
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3IntegrationTestPrecondition.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3IntegrationTestPrecondition.groovy
deleted file mode 100644
index 1f83f36..0000000
--- a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/fixtures/S3IntegrationTestPrecondition.groovy
+++ /dev/null
@@ -1,29 +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.integtests.resource.s3.fixtures
-
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-
-import static org.gradle.util.TestPrecondition.JDK9_OR_LATER
-
-class S3IntegrationTestPrecondition {
-
-    static boolean isFulfilled() {
-        JDK9_OR_LATER.fulfilled || !GradleContextualExecuter.embedded
-    }
-
-}
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyPublishS3IntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyPublishS3IntegrationTest.groovy
index 92332cb..b24c7a2 100644
--- a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyPublishS3IntegrationTest.groovy
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/ivy/IvyPublishS3IntegrationTest.groovy
@@ -17,12 +17,12 @@
 package org.gradle.integtests.resource.s3.ivy
 
 import org.gradle.api.publish.ivy.AbstractIvyPublishIntegTest
-import org.gradle.integtests.resource.s3.fixtures.S3IntegrationTestPrecondition
 import org.gradle.integtests.resource.s3.fixtures.S3Server
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.junit.Rule
-import spock.lang.Requires
 
-@Requires({ S3IntegrationTestPrecondition.fulfilled })
+@Requires(IntegTestPreconditions.CanPublishToS3)
 class IvyPublishS3IntegrationTest extends AbstractIvyPublishIntegTest {
     @Rule
     public S3Server server = new S3Server(temporaryFolder)
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3IntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3IntegrationTest.groovy
index 0788831..b0243b4 100644
--- a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3IntegrationTest.groovy
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenPublishS3IntegrationTest.groovy
@@ -20,14 +20,15 @@
 import org.gradle.integtests.fixtures.publish.maven.AbstractMavenPublishIntegTest
 import org.gradle.integtests.resource.s3.fixtures.MavenS3Repository
 import org.gradle.integtests.resource.s3.fixtures.S3Artifact
-import org.gradle.integtests.resource.s3.fixtures.S3IntegrationTestPrecondition
 import org.gradle.integtests.resource.s3.fixtures.S3Server
 import org.gradle.internal.credentials.DefaultAwsCredentials
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
-import spock.lang.Requires
 
-@Requires({ S3IntegrationTestPrecondition.fulfilled })
+@Requires(IntegTestPreconditions.CanPublishToS3)
 class MavenPublishS3IntegrationTest extends AbstractMavenPublishIntegTest {
     @Rule
     public S3Server server = new S3Server(temporaryFolder)
@@ -126,7 +127,7 @@
         module.rootMetaData.expectDownloadMissing()
         expectPublish(module.rootMetaData)
 
-        if (TestPrecondition.MAC_OS_X_M1.fulfilled) {
+        if (TestPrecondition.doSatisfies(UnitTestPreconditions.MacOsM1)) {
             // FIXME KM M1 support might require aws-sdk-java-v2:2.17.198 and higher; see https://github.com/aws/aws-sdk-java-v2/issues/2942
             //   ...or this is just a harmless ST, see https://github.com/aws/aws-sdk-java/issues/2365
             executer.withStackTraceChecksDisabled()
diff --git a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoErrorsIntegrationTest.groovy b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoErrorsIntegrationTest.groovy
index 6d72504..dd6eb7d 100644
--- a/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoErrorsIntegrationTest.groovy
+++ b/subprojects/resources-s3/src/integTest/groovy/org/gradle/integtests/resource/s3/maven/MavenS3RepoErrorsIntegrationTest.groovy
@@ -22,6 +22,12 @@
 import org.gradle.integtests.resource.s3.AbstractS3DependencyResolutionTest
 import org.gradle.integtests.resource.s3.fixtures.MavenS3Module
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 class MavenS3RepoErrorsIntegrationTest extends AbstractS3DependencyResolutionTest {
     final String artifactVersion = "1.85"
     MavenS3Module module;
@@ -123,9 +129,13 @@
                 """Could not find org.gradle:test:1.85.
 Searched in the following locations:
   - ${module.pom.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
 """)
+        failure.assertHasResolutions(repositoryHint("Maven POM"),
+            STACKTRACE_MESSAGE,
+            DEBUG,
+            SCAN,
+            GET_HELP)
     }
 
     @ToBeFixedForConfigurationCache
diff --git a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoErrorsIntegrationTest.groovy b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoErrorsIntegrationTest.groovy
index 5028549..6ea67d6 100644
--- a/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoErrorsIntegrationTest.groovy
+++ b/subprojects/resources-sftp/src/integTest/groovy/org/gradle/integtests/resolve/resource/sftp/ivy/IvySftpRepoErrorsIntegrationTest.groovy
@@ -19,6 +19,12 @@
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.resolve.resource.sftp.AbstractSftpDependencyResolutionTest
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.GET_HELP
+import static org.gradle.integtests.fixtures.SuggestionsMessages.INFO_DEBUG
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+import static org.gradle.integtests.fixtures.SuggestionsMessages.STACKTRACE_MESSAGE
+import static org.gradle.integtests.fixtures.SuggestionsMessages.repositoryHint
+
 class IvySftpRepoErrorsIntegrationTest extends AbstractSftpDependencyResolutionTest {
 
     void "resolve missing dependencies from a SFTP Ivy repository"() {
@@ -53,9 +59,14 @@
             .assertHasCause("""Could not find org.group.name:projectA:1.2.
 Searched in the following locations:
   - ${module.ivy.uri}
-If the artifact you are trying to retrieve can be found in the repository but without metadata in 'ivy.xml' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
 Required by:
 """)
+        failure.assertHasResolutions(repositoryHint("ivy.xml"),
+            STACKTRACE_MESSAGE,
+            INFO_DEBUG,
+            SCAN,
+            GET_HELP)
+
     }
 
     void "resolve missing dynamic dependencies from a SFTP Ivy repository"() {
diff --git a/subprojects/resources/src/main/java/org/gradle/api/resources/MissingResourceException.java b/subprojects/resources/src/main/java/org/gradle/api/resources/MissingResourceException.java
index 9a442d0..9e7f296 100644
--- a/subprojects/resources/src/main/java/org/gradle/api/resources/MissingResourceException.java
+++ b/subprojects/resources/src/main/java/org/gradle/api/resources/MissingResourceException.java
@@ -37,4 +37,4 @@ public MissingResourceException(URI location, String message) {
     public MissingResourceException(URI location, String message, Throwable cause) {
         super(location, message, cause);
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ExternalResourceNameTest.groovy b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ExternalResourceNameTest.groovy
index 94aed87..9ccbe4d 100644
--- a/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ExternalResourceNameTest.groovy
+++ b/subprojects/resources/src/test/groovy/org/gradle/internal/resource/ExternalResourceNameTest.groovy
@@ -16,9 +16,9 @@
 
 package org.gradle.internal.resource
 
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.Matchers
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.Shared
 import spock.lang.Specification
 
@@ -144,7 +144,7 @@
         "file:/a/b/c"     | "c"
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "can handle UNC paths"() {
         expect:
         def name = new ExternalResourceName(uri)
diff --git a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesApplicationIntegrationTest.groovy b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesApplicationIntegrationTest.groovy
index 504d1894..aef1099 100644
--- a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesApplicationIntegrationTest.groovy
+++ b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesApplicationIntegrationTest.groovy
@@ -19,11 +19,11 @@
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.ScriptExecuter
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
-@Requires(TestPrecondition.JDK9_OR_LATER)
+@Requires(UnitTestPreconditions.Jdk9OrLater)
 class SamplesApplicationIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule Sample sample = new Sample(temporaryFolder, 'java/application')
diff --git a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesCodeQualityIntegrationTest.groovy b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesCodeQualityIntegrationTest.groovy
index 849fbfd..138fe53 100755
--- a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesCodeQualityIntegrationTest.groovy
+++ b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesCodeQualityIntegrationTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.UsesSample
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
 class SamplesCodeQualityIntegrationTest extends AbstractSampleIntegrationTest {
@@ -29,7 +29,7 @@
     Sample sample = new Sample(testDirectoryProvider)
 
     @UsesSample('codeQuality/codeQuality')
-    @Requires([TestPrecondition.STABLE_GROOVY, TestPrecondition.JDK11_OR_LATER]) // FIXME KM temporarily disabling while CodeNarc runs in Worker API with multiple Groovy runtimes
+    @Requires([UnitTestPreconditions.StableGroovy, UnitTestPreconditions.Jdk11OrLater]) // FIXME KM temporarily disabling while CodeNarc runs in Worker API with multiple Groovy runtimes
     def "can generate reports with #dsl dsl"() {
         TestFile projectDir = sample.dir.file(dsl)
         TestFile buildDir = projectDir.file('build')
diff --git a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesJavaPatchModuleIntegrationTest.groovy b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesJavaPatchModuleIntegrationTest.groovy
index bae26eb..7352125 100644
--- a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesJavaPatchModuleIntegrationTest.groovy
+++ b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/SamplesJavaPatchModuleIntegrationTest.groovy
@@ -17,11 +17,11 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.Sample
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
-@Requires(TestPrecondition.JDK9_OR_LATER)
+@Requires(UnitTestPreconditions.Jdk9OrLater)
 class SamplesJavaPatchModuleIntegrationTest extends AbstractIntegrationSpec {
 
     @Rule Sample sample = new Sample(temporaryFolder, 'testing/patch-module')
diff --git a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/files/SamplesCopyIntegrationTest.groovy b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/files/SamplesCopyIntegrationTest.groovy
index a0a1b91..028b2dc 100644
--- a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/files/SamplesCopyIntegrationTest.groovy
+++ b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/files/SamplesCopyIntegrationTest.groovy
@@ -20,9 +20,9 @@
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.UsesSample
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
-import spock.lang.IgnoreIf
 
 class SamplesCopyIntegrationTest extends AbstractSampleIntegrationTest {
 
@@ -339,7 +339,7 @@
     }
 
     @UsesSample("files/copy")
-    @IgnoreIf({ TestPrecondition.CASE_INSENSITIVE_FS.fulfilled })
+    @Requires(UnitTestPreconditions.CaseSensitiveFs)
     def "can use copy task with rename with #dsl dsl"() {
         given:
         def dslDir = sample.dir.file(dsl)
diff --git a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/java/SamplesJavaTestingIntegrationTest.groovy b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/java/SamplesJavaTestingIntegrationTest.groovy
index fe6ce8a5..5d10452 100644
--- a/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/java/SamplesJavaTestingIntegrationTest.groovy
+++ b/subprojects/samples/src/integTest/groovy/org/gradle/integtests/samples/java/SamplesJavaTestingIntegrationTest.groovy
@@ -21,8 +21,8 @@
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.UsesSample
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
 class SamplesJavaTestingIntegrationTest extends AbstractSampleIntegrationTest {
@@ -30,7 +30,7 @@
     @Rule
     Sample sample = new Sample(testDirectoryProvider)
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     @UsesSample("java/basic")
     def "can execute simple Java tests with #dsl dsl"() {
         given:
@@ -308,7 +308,7 @@
         dsl << ['groovy', 'kotlin']
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     @UsesSample("java/basic")
     def "can run simple Java integration tests with #dsl dsl"() {
         given:
@@ -331,7 +331,7 @@
         dsl << ['groovy', 'kotlin']
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
     @UsesSample("java/basic")
     def "can skip the tests with an `onlyIf` condition with #dsl dsl"() {
         given:
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/ScalaAnnotationProcessingIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/ScalaAnnotationProcessingIntegrationTest.groovy
index 17b338b6..ae34369 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/integtests/ScalaAnnotationProcessingIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/integtests/ScalaAnnotationProcessingIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import static org.gradle.util.internal.TextUtil.escapeString
@@ -147,7 +147,7 @@
     }
 
     // https://github.com/rzwitserloot/lombok/issues/2681
-    @Requires(TestPrecondition.JDK15_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk15OrEarlier)
     def "can use external annotation processor for Java class, from processor path"() {
         given:
         buildFile << basicScalaProject()
@@ -181,7 +181,7 @@
 
     static String annotationProcessorDependency(File repoDir, String processorDependency) {
         """
-            sourceCompatibility = '1.7'
+            java.sourceCompatibility = '1.7'
 
             repositories {
                 maven {
@@ -266,7 +266,7 @@
 
                 group = '$group'
                 version = '$version'
-                sourceCompatibility = '1.7'
+                java.sourceCompatibility = '1.7'
 
                 publishing {
                    publications {
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaCompilationFixture.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaCompilationFixture.groovy
index 50dfc5c..dfe9a9c 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaCompilationFixture.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/ScalaCompilationFixture.groovy
@@ -107,8 +107,10 @@
                 }
             }
 
-            sourceCompatibility = '${sourceCompatibility}'
-            targetCompatibility = '${sourceCompatibility}'
+            java {
+                sourceCompatibility = '${sourceCompatibility}'
+                targetCompatibility = '${sourceCompatibility}'
+            }
         """.stripIndent()
     }
 
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ScalaCompileIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ScalaCompileIntegrationTest.groovy
index c55350b..06c15bb 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ScalaCompileIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/ScalaCompileIntegrationTest.groovy
@@ -22,8 +22,8 @@
 import org.gradle.integtests.fixtures.TargetCoverage
 import org.gradle.integtests.fixtures.jvm.JavaToolchainFixture
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.scala.ScalaCompilationFixture.scalaDependency
 
@@ -96,7 +96,7 @@
         "immutable list" | "[].asImmutable()"
     }
 
-    @Requires(TestPrecondition.JDK11_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk11OrLater)
     def "can compile sources using later JDK APIs"() {
         file("src/main/scala/App.scala") << """
             object App {
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/UpToDateScalaCompileIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/UpToDateScalaCompileIntegrationTest.groovy
index a2a5317..8199533 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/UpToDateScalaCompileIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/compile/UpToDateScalaCompileIntegrationTest.groovy
@@ -21,7 +21,8 @@
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.fixtures.jvm.JavaToolchainBuildOperationsFixture
 import org.gradle.integtests.fixtures.jvm.JavaToolchainFixture
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 
 import static org.gradle.api.JavaVersion.VERSION_11
 import static org.gradle.api.JavaVersion.VERSION_1_8
@@ -63,7 +64,10 @@
         changedVersion = defaultScalaVersion != newScalaVersion ? 'scala' : 'zinc'
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdk(VERSION_1_8) && AvailableJavaHomes.getJdk(VERSION_11) })
+    @Requires([
+        IntegTestPreconditions.Java8HomeAvailable,
+        IntegTestPreconditions.Java11HomeAvailable
+    ])
     def "compile is out of date when changing the java version"() {
         def jdk8 = AvailableJavaHomes.getJdk(VERSION_1_8)
         def jdk11 = AvailableJavaHomes.getJdk(VERSION_11)
@@ -103,8 +107,10 @@
                 zincVersion = "${zincVersion}"
             }
 
-            sourceCompatibility = '1.7'
-            targetCompatibility = '1.7'
+            java {
+                sourceCompatibility = '1.7'
+                targetCompatibility = '1.7'
+            }
         """.stripIndent()
     }
 
diff --git a/subprojects/scala/src/integTest/groovy/org/gradle/scala/environment/JreJavaHomeScalaIntegrationTest.groovy b/subprojects/scala/src/integTest/groovy/org/gradle/scala/environment/JreJavaHomeScalaIntegrationTest.groovy
index 195194b..a107ba1 100644
--- a/subprojects/scala/src/integTest/groovy/org/gradle/scala/environment/JreJavaHomeScalaIntegrationTest.groovy
+++ b/subprojects/scala/src/integTest/groovy/org/gradle/scala/environment/JreJavaHomeScalaIntegrationTest.groovy
@@ -16,9 +16,13 @@
 
 package org.gradle.scala.environment
 
-import org.gradle.integtests.fixtures.*
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.ScalaCoverage
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.integtests.fixtures.ZincScalaCompileFixture
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.IgnoreIf
 
@@ -58,7 +62,7 @@
         scalaClassFile("org/test/ScalaClazz.class").exists()
     }
 
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "scala compilation works when gradle is started with no java_home defined"() {
         given:
         writeScalaTestSource("src/main/scala");
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/Person.java b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/Person.java
index f86b6d1..dabe134 100644
--- a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/Person.java
+++ b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/IncrementalScalaCompileIntegrationTest/recompilesSourceWhenPropertiesChange/src/main/scala/Person.java
@@ -1,3 +1,3 @@
 interface Person {
     String getName();
-}
\ No newline at end of file
+}
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ScalaCompileWithJavaLibraryIntegrationTest/javaLibraryCanDependOnScalaLibraryProject/lib/build.gradle b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ScalaCompileWithJavaLibraryIntegrationTest/javaLibraryCanDependOnScalaLibraryProject/lib/build.gradle
index c958e5b..6944179 100644
--- a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ScalaCompileWithJavaLibraryIntegrationTest/javaLibraryCanDependOnScalaLibraryProject/lib/build.gradle
+++ b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ScalaCompileWithJavaLibraryIntegrationTest/javaLibraryCanDependOnScalaLibraryProject/lib/build.gradle
@@ -21,4 +21,4 @@
 
 dependencies {
     implementation("org.scala-lang:scala-library:2.11.12")
-}
\ No newline at end of file
+}
diff --git a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ScalaCompileWithJavaLibraryIntegrationTest/javaLibraryCanDependOnScalaLibraryProject/settings.gradle b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ScalaCompileWithJavaLibraryIntegrationTest/javaLibraryCanDependOnScalaLibraryProject/settings.gradle
index 1459ffa..02f02a5 100644
--- a/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ScalaCompileWithJavaLibraryIntegrationTest/javaLibraryCanDependOnScalaLibraryProject/settings.gradle
+++ b/subprojects/scala/src/integTest/resources/org/gradle/scala/compile/ScalaCompileWithJavaLibraryIntegrationTest/javaLibraryCanDependOnScalaLibraryProject/settings.gradle
@@ -16,4 +16,4 @@
 
 rootProject.name = "java-library-scala"
 
-include "lib"
\ No newline at end of file
+include "lib"
diff --git a/subprojects/scala/src/main/java/org/gradle/api/plugins/scala/ScalaBasePlugin.java b/subprojects/scala/src/main/java/org/gradle/api/plugins/scala/ScalaBasePlugin.java
index a13fc86..babdd2e 100644
--- a/subprojects/scala/src/main/java/org/gradle/api/plugins/scala/ScalaBasePlugin.java
+++ b/subprojects/scala/src/main/java/org/gradle/api/plugins/scala/ScalaBasePlugin.java
@@ -22,7 +22,6 @@
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.api.attributes.AttributeDisambiguationRule;
@@ -32,8 +31,10 @@
 import org.gradle.api.attributes.Usage;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.ConventionMapping;
-import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.internal.plugins.DslObject;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.internal.tasks.DefaultSourceSet;
 import org.gradle.api.internal.tasks.scala.DefaultScalaPluginExtension;
 import org.gradle.api.model.ObjectFactory;
@@ -52,6 +53,7 @@
 import org.gradle.api.tasks.scala.IncrementalCompileOptions;
 import org.gradle.api.tasks.scala.ScalaCompile;
 import org.gradle.api.tasks.scala.ScalaDoc;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.logging.util.Log4jBannedVersion;
 import org.gradle.jvm.tasks.Jar;
 import org.gradle.jvm.toolchain.JavaLauncher;
@@ -110,25 +112,23 @@ public void apply(final Project project) {
 
         Usage incrementalAnalysisUsage = objectFactory.named(Usage.class, "incremental-analysis");
         Category incrementalAnalysisCategory = objectFactory.named(Category.class, "scala-analysis");
-        configureConfigurations(project, incrementalAnalysisCategory, incrementalAnalysisUsage, scalaPluginExtension);
+        configureConfigurations((ProjectInternal) project, incrementalAnalysisCategory, incrementalAnalysisUsage, scalaPluginExtension);
 
         configureCompileDefaults(project, scalaRuntime, (DefaultJavaPluginExtension) javaPluginExtension(project));
-        configureSourceSetDefaults(project, incrementalAnalysisCategory, incrementalAnalysisUsage);
+        configureSourceSetDefaults((ProjectInternal) project, incrementalAnalysisCategory, incrementalAnalysisUsage);
         configureScaladoc(project, scalaRuntime);
     }
 
-    private void configureConfigurations(final Project project, Category incrementalAnalysisCategory, final Usage incrementalAnalysisUsage, ScalaPluginExtension scalaPluginExtension) {
+    private void configureConfigurations(final ProjectInternal project, Category incrementalAnalysisCategory, final Usage incrementalAnalysisUsage, ScalaPluginExtension scalaPluginExtension) {
         DependencyHandler dependencyHandler = project.getDependencies();
 
-        ConfigurationInternal plugins = (ConfigurationInternal) project.getConfigurations().create(SCALA_COMPILER_PLUGINS_CONFIGURATION_NAME);
+        Configuration plugins = project.getConfigurations().resolvableBucket(SCALA_COMPILER_PLUGINS_CONFIGURATION_NAME);
         plugins.setTransitive(false);
-        plugins.setCanBeConsumed(false);
         jvmEcosystemUtilities.configureAsRuntimeClasspath(plugins);
 
-        Configuration zinc = project.getConfigurations().create(ZINC_CONFIGURATION_NAME);
+        Configuration zinc = project.getConfigurations().resolvableBucket(ZINC_CONFIGURATION_NAME);
         zinc.setVisible(false);
         zinc.setDescription("The Zinc incremental compiler to be used for this Scala project.");
-        zinc.setCanBeConsumed(false);
 
         zinc.getResolutionStrategy().eachDependency(rule -> {
             if (rule.getRequested().getGroup().equals("com.typesafe.zinc") && rule.getRequested().getName().equals("zinc")) {
@@ -157,11 +157,9 @@ private void configureConfigurations(final Project project, Category incremental
             version.reject(Log4jBannedVersion.LOG4J2_CORE_VULNERABLE_VERSION_RANGE);
         })));
 
-        final Configuration incrementalAnalysisElements = project.getConfigurations().create("incrementalScalaAnalysisElements");
+        @SuppressWarnings("deprecation") final Configuration incrementalAnalysisElements = project.getConfigurations().createWithRole("incrementalScalaAnalysisElements", ConfigurationRolesForMigration.CONSUMABLE_BUCKET_TO_CONSUMABLE);
         incrementalAnalysisElements.setVisible(false);
         incrementalAnalysisElements.setDescription("Incremental compilation analysis files");
-        incrementalAnalysisElements.setCanBeResolved(false);
-        incrementalAnalysisElements.setCanBeConsumed(true);
         incrementalAnalysisElements.getAttributes().attribute(USAGE_ATTRIBUTE, incrementalAnalysisUsage);
         incrementalAnalysisElements.getAttributes().attribute(CATEGORY_ATTRIBUTE, incrementalAnalysisCategory);
 
@@ -173,7 +171,7 @@ private void configureConfigurations(final Project project, Category incremental
         });
     }
 
-    private void configureSourceSetDefaults(final Project project, Category incrementalAnalysisCategory, final Usage incrementalAnalysisUsage) {
+    private void configureSourceSetDefaults(final ProjectInternal project, Category incrementalAnalysisCategory, final Usage incrementalAnalysisUsage) {
         javaPluginExtension(project).getSourceSets().all(sourceSet -> {
 
             ScalaSourceDirectorySet scalaSource = getScalaSourceDirectorySet(sourceSet);
@@ -201,17 +199,17 @@ private void configureSourceSetDefaults(final Project project, Category incremen
     @SuppressWarnings("deprecation")
     private ScalaSourceDirectorySet getScalaSourceDirectorySet(SourceSet sourceSet) {
         org.gradle.api.internal.tasks.DefaultScalaSourceSet scalaSourceSet = objectFactory.newInstance(org.gradle.api.internal.tasks.DefaultScalaSourceSet.class, ((DefaultSourceSet) sourceSet).getDisplayName(), objectFactory);
-        new DslObject(sourceSet).getConvention().getPlugins().put("scala", scalaSourceSet);
+        DeprecationLogger.whileDisabled(() ->
+            new DslObject(sourceSet).getConvention().getPlugins().put("scala", scalaSourceSet)
+        );
         return scalaSourceSet.getScala();
     }
 
-    private static Configuration createIncrementalAnalysisConfigurationFor(ConfigurationContainer configurations, Category incrementalAnalysisCategory, Usage incrementalAnalysisUsage, SourceSet sourceSet) {
+    private static Configuration createIncrementalAnalysisConfigurationFor(RoleBasedConfigurationContainerInternal configurations, Category incrementalAnalysisCategory, Usage incrementalAnalysisUsage, SourceSet sourceSet) {
         Configuration classpath = configurations.getByName(sourceSet.getImplementationConfigurationName());
-        Configuration incrementalAnalysis = configurations.create("incrementalScalaAnalysisFor" + sourceSet.getName());
+        @SuppressWarnings("deprecation") Configuration incrementalAnalysis = configurations.createWithRole("incrementalScalaAnalysisFor" + sourceSet.getName(), ConfigurationRolesForMigration.RESOLVABLE_BUCKET_TO_RESOLVABLE);
         incrementalAnalysis.setVisible(false);
         incrementalAnalysis.setDescription("Incremental compilation analysis files for " + ((DefaultSourceSet) sourceSet).getDisplayName());
-        incrementalAnalysis.setCanBeResolved(true);
-        incrementalAnalysis.setCanBeConsumed(false);
         incrementalAnalysis.extendsFrom(classpath);
         incrementalAnalysis.getAttributes().attribute(USAGE_ATTRIBUTE, incrementalAnalysisUsage);
         incrementalAnalysis.getAttributes().attribute(CATEGORY_ATTRIBUTE, incrementalAnalysisCategory);
diff --git a/subprojects/scala/src/main/java/org/gradle/api/plugins/scala/ScalaPlugin.java b/subprojects/scala/src/main/java/org/gradle/api/plugins/scala/ScalaPlugin.java
index d94a9ff..8f53c6cc 100644
--- a/subprojects/scala/src/main/java/org/gradle/api/plugins/scala/ScalaPlugin.java
+++ b/subprojects/scala/src/main/java/org/gradle/api/plugins/scala/ScalaPlugin.java
@@ -25,11 +25,11 @@
 import org.gradle.api.plugins.JavaBasePlugin;
 import org.gradle.api.plugins.JavaPlugin;
 import org.gradle.api.plugins.internal.JavaPluginHelper;
+import org.gradle.api.plugins.jvm.internal.JvmFeatureInternal;
 import org.gradle.api.provider.Provider;
 import org.gradle.api.tasks.ScalaSourceDirectorySet;
 import org.gradle.api.tasks.TaskProvider;
 import org.gradle.api.tasks.scala.ScalaDoc;
-import org.gradle.jvm.component.internal.JvmSoftwareComponentInternal;
 import org.gradle.language.scala.tasks.AbstractScalaCompile;
 
 import java.util.concurrent.Callable;
@@ -49,12 +49,12 @@ public void apply(Project project) {
         project.getPluginManager().apply(ScalaBasePlugin.class);
         project.getPluginManager().apply(JavaPlugin.class);
 
-        JvmSoftwareComponentInternal component = JavaPluginHelper.getJavaComponent(project);
+        JvmFeatureInternal mainFeature = JavaPluginHelper.getJavaComponent(project).getMainFeature();
 
-        configureScaladoc(project, component);
+        configureScaladoc(project, mainFeature);
 
         final Configuration incrementalAnalysisElements = project.getConfigurations().getByName("incrementalScalaAnalysisElements");
-        String compileTaskName = component.getSourceSet().getCompileTaskName("scala");
+        String compileTaskName = mainFeature.getSourceSet().getCompileTaskName("scala");
         final TaskProvider<AbstractScalaCompile> compileScala = project.getTasks().withType(AbstractScalaCompile.class).named(compileTaskName);
         final Provider<RegularFile> compileScalaMapping = project.getLayout().getBuildDirectory().file("tmp/scala/compilerAnalysis/" + compileTaskName + ".mapping");
         compileScala.configure(task -> task.getAnalysisMappingFile().set(compileScalaMapping));
@@ -62,16 +62,16 @@ public void apply(Project project) {
             compileScalaMapping, configurablePublishArtifact -> configurablePublishArtifact.builtBy(compileScala));
     }
 
-    private static void configureScaladoc(final Project project, final JvmSoftwareComponentInternal component) {
+    private static void configureScaladoc(final Project project, final JvmFeatureInternal feature) {
         project.getTasks().withType(ScalaDoc.class).configureEach(scalaDoc -> {
             scalaDoc.getConventionMapping().map("classpath", (Callable<FileCollection>) () -> {
                 ConfigurableFileCollection files = project.files();
-                files.from(component.getMainOutput());
-                files.from(component.getSourceSet().getCompileClasspath());
+                files.from(feature.getSourceSet().getOutput());
+                files.from(feature.getSourceSet().getCompileClasspath());
                 return files;
             });
-            scalaDoc.setSource(component.getSourceSet().getExtensions().getByType(ScalaSourceDirectorySet.class));
-            scalaDoc.getCompilationOutputs().from(component.getMainOutput());
+            scalaDoc.setSource(feature.getSourceSet().getExtensions().getByType(ScalaSourceDirectorySet.class));
+            scalaDoc.getCompilationOutputs().from(feature.getSourceSet().getOutput());
         });
         project.getTasks().register(SCALA_DOC_TASK_NAME, ScalaDoc.class, scalaDoc -> {
             scalaDoc.setDescription("Generates Scaladoc for the main source code.");
diff --git a/subprojects/scala/src/main/java/org/gradle/api/tasks/scala/internal/ScalaCompileOptionsConfigurer.java b/subprojects/scala/src/main/java/org/gradle/api/tasks/scala/internal/ScalaCompileOptionsConfigurer.java
index 326770c..f82bb5a 100644
--- a/subprojects/scala/src/main/java/org/gradle/api/tasks/scala/internal/ScalaCompileOptionsConfigurer.java
+++ b/subprojects/scala/src/main/java/org/gradle/api/tasks/scala/internal/ScalaCompileOptionsConfigurer.java
@@ -36,13 +36,20 @@
 public class ScalaCompileOptionsConfigurer {
 
     private static final int FALLBACK_JVM_TARGET = 8;
+
+    /**
+     * Support for these flags in different minor releases of Scala varies,
+     * but we need to detect as many variants as possible to avoid overriding the target or release.
+     */
     private static final List<String> TARGET_DEFINING_PARAMETERS = Arrays.asList(
         // Scala 2
-        "-target:", "--target:",
+        "-target", "--target",
         // Scala 2 and 3
-        "-release:", "--release:",
+        "-release", "--release",
         // Scala 3
-        "-Xtarget:", "-java-output-version:", "-Xunchecked-java-output-version:"
+        "-java-output-version", "--java-output-version",
+        "-Xunchecked-java-output-version", "--Xunchecked-java-output-version",
+        "-Xtarget", "--Xtarget"
     );
 
     private static final VersionNumber PLAIN_TARGET_FORMAT_SINCE_VERSION = VersionNumber.parse("2.13.1");
@@ -76,7 +83,8 @@ public static void configure(ScalaCompileOptions scalaCompileOptions, JavaInstal
     }
 
     private static boolean hasTargetDefiningParameter(List<String> additionalParameters) {
-        return additionalParameters.stream().anyMatch(s -> TARGET_DEFINING_PARAMETERS.stream().anyMatch(s::startsWith));
+        return additionalParameters.stream()
+            .anyMatch(s -> TARGET_DEFINING_PARAMETERS.stream().anyMatch(param -> param.equals(s) || s.startsWith(param + ":")));
     }
 
     /**
diff --git a/subprojects/scala/src/test/groovy/org/gradle/scala/compile/internal/ScalaCompileOptionsConfigurerTest.groovy b/subprojects/scala/src/test/groovy/org/gradle/scala/compile/internal/ScalaCompileOptionsConfigurerTest.groovy
index 18683dd..37c2186 100644
--- a/subprojects/scala/src/test/groovy/org/gradle/scala/compile/internal/ScalaCompileOptionsConfigurerTest.groovy
+++ b/subprojects/scala/src/test/groovy/org/gradle/scala/compile/internal/ScalaCompileOptionsConfigurerTest.groovy
@@ -120,21 +120,38 @@
         ]
     }
 
-    def 'does not configure target jvm if scala compiler already has a target via #targetFlag flag'() {
+    def 'does not configure target jvm if scala compiler already has a configured target via #targetFlagName flag'() {
         given:
         ScalaCompileOptions scalaCompileOptions = TestUtil.newInstance(ScalaCompileOptions)
-        scalaCompileOptions.additionalParameters = [targetFlag]
+        scalaCompileOptions.additionalParameters = targetFlagParts.toList()
         Set<File> classpath = [new File("scala-library-2.13.1.jar")]
 
         when:
         ScalaCompileOptionsConfigurer.configure(scalaCompileOptions, createToolchain(17, false), classpath)
 
         then:
-        scalaCompileOptions.additionalParameters.find { it == targetFlag }
+        scalaCompileOptions.additionalParameters.find { it == targetFlagParts[0] }
         scalaCompileOptions.additionalParameters.find { it.contains("17") } == null
 
         where:
-        targetFlag << ['-target:8', '--target:8', '-release:8', '--release:8', '-Xtarget:8', '-java-output-version:8', '-Xunchecked-java-output-version:8']
+        targetFlagParts                          | _
+        ['-target:8']                            | _
+        ['--target:8']                           | _
+        ['-target', '8']                         | _
+        ['-release:8']                           | _
+        ['--release:8']                          | _
+        ['-release', '8']                        | _
+        ['--release', '8']                       | _
+        ['-java-output-version:8']               | _
+        ['-java-output-version', '8']            | _
+        ['-Xtarget:8']                           | _
+        ['-Xtarget', '8']                        | _
+        ['--Xtarget:8']                          | _
+        ['--Xtarget', '8']                       | _
+        ['-Xunchecked-java-output-version:8']    | _
+        ['-Xunchecked-java-output-version', '8'] | _
+
+        targetFlagName = targetFlagParts[0]
     }
 
     private JavaToolchain createToolchain(int javaVersion, boolean isFallback) {
diff --git a/subprojects/security/src/main/java/org/gradle/security/internal/KeyringStripper.java b/subprojects/security/src/main/java/org/gradle/security/internal/KeyringStripper.java
new file mode 100644
index 0000000..9cfa56a
--- /dev/null
+++ b/subprojects/security/src/main/java/org/gradle/security/internal/KeyringStripper.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2023 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.security.internal;
+
+import com.google.common.collect.ImmutableList;
+import org.bouncycastle.bcpg.PublicKeyPacket;
+import org.bouncycastle.bcpg.TrustPacket;
+import org.bouncycastle.bcpg.UserIDPacket;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
+
+import java.lang.reflect.Constructor;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+/**
+ * A utility class to strip unnecessary information from a keyring
+ */
+public class KeyringStripper {
+    private static final Constructor<PGPPublicKey> KEY_CONSTRUCTOR;
+
+    private static final Constructor<PGPPublicKey> SUBKEY_CONSTRUCTOR;
+
+    static {
+        try {
+            KEY_CONSTRUCTOR = getKeyConstructor();
+            SUBKEY_CONSTRUCTOR = getSubkeyConstructor();
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static PGPPublicKeyRing strip(PGPPublicKeyRing keyring, KeyFingerPrintCalculator fingerprintCalculator) {
+        List<PGPPublicKey> strippedKeys = StreamSupport
+            .stream(keyring.spliterator(), false)
+            .map(key -> stripKey(key, fingerprintCalculator))
+            .collect(Collectors.toList());
+
+        return new PGPPublicKeyRing(strippedKeys);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static PGPPublicKey stripKey(PGPPublicKey key, KeyFingerPrintCalculator fingerprintCalculator) {
+        PGPPublicKey stripped;
+        try {
+            if (key.isMasterKey()) {
+                Optional<String> id = PGPUtils.getUserIDs(key)
+                    .stream()
+                    .filter(KeyringStripper::looksLikeEmail)
+                    .min(Comparator.comparing(String::length));
+
+                List<UserIDPacket> ids;
+                List<List<PGPSignature>> idSignatures;
+                if (id.isPresent()) {
+                    ids = Collections.singletonList(new UserIDPacket(id.get()));
+                    idSignatures = Collections.singletonList(Collections.emptyList());
+                } else {
+                    ids = Collections.emptyList();
+                    idSignatures = Collections.emptyList();
+                }
+
+                // unfortunately, the PGPPublicKey constructor is package private, so we need to use reflection
+                stripped = KEY_CONSTRUCTOR.newInstance(
+                    key.getPublicKeyPacket(),
+                    null,
+                    Collections.emptyList(),
+                    ids,
+                    Collections.singletonList(null),
+                    idSignatures,
+                    fingerprintCalculator
+                );
+            } else {
+                // unfortunately, the PGPPublicKey subKey constructor is package private, so we need to use reflection
+                stripped = SUBKEY_CONSTRUCTOR.newInstance(
+                    key.getPublicKeyPacket(),
+                    null,
+                    ImmutableList.copyOf(key.getKeySignatures()),
+                    fingerprintCalculator
+                );
+            }
+
+            return stripped;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static boolean looksLikeEmail(String id) {
+        return id.length() >= 5 && id.contains("@");
+    }
+
+    private static Constructor<PGPPublicKey> getKeyConstructor() throws NoSuchMethodException {
+        Constructor<PGPPublicKey> constructor = PGPPublicKey.class.getDeclaredConstructor(
+            PublicKeyPacket.class,
+            TrustPacket.class,
+            List.class,
+            List.class,
+            List.class,
+            List.class,
+            KeyFingerPrintCalculator.class
+        );
+        constructor.setAccessible(true);
+        return constructor;
+    }
+
+    private static Constructor<PGPPublicKey> getSubkeyConstructor() throws NoSuchMethodException {
+        Constructor<PGPPublicKey> constructor = PGPPublicKey.class.getDeclaredConstructor(
+            PublicKeyPacket.class,
+            TrustPacket.class,
+            List.class,
+            KeyFingerPrintCalculator.class
+        );
+        constructor.setAccessible(true);
+        return constructor;
+    }
+}
diff --git a/subprojects/security/src/main/java/org/gradle/security/internal/PGPUtils.java b/subprojects/security/src/main/java/org/gradle/security/internal/PGPUtils.java
new file mode 100644
index 0000000..ba8d226
--- /dev/null
+++ b/subprojects/security/src/main/java/org/gradle/security/internal/PGPUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2023 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.security.internal;
+
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class PGPUtils {
+
+    private static final Field KEYS_FIELD = getKeysField();
+    private PGPUtils() {
+    }
+
+    /**
+     * A custom method to get user ids since original method `PGPPublicKey.getUserIDs()` can fail fast in case user id is not correctly encoded in UTF-8.
+     * The original method fails because it is very strict at conversion from bytes to UTF-8 string.
+     * <p>
+     * Example of dependencies with "broken" public key is `com.google.auto.value:auto-value-annotations`.
+     */
+    public static List<String> getUserIDs(PGPPublicKey pk) {
+        List<String> userIds = new ArrayList<>();
+        pk.getRawUserIDs().forEachRemaining(id -> {
+            userIds.add(new String(id, StandardCharsets.UTF_8));
+        });
+        return userIds;
+    }
+
+    /**
+     * Returns the number of keys in the given keyring.
+     * There is no public API to do this, so we use reflection to access the private field.
+     * Public keys iterator is not used because it would require O(n) time to count the keys.
+     */
+    @SuppressWarnings("unchecked")
+    public static int getSize(PGPPublicKeyRing keyring) {
+        try {
+            return ((List<PGPPublicKey>) KEYS_FIELD.get(keyring)).size();
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Field getKeysField() {
+        try {
+            Field keysField = PGPPublicKeyRing.class.getDeclaredField("keys");
+            keysField.setAccessible(true);
+            return keysField;
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
diff --git a/subprojects/security/src/main/java/org/gradle/security/internal/PublicKeyDownloadService.java b/subprojects/security/src/main/java/org/gradle/security/internal/PublicKeyDownloadService.java
index 8bb24d4..3d383af 100644
--- a/subprojects/security/src/main/java/org/gradle/security/internal/PublicKeyDownloadService.java
+++ b/subprojects/security/src/main/java/org/gradle/security/internal/PublicKeyDownloadService.java
@@ -19,6 +19,7 @@
 import org.bouncycastle.openpgp.PGPPublicKey;
 import org.bouncycastle.openpgp.PGPPublicKeyRing;
 import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
 import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -133,11 +134,14 @@ private void findMatchingKey(byte[] fingerprint, PGPPublicKeyRing keyRing, Publi
 
     private void extractKeyRing(InputStream stream, PublicKeyResultBuilder builder, Consumer<? super PGPPublicKeyRing> onKeyring) throws IOException {
         try (InputStream decoderStream = PGPUtil.getDecoderStream(stream)) {
-            PGPObjectFactory objectFactory = new PGPObjectFactory(
-                decoderStream, new BcKeyFingerprintCalculator());
+            KeyFingerPrintCalculator fingerprintCalculator = new BcKeyFingerprintCalculator();
+            PGPObjectFactory objectFactory = new PGPObjectFactory(decoderStream, fingerprintCalculator);
             PGPPublicKeyRing keyring = (PGPPublicKeyRing) objectFactory.nextObject();
-            onKeyring.accept(keyring);
-            builder.keyRing(keyring);
+
+            PGPPublicKeyRing strippedKeyRing = KeyringStripper.strip(keyring, fingerprintCalculator);
+
+            onKeyring.accept(strippedKeyRing);
+            builder.keyRing(strippedKeyRing);
         }
     }
 
diff --git a/subprojects/security/src/main/java/org/gradle/security/internal/SecuritySupport.java b/subprojects/security/src/main/java/org/gradle/security/internal/SecuritySupport.java
index db802ed..79019b1 100644
--- a/subprojects/security/src/main/java/org/gradle/security/internal/SecuritySupport.java
+++ b/subprojects/security/src/main/java/org/gradle/security/internal/SecuritySupport.java
@@ -25,9 +25,11 @@
 import org.bouncycastle.openpgp.PGPSignatureList;
 import org.bouncycastle.openpgp.PGPUtil;
 import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
+import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
 import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
 import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
 import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
 import org.gradle.api.UncheckedIOException;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
@@ -107,10 +109,13 @@ public static List<PGPPublicKeyRing> loadKeyRingFile(File keyringFile) throws IO
         // load existing keys from keyring before
         try (InputStream ins = PGPUtil.getDecoderStream(createInputStreamFor(keyringFile))) {
             PGPObjectFactory objectFactory = new JcaPGPObjectFactory(ins);
+            KeyFingerPrintCalculator fingerprintCalculator = new JcaKeyFingerprintCalculator();
             try {
                 for (Object o : objectFactory) {
                     if (o instanceof PGPPublicKeyRing) {
-                        existingRings.add((PGPPublicKeyRing) o);
+                        // backward compatibility: old keyrings should be stripped too
+                        PGPPublicKeyRing strippedKeyRing = KeyringStripper.strip((PGPPublicKeyRing) o, fingerprintCalculator);
+                        existingRings.add(strippedKeyRing);
                     }
                 }
             } catch (Exception e) {
@@ -121,7 +126,7 @@ public static List<PGPPublicKeyRing> loadKeyRingFile(File keyringFile) throws IO
     }
 
     private static InputStream createInputStreamFor(File keyringFile) throws IOException {
-        InputStream stream = new FileInputStream(keyringFile);
+        InputStream stream = new BufferedInputStream(new FileInputStream(keyringFile));
         if (keyringFile.getName().endsWith(KEYS_FILE_EXT)) {
             return new ArmoredInputStream(stream);
         }
diff --git a/subprojects/security/src/test/groovy/org/gradle/security/internal/SecuritySupportSpec.groovy b/subprojects/security/src/test/groovy/org/gradle/security/internal/SecuritySupportSpec.groovy
new file mode 100644
index 0000000..0871f0b
--- /dev/null
+++ b/subprojects/security/src/test/groovy/org/gradle/security/internal/SecuritySupportSpec.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 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.security.internal
+
+import spock.lang.Specification
+
+class SecuritySupportSpec extends Specification {
+
+    def "strips data from saved keys on read"() {
+        given:
+        def keyringFile = new File(this.class.getResource("/keyrings/valid-with-extra-metadata.keys").getFile())
+
+        when:
+        def keyrings = SecuritySupport.loadKeyRingFile(keyringFile)
+
+        then:
+        keyrings.size() == 5
+        keyrings.forEach { keyRing ->
+            keyRing.publicKeys.forEachRemaining { publicKey ->
+                assert publicKey.getUserAttributes().size() == 0
+                assert publicKey.signatures.size() == publicKey.keySignatures.size()
+            }
+            assert keyRing.publicKey.userIDs.size() <= 1
+        }
+        keyrings[0].publicKey.userIDs[0] == "Gradle Inc. <info@gradle.com>"
+        keyrings[1].publicKey.userIDs[0] == "Stian Soiland <stain@s11.no>"
+        keyrings[2].publicKey.userIDs[0] == "Gradle <marc@gradle.com>"
+        keyrings[3].publicKey.userIDs[0] == "�amonn McManus <eamonn@mcmanus.net>"
+        keyrings[4].publicKey.userIDs.size() == 0
+    }
+
+
+}
diff --git a/subprojects/security/src/test/resources/keyrings/valid-with-extra-metadata.keys b/subprojects/security/src/test/resources/keyrings/valid-with-extra-metadata.keys
new file mode 100644
index 0000000..4ab609d
--- /dev/null
+++ b/subprojects/security/src/test/resources/keyrings/valid-with-extra-metadata.keys
@@ -0,0 +1,1071 @@
+pub    5208812E1E4A6DB0
+uid    Gradle Inc. <info@gradle.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQENBFxaxWYBCADI4/gRCJYfXwZYdUoEGlAlCfRgABy90rvebzcs8MKtolAbPVkG
+iqnjftXd28sZhEDx9YJrUfmzspdrYmU7hy1kgV1/WGIcWyTExTH3bqlnaIWnnTxA
+HD0x4NJ2AzmX5VO8LxhqGID+BErrv7uGZvVmJT6trqUIcKeNEq7mzdDJKqTBY4cw
+q+Dm8P0vs4IFTD8q5f1Vr78FmUth2srIBmsIH1wNV1nAUTmQppNBFlCmcvnWTYI1
+0UMcsFFrJ2pFT1yP2AEGUNl4Lgj4hmVHZwX38/lu9pQ7iWtHSLOwZsfuC34/goS+
+ldFt63JqDV7ZaqwAgk7Iq6jbr4pSVsB4VdglABEBAAG0HUdyYWRsZSBJbmMuIDxp
+bmZvQGdyYWRsZS5jb20+iQFOBBMBCAA4FiEEMU/oLlpMU3e8ou3sUgiBLh5KbbAF
+Al6+3fgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQUgiBLh5KbbDr4ggA
+tmAjpH0VPImxcfV+by6itQkY6BQQ0VO9adO9Q/C7JIaiPQ8Rz+2f1SzAtLB/44SZ
+4mseP/Hb5CQEAysRPd3/3GP56GXzXgGURT9/4j/1RPyHIndBd9mmvd9L9+lKWMbZ
+Y7JzPC4Ew/2WPeB3GVsOgn+sMBVHZcVDATqZ5OdfSmuIil7DueWSPUNbFFJkLy7P
+6nkRkk4GMRXSlAjKOLNNQV41cUChqAqf8Yj1sD6cX75YZylsm9voV6JoR2u8lAfT
+zr1VPKdy9xLfDh9Fizth7r0t3OmrUvlEey2IkOoT5cUl/2/jPaz/ypVw4G8nbzW3
+0lyW43RWIIXZC8WAMRdWUQ==
+=WJqH
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    411063A3A0FFD119
+uid    Stian Soiland-Reyes <stain@apache.org>
+uid    Stian Soiland-Reyes <stian@soiland-reyes.com>
+uid    Stian Soiland <stian@soiland.no>
+uid    Stian Soiland-Reyes <s.soilandreyes@uva.nl>
+uid    Stian Soiland-Reyes <stian@s11.no>
+uid    Stian Soiland-Reyes <stian@esciencelab.org.uk>
+uid    Stian Soiland-Reyes <stian@mygrid.org.uk>
+uid    Stian Soiland-Reyes <stian@bioexcel.eu>
+uid    Stian Soiland-Reyes <soiland-reyes@manchester.ac.uk>
+uid    Stian Soiland <stain@soiland.no>
+uid    Stian Soiland-Reyes <soiland-reyes@cs.manchester.ac.uk>
+uid    Stian Soiland <stain@itea.ntnu.no>
+uid    Stian Soiland <stain@linpro.no>
+uid    Stian Soiland <stain@nvg.org>
+uid    Stian Soiland <stain@s11.no>
+uid    Stian Soiland <stain@stud.ntnu.no>
+uid    Stian Soiland <stian.soiland@ntnu.no>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+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
+JnmuvAv6qdZywYG811rK4l+1EImRpW1tBzf5ZnxX238WyRexLMEBD2gw4qOAenoU
+p4UXV1WKftvFn/8L4iuJKkX5ughlbDOM21zitth9amM184fi3YA8W5K/I4hs9+Sr
+2fGi1Sq7tYK1U9Hq4WOyJMj9SZZAcrWLiiiuLCmgij7Jl+sb5+uMIBuLs6yZf9RU
+kfIDlTOfkvYNs9FnW09ZzP8rARNs0acjGkmdWU1BLCj5NIo+PmKrYe3Bey3GO7kf
+grEi8b0lDW0DfwS4uLW8tus0Lq29Jim30cG5ua5ehaQU7x2Xq3cEDeY0m++etlSO
+ivw37Q4s2rXj5ZArZVKN7p0KaNenU5bMk0b+TStrq7Ekz9TB+GMCAsK65CiWBlEJ
+5N5JHdo+mPyZcjbLCZQGLOtw4WBmklou0osFx594osyaNoXYF3m9kIUPPZRoTQmM
+YqqUra/ZZRr7UZwrrDoowiNanL9snX6MpT1YgO6c2t6fFbCvrMc1dUJvd2F4nHmz
+/wqEmp9h4jm3g0ByODUK72XBcSTL3AcitTwbPiRsBveBSrHbsOXWOCn1cXyutCtT
+dGlhbiBTb2lsYW5kLVJleWVzIDxzLnNvaWxhbmRyZXllc0B1dmEubmw+iHgEExEK
+ADgWIQTd3uh2Eun7lfXI2R5BEGOjoP/RGQUCXUK4gwIbAwULCQgHAwUVCgkICwUW
+AgMBAAIeAQIXgAAKCRBBEGOjoP/RGXfYAJ0cE8fQ0tWo0gBIpVgj7IoithlXeQCg
+g8yeYFY2b4e0dPGSZ5FAbnZTMZm0IlN0aWFuIFNvaWxhbmQtUmV5ZXMgPHN0aWFu
+QHMxMS5ubz6IYQQTEQoAIQUCWuhThgIbAwULCQgHAwUVCgkICwUWAgMBAAIeAQIX
+gAAKCRBBEGOjoP/RGWDDAJ4qn0Yh/CurGJauNwyk73rcR5ODGwCeLNZ8OGkljE52
+HoYFRMeS5VfIncqJAjMEEAEIAB0WIQRFvr7slQq9Bc8O9cNQoE0MO2UX8gUCWzeO
+SgAKCRBQoE0MO2UX8hRcEACS+FkQrpAzE8OZuM2AtJQLn2ED0wMO/bB2WjEZx5i/
+7qTcbl8ysKEoZjOT89UmNsdJKwFF+UEamWwK4PIIx5gjGqGLyI3ku+6BPHvrjdCt
+HdOubI6Xbv06VkCGzYZtW0CwgJrg3f2YLP0TjLRO9onJ98quVo/B9gYDPS+DHMeE
+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
+=A4Zh
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    889B89A539677894
+uid    Gradle <marc@gradle.com>
+
+sub    891E4C2D471515FE
+sub    8B2A34A7D4A9B8B3
+sub    8BA78B161991D5F7
+sub    8C40458A5F28CF7B
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQINBFrDOW0BEAC9byiOScTIrOeZ4+nUJHx5nApGe1jT0RBK6QGl0utlqWubJS3W
+oOt11vQPbugBTTX9QkIFRARxs/gqkToJR2UqfJJKE6FdtKStSsVGGesnYM+5Tp0v
+YAx/lNr/DfbJAwOhs0i5AkUxHD2Q/TxD9gpYDlYGxVtUwjCOEAby750wfjxHVHeW
+lBY22t6lqVdfzBiTpsrsQggoTUR0ZT3RL63UfV4YhO889x73f29X6hE0aWjWDmD2
+9+KciCN0QOE79tjstp/bVI1o2kDD2VdcZ6fHd6/xi5oiAb+SaKQADuMloSHgi9Hc
+WbyjWJznz8magFP/VZKWrmkbiHO2R9i5DGdtHFcTltTNADG3S/qqWpCAKbNX7kFe
+bBPQt0xOmakD6m9zAKkN4HcrHWhhdmnxgCV2SZWKy6K2JEtbS6gncDZJWbXeIqu7
+pCgHvOTzRyKmGpBWmATh0W5ypkIL7+8BYC8cS9DPcx0seIK4w+kHyTxTNguTxrL2
+96EfHU5up75lKhKErNbN+09tpiede3ETv423RxnlvIBR2mFONw8i6/osoE+9rPcU
+vE7qFORR6B02Q0l7mpUF7Qs5KJaclqqafr3xgv5eXrnSUPauprfmrXn5MbKZ7+cf
+i2xdrzFQx2srKVW8L2Tco83ROEZwdEl8MD9ABQ3fWhHdSryVOfxyqcuJTQARAQAB
+tBhHcmFkbGUgPG1hcmNAZ3JhZGxlLmNvbT6JAk4EEwEIADgWIQSbj2HKJoZ4q4/g
+K+yIm4mlOWd4lAUCWsM5bQIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRCI
+m4mlOWd4lNuGEACqPYLYLL4uiflGUmbyOsb6U/IojaAsdWjLo4QsZYBSbZX4TrEA
+QefxZJkQPhm3AyW5hAHqj/zIDugCbiXVflYMC92EDHxWZLEnt08uylFTwF8+pn4h
+p7coNjr6zNXGENeMhmaLmkAIOvdCmlLcJcfDtKriVpimtmoeEpSFt1rYGtH3c0+E
+cZ2aMKHL+9GEr45ShADvkCV92XqD+eTlyfGVOtDHoaG/m9tHRqna5U/ynmKCjH+h
+gy4uGIkH2GVNBjJNQRII8YXG1nDd0iqxD1v2BHRyWxXnDf2OamdfGB/s6vUgEJSr
+nfbvApq8gpLbL2IKP7ZBladTaP1SUPpOJEYi3W2OnEFWWjEMO1dc7osDsUNqgAIJ
+RJsWBtEDkcrCZGnqDQDr9H3GLgYH5ERn05fvgJPTNLX7noUYxyjGZJsNXPdetpAh
+sjdSeKDtS0TmPyekTajnhryCxYRc59B0urzJm4+QjNTdPCh1zA7rzwg4dNAHprZr
+crZf+46jQLELV1aO7ACl1gOD7spO+s94oEk7rMtH+G8hIvzSo2xkkdO9xeLJa3Cg
+xVPAbbQaJ4irKByoL/PjAsRUg6YJTsv3CkbZ+5fE6wDk5wFpVHoq8gYFL7/6OWhV
+US21/SIoJ8ric/wvyNxuVXIoQqh+rLmYaH2HPQsW5+keLc3BltZ4afnZC7kCDQRX
+MjTqARAAx+B0iI7LveP3tMxgSW8qxRQhPCEhkyOxwDX1EHi9AqZexG6UDYyBhWxL
+b3T4Xnms+YSebIN3bf6IN30PaAQF5JMeuV5kZ7C8GLnebiOG0tuNFFfh1wwQQFUx
+P5bkHyKegliJkmN3aX0kWVmq15FN7w7WYI93a0q2PA2ywT1OgfXu0Kk6Mt6pmUPf
+1Te0BF0fgMjQ0dmoxGws4ZFsr1w/u1SYPpc4Q583qSMZg5qW4sJrE/SdYvgaBvBa
+zfvmUVV2jhLJQET9Pq76SCukWRvcHQbW9PUSTgwZ7n8qa2D9P/xpnGcjmQm8iwXy
+ajiO71GLu/McgXG8EIG7pcDKKd7DfyIMQkxhWOEeMGh5oHzHWoATaGwr+W4zg33o
+AHipiP05oOLKaXx99fmGoZ7WGfjGQsYGwlkaTg7reiZWfyltNeSPwQI68XI/1qKA
+zMRoKvO0gGBevipf2ZuHjafHpnbiHy0ILmcpgK8s1kIsVsGRX5IrPw1WADoWVmL5
+V2qNUTwDDBxYYbfue/GrSqSWMzHGulsxl+U7i0oqdrV0UeWPJO71jFREGk5UMh0A
+Jd48OtknOFiO7u7wN3nW1vvtcB/JEL6B0yI3VB6RhJBojQ7lYtnZa9Cx9chKFcqG
+WLvrEnkYQSnsx7T7DndNolQXrySF6Z1QPc/xZUxDgX3M+KBSsokAEQEAAYkCHwQY
+AQgACQUCVzI06gIbDAAKCRAVxxwKTguO3e3yEACZ9EsQHCeYkinpyBws4nqQz/We
+t4Y6roQIJg6FcXNbKeGODDFK16rp0rjeYtkVzWB9jnTwgr8jo7f744yNKT6wMYvg
++pxxOvrxQGjAtM3ihXSsh8m2n4Pcs3dHutL+P71zpLLQavNfl5vGsA9tCcOPawQB
+bvqbIMQ2kqPIynmed+p4vXq002dRU/nkRvKCC5YUW11+jykiX2G78TOvC1iXj8h/
+H43i+A32X3gKmw+TMrfysFOYhcxMZ3yJRtBXtdiXTvxmIMqpPZdh6G7oEXeeEgVv
+oLMjQhuYNtmgZN0nnWlny7azBm3/9sNFF3WkU+EGQJEL4Rucf+gFp2BVPHjcXun+
+OLGhZepf5m9NkKehYDUwLVBUvN+9/sPpHDm820Dya2USowrPHkp/dh5Q3YUi+QNs
+J9fnVCUkLg570nm90WOeihZUqCIE8Bqvrz1jgLu3OkYzYo5yXT6Kj+D0A2RKbpyl
+EJNkrCA5eXQNV5t5aOx23r9uoco48uEufRQ1tSYzPOs+jvXLuqIwOa/kPwr9pWDG
+JfuugNK/09KrCxnebD4RXgtCvXYDDZYba42KM732vTbGo1Tq+8cSCVYf9/mBPh5g
+0S0ZOrKnk81O5iQjsYrJsGAQ+0uNnKS+9Yk2f8/op2ZyzFC2FfdedsIhGrYsMvsD
+gY2ivYNypPI7DdyPLbkCDQRaylvSARAAnQG636wliEOLkXN662OZS6Qz2+cFltCW
+boq9oX9FnA1PHnTY2cAtwS214RfWZxkjg6Stau+d1Wb8TsF/SUN3eKRSyrkAxlX0
+v552vj3xmmfNsslQX47e6aEWZ0du0M8jw7/f7Qxp0InkBfpQwjSg4ECoH4cA6dOF
+JIdxBv8dgS4K90HNuIHa+QYfVSVMjGwOjD9St6Pwkbg1sLedITRo59Bbv0J14nE9
+LdWbCiwNrkDr24jTewdgrDaCpN6msUwcH1E0nYxuKAetHEi2OpgBhaY3RQ6QPQB6
+NywvmD0xRllMqu4hSp70pHFtm8LvJdWOsJ5we3KijHuZzEbBVTTl+2DhNMI0KMoh
++P/OmyNOfWD8DL4NO3pVv+mPDZn82/eZ3XY1/oSQrpyJaCBjRKasVTtfiA/FgYqT
+ml6qZMjy6iywg84rLezELgcxHHvjhAKd4CfxyuCCgnGT0iRLFZKw44ZmOUqPDkyv
+GRddIyHag1K7UaM/2UMn6iPMy7XWcaFiH5Huhz43SiOdsWGuwNk4dDxHdxmzSjps
+0H5dkfCciOFhEc54AFcGEXCWHXuxVqIq/hwqTmVl1RY+PTcQUIOfx36WW1ixJQf8
+TpVxUbooK8vr1jOFF6khorDXoZDJNhI2VKomWp8Y38EPGyiUPZNcnmSiezx+MoQw
+AbeqjFMKG7UAEQEAAYkCNgQYAQgAIBYhBP9uLAAZSMXy84sMw4WRH0JexhtRBQJa
+ylvSAhsMAAoJEIWRH0JexhtR0LEP/RvYGlaokoosAYI5vNORAiYEc1Ow2McPI1Za
+fHhcVxZhlwF48dAC2bYcasDX/PbEdcD6pwo8ZU8eI8Ht0VpRQxeV/sP01m2YEpAu
+yZ6jI7IQQCGcwQdN4qzQJxMAASl9JlplH2NniXV1/994FOtesT59ePMyexm57lzh
+YXP1PGcdt8dH37r6z3XQu0lHRG/KBn7YhyA3zwJcno324KdBRJiynlc7uqQq+Zpt
+U9fR1+Nx0uoWZoFMsrQUmY34aAOPJu7jGMTG+VseMH6vDdNhhZs9JOlD/e/VaF7N
+yadjOUD4j/ud7c0z2EwqjDKMFTHGbIdawT/7jartT+9yGUO+EmScBMiMuJUTdCP4
+YDh3ExRdqefEBff3uE/rAP73ndNYdIVq9U0gY0uSNCD9JPfj4aCN52y9a2pS7Dg7
+KB/Z8SH1R9IWP+t0HvVtAILdsLExNFTedJGHRh7uaC7pwRz01iivmtAKYICzruql
+Jie/IdEFFK/sus6fZek29odTrQxx42HGHO5GCNyEdK9jKVAeuZ10vcaNbuBpiP7s
+f8/BsiEU4wHE8gjFeUPRiSjnERgXQwfJosLgf/K/SShQn2dCkYZRNF+SWJ6Z2tQx
+cW5rpUjtclV/bRVkUX21EYfwA6SMB811mI7AVy8WPXCe8La72ukmaxEGbpJ8mdzS
+2PJko7mmuQGNBGCUUBoBDADG8wfIdEG4YuizDkJePxM/5GXVgAuUOyxtulCGkpik
+AHOzCzUQLUhKkh9XkP7fLJpCL/eFj9Gcjsv65ReqJ4mBJxj6970RiayLQSw21G+L
+rwKbnpVsvmbkOdIpMR380WCUdME9HamEACwLd4dD0i2qPm9AJNPP7YJPHdTFvbGb
+FQBKGUrEnKbSurVUiMx3C+XmTxrdKU2PJ/If6sNUaI8+iND251DLTN8AsW0v+HBI
+V0kZTSunm9E/cDHE7vQ6pBVdxDliqQ/yIjIb/3FH6fu+/o+VgnUI7EhK5dptHpIe
+6q0POyyfKHhTxndUnNOTsJdJCdhuxsOhUO8/3lZy9MU3QA5yXfxIP7xb4meFH6mw
+iWdaCLk1mttwJ47nk7CQRH8WA1TddU4qDvghmJ8rJntEwuOay4dYqWMlHPIOeWAR
+UDE0cBTtzMjCRpFvFxm9KInQFxvqqMXl3SLqU4/azXOPNwj5lJTHthvzebULDVXI
+fyVd7iTyN1oxh+pB8iDIgQkAEQEAAYkBvAQYAQoAJhYhBKVYMBIK7Rd+GqT2akMb
+j4BAOn3BBQJglFAaAhsMBQkDwmcAAAoJEEMbj4BAOn3BE5wL/A/bBcYktpn2gNVq
++/XP0hvAS4IflVmYlinh7d3au43fIMEa96TupKOk1fxHpRvM/uTBmkRFdCwDryk5
+ya/6nUbJEkZlexefpmn3N0gknh+x+DD7eh4Te5xhY+m8oZVVA85TXZYCUpNK1kDL
++O9mfOhdwc6i/hqZUeRvn+2jtdNXAT0kfBtpcsscLA+ksx2dUDC7oXIkpgDpa6Wb
+MWee4aKjLMnAVKhsWTXUxDgMh35L58roGooS5ghVmNDiTjLZTBwVYQQD2ia6RgPH
+WLpbPa//ulgcu8S/pDe5v4tNAiuH+SE8r5/wp6EueMOA+Kz4k0VlxdAryUKM7jGU
+kkM6W+ZLYMxUPo5/5yQv7GMrLpNNdYgwLjK8iMUKjXVlT6p10oQ8h1oBUVvFaQGd
+8KCe7MYCJ7syx4sA1POkojzMRhS95bDxy49RaW1HibH35XvpQrq4qFj1lUoHXXVs
+Ai35aML3XqvzXw+0jIBoMK58iKoL1HmPjTn5HeQFdN4BvLt1BLkBjQRgA8eiAQwA
+uC4Z9laL4sRX8FTseTzd5/8AqBKkgtrZjW5onrse1hWpkjeB42qfhVrfUorkpGY9
+N0xo7jZT7PhXuOEB1WRcJPHA11Q4166WkHRDv7IwPGAQr6LsJAAlZYkV2d3BXoWo
+S4ATCH1jyXaxKT/jNGBazs+NqprhypL6X2xOIqKozehjTMfD1cFzFzoaZvD+G9qd
+k0w7qikUIla0Y3ADswtMLH32mszw9g0ddFSimmWQ8scVcaalt9k9ATX7zMJKmYaY
+i6fWsH/Le13DhJgQMjjh1BeUguIPr6pRoBZ/5xJxJ7OKIRk4pk6h7BImGMKTCONI
+Cf41i4kGsZMoRb2XvLDgSNs9gYKpN9+J7TYTeqofBxxQLH6cVplBPoNCkJun6scY
+JLWAepr4u0K5RTnU7y9iigiTTFeVxbSjuxIEzLk9gVKD1hsbtkLVmkxMljqJG5El
+3I7qu7eM2c1ufo22BFjHom1CmtWdoai56nxG5zv1WDsMRJukaXbDwbpSkb45rj09
+ABEBAAGJAbwEGAEIACYWIQSFaclcrcUIsJ/pDzACIW7YESENqgUCYAPHogIbDAUJ
+A8JnAAAKCRACIW7YESENqpGYC/0QNoVAXMkCa0Iei/kGdzZNLKpiG0nZIJGuml9T
+7eMyp0QQXzenOahCGhna4QQvSBERUZb9HzP/0xY93C8FEXv7Ns972XdeOvYjpOLG
+6euRwWLD//c5Ah7siSgUJ7CFPBHjr9mnZXzYjhvXT0eJlb96j0rBuSblG/NXu1oE
+JPySqP7vkK2ZZsHNoGfSoGlGtushYtUP568KMzz4LsnOfSLnkOc9Hh0qydipY+oc
+fQQhh7tLUzFsMbG80yWw4/2JVicTnTosdl4J9WyI3Xuqa423XEAC25dS0aQNeDa4
+lpfmOOyj5ViJISdutlVC3zmtkpXExUXqb+AcsNDOuulUhVjw7KpKX7xUXJM+LSg5
+7lfyGHiLejDHvPAXBSfzFxT9ZDxO92MhvR7JqP1Z0SvZ/yZ1RAidKaNJs3o1Dk/W
+buxnRYjyf4URhfUVeH8tykNDIMJrgY4uKjJu0S9RuzG1PVw85w5f6UDZlJ01gGvt
+T81JFrizhvS9t0HoPbDcDhG5iVE=
+=GFav
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    B0F3710FA64900E7
+uid    ?amonn McManus <eamonn@mcmanus.net>
+
+sub    B15AD8EFBD28D3CB
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+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
+=D4PY
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub    7DC3076FE22D4F88
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQINBFV3l4gBEADMcF99pe592aoACKLIBm/sZMrtXdOmOzGID51iq1bj4xjgqlRO
+xVZXargCeRovcHSdE78BzhixtKSwyUxrfABV8+9ufk3AqTy+bNRwuPZfe/n+hcFC
+TenyPEGGxvFlr6jGNev/ifRurqKO9AS67W0AlojRqtTLlmsm4FfWDestf6HoqrkU
+6EFnEe5tQK4w96NqLoPC0lHgD1Ig9ztM+D48B7JAA7qxs7saEfUjDMy5TnMSltm5
+sLttNje7Xdgb4hA+oZ3/P9tdMIPgO7ejiYy99lpyjoqvyR8bCcsr1cgdGEkeuLWi
+oEoj05R81vbKps6AqfZA8XUHeJpuWpF5L6yTTxEwvnYZqLCDzDeeoJ/cdwYthVlQ
+4KPSXTa3b3bdaHSEkaSeQ6L9+StYtUT/zN11L57JxnEAtKlGuE8Ud2fnbJizHfsH
+MnvysrM/vCvpa/EBjSAK3kszDXSnqZn4iIWA1jHlIausNkEaALa2h0nwMAxsM00R
+YtPEmY/cjHHeiW4Rf+/vNZ7x4tty7Jhvm7RwnMVviuLu19z4SY8sxegRgawg/PE5
+BB5d+QkFPE9mxUV6qQhzHWBkVT99AIaXIfYfwT/UELHSj5M4r+DAYP47mVDMptac
+05euVDR2t7XmaqXwC15FStr5lFJcrZzdtNAsmtyU1FLMJbhU2mnGN28LQQARAQAB
+=di/n
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/subprojects/security/src/testFixtures/groovy/org/gradle/security/fixtures/SigningFixtures.groovy b/subprojects/security/src/testFixtures/groovy/org/gradle/security/fixtures/SigningFixtures.groovy
index 06d56f7..d2e0348 100644
--- a/subprojects/security/src/testFixtures/groovy/org/gradle/security/fixtures/SigningFixtures.groovy
+++ b/subprojects/security/src/testFixtures/groovy/org/gradle/security/fixtures/SigningFixtures.groovy
@@ -20,9 +20,11 @@
 import org.bouncycastle.bcpg.CompressionAlgorithmTags
 import org.bouncycastle.bcpg.HashAlgorithmTags
 import org.bouncycastle.bcpg.PublicKeyAlgorithmTags
+import org.bouncycastle.bcpg.attr.ImageAttribute
 import org.bouncycastle.bcpg.sig.KeyFlags
 import org.bouncycastle.crypto.generators.RSAKeyPairGenerator
 import org.bouncycastle.crypto.params.RSAKeyGenerationParameters
+import org.bouncycastle.openpgp.PGPKeyPair
 import org.bouncycastle.openpgp.PGPKeyRingGenerator
 import org.bouncycastle.openpgp.PGPObjectFactory
 import org.bouncycastle.openpgp.PGPPublicKey
@@ -31,7 +33,11 @@
 import org.bouncycastle.openpgp.PGPSecretKey
 import org.bouncycastle.openpgp.PGPSecretKeyRing
 import org.bouncycastle.openpgp.PGPSecretKeyRingCollection
+import org.bouncycastle.openpgp.PGPSignature
+import org.bouncycastle.openpgp.PGPSignatureGenerator
 import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator
+import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector
+import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator
 import org.bouncycastle.openpgp.PGPUtil
 import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRingCollection
 import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRingCollection
@@ -189,13 +195,27 @@
         signatureSubpacketGenerator.setPreferredSymmetricAlgorithms(false, PublicKeyAlgorithmTags.DSA)
         signatureSubpacketGenerator.setPreferredHashAlgorithms(false, HashAlgorithmTags.SHA512)
         signatureSubpacketGenerator.setPreferredCompressionAlgorithms(false, CompressionAlgorithmTags.ZIP)
+        signatureSubpacketGenerator.addSignerUserID(false, "www")
+
+        PGPUserAttributeSubpacketVectorGenerator userAttributes = new PGPUserAttributeSubpacketVectorGenerator()
+        userAttributes.setImageAttribute(ImageAttribute.JPEG, new byte[100]);
+        PGPUserAttributeSubpacketVector userAttributesVector = userAttributes.generate()
+
+        PGPSignatureGenerator sGen = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PublicKeyAlgorithmTags.RSA_GENERAL, HashAlgorithmTags.SHA1));
+
+        sGen.init(PGPSignature.POSITIVE_CERTIFICATION, signingKeyPair.privateKey)
+
+        PGPSignature signature = sGen.generateCertification(userAttributesVector, signingKeyPair.publicKey)
+        PGPPublicKey keyWithAttributes = PGPPublicKey.addCertification(signingKeyPair.publicKey, userAttributesVector, signature);
+
+        PGPKeyPair keyPair = new PGPKeyPair(keyWithAttributes, signingKeyPair.privateKey);
 
         def encryptionSubpacketGenerator = new PGPSignatureSubpacketGenerator()
         encryptionSubpacketGenerator.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)
 
         def generator = new PGPKeyRingGenerator(
             PGPPublicKey.RSA_SIGN,
-            signingKeyPair,
+            keyPair,
             userId,
             new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1),
             signatureSubpacketGenerator.generate(),
diff --git a/subprojects/signing/build.gradle.kts b/subprojects/signing/build.gradle.kts
index 83ad9d1..15b16df 100644
--- a/subprojects/signing/build.gradle.kts
+++ b/subprojects/signing/build.gradle.kts
@@ -23,6 +23,10 @@
     implementation(libs.guava)
     implementation(libs.inject)
 
+    testFixturesImplementation(project(":base-services")) {
+        because("Required to access org.gradle.internal.SystemProperties")
+    }
+
     testImplementation(project(":ivy"))
     testImplementation(testFixtures(project(":core")))
 
@@ -42,4 +46,8 @@
     excludePatterns.add("org/gradle/plugins/signing/**")
 }
 
-integTest.usesJavadocCodeSnippets = true
+tasks {
+    integTest {
+        usesJavadocCodeSnippets = true
+    }
+}
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
deleted file mode 100644
index 9ce890c..0000000
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/GpgCmdFixture.groovy
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2017 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.plugins.signing
-
-import org.gradle.internal.SystemProperties
-import org.gradle.test.fixtures.file.TestFile
-
-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.parseInt("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 GpgCmdAndVersion getAvailableGpg() {
-        return tryRun("gpg")
-    }
-
-    static GpgCmdAndVersion tryRun(String cmd) {
-        try {
-            String output = "${cmd} --version".execute().text
-            return new GpgCmdAndVersion(executable: cmd, version: (output =~ GPG_VERSION_REGEX)[0][1].toInteger())
-        } catch (Exception ignored) {
-            return null
-        }
-    }
-
-    static setupGpgCmd(TestFile buildDir) {
-        def gpgHomeSymlink = prepareGnupgHomeSymlink(buildDir.file('gnupg-home'))
-        Properties properties = new Properties()
-        properties.load(buildDir.file('gradle.properties').newInputStream())
-        GpgCmdAndVersion client = getAvailableGpg()
-        assert client
-        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(), '')
-
-        return gpgHomeSymlink
-    }
-
-    static cleanupGpgCmd(def gpgHomeSymlink) {
-        Files.deleteIfExists(gpgHomeSymlink)
-    }
-
-    static prepareGnupgHomeSymlink(File gpgHomeInTest) {
-        // We have to do this, otherwise gpg will complain: can't connect to the agent: File name too long
-        // it's limited to 108 chars due to http://man7.org/linux/man-pages/man7/unix.7.html
-        Path tmpLink = Paths.get(SystemProperties.getInstance().getJavaIoTmpDir()).resolve(createRandomPrefix())
-        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/SigningConfigurationsIntegrationSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningConfigurationsIntegrationSpec.groovy
index 059aaa4..4746952 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningConfigurationsIntegrationSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningConfigurationsIntegrationSpec.groovy
@@ -136,4 +136,26 @@
         and:
         file("build", "libs", "sign-1.0.jar.asc").text
     }
+
+    def "signatures configuration is deprecated for resolution and will warn if resolved, but not fail"() {
+        buildFile.text = """
+            plugins {
+                id 'signing'
+            }
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                signatures 'org.apache.commons:commons-lang3:3.9'
+            }
+
+            assert configurations.signatures.isDeprecatedForResolution()
+            configurations.signatures.files
+        """
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("The signatures configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+        executer.expectDocumentedDeprecationWarning("The signatures configuration has been deprecated for resolution. This will fail with an error in Gradle 9.0. Please resolve another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations")
+        succeeds("help")
+    }
 }
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 a03b4ac..22a2c81 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
@@ -17,9 +17,10 @@
 
 
 import org.gradle.plugins.signing.signatory.internal.gnupg.GnupgSignatoryProvider
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.SigningTestPreconditions
 
-@Requires(adhoc = { GpgCmdFixture.getAvailableGpg() != null })
+@Requires(SigningTestPreconditions.GpgAvailable)
 class SigningConfigurationsWithGpgCmdIntegrationSpec extends SigningConfigurationsIntegrationSpec {
     SignMethod getSignMethod() {
         return SignMethod.GPG_CMD
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningIntegrationSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningIntegrationSpec.groovy
index 79f6816..ac819801 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningIntegrationSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningIntegrationSpec.groovy
@@ -18,6 +18,7 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.test.fixtures.GpgCmdFixture
 import org.gradle.test.fixtures.file.TestFile
 import org.junit.Rule
 
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 3c5b973..ee3107e 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
@@ -19,8 +19,10 @@
 import org.gradle.integtests.fixtures.AbstractSampleIntegrationTest
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.UsesSample
+import org.gradle.test.fixtures.GpgCmdFixture
 import org.gradle.test.fixtures.maven.MavenFileRepository
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.SigningTestPreconditions
 import org.junit.Rule
 
 class SigningSamplesSpec extends AbstractSampleIntegrationTest {
@@ -51,7 +53,7 @@
     }
 
     @UsesSample('signing/gnupg-signatory')
-    @Requires(adhoc = { GpgCmdFixture.getAvailableGpg() != null })
+    @Requires(SigningTestPreconditions.GpgAvailable)
     def "use gnupg signatory with dsl #dsl"() {
         setup:
         def projectDir = sample.dir.file(dsl)
@@ -61,7 +63,7 @@
         inDirectory(projectDir)
 
         and:
-        run "signArchives"
+        run "signRuntimeElements"
 
         then:
         projectDir.file("build/libs/gnupg-signatory-1.0.jar.asc").assertExists()
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 9aa06a3..bc2f9a7 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
@@ -19,7 +19,8 @@
 import org.gradle.plugins.signing.signatory.internal.gnupg.GnupgSignatoryProvider
 import org.gradle.plugins.signing.signatory.pgp.PgpSignatoryProvider
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.SigningTestPreconditions
 
 import static org.gradle.plugins.signing.SigningIntegrationSpec.SignMethod.GPG_CMD
 import static org.gradle.plugins.signing.SigningIntegrationSpec.SignMethod.OPEN_GPG
@@ -83,7 +84,7 @@
         skipped(":signJar", ":signJavadocJar", ":signSourcesJar")
     }
 
-    @Requires(adhoc = { GpgCmdFixture.getAvailableGpg() != null })
+    @Requires(SigningTestPreconditions.GpgAvailable)
     def "out-of-date when signatory changes"() {
         given:
         def originalSignMethod = signMethod
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 050fba0..815cc67 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
@@ -15,9 +15,10 @@
  */
 package org.gradle.plugins.signing
 
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.SigningTestPreconditions
 
-@Requires(adhoc = { GpgCmdFixture.getAvailableGpg() != null })
+@Requires(SigningTestPreconditions.GpgAvailable)
 class SigningTasksWithGpgCmdIntegrationSpec extends SigningTasksIntegrationSpec {
     SignMethod getSignMethod() {
         return SignMethod.GPG_CMD
diff --git a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningWithGpgCmdIntegrationSpec.groovy b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningWithGpgCmdIntegrationSpec.groovy
index dcd4f92..c7f8c81 100644
--- a/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningWithGpgCmdIntegrationSpec.groovy
+++ b/subprojects/signing/src/integTest/groovy/org/gradle/plugins/signing/SigningWithGpgCmdIntegrationSpec.groovy
@@ -17,9 +17,10 @@
 package org.gradle.plugins.signing
 
 
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.SigningTestPreconditions
 
-@Requires(adhoc = { GpgCmdFixture.getAvailableGpg() != null })
+@Requires(SigningTestPreconditions.GpgAvailable)
 class SigningWithGpgCmdIntegrationSpec extends SigningIntegrationSpec {
 
     SignMethod getSignMethod() {
diff --git a/subprojects/signing/src/main/java/org/gradle/plugins/signing/SigningExtension.java b/subprojects/signing/src/main/java/org/gradle/plugins/signing/SigningExtension.java
index ec6b4b9..33040cf 100644
--- a/subprojects/signing/src/main/java/org/gradle/plugins/signing/SigningExtension.java
+++ b/subprojects/signing/src/main/java/org/gradle/plugins/signing/SigningExtension.java
@@ -24,10 +24,12 @@
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.artifacts.PublishArtifact;
 import org.gradle.api.internal.ConventionMapping;
 import org.gradle.api.internal.IConventionAware;
+import org.gradle.api.internal.artifacts.configurations.ConfigurationRolesForMigration;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
+import org.gradle.api.internal.project.ProjectInternal;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.publish.Publication;
 import org.gradle.api.publish.PublicationArtifact;
@@ -154,12 +156,13 @@ public boolean isRequired() {
     /**
      * Provides the configuration that signature artifacts are added to. Called once during construction.
      */
+    @SuppressWarnings("deprecation")
     protected Configuration getDefaultConfiguration() {
-        final ConfigurationContainer configurations = project.getConfigurations();
+        final RoleBasedConfigurationContainerInternal configurations = ((ProjectInternal) project).getConfigurations();
         final Configuration configuration = configurations.findByName(DEFAULT_CONFIGURATION_NAME);
         return configuration != null
             ? configuration
-            : configurations.create(DEFAULT_CONFIGURATION_NAME);
+            : configurations.createWithRole(DEFAULT_CONFIGURATION_NAME, ConfigurationRolesForMigration.LEGACY_TO_CONSUMABLE);
     }
 
     /**
@@ -321,7 +324,7 @@ public List<Sign> sign(Task... tasks) {
     /**
      * Creates signing tasks that sign {@link Configuration#getAllArtifacts() all artifacts} of the given configurations.
      *
-     * <p>The created tasks will be named "sign<i>&lt;configuration name capitalized&gt;</i>". That is, given a configuration with the name "archives" the created task will be named "signArchives".
+     * <p>The created tasks will be named "sign<i>&lt;configuration name capitalized&gt;</i>". That is, given a configuration with the name "conf" the created task will be named "signConf".
      *
      * The signature artifacts for the created tasks are added to the {@link #getConfiguration() configuration} for this settings object.
      *
diff --git a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignOperationSpec.groovy b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignOperationSpec.groovy
index 72e2632..437f860 100644
--- a/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignOperationSpec.groovy
+++ b/subprojects/signing/src/test/groovy/org/gradle/plugins/signing/SignOperationSpec.groovy
@@ -85,4 +85,4 @@
         [output1, output2] == operation.signatures*.file.toList()
     }
     
-}
\ No newline at end of file
+}
diff --git a/subprojects/signing/src/testFixtures/groovy/org/gradle/test/fixtures/GpgCmdFixture.groovy b/subprojects/signing/src/testFixtures/groovy/org/gradle/test/fixtures/GpgCmdFixture.groovy
new file mode 100644
index 0000000..c699615
--- /dev/null
+++ b/subprojects/signing/src/testFixtures/groovy/org/gradle/test/fixtures/GpgCmdFixture.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 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.test.fixtures
+
+import org.gradle.internal.SystemProperties
+import org.gradle.test.fixtures.file.TestFile
+
+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.parseInt("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 GpgCmdAndVersion getAvailableGpg() {
+        return tryRun("gpg")
+    }
+
+    static GpgCmdAndVersion tryRun(String cmd) {
+        try {
+            String output = "${cmd} --version".execute().text
+            return new GpgCmdAndVersion(executable: cmd, version: (output =~ GPG_VERSION_REGEX)[0][1].toInteger())
+        } catch (Exception ignored) {
+            return null
+        }
+    }
+
+    static setupGpgCmd(TestFile buildDir) {
+        def gpgHomeSymlink = prepareGnupgHomeSymlink(buildDir.file('gnupg-home'))
+        Properties properties = new Properties()
+        properties.load(buildDir.file('gradle.properties').newInputStream())
+        GpgCmdAndVersion client = getAvailableGpg()
+        assert client
+        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(), '')
+
+        return gpgHomeSymlink
+    }
+
+    static cleanupGpgCmd(def gpgHomeSymlink) {
+        Files.deleteIfExists(gpgHomeSymlink)
+    }
+
+    static prepareGnupgHomeSymlink(File gpgHomeInTest) {
+        // We have to do this, otherwise gpg will complain: can't connect to the agent: File name too long
+        // it's limited to 108 chars due to http://man7.org/linux/man-pages/man7/unix.7.html
+        Path tmpLink = Paths.get(SystemProperties.getInstance().getJavaIoTmpDir()).resolve(createRandomPrefix())
+        return Files.createSymbolicLink(tmpLink, gpgHomeInTest.toPath())
+    }
+}
+
+class GpgCmdAndVersion {
+    // gpg or gpg2
+    String executable
+    // 1 or 2
+    int version
+}
diff --git a/subprojects/signing/src/testFixtures/groovy/org/gradle/test/preconditions/SigningTestPreconditions.groovy b/subprojects/signing/src/testFixtures/groovy/org/gradle/test/preconditions/SigningTestPreconditions.groovy
new file mode 100644
index 0000000..3182818
--- /dev/null
+++ b/subprojects/signing/src/testFixtures/groovy/org/gradle/test/preconditions/SigningTestPreconditions.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.test.preconditions
+
+import org.gradle.test.fixtures.GpgCmdFixture
+import org.gradle.test.precondition.TestPrecondition
+
+class SigningTestPreconditions {
+
+    static final class GpgAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return GpgCmdFixture.getAvailableGpg() != null
+        }
+    }
+
+}
diff --git a/subprojects/smoke-test/build.gradle.kts b/subprojects/smoke-test/build.gradle.kts
index cfa5b83..c338152 100644
--- a/subprojects/smoke-test/build.gradle.kts
+++ b/subprojects/smoke-test/build.gradle.kts
@@ -20,6 +20,8 @@
 val smokeTestDistributionRuntimeOnly: Configuration by configurations
 
 dependencies {
+    testFixturesImplementation(project(":internal-integ-testing"))
+
     smokeTestImplementation(project(":base-services"))
     smokeTestImplementation(project(":core-api"))
     smokeTestImplementation(project(":test-kit"))
@@ -57,7 +59,7 @@
     val santaTracker by registering(RemoteProject::class) {
         remoteUri = santaGitUri
         // Pinned from branch main
-        ref = "180b7a91054d5fe5b617543bb2f74a3819537b7b"
+        ref = "70b2fec935b82d8783579290512690fe2eca8884"
     }
 
     val gradleBuildCurrent by registering(RemoteProject::class) {
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractAndroidSantaTrackerSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractAndroidSantaTrackerSmokeTest.groovy
index 14aa6b5..b0469ad 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractAndroidSantaTrackerSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractAndroidSantaTrackerSmokeTest.groovy
@@ -18,6 +18,7 @@
 
 import org.gradle.api.JavaVersion
 import org.gradle.integtests.fixtures.daemon.DaemonLogsAnalyzer
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.scan.config.fixtures.ApplyGradleEnterprisePluginFixture
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
@@ -26,6 +27,16 @@
 import org.gradle.testkit.runner.internal.ToolingApiGradleExecutor
 import org.junit.Rule
 
+/**
+ * For these tests to run you need to set ANDROID_SDK_ROOT to your Android SDK directory
+ *
+ * https://developer.android.com/studio/releases/build-tools.html
+ * https://developer.android.com/studio/releases/gradle-plugin.html
+ * https://androidstudio.googleblog.com/
+ *
+ * To run your tests against all AGP versions from agp-versions.properties, use higher version of java by setting -PtestJavaVersion=<version>
+ * See {@link org.gradle.integtests.fixtures.versions.AndroidGradlePluginVersions#assumeCurrentJavaVersionIsSupportedBy() assumeCurrentJavaVersionIsSupportedBy} for more details
+ */
 class AbstractAndroidSantaTrackerSmokeTest extends AbstractSmokeTest {
 
     protected static final Iterable<String> TESTED_AGP_VERSIONS = TestedVersions.androidGradle.versions
@@ -34,7 +45,7 @@
     TestNameTestDirectoryProvider temporaryFolder = new TestNameTestDirectoryProvider(getClass())
     TestFile homeDir
 
-    String kotlinVersion = TestedVersions.kotlin.latestStable()
+    String kotlinVersion = KOTLIN_VERSIONS.latestStable
 
     def setup() {
         homeDir = temporaryFolder.createDir("test-kit-home")
@@ -51,13 +62,75 @@
     }
 
     protected BuildResult buildLocation(File projectDir, String agpVersion) {
-        return runnerForLocation(projectDir, agpVersion, "assembleDebug").build()
+        return runnerForLocation(projectDir, agpVersion, "assembleDebug").deprecations(SantaTrackerDeprecations) {
+            expectBuildIdentifierNameDeprecation()
+            if (GradleContextualExecuter.notConfigCache) {
+                expectProjectConventionDeprecationWarning(agpVersion)
+                expectAndroidConventionTypeDeprecationWarning(agpVersion)
+                expectBasePluginConventionDeprecation(agpVersion)
+                expectConfigUtilDeprecationWarning(agpVersion)
+                expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
+            }
+        }.build()
+    }
+
+    protected BuildResult buildUpToDateLocation(File projectDir, String agpVersion) {
+        return runnerForLocation(projectDir, agpVersion, "assembleDebug").deprecations(SantaTrackerDeprecations) {
+            if (GradleContextualExecuter.notConfigCache) {
+                expectProjectConventionDeprecationWarning(agpVersion)
+                expectAndroidConventionTypeDeprecationWarning(agpVersion)
+                expectBasePluginConventionDeprecation(agpVersion)
+                expectConfigUtilDeprecationWarning(agpVersion)
+                expectBuildIdentifierNameDeprecation()
+                expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
+            } else {
+                expectBuildIdentifierNameDeprecation()
+                // TODO - this is here because AGP 7.4.x reads build/generated/source/kapt/debug at configuration time
+                if (agpVersion.startsWith("7.4")){
+                    expectConfigUtilDeprecationWarning(agpVersion)
+                }
+                // TODO - this is here because AGP > 7.3 reads build/generated/source/kapt/debug at configuration time
+                if (!agpVersion.startsWith("7.3")) {
+                    expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
+                }
+            }
+        }.build()
+    }
+
+    protected BuildResult buildLocationMaybeExpectingWorkerExecutorAndConventionDeprecation(File location, String agpVersion) {
+        return runnerForLocation(location, agpVersion,"assembleDebug")
+            .deprecations(SantaTrackerDeprecations) {
+                expectAndroidWorkerExecutionSubmitDeprecationWarning(agpVersion)
+                expectProjectConventionDeprecationWarning(agpVersion)
+                expectAndroidConventionTypeDeprecationWarning(agpVersion)
+                expectBasePluginConventionDeprecation(agpVersion)
+                expectConfigUtilDeprecationWarning(agpVersion)
+                expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
+                expectBuildIdentifierNameDeprecation()
+            }.build()
     }
 
     protected BuildResult buildLocationMaybeExpectingWorkerExecutorDeprecation(File location, String agpVersion) {
         return runnerForLocation(location, agpVersion,"assembleDebug")
             .deprecations(SantaTrackerDeprecations) {
                 expectAndroidWorkerExecutionSubmitDeprecationWarning(agpVersion)
+                expectBuildIdentifierNameDeprecation()
+            }.build()
+    }
+
+    protected BuildResult buildLocationMaybeExpectingWorkerExecutorAndConfigUtilDeprecation(File location, String agpVersion) {
+        return runnerForLocation(location, agpVersion,"assembleDebug")
+            .deprecations(SantaTrackerDeprecations) {
+                expectAndroidWorkerExecutionSubmitDeprecationWarning(agpVersion)
+                // TODO - this is here because AGP 7.4.x reads build/generated/source/kapt/debug at configuration time
+                if (agpVersion.startsWith("7.4")){
+                    expectConfigUtilDeprecationWarning(agpVersion)
+                }
+                expectBuildIdentifierNameDeprecation()
+                if (!agpVersion.startsWith("7.3")) {
+                    // TODO - this is here because AGP > 7.3 reads build/generated/source/kapt/debug at configuration time
+                    expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
+                }
             }.build()
     }
 
@@ -68,15 +141,23 @@
     }
 
     protected BuildResult cleanLocation(File projectDir, String agpVersion) {
-        return runnerForLocation(projectDir, agpVersion, "clean").build()
+        return runnerForLocation(projectDir, agpVersion, "clean").deprecations(SantaTrackerDeprecations) {
+            expectProjectConventionDeprecationWarning(agpVersion)
+            expectAndroidConventionTypeDeprecationWarning(agpVersion)
+            expectBasePluginConventionDeprecation(agpVersion)
+            expectConfigUtilDeprecationWarning(agpVersion)
+        }.build()
     }
 
     protected SmokeTestGradleRunner runnerForLocation(File projectDir, String agpVersion, String... tasks) {
         def runnerArgs = [[
             // TODO: the versions of KGP we use still access Task.project from a cacheIf predicate
-            "-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true",
+            // A workaround for this has been added to TaskExecutionAccessCheckers;
+            // TODO once we remove it, uncomment the flag below or upgrade AGP
+            // "-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true",
             "-DagpVersion=$agpVersion",
             "-DkotlinVersion=$kotlinVersion",
+            "-DjavaVersion=${AGP_VERSIONS.getMinimumJavaVersionFor(agpVersion).majorVersion}",
             "--stacktrace"],
         tasks].flatten()
         def runner = runner(*runnerArgs)
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractGradleBuildConfigurationCacheSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractGradleBuildConfigurationCacheSmokeTest.groovy
index 5a524b9..4a9b427 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractGradleBuildConfigurationCacheSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractGradleBuildConfigurationCacheSmokeTest.groovy
@@ -21,18 +21,21 @@
 import org.gradle.initialization.StartParameterBuildOptions.ConfigurationCacheProblemsOption
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.TestExecutionResult
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.SmokeTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 /**
  * Smoke test building gradle/gradle with configuration cache enabled.
  *
  * gradle/gradle requires Java >=9 and <=11 to build, see {@link AbstractGradleceptionSmokeTest.GradleBuildJvmSpec}.
  */
-@Requires(value = TestPrecondition.JDK9_OR_LATER, adhoc = {
-    GradleContextualExecuter.isNotConfigCache() && GradleBuildJvmSpec.isAvailable()
-})
+@Requires([
+    UnitTestPreconditions.Jdk9OrLater,
+    IntegTestPreconditions.NotConfigCached,
+    SmokeTestPreconditions.GradleBuildJvmSpecAvailable
+])
 abstract class AbstractGradleBuildConfigurationCacheSmokeTest extends AbstractGradleceptionSmokeTest {
     def setup() {
         // Generate Kotlin DSL sources once so they are included as :kotlin-dsl:compileKotlin inputs.
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractGradleceptionSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractGradleceptionSmokeTest.groovy
index 2beb929..dea46e7 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractGradleceptionSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractGradleceptionSmokeTest.groovy
@@ -19,19 +19,22 @@
 import org.gradle.api.JavaVersion
 import org.gradle.api.specs.Spec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
 import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.SmokeTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testkit.runner.BuildResult
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
 import java.text.SimpleDateFormat
 
-@Requires(value = TestPrecondition.JDK9_OR_LATER, adhoc = {
-    GradleContextualExecuter.isNotConfigCache() && GradleBuildJvmSpec.isAvailable()
-})
+@Requires([
+    UnitTestPreconditions.Jdk9OrLater,
+    IntegTestPreconditions.NotConfigCached,
+    SmokeTestPreconditions.GradleBuildJvmSpecAvailable
+])
 abstract class AbstractGradleceptionSmokeTest extends AbstractSmokeTest {
 
     public static final String TEST_BUILD_TIMESTAMP = "-PbuildTimestamp=" + newTimestamp()
@@ -46,8 +49,6 @@
         and:
         def buildJavaHome = AvailableJavaHomes.getAvailableJdks(new GradleBuildJvmSpec()).last().javaHome
         file("gradle.properties") << "\norg.gradle.java.home=${buildJavaHome}\n"
-        // Enables Kotlin assignment support, should be removed once we enable assignment by default
-        file("gradle.properties") << "systemProp.org.gradle.unsafe.kotlin.assignment=true\n"
     }
 
     BuildResult getResult() {
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractSmokeTest.groovy
index 7a7f658..24bc45b 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AbstractSmokeTest.groovy
@@ -56,54 +56,48 @@
          */
 
         // https://plugins.gradle.org/plugin/com.netflix.nebula.dependency-recommender
-        static nebulaDependencyRecommender = "12.0.0"
+        static nebulaDependencyRecommender = "12.2.0"
 
         // https://plugins.gradle.org/plugin/com.netflix.nebula.plugin-plugin
-        static nebulaPluginPlugin = "20.0.0"
+        static nebulaPluginPlugin = "20.7.5"
 
         // https://plugins.gradle.org/plugin/nebula.lint
-        static nebulaLint = "17.7.1"
+        static nebulaLint = "18.0.3"
 
         // https://plugins.gradle.org/plugin/org.jetbrains.gradle.plugin.idea-ext
-        static ideaExt = "1.1.6"
+        static ideaExt = "1.1.7"
 
         // https://plugins.gradle.org/plugin/com.netflix.nebula.dependency-lock
-        static nebulaDependencyLock = Versions.of("13.1.0")
+        static nebulaDependencyLock = Versions.of("13.2.1")
 
         // https://plugins.gradle.org/plugin/com.netflix.nebula.resolution-rules
-        static nebulaResolutionRules = "10.0.0"
+        static nebulaResolutionRules = "10.2.0"
 
         // https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow
-        static shadow = Versions.of("7.0.0", "7.1.2")
+        static shadow = Versions.of("8.1.1")
 
-        // https://github.com/asciidoctor/asciidoctor-gradle-plugin/releases
-        static asciidoctor = Versions.of("3.3.2")
+        // https://github.com/asciidoctor/asciidoctor-gradle-plugin/tags
+        static asciidoctor = Versions.of("3.3.2", "4.0.0-alpha.1")
 
         // https://plugins.gradle.org/plugin/com.github.spotbugs
-        static spotbugs = "5.0.12"
+        static spotbugs = "5.0.14"
 
         // https://plugins.gradle.org/plugin/com.bmuschko.docker-java-application
-        static docker = "8.0.0"
-
-        // https://plugins.gradle.org/plugin/com.bmuschko.tomcat
-        static tomcat = "2.7.0"
+        static docker = "9.3.1"
 
         // https://plugins.gradle.org/plugin/io.spring.dependency-management
-        static springDependencyManagement = "1.0.14.RELEASE"
+        static springDependencyManagement = "1.1.0"
 
         // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-gradle-plugin
-        static springBoot = "2.7.8"
+        static springBoot = "2.7.11"
 
         // https://developer.android.com/studio/releases/build-tools
-        static androidTools = "30.0.2"
+        static androidTools = "33.0.2"
         // https://developer.android.com/studio/releases/gradle-plugin
-        static androidGradle = Versions.of(*AGP_VERSIONS.getLatestsFromMinorPlusNightly("4.0"))
+        static androidGradle = Versions.of(*AGP_VERSIONS.latests)
 
         // https://search.maven.org/search?q=g:org.jetbrains.kotlin%20AND%20a:kotlin-project&core=gav
-        static kotlin = Versions.of(*KOTLIN_VERSIONS.latests.findAll {
-            def lowerCaseVersion = it.toLowerCase(Locale.US)
-            !lowerCaseVersion.contains('-m') && !(lowerCaseVersion.contains('-beta'))
-        })
+        static kotlin = Versions.of(*KOTLIN_VERSIONS.latests)
 
         // https://plugins.gradle.org/plugin/org.gretty
         static gretty = [
@@ -115,79 +109,84 @@
         static grgit = "4.1.1"
 
         // https://plugins.gradle.org/plugin/com.github.ben-manes.versions
-        static gradleVersions = "0.45.0"
+        static gradleVersions = "0.46.0"
 
         // https://plugins.gradle.org/plugin/org.gradle.playframework
         static playframework = "0.13"
 
         // https://plugins.gradle.org/plugin/net.ltgt.errorprone
-        static errorProne = "2.0.2"
+        static errorProne = "3.1.0"
 
         // https://plugins.gradle.org/plugin/com.google.protobuf
         static protobufPlugin = "0.9.2"
 
-        static protobufTools = "3.21.5"
+        // https://central.sonatype.com/artifact/com.google.protobuf/protobuf-java/4.0.0-rc-2/versions
+        static protobufTools = "3.22.3"
 
         // https://plugins.gradle.org/plugin/org.gradle.test-retry
-        static testRetryPlugin = "1.5.0"
+        static testRetryPlugin = "1.5.2"
 
         // https://plugins.gradle.org/plugin/com.jfrog.artifactory
-        static artifactoryPlugin = "4.29.0"
-        static artifactoryRepoOSSVersion = "6.16.0"
+        static artifactoryPlugin = "4.31.9"
+
+        // https://docker.bintray.io/ui/packages/gav:%2F%2Forg.artifactory.oss.docker:artifactory-oss-docker?name=artifactory-oss&type=packages
+        static artifactoryRepoOSSVersion = "6.23.21"
 
         // https://plugins.gradle.org/plugin/io.freefair.aspectj
-        static aspectj = "6.5.1"
+        static aspectj = "8.0.1"
 
         // https://plugins.gradle.org/plugin/de.undercouch.download
-        static undercouchDownload = Versions.of("5.1.2")
+        static undercouchDownload = Versions.of("5.4.0")
 
         // https://github.com/micronaut-projects/micronaut-gradle-plugin/releases
-        static micronaut = Versions.of("2.0.6")
+        static micronaut = "3.7.8"
 
         // https://plugins.gradle.org/plugin/com.gorylenko.gradle-git-properties
         static gradleGitProperties = Versions.of("2.4.1")
 
         // https://plugins.gradle.org/plugin/org.flywaydb.flyway
-        static flyway = Versions.of("9.2.2")
+        static flyway = Versions.of("9.16.3")
 
         // https://plugins.gradle.org/plugin/net.ltgt.apt
         static apt = Versions.of("0.21")
 
         // https://plugins.gradle.org/plugin/io.gitlab.arturbosch.detekt
-        static detekt = Versions.of("1.21.0")
+        static detekt = Versions.of("1.22.0")
 
         // https://plugins.gradle.org/plugin/com.diffplug.spotless
-        static spotless = Versions.of("6.10.0")
+        static spotless = Versions.of("6.18.0")
 
         // https://plugins.gradle.org/plugin/com.google.cloud.tools.jib
-        static jib = Versions.of("3.3.0")
+        static jib = Versions.of("3.3.1")
 
         // https://plugins.gradle.org/plugin/io.freefair.lombok
-        static lombok = Versions.of("6.5.1")
-
-        // https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.allopen
-        // https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.spring
-        static kotlinPlugins = Versions.of("1.6.10", "1.6.21", "1.7.0", "1.7.10", "1.7.22", "1.8.10")
+        static lombok = Versions.of("8.0.1")
 
         // https://plugins.gradle.org/plugin/com.moowork.grunt
         // https://plugins.gradle.org/plugin/com.moowork.gulp
         // https://plugins.gradle.org/plugin/com.moowork.node
         static node = Versions.of("1.3.1")
 
-        // https://plugins.gradle.org/plugin/org.jlleitschuh.gradle.ktlint
-        static ktlint = Versions.of("11.0.0")
-
         // https://plugins.gradle.org/plugin/com.github.node-gradle.node
-        static newNode = Versions.of("3.4.0")
+        static newNode = Versions.of("4.0.0")
+
+        // https://plugins.gradle.org/plugin/org.jlleitschuh.gradle.ktlint
+        static ktlint = Versions.of("11.3.2")
 
         // https://github.com/davidmc24/gradle-avro-plugin
-        static avro = Versions.of("1.3.0")
+        static avro = Versions.of("1.7.0")
 
         // https://plugins.gradle.org/plugin/io.spring.nohttp
-        static nohttp = Versions.of("0.0.10")
+        static nohttp = Versions.of("0.0.11")
 
         // https://plugins.gradle.org/plugin/org.jenkins-ci.jpi
-        static jenkinsJpi = Versions.of("0.44.0")
+        static jenkinsJpi = Versions.of("0.48.0")
+
+        // https://mvnrepository.com/artifact/com.guardsquare/proguard-gradle
+        static proguardGradle = "7.3.2"
+
+        // https://plugins.gradle.org/plugin/com.palantir.consistent-versions
+        static palantirConsistentVersions = "2.12.0"
     }
 
     static class Versions implements Iterable<String> {
@@ -197,23 +196,6 @@
 
         final List<String> versions
 
-        String latest() {
-            versions.last()
-        }
-
-        String latestStable() {
-            versions.reverse().find { version ->
-                !version.containsIgnoreCase("rc") &&
-                !version.containsIgnoreCase("beta") &&
-                !version.containsIgnoreCase("alpha") &&
-                !version.containsIgnoreCase("milestone")
-            }
-        }
-
-        String latestStartsWith(String prefix) {
-            return versions.reverse().find { it.startsWith(prefix) }
-        }
-
         private Versions(String... given) {
             versions = Arrays.asList(given)
         }
@@ -345,9 +327,6 @@
             def init = AGP_VERSIONS.createAgpNightlyRepositoryInitScript()
             extraArgs += ["-I", init.canonicalPath]
         }
-        if (agpVersion.startsWith("7.2") && JavaVersion.current().java9Compatible) {
-            runner = runner.withJvmArguments('--add-opens', 'java.logging/java.util.logging=ALL-UNNAMED')
-        }
         return runner.withArguments([runner.arguments, extraArgs].flatten())
     }
 
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidCommunityPluginsSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidCommunityPluginsSmokeTest.groovy
index be554d5..94647ce 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidCommunityPluginsSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidCommunityPluginsSmokeTest.groovy
@@ -19,32 +19,51 @@
 
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 
+/**
+ * For these tests to run you need to set ANDROID_SDK_ROOT to your Android SDK directory
+ *
+ * https://developer.android.com/studio/releases/build-tools.html
+ * https://developer.android.com/studio/releases/gradle-plugin.html
+ * https://androidstudio.googleblog.com/
+ *
+ * To run your tests against all AGP versions from agp-versions.properties, use higher version of java by setting -PtestJavaVersion=<version>
+ * See {@link org.gradle.integtests.fixtures.versions.AndroidGradlePluginVersions#assumeCurrentJavaVersionIsSupportedBy() assumeCurrentJavaVersionIsSupportedBy} for more details
+ */
 class AndroidCommunityPluginsSmokeTest extends AbstractPluginValidatingSmokeTest implements ValidationMessageChecker {
 
-    private static final String ANDROID_PLUGIN_VERSION_FOR_TESTS = TestedVersions.androidGradle.latestStartsWith("7.3")
+    private static final String ANDROID_PLUGIN_VERSION_FOR_TESTS = AGP_VERSIONS.latestStable
 
-    private static final String DAGGER_HILT_ANDROID_PLUGIN_ID = 'dagger.hilt.android.plugin'
-    private static final String TRIPLET_PLAY_PLUGIN_ID = 'com.github.triplet.play'
-    private static final String FLADLE_PLUGIN_ID = 'com.osacky.fladle'
-    private static final String SAFEARGS_PLUGIN_ID = 'androidx.navigation.safeargs'
     private static final String GOOGLE_SERVICES_PLUGIN_ID = 'com.google.gms.google-services'
     private static final String CRASHLYTICS_PLUGIN_ID = 'com.google.firebase.crashlytics'
     private static final String FIREBASE_PERF_PLUGIN_ID = 'com.google.firebase.firebase-perf'
-    private static final String SENTRY_PLUGIN_ID = 'io.sentry.android.gradle'
     private static final String BUGSNAG_PLUGIN_ID = 'com.bugsnag.android.gradle'
+    private static final String FLADLE_PLUGIN_ID = 'com.osacky.fladle'
+    private static final String TRIPLET_PLAY_PLUGIN_ID = 'com.github.triplet.play'
+    private static final String SAFEARGS_PLUGIN_ID = 'androidx.navigation.safeargs'
+    private static final String DAGGER_HILT_ANDROID_PLUGIN_ID = 'dagger.hilt.android.plugin'
+    private static final String SENTRY_PLUGIN_ID = 'io.sentry.android.gradle'
 
     @Override
     Map<String, Versions> getPluginsToValidate() {
         [
-            (GOOGLE_SERVICES_PLUGIN_ID): Versions.of('4.3.5'),
-            (CRASHLYTICS_PLUGIN_ID): Versions.of('2.9.1'),
-            (FIREBASE_PERF_PLUGIN_ID): Versions.of('1.4.1'),
-            (BUGSNAG_PLUGIN_ID): Versions.of('7.3.0'),
+            // https://mvnrepository.com/artifact/com.google.gms/google-services?repo=google
+            (GOOGLE_SERVICES_PLUGIN_ID): Versions.of('4.3.15'),
+            // https://mvnrepository.com/artifact/com.google.firebase.crashlytics/com.google.firebase.crashlytics.gradle.plugin
+            (CRASHLYTICS_PLUGIN_ID): Versions.of('2.9.4'),
+            // https://mvnrepository.com/artifact/com.google.firebase/perf-plugin
+            (FIREBASE_PERF_PLUGIN_ID): Versions.of('1.4.2'),
+            // https://plugins.gradle.org/plugin/com.bugsnag.android.gradle
+            (BUGSNAG_PLUGIN_ID): Versions.of('7.4.1'),
+            // https://plugins.gradle.org/plugin/com.osacky.fladle
             (FLADLE_PLUGIN_ID): Versions.of('0.17.4'),
-            (TRIPLET_PLAY_PLUGIN_ID): Versions.of('3.7.0'),
-            (SAFEARGS_PLUGIN_ID): Versions.of('2.5.1'),
-            (DAGGER_HILT_ANDROID_PLUGIN_ID): Versions.of('2.43.2'),
-            (SENTRY_PLUGIN_ID): Versions.of('3.1.6'),
+            // https://plugins.gradle.org/plugin/com.github.triplet.play
+            (TRIPLET_PLAY_PLUGIN_ID): Versions.of('3.8.1'),
+            // https://mvnrepository.com/artifact/androidx.navigation.safeargs/androidx.navigation.safeargs.gradle.plugin
+            (SAFEARGS_PLUGIN_ID): Versions.of('2.5.3'),
+            // https://mvnrepository.com/artifact/com.google.dagger/hilt-android-gradle-plugin
+            (DAGGER_HILT_ANDROID_PLUGIN_ID): Versions.of('2.45'),
+            // https://mvnrepository.com/artifact/io.sentry.android.gradle/io.sentry.android.gradle.gradle.plugin
+            (SENTRY_PLUGIN_ID): Versions.of('3.4.2'),
         ]
     }
 
@@ -97,6 +116,7 @@
         """
         buildFile << """
                 android {
+                    namespace = "org.gradle.smoke.test"
                     compileSdkVersion 24
                     buildToolsVersion '${TestedVersions.androidTools}'
                     defaultConfig {
@@ -140,8 +160,8 @@
             case TRIPLET_PLAY_PLUGIN_ID:
                 buildFile << """
                     play {
-                        serviceAccountCredentials.set(file("your-key.json"))
-                        updatePriority.set(2)
+                        serviceAccountCredentials = file("your-key.json")
+                        updatePriority = 2
                     }
                 """
                 break
@@ -153,7 +173,7 @@
         if (testedPluginId == DAGGER_HILT_ANDROID_PLUGIN_ID) {
             return [
                 'com.android.application': ANDROID_PLUGIN_VERSION_FOR_TESTS,
-                'org.jetbrains.kotlin.android': TestedVersions.kotlin.latestStable()
+                'org.jetbrains.kotlin.android': KOTLIN_VERSIONS.latestStable
             ]
         }
         return ['com.android.application': ANDROID_PLUGIN_VERSION_FOR_TESTS]
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidPluginsSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidPluginsSmokeTest.groovy
index 3d3f2fd..874db53 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidPluginsSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidPluginsSmokeTest.groovy
@@ -16,7 +16,6 @@
 
 package org.gradle.smoketests
 
-
 import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
 import org.gradle.integtests.fixtures.android.AndroidHome
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
@@ -32,6 +31,8 @@
  * https://developer.android.com/studio/releases/gradle-plugin.html
  * https://androidstudio.googleblog.com/
  *
+ * To run your tests against all AGP versions from agp-versions.properties, use higher version of java by setting -PtestJavaVersion=<version>
+ * See {@link org.gradle.integtests.fixtures.versions.AndroidGradlePluginVersions#assumeCurrentJavaVersionIsSupportedBy() assumeCurrentJavaVersionIsSupportedBy} for more details
  */
 class AndroidPluginsSmokeTest extends AbstractPluginValidatingSmokeTest implements ValidationMessageChecker {
 
@@ -46,7 +47,9 @@
         def runner = super.runner(tasks)
         // TODO: AGP's ShaderCompile uses Task.project after the configuration barrier to compute inputs
         return runner.withJvmArguments(runner.jvmArguments + [
-            "-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true"
+            // A workaround for this has been added to TaskExecutionAccessCheckers;
+            // TODO once we remove it, uncomment the flag below or upgrade AGP
+            // "-Dorg.gradle.configuration-cache.internal.task-execution-access-pre-stable=true"
         ])
     }
 
@@ -60,6 +63,13 @@
 
         and:
         def runner = useAgpVersion(agpVersion, runner('sourceSets'))
+        runner.deprecations(AndroidDeprecations) {
+            expectProjectConventionDeprecationWarning(agpVersion)
+            expectAndroidConventionTypeDeprecationWarning(agpVersion)
+            if (GradleContextualExecuter.isNotConfigCache()) {
+                expectBasePluginConventionDeprecation(agpVersion)
+            }
+        }
 
         when:
         def result = runner.build()
@@ -75,6 +85,38 @@
         ].combinations()
     }
 
+    def "simpler android library and application APK assembly test (agp=#agpVersion)"() {
+
+        given:
+        AGP_VERSIONS.assumeCurrentJavaVersionIsSupportedBy(agpVersion)
+
+        and:
+        androidLibraryAndApplicationBuild(agpVersion)
+
+        and:
+        def runner = useAgpVersion(agpVersion, runner(
+            'assembleDebug'
+        ))
+
+        when:
+        SantaTrackerConfigurationCacheWorkaround.beforeBuild(runner.projectDir, IntegrationTestBuildContext.INSTANCE.gradleUserHomeDir)
+        def result = runner.deprecations(AbstractAndroidSantaTrackerSmokeTest.SantaTrackerDeprecations) {
+            expectAndroidWorkerExecutionSubmitDeprecationWarning(agpVersion)
+            expectReportDestinationPropertyDeprecation(agpVersion)
+            expectProjectConventionDeprecationWarning(agpVersion)
+            expectAndroidConventionTypeDeprecationWarning(agpVersion)
+            expectBasePluginConventionDeprecation(agpVersion)
+            expectBuildIdentifierNameDeprecation()
+            expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
+        }.build()
+
+        then:
+        result.task(':app:compileDebugJavaWithJavac').outcome == TaskOutcome.SUCCESS
+
+        where:
+        agpVersion << ["7.4.2"]
+    }
+
     def "android library and application APK assembly (agp=#agpVersion, ide=#ide)"() {
 
         given:
@@ -93,9 +135,14 @@
 
         when: 'first build'
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(runner.projectDir, IntegrationTestBuildContext.INSTANCE.gradleUserHomeDir)
-        def result = runner.deprecations(AndroidDeprecations) {
+        def result = runner.deprecations(AbstractAndroidSantaTrackerSmokeTest.SantaTrackerDeprecations) {
             expectAndroidWorkerExecutionSubmitDeprecationWarning(agpVersion)
             expectReportDestinationPropertyDeprecation(agpVersion)
+            expectProjectConventionDeprecationWarning(agpVersion)
+            expectAndroidConventionTypeDeprecationWarning(agpVersion)
+            expectBasePluginConventionDeprecation(agpVersion)
+            expectBuildIdentifierNameDeprecation()
+            expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
         }.build()
 
         then:
@@ -114,6 +161,10 @@
         result = runner.deprecations(AndroidDeprecations) {
             if (!GradleContextualExecuter.isConfigCache()) {
                 expectReportDestinationPropertyDeprecation(agpVersion)
+                expectProjectConventionDeprecationWarning(agpVersion)
+                expectAndroidConventionTypeDeprecationWarning(agpVersion)
+                expectBasePluginConventionDeprecation(agpVersion)
+                expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
             }
         }.build()
 
@@ -133,6 +184,10 @@
             expectAndroidWorkerExecutionSubmitDeprecationWarning(agpVersion)
             if (!GradleContextualExecuter.isConfigCache()) {
                 expectReportDestinationPropertyDeprecation(agpVersion)
+                expectProjectConventionDeprecationWarning(agpVersion)
+                expectAndroidConventionTypeDeprecationWarning(agpVersion)
+                expectBasePluginConventionDeprecation(agpVersion)
+                expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
             }
         }.build()
 
@@ -146,12 +201,22 @@
         assertConfigurationCacheStateLoaded()
 
         when: 'clean re-build'
-        useAgpVersion(agpVersion, this.runner('clean')).build()
+        def smokeTestRunner = this.runner('clean')
+        useAgpVersion(agpVersion, smokeTestRunner).deprecations(AndroidDeprecations) {
+            expectProjectConventionDeprecationWarning(agpVersion)
+            expectAndroidConventionTypeDeprecationWarning(agpVersion)
+            expectBasePluginConventionDeprecation(agpVersion)
+        }.build()
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(runner.projectDir, IntegrationTestBuildContext.INSTANCE.gradleUserHomeDir)
         result = runner.deprecations(AndroidDeprecations) {
             expectAndroidWorkerExecutionSubmitDeprecationWarning(agpVersion)
+            expectBuildIdentifierNameDeprecation()
             if (!GradleContextualExecuter.isConfigCache()) {
                 expectReportDestinationPropertyDeprecation(agpVersion)
+                expectProjectConventionDeprecationWarning(agpVersion)
+                expectAndroidConventionTypeDeprecationWarning(agpVersion)
+                expectBasePluginConventionDeprecation(agpVersion)
+                expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
             }
         }.build()
 
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy
index 42d3ddd..6b9b909 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerCachingSmokeTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.smoketests
 
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.testkit.runner.BuildResult
 
 import static org.gradle.testkit.runner.TaskOutcome.FROM_CACHE
@@ -41,7 +42,7 @@
 
         when: 'clean build'
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(originalDir, homeDir)
-        buildLocationMaybeExpectingWorkerExecutorDeprecation(originalDir, agpVersion)
+        buildLocationMaybeExpectingWorkerExecutorAndConventionDeprecation(originalDir, agpVersion)
 
         then:
         assertConfigurationCacheStateStored()
@@ -49,7 +50,7 @@
         // TODO - this is here because AGP >=7.4 reads build/generated/source/kapt/debug at configuration time
         when: 'up-to-date build, reusing configuration cache when enabled'
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(originalDir, homeDir)
-        buildLocation(originalDir, agpVersion)
+        buildUpToDateLocation(originalDir, agpVersion)
 
         then:
         if (agpVersion.startsWith('7.3')) {
@@ -67,7 +68,7 @@
 
         when: 'clean cached build'
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(relocatedDir, homeDir)
-        BuildResult relocatedResult = buildLocationMaybeExpectingWorkerExecutorDeprecation(relocatedDir, agpVersion)
+        BuildResult relocatedResult = buildLocationMaybeExpectingWorkerExecutorAndConventionDeprecation(relocatedDir, agpVersion)
 
         then:
         assertConfigurationCacheStateStored()
@@ -85,7 +86,11 @@
         when: 'clean cached build, reusing configuration cache when enabled'
         cleanLocation(relocatedDir, agpVersion)
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(relocatedDir, homeDir)
-        buildLocationMaybeExpectingWorkerExecutorDeprecation(relocatedDir, agpVersion)
+        if (GradleContextualExecuter.notConfigCache) {
+            buildLocationMaybeExpectingWorkerExecutorAndConventionDeprecation(relocatedDir, agpVersion)
+        } else {
+            buildLocationMaybeExpectingWorkerExecutorDeprecation(relocatedDir, agpVersion)
+        }
 
         then:
         assertConfigurationCacheStateLoaded()
@@ -132,7 +137,7 @@
         ':cityquiz:preBuild': UP_TO_DATE,
         ':cityquiz:preDebugBuild': UP_TO_DATE,
         ':cityquiz:processApplicationManifestDebugForBundle': SUCCESS,
-        ':cityquiz:processDebugJavaRes': NO_SOURCE,
+        ':cityquiz:processDebugJavaRes': SUCCESS,
         ':cityquiz:processDebugMainManifest': FROM_CACHE,
         ':cityquiz:processDebugManifestForPackage': FROM_CACHE,
         ':cityquiz:processDebugResources': FROM_CACHE,
@@ -141,7 +146,6 @@
         ':common:assembleDebug': SUCCESS,
         ':common:bundleDebugAar': SUCCESS,
         ':common:bundleLibCompileToJarDebug': SUCCESS,
-        ':common:bundleLibResDebug': SUCCESS,
         ':common:bundleLibRuntimeToJarDebug': SUCCESS,
         ':common:compileDebugJavaWithJavac': FROM_CACHE,
         ':common:compileDebugKotlin': FROM_CACHE,
@@ -149,7 +153,7 @@
         ':common:compileDebugShaders': NO_SOURCE,
         ':common:copyDebugJniLibsProjectAndLocalJars': SUCCESS,
         ':common:copyDebugJniLibsProjectOnly': SUCCESS,
-        ':common:createFullJarDebug': FROM_CACHE,
+        ':common:createFullJarDebug': SUCCESS,
         ':common:dataBindingGenBaseClassesDebug': FROM_CACHE,
         ':common:dataBindingMergeDependencyArtifactsDebug': SUCCESS,
         ':common:extractDebugAnnotations': FROM_CACHE,
@@ -174,7 +178,7 @@
         ':common:preDebugBuild': UP_TO_DATE,
         ':common:prepareDebugArtProfile': SUCCESS,
         ':common:prepareLintJarForPublish': SUCCESS,
-        ':common:processDebugJavaRes': NO_SOURCE,
+        ':common:processDebugJavaRes': SUCCESS,
         ':common:processDebugManifest': FROM_CACHE,
         ':common:stripDebugDebugSymbols': NO_SOURCE,
         ':common:syncDebugLibJars': FROM_CACHE,
@@ -212,7 +216,7 @@
         ':dasherdancer:preBuild': UP_TO_DATE,
         ':dasherdancer:preDebugBuild': UP_TO_DATE,
         ':dasherdancer:processApplicationManifestDebugForBundle': SUCCESS,
-        ':dasherdancer:processDebugJavaRes': NO_SOURCE,
+        ':dasherdancer:processDebugJavaRes': SUCCESS,
         ':dasherdancer:processDebugMainManifest': FROM_CACHE,
         ':dasherdancer:processDebugManifestForPackage': FROM_CACHE,
         ':dasherdancer:processDebugResources': FROM_CACHE,
@@ -221,14 +225,13 @@
         ':doodles-lib:assembleDebug': SUCCESS,
         ':doodles-lib:bundleDebugAar': SUCCESS,
         ':doodles-lib:bundleLibCompileToJarDebug': SUCCESS,
-        ':doodles-lib:bundleLibResDebug': NO_SOURCE,
         ':doodles-lib:bundleLibRuntimeToJarDebug': SUCCESS,
         ':doodles-lib:compileDebugJavaWithJavac': FROM_CACHE,
         ':doodles-lib:compileDebugLibraryResources': FROM_CACHE,
         ':doodles-lib:compileDebugShaders': NO_SOURCE,
         ':doodles-lib:copyDebugJniLibsProjectAndLocalJars': SUCCESS,
         ':doodles-lib:copyDebugJniLibsProjectOnly': SUCCESS,
-        ':doodles-lib:createFullJarDebug': FROM_CACHE,
+        ':doodles-lib:createFullJarDebug': SUCCESS,
         ':doodles-lib:extractDebugAnnotations': FROM_CACHE,
         ':doodles-lib:extractDeepLinksDebug': FROM_CACHE,
         ':doodles-lib:extractDeepLinksForAarDebug': FROM_CACHE,
@@ -326,7 +329,7 @@
         ':jetpack:preBuild': UP_TO_DATE,
         ':jetpack:preDebugBuild': UP_TO_DATE,
         ':jetpack:processApplicationManifestDebugForBundle': SUCCESS,
-        ':jetpack:processDebugJavaRes': NO_SOURCE,
+        ':jetpack:processDebugJavaRes': SUCCESS,
         ':jetpack:processDebugMainManifest': FROM_CACHE,
         ':jetpack:processDebugManifestForPackage': FROM_CACHE,
         ':jetpack:processDebugResources': FROM_CACHE,
@@ -411,14 +414,13 @@
         ':playgames:assembleDebug': SUCCESS,
         ':playgames:bundleDebugAar': SUCCESS,
         ':playgames:bundleLibCompileToJarDebug': SUCCESS,
-        ':playgames:bundleLibResDebug': NO_SOURCE,
         ':playgames:bundleLibRuntimeToJarDebug': SUCCESS,
         ':playgames:compileDebugJavaWithJavac': FROM_CACHE,
         ':playgames:compileDebugLibraryResources': FROM_CACHE,
         ':playgames:compileDebugShaders': NO_SOURCE,
         ':playgames:copyDebugJniLibsProjectAndLocalJars': SUCCESS,
         ':playgames:copyDebugJniLibsProjectOnly': SUCCESS,
-        ':playgames:createFullJarDebug': FROM_CACHE,
+        ':playgames:createFullJarDebug': SUCCESS,
         ':playgames:extractDebugAnnotations': FROM_CACHE,
         ':playgames:extractDeepLinksDebug': FROM_CACHE,
         ':playgames:extractDeepLinksForAarDebug': FROM_CACHE,
@@ -517,7 +519,7 @@
         ':rocketsleigh:preBuild': UP_TO_DATE,
         ':rocketsleigh:preDebugBuild': UP_TO_DATE,
         ':rocketsleigh:processApplicationManifestDebugForBundle': SUCCESS,
-        ':rocketsleigh:processDebugJavaRes': NO_SOURCE,
+        ':rocketsleigh:processDebugJavaRes': SUCCESS,
         ':rocketsleigh:processDebugMainManifest': FROM_CACHE,
         ':rocketsleigh:processDebugManifestForPackage': FROM_CACHE,
         ':rocketsleigh:processDebugResources': FROM_CACHE,
@@ -565,7 +567,7 @@
         ':santa-tracker:packageDebug': SUCCESS,
         ':santa-tracker:preBuild': UP_TO_DATE,
         ':santa-tracker:preDebugBuild': SUCCESS,
-        ':santa-tracker:processDebugJavaRes': NO_SOURCE,
+        ':santa-tracker:processDebugJavaRes': SUCCESS,
         ':santa-tracker:processDebugMainManifest': FROM_CACHE,
         ':santa-tracker:processDebugManifest': FROM_CACHE,
         ':santa-tracker:processDebugManifestForPackage': FROM_CACHE,
@@ -617,7 +619,6 @@
         ':tracker:assembleDebug': SUCCESS,
         ':tracker:bundleDebugAar': SUCCESS,
         ':tracker:bundleLibCompileToJarDebug': SUCCESS,
-        ':tracker:bundleLibResDebug': SUCCESS,
         ':tracker:bundleLibRuntimeToJarDebug': SUCCESS,
         ':tracker:compileDebugJavaWithJavac': SUCCESS,
         ':tracker:compileDebugKotlin': FROM_CACHE,
@@ -625,7 +626,7 @@
         ':tracker:compileDebugShaders': NO_SOURCE,
         ':tracker:copyDebugJniLibsProjectAndLocalJars': SUCCESS,
         ':tracker:copyDebugJniLibsProjectOnly': SUCCESS,
-        ':tracker:createFullJarDebug': FROM_CACHE,
+        ':tracker:createFullJarDebug': SUCCESS,
         ':tracker:extractDebugAnnotations': FROM_CACHE,
         ':tracker:extractDeepLinksDebug': FROM_CACHE,
         ':tracker:extractDeepLinksForAarDebug': FROM_CACHE,
@@ -649,7 +650,7 @@
         ':tracker:preDebugBuild': UP_TO_DATE,
         ':tracker:prepareDebugArtProfile': SUCCESS,
         ':tracker:prepareLintJarForPublish': SUCCESS,
-        ':tracker:processDebugJavaRes': NO_SOURCE,
+        ':tracker:processDebugJavaRes': SUCCESS,
         ':tracker:processDebugManifest': FROM_CACHE,
         ':tracker:stripDebugDebugSymbols': NO_SOURCE,
         ':tracker:syncDebugLibJars': FROM_CACHE,
@@ -687,7 +688,7 @@
         ':wearable:packageDebug': SUCCESS,
         ':wearable:preBuild': UP_TO_DATE,
         ':wearable:preDebugBuild': UP_TO_DATE,
-        ':wearable:processDebugJavaRes': NO_SOURCE,
+        ':wearable:processDebugJavaRes': SUCCESS,
         ':wearable:processDebugMainManifest': FROM_CACHE,
         ':wearable:processDebugManifest': FROM_CACHE,
         ':wearable:processDebugManifestForPackage': FROM_CACHE,
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerSmokeTest.groovy
index 29711d5..81e3199 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AndroidSantaTrackerSmokeTest.groovy
@@ -16,6 +16,8 @@
 
 package org.gradle.smoketests
 
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+
 import java.util.jar.JarOutputStream
 
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
@@ -34,7 +36,7 @@
         setupCopyOfSantaTracker(checkoutDir)
 
         when:
-        buildLocationMaybeExpectingWorkerExecutorDeprecation(checkoutDir, agpVersion)
+        buildLocationMaybeExpectingWorkerExecutorAndConventionDeprecation(checkoutDir, agpVersion)
 
         then:
         assertConfigurationCacheStateStored()
@@ -61,7 +63,7 @@
 
         when:
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(checkoutDir, homeDir)
-        def result = buildLocationMaybeExpectingWorkerExecutorDeprecation(checkoutDir, agpVersion)
+        def result = buildLocationMaybeExpectingWorkerExecutorAndConventionDeprecation(checkoutDir, agpVersion)
         def md5Before = compiledClassFile.md5Hash
 
         then:
@@ -71,7 +73,12 @@
         when:
         fileToChange.replace("computeCurrentVelocity(1000", "computeCurrentVelocity(2000")
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(checkoutDir, homeDir)
-        buildLocationMaybeExpectingWorkerExecutorDeprecation(checkoutDir, agpVersion)
+        if (GradleContextualExecuter.notConfigCache) {
+            buildLocationMaybeExpectingWorkerExecutorAndConventionDeprecation(checkoutDir, agpVersion)
+        } else {
+            buildLocationMaybeExpectingWorkerExecutorAndConfigUtilDeprecation(checkoutDir, agpVersion)
+        }
+
         def md5After = compiledClassFile.md5Hash
 
         then:
@@ -107,6 +114,15 @@
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(checkoutDir, homeDir)
         // Use --continue so that a deterministic set of tasks runs when some tasks fail
         runner.withArguments(runner.arguments + "--continue")
+        runner.deprecations(SantaTrackerDeprecations) {
+            expectProjectConventionDeprecationWarning(agpVersion)
+            expectAndroidConventionTypeDeprecationWarning(agpVersion)
+            expectBasePluginConventionDeprecation(agpVersion)
+            expectBuildIdentifierIsCurrentBuildDeprecation()
+            if (agpVersion.startsWith('7.')) {
+                expectBuildIdentifierNameDeprecation()
+            }
+        }
         def result = runner.buildAndFail()
 
         then:
@@ -120,6 +136,14 @@
         )
         SantaTrackerConfigurationCacheWorkaround.beforeBuild(checkoutDir, homeDir)
         runner.withArguments(runner.arguments + "--continue")
+        runner.deprecations(SantaTrackerDeprecations) {
+            if (GradleContextualExecuter.notConfigCache) {
+                expectProjectConventionDeprecationWarning(agpVersion)
+                expectAndroidConventionTypeDeprecationWarning(agpVersion)
+                expectBasePluginConventionDeprecation(agpVersion)
+                expectBuildIdentifierIsCurrentBuildDeprecation(agpVersion)
+            }
+        }
         result = runner.buildAndFail()
 
         then:
@@ -149,10 +173,25 @@
             androidAnalyticsSetting.parentFile.mkdirs()
             androidAnalyticsSetting.createNewFile()
         }
+        File androidCacheDir = new File(System.getProperty("user.home"), ".android/cache")
+        if (!androidCacheDir.exists()) {
+            androidCacheDir.mkdirs()
+        }
+        File androidLock = new File(gradleHome, "android.lock")
+        if (!androidLock.exists()) {
+            androidLock.parentFile.mkdirs()
+            androidLock.createNewFile()
+        }
         def androidFakeDependency = new File(gradleHome, "android/FakeDependency.jar")
         if (!androidFakeDependency.exists()) {
             androidFakeDependency.parentFile.mkdirs()
             new JarOutputStream(new FileOutputStream(androidFakeDependency)).close()
         }
+        File androidSdkRoot = new File(System.getenv("ANDROID_SDK_ROOT"))
+        File androidSdkPackageXml = new File(androidSdkRoot, "platform-tools/package.xml")
+        if (!androidSdkPackageXml.exists()) {
+            androidSdkPackageXml.parentFile.mkdirs()
+            androidSdkPackageXml.createNewFile()
+        }
     }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ArtifactoryAndDockerSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ArtifactoryAndDockerSmokeTest.groovy
index a8e3461..06d9f6a 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ArtifactoryAndDockerSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ArtifactoryAndDockerSmokeTest.groovy
@@ -18,9 +18,6 @@
 
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-
-import static org.gradle.util.TestPrecondition.HAS_DOCKER
 
 //@Requires(HAS_DOCKER)
 class ArtifactoryAndDockerSmokeTest extends AbstractPluginValidatingSmokeTest {
@@ -115,11 +112,15 @@
 
         then:
         def runner = runner('artifactoryPublish')
-        3.times {
+        runner.expectDeprecationWarning(BaseDeprecations.PROJECT_CONVENTION_DEPRECATION, "https://github.com/jfrog/build-info/issues/711")
+        2.times {
+            runner.expectDeprecationWarning(BaseDeprecations.CONVENTION_TYPE_DEPRECATION, "https://github.com/jfrog/build-info/issues/711")
+        }
+        5.times {
             runner.expectLegacyDeprecationWarning(
                 "The org.gradle.util.ConfigureUtil type has been deprecated. " +
                     "This is scheduled to be removed in Gradle 9.0. " +
-                    "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_7.html#org_gradle_util_reports_deprecations"
+                    "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#org_gradle_util_reports_deprecations"
             )
         }
         runner.build()
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AsciidoctorPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AsciidoctorPluginSmokeTest.groovy
index 493ee1f..28ef934 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AsciidoctorPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/AsciidoctorPluginSmokeTest.groovy
@@ -16,8 +16,11 @@
 
 package org.gradle.smoketests
 
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.util.GradleVersion
+import org.gradle.util.internal.VersionNumber
+
+import static org.gradle.api.internal.DocumentationRegistry.BASE_URL
 
 class AsciidoctorPluginSmokeTest extends AbstractPluginValidatingSmokeTest {
     @ToBeFixedForConfigurationCache(because = "Task.getProject() during execution")
@@ -41,7 +44,7 @@
 
         when:
         runner('asciidoc').deprecations(AsciidocDeprecations) {
-            expectAsciiDocDeprecationWarnings()
+            expectAsciiDocDeprecationWarnings(version)
         }.build()
 
         then:
@@ -83,12 +86,14 @@
             super(runner)
         }
 
-        void expectAsciiDocDeprecationWarnings() {
-            runner.expectDeprecationWarning("The JavaExecSpec.main property has been deprecated." +
+        void expectAsciiDocDeprecationWarnings(String asciidoctorVersion) {
+            runner.expectLegacyDeprecationWarningIf(
+                VersionNumber.parse(asciidoctorVersion).major < 4,
+                "The JavaExecSpec.main property has been deprecated." +
                     " This is scheduled to be removed in Gradle 9.0." +
                     " Please use the mainClass property instead." +
-                    " See https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.process.JavaExecSpec.html#org.gradle.process.JavaExecSpec:main for more details.",
-                    "")
+                    " ${String.format(DocumentationRegistry.RECOMMENDATION, "information", "${BASE_URL}/dsl/org.gradle.process.JavaExecSpec.html#org.gradle.process.JavaExecSpec:main")}"
+            )
         }
     }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BaseDeprecations.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BaseDeprecations.groovy
index 9ef788c..c1395266 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BaseDeprecations.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BaseDeprecations.groovy
@@ -16,29 +16,50 @@
 
 package org.gradle.smoketests
 
-import org.gradle.util.GradleVersion
+import org.gradle.api.internal.DocumentationRegistry
+
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
 
 class BaseDeprecations {
+    public static final DocumentationRegistry DOCUMENTATION_REGISTRY = new DocumentationRegistry()
+
+    public static final String ABSTRACT_ARCHIVE_TASK_ARCHIVE_PATH_DEPRECATION = "The AbstractArchiveTask.archivePath property has been deprecated. " +
+        "This is scheduled to be removed in Gradle 9.0. " +
+        "Please use the archiveFile property instead. " +
+        String.format(RECOMMENDATION, "information", DOCUMENTATION_REGISTRY.getDslRefForProperty("org.gradle.api.tasks.bundling.AbstractArchiveTask","archivePath"))
 
     public static final String WORKER_SUBMIT_DEPRECATION = "The WorkerExecutor.submit() method has been deprecated. This is scheduled to be removed in Gradle 8.0. " +
         "Please use the noIsolation(), classLoaderIsolation() or processIsolation() method instead. " +
-        "See https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#method_workerexecutor_submit_is_deprecated for more details."
+        "See ${DOCUMENTATION_REGISTRY.getDocumentationFor("upgrading_version_5", "method_workerexecutor_submit_is_deprecated")} for more details."
 
-    public static final String JAVAEXEC_SET_MAIN_DEPRECATION = "The JavaExecHandleBuilder.setMain(String) method has been deprecated. " +
-        "This is scheduled to be removed in Gradle 8.0. " +
-        "Please use the mainClass property instead. Consult the upgrading guide for further information: " +
-        "https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_7.html#java_exec_properties"
+    public static final String PROJECT_CONVENTION_DEPRECATION = "The Project.getConvention() method has been deprecated. " +
+        "This is scheduled to be removed in Gradle 9.0. " +
+        "Consult the upgrading guide for further information: " +
+        DOCUMENTATION_REGISTRY.getDocumentationFor("upgrading_version_8", "deprecated_access_to_conventions")
+
+    public static final String CONVENTION_TYPE_DEPRECATION = "The org.gradle.api.plugins.Convention type has been deprecated. " +
+        "This is scheduled to be removed in Gradle 9.0. " +
+        "Consult the upgrading guide for further information: " +
+        DOCUMENTATION_REGISTRY.getDocumentationFor("upgrading_version_8","deprecated_access_to_conventions")
+
+    public static final String APPLICATION_PLUGIN_CONVENTION_DEPRECATION = "The org.gradle.api.plugins.ApplicationPluginConvention type has been deprecated. " +
+        "This is scheduled to be removed in Gradle 9.0. " +
+        "Consult the upgrading guide for further information: " +
+        DOCUMENTATION_REGISTRY.getDocumentationFor("upgrading_version_8","application_convention_deprecation")
+
+    public static final String BASE_PLUGIN_CONVENTION_DEPRECATION = "The org.gradle.api.plugins.BasePluginConvention type has been deprecated. " +
+        "This is scheduled to be removed in Gradle 9.0. " +
+        "Consult the upgrading guide for further information: " +
+        DOCUMENTATION_REGISTRY.getDocumentationFor("upgrading_version_8","base_convention_deprecation")
+
+    public static final String JAVA_PLUGIN_CONVENTION_DEPRECATION = "The org.gradle.api.plugins.JavaPluginConvention type has been deprecated. " +
+        "This is scheduled to be removed in Gradle 9.0. " +
+        "Consult the upgrading guide for further information: " +
+        DOCUMENTATION_REGISTRY.getDocumentationFor("upgrading_version_8","java_convention_deprecation")
 
     final SmokeTestGradleRunner runner
 
     BaseDeprecations(SmokeTestGradleRunner runner) {
         this.runner = runner
     }
-
-    protected static String getFileTreeForEmptySourcesDeprecationForProperty(String propertyName) {
-        "Relying on FileTrees for ignoring empty directories when using @SkipWhenEmpty has been deprecated. " +
-            "This is scheduled to be removed in Gradle 8.0. " +
-            "Annotate the property ${propertyName} with @IgnoreEmptyDirectories or remove @SkipWhenEmpty. " +
-            "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_7.html#empty_directories_file_tree"
-    }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BomSupportPluginsSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BomSupportPluginsSmokeTest.groovy
index 0be89bd..d532cc8 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BomSupportPluginsSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BomSupportPluginsSmokeTest.groovy
@@ -67,7 +67,7 @@
             module("org.hamcrest:hamcrest-core:1.3").byReason(reason3)
         }
         def springCoreDeps = {
-            module("org.springframework:spring-jcl:${springVersion}")
+            module("org.springframework:spring-jcl:${springVersion}").byReason(reason3)
         }
         def springExpressionDeps = {
             module("org.springframework:spring-core:${springVersion}")
@@ -131,6 +131,13 @@
                 edge("org.springframework:spring-test", "org.springframework:spring-test:${springVersion}", springTestDeps).byReason(reason2)
                 edge("junit:junit", "junit:junit:4.12", junitDeps).byReason(reason2)
             }
+            nodes.each {
+                if (directBomDependency) {
+                    it.maybeByConstraint()
+                } else if (reason1 == "requested") {
+                    it.maybeSelectedByRule()
+                }
+            }
         }
 
         where:
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BuildScanPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BuildScanPluginSmokeTest.groovy
index 6406097..7699aa8 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BuildScanPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/BuildScanPluginSmokeTest.groovy
@@ -19,7 +19,7 @@
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.enterprise.core.GradleEnterprisePluginManager
 import org.gradle.testkit.runner.BuildResult
-import org.gradle.testkit.runner.GradleRunner
+import org.gradle.util.GradleVersion
 import org.gradle.util.internal.VersionNumber
 import org.junit.Assume
 import spock.lang.IgnoreIf
@@ -82,11 +82,18 @@
         "3.12",
         "3.12.1",
         "3.12.2",
-        "3.12.3"
+        "3.12.3",
+        "3.12.4",
+        "3.12.5",
+        "3.12.6",
+        "3.13",
+        "3.13.1",
+        "3.13.2"
     ]
 
     private static final VersionNumber FIRST_VERSION_SUPPORTING_CONFIGURATION_CACHE = VersionNumber.parse("3.4")
     private static final VersionNumber FIRST_VERSION_SUPPORTING_GRADLE_8_CONFIGURATION_CACHE = VersionNumber.parse("3.12")
+    private static final VersionNumber FIRST_VERSION_CALLING_BUILD_PATH = VersionNumber.parse("3.13.1")
 
     @IgnoreIf({ !GradleContextualExecuter.configCache })
     def "can use plugin #version with Gradle 8 configuration cache"() {
@@ -98,7 +105,13 @@
         usePluginVersion version
 
         then:
-        build().output.contains("Build scan written to")
+        scanRunner()
+            .expectLegacyDeprecationWarningIf(versionNumber < FIRST_VERSION_CALLING_BUILD_PATH,
+                "The BuildIdentifier.getName() method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Use getBuildPath() to get a unique identifier for the build. " +
+                    "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation"
+            ).build().output.contains("Build scan written to")
 
         where:
         version << SUPPORTED
@@ -114,7 +127,14 @@
         usePluginVersion version
 
         then:
-        build().output.contains("Build scan written to")
+        scanRunner()
+            .expectLegacyDeprecationWarningIf(versionNumber < FIRST_VERSION_CALLING_BUILD_PATH,
+                "The BuildIdentifier.getName() method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Use getBuildPath() to get a unique identifier for the build. " +
+                    "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation"
+            )
+            .build().output.contains("Build scan written to")
 
         where:
         version << SUPPORTED
@@ -139,7 +159,7 @@
         scanRunner(args).build()
     }
 
-    GradleRunner scanRunner(String... args) {
+    SmokeTestGradleRunner scanRunner(String... args) {
         runner("build", "-Dscan.dump", *args).forwardOutput()
     }
 
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/DockerPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/DockerPluginSmokeTest.groovy
index bf94529..56efd42 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/DockerPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/DockerPluginSmokeTest.groovy
@@ -16,8 +16,8 @@
 
 package org.gradle.smoketests
 
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
@@ -25,7 +25,7 @@
 class DockerPluginSmokeTest extends AbstractSmokeTest {
 
     // Plugin after 7.0.0 requires Java 11+ to run
-    @Requires(TestPrecondition.JDK11_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk11OrLater)
     @Issue('https://plugins.gradle.org/plugin/com.bmuschko.docker-java-application')
     def 'docker plugin'() {
         given:
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/FreefairAspectJPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/FreefairAspectJPluginSmokeTest.groovy
index 44c3c6d..a46d3b2 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/FreefairAspectJPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/FreefairAspectJPluginSmokeTest.groovy
@@ -16,16 +16,14 @@
 
 package org.gradle.smoketests
 
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 class FreefairAspectJPluginSmokeTest extends AbstractPluginValidatingSmokeTest {
     // AspectJ does not support JDK17 yet
-    @Requires(TestPrecondition.JDK16_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk16OrEarlier)
     @Issue('https://plugins.gradle.org/plugin/io.freefair.aspectj')
-    @ToBeFixedForConfigurationCache(because = "Task.getProject() during execution")
     def 'freefair aspectj plugin'() {
         given:
         buildFile << """
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildExternalPluginsValidationSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildExternalPluginsValidationSmokeTest.groovy
index 69c31e2..b161808 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildExternalPluginsValidationSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildExternalPluginsValidationSmokeTest.groovy
@@ -16,18 +16,21 @@
 
 package org.gradle.smoketests
 
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.SmokeTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 /**
  * Smoke test verifying the external plugins.
  *
  */
-@Requires(value = TestPrecondition.JDK9_OR_LATER, adhoc = {
-    GradleContextualExecuter.isNotConfigCache() && GradleBuildJvmSpec.isAvailable()
-})
+@Requires([
+    UnitTestPreconditions.Jdk9OrLater,
+    IntegTestPreconditions.NotConfigCached,
+    SmokeTestPreconditions.GradleBuildJvmSpecAvailable
+])
 class GradleBuildExternalPluginsValidationSmokeTest extends AbstractGradleceptionSmokeTest implements WithPluginValidation, ValidationMessageChecker {
 
     def setup() {
@@ -66,7 +69,12 @@
                 'org.asciidoctor.jvm.convert',
                 'com.gradle.plugin-publish',
                 'kotlin',
-                'com.autonomousapps.dependency-analysis'
+                'com.autonomousapps.dependency-analysis',
+                'dev.adamko.dokkatoo.DokkatooBasePlugin$Inject',
+                'dev.adamko.dokkatoo.adapters.DokkatooJavaAdapter$Inject',
+                'dev.adamko.dokkatoo.adapters.DokkatooKotlinAdapter$Inject',
+                'dev.adamko.dokkatoo.adapters.DokkatooAndroidAdapter$Inject',
+                'dev.adamko.dokkatoo.formats.DokkatooHtmlPlugin$Inject',
             ]
         }
 
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildSanityCheckConfigurationCacheSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildSanityCheckConfigurationCacheSmokeTest.groovy
index 58d25f6..f506a31 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildSanityCheckConfigurationCacheSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildSanityCheckConfigurationCacheSmokeTest.groovy
@@ -65,8 +65,7 @@
         result.task(":configuration-cache:codeQuality").outcome == TaskOutcome.UP_TO_DATE
         result.task(":docs:checkstyleApi").outcome == TaskOutcome.FROM_CACHE
         result.task(":internal-build-reports:allIncubationReportsZip").outcome == TaskOutcome.SUCCESS
-        // For now, this task is not CC compatible:
-        result.task(":architecture-test:checkBinaryCompatibility").outcome == TaskOutcome.SUCCESS
+        result.task(":architecture-test:checkBinaryCompatibility").outcome == TaskOutcome.FROM_CACHE
         result.task(":docs:javadocAll").outcome == TaskOutcome.FROM_CACHE
         result.task(":architecture-test:test").outcome == TaskOutcome.FROM_CACHE
         result.task(":tooling-api:toolingApiShadedJar").outcome == TaskOutcome.SUCCESS
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildSmokeTest.groovy
index 6fa6add..36847d9 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleBuildSmokeTest.groovy
@@ -17,9 +17,13 @@
 package org.gradle.smoketests
 
 import org.gradle.testkit.runner.TaskOutcome
+import spock.lang.TempDir
 
 class GradleBuildSmokeTest extends AbstractGradleceptionSmokeTest {
 
+    @TempDir
+    File targetDir
+
     def "can build Gradle distribution"() {
         def runner = runner(':distributions-full:binDistributionZip', ':distributions-full:binInstallation', '--stacktrace')
 //            .expectDeprecationWarning("The AbstractCompile.destinationDir property has been deprecated. " +
@@ -41,4 +45,18 @@
         result.task(":distributions-full:binDistributionZip").outcome == TaskOutcome.SUCCESS
         result.task(":distributions-full:binInstallation").outcome == TaskOutcome.SUCCESS
     }
+
+    def "can install Gradle distribution over itself"() {
+        def runner = runner('install', "-Pgradle_installPath=$targetDir", '--stacktrace')
+            .ignoreDeprecationWarnings("https://github.com/gradle/gradle-private/issues/3405")
+
+        when:
+        result = runner.build()
+        result = runner.build()
+
+        then:
+        result.task(":distributions-full:install").outcome == TaskOutcome.SUCCESS
+        new File(targetDir, "bin/gradle").exists()
+        new File(targetDir, "LICENSE").exists()
+    }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleGitPropertiesSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleGitPropertiesSmokeTest.groovy
index 17a8b61..3d28a60 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleGitPropertiesSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleGitPropertiesSmokeTest.groovy
@@ -16,10 +16,10 @@
 
 package org.gradle.smoketests
 
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.JDK11_OR_LATER)
+@Requires(UnitTestPreconditions.Jdk11OrLater)
 class GradleGitPropertiesSmokeTest extends AbstractPluginValidatingSmokeTest {
     @Override
     Map<String, Versions> getPluginsToValidate() {
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleVersionsPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleVersionsPluginSmokeTest.groovy
index b8c12c1..060ca39 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleVersionsPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GradleVersionsPluginSmokeTest.groovy
@@ -62,7 +62,7 @@
         declarationConfiguration.each {
             runner.expectDeprecationWarning(
                 "The $it configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations",
-                "https://github.com/ben-manes/gradle-versions-plugin/issues/718"
+                "https://github.com/gradle/gradle/issues/24895"
             )
         }
 
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GrettySmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GrettySmokeTest.groovy
index ac8fc5e..0b924bb 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GrettySmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/GrettySmokeTest.groovy
@@ -62,14 +62,11 @@
 
         when:
         def result = runner('checkContainerUp')
-            .expectDeprecationWarningIf(
-                grettyVersion < VersionNumber.parse('4.0.0'),
-                "The org.gradle.util.VersionNumber type has been deprecated. " +
-                    "This is scheduled to be removed in Gradle 9.0. " +
-                    "Consult the upgrading guide for further information: " +
-                    "https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#org_gradle_util_reports_deprecations",
-                ""
-            ).build()
+            .expectDeprecationWarning(
+                "The org.gradle.api.plugins.WarPluginConvention type has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#war_convention_deprecation",
+                "https://github.com/gretty-gradle-plugin/gretty/issues/266")
+            .expectLegacyDeprecationWarning(BaseDeprecations.CONVENTION_TYPE_DEPRECATION)
+            .build()
 
         then:
         result.task(':checkContainerUp').outcome == SUCCESS
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/JGitPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/JGitPluginSmokeTest.groovy
index d736a1b..07ddb97 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/JGitPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/JGitPluginSmokeTest.groovy
@@ -17,14 +17,14 @@
 package org.gradle.smoketests
 
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.vcs.fixtures.GitFileRepository
 import spock.lang.Issue
 
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
 
-@Requires(TestPrecondition.JDK11_OR_LATER)
+@Requires(UnitTestPreconditions.Jdk11OrLater)
 class JGitPluginSmokeTest extends AbstractPluginValidatingSmokeTest {
     @Issue('https://plugins.gradle.org/plugin/org.ajoberstar.grgit')
     @ToBeFixedForConfigurationCache(because = "Gradle.buildFinished")
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinAndroidDeprecations.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinAndroidDeprecations.groovy
index 615bebb..421916e 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinAndroidDeprecations.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinAndroidDeprecations.groovy
@@ -16,10 +16,14 @@
 
 package org.gradle.smoketests
 
-import org.gradle.util.GradleVersion
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.util.internal.VersionNumber
 
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
+
 class KotlinAndroidDeprecations extends BaseDeprecations implements WithKotlinDeprecations, WithAndroidDeprecations {
+    public static final DocumentationRegistry DOC_REGISTRY = new DocumentationRegistry()
+
     KotlinAndroidDeprecations(SmokeTestGradleRunner runner) {
         super(runner)
     }
@@ -28,7 +32,7 @@
         "Adding a Configuration as a dependency is a confusing behavior which isn't recommended. " +
             "This behavior is scheduled to be removed in Gradle 8.0. " +
             "If you're interested in inheriting the dependencies from the Configuration you are adding, you should use Configuration#extendsFrom instead. " +
-            "See https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.artifacts.Configuration.html#org.gradle.api.artifacts.Configuration:extendsFrom(org.gradle.api.artifacts.Configuration[]) for more details."
+            String.format(RECOMMENDATION,"information",  DOC_REGISTRY.getDslRefForProperty("org.gradle.api.artifacts.Configuration", "extendsFrom(org.gradle.api.artifacts.Configuration[])"))
 
     void expectKotlinConfigurationAsDependencyDeprecation(String version) {
         VersionNumber kotlinVersionNumber = VersionNumber.parse(version)
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinCompilerPluginsSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinCompilerPluginsSmokeTest.groovy
index a498f75..b71ab96 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinCompilerPluginsSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinCompilerPluginsSmokeTest.groovy
@@ -20,8 +20,8 @@
     @Override
     Map<String, Versions> getPluginsToValidate() {
         [
-            'org.jetbrains.kotlin.plugin.allopen': TestedVersions.kotlinPlugins,
-            'org.jetbrains.kotlin.plugin.spring': TestedVersions.kotlinPlugins
+            'org.jetbrains.kotlin.plugin.allopen': TestedVersions.kotlin,
+            'org.jetbrains.kotlin.plugin.spring': TestedVersions.kotlin
         ]
     }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginAndroidGroovyDSLSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginAndroidGroovyDSLSmokeTest.groovy
index 099526e..6b25370 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginAndroidGroovyDSLSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginAndroidGroovyDSLSmokeTest.groovy
@@ -16,11 +16,14 @@
 
 package org.gradle.smoketests
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
 import org.gradle.integtests.fixtures.android.AndroidHome
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.util.internal.VersionNumber
 
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
 
+@LocalOnly
 class KotlinPluginAndroidGroovyDSLSmokeTest extends AbstractSmokeTest {
 
     def "kotlin android on android-kotlin-example (kotlin=#kotlinPluginVersion, agp=#androidPluginVersion, workers=#workers)"(String kotlinPluginVersion, String androidPluginVersion, boolean workers) {
@@ -39,13 +42,16 @@
         }
 
         when:
-        def runner = createRunner(workers, kotlinPluginVersion, 'clean', ":app:testDebugUnitTestCoverage")
+        def runner = createRunner(workers, kotlinPluginVersion, androidPluginVersion, 'clean', ":app:testDebugUnitTestCoverage")
         def result = useAgpVersion(androidPluginVersion, runner)
             .deprecations(KotlinAndroidDeprecations) {
                 expectKotlinConfigurationAsDependencyDeprecation(kotlinPluginVersion)
                 expectAndroidOrKotlinWorkerSubmitDeprecation(androidPluginVersion, workers, kotlinPluginVersion)
                 expectReportDestinationPropertyDeprecation(androidPluginVersion)
                 expectKotlinCompileDestinationDirPropertyDeprecation(kotlinPluginVersion)
+                if (GradleContextualExecuter.configCache || VersionNumber.parse(kotlinPluginVersion) >= VersionNumber.parse("1.8.0")) {
+                    expectBuildIdentifierIsCurrentBuildDeprecation(androidPluginVersion)
+                }
             }.build()
 
         then:
@@ -65,10 +71,13 @@
         ].combinations()
     }
 
-    private SmokeTestGradleRunner createRunner(boolean workers, String kotlinVersion, String... tasks) {
+    private SmokeTestGradleRunner createRunner(boolean workers, String kotlinVersion, String androidPluginVersion, String... tasks) {
         return KotlinPluginSmokeTest.runnerFor(this, workers, VersionNumber.parse(kotlinVersion), tasks)
             .deprecations(KotlinPluginSmokeTest.KotlinDeprecations) {
                 expectOrgGradleUtilWrapUtilDeprecation(kotlinVersion)
+                expectBasePluginConventionDeprecation(kotlinVersion, androidPluginVersion)
+                expectProjectConventionDeprecation(kotlinVersion, androidPluginVersion)
+                expectConventionTypeDeprecation(kotlinVersion, androidPluginVersion)
             }
     }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginAndroidKotlinDSLSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginAndroidKotlinDSLSmokeTest.groovy
index c6b6af9..88ad024 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginAndroidKotlinDSLSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginAndroidKotlinDSLSmokeTest.groovy
@@ -16,11 +16,14 @@
 
 package org.gradle.smoketests
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
 import org.gradle.integtests.fixtures.android.AndroidHome
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.util.internal.VersionNumber
 
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
 
+@LocalOnly(because = "Needs Android environment")
 class KotlinPluginAndroidKotlinDSLSmokeTest extends AbstractSmokeTest {
 
     def "kotlin android on android-kotlin-example-kotlin-dsl (kotlin=#kotlinPluginVersion, agp=#androidPluginVersion, workers=#workers)"(String kotlinPluginVersion, String androidPluginVersion, boolean workers) {
@@ -39,14 +42,16 @@
         }
 
         when:
-        def runner = createRunner(workers, kotlinPluginVersion, 'clean', ':app:testDebugUnitTestCoverage')
-
+        def runner = createRunner(workers, kotlinPluginVersion, androidPluginVersion, 'clean', ':app:testDebugUnitTestCoverage')
         def result = useAgpVersion(androidPluginVersion, runner)
             .deprecations(KotlinAndroidDeprecations) {
                 expectKotlinConfigurationAsDependencyDeprecation(kotlinPluginVersion)
                 expectAndroidOrKotlinWorkerSubmitDeprecation(androidPluginVersion, workers, kotlinPluginVersion)
                 expectReportDestinationPropertyDeprecation(androidPluginVersion)
                 expectKotlinCompileDestinationDirPropertyDeprecation(kotlinPluginVersion)
+                if (GradleContextualExecuter.isConfigCache() || VersionNumber.parse(kotlinPluginVersion) >= VersionNumber.parse("1.8.0")) {
+                    expectBuildIdentifierIsCurrentBuildDeprecation(androidPluginVersion)
+                }
             }.build()
 
         then:
@@ -55,8 +60,8 @@
         where:
 // To run a specific combination, set the values here, uncomment the following four lines
 //  and comment out the lines coming after
-//        kotlinPluginVersion = TestedVersions.kotlin.versions.last()
-//        androidPluginVersion = TestedVersions.androidGradle.versions.last()
+//        kotlinPluginVersion = TestedVersions.kotlin.latestStable()
+//        androidPluginVersion = TestedVersions.androidGradle.latestStable()
 //        workers = false
 
         [kotlinPluginVersion, androidPluginVersion, workers] << [
@@ -66,10 +71,13 @@
         ].combinations()
     }
 
-    private SmokeTestGradleRunner createRunner(boolean workers, String kotlinVersion, String... tasks) {
+    private SmokeTestGradleRunner createRunner(boolean workers, String kotlinVersion, String agpVersion, String... tasks) {
         return KotlinPluginSmokeTest.runnerFor(this, workers, VersionNumber.parse(kotlinVersion), tasks)
             .deprecations(KotlinPluginSmokeTest.KotlinDeprecations) {
                 expectOrgGradleUtilWrapUtilDeprecation(kotlinVersion)
+                expectBasePluginConventionDeprecation(kotlinVersion, agpVersion)
+                expectProjectConventionDeprecation(kotlinVersion, agpVersion)
+                expectConventionTypeDeprecation(kotlinVersion, agpVersion)
             }
     }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy
index a6323c4..e2ba3e5 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPluginSmokeTest.groovy
@@ -17,12 +17,14 @@
 package org.gradle.smoketests
 
 import org.gradle.api.JavaVersion
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import org.gradle.util.GradleVersion
 import org.gradle.util.internal.VersionNumber
 import spock.lang.Issue
 
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
 import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE
 import static org.junit.Assume.assumeFalse
@@ -43,6 +45,12 @@
                 expectKotlinArchiveNameDeprecation(version)
                 expectAbstractCompileDestinationDirDeprecation(version)
                 expectOrgGradleUtilWrapUtilDeprecation(version)
+                expectProjectConventionDeprecation(version)
+                expectConventionTypeDeprecation(version)
+                expectJavaPluginConventionDeprecation(version)
+                if (GradleContextualExecuter.isConfigCache()) {
+                    expectBasePluginConventionDeprecation(version)
+                }
             }.build()
 
         then:
@@ -54,6 +62,9 @@
             .deprecations(KotlinDeprecations) {
                 if (GradleContextualExecuter.isNotConfigCache()) {
                     expectOrgGradleUtilWrapUtilDeprecation(version)
+                    expectProjectConventionDeprecation(version)
+                    expectConventionTypeDeprecation(version)
+                    expectJavaPluginConventionDeprecation(version)
                 }
             }.build()
 
@@ -88,6 +99,10 @@
                 expectKotlinCompileDestinationDirPropertyDeprecation(version)
                 expectKotlinArchiveNameDeprecation(version)
                 expectOrgGradleUtilWrapUtilDeprecation(version)
+                expectProjectConventionDeprecation(version)
+                expectConventionTypeDeprecation(version)
+                expectJavaPluginConventionDeprecation(version)
+                expectBasePluginConventionDeprecation(version)
             }.build()
 
         then:
@@ -135,6 +150,12 @@
                 expectKotlinArchiveNameDeprecation(kotlinVersion)
                 expectAbstractCompileDestinationDirDeprecation(kotlinVersion)
                 expectOrgGradleUtilWrapUtilDeprecation(kotlinVersion)
+                expectProjectConventionDeprecation(kotlinVersion)
+                expectConventionTypeDeprecation(kotlinVersion)
+                expectJavaPluginConventionDeprecation(kotlinVersion)
+                if (GradleContextualExecuter.isConfigCache()) {
+                    expectBasePluginConventionDeprecation(kotlinVersion)
+                }
             }.build()
 
         then:
@@ -175,6 +196,9 @@
             .deprecations(KotlinDeprecations) {
                 expectAbstractCompileDestinationDirDeprecation(kotlinVersion)
                 expectOrgGradleUtilWrapUtilDeprecation(kotlinVersion)
+                expectProjectConventionDeprecation(kotlinVersion)
+                expectConventionTypeDeprecation(kotlinVersion)
+                expectJavaPluginConventionDeprecation(kotlinVersion)
             }.build()
 
         then:
@@ -185,6 +209,9 @@
             .deprecations(KotlinDeprecations) {
                 if (GradleContextualExecuter.isNotConfigCache()) {
                     expectOrgGradleUtilWrapUtilDeprecation(kotlinVersion)
+                    expectProjectConventionDeprecation(kotlinVersion)
+                    expectConventionTypeDeprecation(kotlinVersion)
+                    expectJavaPluginConventionDeprecation(kotlinVersion)
                 }
             }.build()
 
@@ -218,12 +245,15 @@
         """
 
         when:
-        def versionNumber = VersionNumber.parse(kotlinVersion)
-        def result = runner(false, versionNumber, ':tasks')
-            .expectDeprecationWarning("The TestReport.reportOn(Object...) method has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the testResults method instead. See https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.tasks.testing.TestReport.html#org.gradle.api.tasks.testing.TestReport:testResults for more details.", '')
-            .expectDeprecationWarning("The TestReport.destinationDir property has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the destinationDirectory property instead. See https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.tasks.testing.TestReport.html#org.gradle.api.tasks.testing.TestReport:destinationDir for more details.", '')
+        def result = runner(false, VersionNumber.parse(kotlinVersion), ':tasks')
             .deprecations(KotlinDeprecations) {
                 expectOrgGradleUtilWrapUtilDeprecation(kotlinVersion)
+                expectTestReportReportOnDeprecation(kotlinVersion)
+                expectTestReportDestinationDirOnDeprecation(kotlinVersion)
+                expectProjectConventionDeprecation(kotlinVersion)
+                expectConventionTypeDeprecation(kotlinVersion)
+                expectJavaPluginConventionDeprecation(kotlinVersion)
+                expectBuildIdentifierNameDeprecation(kotlinVersion)
             }
             .build()
 
@@ -277,11 +307,20 @@
         def testRunner = runner(false, versionNumber, ':resolve', '--stacktrace')
 
         if (versionNumber < VersionNumber.parse('1.7.22')) {
-            testRunner.expectDeprecationWarning("The AbstractCompile.destinationDir property has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the destinationDirectory property instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_7.html#compile_task_wiring", '')
+            testRunner.expectLegacyDeprecationWarning("The AbstractCompile.destinationDir property has been deprecated. This is scheduled to be removed in Gradle 9.0. " +
+                "Please use the destinationDirectory property instead. " +
+                "Consult the upgrading guide for further information: ${new DocumentationRegistry().getDocumentationFor("upgrading_version_7", "compile_task_wiring")}")
         }
 
         testRunner.deprecations(KotlinDeprecations) {
             expectOrgGradleUtilWrapUtilDeprecation(kotlinVersion)
+            2.times {
+                expectProjectConventionDeprecation(kotlinVersion)
+                expectConventionTypeDeprecation(kotlinVersion)
+                expectJavaPluginConventionDeprecation(kotlinVersion)
+            }
+            expectConfigureUtilDeprecation(kotlinVersion)
+            expectBuildIdentifierNameDeprecation(kotlinVersion)
         }
 
         def result = testRunner.build()
@@ -314,7 +353,7 @@
 
     @Override
     Map<String, String> getExtraPluginsRequiredForValidation(String testedPluginId, String version) {
-        def androidVersion = TestedVersions.androidGradle.latestStable()
+        def androidVersion = AGP_VERSIONS.latestStable
         if (testedPluginId in ['org.jetbrains.kotlin.kapt', 'org.jetbrains.kotlin.plugin.scripting']) {
             return ['org.jetbrains.kotlin.jvm': version]
         }
@@ -335,6 +374,7 @@
             if (isAndroidKotlinPlugin(testedPluginId)) {
                 buildFile << """
                     android {
+                        namespace = "org.gradle.smoke.test"
                         compileSdkVersion 24
                         buildToolsVersion '${TestedVersions.androidTools}'
                     }
@@ -383,9 +423,11 @@
     }
 
     static class KotlinDeprecations extends BaseDeprecations implements WithKotlinDeprecations {
+        public static final DocumentationRegistry DOC_REGISTRY = new DocumentationRegistry()
+
         private static final String ARCHIVE_NAME_DEPRECATION = "The AbstractArchiveTask.archiveName property has been deprecated. " +
             "This is scheduled to be removed in Gradle 8.0. Please use the archiveFileName property instead. " +
-            "See https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.tasks.bundling.AbstractArchiveTask.html#org.gradle.api.tasks.bundling.AbstractArchiveTask:archiveName for more details."
+            String.format(RECOMMENDATION, "information", DOC_REGISTRY.getDslRefForProperty("org.gradle.api.tasks.bundling.AbstractArchiveTask", "archiveName"))
 
         KotlinDeprecations(SmokeTestGradleRunner runner) {
             super(runner)
@@ -413,25 +455,126 @@
 
         void expectAbstractCompileDestinationDirDeprecation(String version) {
             VersionNumber versionNumber = VersionNumber.parse(version)
-            runner.expectDeprecationWarningIf(
+            runner.expectLegacyDeprecationWarningIf(
                 versionNumber <= VersionNumber.parse("1.6.21"),
                 "The AbstractCompile.destinationDir property has been deprecated. " +
                     "This is scheduled to be removed in Gradle 9.0. " +
                     "Please use the destinationDirectory property instead. " +
-                    "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_7.html#compile_task_wiring",
-                ""
+                    "Consult the upgrading guide for further information: ${DOC_REGISTRY.getDocumentationFor("upgrading_version_7", "compile_task_wiring")}"
             )
         }
 
         void expectOrgGradleUtilWrapUtilDeprecation(String version) {
             VersionNumber versionNumber = VersionNumber.parse(version)
-            runner.expectDeprecationWarningIf(
+            runner.expectLegacyDeprecationWarningIf(
                 versionNumber < VersionNumber.parse("1.7.20"),
                 "The org.gradle.util.WrapUtil type has been deprecated. " +
                     "This is scheduled to be removed in Gradle 9.0. " +
                     "Consult the upgrading guide for further information: " +
-                    "https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_7.html#org_gradle_util_reports_deprecations",
-                ""
+                    "${DOC_REGISTRY.getDocumentationFor("upgrading_version_7", "org_gradle_util_reports_deprecations")}"
+            )
+        }
+
+        void expectTestReportReportOnDeprecation(String version) {
+            VersionNumber versionNumber = VersionNumber.parse(version)
+            runner.expectLegacyDeprecationWarningIf(
+                versionNumber.baseVersion < VersionNumber.parse("1.8.20"),
+                "The TestReport.reportOn(Object...) method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Please use the testResults method instead. " +
+                    String.format(RECOMMENDATION,"information",  DOC_REGISTRY.getDslRefForProperty("org.gradle.api.tasks.testing.TestReport", "testResults"))
+            )
+        }
+
+        void expectTestReportDestinationDirOnDeprecation(String version) {
+            VersionNumber versionNumber = VersionNumber.parse(version)
+            runner.expectLegacyDeprecationWarningIf(
+                versionNumber.baseVersion < VersionNumber.parse("1.8.20"),
+                "The TestReport.destinationDir property has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Please use the destinationDirectory property instead. " +
+                    String.format(RECOMMENDATION, "information", DOC_REGISTRY.getDslRefForProperty("org.gradle.api.tasks.testing.TestReport", "destinationDir"))
+            )
+        }
+
+        void expectProjectConventionDeprecation(String kotlinVersion) {
+            VersionNumber kotlinVersionNumber = VersionNumber.parse(kotlinVersion)
+            runner.expectLegacyDeprecationWarningIf(
+                kotlinVersionNumber < VersionNumber.parse("1.7.22"),
+                PROJECT_CONVENTION_DEPRECATION
+            )
+        }
+
+        void expectBasePluginConventionDeprecation(String kotlinVersion) {
+            VersionNumber kotlinVersionNumber = VersionNumber.parse(kotlinVersion)
+            runner.expectLegacyDeprecationWarningIf(
+                kotlinVersionNumber < VersionNumber.parse("1.7.0"),
+                BASE_PLUGIN_CONVENTION_DEPRECATION
+            )
+        }
+
+        void expectBasePluginConventionDeprecation(String kotlinVersion, String agpVersion) {
+            VersionNumber kotlinVersionNumber = VersionNumber.parse(kotlinVersion)
+            VersionNumber agpVersionNumber = VersionNumber.parse(agpVersion)
+            runner.expectLegacyDeprecationWarningIf(
+                agpVersionNumber < VersionNumber.parse("7.4.0") || kotlinVersionNumber < VersionNumber.parse("1.7.0"),
+                BASE_PLUGIN_CONVENTION_DEPRECATION
+            )
+        }
+
+        void expectJavaPluginConventionDeprecation(String kotlinVersion) {
+            VersionNumber kotlinVersionNumber = VersionNumber.parse(kotlinVersion)
+            runner.expectLegacyDeprecationWarningIf(
+                kotlinVersionNumber < VersionNumber.parse("1.7.22"),
+                JAVA_PLUGIN_CONVENTION_DEPRECATION
+            )
+        }
+
+        void expectProjectConventionDeprecation(String kotlinVersion, String agpVersion) {
+            VersionNumber kotlinVersionNumber = VersionNumber.parse(kotlinVersion)
+            VersionNumber agpVersionNumber = VersionNumber.parse(agpVersion)
+            runner.expectLegacyDeprecationWarningIf(
+                agpVersionNumber < VersionNumber.parse("7.4.0") || (agpVersionNumber >= VersionNumber.parse("7.4.0") && kotlinVersionNumber < VersionNumber.parse("1.7.0")),
+                PROJECT_CONVENTION_DEPRECATION
+            )
+        }
+
+        void expectConventionTypeDeprecation(String kotlinVersion) {
+            VersionNumber kotlinVersionNumber = VersionNumber.parse(kotlinVersion)
+            runner.expectLegacyDeprecationWarningIf(
+                kotlinVersionNumber < VersionNumber.parse("1.7.22"),
+                CONVENTION_TYPE_DEPRECATION
+            )
+        }
+
+        void expectConventionTypeDeprecation(String kotlinVersion, String agpVersion) {
+            VersionNumber kotlinVersionNumber = VersionNumber.parse(kotlinVersion)
+            VersionNumber agpVersionNumber = VersionNumber.parse(agpVersion)
+            runner.expectLegacyDeprecationWarningIf(
+                agpVersionNumber < VersionNumber.parse("7.4.0") || (agpVersionNumber >= VersionNumber.parse("7.4.0") && kotlinVersionNumber < VersionNumber.parse("1.7.22")),
+                CONVENTION_TYPE_DEPRECATION
+            )
+        }
+
+        void expectConfigureUtilDeprecation(String version) {
+            VersionNumber versionNumber = VersionNumber.parse(version)
+            runner.expectLegacyDeprecationWarningIf(
+                versionNumber < VersionNumber.parse("1.7.22"),
+                "The org.gradle.util.ConfigureUtil type has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Consult the upgrading guide for further information: " +
+                    DOC_REGISTRY.getDocumentationFor("upgrading_version_8", "org_gradle_util_reports_deprecations")
+            )
+        }
+
+        void expectBuildIdentifierNameDeprecation(String kotlinVersion) {
+            VersionNumber versionNumber = VersionNumber.parse(kotlinVersion)
+            runner.expectDeprecationWarningIf(versionNumber >= VersionNumber.parse("1.8.20"),
+                "The BuildIdentifier.getName() method has been deprecated. " +
+                    "This is scheduled to be removed in Gradle 9.0. " +
+                    "Use getBuildPath() to get a unique identifier for the build. " +
+                    "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation",
+                "https://youtrack.jetbrains.com/issue/KT-58157"
             )
         }
     }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPrecompiledScriptPluginsSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPrecompiledScriptPluginsSmokeTest.groovy
index e77793a..0879166 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPrecompiledScriptPluginsSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/KotlinPrecompiledScriptPluginsSmokeTest.groovy
@@ -103,6 +103,6 @@
         result.task(':help').outcome == SUCCESS
 
         where:
-        pluginPublishGradleVersion << ['7.0', '6.0', '5.6.4']
+        pluginPublishGradleVersion << ['8.0', '7.0', '6.0', '5.6.4']
     }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/MicronautPluginsSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/MicronautPluginsSmokeTest.groovy
index c8adb0a..278564f 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/MicronautPluginsSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/MicronautPluginsSmokeTest.groovy
@@ -16,11 +16,16 @@
 
 package org.gradle.smoketests
 
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+
+@Requires(UnitTestPreconditions.Jdk11OrLater)
 class MicronautPluginsSmokeTest extends AbstractPluginValidatingSmokeTest {
     @Override
     String getBuildScriptConfigurationForValidation() {
-        """micronaut {
-                version '2.5.4'
+        """
+            micronaut {
+                version '${TestedVersions.micronaut}'
            }
 
            pluginManager.withPlugin('io.micronaut.application') {
@@ -37,8 +42,8 @@
     @Override
     Map<String, Versions> getPluginsToValidate() {
         [
-            "io.micronaut.library": TestedVersions.micronaut,
-            "io.micronaut.application": TestedVersions.micronaut,
+            "io.micronaut.library": Versions.of(TestedVersions.micronaut),
+            "io.micronaut.application": Versions.of(TestedVersions.micronaut),
         ]
     }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/NebulaPluginsSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/NebulaPluginsSmokeTest.groovy
index ed19178..7d3394f 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/NebulaPluginsSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/NebulaPluginsSmokeTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Ignore
 import spock.lang.Issue
 
@@ -202,7 +202,7 @@
     }
 
     @Issue('https://plugins.gradle.org/plugin/com.netflix.nebula.resolution-rules')
-    @Requires(TestPrecondition.JDK11_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk11OrEarlier)
     def 'nebula resolution rules plugin'() {
         when:
         file('rules.json') << """
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/PalantirConsistentVersionsPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/PalantirConsistentVersionsPluginSmokeTest.groovy
new file mode 100644
index 0000000..af6b16b
--- /dev/null
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/PalantirConsistentVersionsPluginSmokeTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 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.smoketests
+
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+import org.gradle.util.GradleVersion
+
+@Requires(UnitTestPreconditions.Jdk11OrLater)
+class PalantirConsistentVersionsPluginSmokeTest extends AbstractSmokeTest {
+
+    def 'basic functionality'() {
+        given:
+        buildFile << """
+            plugins {
+                id('java')
+                id("com.palantir.consistent-versions") version "${TestedVersions.palantirConsistentVersions}"
+            }
+            ${mavenCentralRepository()}
+        """
+
+        file("settings.gradle") << "include 'other'"
+        file("other/build.gradle") << """
+            plugins {
+                id("java")
+            }
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                implementation("com.google.guava:guava")
+            }
+        """
+        file("versions.props") << "com.google.guava:guava = 17.0"
+
+        def issueUrl = "https://github.com/gradle/gradle/issues/24895"
+
+        when:
+        runner('--write-locks', '--stacktrace')
+            .expectDeprecationWarning("The Project.getConvention() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#deprecated_access_to_conventions", issueUrl)
+            .expectDeprecationWarning("The org.gradle.api.plugins.Convention type has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#deprecated_access_to_conventions", issueUrl)
+            .expectDeprecationWarning("The org.gradle.api.plugins.JavaPluginConvention type has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#java_convention_deprecation", issueUrl)
+            .expectDeprecationWarning("The compileClasspathCopy configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations", issueUrl)
+            .expectDeprecationWarning("The runtimeClasspathCopy configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations", issueUrl)
+            .expectDeprecationWarning("The testCompileClasspathCopy configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations", issueUrl)
+            .expectDeprecationWarning("The testRuntimeClasspathCopy configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations", issueUrl)
+            .expectDeprecationWarning("The compileClasspathCopy configuration has been deprecated for consumption. This will fail with an error in Gradle 9.0. For more information, please refer to https://docs.gradle.org/${GradleVersion.current().version}/userguide/dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations.html in the Gradle documentation.", issueUrl)
+            .expectDeprecationWarning("The runtimeClasspathCopy configuration has been deprecated for consumption. This will fail with an error in Gradle 9.0. For more information, please refer to https://docs.gradle.org/${GradleVersion.current().version}/userguide/dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations.html in the Gradle documentation.", issueUrl)
+            .expectDeprecationWarning("The testCompileClasspathCopy configuration has been deprecated for consumption. This will fail with an error in Gradle 9.0. For more information, please refer to https://docs.gradle.org/${GradleVersion.current().version}/userguide/dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations.html in the Gradle documentation.", issueUrl)
+            .expectDeprecationWarning("The testRuntimeClasspathCopy configuration has been deprecated for consumption. This will fail with an error in Gradle 9.0. For more information, please refer to https://docs.gradle.org/${GradleVersion.current().version}/userguide/dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations.html in the Gradle documentation.", issueUrl)
+            .build()
+
+        then:
+        file("versions.lock").exists()
+
+        when:
+        def runner = runner("other:dependencies")
+            .expectDeprecationWarning("The Project.getConvention() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#deprecated_access_to_conventions", issueUrl)
+            .expectDeprecationWarning("The org.gradle.api.plugins.Convention type has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#deprecated_access_to_conventions", issueUrl)
+            .expectDeprecationWarning("The org.gradle.api.plugins.JavaPluginConvention type has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#java_convention_deprecation", issueUrl)
+            .expectDeprecationWarning("The compileClasspathCopy configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations", issueUrl)
+            .expectDeprecationWarning("The runtimeClasspathCopy configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations", issueUrl)
+            .expectDeprecationWarning("The testCompileClasspathCopy configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations", issueUrl)
+            .expectDeprecationWarning("The testRuntimeClasspathCopy configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 9.0. Please use another configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations", issueUrl)
+
+        def result = runner.build()
+
+        then:
+        result.output.contains("com.google.guava:guava -> 17.0")
+    }
+}
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/PlayPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/PlayPluginSmokeTest.groovy
index 9e69703..2315d66 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/PlayPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/PlayPluginSmokeTest.groovy
@@ -16,17 +16,17 @@
 
 package org.gradle.smoketests
 
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.integtests.fixtures.RepoScriptBlockUtil
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
 
 class PlayPluginSmokeTest extends AbstractPluginValidatingSmokeTest {
 
-    @Requires(TestPrecondition.JDK11_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk11OrEarlier)
     @ToBeFixedForConfigurationCache(because = "unsupported Configuration field")
     def 'build basic Play project'() {
         given:
@@ -54,9 +54,11 @@
         when:
         def result = runner('build')
             .expectLegacyDeprecationWarning(orgGradleUtilTypeDeprecation("VersionNumber", 7))
-            .expectLegacyDeprecationWarning(orgGradleUtilTypeDeprecation("VersionNumber", 8))
             .expectLegacyDeprecationWarning(orgGradleUtilTypeDeprecation("CollectionUtils", 7))
-            .expectLegacyDeprecationWarning(abstractArchiveTaskArchivePathDeprecation())
+            .expectLegacyDeprecationWarning(BaseDeprecations.ABSTRACT_ARCHIVE_TASK_ARCHIVE_PATH_DEPRECATION)
+            .expectLegacyDeprecationWarning(BaseDeprecations.PROJECT_CONVENTION_DEPRECATION)
+            .expectLegacyDeprecationWarning(BaseDeprecations.CONVENTION_TYPE_DEPRECATION)
+            .expectLegacyDeprecationWarning(BaseDeprecations.JAVA_PLUGIN_CONVENTION_DEPRECATION)
             .build()
 
         then:
@@ -66,15 +68,9 @@
     private String orgGradleUtilTypeDeprecation(String type, int major) {
         return "The org.gradle.util.$type type has been deprecated. " +
             "This is scheduled to be removed in Gradle 9.0. " +
-            "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_${major}.html#org_gradle_util_reports_deprecations"
+            "Consult the upgrading guide for further information: ${new DocumentationRegistry().getDocumentationFor("upgrading_version_${major}","org_gradle_util_reports_deprecations")}"
     }
 
-    private String abstractArchiveTaskArchivePathDeprecation() {
-        return "The AbstractArchiveTask.archivePath property has been deprecated." +
-                " This is scheduled to be removed in Gradle 9.0." +
-                " Please use the archiveFile property instead." +
-                " See https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.tasks.bundling.AbstractArchiveTask.html#org.gradle.api.tasks.bundling.AbstractArchiveTask:archivePath for more details.";
-    }
 
     @Override
     Map<String, Versions> getPluginsToValidate() {
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ProguardSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ProguardSmokeTest.groovy
index 73167c8..6a12b5e 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ProguardSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ProguardSmokeTest.groovy
@@ -18,8 +18,6 @@
 
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 
-import static org.gradle.internal.reflect.validation.Severity.ERROR
-
 class ProguardSmokeTest extends AbstractPluginValidatingSmokeTest implements ValidationMessageChecker {
     @Override
     Map<String, Versions> getPluginsToValidate() {
@@ -44,7 +42,7 @@
             }
 
             dependencies {
-                implementation 'com.guardsquare:proguard-gradle:7.0.0'
+                implementation 'com.guardsquare:proguard-gradle:${TestedVersions.proguardGradle}'
             }
 
             // Configure the validation task here, since there is no ProGuard plugin
@@ -54,66 +52,19 @@
                     // Only test this one task, since analyzing all the classes seems to cause some problems
                     configurations.runtimeClasspath.files.collect { project.zipTree(it).matching { include "**/ProGuardTask.*" } }
                 })
-                classpath.setFrom(configurations.runtimeClasspath)
+                classpath = configurations.runtimeClasspath
             }
 
             tasks.named("validateExternalPlugins") {
                 dependsOn(validationTask)
             }
         """
-        def propertiesWithoutAnnotations = [
-            'adaptclassstrings',
-            'adaptresourcefilecontents',
-            'adaptresourcefilenames',
-            'addconfigurationdebugging',
-            'allowaccessmodification',
-            'android',
-            'configurationFiles',
-            'dontnote',
-            'dontobfuscate',
-            'dontoptimize',
-            'dontpreverify',
-            'dontshrink',
-            'dontskipnonpubliclibraryclassmembers',
-            'dontusemixedcaseclassnames',
-            'dontwarn',
-            'dump',
-            'flattenpackagehierarchy',
-            'forceprocessing',
-            'ignorewarnings',
-            'inJarCounts',
-            'inJarFiles',
-            'inJarFilters',
-            'keepattributes',
-            'keepdirectories',
-            'keepkotlinmetadata',
-            'keeppackagenames',
-            'keepparameternames',
-            'libraryJarFiles',
-            'libraryJarFilters',
-            'mergeinterfacesaggressively',
-            'microedition',
-            'outJarFiles',
-            'outJarFilters',
-            'overloadaggressively',
-            'printconfiguration',
-            'printmapping',
-            'printseeds',
-            'printusage',
-            'renamesourcefileattribute',
-            'repackageclasses',
-            'skipnonpubliclibraryclasses',
-            'useuniqueclassmembernames',
-            'verbose'
-        ]
         validatePlugins {
             onPlugin("ProguardPlugin") {
                 passes()
             }
             onPlugin("proguard") {
-                failsWith(propertiesWithoutAnnotations.collectEntries { propertyName ->
-                    [(missingAnnotationMessage { type('proguard.gradle.ProGuardTask').property(propertyName).missingInputOrOutput().includeLink() }): ERROR]
-                })
+                passes()
             }
         }
     }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ShadowPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ShadowPluginSmokeTest.groovy
index 4c71f78..cd52c4a 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ShadowPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ShadowPluginSmokeTest.groovy
@@ -19,6 +19,7 @@
 import spock.lang.Issue
 
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
+
 class ShadowPluginSmokeTest extends AbstractPluginValidatingSmokeTest {
 
     @Issue('https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow')
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SmokeTestGradleRunner.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SmokeTestGradleRunner.groovy
index c3e1618..67efda2 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SmokeTestGradleRunner.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SmokeTestGradleRunner.groovy
@@ -67,6 +67,9 @@
      *      to record how it will happen.
      */
     SmokeTestGradleRunner expectDeprecationWarning(String warning, String followup) {
+        if (followup == null || followup.isBlank()) {
+            throw new IllegalArgumentException("Follow up is required! Did you mean to expect a legacy deprecation warning instead?")
+        }
         expectedDeprecationWarnings.add(warning)
         return this
     }
@@ -126,7 +129,7 @@
         @DelegatesTo.Target Class<U> deprecationClass,
         @DelegatesTo(
             genericTypeIndex = 0,
-            strategy=Closure.DELEGATE_FIRST)
+            strategy = Closure.DELEGATE_FIRST)
             Closure<T> closure) {
         deprecationClass.newInstance(this).tap(closure)
         return this
@@ -135,7 +138,7 @@
     def <T> SmokeTestGradleRunner deprecations(
         @DelegatesTo(
             value = BaseDeprecations.class,
-            strategy=Closure.DELEGATE_FIRST)
+            strategy = Closure.DELEGATE_FIRST)
             Closure<T> closure) {
         return deprecations(BaseDeprecations, closure)
     }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpotBugsPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpotBugsPluginSmokeTest.groovy
index cb8a395..e3c3433 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpotBugsPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpotBugsPluginSmokeTest.groovy
@@ -16,14 +16,14 @@
 
 package org.gradle.smoketests
 
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 class SpotBugsPluginSmokeTest extends AbstractPluginValidatingSmokeTest {
 
     @Issue('https://plugins.gradle.org/plugin/com.github.spotbugs')
-    @Requires(TestPrecondition.JDK11_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk11OrEarlier)
     def 'spotbugs plugin'() {
         given:
         buildFile << """
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpotlessPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpotlessPluginSmokeTest.groovy
index 54707cf..a7d8c35 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpotlessPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpotlessPluginSmokeTest.groovy
@@ -16,6 +16,10 @@
 
 package org.gradle.smoketests
 
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+
+@Requires(UnitTestPreconditions.Jdk11OrLater)
 class SpotlessPluginSmokeTest extends AbstractPluginValidatingSmokeTest {
     @Override
     Map<String, Versions> getPluginsToValidate() {
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpringBootPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpringBootPluginSmokeTest.groovy
index ce389fb..1b27654 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpringBootPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/SpringBootPluginSmokeTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.smoketests
 
+
 import org.gradle.internal.reflect.validation.ValidationMessageChecker
 import spock.lang.Issue
 
@@ -34,35 +35,42 @@
 
             ${mavenCentralRepository()}
 
+            application {
+                applicationDefaultJvmArgs = ['-DFOO=42']
+            }
+
             dependencies {
                 implementation 'org.springframework.boot:spring-boot-starter'
-                testImplementation 'org.springframework.boot:spring-boot-starter-test'
             }
-            
-            tasks.named('test') {
-                useJUnitPlatform()
+
+            testing.suites.test {
+                useJUnitJupiter()
+                dependencies {
+                    implementation 'org.springframework.boot:spring-boot-starter-test'
+                }
             }
         """.stripIndent()
 
         file('src/main/java/example/Application.java') << """
             package example;
-            
+
             import org.springframework.boot.SpringApplication;
             import org.springframework.boot.autoconfigure.SpringBootApplication;
-            
+
             @SpringBootApplication
             public class Application {
                 public static void main(String[] args) {
                     SpringApplication.run(Application.class, args);
+                    System.out.println("FOO: " + System.getProperty("FOO"));
                 }
             }
         """.stripIndent()
         file("src/test/java/example/ApplicationTest.java") << """
             package example;
-            
+
             import org.junit.jupiter.api.Test;
             import org.springframework.boot.test.context.SpringBootTest;
-            
+
             @SpringBootTest
             class ApplicationTest {
                 @Test
@@ -72,17 +80,31 @@
         """
 
         when:
-        def buildResult = runner('assembleBootDist', 'check').build()
+        def smokeTestRunner = runner('assembleBootDist', 'check')
+        // verified manually: the 3.0.2 version of Spring Boot plugin removed the deprecated API usage
+        smokeTestRunner.expectLegacyDeprecationWarning(BaseDeprecations.PROJECT_CONVENTION_DEPRECATION)
+        smokeTestRunner.expectLegacyDeprecationWarning(BaseDeprecations.CONVENTION_TYPE_DEPRECATION)
+        smokeTestRunner.expectLegacyDeprecationWarning(BaseDeprecations.JAVA_PLUGIN_CONVENTION_DEPRECATION)
+        def buildResult = smokeTestRunner.build()
 
         then:
         buildResult.task(':assembleBootDist').outcome == SUCCESS
         buildResult.task(':check').outcome == SUCCESS
 
         when:
-        def runResult = runner('bootRun').build()
+        smokeTestRunner = runner('bootRun')
+        smokeTestRunner.expectLegacyDeprecationWarning(BaseDeprecations.PROJECT_CONVENTION_DEPRECATION)
+        smokeTestRunner.expectLegacyDeprecationWarning(BaseDeprecations.CONVENTION_TYPE_DEPRECATION)
+        smokeTestRunner.expectDeprecationWarning(
+            BaseDeprecations.APPLICATION_PLUGIN_CONVENTION_DEPRECATION,
+            "No need to follow up as the 2.7.x branch already removed the convention usage"
+        )
+        smokeTestRunner.expectLegacyDeprecationWarning(BaseDeprecations.JAVA_PLUGIN_CONVENTION_DEPRECATION)
+        def runResult = smokeTestRunner.build()
 
         then:
         runResult.task(':bootRun').outcome == SUCCESS
+        runResult.output.contains("FOO: 42")
     }
 
     @Override
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/TestRetryPluginSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/TestRetryPluginSmokeTest.groovy
index bcaac71..7219428 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/TestRetryPluginSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/TestRetryPluginSmokeTest.groovy
@@ -41,6 +41,7 @@
         buildFile << """
             dependencies {
                 testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
+                testRuntimeOnly("org.junit.platform:junit-platform-launcher")
             }
 
             test {
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ThirdPartyGradleModuleMetadataSmokeTest.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ThirdPartyGradleModuleMetadataSmokeTest.groovy
index c64770e..6a85d6e 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ThirdPartyGradleModuleMetadataSmokeTest.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/ThirdPartyGradleModuleMetadataSmokeTest.groovy
@@ -19,14 +19,14 @@
 import groovy.json.JsonSlurper
 import org.gradle.api.JavaVersion
 import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testkit.runner.BuildResult
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
 /**
  * JDK11 or later since AGP 7.x requires Java11
  */
-@Requires(TestPrecondition.JDK11_OR_LATER)
+@Requires(UnitTestPreconditions.Jdk11OrLater)
 class ThirdPartyGradleModuleMetadataSmokeTest extends AbstractSmokeTest {
 
     @Override
@@ -49,7 +49,7 @@
         given:
         BuildResult result
         useSample("gmm-example")
-        def kotlinVersion = TestedVersions.kotlin.latestStartsWith("1.7.10")
+        def kotlinVersion = "1.7.10"
         def androidPluginVersion = AGP_VERSIONS.getLatestOfMinor("7.3")
         def arch = OperatingSystem.current().macOsX ? 'MacosX64' : 'LinuxX64'
 
@@ -147,6 +147,10 @@
     private static SmokeTestGradleRunner expectingDeprecations(SmokeTestGradleRunner runner, String kotlinVersion, String agpVersion) {
         return runner.deprecations(KotlinPluginSmokeTest.KotlinDeprecations) {
             expectOrgGradleUtilWrapUtilDeprecation(kotlinVersion)
+            expectProjectConventionDeprecation(kotlinVersion, agpVersion)
+            expectConventionTypeDeprecation(kotlinVersion, agpVersion)
+            expectJavaPluginConventionDeprecation(kotlinVersion)
+            expectBasePluginConventionDeprecation(kotlinVersion, agpVersion)
         }
     }
 
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/WithAndroidDeprecations.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/WithAndroidDeprecations.groovy
index 2ae981f..b825cc2 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/WithAndroidDeprecations.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/WithAndroidDeprecations.groovy
@@ -17,18 +17,82 @@
 package org.gradle.smoketests
 
 import groovy.transform.SelfType
+import org.gradle.util.GradleVersion
 import org.gradle.util.internal.VersionNumber
 
 @SelfType(BaseDeprecations)
 trait WithAndroidDeprecations implements WithReportDeprecations {
     private static final VersionNumber AGP_VERSION_WITH_FIXED_NEW_WORKERS_API = VersionNumber.parse('4.2')
+    private static final VersionNumber AGP_VERSION_WITHOUT_CONVENTION_USAGES = VersionNumber.parse('7.4')
+    private static final VersionNumber AGP_VERSION_WITHOUT_CONFIG_UTIL = VersionNumber.parse('8.0.0-rc01')
 
     boolean androidPluginUsesOldWorkerApi(String agpVersion) {
-        VersionNumber agpVersionNumber = VersionNumber.parse(agpVersion)
-        agpVersionNumber < AGP_VERSION_WITH_FIXED_NEW_WORKERS_API
+        versionIsLower(agpVersion, AGP_VERSION_WITH_FIXED_NEW_WORKERS_API)
+    }
+
+    boolean androidPluginUsesConventions(String agpVersion) {
+        versionIsLower(agpVersion, AGP_VERSION_WITHOUT_CONVENTION_USAGES)
+    }
+
+    private boolean versionIsLower(String version, VersionNumber threshold) {
+        VersionNumber versionNumber = VersionNumber.parse(version)
+        versionNumber < threshold
     }
 
     void expectAndroidWorkerExecutionSubmitDeprecationWarning(String agpVersion) {
         runner.expectLegacyDeprecationWarningIf(androidPluginUsesOldWorkerApi(agpVersion), WORKER_SUBMIT_DEPRECATION)
     }
+
+    void expectProjectConventionDeprecationWarning(String agpVersion) {
+        runner.expectLegacyDeprecationWarningIf(androidPluginUsesConventions(agpVersion), PROJECT_CONVENTION_DEPRECATION)
+    }
+
+    void expectAndroidConventionTypeDeprecationWarning(String agpVersion) {
+        runner.expectLegacyDeprecationWarningIf(androidPluginUsesConventions(agpVersion), CONVENTION_TYPE_DEPRECATION)
+    }
+
+    void expectBasePluginConventionDeprecation(String agpVersion) {
+        runner.expectLegacyDeprecationWarningIf(androidPluginUsesConventions(agpVersion), BASE_PLUGIN_CONVENTION_DEPRECATION)
+    }
+
+    void expectConfigUtilDeprecationWarning(String agpVersion) {
+        runner.expectLegacyDeprecationWarningIf(
+            versionIsLower(agpVersion, AGP_VERSION_WITHOUT_CONFIG_UTIL),
+            "The org.gradle.util.ConfigureUtil type has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Consult the upgrading guide for further information: " +
+                "https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#org_gradle_util_reports_deprecations",
+        )
+    }
+
+    void expectBuildIdentifierIsCurrentBuildDeprecation(String agpVersion) {
+        VersionNumber agpVersionNumber = VersionNumber.parse(agpVersion)
+        runner.expectLegacyDeprecationWarningIf(
+            agpVersionNumber < VersionNumber.parse("8.0.0-rc01"),
+            "The BuildIdentifier.isCurrentBuild() method has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Use getBuildPath() to get a unique identifier for the build. " +
+                "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation"
+        )
+    }
+
+    void expectBuildIdentifierIsCurrentBuildDeprecation() {
+        runner.expectDeprecationWarning(
+            "The BuildIdentifier.isCurrentBuild() method has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Use getBuildPath() to get a unique identifier for the build. " +
+                "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation",
+            "https://issuetracker.google.com/issues/279306626"
+        )
+    }
+
+    void expectBuildIdentifierNameDeprecation() {
+        runner.expectDeprecationWarning(
+            "The BuildIdentifier.getName() method has been deprecated. " +
+                "This is scheduled to be removed in Gradle 9.0. " +
+                "Use getBuildPath() to get a unique identifier for the build. " +
+                "Consult the upgrading guide for further information: https://docs.gradle.org/${GradleVersion.current().version}/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation",
+            "https://issuetracker.google.com/issues/279306626"
+        )
+    }
 }
diff --git a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/WithReportDeprecations.groovy b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/WithReportDeprecations.groovy
index 278ffbb..22eb1cc 100644
--- a/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/WithReportDeprecations.groovy
+++ b/subprojects/smoke-test/src/smokeTest/groovy/org/gradle/smoketests/WithReportDeprecations.groovy
@@ -17,15 +17,17 @@
 package org.gradle.smoketests
 
 import groovy.transform.SelfType
-import org.gradle.util.GradleVersion
+import org.gradle.api.internal.DocumentationRegistry
 import org.gradle.util.internal.VersionNumber
 
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
+
 @SelfType(BaseDeprecations)
 trait WithReportDeprecations {
     private static final String REPORT_DESTINATION_DEPRECATION = "The Report.destination property has been deprecated. " +
             "This is scheduled to be removed in Gradle 9.0. " +
             "Please use the outputLocation property instead. " +
-            "See https://docs.gradle.org/${GradleVersion.current().version}/dsl/org.gradle.api.reporting.Report.html#org.gradle.api.reporting.Report:destination for more details."
+        String.format(RECOMMENDATION,"information",  new DocumentationRegistry().getDslRefForProperty("org.gradle.api.reporting.Report", "destination"))
 
     void expectReportDestinationPropertyDeprecation(String agpVersion) {
         runner.expectDeprecationWarningIf(
diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/app/build.gradle.kts b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/app/build.gradle.kts
index cf32608..2728dac 100644
--- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/app/build.gradle.kts
+++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/android-kotlin-example-kotlin-dsl/app/build.gradle.kts
@@ -105,8 +105,8 @@
                 sourceDirectories.from(files(coverageSourceDirs))
                 executionData.from(files("${project.buildDir}/jacoco/${testTaskName}.exec"))
                 reports {
-                    xml.required.set(true)
-                    html.required.set(true)
+                    xml.required = true
+                    html.required = true
                 }
             }
         }
diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/consumer/java-app/build.gradle.kts b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/consumer/java-app/build.gradle.kts
index 4a9b493..eded446 100644
--- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/consumer/java-app/build.gradle.kts
+++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/consumer/java-app/build.gradle.kts
@@ -35,7 +35,7 @@
 }
 
 application {
-    mainClass.set("myapp.App")
+    mainClass = "myapp.App"
 }
 
 
diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/consumer/kotlin-app/build.gradle.kts b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/consumer/kotlin-app/build.gradle.kts
index 42a7d58..5807fb2 100644
--- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/consumer/kotlin-app/build.gradle.kts
+++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/consumer/kotlin-app/build.gradle.kts
@@ -40,7 +40,7 @@
 }
 
 application {
-    mainClass.set("myapp.AppKt")
+    mainClass = "myapp.AppKt"
 }
 
 
diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/android-library-single-variant/src/main/java/example/androidlibsingle/AndroidLibrarySingleVariantUtil.java b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/android-library-single-variant/src/main/java/example/androidlibsingle/AndroidLibrarySingleVariantUtil.java
index 80162ba..265a899 100644
--- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/android-library-single-variant/src/main/java/example/androidlibsingle/AndroidLibrarySingleVariantUtil.java
+++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/android-library-single-variant/src/main/java/example/androidlibsingle/AndroidLibrarySingleVariantUtil.java
@@ -6,4 +6,4 @@ public static String use() {
         System.out.println(info);
         return info;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/android-library/src/main/java/example/androidlib/AndroidLibraryUtil.java b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/android-library/src/main/java/example/androidlib/AndroidLibraryUtil.java
index 7e4be3a..5bf6ae7 100644
--- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/android-library/src/main/java/example/androidlib/AndroidLibraryUtil.java
+++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/android-library/src/main/java/example/androidlib/AndroidLibraryUtil.java
@@ -6,4 +6,4 @@ public static String use() {
         System.out.println(info);
         return info;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/java-library/src/main/java/example/javalib/JavaLibraryUtil.java b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/java-library/src/main/java/example/javalib/JavaLibraryUtil.java
index 08999b5..fdf066e 100644
--- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/java-library/src/main/java/example/javalib/JavaLibraryUtil.java
+++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/gmm-example/producer/java-library/src/main/java/example/javalib/JavaLibraryUtil.java
@@ -6,4 +6,4 @@ public static String use() {
         System.out.println(info);
         return info;
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/validate-external-gradle-plugin.gradle.kts b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/validate-external-gradle-plugin.gradle.kts
index 6bd1990..7f8711f 100644
--- a/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/validate-external-gradle-plugin.gradle.kts
+++ b/subprojects/smoke-test/src/smokeTest/resources/org/gradle/smoketests/validate-external-gradle-plugin.gradle.kts
@@ -67,17 +67,17 @@
     val idWithoutDots = pluginId.replace('.', '_')
     return project.tasks.register<ValidatePlugins>("validatePluginWithId_" + idWithoutDots) {
         group = "Plugin development"
-        outputFile.set(project.layout.buildDirectory.file("reports/plugins/validation-report-for-$idWithoutDots.txt"))
+        outputFile = project.layout.buildDirectory.file("reports/plugins/validation-report-for-$idWithoutDots.txt")
 
         val scriptHandler = project.buildscript as ScriptHandlerInternal
         val scriptClassPath = scriptHandler.scriptClassPath.asFiles
-        classpath.setFrom(scriptClassPath)
+        classpath = project.files(scriptClassPath)
 
         val archiveOperations = findArchiveOperations(project)
         val pluginClassesOf = pluginJars.stream()
             .map { zipPath: File? -> archiveOperations.zipTree(zipPath!!) }
             .collect(Collectors.toList())
-        classes.setFrom(pluginClassesOf)
+        classes = project.files(pluginClassesOf)
     }
 }
 
diff --git a/subprojects/smoke-test/src/testFixtures/groovy/org/gradle/test/GradleBuildJvmSpec.groovy b/subprojects/smoke-test/src/testFixtures/groovy/org/gradle/test/GradleBuildJvmSpec.groovy
new file mode 100644
index 0000000..0cbfb2f
--- /dev/null
+++ b/subprojects/smoke-test/src/testFixtures/groovy/org/gradle/test/GradleBuildJvmSpec.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 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.test
+
+import org.gradle.api.JavaVersion
+import org.gradle.api.specs.Spec
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
+
+class GradleBuildJvmSpec implements Spec<JvmInstallationMetadata> {
+
+    static boolean isAvailable() {
+        return AvailableJavaHomes.getAvailableJdk(new GradleBuildJvmSpec()) != null
+    }
+
+    @Override
+    boolean isSatisfiedBy(JvmInstallationMetadata jvm) {
+        return jvm.languageVersion == JavaVersion.VERSION_11
+    }
+}
diff --git a/subprojects/smoke-test/src/testFixtures/groovy/org/gradle/test/preconditions/SmokeTestPreconditions.groovy b/subprojects/smoke-test/src/testFixtures/groovy/org/gradle/test/preconditions/SmokeTestPreconditions.groovy
new file mode 100644
index 0000000..30ee633
--- /dev/null
+++ b/subprojects/smoke-test/src/testFixtures/groovy/org/gradle/test/preconditions/SmokeTestPreconditions.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.test.preconditions
+
+import org.gradle.test.GradleBuildJvmSpec
+import org.gradle.test.precondition.TestPrecondition
+
+class SmokeTestPreconditions {
+
+    static class GradleBuildJvmSpecAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return GradleBuildJvmSpec::isAvailable()
+        }
+    }
+
+}
diff --git a/subprojects/snapshots/src/main/java/org/gradle/internal/snapshot/SnapshotUtil.java b/subprojects/snapshots/src/main/java/org/gradle/internal/snapshot/SnapshotUtil.java
index 14c2da4..d0d6d07 100644
--- a/subprojects/snapshots/src/main/java/org/gradle/internal/snapshot/SnapshotUtil.java
+++ b/subprojects/snapshots/src/main/java/org/gradle/internal/snapshot/SnapshotUtil.java
@@ -113,4 +113,26 @@ public static ImmutableMultimap<String, HashCode> getRootHashes(FileSystemSnapsh
         });
         return builder.build();
     }
+
+    /**
+     * For a {@link RegularFileSnapshot} returns the file length, otherwise {@code 0}.
+     */
+    public static long getLength(FileSystemLocationSnapshot snapshot) {
+        return snapshot.accept(new FileSystemLocationSnapshot.FileSystemLocationSnapshotTransformer<Long>() {
+            @Override
+            public Long visitDirectory(DirectorySnapshot directorySnapshot) {
+                return 0L;
+            }
+
+            @Override
+            public Long visitRegularFile(RegularFileSnapshot fileSnapshot) {
+                return fileSnapshot.getMetadata().getLength();
+            }
+
+            @Override
+            public Long visitMissing(MissingFileSnapshot missingSnapshot) {
+                return 0L;
+            }
+        });
+    }
 }
diff --git a/subprojects/snapshots/src/main/java/org/gradle/internal/vfs/impl/DefaultFileSystemAccess.java b/subprojects/snapshots/src/main/java/org/gradle/internal/vfs/impl/DefaultFileSystemAccess.java
index f1636d9..4236dc5 100644
--- a/subprojects/snapshots/src/main/java/org/gradle/internal/vfs/impl/DefaultFileSystemAccess.java
+++ b/subprojects/snapshots/src/main/java/org/gradle/internal/vfs/impl/DefaultFileSystemAccess.java
@@ -79,7 +79,7 @@ public DefaultFileSystemAccess(
 
     @Override
     public FileSystemLocationSnapshot read(String location) {
-        return readLocation(location);
+        return readSnapshotFromLocation(location, () -> snapshot(location, SnapshottingFilter.EMPTY));
     }
 
     @Override
@@ -162,10 +162,6 @@ private FileSystemLocationSnapshot snapshot(String location, SnapshottingFilter
         });
     }
 
-    private FileSystemLocationSnapshot readLocation(String location) {
-        return readSnapshotFromLocation(location, () -> snapshot(location, SnapshottingFilter.EMPTY));
-    }
-
     private FileSystemLocationSnapshot readSnapshotFromLocation(
         String location,
         Supplier<FileSystemLocationSnapshot> readFromDisk
diff --git a/subprojects/snapshots/src/test/groovy/org/gradle/internal/fingerprint/impl/BrokenSymlinkNormalizationStrategyTest.groovy b/subprojects/snapshots/src/test/groovy/org/gradle/internal/fingerprint/impl/BrokenSymlinkNormalizationStrategyTest.groovy
index 21fcbbf..f348e7c 100644
--- a/subprojects/snapshots/src/test/groovy/org/gradle/internal/fingerprint/impl/BrokenSymlinkNormalizationStrategyTest.groovy
+++ b/subprojects/snapshots/src/test/groovy/org/gradle/internal/fingerprint/impl/BrokenSymlinkNormalizationStrategyTest.groovy
@@ -25,12 +25,12 @@
 import org.gradle.test.fixtures.file.CleanupTestDirectory
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 import spock.lang.Specification
 
-@Requires(TestPrecondition.SYMLINKS)
+@Requires(UnitTestPreconditions.Symlinks)
 @CleanupTestDirectory
 class BrokenSymlinkNormalizationStrategyTest extends Specification {
     @Rule
diff --git a/subprojects/snapshots/src/test/groovy/org/gradle/internal/snapshot/impl/DirectorySnapshotterStatisticsTest.groovy b/subprojects/snapshots/src/test/groovy/org/gradle/internal/snapshot/impl/DirectorySnapshotterStatisticsTest.groovy
index e835827..4ed287b 100644
--- a/subprojects/snapshots/src/test/groovy/org/gradle/internal/snapshot/impl/DirectorySnapshotterStatisticsTest.groovy
+++ b/subprojects/snapshots/src/test/groovy/org/gradle/internal/snapshot/impl/DirectorySnapshotterStatisticsTest.groovy
@@ -20,8 +20,8 @@
 import org.gradle.internal.hash.TestFileHasher
 import org.gradle.test.fixtures.file.CleanupTestDirectory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
@@ -96,7 +96,7 @@
         0 * _
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "can visit broken symlink"() {
         given:
         def rootDir = tmpDir.createDir("root")
@@ -115,7 +115,7 @@
         0 * _
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "can visit symlinked hierarchy"() {
         given:
         def rootDir = tmpDir.createDir("root")
@@ -136,7 +136,7 @@
         0 * _
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "can visit unreadable #type"() {
         given:
         def rootDir = tmpDir.createDir("root")
diff --git a/subprojects/snapshots/src/test/groovy/org/gradle/internal/snapshot/impl/DirectorySnapshotterTest.groovy b/subprojects/snapshots/src/test/groovy/org/gradle/internal/snapshot/impl/DirectorySnapshotterTest.groovy
index 8892ed5..83c8d76 100644
--- a/subprojects/snapshots/src/test/groovy/org/gradle/internal/snapshot/impl/DirectorySnapshotterTest.groovy
+++ b/subprojects/snapshots/src/test/groovy/org/gradle/internal/snapshot/impl/DirectorySnapshotterTest.groovy
@@ -31,8 +31,8 @@
 import org.gradle.internal.snapshot.SnapshottingFilter
 import org.gradle.test.fixtures.file.CleanupTestDirectory
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Issue
@@ -173,7 +173,7 @@
         ]*.absolutePath
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "symlinked directories in tree are marked as accessed via symlink"() {
         def rootDir = tmpDir.createDir("root")
         def linkTarget = tmpDir.createDir("linkTarget")
@@ -193,7 +193,7 @@
         unfilteredSubsnapshots == [snapshot]
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "symlinked directories are snapshot correctly"() {
         def rootDir = tmpDir.file("root")
         def linkTarget = tmpDir.createDir("linkTarget")
@@ -211,7 +211,7 @@
         unfilteredSubsnapshots == [snapshot]
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "symlinked directories and files can be filtered correctly"() {
         def rootDir = tmpDir.file("root")
         def linkTarget1 = tmpDir.createDir("linkTarget1")
@@ -261,7 +261,7 @@
             "included")
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "parent directories of filtered symlinked directories are marked as filtered"() {
         def rootDir = tmpDir.createDir("root")
         rootDir.createFile("included/text.txt")
@@ -295,7 +295,7 @@
         return relativePaths.collect { new File(rootDir, it).absolutePath }
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "can snapshot symlinked directories and files within another"() {
         def rootDir = tmpDir.file("root")
         def linkTarget1 = tmpDir.createDir("linkTarget1")
@@ -341,7 +341,7 @@
         unfilteredSubsnapshots == [snapshot]
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "broken symlinks are snapshotted as missing"() {
         def rootDir = tmpDir.createDir("root")
         rootDir.file('brokenSymlink').createLink("linkTarget")
@@ -372,7 +372,7 @@
         0 * _
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "can snapshot a directory with cycles introduced via symlinks"() {
         def rootDir = tmpDir.createDir("root")
         def dir = rootDir.file("dir").createDir()
@@ -391,7 +391,7 @@
         0 * _
     }
 
-    @Requires(TestPrecondition.SYMLINKS)
+    @Requires(UnitTestPreconditions.Symlinks)
     def "can snapshot a directory with symlink cycle inside"() {
         def rootDir = tmpDir.createDir("root")
         def first = rootDir.file("first")
@@ -411,7 +411,7 @@
         0 * _
     }
 
-    @Requires(TestPrecondition.FILE_PERMISSIONS)
+    @Requires(UnitTestPreconditions.FilePermissions)
     def "snapshotting unreadable #type fails"() {
         given:
         def rootDir = tmpDir.createDir("root")
@@ -439,7 +439,7 @@
     }
 
 
-    @Requires(TestPrecondition.UNIX_DERIVATIVE)
+    @Requires(UnitTestPreconditions.UnixDerivative)
     @Issue("https://github.com/gradle/gradle/issues/2552")
     def "snapshotting named pipe fails"() {
         def rootDir = tmpDir.createDir("root")
diff --git a/subprojects/soak/build.gradle.kts b/subprojects/soak/build.gradle.kts
index c6c5020..2c8eda3 100644
--- a/subprojects/soak/build.gradle.kts
+++ b/subprojects/soak/build.gradle.kts
@@ -5,6 +5,7 @@
 dependencies {
     testFixturesImplementation(project(":base-services"))
     testFixturesImplementation(project(":core"))
+    testFixturesImplementation(project(":logging"))
     testFixturesImplementation(project(":internal-integ-testing"))
     testFixturesImplementation(project(":jvm-services"))
 
diff --git a/subprojects/soak/src/integTest/groovy/org/gradle/buildinit/JavaApplicationInitSoakTest.groovy b/subprojects/soak/src/integTest/groovy/org/gradle/buildinit/JavaApplicationInitSoakTest.groovy
new file mode 100644
index 0000000..61f5fa3
--- /dev/null
+++ b/subprojects/soak/src/integTest/groovy/org/gradle/buildinit/JavaApplicationInitSoakTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.buildinit
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class JavaApplicationInitSoakTest extends AbstractIntegrationSpec {
+
+    def "toolchain auto-provisioning works"() {
+        given:
+        useTestDirectoryThatIsNotEmbeddedInAnotherBuild()
+        executer.beforeExecute {
+            requireOwnGradleUserHomeDir()
+        }
+
+        when:
+        succeeds('init', '--type', 'java-application', '--dsl', 'groovy')
+
+        and:
+        executer.withArgument("-Porg.gradle.java.installations.auto-detect=false")
+        executer.withArgument("-Porg.gradle.java.installations.auto-download=true")
+        succeeds('run')
+
+        then:
+        outputContains("Hello World!")
+    }
+}
diff --git a/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenCentralDependencyResolveIntegrationTest.groovy b/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenCentralDependencyResolveIntegrationTest.groovy
index c6ee64a..89429d2 100644
--- a/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenCentralDependencyResolveIntegrationTest.groovy
+++ b/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenCentralDependencyResolveIntegrationTest.groovy
@@ -16,10 +16,10 @@
 package org.gradle.connectivity
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 class MavenCentralDependencyResolveIntegrationTest extends AbstractIntegrationSpec {
     def "resolves a minimal dependency from Maven Central"() {
         given:
diff --git a/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenGoogleDependencyResolveIntegrationTest.groovy b/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenGoogleDependencyResolveIntegrationTest.groovy
index 7618890..e9edbb8 100644
--- a/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenGoogleDependencyResolveIntegrationTest.groovy
+++ b/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenGoogleDependencyResolveIntegrationTest.groovy
@@ -17,10 +17,10 @@
 package org.gradle.connectivity
 
 import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 class MavenGoogleDependencyResolveIntegrationTest extends AbstractDependencyResolutionTest {
 
     def setup() {
@@ -58,11 +58,11 @@
             configurations {
                 compile
             }
-            
+
             dependencies {
                 compile '$dependencyGav'
             }
-            
+
             task copyLibs(type: Copy) {
                 from configurations.compile
                 into "\$buildDir/libs"
diff --git a/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenJcenterDependencyResolveIntegrationTest.groovy b/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenJcenterDependencyResolveIntegrationTest.groovy
index 542eba8..7d85b22 100644
--- a/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenJcenterDependencyResolveIntegrationTest.groovy
+++ b/subprojects/soak/src/integTest/groovy/org/gradle/connectivity/MavenJcenterDependencyResolveIntegrationTest.groovy
@@ -16,15 +16,15 @@
 package org.gradle.connectivity
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.ONLINE)
+@Requires(UnitTestPreconditions.Online)
 class MavenJcenterDependencyResolveIntegrationTest extends AbstractIntegrationSpec {
     def "resolves a minimal dependency from bintray's jcenter"() {
         given:
-        executer.expectDeprecationWarning("The RepositoryHandler.jcenter() method has been deprecated.")
-        executer.expectDeprecationWarning("The RepositoryHandler.jcenter(Action<MavenArtifactRepository>) method has been deprecated.")
+        executer.expectDocumentedDeprecationWarning("The RepositoryHandler.jcenter() method has been deprecated. This is scheduled to be removed in Gradle 9.0. JFrog announced JCenter's sunset in February 2021. Use mavenCentral() instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_6.html#jcenter_deprecation")
+        executer.expectDocumentedDeprecationWarning("The RepositoryHandler.jcenter(Action<MavenArtifactRepository>) method has been deprecated. This is scheduled to be removed in Gradle 9.0. JFrog announced JCenter's sunset in February 2021. Use mavenCentral(Action<MavenArtifactRepository> instead. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_6.html#jcenter_deprecation")
         buildFile << """
 repositories {
     jcenter()
diff --git a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerCrossGroovyVersionIntegrationTest.groovy b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerCrossGroovyVersionIntegrationTest.groovy
index 215b410..7726872 100644
--- a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerCrossGroovyVersionIntegrationTest.groovy
+++ b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerCrossGroovyVersionIntegrationTest.groovy
@@ -17,16 +17,16 @@
 package org.gradle.testkit.runner
 
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testkit.runner.fixtures.NonCrossVersion
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.gradle.util.internal.TextUtil
 import spock.lang.IgnoreIf
 
 @IgnoreIf({ GradleContextualExecuter.embedded })
 @NonCrossVersion
-@Requires(TestPrecondition.JDK15_OR_EARLIER)
+@Requires(UnitTestPreconditions.Jdk15OrEarlier)
 class GradleRunnerCrossGroovyVersionIntegrationTest extends BaseGradleRunnerIntegrationTest {
 
     def "current TestKit can run build with old Gradle version that uses Groovy 2"() {
diff --git a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerFileSystemWatchingIntegrationTest.groovy b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerFileSystemWatchingIntegrationTest.groovy
index 7d852ab..734658a 100644
--- a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerFileSystemWatchingIntegrationTest.groovy
+++ b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerFileSystemWatchingIntegrationTest.groovy
@@ -18,10 +18,10 @@
 
 import com.gradle.enterprise.testing.annotations.LocalOnly
 import org.gradle.initialization.StartParameterBuildOptions
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testkit.runner.fixtures.Debug
 import org.gradle.testkit.runner.fixtures.NoDebug
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
 import static org.junit.Assume.assumeTrue
 
@@ -39,7 +39,7 @@
     }
 
     @NoDebug
-    @Requires(TestPrecondition.WINDOWS)
+    @Requires(UnitTestPreconditions.Windows)
     def "disables file system watching on Windows"() {
         when:
         def result = runAssemble()
@@ -48,7 +48,7 @@
     }
 
     @NoDebug
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "file system watching is enabled on non-Windows OSes"() {
         when:
         def result = runAssemble()
diff --git a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerGradleVersionIntegrationTest.groovy b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerGradleVersionIntegrationTest.groovy
index cce1e68..093c978 100644
--- a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerGradleVersionIntegrationTest.groovy
+++ b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerGradleVersionIntegrationTest.groovy
@@ -19,10 +19,11 @@
 import org.gradle.api.Action
 import org.gradle.integtests.fixtures.daemon.DaemonsFixture
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.TestKitPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testkit.runner.fixtures.NonCrossVersion
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.gradle.util.internal.DistributionLocator
 import spock.lang.Retry
 import spock.lang.Shared
@@ -30,7 +31,10 @@
 import static org.gradle.integtests.fixtures.RetryConditions.onIssueWithReleasedGradleVersion
 
 @NonCrossVersion
-@Requires(value = TestPrecondition.ONLINE, adhoc = { BaseGradleRunnerIntegrationTest.findLowestMajorGradleVersion() != null })
+@Requires([
+    UnitTestPreconditions.Online,
+    TestKitPreconditions.LowestMajorGradleIsAvailable
+])
 @Retry(condition = { onIssueWithReleasedGradleVersion(instance, failure) }, count = 2)
 class GradleRunnerGradleVersionIntegrationTest extends BaseGradleRunnerIntegrationTest {
     @Shared
@@ -70,10 +74,10 @@
         killDaemons(version)
 
         where:
-        type         | version                         | configurer
-        "embedded"   | buildContext.version.version    | { if (!GradleContextualExecuter.embedded) { it.withGradleInstallation(buildContext.gradleHomeDir) } }
-        "locator"    | lowestMajorGradleVersion | { it.withGradleDistribution(locator.getDistributionFor(GradleVersion.version(lowestMajorGradleVersion))) }
-        "production" | lowestMajorGradleVersion | { it.withGradleVersion(lowestMajorGradleVersion) }
+        type         | version                      | configurer
+        "embedded"   | buildContext.version.version | { if (!GradleContextualExecuter.embedded) { it.withGradleInstallation(buildContext.gradleHomeDir) } }
+        "locator"    | lowestMajorGradleVersion     | { it.withGradleDistribution(locator.getDistributionFor(GradleVersion.version(lowestMajorGradleVersion))) }
+        "production" | lowestMajorGradleVersion     | { it.withGradleVersion(lowestMajorGradleVersion) }
     }
 
     def "distributions are not stored in the test kit dir"() {
diff --git a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerRetryTest.groovy b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerRetryTest.groovy
index c0d03a7..da615ad 100644
--- a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerRetryTest.groovy
+++ b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerRetryTest.groovy
@@ -16,12 +16,10 @@
 
 package org.gradle.testkit.runner
 
-
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.GradleConnectionException
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-
-import static org.gradle.integtests.fixtures.RetryConditions.runsOnWindowsAndJava7or8
 
 class GradleRunnerRetryTest extends BaseGradleRunnerIntegrationTest {
 
@@ -45,7 +43,11 @@
         true
     }
 
-    @Requires(adhoc = { runsOnWindowsAndJava7or8() })
+    @Requires([
+        UnitTestPreconditions.Windows,
+        UnitTestPreconditions.Jdk7OrLater,
+        UnitTestPreconditions.Jdk8OrEarlier
+    ])
     def "retries if expected socket exception occurs"() {
         given:
         iteration++
@@ -60,7 +62,7 @@
         true
     }
 
-    @Requires(adhoc = { !runsOnWindowsAndJava7or8() })
+    @Requires(UnitTestPreconditions.NotWindowsJavaBefore9)
     def "does not retry on non-windows and non-java environments"() {
         given:
         iteration++
@@ -74,7 +76,11 @@
         ioe.cause?.message == "An existing connection was forcibly closed by the remote host"
     }
 
-    @Requires(adhoc = { runsOnWindowsAndJava7or8() })
+    @Requires([
+        UnitTestPreconditions.Windows,
+        UnitTestPreconditions.Jdk7OrLater,
+        UnitTestPreconditions.Jdk8OrEarlier
+    ])
     def "should fail for unexpected cause on client side"() {
         given:
         iteration++
@@ -88,7 +94,11 @@
         ioe.cause?.message == "A different cause"
     }
 
-    @Requires(adhoc = { runsOnWindowsAndJava7or8() })
+    @Requires([
+        UnitTestPreconditions.Windows,
+        UnitTestPreconditions.Jdk7OrLater,
+        UnitTestPreconditions.Jdk8OrEarlier
+    ])
     def "should fail for unexpected cause on daemon side"() {
         given:
         iteration++
diff --git a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerSupportedBuildJvmIntegrationTest.groovy b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerSupportedBuildJvmIntegrationTest.groovy
index 8ba8a89..fff0ba6 100644
--- a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerSupportedBuildJvmIntegrationTest.groovy
+++ b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerSupportedBuildJvmIntegrationTest.groovy
@@ -19,17 +19,18 @@
 import org.gradle.initialization.StartParameterBuildOptions
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.internal.os.OperatingSystem
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.gradle.testkit.runner.fixtures.NoDebug
 import org.gradle.testkit.runner.fixtures.NonCrossVersion
 import org.gradle.tooling.GradleConnectionException
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
 import spock.lang.Issue
 
 @NonCrossVersion
 class GradleRunnerSupportedBuildJvmIntegrationTest extends BaseGradleRunnerIntegrationTest {
     @NoDebug
-    @Requires(adhoc = { AvailableJavaHomes.getJdks("1.5", "1.6", "1.7") })
+    @Requires(IntegTestPreconditions.UnsupportedJavaHomeAvailable)
     def "fails when build is configured to use Java 7 or earlier"() {
         given:
         testDirectory.file("gradle.properties").writeProperties("org.gradle.java.home": jdk.javaHome.absolutePath)
@@ -51,7 +52,7 @@
 
     @Issue("https://github.com/gradle/gradle/issues/13957")
     @NoDebug
-    @Requires(adhoc = { AvailableJavaHomes.getJdks("1.8") })
+    @Requires(IntegTestPreconditions.Java8HomeAvailable)
     def "supports failing builds on older Java versions"() {
         given:
         testDirectory.file("gradle.properties").writeProperties("org.gradle.java.home": jdk.javaHome.absolutePath)
diff --git a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerUnsupportedFeatureFailureIntegrationTest.groovy b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerUnsupportedFeatureFailureIntegrationTest.groovy
index 5b9cf38..18adac9 100644
--- a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerUnsupportedFeatureFailureIntegrationTest.groovy
+++ b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerUnsupportedFeatureFailureIntegrationTest.groovy
@@ -17,15 +17,16 @@
 package org.gradle.testkit.runner
 
 import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testkit.runner.fixtures.Debug
 import org.gradle.testkit.runner.fixtures.NonCrossVersion
 import org.gradle.testkit.runner.fixtures.PluginUnderTest
-import org.gradle.testkit.runner.internal.feature.TestKitFeature
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.util.GradleVersion
 import spock.lang.Retry
 
 import static org.gradle.integtests.fixtures.RetryConditions.cleanProjectDir
+import static org.gradle.tooling.internal.consumer.DefaultGradleConnector.MINIMUM_SUPPORTED_GRADLE_VERSION
 
 @NonCrossVersion
 @Retry(condition = { failure.class != UnsupportedFeatureException && cleanProjectDir(instance) }, count = 2)
@@ -55,11 +56,11 @@
         }
     }
 
-    @Requires(TestPrecondition.JDK8_OR_EARLIER) // tests against old Gradle version that can only work with Java versions up tp 8
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier) // tests against old Gradle version that can only work with Java versions up tp 8
     @Debug
     def "fails informatively when trying to inspect build output in debug mode with unsupported gradle version"() {
-        def maxUnsupportedVersion = getMaxUnsupportedVersion(TestKitFeature.CAPTURE_BUILD_RESULT_OUTPUT_IN_DEBUG)
-        def minSupportedVersion = TestKitFeature.CAPTURE_BUILD_RESULT_OUTPUT_IN_DEBUG.since.version
+        def maxUnsupportedVersion = getPreviousVersion(MINIMUM_SUPPORTED_GRADLE_VERSION)
+        def minSupportedVersion = MINIMUM_SUPPORTED_GRADLE_VERSION.version
 
         given:
         buildFile << helloWorldTask()
@@ -74,13 +75,13 @@
 
         then:
         def e = thrown UnsupportedFeatureException
-        e.message == "The version of Gradle you are using ($maxUnsupportedVersion) does not capture build output in debug mode with the GradleRunner. Support for this is available in Gradle $minSupportedVersion and all later versions."
+        e.message == "The version of Gradle you are using ($maxUnsupportedVersion) is not supported by TestKit. TestKit supports all Gradle versions Gradle $minSupportedVersion and later."
     }
 
-    @Requires(TestPrecondition.JDK8_OR_EARLIER) // tests against old Gradle version that can only work with Java versions up tp 8
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier) // tests against old Gradle version that can only work with Java versions up tp 8
     def "fails informatively when trying to inject plugin classpath with unsupported gradle version"() {
-        def maxUnsupportedVersion = getMaxUnsupportedVersion(TestKitFeature.PLUGIN_CLASSPATH_INJECTION)
-        def minSupportedVersion = TestKitFeature.PLUGIN_CLASSPATH_INJECTION.since.version
+        def maxUnsupportedVersion = getPreviousVersion(MINIMUM_SUPPORTED_GRADLE_VERSION)
+        def minSupportedVersion = MINIMUM_SUPPORTED_GRADLE_VERSION.version
 
         given:
         buildScript plugin.useDeclaration
@@ -93,14 +94,14 @@
 
         then:
         def e = thrown UnsupportedFeatureException
-        e.message == "The version of Gradle you are using ($maxUnsupportedVersion) does not support plugin classpath injection. Support for this is available in Gradle $minSupportedVersion and all later versions."
+        e.message == "The version of Gradle you are using ($maxUnsupportedVersion) is not supported by TestKit. TestKit supports all Gradle versions Gradle $minSupportedVersion and later."
     }
 
-    @Requires(TestPrecondition.JDK8_OR_EARLIER) // tests against old Gradle version that can only work with Java versions up tp 8
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier) // tests against old Gradle version that can only work with Java versions up tp 8
     def "fails informatively if trying to use conventional plugin classpath on version that does not support injection"() {
         given:
-        def maxUnsupportedVersion = getMaxUnsupportedVersion(TestKitFeature.PLUGIN_CLASSPATH_INJECTION)
-        def minSupportedVersion = TestKitFeature.PLUGIN_CLASSPATH_INJECTION.since.version
+        def maxUnsupportedVersion = getPreviousVersion(MINIMUM_SUPPORTED_GRADLE_VERSION)
+        def minSupportedVersion = MINIMUM_SUPPORTED_GRADLE_VERSION.version
 
         buildScript plugin.useDeclaration
 
@@ -114,11 +115,11 @@
 
         then:
         def e = thrown UnsupportedFeatureException
-        e.message == "The version of Gradle you are using ($maxUnsupportedVersion) does not support plugin classpath injection. Support for this is available in Gradle $minSupportedVersion and all later versions."
+        e.message == "The version of Gradle you are using ($maxUnsupportedVersion) is not supported by TestKit. TestKit supports all Gradle versions Gradle $minSupportedVersion and later."
     }
 
-    static String getMaxUnsupportedVersion(TestKitFeature feature) {
-        RELEASED_VERSION_DISTRIBUTIONS.getPrevious(feature.since).version.version
+    static String getPreviousVersion(GradleVersion feature) {
+        RELEASED_VERSION_DISTRIBUTIONS.getPrevious(feature).version.version
     }
 
 }
diff --git a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerUnsupportedGradleVersionFailureIntegrationTest.groovy b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerUnsupportedGradleVersionFailureIntegrationTest.groovy
index 866b7a1..8dedc57 100644
--- a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerUnsupportedGradleVersionFailureIntegrationTest.groovy
+++ b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/GradleRunnerUnsupportedGradleVersionFailureIntegrationTest.groovy
@@ -18,6 +18,8 @@
 
 import org.gradle.testkit.runner.fixtures.NonCrossVersion
 
+import static org.gradle.tooling.internal.consumer.DefaultGradleConnector.MINIMUM_SUPPORTED_GRADLE_VERSION
+
 @NonCrossVersion
 class GradleRunnerUnsupportedGradleVersionFailureIntegrationTest extends BaseGradleRunnerIntegrationTest {
     def "fails informatively when trying to use unsupported gradle version"() {
@@ -34,6 +36,6 @@
 
         then:
         def e = thrown UnsupportedFeatureException
-        e.message == "The version of Gradle you are using (1.1) is not supported by TestKit. TestKit supports all Gradle versions 2.6 and later."
+        e.message == "The version of Gradle you are using (1.1) is not supported by TestKit. TestKit supports all Gradle versions $MINIMUM_SUPPORTED_GRADLE_VERSION and later."
     }
 }
diff --git a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/enduser/GradleRunnerSamplesEndUserIntegrationTest.groovy b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/enduser/GradleRunnerSamplesEndUserIntegrationTest.groovy
index 86597b2..3705ed1 100644
--- a/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/enduser/GradleRunnerSamplesEndUserIntegrationTest.groovy
+++ b/subprojects/test-kit/src/integTest/groovy/org/gradle/testkit/runner/enduser/GradleRunnerSamplesEndUserIntegrationTest.groovy
@@ -19,21 +19,19 @@
 
 import org.gradle.integtests.fixtures.Sample
 import org.gradle.integtests.fixtures.UsesSample
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testing.internal.util.RetryUtil
 import org.gradle.testkit.runner.fixtures.NoDebug
 import org.gradle.testkit.runner.fixtures.NonCrossVersion
-import org.gradle.util.Requires
 import org.junit.Rule
-import spock.lang.IgnoreIf
-
-import static org.gradle.util.TestPrecondition.JDK11_OR_EARLIER
-import static org.gradle.util.TestPrecondition.ONLINE
 
 @NonCrossVersion
 @NoDebug
-@IgnoreIf({ GradleContextualExecuter.embedded })
-// These tests run builds that themselves run a build in a test worker with 'gradleTestKit()' dependency, which needs to pick up Gradle modules from a real distribution
+@Requires(
+    value = IntegTestPreconditions.NotEmbeddedExecutor,
+    reason = "These tests run builds that themselves run a build in a test worker with 'gradleTestKit()' dependency, which needs to pick up Gradle modules from a real distribution")
 class GradleRunnerSamplesEndUserIntegrationTest extends BaseTestKitEndUserIntegrationTest {
 
     @Rule
@@ -87,7 +85,7 @@
         dsl << ['groovy', 'kotlin']
     }
 
-    @Requires([ONLINE, JDK11_OR_EARLIER])
+    @Requires([UnitTestPreconditions.Online, UnitTestPreconditions.Jdk11OrEarlier])
     // Uses Gradle 5.0 which does not support Java versions >11
     @UsesSample("testKit/gradleVersion")
     def gradleVersion() {
diff --git a/subprojects/test-kit/src/main/java/org/gradle/testkit/runner/internal/ToolingApiGradleExecutor.java b/subprojects/test-kit/src/main/java/org/gradle/testkit/runner/internal/ToolingApiGradleExecutor.java
index 8d05a8b..3765904 100644
--- a/subprojects/test-kit/src/main/java/org/gradle/testkit/runner/internal/ToolingApiGradleExecutor.java
+++ b/subprojects/test-kit/src/main/java/org/gradle/testkit/runner/internal/ToolingApiGradleExecutor.java
@@ -44,8 +44,8 @@
 import org.gradle.tooling.internal.consumer.DefaultBuildLauncher;
 import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
 import org.gradle.tooling.model.build.BuildEnvironment;
-import org.gradle.util.internal.CollectionUtils;
 import org.gradle.util.GradleVersion;
+import org.gradle.util.internal.CollectionUtils;
 import org.gradle.wrapper.GradleUserHomeLookup;
 
 import java.io.File;
@@ -110,7 +110,8 @@ public GradleExecutionResult run(GradleExecutionParameters parameters) {
             connection = gradleConnector.connect();
             targetGradleVersion = determineTargetGradleVersion(connection);
             if (targetGradleVersion.compareTo(TestKitFeature.RUN_BUILDS.getSince()) < 0) {
-                throw new UnsupportedFeatureException(String.format("The version of Gradle you are using (%s) is not supported by TestKit. TestKit supports all Gradle versions 2.6 and later.", targetGradleVersion.getVersion()));
+                throw new UnsupportedFeatureException(String.format("The version of Gradle you are using (%s) is not supported by TestKit. TestKit supports all Gradle versions %s and later.",
+                    targetGradleVersion.getVersion(), TestKitFeature.RUN_BUILDS.getSince()));
             }
 
             DefaultBuildLauncher launcher = (DefaultBuildLauncher) connection.newBuild();
@@ -129,9 +130,6 @@ public GradleExecutionResult run(GradleExecutionParameters parameters) {
             launcher.setEnvironmentVariables(parameters.getEnvironment());
 
             if (!parameters.getInjectedClassPath().isEmpty()) {
-                if (targetGradleVersion.compareTo(TestKitFeature.PLUGIN_CLASSPATH_INJECTION.getSince()) < 0) {
-                    throw new UnsupportedFeatureException("support plugin classpath injection", targetGradleVersion, TestKitFeature.PLUGIN_CLASSPATH_INJECTION.getSince());
-                }
                 launcher.withInjectedClassPath(parameters.getInjectedClassPath());
             }
 
diff --git a/subprojects/test-kit/src/main/java/org/gradle/testkit/runner/internal/feature/TestKitFeature.java b/subprojects/test-kit/src/main/java/org/gradle/testkit/runner/internal/feature/TestKitFeature.java
index 6210b43..7b44769 100644
--- a/subprojects/test-kit/src/main/java/org/gradle/testkit/runner/internal/feature/TestKitFeature.java
+++ b/subprojects/test-kit/src/main/java/org/gradle/testkit/runner/internal/feature/TestKitFeature.java
@@ -18,12 +18,14 @@
 
 import org.gradle.util.GradleVersion;
 
+import static org.gradle.tooling.internal.consumer.DefaultGradleConnector.MINIMUM_SUPPORTED_GRADLE_VERSION;
+
 public enum TestKitFeature {
 
-    RUN_BUILDS(GradleVersion.version("2.6")),
-    CAPTURE_BUILD_RESULT_TASKS(GradleVersion.version("2.6")),
-    PLUGIN_CLASSPATH_INJECTION(GradleVersion.version("2.8")),
-    CAPTURE_BUILD_RESULT_OUTPUT_IN_DEBUG(GradleVersion.version("2.9"));
+    RUN_BUILDS(MINIMUM_SUPPORTED_GRADLE_VERSION),
+    CAPTURE_BUILD_RESULT_TASKS(MINIMUM_SUPPORTED_GRADLE_VERSION),
+    PLUGIN_CLASSPATH_INJECTION(MINIMUM_SUPPORTED_GRADLE_VERSION),
+    CAPTURE_BUILD_RESULT_OUTPUT_IN_DEBUG(MINIMUM_SUPPORTED_GRADLE_VERSION);
 
     private final GradleVersion since;
 
@@ -34,5 +36,4 @@ public enum TestKitFeature {
     public GradleVersion getSince() {
         return since;
     }
-
 }
diff --git a/subprojects/test-kit/src/test/groovy/org/gradle/testkit/runner/internal/FeatureCheckBuildResultTest.groovy b/subprojects/test-kit/src/test/groovy/org/gradle/testkit/runner/internal/FeatureCheckBuildResultTest.groovy
index 525481d..74ebb61 100644
--- a/subprojects/test-kit/src/test/groovy/org/gradle/testkit/runner/internal/FeatureCheckBuildResultTest.groovy
+++ b/subprojects/test-kit/src/test/groovy/org/gradle/testkit/runner/internal/FeatureCheckBuildResultTest.groovy
@@ -24,6 +24,7 @@
 
 import static org.gradle.testkit.runner.TaskOutcome.FAILED
 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
+import static org.gradle.tooling.internal.consumer.DefaultGradleConnector.MINIMUM_SUPPORTED_GRADLE_VERSION
 
 class FeatureCheckBuildResultTest extends Specification {
 
@@ -36,7 +37,7 @@
     def buildTasks = [successBuildResult, failedBuildResult]
 
     def "provides expected field values for supported Gradle version"() {
-        FeatureCheckBuildResult buildResult = new FeatureCheckBuildResult(new BuildOperationParameters(GradleVersion.version('2.6'), false), output, buildTasks)
+        FeatureCheckBuildResult buildResult = new FeatureCheckBuildResult(new BuildOperationParameters(MINIMUM_SUPPORTED_GRADLE_VERSION, false), output, buildTasks)
 
         expect:
         buildResult.output == 'output'
diff --git a/subprojects/test-kit/src/testFixtures/groovy/org/gradle/test/preconditions/TestKitPreconditions.groovy b/subprojects/test-kit/src/testFixtures/groovy/org/gradle/test/preconditions/TestKitPreconditions.groovy
new file mode 100644
index 0000000..1fcaff5
--- /dev/null
+++ b/subprojects/test-kit/src/testFixtures/groovy/org/gradle/test/preconditions/TestKitPreconditions.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 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.test.preconditions
+
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.testkit.runner.BaseGradleRunnerIntegrationTest
+
+class TestKitPreconditions {
+
+    static class LowestMajorGradleIsAvailable implements TestPrecondition {
+        @Override
+        boolean isSatisfied() throws Exception {
+            return BaseGradleRunnerIntegrationTest.findLowestMajorGradleVersion() != null
+        }
+    }
+
+}
diff --git a/subprojects/test-kit/src/testFixtures/groovy/org/gradle/testkit/runner/fixtures/CustomDaemonDirectory.java b/subprojects/test-kit/src/testFixtures/groovy/org/gradle/testkit/runner/fixtures/CustomDaemonDirectory.java
index d2b3f1f..6118b16 100644
--- a/subprojects/test-kit/src/testFixtures/groovy/org/gradle/testkit/runner/fixtures/CustomDaemonDirectory.java
+++ b/subprojects/test-kit/src/testFixtures/groovy/org/gradle/testkit/runner/fixtures/CustomDaemonDirectory.java
@@ -25,4 +25,4 @@
 @Target({ElementType.METHOD})
 @Inherited
 public @interface CustomDaemonDirectory {
-}
\ No newline at end of file
+}
diff --git a/subprojects/testing-base/build.gradle.kts b/subprojects/testing-base/build.gradle.kts
index 8548dfa..b3927497 100644
--- a/subprojects/testing-base/build.gradle.kts
+++ b/subprojects/testing-base/build.gradle.kts
@@ -45,6 +45,7 @@
     testFixturesImplementation(project(":base-services"))
     testFixturesImplementation(project(":model-core"))
     testFixturesImplementation(project(":internal-integ-testing"))
+    testFixturesImplementation(project(":logging"))
     testFixturesImplementation(libs.guava)
     testFixturesImplementation(libs.jsoup)
 
diff --git a/subprojects/testing-base/src/integTest/resources/org/gradle/testing/RetainStacktraceForInheritedTestMethodsTest/retainsStackTraceForInheritedTestMethods/build.gradle b/subprojects/testing-base/src/integTest/resources/org/gradle/testing/RetainStacktraceForInheritedTestMethodsTest/retainsStackTraceForInheritedTestMethods/build.gradle
index 19ff2fd..2f64bef 100644
--- a/subprojects/testing-base/src/integTest/resources/org/gradle/testing/RetainStacktraceForInheritedTestMethodsTest/retainsStackTraceForInheritedTestMethods/build.gradle
+++ b/subprojects/testing-base/src/integTest/resources/org/gradle/testing/RetainStacktraceForInheritedTestMethodsTest/retainsStackTraceForInheritedTestMethods/build.gradle
@@ -8,13 +8,11 @@
     mavenCentral()
 }
 
-dependencies {
-    testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
-}
-
-tasks.named('test', Test) {
-    useJUnitPlatform()
-    testLogging {
-        exceptionFormat = TestExceptionFormat.FULL
+testing.suites.test {
+    useJUnitJupiter()
+    targets.test.testTask.configure {
+        testLogging {
+            exceptionFormat = TestExceptionFormat.FULL
+        }
     }
 }
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/TestFailureSerializationException.java b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/TestFailureSerializationException.java
new file mode 100644
index 0000000..f37f88c
--- /dev/null
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/TestFailureSerializationException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.testing;
+
+import org.gradle.api.GradleException;
+
+import javax.annotation.Nullable;
+
+/**
+ *  Thrown when there is an error while serializing/deserializing a test result.
+ */
+public class TestFailureSerializationException extends GradleException {
+    public TestFailureSerializationException(String message, @Nullable Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/logging/TestExceptionFormatter.java b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/logging/TestExceptionFormatter.java
index 8d5e59a..6f805aa 100644
--- a/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/logging/TestExceptionFormatter.java
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/logging/TestExceptionFormatter.java
@@ -22,4 +22,4 @@
 
 public interface TestExceptionFormatter {
     String format(TestDescriptor descriptor, List<Throwable> exceptions);
-}
\ No newline at end of file
+}
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/ForkedTestClasspath.java b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/ForkedTestClasspath.java
new file mode 100644
index 0000000..28e97d1
--- /dev/null
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/ForkedTestClasspath.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.testing.worker;
+
+import com.google.common.collect.ImmutableList;
+import org.gradle.process.internal.worker.WorkerProcessBuilder;
+
+import java.io.File;
+import java.net.URL;
+
+/**
+ * The classpath of a forked test process, which includes both the application and implementation classpaths.
+ * User-defined classes are loaded by and executed in the context of the application classloader. This classloader
+ * contains classes from the {@code testRuntimeClasspath}. The implementation classpath includes classes required
+ * by Gradle's test framework integrations.
+ *
+ * <p>In some cases, classes from the application classpath may be accessed by the implementation classpath. These
+ * are specified by {@link WorkerProcessBuilder#sharedPackages}, but should likely be tracked in this class as well.</p>
+ *
+ * <p>This classpath is intended to be consumed by the {@link ForkingTestClassProcessor}.</p>
+ */
+public class ForkedTestClasspath {
+    private final ImmutableList<File> applicationClasspath;
+    private final ImmutableList<File> applicationModulepath;
+    private final ImmutableList<URL> implementationClasspath;
+    public final ImmutableList<URL> implementationModulepath;
+
+    public ForkedTestClasspath(
+        ImmutableList<File> applicationClasspath,
+        ImmutableList<File> applicationModulepath,
+        ImmutableList<URL> implementationClasspath,
+        ImmutableList<URL> implementationModulepath
+    ) {
+        this.applicationClasspath = applicationClasspath;
+        this.applicationModulepath = applicationModulepath;
+        this.implementationClasspath = implementationClasspath;
+        this.implementationModulepath = implementationModulepath;
+    }
+
+    public ImmutableList<File> getApplicationClasspath() {
+        return applicationClasspath;
+    }
+
+    public ImmutableList<File> getApplicationModulepath() {
+        return applicationModulepath;
+    }
+
+    public ImmutableList<URL> getImplementationClasspath() {
+        return implementationClasspath;
+    }
+
+    public ImmutableList<URL> getImplementationModulepath() {
+        return implementationModulepath;
+    }
+}
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
index f20fd9e..2cac6a8 100755
--- a/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessor.java
@@ -16,13 +16,14 @@
 
 package org.gradle.api.internal.tasks.testing.worker;
 
+import com.google.common.collect.Sets;
 import org.gradle.api.Action;
 import org.gradle.api.internal.DocumentationRegistry;
-import org.gradle.api.internal.classpath.ModuleRegistry;
 import org.gradle.api.internal.tasks.testing.TestClassProcessor;
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo;
 import org.gradle.api.internal.tasks.testing.TestResultProcessor;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
+import org.gradle.internal.exceptions.DefaultMultiCauseException;
 import org.gradle.internal.remote.ObjectConnection;
 import org.gradle.internal.work.WorkerLeaseRegistry;
 import org.gradle.internal.work.WorkerThreadRegistry;
@@ -31,11 +32,8 @@
 import org.gradle.process.internal.worker.WorkerProcess;
 import org.gradle.process.internal.worker.WorkerProcessBuilder;
 import org.gradle.process.internal.worker.WorkerProcessFactory;
-import org.gradle.util.internal.CollectionUtils;
 
-import java.io.File;
-import java.net.URL;
-import java.util.List;
+import java.util.Set;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -43,10 +41,8 @@ public class ForkingTestClassProcessor implements TestClassProcessor {
     private final WorkerProcessFactory workerFactory;
     private final WorkerTestClassProcessorFactory processorFactory;
     private final JavaForkOptions options;
-    private final Iterable<File> classPath;
-    private final Iterable<File> modulePath;
+    private final ForkedTestClasspath classpath;
     private final Action<WorkerProcessBuilder> buildConfigAction;
-    private final ModuleRegistry moduleRegistry;
     private final Lock lock = new ReentrantLock();
     private final WorkerThreadRegistry workerThreadRegistry;
     private RemoteTestClassProcessor remoteProcessor;
@@ -55,26 +51,24 @@ public class ForkingTestClassProcessor implements TestClassProcessor {
     private WorkerLeaseRegistry.WorkerLeaseCompletion completion;
     private final DocumentationRegistry documentationRegistry;
     private boolean stoppedNow;
+    private final Set<Throwable> unrecoverableExceptions = Sets.newHashSet();
+
 
     public ForkingTestClassProcessor(
         WorkerThreadRegistry workerThreadRegistry,
         WorkerProcessFactory workerFactory,
         WorkerTestClassProcessorFactory processorFactory,
         JavaForkOptions options,
-        Iterable<File> classPath,
-        Iterable<File> modulePath,
+        ForkedTestClasspath classpath,
         Action<WorkerProcessBuilder> buildConfigAction,
-        ModuleRegistry moduleRegistry,
         DocumentationRegistry documentationRegistry
     ) {
         this.workerThreadRegistry = workerThreadRegistry;
         this.workerFactory = workerFactory;
         this.processorFactory = processorFactory;
         this.options = options;
-        this.classPath = classPath;
-        this.modulePath = modulePath;
+        this.classpath = classpath;
         this.buildConfigAction = buildConfigAction;
-        this.moduleRegistry = moduleRegistry;
         this.documentationRegistry = documentationRegistry;
     }
 
@@ -111,9 +105,10 @@ public void processTestClass(TestClassRunInfo testClass) {
     RemoteTestClassProcessor forkProcess() {
         WorkerProcessBuilder builder = workerFactory.create(new TestWorker(processorFactory));
         builder.setBaseName("Gradle Test Executor");
-        builder.setImplementationClasspath(getTestWorkerImplementationClasspath());
-        builder.applicationClasspath(classPath);
-        builder.applicationModulePath(modulePath);
+        builder.setImplementationClasspath(classpath.getImplementationClasspath());
+        builder.setImplementationModulePath(classpath.getImplementationModulepath());
+        builder.applicationClasspath(classpath.getApplicationClasspath());
+        builder.applicationModulePath(classpath.getApplicationModulepath());
         options.copyTo(builder.getJavaCommand());
         builder.getJavaCommand().jvmArgs("-Dorg.gradle.native=false");
         buildConfigAction.execute(builder);
@@ -123,6 +118,19 @@ RemoteTestClassProcessor forkProcess() {
 
         ObjectConnection connection = workerProcess.getConnection();
         connection.useParameterSerializers(TestEventSerializer.create());
+        connection.addUnrecoverableErrorHandler(new Action<Throwable>() {
+            @Override
+            public void execute(Throwable throwable) {
+                lock.lock();
+                try {
+                    if (!stoppedNow) {
+                        unrecoverableExceptions.add(throwable);
+                    }
+                } finally {
+                    lock.unlock();
+                }
+            }
+        });
         connection.addIncoming(TestResultProcessor.class, resultProcessor);
         RemoteTestClassProcessor remoteProcessor = connection.addOutgoing(RemoteTestClassProcessor.class);
         connection.connect();
@@ -130,36 +138,6 @@ RemoteTestClassProcessor forkProcess() {
         return remoteProcessor;
     }
 
-    List<URL> getTestWorkerImplementationClasspath() {
-        return CollectionUtils.flattenCollections(URL.class,
-            moduleRegistry.getModule("gradle-core-api").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-worker-processes").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-core").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-logging").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-logging-api").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-messaging").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-files").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-file-temp").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-hashing").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-base-services").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-enterprise-logging").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-enterprise-workers").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-cli").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-native").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-testing-base").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-testing-jvm-infrastructure").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-testing-junit-platform").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-process-services").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getModule("gradle-build-operations").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getExternalModule("slf4j-api").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getExternalModule("jul-to-slf4j").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getExternalModule("native-platform").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getExternalModule("kryo").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getExternalModule("commons-lang").getImplementationClasspath().getAsURLs(),
-            moduleRegistry.getExternalModule("javax.inject").getImplementationClasspath().getAsURLs()
-        );
-    }
-
     @Override
     public void stop() {
         try {
@@ -178,14 +156,15 @@ public void stop() {
             if (!stoppedNow) {
                 throw new ExecException(e.getMessage()
                     + "\nThis problem might be caused by incorrect test process configuration."
-                    + "\nPlease refer to the test execution section in the User Manual at "
-                    + documentationRegistry.getDocumentationFor("java_testing", "sec:test_execution"), e.getCause());
+                    + "\n" + documentationRegistry.getDocumentationRecommendationFor("on test execution", "java_testing", "sec:test_execution"), e.getCause());
             }
         } finally {
             if (completion != null) {
                 completion.leaseFinish();
             }
         }
+
+        maybeRethrowUnrecoverableExceptions();
     }
 
     @Override
@@ -200,4 +179,16 @@ public void stopNow() {
             lock.unlock();
         }
     }
+
+    /**
+     * If there are communication errors while receiving test results from the test worker,
+     * we can get in a situation where a test appears skipped even though it actually failed.
+     * We want to capture the communication errors and be sure to fail the test execution so
+     * as to avoid any false positives.
+     */
+    private void maybeRethrowUnrecoverableExceptions() {
+        if (!unrecoverableExceptions.isEmpty()) {
+            throw new DefaultMultiCauseException("Unexpected errors were encountered while processing test results that may result in some results being incorrect or incomplete.", unrecoverableExceptions);
+        }
+    }
 }
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/TestEventSerializer.java b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/TestEventSerializer.java
index 063b4ca..56aa21a 100644
--- a/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/TestEventSerializer.java
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/worker/TestEventSerializer.java
@@ -26,6 +26,7 @@
 import org.gradle.api.internal.tasks.testing.DefaultTestOutputEvent;
 import org.gradle.api.internal.tasks.testing.DefaultTestSuiteDescriptor;
 import org.gradle.api.internal.tasks.testing.TestCompleteEvent;
+import org.gradle.api.internal.tasks.testing.TestFailureSerializationException;
 import org.gradle.api.internal.tasks.testing.TestStartEvent;
 import org.gradle.api.tasks.testing.TestFailure;
 import org.gradle.api.tasks.testing.TestOutputEvent;
@@ -38,6 +39,7 @@
 import org.gradle.internal.serialize.Serializer;
 import org.gradle.internal.serialize.SerializerRegistry;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -171,7 +173,6 @@ public DefaultTestFailureSerializer(Serializer<Throwable> throwableSerializer) {
 
         @Override
         public DefaultTestFailure read(Decoder decoder) throws Exception {
-            Throwable rawFailure = throwableSerializer.read(decoder);
             String message = decoder.readNullableString();
             String className = decoder.readString();
             String stacktrace = decoder.readString();
@@ -183,12 +184,28 @@ public DefaultTestFailure read(Decoder decoder) throws Exception {
             for (int i = 0; i < numOfCauses; i++) {
                 causes.add(read(decoder));
             }
+
+            Throwable rawFailure = readThrowableCatchingFailure(decoder);
             return new DefaultTestFailure(rawFailure, new DefaultTestFailureDetails(message, className, stacktrace, isAssertionFailure, expected, actual), causes);
         }
 
+        /**
+         * In the event that the exception thrown from the test cannot be successfully recreated, we capture the error
+         * and put that in the test failure so that we pass on to the user whatever information we can about the test failure.
+         */
+        private Throwable readThrowableCatchingFailure(Decoder decoder) throws IOException {
+            String rawFailureName = decoder.readString();
+            Throwable rawFailure;
+            try {
+               rawFailure = throwableSerializer.read(decoder);
+            } catch(Exception e) {
+                rawFailure = new TestFailureSerializationException("An exception of type " + rawFailureName + " was thrown by the test, but Gradle was unable to recreate the exception in the build process", e);
+            }
+            return rawFailure;
+        }
+
         @Override
         public void write(Encoder encoder, DefaultTestFailure value) throws Exception {
-            throwableSerializer.write(encoder, value.getRawFailure());
             encoder.writeNullableString(value.getDetails().getMessage());
             encoder.writeString(value.getDetails().getClassName());
             encoder.writeString(value.getDetails().getStacktrace());
@@ -199,6 +216,16 @@ public void write(Encoder encoder, DefaultTestFailure value) throws Exception {
             for (TestFailure cause : value.getCauses()) {
                 write(encoder, (DefaultTestFailure) cause);
             }
+            writeThrowableWithType(encoder, value, value.getRawFailure());
+        }
+
+        /**
+         * Serializes the exception thrown by the test and also writes the exception type so that if there is a failure recreating the exception, we can at least
+         * provide the user with some information about what type of exception was thrown.
+         */
+        private void writeThrowableWithType(Encoder encoder, DefaultTestFailure value, Throwable rawFailure) throws Exception {
+            encoder.writeString(rawFailure.getClass().getName());
+            throwableSerializer.write(encoder, value.getRawFailure());
         }
     }
 
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/AbstractTestTask.java b/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/AbstractTestTask.java
index 459a119..b3bb10b 100644
--- a/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/AbstractTestTask.java
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/AbstractTestTask.java
@@ -26,6 +26,7 @@
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.file.FileSystemOperations;
 import org.gradle.api.internal.ConventionTask;
+import org.gradle.api.internal.exceptions.MarkedVerificationException;
 import org.gradle.api.internal.tasks.testing.DefaultTestTaskReports;
 import org.gradle.api.internal.tasks.testing.FailFastTestListenerInternal;
 import org.gradle.api.internal.tasks.testing.TestExecuter;
@@ -59,13 +60,13 @@
 import org.gradle.api.tasks.Nested;
 import org.gradle.api.tasks.OutputDirectory;
 import org.gradle.api.tasks.TaskAction;
-import org.gradle.api.tasks.VerificationException;
 import org.gradle.api.tasks.VerificationTask;
 import org.gradle.api.tasks.options.Option;
 import org.gradle.api.tasks.testing.logging.TestLogging;
 import org.gradle.api.tasks.testing.logging.TestLoggingContainer;
 import org.gradle.internal.Cast;
 import org.gradle.internal.concurrent.CompositeStoppable;
+import org.gradle.internal.deprecation.DeprecationLogger;
 import org.gradle.internal.event.ListenerBroadcast;
 import org.gradle.internal.event.ListenerManager;
 import org.gradle.internal.logging.ConsoleRenderer;
@@ -481,15 +482,31 @@ public void execute(DeleteSpec spec) {
     private void handleCollectedResults(TestCountLogger testCountLogger) {
         if (testCountLogger.hadFailures()) {
             handleTestFailures();
-        } else if (testCountLogger.getTotalTests() == 0 && shouldFailOnNoMatchingTests()) {
-            throw new TestExecutionException(createNoMatchingTestErrorMessage());
+        } else if (testCountLogger.getTotalTests() == 0) {
+            if (!hasFilter()) {
+                emitDeprecationMessage();
+            } else if (shouldFailOnNoMatchingTests()) {
+                throw new TestExecutionException(createNoMatchingTestErrorMessage());
+            }
         }
     }
 
+    private void emitDeprecationMessage() {
+        DeprecationLogger.deprecateBehaviour("No test executed.")
+            .withAdvice("There are test sources present but no test was executed. Please check your test configuration.")
+            .willBecomeAnErrorInGradle9()
+            .withUpgradeGuideSection(8, "test_task_fail_on_no_test_executed")
+            .nagUser();
+    }
+
     private boolean shouldFailOnNoMatchingTests() {
-        return filter.isFailOnNoMatchingTests() && (!filter.getIncludePatterns().isEmpty()
+        return filter.isFailOnNoMatchingTests() && hasFilter();
+    }
+
+    private boolean hasFilter() {
+        return !filter.getIncludePatterns().isEmpty()
             || !filter.getCommandLineIncludePatterns().isEmpty()
-            || !filter.getExcludePatterns().isEmpty());
+            || !filter.getExcludePatterns().isEmpty();
     }
 
     private String createNoMatchingTestErrorMessage() {
@@ -551,7 +568,7 @@ private void createReporting(Map<String, TestClassResult> results, TestOutputSto
      *
      * For more information on supported patterns see {@link TestFilter}
      */
-    @Option(option = "tests", description = "Sets test class or method name to be included, '*' is supported.")
+    @Option(option = "tests", description = "Sets test class or method name to be included (in addition to the test task filters), '*' is supported.")
     public AbstractTestTask setTestNameIncludePatterns(List<String> testNamePattern) {
         filter.setCommandLineIncludePatterns(testNamePattern);
         return this;
@@ -618,7 +635,7 @@ private void handleTestFailures() {
         if (getIgnoreFailures()) {
             getLogger().warn(message);
         } else {
-            throw new VerificationException(message);
+            throw new MarkedVerificationException(message);
         }
     }
 
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/TestListener.java b/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/TestListener.java
index f139810..d056ab3 100644
--- a/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/TestListener.java
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/TestListener.java
@@ -20,6 +20,7 @@
 // Teamcity has the concept of a "wave" of messages
 // where each thread/process uses a unique wave id
 
+import org.gradle.internal.DeprecatedInGradleScope;
 import org.gradle.internal.service.scopes.EventScope;
 import org.gradle.internal.service.scopes.Scopes;
 
@@ -29,6 +30,7 @@
  * from JUnit and TestNG tests.
  */
 @EventScope(Scopes.Build.class)
+@DeprecatedInGradleScope
 public interface TestListener {
     /**
      * Called before a test suite is started.
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/TestOutputListener.java b/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/TestOutputListener.java
index befe812..9311a04 100644
--- a/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/TestOutputListener.java
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/TestOutputListener.java
@@ -16,6 +16,7 @@
 
 package org.gradle.api.tasks.testing;
 
+import org.gradle.internal.DeprecatedInGradleScope;
 import org.gradle.internal.service.scopes.EventScope;
 import org.gradle.internal.service.scopes.Scopes;
 
@@ -23,6 +24,7 @@
  * Listens to the output events like printing to standard output or error
  */
 @EventScope(Scopes.Build.class)
+@DeprecatedInGradleScope
 public interface TestOutputListener {
 
     /**
diff --git a/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/package-info.java b/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/package-info.java
index 002994f..56255f1 100644
--- a/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/package-info.java
+++ b/subprojects/testing-base/src/main/java/org/gradle/api/tasks/testing/package-info.java
@@ -17,4 +17,4 @@
 /**
  * The unit testing {@link org.gradle.api.Task} implementations.
  */
-package org.gradle.api.tasks.testing;
\ No newline at end of file
+package org.gradle.api.tasks.testing;
diff --git a/subprojects/testing-base/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptorTest.groovy b/subprojects/testing-base/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptorTest.groovy
index 102383a..0d9a5bc 100644
--- a/subprojects/testing-base/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptorTest.groovy
+++ b/subprojects/testing-base/src/test/groovy/org/gradle/api/internal/tasks/testing/DefaultTestClassDescriptorTest.groovy
@@ -26,4 +26,4 @@
         expect:
         descriptor.toString() == 'Test class <class-name>'
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/testing-base/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessorTest.groovy b/subprojects/testing-base/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessorTest.groovy
index 4efde6e..46126ee 100755
--- a/subprojects/testing-base/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessorTest.groovy
+++ b/subprojects/testing-base/src/test/groovy/org/gradle/api/internal/tasks/testing/worker/ForkingTestClassProcessorTest.groovy
@@ -16,16 +16,13 @@
 
 package org.gradle.api.internal.tasks.testing.worker
 
-
+import com.google.common.collect.ImmutableList
 import org.gradle.api.Action
 import org.gradle.api.internal.DocumentationRegistry
-import org.gradle.api.internal.classpath.Module
-import org.gradle.api.internal.classpath.ModuleRegistry
 import org.gradle.api.internal.tasks.testing.TestClassRunInfo
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory
-import org.gradle.internal.classpath.ClassPath
+import org.gradle.internal.exceptions.DefaultMultiCauseException
 import org.gradle.internal.remote.ObjectConnection
-import org.gradle.internal.work.WorkerLeaseRegistry
 import org.gradle.internal.work.WorkerThreadRegistry
 import org.gradle.process.JavaForkOptions
 import org.gradle.process.internal.ExecException
@@ -34,72 +31,76 @@
 import org.gradle.process.internal.worker.WorkerProcessBuilder
 import org.gradle.process.internal.worker.WorkerProcessFactory
 import spock.lang.Specification
-import spock.lang.Subject
 
 class ForkingTestClassProcessorTest extends Specification {
-    WorkerLeaseRegistry.WorkerLeaseCompletion workerLease = Mock(WorkerLeaseRegistry.WorkerLeaseCompletion)
     WorkerThreadRegistry workerLeaseRegistry = Mock(WorkerThreadRegistry)
-    WorkerProcessBuilder workerProcessBuilder = Mock(WorkerProcessBuilder)
-    WorkerProcess workerProcess = Mock(WorkerProcess)
-    ModuleRegistry moduleRegistry = Mock(ModuleRegistry)
-    DocumentationRegistry documentationRegistry = Mock(DocumentationRegistry)
-    WorkerProcessFactory workerProcessFactory = Stub(WorkerProcessFactory)
-    JavaForkOptions options = Stub(JavaForkOptions)
-
-    @Subject
-        processor = Spy(ForkingTestClassProcessor, constructorArgs: [workerLeaseRegistry, workerProcessFactory, Mock(WorkerTestClassProcessorFactory), options, [new File("classpath.jar")], [], Mock(Action), moduleRegistry, documentationRegistry])
-
-    def setup() {
-        workerProcessBuilder.build() >> workerProcess
-        workerProcessFactory.create(_) >> workerProcessBuilder
-        workerProcessBuilder.getJavaCommand() >> Stub(JavaExecHandleBuilder)
+    RemoteTestClassProcessor remoteProcessor = Mock(RemoteTestClassProcessor)
+    ObjectConnection connection = Mock(ObjectConnection) {
+        addOutgoing(RemoteTestClassProcessor.class) >> remoteProcessor
+    }
+    WorkerProcess workerProcess = Mock(WorkerProcess) {
+        getConnection() >> connection
+    }
+    WorkerProcessBuilder workerProcessBuilder = Mock(WorkerProcessBuilder) {
+        build() >> workerProcess
+        getJavaCommand() >> Stub(JavaExecHandleBuilder)
+    }
+    WorkerProcessFactory workerProcessFactory = Stub(WorkerProcessFactory) {
+        create(_) >> workerProcessBuilder
     }
 
     def "acquires worker lease and starts worker process on first test"() {
+        given:
         def test1 = Mock(TestClassRunInfo)
         def test2 = Mock(TestClassRunInfo)
-
-        def remoteProcessor = Mock(RemoteTestClassProcessor)
+        def processor = newProcessor()
 
         when:
         processor.processTestClass(test1)
         processor.processTestClass(test2)
 
         then:
-        1 * workerLeaseRegistry.startWorker() >> workerLease
-        1 * processor.forkProcess() >> remoteProcessor
+        1 * workerLeaseRegistry.startWorker()
         1 * remoteProcessor.processTestClass(test1)
         1 * remoteProcessor.processTestClass(test2)
+        1 * remoteProcessor.startProcessing()
         0 * remoteProcessor._
     }
 
-    def "starts process with a limited implementation classpath"() {
-        setup:
-        1 * workerProcess.getConnection() >> Stub(ObjectConnection) { addOutgoing(_) >> Stub(RemoteTestClassProcessor) }
+    def "starts process with the specified classpath"() {
+        given:
+        def appClasspath = ImmutableList.of(new File("cls.jar"))
+        def appModulepath = ImmutableList.of(new File("mod.jar"))
+        def implClasspath = ImmutableList.of(new URL("file://cls.jar"))
+        def implModulepath = ImmutableList.of(new URL("file://mod.jar"))
+        def processor = newProcessor(new ForkedTestClasspath(
+            appClasspath, appModulepath, implClasspath, implModulepath
+        ))
 
         when:
         processor.forkProcess()
 
         then:
-        19 * moduleRegistry.getModule(_) >> { module(it[0]) }
-        6 * moduleRegistry.getExternalModule(_) >> { module(it[0]) }
-        1 * workerProcessBuilder.setImplementationClasspath(_) >> { assert it[0].size() == 25 }
+        1 * workerProcessBuilder.applicationClasspath(_) >> { assert it[0] == appClasspath }
+        1 * workerProcessBuilder.applicationModulePath(_) >> { assert it[0] == appModulepath}
+        1 * workerProcessBuilder.setImplementationClasspath(_) >> { assert it[0] == implClasspath }
+        1 * workerProcessBuilder.setImplementationModulePath(_) >> { assert it[0] == implModulepath }
     }
 
     def "stopNow does nothing when no remote processor"() {
+        given:
+        def processor = newProcessor()
+
         when:
         processor.stopNow()
 
         then:
-        1 * processor.stopNow()
         0 * _
     }
 
     def "stopNow propagates to worker process"() {
-        ForkingTestClassProcessor processor = new ForkingTestClassProcessor(workerLeaseRegistry, workerProcessFactory, Mock(WorkerTestClassProcessorFactory), options, [new File("classpath.jar")], [], Mock(Action), Stub(ModuleRegistry), documentationRegistry)
-
-        setup:
-        1 * workerProcess.getConnection() >> Stub(ObjectConnection) { addOutgoing(_) >> Stub(RemoteTestClassProcessor) }
+        given:
+        def processor = newProcessor()
 
         when:
         processor.processTestClass(Mock(TestClassRunInfo))
@@ -110,10 +111,7 @@
     }
 
     def "no exception when stop after stopNow"() {
-        ForkingTestClassProcessor processor = new ForkingTestClassProcessor(workerLeaseRegistry, workerProcessFactory, Mock(WorkerTestClassProcessorFactory), options, [new File("classpath.jar")], [], Mock(Action), Stub(ModuleRegistry), documentationRegistry)
-
-        setup:
-        1 * workerProcess.getConnection() >> Stub(ObjectConnection) { addOutgoing(_) >> Stub(RemoteTestClassProcessor) }
+        def processor = newProcessor()
 
         when:
         processor.processTestClass(Mock(TestClassRunInfo))
@@ -126,13 +124,62 @@
         notThrown(ExecException)
     }
 
-    def module(String module) {
-        return Stub(Module) {
-            _ * getImplementationClasspath() >> {
-                Stub(ClassPath) {
-                    _ * getAsURLs() >> { [new URL("file://${module}.jar")] }
-                }
-            }
+    def "captures and rethrows unrecoverable exceptions thrown by the connection"() {
+        def handler
+        def processor = newProcessor()
+
+        when:
+        processor.processTestClass(Mock(TestClassRunInfo))
+
+        then:
+        1 * workerProcess.getConnection() >> Stub(ObjectConnection) {
+            addUnrecoverableErrorHandler(_) >> { args -> handler = args[0] }
+            addOutgoing(_) >> Stub(RemoteTestClassProcessor)
         }
+
+        when:
+        def unexpectedException = new Throwable('BOOM!')
+        handler.execute(unexpectedException)
+
+        and:
+        processor.stop()
+
+        then:
+        def e = thrown(DefaultMultiCauseException)
+        e.causes.contains(unexpectedException)
+    }
+
+    def "ignores unrecoverable exceptions after stopNow() is called"() {
+        def handler
+        def processor = newProcessor()
+
+        when:
+        processor.processTestClass(Mock(TestClassRunInfo))
+
+        then:
+        1 * workerProcess.getConnection() >> Stub(ObjectConnection) {
+            addUnrecoverableErrorHandler(_) >> { args -> handler = args[0] }
+            addOutgoing(_) >> Stub(RemoteTestClassProcessor)
+        }
+
+        when:
+        processor.stopNow()
+        def unexpectedException = new Throwable('BOOM!')
+        handler.execute(unexpectedException)
+
+        and:
+        processor.stop()
+
+        then:
+        noExceptionThrown()
+    }
+
+    def newProcessor(
+        ForkedTestClasspath classpath = new ForkedTestClasspath(ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), ImmutableList.of())
+    ) {
+        return new ForkingTestClassProcessor(
+            workerLeaseRegistry, workerProcessFactory, Mock(WorkerTestClassProcessorFactory),
+            Stub(JavaForkOptions), classpath, Mock(Action), Mock(DocumentationRegistry)
+        )
     }
 }
diff --git a/subprojects/testing-base/src/testFixtures/groovy/org/gradle/testing/AbstractTestFrameworkIntegrationTest.groovy b/subprojects/testing-base/src/testFixtures/groovy/org/gradle/testing/AbstractTestFrameworkIntegrationTest.groovy
index c4f9f79..cfb68d9 100644
--- a/subprojects/testing-base/src/testFixtures/groovy/org/gradle/testing/AbstractTestFrameworkIntegrationTest.groovy
+++ b/subprojects/testing-base/src/testFixtures/groovy/org/gradle/testing/AbstractTestFrameworkIntegrationTest.groovy
@@ -161,6 +161,9 @@
         createEmptyProject()
 
         when:
+        executer.expectDocumentedDeprecationWarning("No test executed. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. There are test sources present but no test was executed. Please check your test configuration. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_fail_on_no_test_executed")
         succeeds "check"
 
         then:
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractJvmFailFastIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractJvmFailFastIntegrationSpec.groovy
new file mode 100644
index 0000000..b62e7e3
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractJvmFailFastIntegrationSpec.groovy
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2023 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.testing
+
+import org.gradle.api.logging.configuration.ConsoleOutput
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.RichConsoleStyling
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.test.fixtures.ConcurrentTestUtil
+import org.gradle.test.fixtures.server.http.BlockingHttpServer
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import org.gradle.testing.fixture.JvmBlockingTestClassGenerator
+import org.hamcrest.CoreMatchers
+import org.junit.Rule
+import spock.lang.IgnoreIf
+
+import static org.gradle.testing.fixture.JvmBlockingTestClassGenerator.*
+
+abstract class AbstractJvmFailFastIntegrationSpec extends AbstractTestingMultiVersionIntegrationTest {
+    @Rule
+    BlockingHttpServer server = new BlockingHttpServer()
+    JvmBlockingTestClassGenerator generator
+
+    def setup() {
+        server.start()
+        generator = new JvmBlockingTestClassGenerator(testDirectory, server, testFrameworkImports, testFrameworkDependencies, configureTestFramework)
+    }
+
+    def "all tests run with #description"() {
+        given:
+        buildFile.text = generator.initBuildFile()
+        buildFile << buildConfig
+        generator.withFailingTest()
+        generator.withNonfailingTest()
+        def testExecution = server.expectConcurrentAndBlock(DEFAULT_MAX_WORKERS, FAILED_RESOURCE, OTHER_RESOURCE)
+
+        when:
+        def gradleHandle = executer.withTasks(taskList).start()
+        testExecution.waitForAllPendingCalls()
+
+        then:
+        testExecution.release(FAILED_RESOURCE)
+        testExecution.release(OTHER_RESOURCE)
+        gradleHandle.waitForFailure()
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass('pkg.FailedTest').assertTestFailed('failTest', CoreMatchers.anything())
+        result.testClass('pkg.OtherTest').assertTestPassed('passingTest')
+
+        where:
+        description        | taskList                   | buildConfig
+        'default config'   | ['test']                   | ''
+        'failFast = false' | ['test']                   | 'test { failFast = false }'
+    }
+
+    def "stop test execution with #description"() {
+        given:
+        buildFile.text = generator.initBuildFile()
+        buildFile << buildConfig
+        generator.withFailingTest()
+        generator.withNonfailingTest()
+        def testExecution = server.expectOptionalAndBlock(DEFAULT_MAX_WORKERS, FAILED_RESOURCE, OTHER_RESOURCE)
+
+        when:
+        def gradleHandle = executer.withTasks(taskList).start()
+        testExecution.waitForAllPendingCalls()
+
+        then:
+        testExecution.release(FAILED_RESOURCE)
+        gradleHandle.waitForFailure()
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass('pkg.FailedTest').assertTestFailed('failTest', CoreMatchers.anything())
+        result.testClass('pkg.OtherTest').assertTestSkipped('passingTest')
+
+        where:
+        description       | taskList                   | buildConfig
+        'failFast = true' | ['test']                   | 'test { failFast = true }'
+        '--fail-fast'     | ['test', '--fail-fast']    | ''
+    }
+
+    def "ensure fail fast with forkEvery #forkEvery, maxWorkers #maxWorkers, omittedTests #testOmitted"() {
+        given:
+        buildFile.text = generator.initBuildFile(maxWorkers, forkEvery)
+        def resourceForTest = generator.withFailingTests(testOmitted + 1)
+        def testExecution = server.expectOptionalAndBlock(maxWorkers, resourceForTest.values() as String[])
+
+        when:
+        def gradleHandle = executer.withTasks('test', '--fail-fast').start()
+        testExecution.waitForAllPendingCalls()
+
+        then:
+        testExecution.release(1)
+        gradleHandle.waitForFailure()
+        def result = new DefaultTestExecutionResult(testDirectory)
+        assert 1 == resourceForTest.keySet().count { result.testClassExists(it) && result.testClass(it).testFailed('failedTest', CoreMatchers.anything()) }
+        assert testOmitted == resourceForTest.keySet().with {
+            count { !result.testClassExists(it) } +
+                count { result.testClassExists(it) && result.testClass(it).testCount == 0 } +
+                count { result.testClassExists(it) && result.testClass(it).testSkippedCount == 1 }
+        }
+
+        where:
+        forkEvery | maxWorkers | testOmitted
+        0         | 1          | 1
+        1         | 1          | 1
+        2         | 1          | 1
+        0         | 2          | 5
+        1         | 2          | 5
+        2         | 2          | 5
+    }
+
+    def "fail fast console output shows failure"() {
+        given:
+        buildFile.text = generator.initBuildFile()
+        generator.withFailingTest()
+        generator.withNonfailingTest()
+        def testExecution = server.expectConcurrentAndBlock(DEFAULT_MAX_WORKERS, FAILED_RESOURCE, OTHER_RESOURCE)
+
+        when:
+        def gradleHandle = executer.withTasks('test', '--fail-fast').start()
+        testExecution.waitForAllPendingCalls()
+
+        then:
+        testExecution.release(FAILED_RESOURCE)
+        gradleHandle.waitForFailure()
+        assert gradleHandle.standardOutput.matches(/(?s).*FailedTest.*failTest.*FAILED.*java.lang.RuntimeException at FailedTest.java.*/)
+        assert !gradleHandle.standardOutput.contains('pkg.OtherTest')
+    }
+
+    @IgnoreIf({ GradleContextualExecuter.isParallel() })
+    def "fail fast console output shows test class in work-in-progress"() {
+        given:
+        executer.withConsole(ConsoleOutput.Rich).withArguments('--parallel', "--max-workers=$DEFAULT_MAX_WORKERS")
+        buildFile.text = generator.initBuildFile()
+        generator.withFailingTest()
+        generator.withNonfailingTest()
+        def testExecution = server.expectConcurrentAndBlock(DEFAULT_MAX_WORKERS, FAILED_RESOURCE, OTHER_RESOURCE)
+
+        when:
+        def gradleHandle = executer.withTasks('test', '--fail-fast').start()
+        testExecution.waitForAllPendingCalls()
+
+        then:
+        ConcurrentTestUtil.poll {
+            RichConsoleStyling.assertHasWorkInProgress(gradleHandle, '> :test > Executing test pkg.FailedTest')
+            RichConsoleStyling.assertHasWorkInProgress(gradleHandle, '> :test > Executing test pkg.OtherTest')
+        }
+
+        testExecution.release(FAILED_RESOURCE)
+        gradleHandle.waitForFailure()
+    }
+
+    def "fail fast works with --tests filter"() {
+        given:
+        buildFile.text = generator.initBuildFile()
+        def resourceForTest = generator.withFailingTests(5)
+        def testExecution = server.expectOptionalAndBlock(DEFAULT_MAX_WORKERS, resourceForTest.values() as String[])
+
+        when:
+        def gradleHandle = executer.withTasks('test', '--fail-fast', '--tests=*OtherTest_*').start()
+        testExecution.waitForAllPendingCalls()
+
+        then:
+        testExecution.release(DEFAULT_MAX_WORKERS)
+        gradleHandle.waitForFailure()
+
+        assert !gradleHandle.errorOutput.contains('No tests found for given includes:')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestEnvironmentIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestEnvironmentIntegrationTest.groovy
new file mode 100644
index 0000000..d508ea8
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestEnvironmentIntegrationTest.groovy
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2012 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.testing
+
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import org.gradle.util.Matchers
+
+abstract class AbstractTestEnvironmentIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    abstract String getModuleName()
+
+    def setup() {
+        buildFile << """
+            allprojects {
+                apply plugin: 'java'
+
+                repositories {
+                    mavenCentral()
+                }
+
+                dependencies {
+                    ${testFrameworkDependencies}
+                }
+
+                test.${configureTestFramework}
+            }
+        """.stripIndent()
+    }
+
+    def "can run tests with custom system classloader"() {
+        given:
+        file('src/test/java/org/gradle/MySystemClassLoader.java') << customSystemClassLoaderClass
+        file('src/test/java/org/gradle/JUnitTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class JUnitTest {
+                @Test
+                public void mySystemClassLoaderIsUsed() throws ClassNotFoundException {
+                    assertTrue(ClassLoader.getSystemClassLoader() instanceof MySystemClassLoader);
+                    assertEquals(getClass().getClassLoader(), ClassLoader.getSystemClassLoader().getParent());
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            test {
+                systemProperties 'java.system.class.loader':'org.gradle.MySystemClassLoader'
+            }
+        """.stripIndent()
+
+        when:
+        run 'test'
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.JUnitTest')
+        result.testClass('org.gradle.JUnitTest').assertTestPassed('mySystemClassLoaderIsUsed')
+    }
+
+    def "can run tests referencing slf4j with custom system classloader"() {
+        given:
+        file('src/test/java/org/gradle/MySystemClassLoader.java') << customSystemClassLoaderClass
+        file('src/test/java/org/gradle/TestUsingSlf4j.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            import org.slf4j.Logger;
+            import org.slf4j.LoggerFactory;
+
+            public class TestUsingSlf4j {
+                @Test
+                public void mySystemClassLoaderIsUsed() throws ClassNotFoundException {
+                    assertTrue(ClassLoader.getSystemClassLoader() instanceof MySystemClassLoader);
+                    assertEquals(getClass().getClassLoader(), ClassLoader.getSystemClassLoader().getParent());
+
+                    Logger logger = LoggerFactory.getLogger(TestUsingSlf4j.class);
+                    logger.info("INFO via slf4j");
+                    logger.warn("WARN via slf4j");
+                    logger.error("ERROR via slf4j");
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            dependencies {
+                implementation 'org.slf4j:slf4j-api:1.7.30'
+                testRuntimeOnly 'org.slf4j:slf4j-simple:1.7.30'
+            }
+
+            test {
+                systemProperties 'java.system.class.loader':'org.gradle.MySystemClassLoader'
+            }
+        """.stripIndent()
+
+        when:
+        run 'test'
+
+        then:
+        def testResults = new DefaultTestExecutionResult(testDirectory)
+        testResults.assertTestClassesExecuted('org.gradle.TestUsingSlf4j')
+        with(testResults.testClass('org.gradle.TestUsingSlf4j')) {
+            assertTestPassed('mySystemClassLoaderIsUsed')
+            assertStderr(Matchers.containsText("ERROR via slf4j"))
+            assertStderr(Matchers.containsText("WARN via slf4j"))
+            assertStderr(Matchers.containsText("INFO via slf4j"))
+        }
+    }
+
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
+    def "can run tests referencing slf4j with modular java"() {
+        given:
+        file('src/test/java/org/gradle/example/TestUsingSlf4j.java') << """
+            package org.gradle.example;
+
+            ${testFrameworkImports}
+
+            import org.slf4j.Logger;
+            import org.slf4j.LoggerFactory;
+
+            public class TestUsingSlf4j {
+                @Test
+                public void testModular() {
+                    Logger logger = LoggerFactory.getLogger(org.gradle.example.TestUsingSlf4j.class);
+                    logger.info("INFO via slf4j");
+                    logger.warn("WARN via slf4j");
+                    logger.error("ERROR via slf4j");
+                }
+            }
+        """.stripIndent()
+        file("src/test/java/module-info.java") << """
+            module org.gradle.example {
+                exports org.gradle.example;
+                requires ${moduleName};
+                requires org.slf4j;
+            }
+        """.stripIndent()
+        buildFile << """
+            dependencies {
+                implementation 'org.slf4j:slf4j-api:1.7.30'
+                testRuntimeOnly 'org.slf4j:slf4j-simple:1.7.30'
+            }
+        """.stripIndent()
+
+        when:
+        run 'test'
+
+        then:
+        def testResults = new DefaultTestExecutionResult(testDirectory)
+        testResults.assertTestClassesExecuted('org.gradle.example.TestUsingSlf4j')
+        with(testResults.testClass('org.gradle.example.TestUsingSlf4j')) {
+            assertTestPassed('testModular')
+            assertStderr(Matchers.containsText("ERROR via slf4j"))
+            assertStderr(Matchers.containsText("WARN via slf4j"))
+            assertStderr(Matchers.containsText("INFO via slf4j"))
+        }
+    }
+
+    @Requires(
+        value = UnitTestPreconditions.Jdk8OrEarlier,
+        reason = "Hangs on Java 9"
+    )
+    def "can run tests with custom system classloader and java agent"() {
+        given:
+        file('src/main/java/org/gradle/MySystemClassLoader.java') << customSystemClassLoaderClass
+        file('src/main/java/org/gradle/MyAgent.java') << """
+            package org.gradle;
+
+            import java.lang.instrument.Instrumentation;
+
+            public class MyAgent {
+                public static void premain(String args, Instrumentation instrumentation) {
+                    System.setProperty("using.custom.agent", "true");
+
+                    // This agent should be loaded via the custom system ClassLoader
+                    assert ClassLoader.getSystemClassLoader() instanceof MySystemClassLoader : "systemClassLoader is not an instanceof MySystemClassLoader";
+                    assert MyAgent.class.getClassLoader() == ClassLoader.getSystemClassLoader().getParent() : "MyAgent is not loaded via the system classloader: " + MyAgent.class.getClassLoader().getClass().getName();
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/JUnitTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class JUnitTest {
+                @Test
+                public void mySystemClassLoaderIsUsed() throws ClassNotFoundException {
+                    assertEquals("true", System.getProperty("using.custom.agent"));
+
+                    // This test class should be loaded via the custom system ClassLoader
+                    assertTrue(ClassLoader.getSystemClassLoader() instanceof MySystemClassLoader);
+                    assertTrue(getClass().getClassLoader() == ClassLoader.getSystemClassLoader().getParent());
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            jar {
+                manifest {
+                    attributes 'Premain-Class': 'org.gradle.MyAgent'
+                }
+            }
+
+            test {
+                dependsOn jar
+                systemProperties 'java.system.class.loader':'org.gradle.MySystemClassLoader'
+                jvmArgs "-javaagent:\${jar.archiveFile.asFile.get()}"
+            }
+        """.stripIndent()
+
+        when:
+        run 'test'
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.JUnitTest')
+        result.testClass('org.gradle.JUnitTest').assertTestPassed('mySystemClassLoaderIsUsed')
+    }
+
+    def "can run tests with custom security manager"() {
+        executer
+                .withArgument("-Porg.gradle.java.installations.paths=${AvailableJavaHomes.getAvailableJvms().collect { it.javaHome.absolutePath }.join(",")}")
+                .withToolchainDetectionEnabled()
+
+        given:
+        file('src/test/java/org/gradle/JUnitTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class JUnitTest {
+                @Test
+                public void mySecurityManagerIsUsed() throws ClassNotFoundException {
+                    assertTrue(System.getSecurityManager() instanceof MySecurityManager);
+                    assertEquals(ClassLoader.getSystemClassLoader(), MySecurityManager.class.getClassLoader());
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/MySecurityManager.java') << """
+            package org.gradle;
+
+            import java.security.Permission;
+
+            public class MySecurityManager extends SecurityManager {
+                public MySecurityManager() {
+                    assert getClass().getName().equals(System.getProperty("java.security.manager"));
+                }
+
+                @Override
+                public void checkPermission(Permission permission) {
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            java {
+                toolchain {
+                    languageVersion = JavaLanguageVersion.of(11)
+                }
+            }
+            test {
+                systemProperties 'java.security.manager': 'org.gradle.MySecurityManager'
+            }
+        """.stripIndent()
+
+        when:
+        run 'test'
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.JUnitTest')
+        result.testClass('org.gradle.JUnitTest').assertTestPassed('mySecurityManagerIsUsed')
+    }
+
+    String getCustomSystemClassLoaderClass() {
+        return """
+            package org.gradle;
+
+            import java.net.URL;
+            import java.net.URLClassLoader;
+
+            public class MySystemClassLoader extends URLClassLoader {
+                public MySystemClassLoader(ClassLoader parent) {
+                    super(new URL[0], parent);
+                    // Should be constructed with the default system ClassLoader as root
+                    if (!getClass().getClassLoader().equals(parent)) {
+                        throw new AssertionError();
+                    }
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..87b94c2
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestFilteringIntegrationTest.groovy
@@ -0,0 +1,462 @@
+/*
+ * Copyright 2023 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.testing
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.Issue
+
+abstract class AbstractTestFilteringIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+
+    def setup() {
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """
+    }
+
+    def "can run single tests"() {
+        given:
+        file('src/test/java/NotATest.java') << """
+            public class NotATest {
+            }
+        """.stripIndent()
+        file('src/test/java/Ok.java') << """
+            ${testFrameworkImports}
+            public class Ok {
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/Ok2.java') << """
+            ${testFrameworkImports}
+            public class Ok2 {
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        succeeds("test", "--tests=Ok2*")
+
+        then:
+        def testResult = new DefaultTestExecutionResult(testDirectory)
+        testResult.assertTestClassesExecuted('Ok2')
+
+        when:
+        succeeds("cleanTest", "test", "--tests=Ok*")
+
+        then:
+        testResult.assertTestClassesExecuted('Ok', 'Ok2')
+
+        when:
+        fails("test", "--tests=DoesNotMatchAClass*")
+
+        then:
+        result.assertHasCause('No tests found for given includes: [DoesNotMatchAClass*](--tests filter)')
+
+        when:
+        fails("test", "--tests=NotATest*")
+        then:
+        result.assertHasCause('No tests found for given includes: [NotATest*](--tests filter)')
+    }
+
+    def "executes single method from a test class"() {
+        buildFile << """
+            test {
+              filter {
+                includeTestsMatching "${pattern}"
+              }
+            }
+        """
+        file("src/test/java/org/gradle/FooTest.java") << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test public void pass() {}
+                @Test public void fail() { throw new RuntimeException("Boo!"); }
+            }
+        """
+        file("src/test/java/org/gradle/OtherTest.java") << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public class OtherTest {
+                @Test public void pass() {}
+                @Test public void fail() { throw new RuntimeException("Boo!"); }
+            }
+        """
+
+        when:
+        run("test")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("org.gradle.FooTest")
+        result.testClass("org.gradle.FooTest").assertTestsExecuted("pass")
+
+        where:
+        pattern << ['FooTest.pass', 'org.gradle.FooTest.pass']
+    }
+
+    def "executes multiple methods from a test class"() {
+        buildFile << """
+            test {
+              include 'FooTest*'
+              def cls = "FooTest"
+              filter {
+                includeTestsMatching "\${cls}.passOne" //make sure GStrings work
+                includeTestsMatching "\${cls}.passTwo"
+              }
+            }
+        """
+        file("src/test/java/FooTest.java") << """
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test public void passOne() {}
+                @Test public void passTwo() {}
+                @Test public void fail() { throw new RuntimeException("Boo!"); }
+            }
+        """
+        file("src/test/java/OtherTest.java") << """
+            ${testFrameworkImports}
+            public class OtherTest {
+                @Test public void passOne() {}
+                @Test public void fail() { throw new RuntimeException("Boo!"); }
+            }
+        """
+
+        when:
+        run("test")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("FooTest")
+        result.testClass("FooTest").assertTestCount(2, 0, 0)
+    }
+
+    def "executes multiple methods from different classes"() {
+        buildFile << """
+            test {
+              filter.setIncludePatterns 'Foo*.pass*'
+            }
+        """
+        file("src/test/java/Foo1Test.java") << """
+            ${testFrameworkImports}
+            public class Foo1Test {
+                @Test public void pass1() {}
+                @Test public void boo() {}
+            }
+        """
+        file("src/test/java/Foo2Test.java") << """
+            ${testFrameworkImports}
+            public class Foo2Test {
+                @Test public void pass2() {}
+                @Test public void boo() {}
+            }
+        """
+        file("src/test/java/Foo3Test.java") << """
+            ${testFrameworkImports}
+            public class Foo3Test {
+                @Test public void boo() {}
+            }
+        """
+        file("src/test/java/OtherTest.java") << """
+            ${testFrameworkImports}
+            public class OtherTest {
+                @Test public void pass3() {}
+                @Test public void boo() {}
+            }
+        """
+
+        when:
+        run("test")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("Foo1Test", "Foo2Test")
+        result.testClass("Foo1Test").assertTestsExecuted("pass1")
+        result.testClass("Foo2Test").assertTestsExecuted("pass2")
+    }
+
+    def "reports when no matching methods found"() {
+        file("src/test/java/org/gradle/FooTest.java") << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test public void pass() {}
+            }
+        """
+
+        //by command line
+        when: fails("test", "--tests", pattern)
+        then: failure.assertHasCause("No tests found for given includes: [${pattern}](--tests filter)")
+
+        //by build script
+        when:
+        buildFile << "test.filter.includeTestsMatching '${pattern}'"
+        fails("test")
+        then: failure.assertHasCause("No tests found for given includes: [${pattern}](filter.includeTestsMatching)")
+
+        where:
+        pattern << ['FooTest.missingMethod', 'org.gradle.FooTest.missingMethod']
+    }
+
+    def "adds import/export rules to report about no matching methods found"() {
+        file("src/test/java/FooTest.java") << """
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test public void pass() {}
+            }
+        """
+
+        when:
+        buildFile << """
+            test {
+                include 'FooTest*'
+                exclude 'NotImportant*'
+            }
+        """
+        fails("test", "--tests", 'FooTest.missingMethod')
+        then: failure.assertHasCause("No tests found for given includes: [FooTest*](include rules) [NotImportant*](exclude rules) [FooTest.missingMethod](--tests filter)")
+    }
+
+    def "does not report when matching method has been filtered before via include/exclude"() { //current behavior, not necessarily desired
+        file("src/test/java/FooTest.java") << """
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test public void pass() {}
+            }
+        """
+
+        when:
+        buildFile << "test.include 'FooTest.missingMethod'"
+        then:
+        succeeds("test", "--tests", 'FooTest.missingMethod')
+    }
+
+    def "task is out of date when --tests argument changes"() {
+        file("src/test/java/FooTest.java") << """
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test public void pass() {}
+                @Test public void pass2() {}
+            }
+        """
+
+        when: run("test", "--tests", "FooTest.pass")
+        then: new DefaultTestExecutionResult(testDirectory).testClass("FooTest").assertTestsExecuted("pass")
+
+        when: run("test", "--tests", "FooTest.pass")
+        then: skipped(":test") //up-to-date
+
+        when:
+        run("test", "--tests", "FooTest.pass*")
+
+        then:
+        executedAndNotSkipped(":test")
+        new DefaultTestExecutionResult(testDirectory).testClass("FooTest").assertTestsExecuted("pass", "pass2")
+    }
+
+    def "can select multiple tests from commandline #scenario"() {
+        given:
+        file("src/test/java/Foo1Test.java") << """
+            ${testFrameworkImports}
+            public class Foo1Test {
+                @Test public void pass1() {}
+                @Test public void bar() {}
+            }
+        """
+        file("src/test/java/Foo2Test.java") << """
+            ${testFrameworkImports}
+            public class Foo2Test {
+                @Test public void pass2() {}
+                @Test public void bar() {}
+            }
+        """
+        file("src/test/java/BarTest.java") << """
+            ${testFrameworkImports}
+            public class BarTest {
+                @Test public void bar() {}
+            }
+        """
+        file("src/test/java/OtherTest.java") << """
+            ${testFrameworkImports}
+            public class OtherTest {
+                @Test public void pass3() {}
+                @Test public void bar() {}
+            }
+        """
+
+        when:
+        run(stringArrayOf(command))
+
+        then:
+
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted(stringArrayOf(classesExecuted))
+        if (!foo1TestsExecuted.isEmpty()) {
+            result.testClass("Foo1Test").assertTestsExecuted(stringArrayOf(foo1TestsExecuted))
+        }
+        if (!foo2TestsExecuted.isEmpty()) {
+            result.testClass("Foo2Test").assertTestsExecuted(stringArrayOf(foo2TestsExecuted))
+        }
+        if (!barTestsExecuted.isEmpty()) {
+            result.testClass("BarTest").assertTestsExecuted(stringArrayOf(barTestsExecuted))
+        }
+        if (!otherTestsExecuted.isEmpty()) {
+            result.testClass("OtherTest").assertTestsExecuted(stringArrayOf(otherTestsExecuted))
+        }
+
+        where:
+        scenario         | command                                                  | classesExecuted                                  | foo1TestsExecuted | foo2TestsExecuted | barTestsExecuted | otherTestsExecuted
+        "no options"     | ["test"]                                                 | ["Foo1Test", "Foo2Test", "BarTest", "OtherTest"] | ["bar", "pass1"]  | ["bar", "pass2"]  | ["bar"]          | ["bar", "pass3"]
+        "pass and Other" | ["test", "--tests", "*.pass1", "--tests", "*OtherTest*"] | ["Foo1Test", "OtherTest"]                        | ["pass1"]         | []                | []               | ["bar", "pass3"]
+        "pass and *ar"   | ["test", "--tests", "*.pass1", "--tests", "*arTest"]     | ["BarTest", "Foo1Test"]                          | ["pass1"]         | []                | ["bar"]          | []
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/1571")
+    def "option --tests filter in combined with #includeType"() {
+        given:
+        buildFile << """
+        test {
+            $includeConfig
+        }
+        """
+
+        when:
+        createTestABC()
+
+        then:
+        succeeds('test', '--tests', '*ATest*', '--tests', '*BTest*', '--info')
+
+        output.contains('ATest!')
+        !output.contains('BTest!')
+        !output.contains('CTest!')
+
+        where:
+        includeType                   | includeConfig
+        "include and exclude"         | "include '*Test*'; exclude '*BTest*'"
+        "filter.includeTestsMatching" | "filter { includeTestsMatching '*ATest*'; includeTestsMatching '*CTest*' }"
+        "filter.includePatterns"      | "filter { includePatterns = ['*ATest*', '*CTest*'] }"
+    }
+
+    def "invoking testNameIncludePatterns does not influence include/exclude filter"() {
+        given:
+        buildFile << """
+        test {
+            include '*ATest*', '*BTest*'
+            testNameIncludePatterns = [ '*BTest*', '*CTest*' ]
+        }
+        """
+
+        when:
+        createTestABC()
+
+        then:
+        succeeds('test', '--info')
+
+        !output.contains('ATest!')
+        output.contains('BTest!')
+        !output.contains('CTest!')
+    }
+
+    def "invoking filter.includePatterns not disable include/exclude filter"() {
+        given:
+        buildFile << """
+        test {
+            include '*ATest*', '*BTest*'
+            filter.includePatterns = [ '*BTest*', '*CTest*' ]
+        }
+        """
+
+        when:
+        createTestABC()
+
+        then:
+        succeeds('test', '--info')
+
+        !output.contains('ATest!')
+        output.contains('BTest!')
+        !output.contains('CTest!')
+    }
+
+    def "can exclude tests"() {
+        given:
+        buildFile << """
+        test {
+            filter.excludeTestsMatching("*BTest.test*")
+        }
+        """
+
+        createTestABC()
+
+        when:
+        succeeds('test', '--info')
+
+        then:
+        executedAndNotSkipped(":test")
+
+        and:
+        def executionResult = new DefaultTestExecutionResult(testDirectory)
+        executionResult.testClass("ATest").assertTestsExecuted("test")
+        !executionResult.testClassExists("BTest")
+        executionResult.testClass("CTest").assertTestsExecuted("test")
+
+        and:
+        output.contains('ATest!')
+        !output.contains('BTest!')
+        output.contains('CTest!')
+    }
+
+    private createTestABC(){
+        file('src/test/java/ATest.java') << """
+            ${testFrameworkImports}
+            public class ATest {
+                @Test public void test() { System.out.println("ATest!"); }
+            }
+        """
+        file('src/test/java/BTest.java') << """
+            ${testFrameworkImports}
+            public class BTest {
+                @Test public void test() { System.out.println("BTest!"); }
+            }
+        """
+        file('src/test/java/CTest.java') << """
+            ${testFrameworkImports}
+            public class CTest {
+                @Test public void test() { System.out.println("CTest!"); }
+            }
+        """
+    }
+
+    private String[] stringArrayOf(List<String> strings) {
+        return strings.toArray()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestListenerBuildOperationAdapterIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestListenerBuildOperationAdapterIntegrationTest.groovy
new file mode 100644
index 0000000..1df5c32
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestListenerBuildOperationAdapterIntegrationTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 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.testing
+
+import org.gradle.api.internal.tasks.testing.operations.ExecuteTestBuildOperationType
+import org.gradle.integtests.fixtures.BuildOperationsFixture
+import org.gradle.internal.operations.trace.BuildOperationRecord
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractTestListenerBuildOperationAdapterIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    def operations = new BuildOperationsFixture(executer, temporaryFolder)
+
+    abstract boolean isEmitsTestClassOperations()
+    abstract void writeTestSources()
+
+    def "emits build operations for junit tests"() {
+        given:
+        buildFile << """
+            apply plugin: 'java'
+            repositories { mavenCentral() }
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test {
+                ${configureTestFramework}
+                include '**/ASuite.class'
+                exclude '**/*Test.class'
+            }
+        """
+        writeTestSources()
+
+        when:
+        succeeds "test"
+
+        then:
+        def ops = operations.all(ExecuteTestBuildOperationType) { true }
+        def iterator = ops.iterator()
+
+        with(iterator.next()) {
+            details.testDescriptor.name == "Gradle Test Run :test"
+            details.testDescriptor.className == null
+            details.testDescriptor.composite == true
+        }
+        with(iterator.next()) {
+            details.testDescriptor.name ==~ "Gradle Test Executor \\d+"
+            details.testDescriptor.className == null
+            details.testDescriptor.composite == true
+        }
+        checkForSuiteOperations(iterator, "org.gradle.ASuite")
+        if (emitsTestClassOperations) {
+            checkForTestClassOperations(iterator, "org.gradle.OkTest")
+        }
+        checkForTestOperations(iterator, "org.gradle.OkTest", "anotherOk")
+        checkForTestOperations(iterator, "org.gradle.OkTest", "ok")
+        if (emitsTestClassOperations) {
+            checkForTestClassOperations(iterator, "org.gradle.OtherTest")
+        }
+        checkForTestOperations(iterator, "org.gradle.OtherTest", "ok")
+
+        !iterator.hasNext()
+    }
+
+    void checkForSuiteOperations(Iterator<BuildOperationRecord> iterator, String suiteName) {
+        with(iterator.next()) {
+            details.testDescriptor.name == suiteName
+            details.testDescriptor.className == suiteName
+            details.testDescriptor.composite == true
+        }
+    }
+
+    void checkForTestClassOperations(Iterator<BuildOperationRecord> iterator, String className) {
+        with(iterator.next()) {
+            details.testDescriptor.name == className
+            details.testDescriptor.className == className
+            details.testDescriptor.composite == true
+        }
+    }
+
+    void checkForTestOperations(Iterator<BuildOperationRecord> iterator, String className, String methodName) {
+        with(iterator.next()) {
+            details.testDescriptor.name == maybeParentheses(methodName)
+            details.testDescriptor.className == className
+            details.testDescriptor.composite == false
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestOutputListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestOutputListenerIntegrationTest.groovy
new file mode 100644
index 0000000..6fa6121
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestOutputListenerIntegrationTest.groovy
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2011 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.testing
+
+import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.Issue
+
+@Issue("GRADLE-1009")
+abstract class AbstractTestOutputListenerIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    def setup() {
+        executer.noExtraLogging()
+    }
+
+    def "can use standard output listener for tests"() {
+        given:
+        file("src/test/java/SomeTest.java") << """
+            ${testFrameworkImports}
+
+            public class SomeTest {
+                @Test public void showsOutputWhenPassing() {
+                    System.out.println("out passing");
+                    System.err.println("err passing");
+                    assertTrue(true);
+                }
+
+                @Test public void showsOutputWhenFailing() {
+                    System.out.println("out failing");
+                    System.err.println("err failing");
+                    assertTrue(false);
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+
+            test.${configureTestFramework}
+            test.addTestOutputListener(new VerboseOutputListener(logger: project.logger))
+
+            def removeMe = new RemoveMeListener()
+            test.addTestOutputListener(removeMe)
+            test.removeTestOutputListener(removeMe)
+
+            class VerboseOutputListener implements TestOutputListener {
+
+                def logger
+
+                public void onOutput(TestDescriptor descriptor, TestOutputEvent event) {
+                    logger.lifecycle(descriptor.toString() + " " + event.destination + " " + event.message);
+                }
+            }
+
+            class RemoveMeListener implements TestOutputListener {
+                public void onOutput(TestDescriptor descriptor, TestOutputEvent event) {
+                    println "remove me!"
+                }
+            }
+        """.stripIndent()
+
+        when:
+        def failure = executer.withTasks('test').runWithFailure()
+
+        then:
+        failure.output.contains("Test ${maybeParentheses('showsOutputWhenPassing')}(SomeTest) StdOut out passing")
+        failure.output.contains("Test ${maybeParentheses('showsOutputWhenFailing')}(SomeTest) StdOut out failing")
+        failure.output.contains("Test ${maybeParentheses('showsOutputWhenPassing')}(SomeTest) StdErr err passing")
+        failure.output.contains("Test ${maybeParentheses('showsOutputWhenFailing')}(SomeTest) StdErr err failing")
+
+        !failure.output.contains("remove me!")
+    }
+
+    @UnsupportedWithConfigurationCache
+    def "can register output listener at gradle level and using onOutput method"() {
+        given:
+        def test = file("src/test/java/SomeTest.java")
+        test << """
+            ${testFrameworkImports}
+
+            public class SomeTest {
+                @Test public void foo() {
+                    System.out.println("message from foo");
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+
+            test.${configureTestFramework}
+
+            test.onOutput { descriptor, event ->
+                logger.lifecycle("first: " + event.message)
+            }
+
+            gradle.addListener(new VerboseOutputListener(logger: project.logger))
+
+            class VerboseOutputListener implements TestOutputListener {
+
+                def logger
+
+                public void onOutput(TestDescriptor descriptor, TestOutputEvent event) {
+                    logger.lifecycle("second: " + event.message);
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds('test')
+
+        then:
+        outputContains('first: message from foo')
+        outputContains('second: message from foo')
+    }
+
+    def "shows standard streams configured via closure"() {
+        given:
+        def test = file("src/test/java/SomeTest.java")
+        test << """
+            ${testFrameworkImports}
+
+            public class SomeTest {
+                @Test public void foo() {
+                    System.out.println("message from foo");
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+
+            test.${configureTestFramework}
+            test.testLogging {
+                showStandardStreams = true
+            }
+        """
+
+        when:
+        executer.withArgument('-i')
+        succeeds('test')
+
+        then:
+        outputContains('message from foo')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestProgressLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestProgressLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..f2dc19e
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestProgressLoggingIntegrationTest.groovy
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2016 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.testing
+
+import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import org.junit.Rule
+
+abstract class AbstractTestProgressLoggingIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    @Rule ProgressLoggingFixture events = new ProgressLoggingFixture(executer, temporaryFolder)
+
+    def setup() {
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """
+    }
+
+    def "captures test progress logging events" () {
+        withGoodTestClasses(10)
+
+        when:
+        succeeds("test")
+
+        then:
+        events.statusLogged("0 tests completed")
+        events.statusLogged("1 test completed")
+        (2..10).each { count ->
+            assert events.statusLogged("${count} tests completed")
+        }
+    }
+
+    def "captures failing test progress logging events" () {
+        withFailingTestClasses(10)
+
+        when:
+        fails("test")
+
+        then:
+        events.statusLogged("0 tests completed")
+        events.statusLogged("1 test completed, 1 failed")
+        (2..10).each { count ->
+            assert events.statusLogged("${count} tests completed, ${count} failed")
+        }
+    }
+
+    def "captures skipped test progress logging events" () {
+        withSkippedTestClasses(10)
+
+        when:
+        succeeds("test")
+
+        then:
+        events.statusLogged("0 tests completed")
+        events.statusLogged("1 test completed, 1 skipped")
+        (2..10).each { count ->
+            assert events.statusLogged("${count} tests completed, ${count} skipped")
+        }
+    }
+
+    def "captures mixed test progress logging events" () {
+        withGoodTestClasses(5)
+        withFailingTestClasses(3)
+        withSkippedTestClasses(2)
+
+        when:
+        fails("test")
+
+        then:
+        (0..10).each { count ->
+            assert events.statusMatches("${count} tests? completed(,.*)*")
+        }
+
+        and:
+        (1..3).each { count ->
+            assert events.statusMatches("\\d+ tests? completed, ${count} failed(,.*)?")
+        }
+
+        and:
+        (1..2).each { count ->
+            assert events.statusMatches("\\d+ tests? completed,( \\d failed,)? ${count} skipped")
+        }
+    }
+
+    def "captures test progress logging events when tests are run in parallel" () {
+        withGoodTestClasses(4)
+        buildFile << """
+            test {
+                maxParallelForks = 4
+            }
+        """
+
+        when:
+        succeeds("test")
+
+        then:
+        events.statusLogged("0 tests completed")
+        events.statusLogged("1 test completed")
+        (2..4).each { count ->
+            assert events.statusLogged("${count} tests completed")
+        }
+    }
+
+    def createTestClass(int index, String type, String method, String assertion) {
+        String className = "${type.capitalize()}Test${index}"
+        file("src/test/java/${className}.java") << """
+            ${testFrameworkImports}
+            public class ${className} {
+                @Test
+                public void ${method}() {
+                    ${assertion};
+                }
+            }
+        """
+    }
+
+    def createGoodTestClass(int index=1) {
+        createTestClass(index, "good", "shouldPass", "assertEquals(1,1)")
+    }
+
+    def createFailingTestClass(int index=1) {
+        createTestClass(index, "failing", "shouldFail", "assertEquals(1,2)")
+    }
+
+    def createSkippedTestClass(int index=1) {
+        createTestClass(index, "skipped", "shouldBeSkipped", "assumeTrue(false)")
+    }
+
+    def withGoodTestClasses(int count) {
+        count.times { index ->
+            createGoodTestClass(index)
+        }
+    }
+
+    def withFailingTestClasses(int count) {
+        count.times { index ->
+            createFailingTestClass(index)
+        }
+    }
+
+    def withSkippedTestClasses(int count) {
+        count.times { index ->
+            createSkippedTestClass(index)
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestReportIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestReportIntegrationTest.groovy
new file mode 100644
index 0000000..55431d0
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestReportIntegrationTest.groovy
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2013 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.testing
+
+import org.gradle.integtests.fixtures.HtmlTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+import static org.hamcrest.CoreMatchers.equalTo
+
+abstract class AbstractTestReportIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+
+    def "report includes results of most recent invocation"() {
+        given:
+        buildFile << """
+            $junitSetup
+            test {
+                def logLessStuff = providers.systemProperty('LogLessStuff')
+                systemProperty 'LogLessStuff', logLessStuff.orNull
+            }
+        """.stripIndent()
+
+        and:
+        file("src/test/java/LoggingTest.java") << """
+            ${testFrameworkImports}
+            public class LoggingTest {
+                @Test
+                public void test() {
+                    if (System.getProperty("LogLessStuff", "false").equals("true")) {
+                        System.out.println("stdout.");
+                        System.err.println("stderr.");
+                    } else {
+                        System.out.println("This is stdout.");
+                        System.err.println("This is stderr.");
+                    }
+                }
+            }
+        """.stripIndent()
+
+        when:
+        run "test"
+
+        then:
+        def result = new HtmlTestExecutionResult(testDirectory)
+        result.testClass("LoggingTest").assertStdout(equalTo("This is stdout.\n"))
+        result.testClass("LoggingTest").assertStderr(equalTo("This is stderr.\n"))
+
+        when:
+        executer.withArguments("-DLogLessStuff=true")
+        run "test"
+
+        then:
+        result.testClass("LoggingTest").assertStdout(equalTo("stdout.\n"))
+        result.testClass("LoggingTest").assertStderr(equalTo("stderr.\n"))
+    }
+
+    def "test report task can handle test tasks that did not run tests"() {
+        given:
+        buildScript """
+            $junitSetup
+
+            def test = tasks.named('test', Test)
+
+            def otherTests = tasks.register('otherTests', Test) {
+                binaryResultsDirectory = file("bin")
+                classpath = files('blahClasspath')
+                testClassesDirs = files("blah")
+                ${configureTestFramework}
+            }
+
+            tasks.register('testReport', TestReport) {
+                testResults.from(test, otherTests)
+                destinationDirectory = reporting.baseDirectory.dir('tr')
+            }
+        """
+
+        and:
+        testClass("Thing")
+
+        when:
+        succeeds "testReport"
+
+        then:
+        skipped(":otherTests")
+        executedAndNotSkipped(":test")
+        new HtmlTestExecutionResult(testDirectory, "build/reports/tr").assertTestClassesExecuted("Thing")
+    }
+
+    def "results or reports are linked to in error output"() {
+        given:
+        buildScript """
+            $junitSetup
+            test {
+                reports.all { it.required = true }
+            }
+        """
+
+        and:
+        failingTestClass "SomeTest"
+
+        when:
+        fails "test"
+
+        then:
+        executedAndNotSkipped(":test")
+        failure.assertHasCause("There were failing tests. See the report at: ")
+
+        when:
+        buildFile << "\ntest.reports.html.required = false\n"
+        fails "test"
+
+        then:
+        executedAndNotSkipped(":test")
+        failure.assertHasCause("There were failing tests. See the results at: ")
+
+        when:
+        buildFile << "\ntest.reports.junitXml.required = false\n"
+        fails "test"
+
+        then:
+        executedAndNotSkipped(":test")
+        failure.assertHasCause("There were failing tests")
+        failure.assertHasNoCause("See the")
+    }
+
+    def "output per test case flag invalidates outputs"() {
+        when:
+        buildScript """
+            $junitSetup
+            test.reports.junitXml.outputPerTestCase = false
+        """
+        testClass "SomeTest"
+        succeeds "test"
+
+        then:
+        executedAndNotSkipped(":test")
+
+        when:
+        buildFile << "\ntest.reports.junitXml.outputPerTestCase = true\n"
+        succeeds "test"
+
+        then:
+        executedAndNotSkipped(":test")
+    }
+
+    protected String getJunitSetup() {
+        """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+    }
+
+    protected void failingTestClass(String name) {
+        testClass(name, true)
+    }
+
+    protected void testClass(String name, boolean failing = false) {
+        file("src/test/java/${name}.java") << """
+            ${testFrameworkImports}
+            public class $name {
+                @Test
+                public void test() {
+                    assert false == ${failing};
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestTaskIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestTaskIntegrationTest.groovy
new file mode 100644
index 0000000..11c5108
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/AbstractTestTaskIntegrationTest.groovy
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2013 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.testing
+
+import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.Issue
+
+import static org.gradle.api.internal.DocumentationRegistry.BASE_URL
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
+
+abstract class AbstractTestTaskIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    abstract String getStandaloneTestClass()
+    abstract String testClass(String className)
+
+    def setup() {
+        buildFile << """
+            allprojects {
+                apply plugin: 'java'
+
+                repositories {
+                    mavenCentral()
+                }
+
+                dependencies {
+                    ${testFrameworkDependencies}
+                }
+
+                test.${configureTestFramework}
+            }
+        """
+    }
+
+    @Issue("GRADLE-2702")
+    @ToBeFixedForConfigurationCache(because = "early dependency resolution")
+    def "should not resolve configuration results when there are no tests"() {
+        given:
+        buildFile << """
+            configurations.all {
+                if (it.canBeResolved) {
+                    incoming.beforeResolve { throw new RuntimeException() }
+                }
+            }
+        """
+
+        when:
+        run("build")
+
+        then:
+        noExceptionThrown()
+    }
+
+    def "test task is skipped when there are no tests"() {
+        given:
+        file("src/test/java/not_a_test.txt")
+
+        when:
+        run("build")
+
+        then:
+        result.assertTaskSkipped(":test")
+    }
+
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
+    def "compiles and executes a Java 9 test suite"() {
+        given:
+        buildFile << java9Build()
+
+        file('src/test/java/MyTest.java') << standaloneTestClass
+
+        when:
+        succeeds 'test'
+
+        then:
+        noExceptionThrown()
+
+        and:
+        classFormat(classFile('java', 'test', 'MyTest.class')) == 53
+
+    }
+
+    @Requires(UnitTestPreconditions.Jdk9OrLater)
+    def "compiles and executes a Java 9 test suite even if a module descriptor is on classpath"() {
+        given:
+        buildFile << java9Build()
+
+        file('src/test/java/MyTest.java') << standaloneTestClass
+        file('src/main/java/com/acme/Foo.java') << '''package com.acme;
+            public class Foo {}
+        '''
+        file('src/main/java/com/acme/module-info.java') << '''module com.acme {
+            exports com.acme;
+        }'''
+
+        when:
+        succeeds 'test'
+
+        then:
+        noExceptionThrown()
+
+        and:
+        classFormat(javaClassFile('module-info.class')) == 53
+        classFormat(classFile('java', 'test', 'MyTest.class')) == 53
+    }
+
+    def "test task does not hang if maxParallelForks is greater than max-workers (#maxWorkers)"() {
+        given:
+        def maxParallelForks = maxWorkers + 1
+
+        and:
+        2000.times { num ->
+            file("src/test/java/SomeTest${num}.java") << testClass("SomeTest${num}")
+        }
+
+        and:
+        buildFile << """
+            test {
+                maxParallelForks = $maxParallelForks
+            }
+        """.stripIndent()
+
+        when:
+        executer.withArguments("--max-workers=${maxWorkers}", "-i")
+        succeeds 'test'
+
+        then:
+        output.contains("test.maxParallelForks ($maxParallelForks) is larger than max-workers ($maxWorkers), forcing it to $maxWorkers")
+
+        where:
+        maxWorkers                                | _
+        Runtime.runtime.availableProcessors()     | _
+        Runtime.runtime.availableProcessors() - 1 | _
+        Runtime.runtime.availableProcessors() + 1 | _
+    }
+
+    @Requires(UnitTestPreconditions.Online)
+    def "re-runs tests when resources are renamed in a jar"() {
+        given:
+        buildFile << """
+            dependencies {
+                testImplementation project(":dependency")
+            }
+        """
+        settingsFile << """
+            include 'dependency'
+        """
+        file("src/test/java/MyTest.java") << """
+            ${testFrameworkImports}
+
+            public class MyTest {
+               @Test
+               public void test() {
+                  assertNotNull(getClass().getResource("dependency/foo.properties"));
+               }
+            }
+        """.stripIndent()
+
+        def resourceFile = file("dependency/src/main/resources/dependency/foo.properties")
+        resourceFile << """
+            someProperty = true
+        """
+
+        when:
+        succeeds 'test'
+        then:
+        noExceptionThrown()
+
+        when:
+        resourceFile.renameTo(file("dependency/src/main/resources/dependency/bar.properties"))
+        then:
+        fails 'test'
+    }
+
+    @Requires(UnitTestPreconditions.Online)
+    def "re-runs tests when resources are renamed"() {
+        given:
+        file("src/test/java/MyTest.java") << """
+            ${testFrameworkImports}
+
+            public class MyTest {
+               @Test
+               public void test() {
+                  assertNotNull(getClass().getResource("dependency/foo.properties"));
+               }
+            }
+        """.stripIndent()
+
+        def resourceFile = file("src/main/resources/dependency/foo.properties")
+        resourceFile << """
+            someProperty = true
+        """
+
+        when:
+        succeeds 'test'
+        then:
+        noExceptionThrown()
+
+        when:
+        resourceFile.renameTo(file("src/main/resources/dependency/bar.properties"))
+        then:
+        fails 'test'
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/3627")
+    def "can reference properties from TestTaskReports when using @CompileStatic"() {
+        given:
+        buildFile.text = """
+            import groovy.transform.CompileStatic
+
+            @CompileStatic
+            class StaticallyCompiledPlugin implements Plugin<Project> {
+                @Override
+                void apply(Project project) {
+                    project.apply plugin: 'java'
+                    Test test = (Test) project.tasks.getByName("test")
+                    if (test.reports.junitXml.outputLocation.asFile.get().exists()) {
+                        println 'JUnit XML report exists!'
+                    }
+                }
+            }
+
+            apply plugin: StaticallyCompiledPlugin
+        """
+
+        expect:
+        succeeds("help")
+    }
+
+    def "reports failure of TestExecuter regardless of filters"() {
+        given:
+        file('src/test/java/MyTest.java') << standaloneTestClass
+        buildFile << """
+            import org.gradle.api.internal.tasks.testing.*
+
+            test {
+                doFirst {
+                    testExecuter = new TestExecuter<JvmTestExecutionSpec>() {
+                        @Override
+                        void execute(JvmTestExecutionSpec testExecutionSpec, TestResultProcessor resultProcessor) {
+                            DefaultTestSuiteDescriptor suite = new DefaultTestSuiteDescriptor(testExecutionSpec.path, testExecutionSpec.path)
+                            resultProcessor.started(suite, new TestStartEvent(System.currentTimeMillis()))
+                            try {
+                                throw new RuntimeException("boom!")
+                            } finally {
+                                resultProcessor.completed(suite.getId(), new TestCompleteEvent(System.currentTimeMillis()))
+                            }
+                        }
+
+                        @Override
+                        void stopNow() {
+                            // do nothing
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        fails("test", *extraArgs)
+
+        then:
+        result.assertHasCause('boom!')
+
+        where:
+        extraArgs << [[], ["--tests", "MyTest"]]
+    }
+
+    def "test framework can be set to the same value twice"() {
+        given:
+        file('src/test/java/MyTest.java') << standaloneTestClass
+
+        settingsFile << "rootProject.name = 'Sample'"
+        buildFile << """
+            test {
+                $configureTestFramework
+                $configureTestFramework
+            }
+        """.stripIndent()
+
+        expect:
+        succeeds("test")
+    }
+
+    def "options configured after setting test framework"() {
+        given:
+        file('src/test/java/MyTest.java') << standaloneTestClass
+
+        settingsFile << "rootProject.name = 'Sample'"
+        buildFile << """
+            test {
+                options {
+                    ${excludeCategoryOrTag("MyTest\$Slow")}
+                }
+            }
+
+            tasks.register('verifyTestOptions') {
+                doLast {
+                    assert tasks.getByName("test").getOptions().${buildScriptConfiguration.excludeCategoryOrTagConfigurationElement}.contains("MyTest\\\$Slow")
+                }
+            }
+        """.stripIndent()
+
+        expect:
+        succeeds("test", "verifyTestOptions", "--warn")
+    }
+
+    def "setForkEvery null emits deprecation warning"() {
+        given:
+        buildFile << """
+            tasks.withType(Test).configureEach {
+                forkEvery = null
+            }
+        """
+
+        when:
+        executer.expectDocumentedDeprecationWarning("Setting Test.forkEvery to null. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. Set Test.forkEvery to 0 instead. " +
+            String.format(RECOMMENDATION, "information", "${BASE_URL}/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:forkEvery"))
+
+        then:
+        succeeds "test", "--dry-run"
+    }
+
+    def "setForkEvery Long emits deprecation warning"() {
+        given:
+        buildFile << """
+            tasks.withType(Test).configureEach {
+                setForkEvery(Long.valueOf(1))
+            }
+        """
+
+        when:
+        executer.expectDocumentedDeprecationWarning("The Test.setForkEvery(Long) method has been deprecated. " +
+            "This is scheduled to be removed in Gradle 9.0. Please use the Test.setForkEvery(long) method instead. " +
+            String.format(RECOMMENDATION, "information", "${BASE_URL}/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:forkEvery"))
+
+        then:
+        succeeds "test", "--dry-run"
+    }
+
+    private String java9Build() {
+        """
+            java {
+                sourceCompatibility = 1.9
+                targetCompatibility = 1.9
+            }
+        """
+    }
+
+    private static int classFormat(TestFile path) {
+        path.bytes[7] & 0xFF
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/IncrementalTestIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/IncrementalTestIntegrationTest.groovy
index 2fa7717..86c047a 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/IncrementalTestIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/IncrementalTestIntegrationTest.groovy
@@ -101,7 +101,7 @@
         file("build.gradle") << """
             apply plugin: 'java'
             ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:4.13' }
+            dependencies { testImplementation '$testJunitCoordinates' }
             test.beforeTest { println "executed " + it }
         """
 
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/ParallelTestExecutionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/ParallelTestExecutionIntegrationTest.groovy
index ba9d738..4c99a81 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/ParallelTestExecutionIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/ParallelTestExecutionIntegrationTest.groovy
@@ -21,7 +21,7 @@
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
 import org.junit.Rule
 
-import static org.gradle.testing.fixture.JUnitCoverage.NEWEST
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUNIT4_VERSION
 
 @IntegrationTestTimeout(240)
 class ParallelTestExecutionIntegrationTest extends AbstractIntegrationSpec {
@@ -36,7 +36,7 @@
             ${mavenCentralRepository()}
             dependencies {
                 testImplementation localGroovy()
-                testImplementation "junit:junit:${NEWEST}"
+                testImplementation "junit:junit:${LATEST_JUNIT4_VERSION}"
             }
         """.stripIndent()
 
@@ -112,7 +112,7 @@
                 plugins { id "java" }
                 ${mavenCentralRepository()}
                 dependencies {
-                    testImplementation "junit:junit:${NEWEST}"
+                    testImplementation "junit:junit:${LATEST_JUNIT4_VERSION}"
                 }
                 test.maxParallelForks = 2
             """
@@ -138,7 +138,7 @@
             ${mavenCentralRepository()}
             testing.suites {
                 all {
-                    useJUnit("$NEWEST")
+                    useJUnit("$LATEST_JUNIT4_VERSION")
                     targets.all {
                         testTask.configure {
                             maxParallelForks = 2
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/SuiteTimestampIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/SuiteTimestampIntegrationTest.groovy
index 65f570f4..ecedbd5 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/SuiteTimestampIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/SuiteTimestampIntegrationTest.groovy
@@ -27,7 +27,7 @@
         file("build.gradle") << """
             apply plugin: 'java'
                 ${mavenCentralRepository()}
-                dependencies { testImplementation 'junit:junit:4.13' }
+                dependencies { testImplementation '$testJunitCoordinates' }
         """
 
         file("src/test/java/SomeTest.java") << """
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/SuppressedExceptionTestingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/SuppressedExceptionTestingIntegrationTest.groovy
index 2da95c6..6b8c3aa 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/SuppressedExceptionTestingIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/SuppressedExceptionTestingIntegrationTest.groovy
@@ -34,13 +34,7 @@
         buildFile << """
             apply plugin:'java-library'
             ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
-            }
-
-            test {
-                useJUnitPlatform()
-            }
+            testing.suites.test.useJUnitJupiter()
         """
 
         file('src/test/java/TestCaseWithThrowingBeforeAllAndAfterAllCallbacks.java') << """
@@ -94,13 +88,7 @@
         buildFile << """
             apply plugin:'java-library'
             ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
-            }
-
-            test {
-                useJUnitPlatform()
-            }
+            testing.suites.test.useJUnitJupiter()
         """
 
         file('src/test/java/SuppressedExceptionsAccidentallyThrownNotShownByGradleTest.java') << """
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TaskBooleanOptionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TaskBooleanOptionIntegrationTest.groovy
new file mode 100644
index 0000000..978d445
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TaskBooleanOptionIntegrationTest.groovy
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2023 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.testing
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+class TaskBooleanOptionIntegrationTest extends AbstractIntegrationSpec {
+
+    def "if not passed, boolean options have default value with #optionName=#value"() {
+        given:
+        file('buildSrc/src/main/java/SampleTask.java') << taskWithBooleanOptions()
+        buildFile << sampleTask()
+
+        when:
+        run('sample')
+
+        then:
+        outputContains("Value of $optionName: $value")
+
+        where:
+        optionName                 | value
+        'myBooleanPrimitiveOption' | 'false'
+        'myBooleanObjectOption'    | 'null'
+        'myBooleanPropertyOption'  | 'property(java.lang.Boolean, undefined)'
+        'myFieldOption'            | 'null'
+    }
+
+    def "can pass boolean option with #option=#value"() {
+        given:
+        file('buildSrc/src/main/java/SampleTask.java') << taskWithBooleanOptions()
+        buildFile << sampleTask()
+
+        when:
+        run('sample', "--$option")
+
+        then:
+        outputContains("Value of $option: $value")
+
+        where:
+        option                     | value
+        'myBooleanPrimitiveOption' | 'true'
+        'myBooleanObjectOption'    | 'true'
+        'myBooleanPropertyOption'  | 'property(java.lang.Boolean, fixed(class java.lang.Boolean, true))'
+        'myFieldOption'            | 'true'
+    }
+
+    def "can pass boolean disable option #option=#value"() {
+        given:
+        file('buildSrc/src/main/java/SampleTask.java') << taskWithBooleanOptions()
+        buildFile << sampleTask()
+
+        when:
+        run('sample', "--no-$option")
+
+        then:
+        outputContains("Value of $option: $value")
+
+        where:
+        option                     | value
+        'myBooleanPrimitiveOption' | 'false'
+        'myBooleanObjectOption'    | 'false'
+        'myBooleanPropertyOption'  | 'property(java.lang.Boolean, fixed(class java.lang.Boolean, false))'
+        'myFieldOption'            | 'false'
+    }
+
+    def "cannot pass boolean option value with #option"() {
+        given:
+        file('buildSrc/src/main/java/SampleTask.java') << taskWithBooleanOptions()
+        buildFile << sampleTask()
+
+        expect:
+        def failure = fails('sample', "$option=false")
+        failure.assertHasCause("Command-line option '$option' does not take an argument.")
+
+        where:
+        option  << ['--myBooleanPrimitiveOption',
+                    '--myBooleanObjectOption',
+                    '--myBooleanPropertyOption',
+                    '--myFieldOption',
+                    '--no-myBooleanPrimitiveOption',
+                    '--no-myBooleanObjectOption',
+                    '--no-myBooleanPropertyOption',
+                    '--no-myFieldOption'
+        ]
+    }
+
+    def "if option and disable option are passed multiple times, last one wins with #option value1=#value1 value2=#value2"() {
+        given:
+        file('buildSrc/src/main/java/SampleTask.java') << taskWithBooleanOptions()
+        buildFile << sampleTask()
+
+        when:
+        run('sample', "--$option", "--no-$option")
+
+        then:
+        outputContains("Value of $option: $value1")
+
+        when:
+        run('sample', "--no-$option", "--$option")
+
+        then:
+        outputContains("Value of $option: $value2")
+
+        where:
+        option                     | value1                                                               | value2
+        'myBooleanPrimitiveOption' | 'false'                                                              | 'true'
+        'myBooleanObjectOption'    | 'false'                                                              | 'true'
+        'myBooleanPropertyOption'  | 'property(java.lang.Boolean, fixed(class java.lang.Boolean, false))' | 'property(java.lang.Boolean, fixed(class java.lang.Boolean, true))'
+        'myFieldOption'            | 'false'                                                              | 'true'
+    }
+
+    def "options of a task shadow clash with generated opposite options"() {
+        given:
+        file('buildSrc/src/main/java/SampleTask.java') << taskWithBooleanOppositeOptionNameClashing()
+        buildFile << sampleTask()
+
+        when:
+        run('sample', "--my-option")
+
+        then:
+        outputContains("Value of my-option: true")
+        outputContains("Value of no-my-option: null")
+        outputContains("Opposite option 'my-option' in task task ':sample' was disabled for clashing with another option of same name")
+        outputContains("Opposite option 'no-my-option' in task task ':sample' was disabled for clashing with another option of same name")
+    }
+
+    def "can render boolean options with help task"() {
+        given:
+        file('buildSrc/src/main/java/SampleTask.java') << taskWithBooleanOptions()
+        buildFile << sampleTask()
+
+        when:
+        succeeds('help', '--task', 'sample')
+
+        then:
+        outputContains("""
+Detailed task information for sample
+
+Path
+     :sample
+
+Type
+     SampleTask (SampleTask)
+
+Options
+     --myBooleanObjectOption     Configures boolean option 'myBooleanObjectOption'
+
+     --myBooleanPrimitiveOption     Configures boolean option 'myBooleanPrimitiveOption'
+
+     --myBooleanPropertyOption     Configures Property<Boolean> option 'myBooleanPropertyOption'
+
+     --myFieldOption     Configures boolean option 'myFieldOption'
+
+     --no-myBooleanObjectOption     Disables option --myBooleanObjectOption
+
+     --no-myBooleanPrimitiveOption     Disables option --myBooleanPrimitiveOption
+
+     --no-myBooleanPropertyOption     Disables option --myBooleanPropertyOption
+
+     --no-myFieldOption     Disables option --myFieldOption
+
+     --rerun     Causes the task to be re-run even if up-to-date.
+
+Description
+     -
+
+Group
+     -""")
+    }
+
+    static String sampleTask() {
+        """
+            task sample(type: SampleTask)
+        """
+    }
+
+    static String taskWithBooleanOptions() {
+        """
+            import org.gradle.api.DefaultTask;
+            import org.gradle.api.provider.Property;
+            import org.gradle.api.tasks.TaskAction;
+            import org.gradle.api.tasks.options.Option;
+
+            public class SampleTask extends DefaultTask {
+                private boolean myBooleanPrimitiveOption;
+                private Boolean myBooleanObjectOption;
+                private Property<Boolean> myBooleanPropertyOption = getProject().getObjects().property(Boolean.class).convention((Boolean)null);
+
+                @Option(description = "Configures boolean option 'myFieldOption'")
+                private Boolean myFieldOption;
+
+                public SampleTask() {}
+
+                @Option(option = "myBooleanPrimitiveOption", description = "Configures boolean option 'myBooleanPrimitiveOption'")
+                public void setMyBooleanPrimitiveOption(boolean myBooleanPrimitiveOption) {
+                    this.myBooleanPrimitiveOption = myBooleanPrimitiveOption;
+                }
+
+                @Option(option = "myBooleanObjectOption", description = "Configures boolean option 'myBooleanObjectOption'")
+                public void setMyBooleanObjectOption(Boolean myBooleanObjectOption) {
+                    this.myBooleanObjectOption = myBooleanObjectOption;
+                }
+
+                @Option(option = "myBooleanPropertyOption", description = "Configures Property<Boolean> option 'myBooleanPropertyOption'")
+                public void setMyBooleanPropertyOption(Boolean myBooleanPropertyOption) {
+                    this.myBooleanPropertyOption.set(myBooleanPropertyOption);
+                }
+
+                public void setMyFieldOption(Boolean myFieldOption) {
+                    this.myFieldOption = myFieldOption;
+                }
+
+                @TaskAction
+                public void renderOptionValue() {
+                    System.out.println("Value of myBooleanPrimitiveOption: " + myBooleanPrimitiveOption);
+                    System.out.println("Value of myBooleanObjectOption: " + myBooleanObjectOption);
+                    System.out.println("Value of myBooleanPropertyOption: " + myBooleanPropertyOption);
+                    System.out.println("Value of myFieldOption: " + myFieldOption);
+                }
+            }
+        """
+    }
+
+    static String taskWithBooleanOppositeOptionNameClashing() {
+        """
+            import org.gradle.api.DefaultTask;
+            import org.gradle.api.provider.Property;
+            import org.gradle.api.tasks.TaskAction;
+            import org.gradle.api.tasks.options.Option;
+
+            public class SampleTask extends DefaultTask {
+
+                @Option(option = "my-option", description = "Option to trigger generation of opposite option")
+                private Boolean myOption;
+
+                @Option(option = "no-my-option", description = "Option clashing with generated opposite option")
+                private Boolean noMyOption;
+
+                public void setMyOption(Boolean myOption) {
+                    this.myOption = myOption;
+                }
+
+                public void setNoMyOption(Boolean noMyOption) {
+                    this.noMyOption = noMyOption;
+                }
+
+                public SampleTask() {}
+
+                @TaskAction
+                public void renderOptionValue() {
+                    System.out.println("Value of my-option: " + myOption);
+                    System.out.println("Value of no-my-option: " + noMyOption);
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestEnvironmentIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestEnvironmentIntegrationTest.groovy
deleted file mode 100644
index 2d6080c..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestEnvironmentIntegrationTest.groovy
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2012 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.testing
-
-import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.gradle.util.Matchers
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_PLATFORM_VERSION
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class TestEnvironmentIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    @Rule public final TestResources resources = new TestResources(temporaryFolder)
-
-    def canRunTestsWithCustomSystemClassLoader() {
-        when:
-        run 'test'
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.JUnitTest')
-        result.testClass('org.gradle.JUnitTest').assertTestPassed('mySystemClassLoaderIsUsed')
-    }
-
-    def canRunTestsReferencingSlf4jWithCustomSystemClassLoader() {
-        when:
-        run 'test'
-
-        then:
-        def testResults = new DefaultTestExecutionResult(testDirectory)
-        testResults.assertTestClassesExecuted('org.gradle.TestUsingSlf4j')
-        with(testResults.testClass('org.gradle.TestUsingSlf4j')) {
-            assertTestPassed('mySystemClassLoaderIsUsed')
-            assertStderr(Matchers.containsText("ERROR via slf4j"))
-            assertStderr(Matchers.containsText("WARN via slf4j"))
-            assertStderr(Matchers.containsText("INFO via slf4j"))
-        }
-    }
-
-    @Requires(TestPrecondition.JDK9_OR_LATER)
-    def canRunTestsReferencingSlf4jWithModularJava() {
-        given:
-        if(isJupiter() && TestPrecondition.JDK14_OR_LATER.fulfilled) {
-            // Otherwise it throws exception:
-            // java.lang.IllegalAccessError: class org.junit.platform.launcher.core.LauncherFactory (in unnamed module @0x2f2a5b2d)
-            // cannot access class org.junit.platform.commons.util.Preconditions (in module org.junit.platform.commons) because module org.junit.platform.commons does not export org.junit.platform.commons.util to unnamed module @0x2f2a5b2d
-            //
-            // See https://github.com/openjdk/skara/pull/66 for details of this workaround
-            buildFile.text = buildFile.text.replace('dependencies {', """dependencies {
-                testRuntimeOnly 'org.junit.platform:junit-platform-launcher:${LATEST_PLATFORM_VERSION}'
-                """)
-        }
-
-        when:
-        run 'test'
-
-        then:
-        def testResults = new DefaultTestExecutionResult(testDirectory)
-        testResults.assertTestClassesExecuted('org.gradle.example.TestUsingSlf4j')
-        with(testResults.testClass('org.gradle.example.TestUsingSlf4j')) {
-            assertTestPassed('testModular')
-            assertStderr(Matchers.containsText("ERROR via slf4j"))
-            assertStderr(Matchers.containsText("WARN via slf4j"))
-            assertStderr(Matchers.containsText("INFO via slf4j"))
-        }
-    }
-
-    @Requires(TestPrecondition.JDK8_OR_EARLIER) //hangs on Java9
-    def canRunTestsWithCustomSystemClassLoaderAndJavaAgent() {
-        when:
-        run 'test'
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.JUnitTest')
-        result.testClass('org.gradle.JUnitTest').assertTestPassed('mySystemClassLoaderIsUsed')
-    }
-
-    def canRunTestsWithCustomSecurityManager() {
-        executer
-                .withArgument("-Porg.gradle.java.installations.paths=${AvailableJavaHomes.getAvailableJvms().collect { it.javaHome.absolutePath }.join(",")}")
-                .withToolchainDetectionEnabled()
-
-        when:
-        run 'test'
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.JUnitTest')
-        result.testClass('org.gradle.JUnitTest').assertTestPassed('mySecurityManagerIsUsed')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestListenerBuildOperationAdapterIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestListenerBuildOperationAdapterIntegrationTest.groovy
deleted file mode 100644
index 92d8f78..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestListenerBuildOperationAdapterIntegrationTest.groovy
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2017 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.testing
-
-import org.gradle.api.internal.tasks.testing.operations.ExecuteTestBuildOperationType
-import org.gradle.integtests.fixtures.BuildOperationsFixture
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.*
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE })
-class TestListenerBuildOperationAdapterIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-
-    @Rule
-    final TestResources resources = new TestResources(testDirectoryProvider)
-
-    def operations = new BuildOperationsFixture(executer, temporaryFolder)
-
-    def "emits build operations for junit tests"() {
-        given:
-        resources.maybeCopy('/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible')
-
-        when:
-        succeeds "test"
-
-        then:
-        def ops = operations.all(ExecuteTestBuildOperationType) { true }
-        def iterator = ops.iterator()
-
-        with(iterator.next()) {
-            details.testDescriptor.name == "Gradle Test Run :test"
-            details.testDescriptor.className == null
-            details.testDescriptor.composite == true
-        }
-        with(iterator.next()) {
-            details.testDescriptor.name ==~ "Gradle Test Executor \\d+"
-            details.testDescriptor.className == null
-            details.testDescriptor.composite == true
-        }
-        with(iterator.next()) {
-            details.testDescriptor.name == "org.gradle.ASuite"
-            details.testDescriptor.className == "org.gradle.ASuite"
-            details.testDescriptor.composite == true
-        }
-        if (isVintage()) {
-            with(iterator.next()) {
-                details.testDescriptor.name == "org.gradle.OkTest"
-                details.testDescriptor.className == "org.gradle.OkTest"
-                details.testDescriptor.composite == true
-            }
-        }
-        with(iterator.next()) {
-            details.testDescriptor.name == "anotherOk"
-            details.testDescriptor.className == "org.gradle.OkTest"
-            details.testDescriptor.composite == false
-        }
-        with(iterator.next()) {
-            details.testDescriptor.name == "ok"
-            details.testDescriptor.className == "org.gradle.OkTest"
-            details.testDescriptor.composite == false
-        }
-        if (isVintage()) {
-            with(iterator.next()) {
-                details.testDescriptor.name == "org.gradle.OtherTest"
-                details.testDescriptor.className == "org.gradle.OtherTest"
-                details.testDescriptor.composite == true
-            }
-        }
-        with(iterator.next()) {
-            details.testDescriptor.name == "ok"
-            details.testDescriptor.className == "org.gradle.OtherTest"
-            details.testDescriptor.composite == false
-        }
-        !iterator.hasNext()
-    }
-
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestOptionsIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestOptionsIntegrationSpec.groovy
index 4652fde..1957542 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestOptionsIntegrationSpec.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestOptionsIntegrationSpec.groovy
@@ -18,6 +18,8 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.HtmlTestExecutionResult
+import org.gradle.testing.fixture.JUnitCoverage
+import spock.lang.Issue
 
 /**
  * These tests demonstrate what is and isn't allowed in terms of modifying the {@link org.gradle.api.tasks.testing.TestFrameworkOptions TestFrameworkOptions}
@@ -68,29 +70,6 @@
         """
     }
     // region default test suite
-    def "can NOT set options and then change framework within the default suite"() {
-        given:
-        buildFile << """
-        testing {
-           suites {
-               test {
-                   targets.all {
-                       testTask.configure {
-                           options {
-                               // forces the test framework to be JUnit
-                               includeCategories 'org.gradle.CategoryA'
-                           }
-                           useJUnitPlatform()
-                       }
-                   }
-               }
-           }
-        }""".stripMargin()
-
-        expect:
-        fails ":test"
-        failure.assertHasCause("The value for task ':test' property 'testFrameworkProperty' is final and cannot be changed any further.")
-    }
 
     def "can set test framework in default test suite prior to setting options within test task"() {
         given:
@@ -174,7 +153,8 @@
         given:
         buildFile << """
         dependencies {
-            implementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+            testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+            testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
         }
 
         test {
@@ -242,56 +222,6 @@
         succeeds ":integTest"
         assertIntegTestsExecuted()
     }
-
-    def "can NOT set new framework for custom suite in different testing block after configuring options"() {
-        buildFile << """
-            testing {
-                suites {
-                    integTest(JvmTestSuite) {
-                        useJUnit()
-                        targets.all {
-                            // explicitly realize the task now to cause this configuration to run now
-                            testTask.get().configure {
-                                options {
-                                    excludeCategories "com.example.Exclude"
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            testing.suites.integTest {
-                useTestNG()
-            }
-        """
-
-        when:
-        fails("integTest")
-
-        then:
-        failure.assertHasCause("The value for property 'testSuiteTestingFramework' is final and cannot be changed any further.")
-    }
-
-    def "can NOT set test framework in test task when created by test suite"() {
-        given:
-        buildFile << """
-        // Ensure non-default suites exist
-        testing {
-           suites {
-               integTest(JvmTestSuite)
-            }
-        }
-
-        // Configure task directly using name
-        integTest {
-            useJUnitPlatform()
-        }""".stripMargin()
-
-        expect:
-        fails ":integTest"
-        failure.assertHasCause("The value for task ':integTest' property 'testFrameworkProperty' cannot be changed any further.")
-    }
     // endregion custom test suite
 
     // region all suites
@@ -405,6 +335,109 @@
     }
     // endregion stand-alone test tasks
 
+    @Issue("https://github.com/gradle/gradle/issues/24331")
+    def "options can be set with framework before java plugin is applied"() {
+        given:
+        settingsFile << "rootProject.name = 'Sample'"
+        buildFile.text = """
+            tasks.withType(Test).configureEach {
+                useJUnitPlatform() {
+                    excludeTags = ["Slow"]
+                }
+                doFirst {
+                    assert options.excludeTags.contains("Slow")
+                }
+            }
+
+            apply plugin: 'java'
+
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'org.junit.jupiter:junit-jupiter:${JUnitCoverage.LATEST_JUPITER_VERSION}'
+                testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+            }
+        """.stripIndent()
+
+        expect:
+        succeeds("check")
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/24331")
+    def "options can be set with matching framework after test suite is created"() {
+        given:
+        settingsFile << "rootProject.name = 'Sample'"
+        buildFile << """
+            dependencies {
+                testImplementation 'org.junit.jupiter:junit-jupiter:${JUnitCoverage.LATEST_JUPITER_VERSION}'
+            }
+
+            testing {
+                suites {
+                    test {
+                        useJUnitJupiter()
+                    }
+                    integrationTest(JvmTestSuite) {
+                        useJUnitJupiter()
+                    }
+                }
+            }
+
+            tasks.withType(Test).configureEach {
+                useJUnitPlatform() {
+                    excludeTags = ["Slow"]
+                }
+                doFirst {
+                    assert options.excludeTags.contains("Slow")
+                }
+            }
+
+            tasks.named('check') {
+                dependsOn(testing.suites.integrationTest)
+            }
+        """.stripIndent()
+
+        expect:
+        succeeds("check")
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/24331")
+    def "options can be set with matching framework before test suite is created"() {
+        given:
+        settingsFile << "rootProject.name = 'Sample'"
+        buildFile << """
+            dependencies {
+                testImplementation 'org.junit.jupiter:junit-jupiter:${JUnitCoverage.LATEST_JUPITER_VERSION}'
+            }
+
+            tasks.withType(Test).configureEach {
+                useJUnitPlatform() {
+                    excludeTags = ["Slow"]
+                }
+                doFirst {
+                    assert options.excludeTags.contains("Slow")
+                }
+            }
+
+            testing {
+                suites {
+                    test {
+                        useJUnitJupiter()
+                    }
+                    integrationTest(JvmTestSuite) {
+                        useJUnitJupiter()
+                    }
+                }
+            }
+
+            tasks.named('check') {
+                dependsOn(testing.suites.integrationTest)
+            }
+        """.stripIndent()
+
+        expect:
+        succeeds("check")
+    }
+
     private void assertTestsExecuted() {
         def result = new HtmlTestExecutionResult(testDirectory)
         result.assertTestClassesExecuted("org.example.SomeTestClass")
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestOutputListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestOutputListenerIntegrationTest.groovy
deleted file mode 100644
index ae3639f..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestOutputListenerIntegrationTest.groovy
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright 2011 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.testing
-
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.UnsupportedWithConfigurationCache
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Before
-import org.junit.Rule
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
-
-@Issue("GRADLE-1009")
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class TestOutputListenerIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    @Rule public final TestResources resources = new TestResources(temporaryFolder)
-
-    @Before
-    void before() {
-        executer.noExtraLogging()
-    }
-
-    def "can use standard output listener for tests"() {
-        given:
-        def test = file("src/test/java/SomeTest.java")
-        test << """
-import org.junit.*;
-
-public class SomeTest {
-    @Test public void showsOutputWhenPassing() {
-        System.out.println("out passing");
-        System.err.println("err passing");
-        Assert.assertTrue(true);
-    }
-
-    @Test public void showsOutputWhenFailing() {
-        System.out.println("out failing");
-        System.err.println("err failing");
-        Assert.assertTrue(false);
-    }
-}
-"""
-        def buildFile = file('build.gradle')
-        buildFile << """
-apply plugin: 'java'
-${mavenCentralRepository()}
-dependencies { testImplementation "junit:junit:4.13" }
-
-test.addTestOutputListener(new VerboseOutputListener(logger: project.logger))
-
-def removeMe = new RemoveMeListener()
-test.addTestOutputListener(removeMe)
-test.removeTestOutputListener(removeMe)
-
-class VerboseOutputListener implements TestOutputListener {
-
-    def logger
-
-    public void onOutput(TestDescriptor descriptor, TestOutputEvent event) {
-        logger.lifecycle(descriptor.toString() + " " + event.destination + " " + event.message);
-    }
-}
-
-class RemoveMeListener implements TestOutputListener {
-    public void onOutput(TestDescriptor descriptor, TestOutputEvent event) {
-        println "remove me!"
-    }
-}
-"""
-
-        when:
-        def failure = executer.withTasks('test').runWithFailure()
-
-        then:
-        failure.output.contains('Test showsOutputWhenPassing(SomeTest) StdOut out passing')
-        failure.output.contains('Test showsOutputWhenFailing(SomeTest) StdOut out failing')
-        failure.output.contains('Test showsOutputWhenPassing(SomeTest) StdErr err passing')
-        failure.output.contains('Test showsOutputWhenFailing(SomeTest) StdErr err failing')
-
-        !failure.output.contains("remove me!")
-    }
-
-    @UnsupportedWithConfigurationCache
-    def "can register output listener at gradle level and using onOutput method"() {
-        given:
-        def test = file("src/test/java/SomeTest.java")
-        test << """
-import org.junit.*;
-
-public class SomeTest {
-    @Test public void foo() {
-        System.out.println("message from foo");
-    }
-}
-"""
-        def buildFile = file('build.gradle')
-        buildFile << """
-apply plugin: 'java'
-${mavenCentralRepository()}
-dependencies { testImplementation "junit:junit:4.13" }
-
-test.onOutput { descriptor, event ->
-    logger.lifecycle("first: " + event.message)
-}
-
-gradle.addListener(new VerboseOutputListener(logger: project.logger))
-
-class VerboseOutputListener implements TestOutputListener {
-
-    def logger
-
-    public void onOutput(TestDescriptor descriptor, TestOutputEvent event) {
-        logger.lifecycle("second: " + event.message);
-    }
-}
-"""
-
-        when:
-        succeeds('test')
-
-        then:
-        outputContains('first: message from foo')
-        outputContains('second: message from foo')
-    }
-
-    def "shows standard streams configured via closure"() {
-        given:
-        def test = file("src/test/java/SomeTest.java")
-        test << """
-import org.junit.*;
-
-public class SomeTest {
-    @Test public void foo() {
-        System.out.println("message from foo");
-    }
-}
-"""
-        def buildFile = file('build.gradle')
-        buildFile << """
-apply plugin: 'java'
-${mavenCentralRepository()}
-dependencies { testImplementation "junit:junit:4.13" }
-
-test.testLogging {
-    showStandardStreams = true
-}
-"""
-
-        when:
-        executer.withArgument('-i')
-        succeeds('test')
-
-        then:
-        outputContains('message from foo')
-    }
-
-    def "shows standard stream also for testNG"() {
-        given:
-        ignoreWhenJUnitPlatform()
-        def test = file("src/test/java/SomeTest.java")
-        test << """
-import org.testng.*;
-import org.testng.annotations.*;
-
-public class SomeTest {
-    @Test public void foo() {
-        System.out.println("output from foo");
-        System.err.println("error from foo");
-    }
-}
-"""
-
-        def buildFile = file('build.gradle')
-        buildFile << """
-apply plugin: 'java'
-${mavenCentralRepository()}
-dependencies { testImplementation 'org.testng:testng:6.3.1' }
-
-test {
-    useTestNG()
-    testLogging.showStandardStreams = true
-}
-"""
-        when: "run with quiet"
-        executer.withArguments("-q")
-        succeeds('test')
-
-        then:
-        outputDoesNotContain('output from foo')
-
-        when: "run with lifecycle"
-        executer.noExtraLogging()
-        succeeds('cleanTest', 'test')
-
-        then:
-        outputContains('output from foo')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestProgressLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestProgressLoggingIntegrationTest.groovy
deleted file mode 100644
index 91fc149..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestProgressLoggingIntegrationTest.groovy
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2016 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.testing
-
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.executer.ProgressLoggingFixture
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class TestProgressLoggingIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    @Rule final TestResources resources = new TestResources(temporaryFolder)
-    @Rule ProgressLoggingFixture events = new ProgressLoggingFixture(executer, temporaryFolder)
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation "junit:junit:4.13" }
-        """
-    }
-
-    def "captures test progress logging events" () {
-        withGoodTestClasses(10)
-
-        when:
-        succeeds("test")
-
-        then:
-        events.statusLogged("0 tests completed")
-        events.statusLogged("1 test completed")
-        (2..10).each { count ->
-            assert events.statusLogged("${count} tests completed")
-        }
-    }
-
-    def "captures failing test progress logging events" () {
-        withFailingTestClasses(10)
-
-        when:
-        fails("test")
-
-        then:
-        events.statusLogged("0 tests completed")
-        events.statusLogged("1 test completed, 1 failed")
-        (2..10).each { count ->
-            assert events.statusLogged("${count} tests completed, ${count} failed")
-        }
-    }
-
-    def "captures skipped test progress logging events" () {
-        withSkippedTestClasses(10)
-
-        when:
-        succeeds("test")
-
-        then:
-        events.statusLogged("0 tests completed")
-        events.statusLogged("1 test completed, 1 skipped")
-        (2..10).each { count ->
-            assert events.statusLogged("${count} tests completed, ${count} skipped")
-        }
-    }
-
-    def "captures mixed test progress logging events" () {
-        withGoodTestClasses(5)
-        withFailingTestClasses(3)
-        withSkippedTestClasses(2)
-
-        when:
-        fails("test")
-
-        then:
-        (0..10).each { count ->
-            assert events.statusMatches("${count} tests? completed(,.*)*")
-        }
-
-        and:
-        (1..3).each { count ->
-            assert events.statusMatches("\\d+ tests? completed, ${count} failed(,.*)?")
-        }
-
-        and:
-        (1..2).each { count ->
-            assert events.statusMatches("\\d+ tests? completed,( \\d failed,)? ${count} skipped")
-        }
-    }
-
-    def "captures test progress logging events when tests are run in parallel" () {
-        withGoodTestClasses(4)
-        buildFile << """
-            test {
-                maxParallelForks = 4
-            }
-        """
-
-        when:
-        succeeds("test")
-
-        then:
-        events.statusLogged("0 tests completed")
-        events.statusLogged("1 test completed")
-        (2..4).each { count ->
-            assert events.statusLogged("${count} tests completed")
-        }
-    }
-
-    def createTestClass(int index, String type, String method, String assertion) {
-        String className = "${type.capitalize()}Test${index}"
-        file("src/test/java/${className}.java") << """
-            public class ${className} {
-                @org.junit.Test
-                public void ${method}() {
-                    ${assertion};
-                }
-            }
-        """
-    }
-
-    def createGoodTestClass(int index=1) {
-        createTestClass(index, "good", "shouldPass", "org.junit.Assert.assertEquals(1,1)")
-    }
-
-    def createFailingTestClass(int index=1) {
-        createTestClass(index, "failing", "shouldFail", "org.junit.Assert.assertEquals(1,2)")
-    }
-
-    def createSkippedTestClass(int index=1) {
-        createTestClass(index, "skipped", "shouldBeSkipped", "org.junit.Assume.assumeTrue(false)")
-    }
-
-    def withGoodTestClasses(int count) {
-        count.times { index ->
-            createGoodTestClass(index)
-        }
-    }
-
-    def withFailingTestClasses(int count) {
-        count.times { index ->
-            createFailingTestClass(index)
-        }
-    }
-
-    def withSkippedTestClasses(int count) {
-        count.times { index ->
-            createSkippedTestClass(index)
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestReportIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestReportIntegrationTest.groovy
deleted file mode 100644
index 6a4c887..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestReportIntegrationTest.groovy
+++ /dev/null
@@ -1,648 +0,0 @@
-/*
- * Copyright 2013 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.testing
-
-import org.gradle.integtests.fixtures.HtmlTestExecutionResult
-import org.gradle.integtests.fixtures.JUnitTestClassExecutionResult
-import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.UsesSample
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.hamcrest.CoreMatchers
-import org.junit.Rule
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUPITER
-import static org.gradle.testing.fixture.JUnitCoverage.VINTAGE
-import static org.hamcrest.CoreMatchers.allOf
-import static org.hamcrest.CoreMatchers.containsString
-import static org.hamcrest.CoreMatchers.equalTo
-import static org.hamcrest.CoreMatchers.is
-import static org.junit.Assume.assumeTrue
-
-@TargetCoverage({ JUNIT_4_LATEST + [JUPITER, VINTAGE] })
-class TestReportIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    @Rule
-    Sample sample = new Sample(temporaryFolder)
-
-    def "report includes results of most recent invocation"() {
-        given:
-        buildFile << """
-$junitSetup
-test {
-    def logLessStuff = providers.systemProperty('LogLessStuff')
-    systemProperty 'LogLessStuff', logLessStuff.orNull
-}
-"""
-
-        and:
-        file("src/test/java/LoggingTest.java") << """
-public class LoggingTest {
-    @org.junit.Test
-    public void test() {
-        if (System.getProperty("LogLessStuff", "false").equals("true")) {
-            System.out.println("stdout.");
-            System.err.println("stderr.");
-        } else {
-            System.out.println("This is stdout.");
-            System.err.println("This is stderr.");
-        }
-    }
-}
-"""
-
-        when:
-        run "test"
-
-        then:
-        def result = new HtmlTestExecutionResult(testDirectory)
-        result.testClass("LoggingTest").assertStdout(equalTo("This is stdout.\n"))
-        result.testClass("LoggingTest").assertStderr(equalTo("This is stderr.\n"))
-
-        when:
-        executer.withArguments("-DLogLessStuff=true")
-        run "test"
-
-        then:
-        result.testClass("LoggingTest").assertStdout(equalTo("stdout.\n"))
-        result.testClass("LoggingTest").assertStderr(equalTo("stderr.\n"))
-    }
-
-    @UsesSample("testing/testReport/groovy")
-    def "can generate report for subprojects"() {
-        given:
-        super.sample(sample)
-
-        when:
-        run "testReport"
-
-        then:
-        def htmlReport = new HtmlTestExecutionResult(sample.dir, "build/reports/allTests")
-        htmlReport.testClass("org.gradle.sample.CoreTest").assertTestCount(1, 0, 0).assertTestPassed("ok").assertStdout(equalTo("hello from CoreTest.\n"))
-        htmlReport.testClass("org.gradle.sample.UtilTest").assertTestCount(1, 0, 0).assertTestPassed("ok").assertStdout(equalTo("hello from UtilTest.\n"))
-    }
-
-    def "merges report with duplicated classes and methods"() {
-        given:
-        ignoreWhenJupiter()
-        buildFile << """
-$junitSetup
-def test = tasks.named('test', Test)
-test.configure {
-    classpath = tasks.test.classpath
-    testClassesDirs = tasks.test.testClassesDirs
-    ignoreFailures true
-    useJUnit {
-        excludeCategories 'org.gradle.testing.SuperClassTests'
-        excludeCategories 'org.gradle.testing.SubClassTests'
-    }
-}
-
-def superTest = tasks.register('superTest', Test) {
-    classpath = tasks.test.classpath
-    testClassesDirs = tasks.test.testClassesDirs
-    ignoreFailures true
-    systemProperty 'category', 'super'
-    useJUnit {
-        includeCategories 'org.gradle.testing.SuperClassTests'
-    }
-}
-
-def subTest = tasks.register('subTest', Test) {
-    classpath = tasks.test.classpath
-    testClassesDirs = tasks.test.testClassesDirs
-    ignoreFailures true
-    systemProperty 'category', 'sub'
-    useJUnit {
-        includeCategories 'org.gradle.testing.SubClassTests'
-    }
-}
-
-def testReport = tasks.register('testReport', TestReport) {
-    destinationDirectory = reporting.baseDirectory.dir('allTests')
-    testResults.from(test, superTest, subTest)
-}
-
-tasks.named('build').configure { it.dependsOn testReport }
-"""
-
-        and:
-        file("src/test/java/org/gradle/testing/UnitTest.java") << """
-$testFilePrelude
-public class UnitTest {
-    @Test public void foo() {
-        System.out.println("org.gradle.testing.UnitTest#foo");
-    }
-}
-"""
-        file("src/test/java/org/gradle/testing/SuperTest.java") << """
-$testFilePrelude
-public class SuperTest {
-    @Category(SuperClassTests.class) @Test public void failing() {
-        System.out.println("org.gradle.testing.SuperTest#failing");
-        fail("failing test");
-    }
-    @Category(SuperClassTests.class) @Test public void passing() {
-        System.out.println("org.gradle.testing.SuperTest#passing");
-    }
-}
-"""
-        file("src/test/java/org/gradle/testing/SubTest.java") << """
-$testFilePrelude
-public class SubTest {
-    @Category(SubClassTests.class) @Test public void onlySub() {
-        System.out.println("org.gradle.testing.SubTest#onlySub " + System.getProperty("category"));
-        assertEquals("sub", System.getProperty("category"));
-    }
-    @Category(SubClassTests.class) @Test public void passing() {
-        System.out.println("org.gradle.testing.SubTest#passing " + System.getProperty("category"));
-    }
-}
-"""
-        file("src/test/java/org/gradle/testing/SuperClassTests.java") << """
-$testFilePrelude
-public class SuperClassTests {
-}
-"""
-        file("src/test/java/org/gradle/testing/SubClassTests.java") << """
-$testFilePrelude
-public class SubClassTests extends SuperClassTests {
-}
-"""
-
-        when:
-        run "testReport"
-
-        then:
-        def htmlReport = new HtmlTestExecutionResult(testDirectory, 'build/reports/allTests')
-        htmlReport.testClass("org.gradle.testing.UnitTest").assertTestCount(1, 0, 0).assertTestPassed("foo").assertStdout(equalTo('org.gradle.testing.UnitTest#foo\n'))
-        htmlReport.testClass("org.gradle.testing.SuperTest").assertTestCount(2, 1, 0).assertTestPassed("passing")
-            .assertTestFailed("failing", equalTo('java.lang.AssertionError: failing test'))
-            .assertStdout(allOf(containsString('org.gradle.testing.SuperTest#failing\n'), containsString('org.gradle.testing.SuperTest#passing\n')))
-        htmlReport.testClass("org.gradle.testing.SubTest").assertTestCount(4, 1, 0).assertTestPassed("passing") // onlySub is passing once and failing once
-            .assertStdout(allOf(containsString('org.gradle.testing.SubTest#passing sub\n'),
-                containsString('org.gradle.testing.SubTest#passing super\n'),
-                containsString('org.gradle.testing.SubTest#onlySub sub\n'),
-                containsString('org.gradle.testing.SubTest#onlySub super\n')))
-    }
-
-    def "test report task can handle test tasks that did not run tests"() {
-        given:
-        buildScript """
-            apply plugin: 'java'
-
-             $junitSetup
-
-            def test = tasks.named('test', Test)
-
-            def otherTests = tasks.register('otherTests', Test) {
-                binaryResultsDirectory = file("bin")
-                classpath = files('blahClasspath')
-                testClassesDirs = files("blah")
-            }
-
-            tasks.register('testReport', TestReport) {
-                testResults.from(test, otherTests)
-                destinationDirectory = reporting.baseDirectory.dir('tr')
-            }
-        """
-
-        and:
-        testClass("Thing")
-
-        when:
-        succeeds "testReport"
-
-        then:
-        skipped(":otherTests")
-        executedAndNotSkipped(":test")
-        new HtmlTestExecutionResult(testDirectory, "build/reports/tr").assertTestClassesExecuted("Thing")
-    }
-
-
-    // TODO: remove in Gradle 9.0
-    def "nag with deprecation warnings when using legacy TestReport APIs"() {
-        given:
-        buildScript """
-            apply plugin: 'java'
-             $junitSetup
-            tasks.register('otherTests', Test) {
-                binaryResultsDirectory = file("bin")
-                classpath = files('blahClasspath')
-                testClassesDirs = files("blah")
-            }
-            tasks.register('testReport', TestReport) {
-                reportOn test, otherTests
-                destinationDir reporting.file("tr")
-            }
-        """
-
-        and:
-        testClass("Thing")
-
-        when:
-        executer.expectDocumentedDeprecationWarning('The TestReport.reportOn(Object...) method has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the testResults method instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.TestReport.html#org.gradle.api.tasks.testing.TestReport:testResults for more details.')
-        executer.expectDocumentedDeprecationWarning('The TestReport.destinationDir property has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the destinationDirectory property instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.TestReport.html#org.gradle.api.tasks.testing.TestReport:destinationDir for more details.')
-        succeeds "testReport"
-
-        then:
-        skipped(":otherTests")
-        executedAndNotSkipped(":test")
-        new HtmlTestExecutionResult(testDirectory, "build/reports/tr").assertTestClassesExecuted("Thing")
-    }
-
-    @Issue("https://issues.gradle.org//browse/GRADLE-2915")
-    def "test report task can handle tests tasks not having been executed"() {
-        when:
-        buildScript """
-            apply plugin: 'java'
-
-             $junitSetup
-
-            tasks.register('testReport', TestReport) {
-                testResults.from(tasks.named('test', Test))
-                destinationDirectory = reporting.baseDirectory.dir("tr")
-            }
-        """
-
-        and:
-        testClass("Thing")
-
-        then:
-        succeeds "testReport"
-
-        // verify incremental behavior on 2nd invocation
-        succeeds "testReport"
-        skipped ":testReport"
-    }
-
-    def "test report task is skipped when there are no results"() {
-        given:
-        buildScript """
-            apply plugin: 'java'
-
-            tasks.register('testReport', TestReport) {
-                testResults.from(tasks.named('test', Test))
-                destinationDirectory = reporting.baseDirectory.dir('tr')
-            }
-        """
-
-        when:
-        succeeds "testReport"
-
-        then:
-        skipped(":test")
-        skipped(":testReport")
-    }
-
-    def "#type report files are considered outputs"() {
-        given:
-        buildScript """
-            $junitSetup
-        """
-
-        and:
-        testClass "SomeTest"
-
-        when:
-        run "test"
-
-        then:
-        executedAndNotSkipped(":test")
-        file(reportsDir).exists()
-
-        when:
-        run "test"
-
-        then:
-        skipped(":test")
-        file(reportsDir).exists()
-
-        when:
-        file(reportsDir).deleteDir()
-        run "test"
-
-        then:
-        executedAndNotSkipped(":test")
-        file(reportsDir).exists()
-
-        where:
-        type   | reportsDir
-        "xml"  | "build/test-results"
-        "html" | "build/reports/tests"
-    }
-
-    def "results or reports are linked to in error output"() {
-        given:
-        buildScript """
-            $junitSetup
-            test {
-                reports.all { it.required = true }
-            }
-        """
-
-        and:
-        failingTestClass "SomeTest"
-
-        when:
-        fails "test"
-
-        then:
-        executedAndNotSkipped(":test")
-        failure.assertHasCause("There were failing tests. See the report at: ")
-
-        when:
-        buildFile << "\ntest.reports.html.required = false\n"
-        fails "test"
-
-        then:
-        executedAndNotSkipped(":test")
-        failure.assertHasCause("There were failing tests. See the results at: ")
-
-        when:
-        buildFile << "\ntest.reports.junitXml.required = false\n"
-        fails "test"
-
-        then:
-        executedAndNotSkipped(":test")
-        failure.assertHasCause("There were failing tests")
-        failure.assertHasNoCause("See the")
-    }
-
-    def "output per test case flag invalidates outputs"() {
-        when:
-        buildScript """
-            $junitSetup
-            test.reports.junitXml.outputPerTestCase = false
-        """
-        testClass "SomeTest"
-        succeeds "test"
-
-        then:
-        executedAndNotSkipped(":test")
-
-        when:
-        buildFile << "\ntest.reports.junitXml.outputPerTestCase = true\n"
-        succeeds "test"
-
-        then:
-        executedAndNotSkipped(":test")
-    }
-
-    def "can enable merge rerun in xml report"() {
-        when:
-        assumeTrue("This test uses JUnit4-specific API", isJUnit4())
-        buildScript """
-            $junitSetup
-            test.reports.junitXml.mergeReruns = true
-        """
-        rerunningTest("SomeTest")
-        fails "test"
-
-        then:
-        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
-        def clazz = xmlReport.testClass("SomeTest")
-        clazz.testCount == 6
-        (clazz as JUnitTestClassExecutionResult).testCasesCount == 2
-        clazz.assertTestPassed("testFlaky[]")
-        clazz.assertTestFailed("testFailing[]", CoreMatchers.anything())
-    }
-
-    def "merge rerun defaults to false"() {
-        when:
-        assumeTrue("This test uses JUnit4-specific API", isJUnit4())
-        buildScript """
-            $junitSetup
-        """
-        rerunningTest("SomeTest")
-        fails "test"
-
-        then:
-        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
-        def clazz = xmlReport.testClass("SomeTest")
-        clazz.testCount == 6
-        (clazz as JUnitTestClassExecutionResult).testCasesCount == 6
-    }
-
-    private TestFile rerunningTest(String className) {
-        file("src/test/java/${className}.java") << """
-            import org.junit.Assert;
-            import org.junit.Test;
-
-            @org.junit.runner.RunWith(org.junit.runners.Parameterized.class)
-            public class $className {
-
-                @org.junit.runners.Parameterized.Parameters(name = "")
-                public static Object[] data() {
-                    return new Object[] { 1, 2, 3 };
-                }
-
-                private final int i;
-
-                public SomeTest(int i) {
-                    this.i = i;
-                }
-
-                @Test
-                public void testFlaky() {
-                    System.out.println("execution " + i);
-                    Assert.assertTrue(i == 3);
-                }
-
-                @Test
-                public void testFailing() {
-                    System.out.println("execution " + i);
-                    Assert.assertTrue(false);
-                }
-            }
-        """
-    }
-
-    def "outputs over lifecycle"() {
-        when:
-        buildScript """
-            $junitSetup
-            test.reports.junitXml.outputPerTestCase = true
-        """
-
-        file("src/test/java/OutputLifecycleTest.java") << """
-            import org.junit.*;
-
-            public class OutputLifecycleTest {
-
-                public OutputLifecycleTest() {
-                    System.out.println("constructor out");
-                    System.err.println("constructor err");
-                }
-
-                @BeforeClass
-                public static void beforeClass() {
-                    System.out.println("beforeClass out");
-                    System.err.println("beforeClass err");
-                }
-
-                @Before
-                public void beforeTest() {
-                    System.out.println("beforeTest out");
-                    System.err.println("beforeTest err");
-                }
-
-                @Test public void m1() {
-                    System.out.println("m1 out");
-                    System.err.println("m1 err");
-                }
-
-                @Test public void m2() {
-                    System.out.println("m2 out");
-                    System.err.println("m2 err");
-                }
-
-                @After
-                public void afterTest() {
-                    System.out.println("afterTest out");
-                    System.err.println("afterTest err");
-                }
-
-                @AfterClass
-                public static void afterClass() {
-                    System.out.println("afterClass out");
-                    System.err.println("afterClass err");
-                }
-            }
-        """
-
-        succeeds "test"
-
-        then:
-        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
-        def clazz = xmlReport.testClass("OutputLifecycleTest")
-        if (isJupiter()) {
-            clazz.assertTestCaseStderr("m1", is("beforeTest err\nm1 err\nafterTest err\n"))
-            clazz.assertTestCaseStderr("m2", is("beforeTest err\nm2 err\nafterTest err\n"))
-            clazz.assertTestCaseStdout("m1", is("beforeTest out\nm1 out\nafterTest out\n"))
-            clazz.assertTestCaseStdout("m2", is("beforeTest out\nm2 out\nafterTest out\n"))
-            clazz.assertStderr(is("beforeClass err\nconstructor err\nconstructor err\nafterClass err\n"))
-            clazz.assertStdout(is("beforeClass out\nconstructor out\nconstructor out\nafterClass out\n"))
-        } else {
-            // Output behavior change in JUnit 4.13
-            clazz.assertTestCaseStderr("m1", is("constructor err\nbeforeTest err\nm1 err\nafterTest err\n"))
-            clazz.assertTestCaseStderr("m2", is("constructor err\nbeforeTest err\nm2 err\nafterTest err\n"))
-            clazz.assertTestCaseStdout("m1", is("constructor out\nbeforeTest out\nm1 out\nafterTest out\n"))
-            clazz.assertTestCaseStdout("m2", is("constructor out\nbeforeTest out\nm2 out\nafterTest out\n"))
-            clazz.assertStderr(is("beforeClass err\nafterClass err\n"))
-            clazz.assertStdout(is("beforeClass out\nafterClass out\n"))
-        }
-    }
-
-    def "collects output for failing non-root suite descriptors"() {
-        assumeTrue("TestExecutionListener only works on the JUnit Platform", isJUnitPlatform())
-
-        given:
-        buildScript """
-            $junitSetup
-            dependencies {
-                testImplementation(platform('org.junit:junit-bom:$dependencyVersion'))
-                testImplementation('org.junit.platform:junit-platform-launcher')
-            }
-        """
-
-        and:
-        testClass "SomeTest"
-        file("src/test/java/ThrowingListener.java") << """
-            import org.junit.platform.launcher.*;
-            public class ThrowingListener implements TestExecutionListener {
-                @Override
-                public void testPlanExecutionStarted(TestPlan testPlan) {
-                    System.out.println("System.out from ThrowingListener");
-                    System.err.println("System.err from ThrowingListener");
-                    throw new OutOfMemoryError("not caught by JUnit Platform");
-                }
-            }
-        """
-        file("src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener") << "ThrowingListener"
-
-        when:
-        fails "test"
-
-        then:
-        new HtmlTestExecutionResult(testDirectory)
-            .testClassStartsWith("Gradle Test Executor")
-            .assertTestFailed("failed to execute tests", containsString("Could not complete execution"))
-            .assertStdout(containsString("System.out from ThrowingListener"))
-            .assertStderr(containsString("System.err from ThrowingListener"))
-    }
-
-    // TODO: remove in Gradle 9.0
-    def "using deprecated testReport elements emits deprecation warnings"() {
-        when:
-        buildScript """
-            apply plugin: 'java'
-            $junitSetup
-            // Need a second test task to reportOn
-            tasks.register('otherTests', Test) {
-                binaryResultsDirectory = file('otherBin')
-                classpath = files('otherClasspath')
-                testClassesDirs = files('otherClasses')
-            }
-            tasks.register('testReport', TestReport) {
-                reportOn test, otherTests
-                destinationDir reporting.file("myTestReports")
-            }
-        """
-
-        then:
-        executer.expectDocumentedDeprecationWarning('The TestReport.reportOn(Object...) method has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the testResults method instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.TestReport.html#org.gradle.api.tasks.testing.TestReport:testResults for more details.')
-        executer.expectDocumentedDeprecationWarning('The TestReport.destinationDir property has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the destinationDirectory property instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.TestReport.html#org.gradle.api.tasks.testing.TestReport:destinationDir for more details.')
-        succeeds "testReport"
-    }
-
-    private String getJunitSetup() {
-        """
-        apply plugin: 'java'
-        ${mavenCentralRepository()}
-        dependencies { testImplementation 'junit:junit:4.13' }
-        """
-    }
-
-    private String getTestFilePrelude() {
-        """
-package org.gradle.testing;
-
-import static org.junit.Assert.*;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-"""
-    }
-
-    private void failingTestClass(String name) {
-        testClass(name, true)
-    }
-
-    private void testClass(String name, boolean failing = false) {
-        file("src/test/java/${name}.java") << """
-            public class $name {
-                @org.junit.Test
-                public void test() {
-                    assert false == ${failing};
-                }
-            }
-        """
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestReportTaskIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestReportTaskIntegrationTest.groovy
new file mode 100644
index 0000000..6e05669
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestReportTaskIntegrationTest.groovy
@@ -0,0 +1,466 @@
+/*
+ * Copyright 2013 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.testing
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.HtmlTestExecutionResult
+import org.gradle.integtests.fixtures.JUnitTestClassExecutionResult
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.UsesSample
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.testing.fixture.JUnitCoverage
+import org.hamcrest.CoreMatchers
+import org.junit.Rule
+import spock.lang.Issue
+
+import static org.gradle.api.internal.DocumentationRegistry.BASE_URL
+import static org.gradle.api.internal.DocumentationRegistry.RECOMMENDATION
+
+import static org.hamcrest.CoreMatchers.allOf
+import static org.hamcrest.CoreMatchers.containsString
+import static org.hamcrest.CoreMatchers.equalTo
+
+class TestReportTaskIntegrationTest extends AbstractIntegrationSpec {
+    @Rule
+    Sample sample = new Sample(temporaryFolder)
+
+    @UsesSample("testing/testReport/groovy")
+    def "can generate report for subprojects"() {
+        given:
+        super.sample(sample)
+
+        when:
+        run "testReport"
+
+        then:
+        def htmlReport = new HtmlTestExecutionResult(sample.dir, "build/reports/allTests")
+        htmlReport.testClass("org.gradle.sample.CoreTest").assertTestCount(1, 0, 0).assertTestPassed("ok").assertStdout(equalTo("hello from CoreTest.\n"))
+        htmlReport.testClass("org.gradle.sample.UtilTest").assertTestCount(1, 0, 0).assertTestPassed("ok").assertStdout(equalTo("hello from UtilTest.\n"))
+    }
+
+    def "merges report with duplicated classes and methods"() {
+        given:
+        buildFile << """
+            $junitSetup
+            def test = tasks.named('test', Test)
+            test.configure {
+                ignoreFailures true
+                useJUnit {
+                    excludeCategories 'org.gradle.testing.SuperClassTests'
+                    excludeCategories 'org.gradle.testing.SubClassTests'
+                }
+            }
+
+            testing {
+                suites {
+                    superTest(JvmTestSuite) {
+                        useJUnit()
+                        sources.java.srcDirs(testing.suites.test.sources.allJava.srcDirs)
+                        targets.all {
+                            testTask.configure {
+                                ignoreFailures true
+                                systemProperty 'category', 'super'
+                                testFramework {
+                                    includeCategories 'org.gradle.testing.SuperClassTests'
+                                }
+                            }
+                        }
+                    }
+                    subTest(JvmTestSuite) {
+                        useJUnit()
+                        sources.java.srcDirs(testing.suites.test.sources.allJava.srcDirs)
+                        targets.all {
+                            testTask.configure {
+                                ignoreFailures true
+                                systemProperty 'category', 'sub'
+                                testFramework {
+                                    includeCategories 'org.gradle.testing.SubClassTests'
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            def testReport = tasks.register('testReport', TestReport) {
+                destinationDirectory = reporting.baseDirectory.dir('allTests')
+                testResults.from([
+                    testing.suites.test.targets.test.testTask,
+                    testing.suites.superTest.targets.superTest.testTask,
+                    testing.suites.subTest.targets.subTest.testTask
+                ])
+            }
+
+            tasks.named('build').configure { it.dependsOn testReport }
+        """.stripIndent()
+
+        and:
+        file("src/test/java/org/gradle/testing/UnitTest.java") << """
+            $packageAndImportsWithCategory
+            public class UnitTest {
+                @Test public void foo() {
+                    System.out.println("org.gradle.testing.UnitTest#foo");
+                }
+            }
+        """.stripIndent()
+        file("src/test/java/org/gradle/testing/SuperTest.java") << """
+            $packageAndImportsWithCategory
+            public class SuperTest {
+                @Category(SuperClassTests.class) @Test public void failing() {
+                    System.out.println("org.gradle.testing.SuperTest#failing");
+                    fail("failing test");
+                }
+                @Category(SuperClassTests.class) @Test public void passing() {
+                    System.out.println("org.gradle.testing.SuperTest#passing");
+                }
+            }
+        """.stripIndent()
+        file("src/test/java/org/gradle/testing/SubTest.java") << """
+            $packageAndImportsWithCategory
+            public class SubTest {
+                @Category(SubClassTests.class) @Test public void onlySub() {
+                    System.out.println("org.gradle.testing.SubTest#onlySub " + System.getProperty("category"));
+                    assertEquals("sub", System.getProperty("category"));
+                }
+                @Category(SubClassTests.class) @Test public void passing() {
+                    System.out.println("org.gradle.testing.SubTest#passing " + System.getProperty("category"));
+                }
+            }
+        """.stripIndent()
+        file("src/test/java/org/gradle/testing/SuperClassTests.java") << """
+            $packageAndImportsWithCategory
+            public class SuperClassTests {
+            }
+        """.stripIndent()
+        file("src/test/java/org/gradle/testing/SubClassTests.java") << """
+            $packageAndImportsWithCategory
+            public class SubClassTests extends SuperClassTests {
+            }
+        """.stripIndent()
+
+        when:
+        run "testReport"
+
+        then:
+        def htmlReport = new HtmlTestExecutionResult(testDirectory, 'build/reports/allTests')
+        htmlReport.testClass("org.gradle.testing.UnitTest").assertTestCount(1, 0, 0).assertTestPassed("foo").assertStdout(equalTo('org.gradle.testing.UnitTest#foo\n'))
+        htmlReport.testClass("org.gradle.testing.SuperTest").assertTestCount(2, 1, 0).assertTestPassed("passing")
+            .assertTestFailed("failing", equalTo('java.lang.AssertionError: failing test'))
+            .assertStdout(allOf(containsString('org.gradle.testing.SuperTest#failing\n'), containsString('org.gradle.testing.SuperTest#passing\n')))
+        htmlReport.testClass("org.gradle.testing.SubTest").assertTestCount(4, 1, 0).assertTestPassed("passing") // onlySub is passing once and failing once
+            .assertStdout(allOf(containsString('org.gradle.testing.SubTest#passing sub\n'),
+                containsString('org.gradle.testing.SubTest#passing super\n'),
+                containsString('org.gradle.testing.SubTest#onlySub sub\n'),
+                containsString('org.gradle.testing.SubTest#onlySub super\n')))
+    }
+
+    def "report includes results of most recent invocation"() {
+        given:
+        buildFile << """
+            $junitSetup
+            test {
+                def logLessStuff = providers.systemProperty('LogLessStuff')
+                systemProperty 'LogLessStuff', logLessStuff.orNull
+            }
+        """.stripIndent()
+
+        and:
+        file("src/test/java/LoggingTest.java") << """
+            import org.junit.Test;
+            public class LoggingTest {
+                @Test
+                public void test() {
+                    if (System.getProperty("LogLessStuff", "false").equals("true")) {
+                        System.out.println("stdout.");
+                        System.err.println("stderr.");
+                    } else {
+                        System.out.println("This is stdout.");
+                        System.err.println("This is stderr.");
+                    }
+                }
+            }
+        """.stripIndent()
+
+        when:
+        run "test"
+
+        then:
+        def result = new HtmlTestExecutionResult(testDirectory)
+        result.testClass("LoggingTest").assertStdout(equalTo("This is stdout.\n"))
+        result.testClass("LoggingTest").assertStderr(equalTo("This is stderr.\n"))
+
+        when:
+        executer.withArguments("-DLogLessStuff=true")
+        run "test"
+
+        then:
+        result.testClass("LoggingTest").assertStdout(equalTo("stdout.\n"))
+        result.testClass("LoggingTest").assertStderr(equalTo("stderr.\n"))
+    }
+
+    // TODO: remove in Gradle 9.0
+    def "nag with deprecation warnings when using legacy TestReport APIs"() {
+        given:
+        buildScript """
+            apply plugin: 'java'
+            $junitSetup
+            tasks.register('otherTests', Test) {
+                binaryResultsDirectory = file("bin")
+                classpath = files('blahClasspath')
+                testClassesDirs = files("blah")
+            }
+            tasks.register('testReport', TestReport) {
+                reportOn test, otherTests
+                destinationDir reporting.file("tr")
+            }
+        """
+
+        and:
+        testClass("Thing")
+
+        when:
+        executer.expectDocumentedDeprecationWarning('The TestReport.reportOn(Object...) method has been deprecated. This is scheduled to be removed in Gradle 9.0. ' +
+            'Please use the testResults method instead. ' +
+            getTestReportLink("testResults"))
+        executer.expectDocumentedDeprecationWarning('The TestReport.destinationDir property has been deprecated. ' +
+            'This is scheduled to be removed in Gradle 9.0. Please use the destinationDirectory property instead. ' +
+            getTestReportLink("destinationDir"))
+        succeeds "testReport"
+
+        then:
+        skipped(":otherTests")
+        executedAndNotSkipped(":test")
+        new HtmlTestExecutionResult(testDirectory, "build/reports/tr").assertTestClassesExecuted("Thing")
+    }
+
+    @Issue("https://issues.gradle.org//browse/GRADLE-2915")
+    def "test report task can handle tests tasks not having been executed"() {
+        when:
+        buildScript """
+            apply plugin: 'java'
+
+            $junitSetup
+
+            tasks.register('testReport', TestReport) {
+                testResults.from(tasks.named('test', Test))
+                destinationDirectory = reporting.baseDirectory.dir("tr")
+            }
+        """
+
+        and:
+        testClass("Thing")
+
+        then:
+        succeeds "testReport"
+
+        // verify incremental behavior on 2nd invocation
+        succeeds "testReport"
+        skipped ":testReport"
+    }
+
+    def "test report task is skipped when there are no results"() {
+        given:
+        buildFile << """
+            ${junitSetup}
+            tasks.register('testReport', TestReport) {
+                testResults.from(tasks.named('test', Test))
+                destinationDirectory = reporting.baseDirectory.dir('tr')
+            }
+        """
+
+        when:
+        succeeds "testReport"
+
+        then:
+        skipped(":test")
+        skipped(":testReport")
+    }
+
+    def "#type report files are considered outputs"() {
+        given:
+        buildScript """
+            $junitSetup
+        """
+
+        and:
+        testClass "SomeTest"
+
+        when:
+        run "test"
+
+        then:
+        executedAndNotSkipped(":test")
+        file(reportsDir).exists()
+
+        when:
+        run "test"
+
+        then:
+        skipped(":test")
+        file(reportsDir).exists()
+
+        when:
+        file(reportsDir).deleteDir()
+        run "test"
+
+        then:
+        executedAndNotSkipped(":test")
+        file(reportsDir).exists()
+
+        where:
+        type   | reportsDir
+        "xml"  | "build/test-results"
+        "html" | "build/reports/tests"
+    }
+
+    def "merge rerun defaults to false"() {
+        when:
+        buildScript """
+            $junitSetup
+        """
+        rerunningTest("SomeTest")
+        fails "test"
+
+        then:
+        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+        def clazz = xmlReport.testClass("SomeTest")
+        clazz.testCount == 6
+        (clazz as JUnitTestClassExecutionResult).testCasesCount == 6
+    }
+
+    def "can enable merge rerun in xml report"() {
+        when:
+        buildScript """
+            $junitSetup
+            test.reports.junitXml.mergeReruns = true
+        """
+        rerunningTest("SomeTest")
+        fails "test"
+
+        then:
+        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+        def clazz = xmlReport.testClass("SomeTest")
+        clazz.testCount == 6
+        (clazz as JUnitTestClassExecutionResult).testCasesCount == 2
+        clazz.assertTestPassed("testFlaky[]")
+        clazz.assertTestFailed("testFailing[]", CoreMatchers.anything())
+    }
+
+    // TODO: remove in Gradle 9.0
+    def "using deprecated testReport elements emits deprecation warnings"() {
+        when:
+        buildScript """
+            apply plugin: 'java'
+            $junitSetup
+            // Need a second test task to reportOn
+            tasks.register('otherTests', Test) {
+                binaryResultsDirectory = file('otherBin')
+                classpath = files('otherClasspath')
+                testClassesDirs = files('otherClasses')
+            }
+            tasks.register('testReport', TestReport) {
+                reportOn test, otherTests
+                destinationDir reporting.file("myTestReports")
+            }
+        """
+
+        then:
+        executer.expectDocumentedDeprecationWarning('The TestReport.reportOn(Object...) method has been deprecated. ' +
+            'This is scheduled to be removed in Gradle 9.0. ' +
+            'Please use the testResults method instead. ' +
+            getTestReportLink("testResults"))
+        executer.expectDocumentedDeprecationWarning('The TestReport.destinationDir property has been deprecated. ' +
+            'This is scheduled to be removed in Gradle 9.0. ' +
+            'Please use the destinationDirectory property instead. ' +
+            getTestReportLink("destinationDir"))
+        succeeds "testReport"
+    }
+
+    protected static String getJunitSetup() {
+        """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'junit:junit:${JUnitCoverage.JUNIT_4_LATEST}'
+            }
+        """.stripIndent()
+    }
+
+    private static String getPackageAndImportsWithCategory() {
+        return """
+            package org.gradle.testing;
+
+            import static org.junit.Assert.*;
+            import org.junit.Test;
+            import org.junit.experimental.categories.Category;
+        """.stripIndent()
+    }
+
+    TestFile rerunningTest(String className) {
+        file("src/test/java/${className}.java") << """
+            import org.junit.Assert;
+            import org.junit.Test;
+
+            @org.junit.runner.RunWith(org.junit.runners.Parameterized.class)
+            public class $className {
+
+                @org.junit.runners.Parameterized.Parameters(name = "")
+                public static Object[] data() {
+                    return new Object[] { 1, 2, 3 };
+                }
+
+                private final int i;
+
+                public SomeTest(int i) {
+                    this.i = i;
+                }
+
+                @Test
+                public void testFlaky() {
+                    System.out.println("execution " + i);
+                    Assert.assertTrue(i == 3);
+                }
+
+                @Test
+                public void testFailing() {
+                    System.out.println("execution " + i);
+                    Assert.assertTrue(false);
+                }
+            }
+        """
+    }
+
+    private getTestReportLink(sectionPart) {
+        String.format(RECOMMENDATION, "information", "${BASE_URL}/dsl/org.gradle.api.tasks.testing.TestReport.html#org.gradle.api.tasks.testing.TestReport:${sectionPart}")
+    }
+
+    private void failingTestClass(String name) {
+        testClass(name, true)
+    }
+
+    private void testClass(String name, boolean failing = false) {
+        file("src/test/java/${name}.java") << """
+            public class $name {
+                @org.junit.Test
+                public void test() {
+                    assert false == ${failing};
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskFailOnNoTestIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskFailOnNoTestIntegrationTest.groovy
new file mode 100644
index 0000000..4b3e414
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskFailOnNoTestIntegrationTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 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.testing
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+import static org.gradle.testing.fixture.JUnitCoverage.getLATEST_JUPITER_VERSION
+
+class TestTaskFailOnNoTestIntegrationTest extends AbstractIntegrationSpec {
+
+    def "test succeeds if a test was executed"() {
+        createBuildFileWithJUnitJupiter()
+
+        file("src/test/java/SomeTest.java") << """
+            public class SomeTest {
+                @org.junit.jupiter.api.Test
+                public void foo() { }
+            }
+        """
+
+        expect:
+        succeeds("test")
+    }
+
+    def "test succeeds with warning if no test was executed"() {
+        createBuildFileWithJUnitJupiter()
+
+        file("src/test/java/NotATest.java") << """
+            public class NotATest {}
+        """
+
+        executer.expectDocumentedDeprecationWarning("No test executed. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. There are test sources present but no test was executed. Please check your test configuration. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_fail_on_no_test_executed")
+
+        expect:
+        succeeds("test")
+    }
+
+    def "test is skipped if no test source detected"() {
+        buildFile << "apply plugin: 'java'"
+
+        file("src/test/java/not_a_test.txt")
+
+        when:
+        succeeds("test")
+
+        then:
+        skipped(":test")
+    }
+
+    def createBuildFileWithJUnitJupiter() {
+        buildFile << """
+            plugins {
+                id 'java'
+                id 'jvm-test-suite'
+            }
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'org.junit.jupiter:junit-jupiter:${LATEST_JUPITER_VERSION}'
+            }
+            testing.suites.test {
+                useJUnitJupiter()
+            }
+        """.stripIndent()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskIntegrationTest.groovy
deleted file mode 100644
index 9fb69a8..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskIntegrationTest.groovy
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright 2013 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.testing
-
-import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.testing.fixture.JUnitCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class TestTaskIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-
-    @Issue("GRADLE-2702")
-    @ToBeFixedForConfigurationCache(because = "early dependency resolution")
-    def "should not resolve configuration results when there are no tests"() {
-        buildFile << """
-            apply plugin: 'java'
-
-            configurations.all { incoming.beforeResolve { throw new RuntimeException() } }
-        """
-
-        when:
-        run("build")
-
-        then:
-        noExceptionThrown()
-    }
-
-    def "test task is skipped when there are no tests"() {
-        buildFile << "apply plugin: 'java'"
-        file("src/test/java/not_a_test.txt")
-
-        when:
-        run("build")
-
-        then:
-        result.assertTaskSkipped(":test")
-    }
-
-    @Requires(TestPrecondition.JDK9_OR_LATER)
-    def "compiles and executes a Java 9 test suite"() {
-        given:
-        buildFile << java9Build()
-
-        file('src/test/java/MyTest.java') << standaloneTestClass()
-
-        when:
-        succeeds 'test'
-
-        then:
-        noExceptionThrown()
-
-        and:
-        classFormat(classFile('java', 'test', 'MyTest.class')) == 53
-
-    }
-
-    @Requires(TestPrecondition.JDK9_OR_LATER)
-    def "compiles and executes a Java 9 test suite even if a module descriptor is on classpath"() {
-        given:
-        buildFile << java9Build()
-
-        file('src/test/java/MyTest.java') << standaloneTestClass()
-        file('src/main/java/com/acme/Foo.java') << '''package com.acme;
-            public class Foo {}
-        '''
-        file('src/main/java/com/acme/module-info.java') << '''module com.acme {
-            exports com.acme;
-        }'''
-
-        when:
-        succeeds 'test'
-
-        then:
-        noExceptionThrown()
-
-        and:
-        classFormat(javaClassFile('module-info.class')) == 53
-        classFormat(classFile('java', 'test', 'MyTest.class')) == 53
-    }
-
-    def "test task does not hang if maxParallelForks is greater than max-workers (#maxWorkers)"() {
-        given:
-        def maxParallelForks = maxWorkers + 1
-
-        and:
-        2000.times { num ->
-            file("src/test/java/SomeTest${num}.java") << testClass("SomeTest${num}")
-        }
-
-        and:
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:4.13' }
-            test {
-                maxParallelForks = $maxParallelForks
-            }
-        """.stripIndent()
-
-        when:
-        executer.withArguments("--max-workers=${maxWorkers}", "-i")
-        succeeds 'test'
-
-        then:
-        output.contains("test.maxParallelForks ($maxParallelForks) is larger than max-workers ($maxWorkers), forcing it to $maxWorkers")
-
-        where:
-        maxWorkers                                | _
-        Runtime.runtime.availableProcessors()     | _
-        Runtime.runtime.availableProcessors() - 1 | _
-        Runtime.runtime.availableProcessors() + 1 | _
-    }
-
-    @Requires(TestPrecondition.ONLINE)
-    def "re-runs tests when resources are renamed in a jar"() {
-        buildFile << """
-            allprojects {
-                apply plugin: 'java'
-                ${mavenCentralRepository()}
-            }
-            dependencies {
-                testImplementation 'junit:junit:4.13'
-                testImplementation project(":dependency")
-            }
-        """
-        settingsFile << """
-            include 'dependency'
-        """
-        file("src/test/java/MyTest.java") << """
-            import org.junit.*;
-
-            public class MyTest {
-               @Test
-               public void test() {
-                  Assert.assertNotNull(getClass().getResource("dependency/foo.properties"));
-               }
-            }
-        """.stripIndent()
-
-        def resourceFile = file("dependency/src/main/resources/dependency/foo.properties")
-        resourceFile << """
-            someProperty = true
-        """
-
-        when:
-        succeeds 'test'
-        then:
-        noExceptionThrown()
-
-        when:
-        resourceFile.renameTo(file("dependency/src/main/resources/dependency/bar.properties"))
-        then:
-        fails 'test'
-    }
-
-    @Requires(TestPrecondition.ONLINE)
-    def "re-runs tests when resources are renamed"() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-
-            dependencies {
-                testImplementation 'junit:junit:4.13'
-            }
-        """
-        file("src/test/java/MyTest.java") << """
-            import org.junit.*;
-
-            public class MyTest {
-               @Test
-               public void test() {
-                  Assert.assertNotNull(getClass().getResource("dependency/foo.properties"));
-               }
-            }
-        """.stripIndent()
-
-        def resourceFile = file("src/main/resources/dependency/foo.properties")
-        resourceFile << """
-            someProperty = true
-        """
-
-        when:
-        succeeds 'test'
-        then:
-        noExceptionThrown()
-
-        when:
-        resourceFile.renameTo(file("src/main/resources/dependency/bar.properties"))
-        then:
-        fails 'test'
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/3627")
-    def "can reference properties from TestTaskReports when using @CompileStatic"() {
-        buildFile << """
-            import groovy.transform.CompileStatic
-
-            @CompileStatic
-            class StaticallyCompiledPlugin implements Plugin<Project> {
-                @Override
-                void apply(Project project) {
-                    project.apply plugin: 'java'
-                    Test test = (Test) project.tasks.getByName("test")
-                    if (test.reports.junitXml.outputLocation.asFile.get().exists()) {
-                        println 'JUnit XML report exists!'
-                    }
-                }
-            }
-
-            apply plugin: StaticallyCompiledPlugin
-        """
-
-        expect:
-        succeeds("help")
-    }
-
-    def "reports failure of TestExecuter regardless of filters"() {
-        given:
-        file('src/test/java/MyTest.java') << standaloneTestClass()
-        buildFile << """
-            import org.gradle.api.internal.tasks.testing.*
-
-            apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'junit:junit:${JUnitCoverage.NEWEST}'
-            }
-
-            test {
-                doFirst {
-                    testExecuter = new TestExecuter<JvmTestExecutionSpec>() {
-                        @Override
-                        void execute(JvmTestExecutionSpec testExecutionSpec, TestResultProcessor resultProcessor) {
-                            DefaultTestSuiteDescriptor suite = new DefaultTestSuiteDescriptor(testExecutionSpec.path, testExecutionSpec.path)
-                            resultProcessor.started(suite, new TestStartEvent(System.currentTimeMillis()))
-                            try {
-                                throw new RuntimeException("boom!")
-                            } finally {
-                                resultProcessor.completed(suite.getId(), new TestCompleteEvent(System.currentTimeMillis()))
-                            }
-                        }
-
-                        @Override
-                        void stopNow() {
-                            // do nothing
-                        }
-                    }
-                }
-            }
-        """
-
-        when:
-        fails("test", *extraArgs)
-
-        then:
-        result.assertHasCause('boom!')
-
-        where:
-        extraArgs << [[], ["--tests", "MyTest"]]
-    }
-
-    def "test framework can be set to the same value (#frameworkName) twice"() {
-        ignoreWhenJUnitPlatform()
-
-        given:
-        file('src/test/java/MyTest.java') << (frameworkName == "JUnit" ? standaloneTestClass() : junitJupiterStandaloneTestClass())
-
-        settingsFile << "rootProject.name = 'Sample'"
-        buildFile << """apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation $frameworkDeps
-            }
-
-            test {
-                $useMethod
-                $useMethod
-            }
-        """.stripIndent()
-
-        expect:
-        succeeds("test")
-
-        where:
-        frameworkName       | useMethod                 | frameworkDeps
-        "JUnit"             | "useJUnit()"              | "'junit:junit:${JUnitCoverage.NEWEST}'"
-        "JUnit Platform"    | "useJUnitPlatform()"      | "'org.junit.jupiter:junit-jupiter:${JUnitCoverage.LATEST_JUPITER_VERSION}'"
-    }
-
-    def "options can be set prior to setting same test framework for the default test task"() {
-        ignoreWhenJUnitPlatform()
-
-        given:
-        file('src/test/java/MyTest.java') << standaloneTestClass()
-        file("src/test/java/Slow.java") << """public interface Slow {}"""
-
-        settingsFile << "rootProject.name = 'Sample'"
-        buildFile << """apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'junit:junit:${JUnitCoverage.NEWEST}'
-            }
-
-            test {
-                options {
-                    excludeCategories = ["Slow"]
-                }
-                useJUnit()
-            }
-        """.stripIndent()
-
-        expect:
-        succeeds("test")
-    }
-
-    def "options cannot be set prior to changing test framework for the default test task"() {
-        ignoreWhenJUnitPlatform()
-
-        given:
-        file('src/test/java/MyTest.java') << junitJupiterStandaloneTestClass()
-
-        settingsFile << "rootProject.name = 'Sample'"
-        buildFile << """apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:${JUnitCoverage.LATEST_JUPITER_VERSION}'
-            }
-
-            test {
-                options {
-                    excludeCategories = ["Slow"]
-                }
-            }
-
-            test {
-                useJUnitPlatform()
-            }
-        """.stripIndent()
-
-        expect:
-        fails("test")
-        failure.assertHasCause("The value for task ':test' property 'testFrameworkProperty' is final and cannot be changed any further.")
-    }
-
-    def "options can be set prior to setting same test framework for a custom test task"() {
-        ignoreWhenJUnitPlatform()
-
-        given:
-        file('src/test/java/MyTest.java') << standaloneTestClass()
-        file("src/test/java/Slow.java") << """public interface Slow {}"""
-
-        settingsFile << "rootProject.name = 'Sample'"
-        buildFile << """apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-
-            sourceSets {
-                customTest
-            }
-
-            dependencies {
-                customTestImplementation 'junit:junit:${JUnitCoverage.NEWEST}'
-            }
-
-            tasks.create('customTest', Test) {
-                classpath = sourceSets.customTest.runtimeClasspath
-                testClassesDirs = sourceSets.customTest.output.classesDirs
-                options {
-                    excludeCategories = ["Slow"]
-                }
-                useJUnit()
-            }
-        """.stripIndent()
-
-        expect:
-        succeeds("customTest")
-    }
-
-    def "options cannot be set prior to changing test framework for a custom test task"() {
-        ignoreWhenJUnitPlatform()
-
-        given:
-        file('src/test/java/MyTest.java') << junitJupiterStandaloneTestClass()
-
-        settingsFile << "rootProject.name = 'Sample'"
-        buildFile << """apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:${JUnitCoverage.LATEST_JUPITER_VERSION}'
-            }
-
-            tasks.create('customTest', Test) {
-                options {
-                    excludeCategories = ["Slow"]
-                }
-                useJUnitPlatform()
-            }
-        """.stripIndent()
-
-        expect:
-        fails("customTest")
-        failure.assertHasCause("The value for this property is final and cannot be changed any further.")
-    }
-
-    def "options configured after setting test framework works"() {
-        given:
-        file('src/test/java/MyTest.java') << junitJupiterStandaloneTestClass()
-
-        settingsFile << "rootProject.name = 'Sample'"
-        buildFile << """
-            import org.gradle.api.internal.tasks.testing.*
-
-            apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:${JUnitCoverage.LATEST_JUPITER_VERSION}'
-            }
-
-            test {
-                useJUnitPlatform()
-                options {
-                    excludeTags = ["Slow"]
-                }
-            }
-
-            tasks.register('verifyTestOptions') {
-                doLast {
-                    assert tasks.getByName("test").getOptions().getExcludeTags().contains("Slow")
-                }
-            }
-        """.stripIndent()
-
-        expect:
-        succeeds("test", "verifyTestOptions", "--warn")
-    }
-
-    def "setForkEvery null emits deprecation warning"() {
-        given:
-        buildScript """
-            plugins {
-                id 'java'
-            }
-            tasks.withType(Test).configureEach {
-                forkEvery = null
-            }
-        """
-
-        when:
-        executer.expectDocumentedDeprecationWarning("Setting Test.forkEvery to null. This behavior has been deprecated. This will fail with an error in Gradle 9.0. Set Test.forkEvery to 0 instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:forkEvery for more details.")
-
-        then:
-        succeeds "test", "--dry-run"
-    }
-
-    def "setForkEvery Long emits deprecation warning"() {
-        given:
-        buildScript """
-            plugins {
-                id 'java'
-            }
-            tasks.withType(Test).configureEach {
-                setForkEvery(Long.valueOf(1))
-            }
-        """
-
-        when:
-        executer.expectDocumentedDeprecationWarning("The Test.setForkEvery(Long) method has been deprecated. This is scheduled to be removed in Gradle 9.0. Please use the Test.setForkEvery(long) method instead. See https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:forkEvery for more details.")
-
-        then:
-        succeeds "test", "--dry-run"
-    }
-
-    private static String standaloneTestClass() {
-        return testClass('MyTest')
-    }
-
-    private static String testClass(String className) {
-        return """
-            import org.junit.*;
-
-            public class $className {
-               @Test
-               public void test() {
-                  System.out.println(System.getProperty("java.version"));
-                  Assert.assertEquals(1,1);
-               }
-            }
-        """.stripIndent()
-    }
-
-    private static String junitJupiterStandaloneTestClass() {
-        return junitJupiterTestClass('MyTest')
-    }
-
-    private static String junitJupiterTestClass(String className) {
-        return """
-            import org.junit.jupiter.api.*;
-
-            public class $className {
-               @Test
-               public void test() {
-                  System.out.println(System.getProperty("java.version"));
-                  Assertions.assertEquals(1,1);
-               }
-            }
-        """.stripIndent()
-    }
-
-    private static String java9Build() {
-        """
-            apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-
-            dependencies {
-                testImplementation 'junit:junit:4.13'
-            }
-
-            sourceCompatibility = 1.9
-            targetCompatibility = 1.9
-        """
-    }
-
-    private static int classFormat(TestFile path) {
-        path.bytes[7] & 0xFF
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskJdkRelocationIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskJdkRelocationIntegrationTest.groovy
index cb5d00e..272e92f 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskJdkRelocationIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskJdkRelocationIntegrationTest.groovy
@@ -20,12 +20,13 @@
 import org.gradle.integtests.fixtures.AbstractTaskRelocationIntegrationTest
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.gradle.util.internal.TextUtil
 
 import static org.gradle.util.internal.TextUtil.normaliseLineSeparators
 
-@Requires(adhoc = { AvailableJavaHomes.getAvailableJdks(JavaVersion.VERSION_1_8).size() > 1 })
+@Requires(IntegTestPreconditions.MoreThanOneJava8HomeAvailable)
 class TestTaskJdkRelocationIntegrationTest extends AbstractTaskRelocationIntegrationTest {
 
     private File getOriginalJavaExecutable() {
@@ -68,8 +69,10 @@
                 testImplementation "junit:junit:4.13"
             }
 
-            sourceCompatibility = "1.7"
-            targetCompatibility = "1.7"
+            java {
+                sourceCompatibility = "1.7"
+                targetCompatibility = "1.7"
+            }
 
             test {
                 executable "${TextUtil.escapeString(originalJavaExecutable)}"
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskJvmArgsProviderIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskJvmArgsProviderIntegrationTest.groovy
index 0428da4..a02a561 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskJvmArgsProviderIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskJvmArgsProviderIntegrationTest.groovy
@@ -18,6 +18,8 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 
+import static org.gradle.integtests.fixtures.SuggestionsMessages.SCAN
+
 class TestTaskJvmArgsProviderIntegrationTest extends AbstractIntegrationSpec {
 
     def "jvm argument providers are passed to the test worker"() {
@@ -37,13 +39,13 @@
             }
         """
 
-        file("build.gradle") << """
+        buildFile << """
             apply plugin: "java"
 
             ${mavenCentralRepository()}
 
             dependencies {
-                testImplementation "junit:junit:4.13"
+                testImplementation "$testJunitCoordinates"
             }
 
             class MyTestSystemProperties implements CommandLineArgumentProvider {
@@ -81,5 +83,6 @@
         fails "test", "-PinputFile=different-file.txt"
         failure.assertHasDescription("Execution failed for task ':test'.")
         failure.assertHasCause("There were failing tests.")
+        failure.assertHasResolutions(SCAN)
     }
 }
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskLocaleIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskLocaleIntegrationTest.groovy
new file mode 100644
index 0000000..2311003
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskLocaleIntegrationTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 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.testing
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
+
+class TestTaskLocaleIntegrationTest extends AbstractIntegrationSpec {
+    @Issue("https://github.com/gradle/gradle/issues/2661")
+    def "test logging can be configured on turkish locale"() {
+        given:
+        buildFile << """
+            apply plugin:'java'
+            test {
+                testLogging {
+                    events "passed", "skipped", "failed"
+                }
+            }
+        """.stripIndent()
+
+        when:
+        executer
+            .requireDaemon()
+            .requireIsolatedDaemons()
+            .withBuildJvmOpts("-Duser.language=tr", "-Duser.country=TR")
+            .withTasks("help")
+            .run()
+
+        then:
+        noExceptionThrown()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskRelocationIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskRelocationIntegrationTest.groovy
index 938cf3f..503e7f2 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskRelocationIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskRelocationIntegrationTest.groovy
@@ -48,7 +48,7 @@
             ${mavenCentralRepository()}
 
             dependencies {
-                testImplementation "junit:junit:4.13"
+                testImplementation "$testJunitCoordinates"
             }
 
             sourceSets.test.java.destinationDirectory.set(file("build/classes/test"))
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskToolchainIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskToolchainIntegrationTest.groovy
index dfa504a..5083457 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskToolchainIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestTaskToolchainIntegrationTest.groovy
@@ -150,6 +150,31 @@
         "assigned tool" | "when configured"                    | "other"  | null           | "other"
     }
 
+    def "emits deprecation warning if executable specified as relative path"() {
+        given:
+        def executable = TextUtil.normaliseFileSeparators(Jvm.current().javaExecutable.toString())
+
+        buildFile << """
+            apply plugin:'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'junit:junit:4.13'
+            }
+            test {
+                executable = new File(".").getAbsoluteFile().toPath().relativize(new File("${executable}").toPath()).toString()
+            }
+        """
+        when:
+        executer.expectDocumentedDeprecationWarning("Configuring a Java executable via a relative path. " +
+            "This behavior has been deprecated. This will fail with an error in Gradle 9.0. " +
+            "Resolving relative file paths might yield unexpected results, there is no single clear location it would make sense to resolve against. " +
+            "Configure an absolute path to a Java executable instead. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#no_relative_paths_for_java_executables")
+
+        then:
+        succeeds("test")
+    }
+
     private TestFile configureProjectWithJavaPlugin(JavaVersion compileJavaVersion) {
         buildFile << """
             apply plugin: "java"
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestingIntegrationTest.groovy
deleted file mode 100644
index 9317b74..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/TestingIntegrationTest.groovy
+++ /dev/null
@@ -1,616 +0,0 @@
-/*
- * Copyright 2011 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.testing
-
-import org.apache.commons.lang.RandomStringUtils
-import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.integtests.fixtures.jvm.JavaToolchainFixture
-import org.gradle.internal.jvm.Jvm
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.gradle.util.internal.TextUtil
-import org.hamcrest.CoreMatchers
-import spock.lang.IgnoreIf
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.getJUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.getJUNIT_VINTAGE_JUPITER
-import static org.gradle.testing.fixture.JUnitCoverage.getNEWEST
-import static org.hamcrest.CoreMatchers.equalTo
-/**
- * General tests for the JVM testing infrastructure that don't deserve their own test class.
- */
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class TestingIntegrationTest extends JUnitMultiVersionIntegrationSpec implements JavaToolchainFixture {
-
-    @Issue("https://issues.gradle.org/browse/GRADLE-1948")
-    def "test interrupting its own thread does not kill test execution"() {
-        given:
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation "junit:junit:4.13" }
-        """
-
-        and:
-        file("src/test/java/SomeTest.java") << """
-            import org.junit.*;
-
-            public class SomeTest {
-                @Test public void foo() {
-                    Thread.currentThread().interrupt();
-                }
-            }
-        """
-
-        when:
-        run "test"
-
-        then:
-        executedAndNotSkipped(":test")
-    }
-
-    def "fails cleanly even if an exception is thrown that doesn't serialize cleanly"() {
-        given:
-        file('src/test/java/ExceptionTest.java') << """
-            import org.junit.*;
-            import java.io.*;
-
-            public class ExceptionTest {
-
-                static class BadlyBehavedException extends Exception {
-                    BadlyBehavedException() {
-                        super("Broken writeObject()");
-                    }
-
-                    private void writeObject(ObjectOutputStream os) throws IOException {
-                        throw new IOException("Failed strangely");
-                    }
-                }
-
-                @Test
-                public void testThrow() throws Throwable {
-                    throw new BadlyBehavedException();
-                }
-            }
-        """
-        file('build.gradle') << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:4.13' }
-        """
-
-        when:
-        runAndFail "test"
-
-        then:
-        failureHasCause "There were failing tests"
-
-        and:
-        def results = new DefaultTestExecutionResult(file("."))
-        results.assertTestClassesExecuted("ExceptionTest")
-        results.testClass("ExceptionTest").assertTestFailed("testThrow", equalTo('ExceptionTest$BadlyBehavedException: Broken writeObject()'))
-    }
-
-    def "fails cleanly even if an exception is thrown that doesn't de-serialize cleanly"() {
-        given:
-
-        file('src/test/java/ExceptionTest.java') << """
-            import org.junit.*;
-            import java.io.*;
-
-            public class ExceptionTest {
-                static class BadlyBehavedException extends Exception {
-                    BadlyBehavedException() {
-                        super("Broken readObject()");
-                    }
-
-                    private void readObject(ObjectInputStream os) throws IOException {
-                        throw new IOException("Failed strangely");
-                    }
-                }
-
-                @Test
-                public void testThrow() throws Throwable {
-                    throw new BadlyBehavedException();
-                }
-            }
-        """
-        file('build.gradle') << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'junit:junit:4.13'
-            }
-        """
-
-        when:
-        // an exception was thrown so we should fail here
-        runAndFail "test"
-
-        then:
-        failureHasCause "There were failing tests"
-
-        and:
-        def results = new DefaultTestExecutionResult(file("."))
-        results.assertTestClassesExecuted("ExceptionTest")
-        results.testClass("ExceptionTest").assertTestFailed("testThrow", equalTo('ExceptionTest$BadlyBehavedException: Broken readObject()'))
-    }
-
-    @Requires(TestPrecondition.NOT_WINDOWS)
-    def "can use long paths for working directory"() {
-        given:
-        // windows can handle a path up to 260 characters
-        // we create a path that is 260 +1 (offset + "/" + randompath)
-        def pathoffset = 260 - testDirectory.getAbsolutePath().length()
-        def alphanumeric = RandomStringUtils.randomAlphanumeric(pathoffset)
-        def testWorkingDir = testDirectory.createDir("$alphanumeric")
-
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation "junit:junit:4.13" }
-            test.workingDir = "${testWorkingDir.toURI()}"
-        """
-
-        and:
-        file("src/test/java/SomeTest.java") << """
-            import org.junit.*;
-
-            public class SomeTest {
-                @Test public void foo() {
-                    System.out.println(new java.io.File(".").getAbsolutePath());
-                }
-            }
-        """
-
-        expect:
-        succeeds "test"
-    }
-
-    @Issue("https://issues.gradle.org/browse/GRADLE-2313")
-    def "can clean test after extracting class file with #framework"() {
-        when:
-        ignoreWhenJUnitPlatform()
-        buildFile << """
-            apply plugin: "java"
-            ${mavenCentralRepository()}
-            dependencies { testImplementation "$dependency" }
-            test { $framework() }
-        """
-        and:
-        file("src/test/java/SomeTest.java") << """
-            public class SomeTest extends $superClass {
-            }
-        """
-        then:
-        succeeds "clean", "test"
-
-        and:
-        file("build/tmp/test").exists() // ensure we extracted classes
-
-        where:
-        framework   | dependency                | superClass
-        "useJUnit"  | "junit:junit:4.13"        | "org.junit.runner.Result"
-        "useTestNG" | "org.testng:testng:6.3.1" | "org.testng.Converter"
-    }
-
-    @Issue("https://issues.gradle.org/browse/GRADLE-2527")
-    def "test class detection works for custom test tasks"() {
-        given:
-        ignoreWhenJupiter()
-        buildFile << """
-                apply plugin:'java'
-                ${mavenCentralRepository()}
-
-                sourceSets {
-	                othertests {
-		                java.srcDir file('src/othertests/java')
-	                    resources.srcDir file('src/othertests/resources')
-	                }
-                }
-
-                dependencies{
-	                othertestsImplementation "junit:junit:4.13"
-                }
-
-                task othertestsTest(type:Test){
-	                useJUnit()
-	                classpath = sourceSets.othertests.runtimeClasspath
-	                testClassesDirs = sourceSets.othertests.output.classesDirs
-	            }
-            """
-
-        and:
-        file("src/othertests/java/AbstractTestClass.java") << """
-                import junit.framework.TestCase;
-                public abstract class AbstractTestClass extends TestCase {
-                }
-            """
-
-        file("src/othertests/java/TestCaseExtendsAbstractClass.java") << """
-                import junit.framework.Assert;
-                public class TestCaseExtendsAbstractClass extends AbstractTestClass{
-                    public void testTrue() {
-                        Assert.assertTrue(true);
-                    }
-                }
-            """
-
-        when:
-        run "othertestsTest"
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory, 'build', '', '', 'othertestsTest')
-        result.assertTestClassesExecuted("TestCaseExtendsAbstractClass")
-    }
-
-    @Issue("https://issues.gradle.org/browse/GRADLE-2962")
-    def "incompatible user versions of classes that we also use don't affect test execution"() {
-
-        // These dependencies are quite particular.
-        // Both jars contain 'com.google.common.collect.ImmutableCollection'
-        // 'google-collections' contains '$EmptyImmutableCollection' which extends '$AbstractImmutableCollection' which is also in guava 15.
-        // In the google-collections version '$EmptyImmutableCollection' overrides `toArray()`.
-        // In guava 15, this method is final.
-        // This causes a verifier error when loading $EmptyImmutableCollection (can't override final method).
-
-        // Our test infrastructure loads org.gradle.util.SystemProperties, which depends on $EmptyImmutableCollection from guava 14.
-        // The below test is testing that out infrastructure doesn't throw a VerifyError while bootstrapping.
-        // This is testing classloader isolation, but this was not the real problem that triggered GRADLE-2962.
-        // The problem was that we tried to load the user's $EmptyImmutableCollection in a class loader structure we wouldn't have used anyway,
-        // but this caused the infrastructure to fail with an internal error because of the VerifyError.
-        // In a nutshell, this tests that we don't even try to load classes that are there, but that we shouldn't see.
-
-        when:
-        executer
-            .withArgument("-Porg.gradle.java.installations.paths=${AvailableJavaHomes.getAvailableJvms().collect { it.javaHome.absolutePath }.join(",")}")
-            .withToolchainDetectionEnabled()
-        buildScript """
-            plugins {
-                id("java")
-            }
-            ${javaPluginToolchainVersion(11)}
-            ${mavenCentralRepository()}
-            configurations { first {}; last {} }
-            dependencies {
-                // guarantee ordering
-                first 'com.google.guava:guava:15.0'
-                last 'com.google.collections:google-collections:1.0'
-                implementation configurations.first + configurations.last
-
-                testImplementation 'junit:junit:4.13'
-            }
-        """
-
-        and:
-        file("src/test/java/TestCase.java") << """
-            import org.junit.Test;
-            public class TestCase {
-                @Test
-                public void test() throws Exception {
-                    getClass().getClassLoader().loadClass("com.google.common.collect.ImmutableCollection\$EmptyImmutableCollection");
-                }
-            }
-        """
-
-        then:
-        fails "test"
-
-        and:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass("TestCase").with {
-            assertTestFailed("test", CoreMatchers.containsString("java.lang.VerifyError"))
-            assertTestFailed("test", CoreMatchers.containsString("\$EmptyImmutableCollection"))
-        }
-    }
-
-    @Issue("https://issues.gradle.org/browse/GRADLE-3157")
-    def "test class detection works when '-parameters' compiler option is used (JEP 118)"() {
-        when:
-        buildScript """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'junit:junit:4.13'
-            }
-            tasks.withType(JavaCompile) {
-                options.with {
-                    compilerArgs << '-parameters'
-                }
-            }
-        """
-
-        and:
-        file("src/test/java/TestHelper.java") << """
-            public class TestHelper {
-                public void helperMethod(String foo, int bar) {
-                    // this method shouldn't cause failure due to API version check
-                    // in org.objectweb.asm.MethodVisitor#visitParameter
-                }
-            }
-        """
-
-        and:
-        file("src/test/java/TestCase.java") << """
-            import org.junit.Test;
-            import static org.junit.Assert.assertTrue;
-            public class TestCase {
-                @Test
-                public void test() {
-                    assertTrue(Double.parseDouble(System.getProperty("java.specification.version")) >= 1.8);
-                }
-            }
-        """
-
-        then:
-        run "test"
-
-        and:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass("TestCase").with {
-            assertTestCount(1, 0, 0)
-        }
-    }
-
-    def "tests are re-executed when set of candidate classes change"() {
-        given:
-        buildFile << """
-            apply plugin:'java'
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'junit:junit:4.13'
-            }
-            test {
-                testLogging {
-                    events "passed", "skipped", "failed"
-                }
-            }
-        """
-
-        and:
-        file("src/test/java/FirstTest.java") << """
-            import org.junit.*;
-            public class FirstTest {
-                @Test public void test() {}
-            }
-        """
-
-        file("src/test/java/SecondTest.java") << """
-            import org.junit.*;
-            public class SecondTest {
-                @Test public void test() {}
-            }
-        """
-
-        when:
-        run "test"
-        then:
-        executedAndNotSkipped ":test"
-        output.contains("FirstTest > test PASSED")
-        output.contains("SecondTest > test PASSED")
-
-        when:
-        run "test"
-        then:
-        skipped ":test"
-
-        when:
-        buildFile << """
-        test {
-            filter {
-                includeTestsMatching "First*"
-            }
-        }
-        """
-        then:
-        run "test"
-        then:
-        executedAndNotSkipped ":test"
-        output.contains("FirstTest > test PASSED")
-        !output.contains("SecondTest > test PASSED")
-    }
-
-    def "emits deprecation warning if executable specified as relative path"() {
-        given:
-        def executable = TextUtil.normaliseFileSeparators(Jvm.current().javaExecutable.toString())
-
-        buildFile << """
-            apply plugin:'java'
-            test {
-                executable = new File(".").getAbsoluteFile().toPath().relativize(new File("${executable}").toPath()).toString()
-            }
-        """
-        when:
-        executer.expectDeprecationWarning("Configuring a Java executable via a relative path. " +
-                "This behavior has been deprecated. This will fail with an error in Gradle 9.0. " +
-                "Resolving relative file paths might yield unexpected results, there is no single clear location it would make sense to resolve against. " +
-                "Configure an absolute path to a Java executable instead.")
-
-        then:
-        succeeds("test")
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/2661")
-    def "test logging can be configured on turkish locale"() {
-        given:
-        buildFile << """
-            apply plugin:'java'
-            test {
-                testLogging {
-                    events "passed", "skipped", "failed"
-                }
-            }
-        """
-        when:
-        executer
-            .requireDaemon()
-            .requireIsolatedDaemons()
-            .withBuildJvmOpts("-Duser.language=tr", "-Duser.country=TR")
-            .withTasks("help")
-            .run()
-
-        then:
-        noExceptionThrown()
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/5305")
-    def "test can install an irreplaceable SecurityManager"() {
-        given:
-        executer
-            .withStackTraceChecksDisabled()
-            .withToolchainDetectionEnabled()
-        withInstallations(AvailableJavaHomes.getAvailableJvms())
-        buildFile << """
-            plugins {
-                id("java")
-            }
-            ${javaPluginToolchainVersion(11)}
-            ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:4.13' }
-        """
-
-        and:
-        file('src/test/java/SecurityManagerInstallationTest.java') << """
-            import org.junit.Test;
-            import java.security.Permission;
-
-            public class SecurityManagerInstallationTest {
-                @Test
-                public void testSecurityManagerCleanExit() {
-                    System.setSecurityManager(new SecurityManager() {
-                        @Override
-                        public void checkPermission(Permission perm) {
-                            if ("setSecurityManager".equals(perm.getName())) {
-                                throw new SecurityException("You cannot replace this security manager!");
-                            }
-                        }
-                    });
-                }
-            }
-        """
-
-        when:
-        succeeds "test"
-
-        then:
-        outputContains "Unable to reset SecurityManager"
-    }
-
-    @IgnoreIf({ GradleContextualExecuter.embedded })
-    @Requires(TestPrecondition.JDK14_OR_LATER)
-    def "useful NPE messages are transported to the daemon"() {
-        buildFile << """
-            apply plugin:'java-library'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:4.13' }
-
-            test {
-                jvmArgs("-XX:+ShowCodeDetailsInExceptionMessages")
-            }
-        """
-
-        file('src/test/java/UsefulNPETest.java') << """
-            import org.junit.Test;
-
-            public class UsefulNPETest {
-                @Test
-                public void testUsefulNPE() {
-                    Object o = null;
-                    o.toString();
-                }
-
-                @Test
-                public void testDeepUsefulNPE() {
-                    other(null);
-                }
-
-                @Test
-                public void testFailingGetMessage() {
-                    throw new NullPointerException() {
-                        public String getMessage() {
-                            throw new RuntimeException();
-                        }
-                    };
-                }
-
-                void other(Object param) {
-                    try {
-                       System.out.println(param.toString());
-                    } catch (NullPointerException ex) {
-                        throw new RuntimeException(ex);
-                    }
-                }
-
-            }
-        """
-
-        when:
-        fails 'test'
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass("UsefulNPETest")
-            .testFailed("testUsefulNPE", equalTo('java.lang.NullPointerException: Cannot invoke "Object.toString()" because "o" is null'))
-        result.testClass("UsefulNPETest")
-            .testFailed("testDeepUsefulNPE", equalTo('java.lang.RuntimeException: java.lang.NullPointerException: Cannot invoke "Object.toString()" because "param" is null'))
-        result.testClass("UsefulNPETest")
-            .testFailed("testFailingGetMessage", equalTo('Could not determine failure message for exception of type UsefulNPETest$1: java.lang.RuntimeException'))
-    }
-
-    def "test thread name is reset after test execution"() {
-        when:
-        ignoreWhenJUnitPlatform()
-        buildFile << """
-            apply plugin: "java"
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation "junit:junit:${NEWEST}"
-            }
-            test { useJUnit() }
-        """
-
-        and:
-        file("src/test/java/SomeTest.java") << threadNameCheckTest("SomeTest")
-        file("src/test/java/AnotherTest.java") << threadNameCheckTest("AnotherTest")
-
-        then:
-        succeeds "clean", "test"
-
-        and:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass("SomeTest").assertTestPassed("checkThreadName")
-        result.testClass("AnotherTest").assertTestPassed("checkThreadName")
-    }
-
-    private static String threadNameCheckTest(String className) {
-        return """
-            import org.junit.Test;
-            import static org.junit.Assert.assertEquals;
-
-            public class ${className} {
-                @Test
-                public void checkThreadName() {
-                    assertEquals("Test worker", Thread.currentThread().getName());
-                    Thread.currentThread().setName(getClass().getSimpleName());
-                }
-            }
-        """
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractJvmFailFastIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractJvmFailFastIntegrationSpec.groovy
deleted file mode 100644
index 2fc4186..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractJvmFailFastIntegrationSpec.groovy
+++ /dev/null
@@ -1,185 +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.testing.fixture
-
-import org.gradle.api.logging.configuration.ConsoleOutput
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.RichConsoleStyling
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.test.fixtures.ConcurrentTestUtil
-import org.gradle.test.fixtures.server.http.BlockingHttpServer
-import org.hamcrest.CoreMatchers
-import org.junit.Rule
-import spock.lang.IgnoreIf
-
-import static org.gradle.testing.fixture.JvmBlockingTestClassGenerator.*
-
-abstract class AbstractJvmFailFastIntegrationSpec extends AbstractIntegrationSpec {
-    @Rule
-    BlockingHttpServer server = new BlockingHttpServer()
-    JvmBlockingTestClassGenerator generator
-
-    def setup() {
-        server.start()
-        generator = new JvmBlockingTestClassGenerator(testDirectory, server, testAnnotationClass(), testDependency(), testFrameworkConfiguration())
-    }
-
-    def "all tests run with #description"() {
-        given:
-        buildFile.text = generator.initBuildFile()
-        buildFile << buildConfig
-        generator.withFailingTest()
-        generator.withNonfailingTest()
-        def testExecution = server.expectConcurrentAndBlock(DEFAULT_MAX_WORKERS, FAILED_RESOURCE, OTHER_RESOURCE)
-
-        when:
-        def gradleHandle = executer.withTasks(taskList).start()
-        testExecution.waitForAllPendingCalls()
-
-        then:
-        testExecution.release(FAILED_RESOURCE)
-        testExecution.release(OTHER_RESOURCE)
-        gradleHandle.waitForFailure()
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass('pkg.FailedTest').assertTestFailed('failTest', CoreMatchers.anything())
-        result.testClass('pkg.OtherTest').assertTestPassed('passingTest')
-
-        where:
-        description        | taskList                   | buildConfig
-        'default config'   | ['test']                   | ''
-        'failFast = false' | ['test']                   | 'test { failFast = false }'
-    }
-
-    def "stop test execution with #description"() {
-        given:
-        buildFile.text = generator.initBuildFile()
-        buildFile << buildConfig
-        generator.withFailingTest()
-        generator.withNonfailingTest()
-        def testExecution = server.expectOptionalAndBlock(DEFAULT_MAX_WORKERS, FAILED_RESOURCE, OTHER_RESOURCE)
-
-        when:
-        def gradleHandle = executer.withTasks(taskList).start()
-        testExecution.waitForAllPendingCalls()
-
-        then:
-        testExecution.release(FAILED_RESOURCE)
-        gradleHandle.waitForFailure()
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass('pkg.FailedTest').assertTestFailed('failTest', CoreMatchers.anything())
-        result.testClass('pkg.OtherTest').assertTestSkipped('passingTest')
-
-        where:
-        description       | taskList                   | buildConfig
-        'failFast = true' | ['test']                   | 'test { failFast = true }'
-        '--fail-fast'     | ['test', '--fail-fast']    | ''
-    }
-
-    def "ensure fail fast with forkEvery #forkEvery, maxWorkers #maxWorkers, omittedTests #testOmitted"() {
-        given:
-        buildFile.text = generator.initBuildFile(maxWorkers, forkEvery)
-        def resourceForTest = generator.withFailingTests(testOmitted + 1)
-        def testExecution = server.expectOptionalAndBlock(maxWorkers, resourceForTest.values() as String[])
-
-        when:
-        def gradleHandle = executer.withTasks('test', '--fail-fast').start()
-        testExecution.waitForAllPendingCalls()
-
-        then:
-        testExecution.release(1)
-        gradleHandle.waitForFailure()
-        def result = new DefaultTestExecutionResult(testDirectory)
-        assert 1 == resourceForTest.keySet().count { result.testClassExists(it) && result.testClass(it).testFailed('failedTest', CoreMatchers.anything()) }
-        assert testOmitted == resourceForTest.keySet().with {
-            count { !result.testClassExists(it) } +
-                count { result.testClassExists(it) && result.testClass(it).testCount == 0 } +
-                count { result.testClassExists(it) && result.testClass(it).testSkippedCount == 1 }
-        }
-
-        where:
-        forkEvery | maxWorkers | testOmitted
-        0         | 1          | 1
-        1         | 1          | 1
-        2         | 1          | 1
-        0         | 2          | 5
-        1         | 2          | 5
-        2         | 2          | 5
-    }
-
-    def "fail fast console output shows failure"() {
-        given:
-        buildFile.text = generator.initBuildFile()
-        generator.withFailingTest()
-        generator.withNonfailingTest()
-        def testExecution = server.expectConcurrentAndBlock(DEFAULT_MAX_WORKERS, FAILED_RESOURCE, OTHER_RESOURCE)
-
-        when:
-        def gradleHandle = executer.withTasks('test', '--fail-fast').start()
-        testExecution.waitForAllPendingCalls()
-
-        then:
-        testExecution.release(FAILED_RESOURCE)
-        gradleHandle.waitForFailure()
-        assert gradleHandle.standardOutput.matches(/(?s).*FailedTest.*failTest.*FAILED.*java.lang.RuntimeException at FailedTest.java.*/)
-        assert !gradleHandle.standardOutput.contains('pkg.OtherTest')
-    }
-
-    @IgnoreIf({ GradleContextualExecuter.isParallel() })
-    def "fail fast console output shows test class in work-in-progress"() {
-        given:
-        executer.withConsole(ConsoleOutput.Rich).withArguments('--parallel', "--max-workers=$DEFAULT_MAX_WORKERS")
-        buildFile.text = generator.initBuildFile()
-        generator.withFailingTest()
-        generator.withNonfailingTest()
-        def testExecution = server.expectConcurrentAndBlock(DEFAULT_MAX_WORKERS, FAILED_RESOURCE, OTHER_RESOURCE)
-
-        when:
-        def gradleHandle = executer.withTasks('test', '--fail-fast').start()
-        testExecution.waitForAllPendingCalls()
-
-        then:
-        ConcurrentTestUtil.poll {
-            RichConsoleStyling.assertHasWorkInProgress(gradleHandle, '> :test > Executing test pkg.FailedTest')
-            RichConsoleStyling.assertHasWorkInProgress(gradleHandle, '> :test > Executing test pkg.OtherTest')
-        }
-
-        testExecution.release(FAILED_RESOURCE)
-        gradleHandle.waitForFailure()
-    }
-
-    def "fail fast works with --tests filter"() {
-        given:
-        buildFile.text = generator.initBuildFile()
-        def resourceForTest = generator.withFailingTests(5)
-        def testExecution = server.expectOptionalAndBlock(DEFAULT_MAX_WORKERS, resourceForTest.values() as String[])
-
-        when:
-        def gradleHandle = executer.withTasks('test', '--fail-fast', '--tests=*OtherTest_*').start()
-        testExecution.waitForAllPendingCalls()
-
-        then:
-        testExecution.release(DEFAULT_MAX_WORKERS)
-        gradleHandle.waitForFailure()
-
-        assert !gradleHandle.errorOutput.contains('No tests found for given includes:')
-    }
-
-    abstract String testAnnotationClass()
-    abstract String testDependency()
-    abstract String testFrameworkConfiguration()
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestFilteringIntegrationTest.groovy
deleted file mode 100644
index 428d7b7..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestFilteringIntegrationTest.groovy
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * Copyright 2013 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.testing.fixture
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
-import spock.lang.Issue
-
-abstract class AbstractTestFilteringIntegrationTest extends MultiVersionIntegrationSpec {
-
-    abstract String getImports()
-    abstract String getFramework()
-    abstract String getDependencies()
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { ${dependencies} }
-            test { use${framework}() }
-        """
-    }
-
-    def "executes single method from a test class"() {
-        buildFile << """
-            test {
-              filter {
-                includeTestsMatching "${pattern}"
-              }
-            }
-        """
-        file("src/test/java/org/gradle/FooTest.java") << """
-            package org.gradle;
-            import $imports;
-            public class FooTest {
-                @Test public void pass() {}
-                @Test public void fail() { throw new RuntimeException("Boo!"); }
-            }
-        """
-        file("src/test/java/org/gradle/OtherTest.java") << """
-            package org.gradle;
-            import $imports;
-            public class OtherTest {
-                @Test public void pass() {}
-                @Test public void fail() { throw new RuntimeException("Boo!"); }
-            }
-        """
-
-        when:
-        run("test")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("org.gradle.FooTest")
-        result.testClass("org.gradle.FooTest").assertTestsExecuted("pass")
-
-        where:
-        pattern << ['FooTest.pass', 'org.gradle.FooTest.pass']
-    }
-
-    def "executes multiple methods from a test class"() {
-        buildFile << """
-            test {
-              include 'FooTest*'
-              def cls = "FooTest"
-              filter {
-                includeTestsMatching "\${cls}.passOne" //make sure GStrings work
-                includeTestsMatching "\${cls}.passTwo"
-              }
-            }
-        """
-        file("src/test/java/FooTest.java") << """import $imports;
-            public class FooTest {
-                @Test public void passOne() {}
-                @Test public void passTwo() {}
-                @Test public void fail() { throw new RuntimeException("Boo!"); }
-            }
-        """
-        file("src/test/java/OtherTest.java") << """import $imports;
-            public class OtherTest {
-                @Test public void passOne() {}
-                @Test public void fail() { throw new RuntimeException("Boo!"); }
-            }
-        """
-
-        when:
-        run("test")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("FooTest")
-        result.testClass("FooTest").assertTestCount(2, 0, 0)
-    }
-
-    def "executes multiple methods from different classes"() {
-        buildFile << """
-            test {
-              filter.setIncludePatterns 'Foo*.pass*'
-            }
-        """
-        file("src/test/java/Foo1Test.java") << """import $imports;
-            public class Foo1Test {
-                @Test public void pass1() {}
-                @Test public void boo() {}
-            }
-        """
-        file("src/test/java/Foo2Test.java") << """import $imports;
-            public class Foo2Test {
-                @Test public void pass2() {}
-                @Test public void boo() {}
-            }
-        """
-        file("src/test/java/Foo3Test.java") << """import $imports;
-            public class Foo3Test {
-                @Test public void boo() {}
-            }
-        """
-        file("src/test/java/OtherTest.java") << """import $imports;
-            public class OtherTest {
-                @Test public void pass3() {}
-                @Test public void boo() {}
-            }
-        """
-
-        when:
-        run("test")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("Foo1Test", "Foo2Test")
-        result.testClass("Foo1Test").assertTestsExecuted("pass1")
-        result.testClass("Foo2Test").assertTestsExecuted("pass2")
-    }
-
-    def "reports when no matching methods found"() {
-        file("src/test/java/org/gradle/FooTest.java") << """
-            package org.gradle;
-            import $imports;
-            public class FooTest {
-                @Test public void pass() {}
-            }
-        """
-
-        //by command line
-        when: fails("test", "--tests", pattern)
-        then: failure.assertHasCause("No tests found for given includes: [${pattern}](--tests filter)")
-
-        //by build script
-        when:
-        buildFile << "test.filter.includeTestsMatching '${pattern}'"
-        fails("test")
-        then: failure.assertHasCause("No tests found for given includes: [${pattern}](filter.includeTestsMatching)")
-
-        where:
-        pattern << ['FooTest.missingMethod', 'org.gradle.FooTest.missingMethod']
-    }
-
-    def "adds import/export rules to report about no matching methods found"() {
-        file("src/test/java/FooTest.java") << """import $imports;
-            public class FooTest {
-                @Test public void pass() {}
-            }
-        """
-
-        when:
-        buildFile << """
-            test {
-                include 'FooTest*'
-                exclude 'NotImportant*'
-            }
-        """
-        fails("test", "--tests", 'FooTest.missingMethod')
-        then: failure.assertHasCause("No tests found for given includes: [FooTest*](include rules) [NotImportant*](exclude rules) [FooTest.missingMethod](--tests filter)")
-    }
-
-    def "does not report when matching method has been filtered before via include/exclude"() { //current behavior, not necessarily desired
-        file("src/test/java/FooTest.java") << """import $imports;
-            public class FooTest {
-                @Test public void pass() {}
-            }
-        """
-
-        when:
-        buildFile << "test.include 'FooTest.missingMethod'"
-        then:
-        succeeds("test", "--tests", 'FooTest.missingMethod')
-    }
-
-    def "task is out of date when --tests argument changes"() {
-        file("src/test/java/FooTest.java") << """import $imports;
-            public class FooTest {
-                @Test public void pass() {}
-                @Test public void pass2() {}
-            }
-        """
-
-        when: run("test", "--tests", "FooTest.pass")
-        then: new DefaultTestExecutionResult(testDirectory).testClass("FooTest").assertTestsExecuted("pass")
-
-        when: run("test", "--tests", "FooTest.pass")
-        then: skipped(":test") //up-to-date
-
-        when:
-        run("test", "--tests", "FooTest.pass*")
-
-        then:
-        executedAndNotSkipped(":test")
-        new DefaultTestExecutionResult(testDirectory).testClass("FooTest").assertTestsExecuted("pass", "pass2")
-    }
-
-    def "can select multiple tests from commandline #scenario"() {
-        given:
-        file("src/test/java/Foo1Test.java") << """import $imports;
-            public class Foo1Test {
-                @Test public void pass1() {}
-                @Test public void bar() {}
-            }
-        """
-        file("src/test/java/Foo2Test.java") << """import $imports;
-            public class Foo2Test {
-                @Test public void pass2() {}
-                @Test public void bar() {}
-            }
-        """
-        file("src/test/java/BarTest.java") << """import $imports;
-            public class BarTest {
-                @Test public void bar() {}
-            }
-        """
-        file("src/test/java/OtherTest.java") << """import $imports;
-            public class OtherTest {
-                @Test public void pass3() {}
-                @Test public void bar() {}
-            }
-        """
-
-        when:
-        run(stringArrayOf(command))
-
-        then:
-
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted(stringArrayOf(classesExecuted))
-        if (!foo1TestsExecuted.isEmpty()) {
-            result.testClass("Foo1Test").assertTestsExecuted(stringArrayOf(foo1TestsExecuted))
-        }
-        if (!foo2TestsExecuted.isEmpty()) {
-            result.testClass("Foo2Test").assertTestsExecuted(stringArrayOf(foo2TestsExecuted))
-        }
-        if (!barTestsExecuted.isEmpty()) {
-            result.testClass("BarTest").assertTestsExecuted(stringArrayOf(barTestsExecuted))
-        }
-        if (!otherTestsExecuted.isEmpty()) {
-            result.testClass("OtherTest").assertTestsExecuted(stringArrayOf(otherTestsExecuted))
-        }
-
-        where:
-        scenario         | command                                                  | classesExecuted                                  | foo1TestsExecuted | foo2TestsExecuted | barTestsExecuted | otherTestsExecuted
-        "no options"     | ["test"]                                                 | ["Foo1Test", "Foo2Test", "BarTest", "OtherTest"] | ["bar", "pass1"]  | ["bar", "pass2"]  | ["bar"]          | ["bar", "pass3"]
-        "pass and Other" | ["test", "--tests", "*.pass1", "--tests", "*OtherTest*"] | ["Foo1Test", "OtherTest"]                        | ["pass1"]         | []                | []               | ["bar", "pass3"]
-        "pass and *ar"   | ["test", "--tests", "*.pass1", "--tests", "*arTest"]     | ["BarTest", "Foo1Test"]                          | ["pass1"]         | []                | ["bar"]          | []
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/1571")
-    def "option --tests filter in combined with #includeType"() {
-        given:
-        buildFile << """
-        test {
-            $includeConfig
-        }
-        """
-
-        when:
-        createTestABC()
-
-        then:
-        succeeds('test', '--tests', '*ATest*', '--tests', '*BTest*', '--info')
-
-        output.contains('ATest!')
-        !output.contains('BTest!')
-        !output.contains('CTest!')
-
-        where:
-        includeType                   | includeConfig
-        "include and exclude"         | "include '*Test*'; exclude '*BTest*'"
-        "filter.includeTestsMatching" | "filter { includeTestsMatching '*ATest*'; includeTestsMatching '*CTest*' }"
-        "filter.includePatterns"      | "filter { includePatterns = ['*ATest*', '*CTest*'] }"
-    }
-
-    def "invoking testNameIncludePatterns does not influence include/exclude filter"() {
-        given:
-        buildFile << """
-        test {
-            include '*ATest*', '*BTest*'
-            testNameIncludePatterns = [ '*BTest*', '*CTest*' ]
-        }
-        """
-
-        when:
-        createTestABC()
-
-        then:
-        succeeds('test', '--info')
-
-        !output.contains('ATest!')
-        output.contains('BTest!')
-        !output.contains('CTest!')
-    }
-
-    def "invoking filter.includePatterns not disable include/exclude filter"() {
-        given:
-        buildFile << """
-        test {
-            include '*ATest*', '*BTest*'
-            filter.includePatterns = [ '*BTest*', '*CTest*' ]
-        }
-        """
-
-        when:
-        createTestABC()
-
-        then:
-        succeeds('test', '--info')
-
-        !output.contains('ATest!')
-        output.contains('BTest!')
-        !output.contains('CTest!')
-    }
-
-    def "can exclude tests"() {
-        given:
-        buildFile << """
-        test {
-            filter.excludeTestsMatching("*BTest.test*")
-        }
-        """
-
-        createTestABC()
-
-        when:
-        succeeds('test', '--info')
-
-        then:
-        executedAndNotSkipped(":test")
-
-        and:
-        def executionResult = new DefaultTestExecutionResult(testDirectory)
-        executionResult.testClass("ATest").assertTestsExecuted("test")
-        !executionResult.testClassExists("BTest")
-        executionResult.testClass("CTest").assertTestsExecuted("test")
-
-        and:
-        output.contains('ATest!')
-        !output.contains('BTest!')
-        output.contains('CTest!')
-    }
-
-    private createTestABC(){
-        file('src/test/java/ATest.java') << """import $imports;
-            public class ATest {
-                @Test public void test() { System.out.println("ATest!"); }
-            }
-        """
-        file('src/test/java/BTest.java') << """import $imports;
-            public class BTest {
-                @Test public void test() { System.out.println("BTest!"); }
-            }
-        """
-        file('src/test/java/CTest.java') << """import $imports;
-            public class CTest {
-                @Test public void test() { System.out.println("CTest!"); }
-            }
-        """
-    }
-
-    private String[] stringArrayOf(List<String> strings) {
-        return strings.toArray()
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestingMultiVersionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestingMultiVersionIntegrationTest.groovy
new file mode 100644
index 0000000..b647eb9
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/AbstractTestingMultiVersionIntegrationTest.groovy
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2023 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.testing.fixture
+
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+
+/**
+ * Base class for multi-version integration tests that use various versions of JUnit.  This class provides the common
+ * configuration for the build script and test framework.  Subclasses must provide test framework/engine specific
+ * configuration for the build script.
+ *
+ * The following depicts the general pattern of implementation where function-specific tests extend from this class,
+ * while framework/engine-specific classes extend from the function-specific classes.  The framework/engine-specific
+ * classes should provide the {@link BuildScriptConfiguration} and {@link TestSourceConfiguration} required by this
+ * class via a reusable trait.
+ *
+ *  ┌──────────────────────────────────────────────┐
+ *  │  AbstractTestingMultiVersionIntegrationTest  │
+ *  └──────────────────────────────────────────────┘
+ *                        ▲
+ *                        │
+ *  ┌─────────────────────┴────────────────────────┐
+ *  │       AbstractTestTaskIntegrationTest        │
+ *  └──────────────────────────────────────────────┘
+ *                        ▲
+ *                        │
+ *  ┌─────────────────────┴────────────────────────┐      ┌──────────────────────────────────────────────┐
+ *  │     JUnitJupiterTestTaskIntegrationTest      ├─────►│          JUnitJupiterMultiVersionTest        │
+ *  └──────────────────────────────────────────────┘      └──────────────────────────────────────────────┘
+ *
+ */
+abstract class AbstractTestingMultiVersionIntegrationTest extends MultiVersionIntegrationSpec {
+    abstract BuildScriptConfiguration getBuildScriptConfiguration()
+    abstract TestSourceConfiguration getTestSourceConfiguration()
+
+    String getTestFrameworkDependencies() {
+        return buildScriptConfiguration.getTestFrameworkDependencies('test')
+    }
+
+    String getTestFrameworkDependencies(String sourceSet) {
+        return buildScriptConfiguration.getTestFrameworkDependencies(sourceSet)
+    }
+
+    String getConfigureTestFramework() {
+        return buildScriptConfiguration.configureTestFramework
+    }
+
+    String includeCategoryOrTag(String categoryOrTag) {
+        return "${buildScriptConfiguration.includeCategoryOrTagConfigurationElement} '${categoryOrTag}'"
+    }
+
+    String excludeCategoryOrTag(String categoryOrTag) {
+        return "${buildScriptConfiguration.excludeCategoryOrTagConfigurationElement} '${categoryOrTag}'"
+    }
+
+    String getTestFrameworkImports() {
+        return testSourceConfiguration.testFrameworkImports
+    }
+
+    String getBeforeClassAnnotation() {
+        return testSourceConfiguration.beforeClassAnnotation
+    }
+
+    String getAfterClassAnnotation() {
+        return testSourceConfiguration.afterClassAnnotation
+    }
+
+    String getBeforeTestAnnotation() {
+        return testSourceConfiguration.beforeTestAnnotation
+    }
+
+    String getAfterTestAnnotation() {
+        return testSourceConfiguration.afterTestAnnotation
+    }
+
+    String getRunOrExtendWithAnnotation(String runOrExtendWithClasses) {
+        return testSourceConfiguration.getRunOrExtendWithAnnotation(runOrExtendWithClasses)
+    }
+
+    String maybeParentheses(String methodName) {
+        return testSourceConfiguration.maybeParentheses(methodName)
+    }
+
+    String getIgnoreOrDisabledAnnotation() {
+        return testSourceConfiguration.getIgnoreOrDisabledAnnotation()
+    }
+
+    def setup() {
+        executer.withRepositoryMirrors()
+    }
+
+    /**
+     * Test framework specific configuration for the build script.
+     *
+     * These values will be provided by test framework traits that can be attached to any test class.  As such, we use an interface
+     * to ensure type safety and avoid decoupling of these methods in the trait classes.  The trait classes simply have to provide
+     * an implementation of this interface specific to the test framework.
+     */
+    interface BuildScriptConfiguration {
+        String getTestFrameworkDependencies(String sourceSet)
+        String getConfigureTestFramework()
+        String getIncludeCategoryOrTagConfigurationElement()
+        String getExcludeCategoryOrTagConfigurationElement()
+
+        default configurationFor(String sourceSet, String configurationName) {
+            if (sourceSet == 'main') {
+                return configurationName
+            } else {
+                return sourceSet + configurationName.capitalize()
+            }
+        }
+    }
+
+    /**
+     * Test framework specific configuration for test sources.
+     *
+     * These values will be provided by test framework traits that can be attached to any test class.  As such, we use an interface
+     * to ensure type safety and avoid decoupling of these methods in the trait classes.  The trait classes simply have to provide
+     * an implementation of this interface specific to the test framework.
+     */
+    interface TestSourceConfiguration {
+        String getTestFrameworkImports()
+        String getBeforeClassAnnotation()
+        String getAfterClassAnnotation()
+        String getBeforeTestAnnotation()
+        String getAfterTestAnnotation()
+        String getIgnoreOrDisabledAnnotation()
+        String getRunOrExtendWithAnnotation(String runOrExtendWithClasses)
+        String maybeParentheses(String methodName)
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/JUnitMultiVersionIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/JUnitMultiVersionIntegrationSpec.groovy
deleted file mode 100644
index 2d3879d..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/JUnitMultiVersionIntegrationSpec.groovy
+++ /dev/null
@@ -1,157 +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.testing.fixture
-
-import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
-import org.gradle.integtests.fixtures.executer.ExecutionFailure
-import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.junit.Assume
-
-import java.util.regex.Pattern
-
-import static org.gradle.test.fixtures.junitplatform.JUnitPlatformTestRewriter.replaceCategoriesWithTags
-import static org.gradle.test.fixtures.junitplatform.JUnitPlatformTestRewriter.rewriteWithJupiter
-import static org.gradle.test.fixtures.junitplatform.JUnitPlatformTestRewriter.rewriteWithVintage
-
-/**
- * To avoid rework when testing JUnit Platform (a.k.a JUnit 5), we'd like to reuse previous test cases in following aspects:
- *
- *  <ul>
- *   <li>1. Rewrite existing JUnit 4 tests with Jupiter annotations to test Jupiter engine, e.g. @org.junit.Test -> @org.junit.jupiter.api.Test </li>
- *   <li>2. Replace existing JUnit 4 dependencies with Vintage to test Vintage engine's backward compatibility.</li>
- * </ul>
- *
- * {@see org.gradle.test.fixtures.junitplatform.JUnitPlatformTestRewriter}
- *
- */
-abstract class JUnitMultiVersionIntegrationSpec extends MultiVersionIntegrationSpec {
-    // JUnit 5's test case name contains parentheses which might break test assertion, e.g. testMethod() PASSED -> testMethod PASSED
-    static final Pattern TEST_CASE_RESULT_PATTERN = ~/(.*)(\w+)\(\) (PASSED|FAILED|SKIPPED|STANDARD_OUT)/
-
-    def setup() {
-        executer.withRepositoryMirrors()
-    }
-
-    @Override
-    protected ExecutionResult succeeds(String... tasks) {
-        rewriteProjectDirectory()
-        assertUsingJUnitPlatform()
-        super.succeeds(tasks)
-    }
-
-    @Override
-    protected ExecutionFailure fails(String... tasks) {
-        rewriteProjectDirectory()
-        assertUsingJUnitPlatform()
-        super.fails(tasks)
-    }
-
-    @Override
-    void outputContains(String string) {
-        assert getOutput().contains(string.trim())
-    }
-
-    @Override
-    String getOutput() {
-        return outputWithoutTestCaseParentheses()
-    }
-
-    private void rewriteProjectDirectory() {
-        if (isJupiter()) {
-            rewriteWithJupiter(executer.workingDir, dependencyVersion)
-        } else if (isVintage()) {
-            replaceCategoriesWithTags(executer.workingDir)
-            rewriteWithVintage(executer.workingDir, dependencyVersion)
-        }
-    }
-
-    private assertUsingJUnitPlatform() {
-        if (isJupiter() || isVintage()) {
-            File buildFile = new File(executer.workingDir, 'build.gradle')
-            if (buildFile.exists()) {
-                String text = buildFile.text
-                assert text.contains('useJUnitPlatform')
-                assert !text.contains('useJUnit()')
-                assert !text.contains('useJUnit {')
-                assert !text.contains('useTestNG')
-            }
-        }
-    }
-
-    protected List<String> getDependencyNotation() {
-        if (isJupiter()) {
-            return ["org.junit.jupiter:junit-jupiter-api:${dependencyVersion}", "org.junit.jupiter:junit-jupiter-engine:${dependencyVersion}"]
-        } else if (isVintage()) {
-            return ["org.junit.vintage:junit-vintage-engine:${dependencyVersion}","junit:junit:4.13"]
-        } else {
-            return ["junit:junit:${version}"]
-        }
-    }
-
-    protected ignoreWhenJupiter() {
-        Assume.assumeFalse(isJupiter())
-    }
-
-    protected ignoreWhenJUnitPlatform() {
-        Assume.assumeFalse(isJUnitPlatform())
-    }
-
-    protected ignoreWhenJUnit4() {
-        Assume.assumeFalse(isJUnit4())
-    }
-
-    static String getTestFramework() {
-        isJUnitPlatform() ? "JUnitPlatform" : "JUnit"
-    }
-
-    static boolean isJUnitPlatform() {
-        isJupiter() || isVintage()
-    }
-
-    static boolean isJUnit4() {
-        return version.toString() == JUnitCoverage.NEWEST
-    }
-
-    static boolean isVintage() {
-        return version.toString().startsWith("Vintage")
-    }
-
-    static boolean isJupiter() {
-        return version.toString().startsWith("Jupiter")
-    }
-
-    static String getDependencyVersion() {
-        if (isJupiter()) {
-            return version.toString().substring("Jupiter:".length())
-        } else if (isVintage()) {
-            return version.toString().substring("Vintage:".length())
-        } else {
-            return version
-        }
-    }
-
-    String outputWithoutTestCaseParentheses() {
-        List<String> lines = super.getOutput().split(/\n/)
-        return lines.collect {
-            if (TEST_CASE_RESULT_PATTERN.matcher(it).matches()) {
-                TEST_CASE_RESULT_PATTERN.matcher(it).replaceFirst('$1$2 $3')
-            } else {
-                it
-            }
-        }.join('\n')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/JvmBlockingTestClassGenerator.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/JvmBlockingTestClassGenerator.groovy
index 5e8a05e..a55d4c7 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/JvmBlockingTestClassGenerator.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/fixture/JvmBlockingTestClassGenerator.groovy
@@ -27,15 +27,15 @@
 
     private final TestFile root
     private final BlockingHttpServer server
-    private final String testAnnotationClass
-    private final String testDependency
+    private final String testFrameworkImports
+    private final String testFrameworkDependencies
     private final String testFrameworkConfiguration
 
-    JvmBlockingTestClassGenerator(TestFile root, BlockingHttpServer server, String testAnnotationClass, String testDependency, String testFrameworkConfiguration) {
+    JvmBlockingTestClassGenerator(TestFile root, BlockingHttpServer server, String testFrameworkImports, String testFrameworkDependencies, String testFrameworkConfiguration) {
         this.root = root
         this.server = server
-        this.testAnnotationClass = testAnnotationClass
-        this.testDependency = testDependency
+        this.testFrameworkImports = testFrameworkImports
+        this.testFrameworkDependencies = testFrameworkDependencies
         this.testFrameworkConfiguration = testFrameworkConfiguration
     }
 
@@ -46,7 +46,7 @@
             ${RepoScriptBlockUtil.mavenCentralRepository()}
 
             dependencies {
-                testImplementation '$testDependency'
+                $testFrameworkDependencies
             }
 
             tasks.withType(Test) {
@@ -54,14 +54,14 @@
                 forkEvery = $forkEvery
             }
 
-            $testFrameworkConfiguration
+            test.$testFrameworkConfiguration
         """
     }
 
     void withFailingTest() {
         root.file('src/test/java/pkg/FailedTest.java') << """
             package pkg;
-            import $testAnnotationClass;
+            $testFrameworkImports
             public class FailedTest {
                 @Test
                 public void failTest() {
@@ -75,7 +75,7 @@
     void withNonfailingTest() {
         root.file('src/test/java/pkg/OtherTest.java') << """
             package pkg;
-            import $testAnnotationClass;
+            $testFrameworkImports
             public class OtherTest {
                 @Test
                 public void passingTest() {
@@ -90,7 +90,7 @@
             final resource = "test_${it}" as String
             root.file("src/test/java/pkg/OtherTest_${it}.java") << """
                 package pkg;
-                import $testAnnotationClass;
+                $testFrameworkImports
                 public class OtherTest_${it} {
                     @Test
                     public void passingTest() {
@@ -107,7 +107,7 @@
             final testName = "OtherTest_${it}" as String
             final resource = "test_${it}" as String
             root.file("src/test/java/OtherTest_${it}.java") << """
-                import $testAnnotationClass;
+                $testFrameworkImports
                 public class ${testName} {
                     @Test
                     public void failedTest() {
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnit3FilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnit3FilteringIntegrationTest.groovy
new file mode 100644
index 0000000..6931fd7
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnit3FilteringIntegrationTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractJUnit3FilteringIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    void "filters tests implemented using 3.x test cases"() {
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        file("src/test/java/FooTest.java") << """
+            import junit.framework.*;
+
+            public class FooTest extends TestCase {
+                public void testPass() {}
+                public void testOk() {}
+            }
+        """.stripIndent()
+
+        when:
+        succeeds("test", "--tests", "FooTest.testPass")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("FooTest")
+        result.testClass("FooTest").assertTestsExecuted("testPass")
+
+        when:
+        fails("test", "--tests", "FooTest.unknown")
+
+        then:
+        failure.assertHasCause("No tests found for given includes: [FooTest.unknown]")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitAbortedTestClassIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitAbortedTestClassIntegrationTest.groovy
new file mode 100644
index 0000000..87348a9
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitAbortedTestClassIntegrationTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractJUnitAbortedTestClassIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+
+    // Note: there're some behavior changes in 4.13:
+    // https://github.com/junit-team/junit4/issues/1066
+    // So this test was adjusted accordingly.
+    def "supports assumptions in JUnit4 rules"() {
+        given:
+        executer.noExtraLogging()
+        file('src/test/java/org/gradle/SkippingRuleTests.java') << """
+            package org.gradle;
+
+            import org.junit.FixMethodOrder;
+            import org.junit.Rule;
+            import org.junit.Test;
+            import org.junit.rules.MethodRule;
+
+            import static org.junit.Assume.assumeFalse;
+            import static org.junit.runners.MethodSorters.NAME_ASCENDING;
+
+            @FixMethodOrder(NAME_ASCENDING)
+            public class SkippingRuleTests {
+
+                @Rule
+                public MethodRule misbehavingSkippingRule = (statement, method, target) -> {
+                    assumeFalse(method.getName().equals("b"));
+                    return statement;
+                };
+
+                @Test
+                public void a() {
+                }
+
+                @Test
+                public void b() {
+                }
+
+                @Test
+                public void c() {
+                }
+
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        run('test')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.SkippingRuleTests')
+        result.testClass('org.gradle.SkippingRuleTests')
+            .assertTestCount(3, 0, 0)
+            .assertTestsExecuted('a', 'c')
+            .assertTestsSkipped('b')
+    }
+
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitAssumptionsIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitAssumptionsIntegrationTest.groovy
new file mode 100644
index 0000000..4e7a7cb
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitAssumptionsIntegrationTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractJUnitAssumptionsIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    def "supports assumptions"() {
+        given:
+        executer.noExtraLogging()
+        file('src/test/java/org/gradle/TestWithAssumptions.java').text = """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class TestWithAssumptions {
+                @Test
+                public void assumptionFailed() {
+                    assumeTrue(false);
+                }
+
+                @Test
+                public void assumptionSucceeded() {
+                    assumeTrue(true);
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        run('check')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.TestWithAssumptions')
+        result.testClass('org.gradle.TestWithAssumptions')
+                .assertTestCount(2, 0, 0)
+                .assertTestsExecuted('assumptionSucceeded')
+                .assertTestPassed('assumptionSucceeded')
+                .assertTestsSkipped('assumptionFailed')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.groovy
new file mode 100644
index 0000000..6da8182
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.groovy
@@ -0,0 +1,411 @@
+/*
+ * Copyright 2013 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec extends AbstractTestingMultiVersionIntegrationTest {
+    TestSourceFixture testSources = new TestSourceFixture()
+
+    abstract TestSourceGenerator getTestSourceGenerator()
+    abstract String getSingularCategoryOrTagName()
+    abstract String getPluralCategoryOrTagName()
+
+    def setup() {
+        executer.noExtraLogging()
+        buildFile << """
+            apply plugin: "java"
+
+            repositories {
+                mavenCentral()
+            }
+
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+        """
+    }
+
+    def "can exclude categories or tags only"() {
+        given:
+        testSources.with {
+            testClass('CategoryATests')
+                .withCategoryOrTag('CategoryA')
+                .with {
+                    testMethod('catAOk1').shouldPass()
+                    testMethod('catAOk2').shouldPass()
+                    testMethod('catAOk3').shouldPass()
+                    testMethod('catAOk4').shouldPass()
+                }
+            testClass('NoCatTests')
+                .with {
+                    testMethod('noCatOk1').shouldPass()
+                    testMethod('noCatOk2').shouldPass()
+                }
+            testClass('SomeOtherCatTests')
+                .withCategoryOrTag('SomeOtherCat')
+                .with {
+                    testMethod('someOtherOk1').shouldPass()
+                    testMethod('someOtherOk2').shouldPass()
+                }
+            testClass('SomeTests')
+                .with {
+                    testMethod('catAOk1').withCategoryOrTag('CategoryA').shouldPass()
+                    testMethod('someOtherCatOk2').withCategoryOrTag('SomeOtherCat').shouldPass()
+                    testMethod('noCatOk3').shouldPass()
+                    testMethod('noCatOk4').shouldPass()
+                }
+
+            testCategory('CategoryA')
+            testCategory('SomeOtherCat')
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                ${configureTestFramework} {
+                    ${excludeCategoryOrTag('CategoryA')}
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('NoCatTests', 'SomeTests', 'SomeOtherCatTests')
+        result.testClass("SomeOtherCatTests").assertTestCount(2, 0, 0)
+        result.testClass("SomeOtherCatTests").assertTestsExecuted('someOtherOk1', 'someOtherOk2')
+        result.testClass("NoCatTests").assertTestCount(2, 0, 0)
+        result.testClass("NoCatTests").assertTestsExecuted('noCatOk1', 'noCatOk2')
+        result.testClass("SomeTests").assertTestCount(3, 0, 0)
+        result.testClass("SomeTests").assertTestsExecuted('noCatOk3', 'noCatOk4', 'someOtherCatOk2')
+    }
+
+    def "can include categories or tags only"() {
+        given:
+        testSources.with {
+            testClass('CategoryATests')
+                .withCategoryOrTag('CategoryA')
+                .with {
+                    testMethod('catAOk1').shouldPass()
+                    testMethod('catAOk2').shouldPass()
+                    testMethod('catAOk3').shouldPass()
+                    testMethod('catAOk4').shouldPass()
+                }
+            testClass('NoCatTests')
+                .with {
+                    testMethod('noCatOk1').shouldPass()
+                    testMethod('noCatOk2').shouldPass()
+                }
+            testClass('SomeOtherCatTests')
+                .withCategoryOrTag('SomeOtherCat')
+                .with {
+                    testMethod('someOtherOk1').shouldPass()
+                    testMethod('someOtherOk2').shouldPass()
+                }
+            testClass('SomeTests')
+                .with {
+                    testMethod('catAOk1').withCategoryOrTag('CategoryA').shouldPass()
+                    testMethod('someOtherCatOk2').withCategoryOrTag('SomeOtherCat').shouldPass()
+                    testMethod('noCatOk3').shouldPass()
+                    testMethod('noCatOk4').shouldPass()
+                }
+
+            testCategory('CategoryA')
+            testCategory('SomeOtherCat')
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                ${configureTestFramework} {
+                    ${includeCategoryOrTag('CategoryA')}
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('CategoryATests', 'SomeTests')
+        result.testClass("CategoryATests").assertTestCount(4, 0, 0)
+        result.testClass("CategoryATests").assertTestsExecuted('catAOk1', 'catAOk2', 'catAOk3', 'catAOk4')
+        result.testClass("SomeTests").assertTestCount(1, 0, 0)
+        result.testClass("SomeTests").assertTestsExecuted('catAOk1')
+    }
+
+    def "emits warning when specifying a conflicting include/exclude"() {
+        given:
+        testSources.with {
+            testClass('CategoryATests')
+                .withCategoryOrTag('CategoryA')
+                .with {
+                    testMethod('catAOk1').shouldPass()
+                    testMethod('catAOk2').shouldPass()
+                    testMethod('catAOk3').shouldPass()
+                    testMethod('catAOk4').shouldPass()
+                }
+            testClass('CategoryCTests')
+                .withCategoryOrTag('CategoryC')
+                .with {
+                    testMethod('catCOk1').shouldPass()
+                    testMethod('catCOk2').shouldPass()
+                    testMethod('catCOk3').shouldPass()
+                    testMethod('catCOk4').shouldPass()
+                }
+            testCategory('CategoryA')
+            testCategory('CategoryC')
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                ${configureTestFramework} {
+                    ${includeCategoryOrTag('CategoryA')}
+                    ${includeCategoryOrTag('CategoryC')}
+                    ${excludeCategoryOrTag('CategoryC')}
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        assertOutputContainsCategoryOrTagWarning('CategoryC')
+
+        and:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('CategoryATests')
+        result.testClass("CategoryATests").assertTestCount(4, 0, 0)
+        result.testClass("CategoryATests").assertTestsExecuted('catAOk1', 'catAOk2', 'catAOk3', 'catAOk4')
+    }
+
+    def "emits warning when specifying multiple conflicting includes/excludes"() {
+        given:
+        testSources.with {
+            testClass('CategoryATests')
+                .withCategoryOrTag('CategoryA')
+                .with {
+                    testMethod('catAOk1').shouldPass()
+                    testMethod('catAOk2').shouldPass()
+                    testMethod('catAOk3').shouldPass()
+                    testMethod('catAOk4').shouldPass()
+                }
+            testClass('CategoryCTests')
+                .withCategoryOrTag('CategoryC')
+                .with {
+                    testMethod('catCOk1').shouldPass()
+                    testMethod('catCOk2').shouldPass()
+                    testMethod('catCOk3').shouldPass()
+                    testMethod('catCOk4').shouldPass()
+                }
+            testCategory('CategoryA')
+            testCategory('CategoryC')
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                ${configureTestFramework} {
+                    ${includeCategoryOrTag('CategoryA')}
+                    ${includeCategoryOrTag('CategoryC')}
+                    ${excludeCategoryOrTag('CategoryA')}
+                    ${excludeCategoryOrTag('CategoryC')}
+                }
+            }
+        """
+
+        when:
+        executer.expectDocumentedDeprecationWarning("No test executed. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. There are test sources present but no test was executed. Please check your test configuration. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_fail_on_no_test_executed")
+        run('test')
+
+        then:
+        assertOutputContainsCategoryOrTagWarning('CategoryA', 'CategoryC')
+
+        and:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertNoTestClassesExecuted()
+    }
+
+    void assertOutputContainsCategoryOrTagWarning(String... categories) {
+        if (categories.size() == 1) {
+            outputContains("The ${singularCategoryOrTagName} '${categories[0]}' is both included and excluded.")
+        } else {
+            String allCategories = categories.collect {"'${it}'" }.join(", ")
+            outputContains("The ${pluralCategoryOrTagName} ${allCategories} are both included and excluded.")
+        }
+    }
+
+    /**
+     * Fixture for generating test classes from a given test source fixture.  This should be implemented by subclasses that generate
+     * unique test sources (e.g. JUnit4 vs JUnit5).
+     */
+    interface TestSourceGenerator {
+        void writeAllSources(TestSourceFixture fixture)
+    }
+
+    /**
+     * Fixture for capturing test source requirements.  This will be provided to a {@link TestSourceGenerator} to generate the test sources.
+     */
+    class TestSourceFixture {
+        List<TestClass> testClasses = []
+        List<Category> categories = []
+        List<TestSource> sources = []
+
+        /**
+         * Add a new simple test class to the sources.
+         */
+        TestClass testClass(String name, String sourceSet = 'test') {
+            TestClass testClass = new TestClass(name, sourceSet)
+            testClasses << testClass
+            return testClass
+        }
+
+        /**
+         * Add a JUnit4 category class to the sources.  Note that these are not used by JUnit5 test generators.
+         */
+        Category testCategory(String name, String sourceSet = 'test') {
+            Category category = new Category(name, sourceSet)
+            categories << category
+            return category
+        }
+
+        /**
+         * Add an arbitrary test source file to the sources.  This can be used to add supplementary classes or complex test classes to the sources.
+         */
+        TestSource sourceFile(String relativePath, String sourceSet = 'test') {
+            TestSource source = new TestSource(relativePath, sourceSet)
+            sources << source
+            return source
+        }
+    }
+
+    /**
+     * Fixture for capturing simple test class requirements.  Note that this class should be used only for simple test classes
+     * and should not be enhanced to capture complex test classes with arbitrary features.  Complex test classes should be captured
+     * with a raw {@link TestSource} fixture.
+     */
+    class TestClass {
+        final String name
+        final String packageName
+        final String sourceSet
+
+        List<String> categoriesOrTags = []
+        List<TestMethod> methods = []
+
+        TestClass(String name, String sourceSet) {
+            this.name = name.substring(name.lastIndexOf('.') + 1)
+            this.packageName = name.contains('.') ? name.substring(0, name.lastIndexOf('.')) : ''
+            this.sourceSet = sourceSet
+        }
+
+        TestClass withCategoryOrTag(String name) {
+            categoriesOrTags << name
+            return this
+        }
+
+        TestMethod testMethod(String name) {
+            TestMethod method = new TestMethod(name)
+            methods << method
+            return method
+        }
+    }
+
+    /**
+     * Fixture for capturing simple test method requirements.
+     */
+    class TestMethod {
+        final String name
+        boolean shouldPass = true
+        boolean isIgnoredOrDisabled
+        List<String> categoriesOrTags = []
+
+        TestMethod(name) {
+            this.name = name
+        }
+
+        TestMethod withCategoryOrTag(String name) {
+            categoriesOrTags << name
+            return this
+        }
+
+        TestMethod shouldPass() {
+            shouldPass = true
+            return this
+        }
+
+        TestMethod ignoreOrDisable() {
+            isIgnoredOrDisabled = true
+            return this
+        }
+
+        TestMethod shouldFail() {
+            shouldPass = false
+            return this
+        }
+    }
+
+    /**
+     * Fixture for capturing test categories for JUnit4.
+     */
+    class Category {
+        final String name
+        final String packageName
+        final String sourceSet
+        List<String> extendsCategories = []
+
+        Category(String name, String sourceSet) {
+            this.name = name.substring(name.lastIndexOf('.') + 1)
+            this.packageName = name.contains('.') ? name.substring(0, name.lastIndexOf('.')) : ''
+            this.extendsCategories = extendsCategories
+            this.sourceSet = sourceSet
+        }
+
+        Category extendsCategory(String name) {
+            extendsCategories << name
+            return this
+        }
+    }
+
+    /**
+     * Fixture for capturing raw test source files.  This should be used for complex test classes that cannot be captured with {@link TestClass}.
+     */
+    class TestSource {
+        final String relativePath
+        final String sourceSet
+        String source
+
+        TestSource(String relativePath, String sourceSet) {
+            this.relativePath = relativePath
+            this.source = source
+            this.sourceSet = sourceSet
+        }
+
+        TestSource withSource(String source) {
+            this.source = source
+            return this
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitClassDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..84dc22d
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitClassDetectionIntegrationTest.groovy
@@ -0,0 +1,208 @@
+/*
+ * 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.Issue
+
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
+import java.util.zip.ZipOutputStream
+
+@Issue("https://github.com/gradle/gradle/issues/18486")
+abstract class AbstractJUnitClassDetectionIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+
+    def setup() {
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+    }
+
+    def jarName = "testFramework.jar"
+    def jarNameNew = "anotherTestFramework.jar"
+    def jar = new File(testDirectory, "build/libs/$jarName")
+    def jarNew = new File(testDirectory, "build/libs/$jarNameNew")
+
+    def 'support detecting classes whose package is not a zip entry'() {
+        given:
+        buildFile << """
+            dependencies {
+                ${getTestFrameworkDependencies('main')}
+                testImplementation files('build/libs/testFramework.jar')
+                ${testFrameworkDependencies}
+            }
+            jar {
+                manifest {
+                    attributes 'Manifest-Version': '1.0'
+                }
+                archiveBaseName = 'testFramework'
+            }
+        """
+
+        file("src/main/java/org/gradle/BasePlatformTestCase.java") << """
+            package org.gradle;
+            import junit.framework.TestCase;
+            public class BasePlatformTestCase extends TestCase { }
+        """
+
+        when:
+        succeeds('build')
+
+        then:
+        Set<String> entries = ["org/", "org/gradle/", "org/gradle/BasePlatformTestCase.class"]
+        assertJarContainsAllEntries(jar, entries)
+        file('src/test/java/SomeTest.java') << """
+            package com.example;
+            import org.gradle.BasePlatformTestCase;
+            public class SomeTest extends BasePlatformTestCase {
+                public void testPass() { }
+            }
+        """
+
+        when:
+        succeeds("test", "--tests", "SomeTest")
+        then:
+        new DefaultTestExecutionResult(testDirectory).testClass('com.example.SomeTest').assertTestCount(1, 0, 0)
+        new DefaultTestExecutionResult(testDirectory).assertTestClassesExecuted('com.example.SomeTest')
+
+        when:
+        createNewJarWithoutPackageEntries(jar, jarNew)
+        String entry = "org/gradle/BasePlatformTestCase.class"
+        assertJarContainsOnlyOneEntry(jarNew, entry)
+        testDirectory.file("build.gradle").replace(jarName, jarNameNew)
+        succeeds("test", "--tests", "SomeTest")
+        then:
+        new DefaultTestExecutionResult(testDirectory).testClass('com.example.SomeTest').assertTestCount(1, 0, 0)
+        new DefaultTestExecutionResult(testDirectory).assertTestClassesExecuted('com.example.SomeTest')
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/18465")
+    def 'does not try to execute non-test class as test class'() {
+        given:
+        file('src/test/java/com/example/MyTest.java') << """
+            package com.example;
+
+            ${testFrameworkImports}
+
+            public class MyTest {
+                @Test public void someTest() {
+                    assertTrue(true);
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/com/example/CacheSpec.java') << """
+            package com.example;
+
+            import static java.lang.annotation.ElementType.METHOD;
+            import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+            import java.lang.annotation.Retention;
+            import java.lang.annotation.Target;
+            import java.util.concurrent.Executor;
+            import java.util.concurrent.ForkJoinPool;
+
+            @SuppressWarnings("ImmutableEnumChecker")
+            @Target(METHOD) @Retention(RUNTIME)
+            public @interface CacheSpec {
+
+              enum CacheExecutor {
+                DEFAULT {
+                  @Override public Executor create() {
+                    return ForkJoinPool.commonPool();
+                  }
+                },
+                DIRECT {
+                  @Override public Executor create() {
+                    return Runnable::run;
+                  }
+                };
+
+                public abstract Executor create();
+              }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory).testClass('com.example.MyTest').assertTestCount(1, 0, 0)
+    }
+
+    private static void assertJarContainsAllEntries(final File jar, final Set<String> entries) {
+        ZipFile originalJar = new ZipFile(jar)
+
+        originalJar.withCloseable {
+            Enumeration<? extends ZipEntry> enumeration = originalJar.entries()
+            while (enumeration.hasMoreElements()) {
+                ZipEntry entry = enumeration.nextElement()
+                String name = entry.getName()
+                if (entries.contains(name)) {
+                    entries.remove(name)
+                }
+            }
+        }
+
+        assert entries.isEmpty()
+    }
+
+    private static void assertJarContainsOnlyOneEntry(final File jar, final String entry) {
+        ZipFile originalJar = new ZipFile(jar)
+
+        originalJar.withCloseable {
+            Enumeration<? extends ZipEntry> enumeration = originalJar.entries()
+            assert enumeration.hasMoreElements()
+            String name = enumeration.nextElement().name
+            assert name == entry
+            assert !enumeration.hasMoreElements()
+        }
+    }
+
+    private static void createNewJarWithoutPackageEntries(final File jar, final File jarNew) {
+        ZipFile originalJar = new ZipFile(jar)
+        originalJar.withCloseable {
+            Enumeration<? extends ZipEntry> entries = originalJar.entries()
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = entries.nextElement()
+                if (entry.getName().contains("BasePlatformTestCase")) {
+                    ZipEntry newEntry = new ZipEntry(entry.getName())
+                    ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(jarNew))
+                    outputStream.withCloseable { output ->
+                        output.putNextEntry(newEntry)
+                        InputStream inputStream = originalJar.getInputStream(entry)
+                        inputStream.withCloseable { input ->
+                            byte[] buffer = new byte[512]
+                            while (input.available() > 0) {
+                                int read = input.read(buffer)
+                                if (read > 0) {
+                                    output.write(buffer, 0, read)
+                                }
+                            }
+                        }
+                    }
+                    return
+                }
+            }
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitClassLevelFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitClassLevelFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..9da2b8d
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitClassLevelFilteringIntegrationTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractJUnitClassLevelFilteringIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+
+    def setup() {
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """
+    }
+
+    def "can filter tests from a superclass"() {
+        given:
+        file('src/test/java/SuperClass.java') << """
+            ${testFrameworkImports}
+
+            public abstract class SuperClass {
+                @Test
+                public void superTest() {
+                }
+            }
+        """
+        file('src/test/java/SubClass.java') << """
+            ${testFrameworkImports}
+
+            public class SubClass extends SuperClass {
+                @Test
+                public void subTest() {
+                }
+            }
+        """
+
+        when:
+        succeeds('test', '--tests', 'SubClass.superTest')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted('SubClass')
+            .testClass('SubClass')
+            .assertTestCount(1, 0, 0)
+            .assertTestPassed('superTest')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitConsoleLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitConsoleLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..04defac
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitConsoleLoggingIntegrationTest.groovy
@@ -0,0 +1,226 @@
+/*
+ * 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+import static org.hamcrest.CoreMatchers.containsString
+import static org.hamcrest.CoreMatchers.equalTo
+
+abstract class AbstractJUnitConsoleLoggingIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    TestFile testFile = file('src/test/java/org/gradle/SomeTest.java')
+    abstract String getMaybePackagePrefix()
+
+    def setup() {
+        executer.noExtraLogging()
+        testFile << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class SomeTest {
+                @Test
+                public void goodTest() {}
+
+                @Test
+                public void badTest() {
+                    beBad();
+                }
+
+                @Test
+                public void printTest() {
+                    System.out.println("line 1\\nline 2");
+                    System.out.println("line 3");
+                }
+
+                @Test
+                ${ignoreOrDisabledAnnotation}
+                public void ignoredTest() {
+                    throw new RuntimeException("ignored");
+                }
+
+                private void beBad() {
+                    throw new RuntimeException("bad");
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: "groovy"
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+
+            test {
+                ${configureTestFramework}
+                testLogging {
+                    quiet {
+                        events "skipped", "failed"
+                        minGranularity 2
+                        maxGranularity -1
+                        displayGranularity 3
+                        exceptionFormat "full"
+                        stackTraceFilters "truncate", "groovy"
+                    }
+                }
+            }
+        """
+    }
+
+    def "default lifecycle logging"() {
+        when:
+        fails("test")
+
+        then:
+        outputContains("""
+            ${maybePackagePrefix}SomeTest > ${maybeParentheses('badTest')} FAILED
+                java.lang.RuntimeException at SomeTest.java:${lineNumberOf('RuntimeException("bad")')}
+        """.stripIndent())
+    }
+
+    def "custom quiet logging"() {
+        when:
+        executer.withStackTraceChecksDisabled()
+        args("-q")
+        fails("test")
+
+        then:
+        outputContains("""
+            ${maybeParentheses('badTest')} FAILED
+                java.lang.RuntimeException: bad
+                    at org.gradle.SomeTest.beBad(SomeTest.java:${lineNumberOf('throw new RuntimeException("bad")')})
+                    at org.gradle.SomeTest.badTest(SomeTest.java:${lineNumberOf('beBad();')})
+        """.stripIndent())
+
+        outputContains("${maybeParentheses('ignoredTest')} SKIPPED")
+
+        outputContains("${maybePackagePrefix}SomeTest FAILED")
+    }
+
+    def "standard output logging"() {
+        given:
+        file('src/test/java/org/gradle/StandardOutputTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class StandardOutputTest {
+                @Test
+                public void goodTest() {}
+
+                @Test
+                public void badTest() {
+                    beBad() ;
+                }
+
+                @Test
+                public void printTest() {
+                    System.out.println("line 1\\nline 2");
+                    System.out.println("line 3");
+                }
+
+                @Test
+                ${ignoreOrDisabledAnnotation}
+                public void ignoredTest() {
+                    throw new RuntimeException("ignored");
+                }
+
+                private void beBad() {
+                    throw new RuntimeException("bad");
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            test {
+                testLogging {
+                    quiet {
+                        events "standardOut", "standardError"
+                    }
+                }
+            }
+        """
+
+        when:
+        executer.withStackTraceChecksDisabled()
+        args("-q")
+        fails("test")
+
+        then:
+        outputContains("""
+            ${maybeParentheses('printTest')} STANDARD_OUT
+                line 1
+                line 2
+                line 3
+        """.stripIndent())
+    }
+
+    def "test logging is included in XML results"() {
+        file("build.gradle") << """
+            apply plugin: 'java'
+                ${mavenCentralRepository()}
+                dependencies {
+                    ${testFrameworkDependencies}
+                }
+        """.stripIndent()
+
+        file("src/test/java/EncodingTest.java") << """
+            ${testFrameworkImports}
+
+            public class EncodingTest {
+                @Test public void encodesCdata() {
+                    System.out.println("< html allowed, cdata closing token ]]> encoded!");
+                    System.out.print("no EOL, ");
+                    System.out.println("non-asci char: ż");
+                    System.out.println("xml entity: &amp;");
+                    System.err.println("< html allowed, cdata closing token ]]> encoded!");
+                }
+                @Test public void encodesAttributeValues() {
+                    throw new RuntimeException("html: <> cdata: ]]>");
+                }
+            }
+        """.stripIndent()
+
+        when:
+        fails("test")
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+                .testClass("EncodingTest")
+                .assertTestPassed("encodesCdata")
+                .assertTestFailed("encodesAttributeValues", equalTo('java.lang.RuntimeException: html: <> cdata: ]]>'))
+                .assertStdout(containsString(
+                    "< html allowed, cdata closing token ]]> encoded!\n" +
+                    "no EOL, non-asci char: ż\n" +
+                    "xml entity: &amp;"
+                ))
+                .assertStderr(equalTo("< html allowed, cdata closing token ]]> encoded!\n"))
+    }
+
+    String lineNumberOf(String text) {
+        int i = 1
+        for (String line : testFile.readLines()) {
+            if (line.contains(text)) {
+                return i as String
+            }
+            i++
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitEnclosedRunnerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitEnclosedRunnerIntegrationTest.groovy
new file mode 100644
index 0000000..a4bc5be
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitEnclosedRunnerIntegrationTest.groovy
@@ -0,0 +1,142 @@
+/*
+ * 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.Issue
+
+abstract class AbstractJUnitEnclosedRunnerIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    def setup() {
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/2319')
+    def 'can run tests in Enclosed runner'() {
+        given:
+        file('src/test/java/EnclosedTest.java') << """
+            ${testFrameworkImports}
+            import org.junit.experimental.runners.Enclosed;
+
+            @RunWith( Enclosed.class )
+            public class EnclosedTest {
+                public static class InnerClass {
+                   @Test
+                    public void aTest() {
+                        Assert.assertEquals( "test", "test" );
+                    }
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClass('EnclosedTest$InnerClass').assertTestCount(1, 0, 0)
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/2320')
+    def 'can run @BeforeClass in Enclosed runner'() {
+        given:
+        file('src/test/java/EnclosedTest.java') << """
+            ${testFrameworkImports}
+            import org.junit.experimental.runners.Enclosed;
+
+            @RunWith( Enclosed.class )
+            public class EnclosedTest {
+                private static String someValue;
+
+                @BeforeClass
+                public static void setSomeValue() {
+                    someValue = "test";
+                }
+
+                public static class InnerClass {
+                    @Test
+                    public void aTest() {
+                        Assert.assertEquals( "test", someValue );
+                    }
+                }
+            }
+        """.stripIndent()
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClass('EnclosedTest$InnerClass').assertTestCount(1, 0, 0)
+    }
+
+    @Issue('https://github.com/junit-team/junit4/issues/1354')
+    def 'can run tests in Enclosed runner with Category'() {
+        given:
+        file('src/test/java/EnclosedTest.java') << """
+            ${testFrameworkImports}
+            import org.junit.experimental.runners.Enclosed;
+            import org.junit.experimental.categories.Category;
+
+            @RunWith(Enclosed.class)
+            public class EnclosedTest {
+              private static int outer;
+
+              @BeforeClass
+              public static void runBeforeEnclosedSuite() {
+                  outer = 1;
+              }
+
+              @Category(Fast.class)
+              public static class InnerClass {
+                private int inner;
+
+                @Before
+                public void setUp() {
+                   inner = 2;
+                }
+
+                @Test
+                public void test() {
+                   Assert.assertTrue(outer==1 && inner==2);
+                }
+              }
+            }
+        """.stripIndent()
+        file('src/test/java/Fast.java') << 'public interface Fast {}'
+        buildFile << '''
+            test {
+                useJUnit {
+                    includeCategories 'Fast'
+                }
+            }
+        '''
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClass('EnclosedTest$InnerClass').assertTestCount(1, 0, 0)
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitIgnoreClassIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitIgnoreClassIntegrationTest.groovy
new file mode 100644
index 0000000..2f7f8c0
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitIgnoreClassIntegrationTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractJUnitIgnoreClassIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+
+    def "can handle class level ignored tests"() {
+        given:
+        executer.noExtraLogging()
+        file('src/test/java/org/gradle/IgnoredTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            ${ignoreOrDisabledAnnotation}
+            public class IgnoredTest {
+                @Test
+                public void testIgnored() {
+                }
+            }
+        """
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """
+
+        when:
+        run('check')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.IgnoredTest')
+        result.testClass('org.gradle.IgnoredTest').assertTestCount(1, 0, 0).assertTestsSkipped("testIgnored")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitIntegrationTest.groovy
new file mode 100755
index 0000000..d774fca
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitIntegrationTest.groovy
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2011 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.testing.junit
+
+import org.apache.commons.lang.RandomStringUtils
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.IgnoreIf
+
+/**
+ * This class contains tests that don't fit well into other function-specific test classes.
+ */
+abstract class AbstractJUnitIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    def setup() {
+        executer.noExtraLogging()
+    }
+
+    def "can use test super classes from another project"() {
+        given:
+        file('settings.gradle').write("include 'a', 'b'")
+        file('b/build.gradle') << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${getTestFrameworkDependencies('main')}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+        file('b/src/main/java/org/gradle/AbstractTest.java') << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public abstract class AbstractTest {
+                @Test public void ok() { }
+            }
+        """.stripIndent()
+        file('a/build.gradle') << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+                testImplementation project(':b')
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+        file('a/src/test/java/org/gradle/SomeTest.java') << '''
+            package org.gradle;
+            public class SomeTest extends AbstractTest {
+            }
+        '''.stripIndent()
+
+        when:
+        executer.withTasks('a:test').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory.file('a'))
+        result.assertTestClassesExecuted('org.gradle.SomeTest')
+        result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
+    }
+
+    def "can exclude super classes from execution"() {
+        given:
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test {
+                ${configureTestFramework}
+                exclude '**/BaseTest.*'
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/BaseTest.java') << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public class BaseTest {
+                @Test public void ok() { }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeTest.java') << '''
+            package org.gradle;
+            public class SomeTest extends BaseTest {
+            }
+        '''.stripIndent()
+
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.SomeTest')
+        result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
+    }
+
+    @IgnoreIf({ GradleContextualExecuter.parallel })
+    def "can have multiple test task instances"() {
+        given:
+        file('src/test/java/org/gradle/Test1.java') << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public class Test1 {
+                @Test public void ok() { }
+            }
+        """.stripIndent()
+        file('src/test2/java/org/gradle/Test2.java') << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public class Test2 {
+                @Test public void ok() { }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+
+            ${mavenCentralRepository()}
+
+            sourceSets {
+                test2
+            }
+
+            test.${configureTestFramework}
+
+            task test2(type: Test) {
+                ${configureTestFramework}
+                classpath = sourceSets.test2.runtimeClasspath
+                testClassesDirs = sourceSets.test2.output.classesDirs
+            }
+
+            check {
+                dependsOn test2
+            }
+
+            dependencies {
+                ${getTestFrameworkDependencies('test')}
+                ${getTestFrameworkDependencies('test2')}
+            }
+        """.stripIndent()
+
+        when:
+        executer.withTasks('check').run()
+
+        then:
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        testResult.assertTestClassesExecuted('org.gradle.Test1')
+        testResult.testClass('org.gradle.Test1').assertTestPassed('ok')
+
+        def test2Result = new JUnitXmlTestExecutionResult(testDirectory, 'build/test-results/test2')
+        test2Result.assertTestClassesExecuted('org.gradle.Test2')
+        test2Result.testClass('org.gradle.Test2').assertTestPassed('ok')
+    }
+
+    @Requires(UnitTestPreconditions.NotWindows)
+    def "can use long paths for working directory"() {
+        given:
+        // windows can handle a path up to 260 characters
+        // we create a path that is 260 +1 (offset + "/" + randompath)
+        def pathoffset = 260 - testDirectory.getAbsolutePath().length()
+        def alphanumeric = RandomStringUtils.randomAlphanumeric(pathoffset)
+        def testWorkingDir = testDirectory.createDir("$alphanumeric")
+
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+            test.workingDir = "${testWorkingDir.toURI()}"
+        """.stripIndent()
+
+        and:
+        file("src/test/java/SomeTest.java") << """
+            ${testFrameworkImports}
+
+            public class SomeTest {
+                @Test public void foo() {
+                    System.out.println(new java.io.File(".").getAbsolutePath());
+                }
+            }
+        """.stripIndent()
+
+        expect:
+        succeeds "test"
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitJdkNavigationIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitJdkNavigationIntegrationTest.groovy
new file mode 100644
index 0000000..07f7f44
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitJdkNavigationIntegrationTest.groovy
@@ -0,0 +1,72 @@
+/*
+ * 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.Issue
+
+@Issue("GRADLE-1682")
+abstract class AbstractJUnitJdkNavigationIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    def shouldNotNavigateToJdkClasses() {
+        given:
+        file('src/test/java/org/gradle/AbstractTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public abstract class AbstractTest {
+
+                protected int value = 0;
+
+                ${beforeTestAnnotation}
+                public void before() {
+                    value = 1;
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/Test1.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class Test1 extends AbstractTest {
+
+                @Test
+                public void shouldPass() {
+                    assertEquals(1, value);
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+                testImplementation 'org.apache.felix:org.osgi.foundation:1.2.0'
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        succeeds('test')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass('org.gradle.Test1').assertTestPassed('shouldPass')
+    }
+
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitJnaIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitJnaIntegrationTest.groovy
new file mode 100644
index 0000000..2fa5fc0
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitJnaIntegrationTest.groovy
@@ -0,0 +1,59 @@
+/*
+ * 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractJUnitJnaIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    @Requires(UnitTestPreconditions.Windows)
+    def canRunTestsUsingJna() {
+        given:
+        file('src/test/java/OkTest.java') << """
+            ${testFrameworkImports}
+            import com.sun.jna.platform.win32.Shell32;
+
+            public class OkTest {
+                @Test
+                public void ok() {
+                    assert Shell32.INSTANCE != null;
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                ${testFrameworkDependencies}
+                testImplementation 'net.java.dev.jna:jna-platform:4.1.0'
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        executer.withTasks('build').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('OkTest')
+        result.testClass('OkTest').assertTestPassed('ok')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitLoggingOutputCaptureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitLoggingOutputCaptureIntegrationTest.groovy
new file mode 100644
index 0000000..0cdcdd8
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitLoggingOutputCaptureIntegrationTest.groovy
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014 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.testing.junit
+
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+import static org.hamcrest.CoreMatchers.containsString
+import static org.hamcrest.CoreMatchers.is
+
+abstract class AbstractJUnitLoggingOutputCaptureIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    def setup() {
+        buildFile << """
+            apply plugin: "java"
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test {
+                ${configureTestFramework}
+                reports.junitXml.outputPerTestCase = true
+                // JUnit 5's test name contains parentheses
+                onOutput { test, event -> print "\${test.toString().replace('()(', '(')} -> \$event.message" }
+            }
+        """.stripIndent()
+    }
+
+    def "captures output from logging frameworks"() {
+        buildFile << """
+            dependencies { testImplementation "org.slf4j:slf4j-simple:1.7.10", "org.slf4j:slf4j-api:1.7.10" }
+        """.stripIndent()
+        file("src/test/java/FooTest.java") << """
+            ${testFrameworkImports}
+            public class FooTest {
+                private final static org.slf4j.Logger SLF4J = org.slf4j.LoggerFactory.getLogger(FooTest.class);
+                private final static java.util.logging.Logger JUL = java.util.logging.Logger.getLogger(FooTest.class.getName());
+
+                @Test
+                public void foo() {
+                  SLF4J.info("slf4j info");
+                  JUL.info("jul info");
+                  JUL.warning("jul warning");
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds("test")
+
+        then:
+        outputContains("Test foo(FooTest) -> [Test worker] INFO FooTest - slf4j info")
+        outputContains("Test foo(FooTest) -> ${java.util.logging.Level.INFO.getLocalizedName()}: jul info")
+        outputContains("Test foo(FooTest) -> ${java.util.logging.Level.WARNING.getLocalizedName()}: jul warning")
+
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = testResult.testClass("FooTest")
+        classResult.assertTestCaseStderr("foo", containsString("[Test worker] INFO FooTest - slf4j info"))
+        classResult.assertTestCaseStderr("foo", containsString("${java.util.logging.Level.INFO.getLocalizedName()}: jul info"))
+        classResult.assertTestCaseStderr("foo", containsString("${java.util.logging.Level.WARNING.getLocalizedName()}: jul warning"))
+    }
+
+    def "test can generate output from multiple threads"() {
+        file("src/test/java/OkTest.java") << """
+            import java.util.logging.Logger;
+            import java.util.List;
+            import java.util.ArrayList;
+
+            ${testFrameworkImports}
+
+            public class OkTest {
+                @Test
+                public void ok() throws Exception {
+                    // logging from multiple threads
+                    List<Thread> threads  = new ArrayList<Thread>();
+                    for (int i = 0; i < 5; i++) {
+                        Thread thread = new Thread("thread " + i) {
+                            @Override
+                            public void run() {
+                                System.out.print("stdout from "); // print a partial line
+                                System.err.println("stderr from " + getName());
+                                System.out.println(getName());
+                                Logger.getLogger("test-logger").info("info from " + getName());
+                            }
+                        };
+                        thread.start();
+                        threads.add(thread);
+                    }
+                    for(Thread thread: threads) {
+                        thread.join();
+                    }
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds("test")
+
+        then:
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = testResult.testClass("OkTest")
+
+        5.times { n ->
+            outputContains("Test ok(OkTest) -> stdout from thread $n")
+            outputContains("Test ok(OkTest) -> stderr from thread $n")
+            outputContains("Test ok(OkTest) -> ${java.util.logging.Level.INFO.getLocalizedName()}: info from thread $n")
+
+            classResult.assertTestCaseStdout("ok", containsString("stdout from thread $n"))
+            classResult.assertTestCaseStderr("ok", containsString("stderr from thread $n"))
+            classResult.assertTestCaseStderr("ok", containsString("${java.util.logging.Level.INFO.getLocalizedName()}: info from thread $n"))
+        }
+    }
+
+    def "output does not require trailing end-of-line separator"() {
+        file("src/test/java/OkTest.java") << """
+            ${testFrameworkImports}
+
+            public class OkTest {
+                ${beforeTestAnnotation}
+                public void before() {
+                    System.out.print("[before out]");
+                    System.err.print("[before err]");
+                }
+
+                ${afterTestAnnotation}
+                public void after() {
+                    System.out.print("[after out]");
+                    System.err.print("[after err]");
+                }
+
+                ${beforeClassAnnotation}
+                public static void init() {
+                    System.out.print("[before class out]");
+                    System.err.print("[before class err]");
+                }
+
+                ${afterClassAnnotation}
+                public static void end() {
+                    System.out.print("[after class out]");
+                    System.err.print("[after class err]");
+                }
+
+                @Test
+                public void ok() {
+                    System.out.print("[test out]");
+                    System.err.print("[test err]");
+                }
+
+                @Test
+                public void anotherOk() {
+                    System.out.println();
+                    System.err.println();
+                    System.out.print("[ok out]");
+                    System.err.print("[ok err]");
+                }
+            }
+        """.stripIndent()
+
+        when:
+        run "test"
+
+        then:
+        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = testResult.testClass("OkTest")
+        classResult.assertTestCaseStdout("ok", is("[before out][test out][after out]"))
+        classResult.assertTestCaseStderr("ok", is("[before err][test err][after err]"))
+        classResult.assertTestCaseStdout("anotherOk", is("[before out]\n[ok out][after out]"))
+        classResult.assertTestCaseStderr("anotherOk", is("[before err]\n[ok err][after err]"))
+        classResult.assertStdout(is("[before class out][after class out]"))
+        classResult.assertStderr(is("[before class err][after class err]"))
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSmokeMultiVersionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSmokeMultiVersionIntegrationTest.groovy
new file mode 100644
index 0000000..c0071a4
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSmokeMultiVersionIntegrationTest.groovy
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+import static org.hamcrest.CoreMatchers.containsString
+
+abstract class AbstractJUnitSmokeMultiVersionIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    abstract void assertTestSkippedOrPassed(TestClassExecutionResult testClassResult, String testName)
+
+    def "can run tests using JUnit"() {
+        given:
+        file('src/test/java/org/gradle/Junit3Test.java') << """
+            package org.gradle;
+
+            import junit.framework.TestCase;
+
+            public class Junit3Test extends TestCase {
+                public void testRenamesItself() {
+                    setName("a test that renames itself");
+                    fail("epic");
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/Junit4Test.java') << """
+            package org.gradle;
+
+            import org.junit.Ignore;
+            import org.junit.Test;
+
+            public class Junit4Test {
+                @Test
+                public void ok() {
+                }
+
+                @Test
+                @Ignore
+                public void broken() {
+                    throw new RuntimeException();
+                }
+
+                public void helpermethod() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/NoTest.java') << """
+            package org.gradle;
+
+            public class NoTest {
+                public void notATest() {
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        fails('test')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.Junit3Test', 'org.gradle.Junit4Test')
+        def junit3TestClass = result.testClass('org.gradle.Junit3Test')
+            .assertTestCount(2, 1, 0)
+            .assertTestFailed('a test that renames itself', containsString("epic"))
+        assertTestSkippedOrPassed(junit3TestClass, 'testRenamesItself')
+        result.testClass('org.gradle.Junit4Test')
+                .assertTestCount(2, 0, 0)
+                .assertTestsExecuted('ok')
+                .assertTestPassed('ok')
+                .assertTestsSkipped('broken')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSpockIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSpockIntegrationTest.groovy
new file mode 100644
index 0000000..eeead9a
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSpockIntegrationTest.groovy
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2016 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.Issue
+
+abstract class AbstractJUnitSpockIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    def "can run spock tests with mock of class using gradleApi"() {
+        file("build.gradle") << """
+            plugins {
+                id("groovy")
+            }
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                implementation gradleApi()
+                implementation localGroovy()
+                ${testFrameworkDependencies}
+            }
+
+            testing {
+                suites {
+                    // Must explicitly use `named` to avoid being rewritten by JUnitPlatformTestRewriter.rewriteBuildFile
+                    named('test') {
+                        useSpock()
+                        dependencies {
+                            // Required to use Spock mocking
+                            runtimeOnly 'net.bytebuddy:byte-buddy:1.12.17'
+                        }
+                    }
+                }
+            }
+        """
+        file("src/main/groovy/MockIt.groovy") << """
+            class MockIt {
+                void call() {
+                }
+            }
+        """
+
+        file("src/main/groovy/Caller.groovy") << """
+            class Caller {
+                private MockIt callable
+
+                Caller(MockIt callable) {
+                    this.callable = callable
+                }
+
+                void call() {
+                   callable.call()
+                }
+            }
+        """
+        file("src/test/groovy/TestSpec.groovy") << """
+            import spock.lang.Specification
+
+            class TestSpec extends Specification {
+                def testMethod() {
+                    final callable = Mock(MockIt)
+                    def caller = new Caller(callable)
+                    when:
+                    caller.call()
+                    then:
+                    1 * callable.call()
+                    0 * _
+                }
+            }
+        """
+        expect:
+        succeeds("test")
+    }
+
+    def 'can run spock with @Unroll'() {
+        given:
+        writeSpockDependencies()
+        file('src/test/groovy/UnrollTest.groovy') << '''
+            import spock.lang.Specification
+            import spock.lang.Unroll
+
+            class UnrollTest extends Specification {
+                @Unroll
+                def "can test #type"() {
+                    expect: type
+
+                    where:
+                    type << ['1', '2']
+                }
+            }
+        '''
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClass("UnrollTest").assertTestCount(2, 0, 0)
+            .assertTestPassed('can test 1')
+            .assertTestPassed('can test 2')
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/4358')
+    def 'can run spock test with same method name in super class and base class'() {
+        given:
+        writeSpockDependencies()
+        file('src/test/groovy/Base.groovy') << '''
+            import spock.lang.Specification
+
+            abstract class Base extends Specification {
+                def ok() {
+                    expect: "success"
+                }
+            }
+
+            class Sub extends Base {
+                def ok() {
+                    expect: "success"
+                }
+            }
+        '''
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClass("Sub").assertTestCount(2, 0, 0)
+    }
+
+    private void writeSpockDependencies() {
+        file("build.gradle") << """
+            apply plugin: 'groovy'
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+
+            testing {
+                suites {
+                    test {
+                        useSpock()
+                        dependencies {
+                            implementation localGroovy()
+                        }
+                    }
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSuitesIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSuitesIntegrationTest.groovy
new file mode 100644
index 0000000..7d19df6
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitSuitesIntegrationTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractJUnitSuitesIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    abstract String getTestFrameworkSuiteDependencies()
+    abstract String getTestFrameworkSuiteImports()
+    abstract String getTestFrameworkSuiteAnnotations(String classes)
+
+    def "test classes can be shared by multiple suites"() {
+        given:
+        file('src/test/java/org/gradle/SomeTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class SomeTest {
+                @Test
+                public void ok() throws Exception {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeTestSuite.java') << """
+            package org.gradle;
+
+            ${testFrameworkSuiteImports}
+
+            ${getTestFrameworkSuiteAnnotations("SomeTest.class")}
+            public class SomeTestSuite {
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeOtherTestSuite.java') << """
+            package org.gradle;
+
+            ${testFrameworkSuiteImports}
+
+            ${getTestFrameworkSuiteAnnotations("SomeTest.class")}
+            public class SomeOtherTestSuite {
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+                ${testFrameworkSuiteDependencies}
+            }
+            test {
+                ${configureTestFramework}
+                include '**/*Suite.class'
+                exclude '**/*Test.class'
+            }
+        """.stripIndent()
+
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.SomeTest')
+        result.testClass("org.gradle.SomeTest").assertTestCount(2, 0, 0)
+        result.testClass("org.gradle.SomeTest").assertTestsExecuted("ok", "ok")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestClassDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..ef83c429
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestClassDetectionIntegrationTest.groovy
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2023 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import spock.lang.Issue
+
+abstract class AbstractJUnitTestClassDetectionIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2527")
+    def "test class detection works for custom test tasks"() {
+        given:
+        buildFile << """
+            apply plugin:'java'
+            ${mavenCentralRepository()}
+
+            sourceSets {
+                othertests {
+                    java.srcDir file('src/othertests/java')
+                    resources.srcDir file('src/othertests/resources')
+                }
+            }
+
+            dependencies{
+                ${getTestFrameworkDependencies('othertests')}
+            }
+
+            task othertestsTest(type:Test){
+                ${configureTestFramework}
+                classpath = sourceSets.othertests.runtimeClasspath
+                testClassesDirs = sourceSets.othertests.output.classesDirs
+            }
+        """.stripIndent()
+
+        and:
+        file("src/othertests/java/SomeTestClass.java") << """
+            ${testFrameworkImports}
+            public class SomeTestClass {
+                @Test
+                public void testTrue() {
+                    assertTrue(true);
+                }
+            }
+        """.stripIndent()
+
+        when:
+        run "othertestsTest"
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory, 'build', '', '', 'othertestsTest')
+        result.assertTestClassesExecuted("SomeTestClass")
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-3157")
+    def "test class detection works when '-parameters' compiler option is used (JEP 118)"() {
+        when:
+        buildScript """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            tasks.withType(JavaCompile) {
+                options.with {
+                    compilerArgs << '-parameters'
+                }
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        and:
+        file("src/test/java/TestHelper.java") << """
+            public class TestHelper {
+                public void helperMethod(String foo, int bar) {
+                    // this method shouldn't cause failure due to API version check
+                    // in org.objectweb.asm.MethodVisitor#visitParameter
+                }
+            }
+        """.stripIndent()
+
+        and:
+        file("src/test/java/TestCase.java") << """
+            ${testFrameworkImports}
+            public class TestCase {
+                @Test
+                public void test() {
+                    assertTrue(Double.parseDouble(System.getProperty("java.specification.version")) >= 1.8);
+                }
+            }
+        """.stripIndent()
+
+        then:
+        run "test"
+
+        and:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass("TestCase").with {
+            assertTestCount(1, 0, 0)
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestExecutionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..b5db633
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestExecutionIntegrationTest.groovy
@@ -0,0 +1,507 @@
+/*
+ * Copyright 2023 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.testing.junit
+
+import org.gradle.api.JavaVersion
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.integtests.fixtures.TestExecutionResult
+import org.gradle.integtests.fixtures.jvm.JavaToolchainFixture
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import org.hamcrest.CoreMatchers
+import spock.lang.Issue
+
+import static org.hamcrest.CoreMatchers.containsString
+import static org.hamcrest.CoreMatchers.equalTo
+import static org.hamcrest.CoreMatchers.not
+import static org.hamcrest.MatcherAssert.assertThat
+
+abstract class AbstractJUnitTestExecutionIntegrationTest extends AbstractTestingMultiVersionIntegrationTest implements JavaToolchainFixture {
+    abstract String getJUnitVersionAssertion()
+    abstract TestClassExecutionResult assertFailedToExecute(TestExecutionResult testResult, String testClassName)
+
+    def "executes tests in the correct environment"() {
+        given:
+        file('src/test/java/org/gradle/OkTest.java') << """
+            package org.gradle;
+
+            import java.io.File;
+            import java.io.PrintStream;
+            import java.net.MalformedURLException;
+            import java.net.URL;
+            import java.net.URLClassLoader;
+            import java.util.ArrayList;
+            import java.util.Arrays;
+            import java.util.List;
+            import java.util.logging.Logger;
+            import java.util.regex.Pattern;
+
+            ${testFrameworkImports}
+
+            public class OkTest {
+                @Test
+                public void ok() throws Exception {
+                    // check versions of dependencies
+                    ${JUnitVersionAssertion}
+                    assertTrue(org.apache.tools.ant.Main.getAntVersion().contains("1.6.1"));
+
+                    // check working dir
+                    assertEquals(System.getProperty("projectDir"), System.getProperty("user.dir"));
+
+                    // check sys properties
+                    assertEquals("value", System.getProperty("testSysProperty"));
+
+                    // check env vars
+                    assertEquals("value", System.getenv("TEST_ENV_VAR"));
+
+                    // check classloader and classpath
+                    assertSame(ClassLoader.getSystemClassLoader(), getClass().getClassLoader());
+                    assertSame(getClass().getClassLoader(), Thread.currentThread().getContextClassLoader());
+                    boolean isJava9 = Boolean.parseBoolean(System.getProperty("isJava9"));
+                    String[] splitTestRuntimeClasspath = splitClasspath(System.getProperty("testRuntimeClasspath"));
+                    if (isJava9) {
+                        String[] splitCliClasspath = splitClasspath(System.getProperty("java.class.path"));
+
+                        // The worker jar is first on the classpath.
+                        String workerJar = splitCliClasspath[0];
+                        assertTrue(workerJar.contains("gradle-worker.jar"));
+
+                        // After, we expect the test runtime classpath.
+                        String[] filteredCliClasspath = Arrays.copyOfRange(splitCliClasspath, 1, splitCliClasspath.length);
+                        assertArrayEquals(splitTestRuntimeClasspath, filteredCliClasspath);
+                    } else {
+                        List<URL> systemClasspath = Arrays.asList(((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs());
+
+                        // The worker jar is first on the classpath.
+                        String workerJar = systemClasspath.get(0).getPath();
+                        assertTrue(workerJar.endsWith("gradle-worker.jar"));
+
+                        // After, we expect the test runtime classpath.
+                        List<URL> filteredSystemClasspath = systemClasspath.subList(1, systemClasspath.size());
+                        List<URL> testRuntimeClasspath = getTestRuntimeClasspath(splitTestRuntimeClasspath);
+                        assertEquals(testRuntimeClasspath, filteredSystemClasspath);
+                    }
+
+                    // check Gradle and impl classes not visible
+                    try {
+                        getClass().getClassLoader().loadClass("org.gradle.api.Project");
+                        fail();
+                    } catch (ClassNotFoundException e) {
+                    }
+                    try {
+                        getClass().getClassLoader().loadClass("org.slf4j.Logger");
+                        fail();
+                    } catch (ClassNotFoundException e) {
+                    }
+
+                    // check other environmental stuff
+                    assertEquals("Test worker", Thread.currentThread().getName());
+                    assertNull(System.console());
+
+                    final PrintStream out = System.out;
+                    // logging from a shutdown hook
+                    Runtime.getRuntime().addShutdownHook(new Thread() {
+                        @Override
+                        public void run() {
+                            out.println("stdout from a shutdown hook.");
+                            Logger.getLogger("test-logger").info("info from a shutdown hook.");
+                        }
+                    });
+                }
+
+                public List<URL> getTestRuntimeClasspath(String[] splitTestRuntimeClasspath) throws MalformedURLException {
+                    List<URL> urls = new ArrayList<URL>();
+                    for (String path : splitTestRuntimeClasspath) {
+                        urls.add(new File(path).toURI().toURL());
+                    }
+                    return urls;
+                }
+
+                private String[] splitClasspath(String classpath) {
+                    return classpath.split(Pattern.quote(File.pathSeparator));
+                }
+
+                @Test
+                public void anotherOk() {
+                }
+            }
+        """.stripIndent()
+
+        file('src/test/java/org/gradle/OtherTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class OtherTest {
+                @Test
+                public void ok() throws Exception {
+                }
+            }
+        """.stripIndent()
+
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+                testImplementation 'ant:ant:1.6.1', 'ant:ant-launcher:1.6.1'
+            }
+            test {
+                ${configureTestFramework}
+                systemProperties.isJava9 = ${JavaVersion.current().isJava9Compatible()}
+                systemProperties.testSysProperty = 'value'
+                systemProperties.projectDir = projectDir
+                jvmArgumentProviders.add(new TestClassPathProvider(testClasspath: sourceSets.test.runtimeClasspath))
+                environment.TEST_ENV_VAR = 'value'
+            }
+
+            class TestClassPathProvider implements CommandLineArgumentProvider {
+                @Classpath
+                FileCollection testClasspath
+
+                @Override
+                List<String> asArguments() {
+                    FileCollection filteredTestClasspath = testClasspath.filter { f -> f.exists() || ("*".equals(f.getName()) && f.getParentFile() != null && f.getParentFile().exists()) }
+                    ["-DtestRuntimeClasspath=\${filteredTestClasspath.asPath}".toString()]
+                }
+            }
+        """.stripIndent()
+
+        when:
+        executer.withTasks('build').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.OkTest', 'org.gradle.OtherTest')
+        result.testClass('org.gradle.OkTest').assertTestPassed('ok')
+        result.testClass('org.gradle.OtherTest').assertTestPassed('ok')
+    }
+
+    def "runs all tests in the same forked jvm"() {
+        given:
+        file('src/test/java/org/gradle/AbstractTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public abstract class AbstractTest {
+                @Test public void ok() {
+                    long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();
+                    System.out.println(String.format(\"VM START TIME = %s\", time));
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeTest.java') << """
+            package org.gradle;
+            public class SomeTest extends AbstractTest {
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeTest2.java') << """
+            package org.gradle;
+            public class SomeTest2 extends AbstractTest {
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test {
+                ${configureTestFramework}
+            }
+        """.stripIndent()
+
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        TestFile results1 = testDirectory.file('build/test-results/test/TEST-org.gradle.SomeTest.xml')
+        TestFile results2 = testDirectory.file('build/test-results/test/TEST-org.gradle.SomeTest2.xml')
+        results1.assertIsFile()
+        results2.assertIsFile()
+        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), equalTo(results2.linesThat(containsString('VM START TIME =')).get(0)))
+    }
+
+    def "can specify maximum number of test classes to execute in a forked jvm"() {
+        given:
+        file('src/test/java/org/gradle/AbstractTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public abstract class AbstractTest {
+                @Test public void ok() {
+                    long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();
+                    System.out.println(String.format(\"VM START TIME = %s\", time));
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeTest.java') << """
+            package org.gradle;
+            public class SomeTest extends AbstractTest {
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeTest2.java') << """
+            package org.gradle;
+            public class SomeTest2 extends AbstractTest {
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test {
+                ${configureTestFramework}
+                forkEvery = 1
+            }
+        """.stripIndent()
+
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        TestFile results1 = testDirectory.file('build/test-results/test/TEST-org.gradle.SomeTest.xml')
+        TestFile results2 = testDirectory.file('build/test-results/test/TEST-org.gradle.SomeTest2.xml')
+        results1.assertIsFile()
+        results2.assertIsFile()
+        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), not(equalTo(results2.linesThat(
+            containsString('VM START TIME =')).get(0))))
+    }
+
+    def "tries to execute unparseable test classes"() {
+        given:
+        file('build/classes/java/test/com/example/Foo.class').text = "invalid class file"
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """
+
+        when:
+        fails('test', '-x', 'compileTestJava')
+
+        then:
+        failureCauseContains("There were failing tests")
+        DefaultTestExecutionResult testResult = new DefaultTestExecutionResult(testDirectory)
+        assertFailedToExecute(testResult, 'com.example.Foo').assertTestCount(1, 1, 0)
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-1948")
+    def "test interrupting its own thread does not kill test execution"() {
+        given:
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        and:
+        file("src/test/java/SomeTest.java") << """
+            ${testFrameworkImports}
+
+            public class SomeTest {
+                @Test public void foo() {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        """.stripIndent()
+
+        when:
+        run "test"
+
+        then:
+        executedAndNotSkipped(":test")
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2962")
+    def "incompatible user versions of classes that we also use don't affect test execution"() {
+
+        // These dependencies are quite particular.
+        // Both jars contain 'com.google.common.collect.ImmutableCollection'
+        // 'google-collections' contains '$EmptyImmutableCollection' which extends '$AbstractImmutableCollection' which is also in guava 15.
+        // In the google-collections version '$EmptyImmutableCollection' overrides `toArray()`.
+        // In guava 15, this method is final.
+        // This causes a verifier error when loading $EmptyImmutableCollection (can't override final method).
+
+        // Our test infrastructure loads org.gradle.util.SystemProperties, which depends on $EmptyImmutableCollection from guava 14.
+        // The below test is testing that out infrastructure doesn't throw a VerifyError while bootstrapping.
+        // This is testing classloader isolation, but this was not the real problem that triggered GRADLE-2962.
+        // The problem was that we tried to load the user's $EmptyImmutableCollection in a class loader structure we wouldn't have used anyway,
+        // but this caused the infrastructure to fail with an internal error because of the VerifyError.
+        // In a nutshell, this tests that we don't even try to load classes that are there, but that we shouldn't see.
+
+        when:
+        executer
+            .withArgument("-Porg.gradle.java.installations.paths=${AvailableJavaHomes.getAvailableJvms().collect { it.javaHome.absolutePath }.join(",")}")
+            .withToolchainDetectionEnabled()
+        buildFile << """
+            plugins {
+                id("java")
+            }
+            ${javaPluginToolchainVersion(11)}
+            ${mavenCentralRepository()}
+            configurations { first {}; last {} }
+            dependencies {
+                // guarantee ordering
+                first 'com.google.guava:guava:15.0'
+                last 'com.google.collections:google-collections:1.0'
+                implementation configurations.first + configurations.last
+
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        and:
+        file("src/test/java/TestCase.java") << """
+            ${testFrameworkImports}
+            public class TestCase {
+                @Test
+                public void test() throws Exception {
+                    getClass().getClassLoader().loadClass("com.google.common.collect.ImmutableCollection\$EmptyImmutableCollection");
+                }
+            }
+        """.stripIndent()
+
+        then:
+        fails "test"
+
+        and:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass("TestCase").with {
+            assertTestFailed("test", CoreMatchers.containsString("java.lang.VerifyError"))
+            assertTestFailed("test", CoreMatchers.containsString("\$EmptyImmutableCollection"))
+        }
+    }
+
+    def "tests are re-executed when set of candidate classes change"() {
+        given:
+        buildFile << """
+            apply plugin:'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test {
+                ${configureTestFramework}
+                testLogging {
+                    events "passed", "skipped", "failed"
+                }
+            }
+        """.stripIndent()
+
+        and:
+        file("src/test/java/FirstTest.java") << """
+            ${testFrameworkImports}
+            public class FirstTest {
+                @Test public void test() {}
+            }
+        """.stripIndent()
+
+        file("src/test/java/SecondTest.java") << """
+            ${testFrameworkImports}
+            public class SecondTest {
+                @Test public void test() {}
+            }
+        """.stripIndent()
+
+        when:
+        run "test"
+        then:
+        executedAndNotSkipped ":test"
+        output.contains("FirstTest > ${maybeParentheses('test')} PASSED")
+        output.contains("SecondTest > ${maybeParentheses('test')} PASSED")
+
+        when:
+        run "test"
+        then:
+        skipped ":test"
+
+        when:
+        buildFile << """
+        test {
+            filter {
+                includeTestsMatching "First*"
+            }
+        }
+        """
+        then:
+        run "test"
+        then:
+        executedAndNotSkipped ":test"
+        output.contains("FirstTest > ${maybeParentheses('test')} PASSED")
+        !output.contains("SecondTest > ${maybeParentheses('test')} PASSED")
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/5305")
+    def "test can install an irreplaceable SecurityManager"() {
+        given:
+        executer
+            .withStackTraceChecksDisabled()
+            .withToolchainDetectionEnabled()
+        withInstallations(AvailableJavaHomes.getAvailableJvms())
+        buildFile << """
+            plugins {
+                id("java")
+            }
+            ${javaPluginToolchainVersion(11)}
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        and:
+        file('src/test/java/SecurityManagerInstallationTest.java') << """
+            ${testFrameworkImports}
+            import java.security.Permission;
+
+            public class SecurityManagerInstallationTest {
+                @Test
+                public void testSecurityManagerCleanExit() {
+                    System.setSecurityManager(new SecurityManager() {
+                        @Override
+                        public void checkPermission(Permission perm) {
+                            if ("setSecurityManager".equals(perm.getName())) {
+                                throw new SecurityException("You cannot replace this security manager!");
+                            }
+                        }
+                    });
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds "test"
+
+        then:
+        outputContains "Unable to reset SecurityManager"
+    }
+
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestFailureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestFailureIntegrationTest.groovy
new file mode 100644
index 0000000..76775b0
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestFailureIntegrationTest.groovy
@@ -0,0 +1,500 @@
+/*
+ * Copyright 2023 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import org.hamcrest.Matcher
+import spock.lang.Issue
+
+import static org.hamcrest.CoreMatchers.containsString
+import static org.hamcrest.CoreMatchers.equalTo
+import static org.hamcrest.CoreMatchers.startsWith
+
+abstract class AbstractJUnitTestFailureIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    abstract void writeBrokenRunnerOrExtension(String className)
+    abstract void writeClassUsingBrokenRunnerOrExtension(String className, String runnerOrExtensionName)
+    abstract String getInitializationErrorTestName()
+    abstract String getAssertionFailureClassName()
+    abstract String getBeforeClassErrorTestName()
+    abstract String getAfterClassErrorTestName()
+    abstract Matcher<? super String>[] getBrokenBeforeAndAfterMatchers()
+
+    def "reports and breaks build when tests fail"() {
+        given:
+        file('src/test/java/org/gradle/BrokenAfter.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class BrokenAfter {
+                ${afterTestAnnotation}
+                public void broken() {
+                    fail("failed");
+                }
+
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/BrokenAfterClass.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class BrokenAfterClass {
+                ${afterClassAnnotation}
+                public static void broken() {
+                    fail("failed");
+                }
+
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/BrokenBefore.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class BrokenBefore {
+                ${beforeTestAnnotation}
+                public void broken() {
+                    fail("failed");
+                }
+
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/BrokenBeforeClass.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class BrokenBeforeClass {
+                ${beforeClassAnnotation}
+                public static void broken() {
+                    fail("failed");
+                }
+
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/BrokenBeforeAndAfter.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class BrokenBeforeAndAfter {
+                ${beforeTestAnnotation}
+                public void brokenBefore() {
+                    fail("before failed");
+                }
+
+                ${afterTestAnnotation}
+                public void brokenAfter() {
+                    fail("after failed");
+                }
+
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/BrokenConstructor.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class BrokenConstructor {
+                public BrokenConstructor() {
+                    fail("failed");
+                }
+
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/BrokenException.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class BrokenException {
+                @Test
+                public void broken() {
+                    // Exception's toString() throws an exception
+                    throw new BrokenRuntimeException();
+                }
+
+                private static class BrokenRuntimeException extends RuntimeException {
+                    @Override
+                    public String toString() {
+                        throw new UnsupportedOperationException();
+                    }
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/BrokenTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class BrokenTest {
+                @Test
+                public void failure() {
+                    fail("failed");
+                }
+
+                @Test
+                public void broken() {
+                    throw new IllegalStateException("html: <> cdata: ]]>");
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/CustomException.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class CustomException {
+                @Test
+                public void custom() {
+                    throw new CustomException.CustomRuntimeException();
+                }
+
+                private static class CustomRuntimeException extends RuntimeException {
+                    @Override
+                    public String toString() {
+                        return "Exception with a custom toString implementation";
+                    }
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/Unloadable.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class Unloadable {
+                static {
+                    fail("failed");
+                }
+
+                @Test
+                public void ok() {
+                }
+
+                @Test
+                public void ok2() {
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/UnserializableException.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            import java.io.IOException;
+            import java.io.ObjectOutputStream;
+
+            public class UnserializableException {
+
+                @Test
+                public void unserialized() {
+                    throw new UnserializableRuntimeException("whatever", null);
+                }
+
+                static class UnserializableRuntimeException extends RuntimeException {
+                    UnserializableRuntimeException(String message, Throwable cause) {
+                        super(message, cause);
+                    }
+
+                    private void writeObject(ObjectOutputStream outstr) throws IOException {
+                        outstr.writeObject(new Object());
+                    }
+                }
+            }
+        """.stripIndent()
+        writeBrokenRunnerOrExtension('BrokenRunnerOrExtension')
+        writeClassUsingBrokenRunnerOrExtension('ClassWithBrokenRunnerOrExtension', 'BrokenRunnerOrExtension')
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        executer.withTasks('build').runWithFailure().assertTestsFailed()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted(
+            'org.gradle.ClassWithBrokenRunnerOrExtension',
+            'org.gradle.CustomException',
+            'org.gradle.BrokenTest',
+            'org.gradle.BrokenBefore',
+            'org.gradle.BrokenAfter',
+            'org.gradle.BrokenBeforeClass',
+            'org.gradle.BrokenAfterClass',
+            'org.gradle.BrokenBeforeAndAfter',
+            'org.gradle.BrokenConstructor',
+            'org.gradle.BrokenException',
+            'org.gradle.Unloadable',
+            'org.gradle.UnserializableException')
+        result.testClass('org.gradle.ClassWithBrokenRunnerOrExtension').assertTestFailed(initializationErrorTestName, equalTo('java.lang.UnsupportedOperationException: broken'))
+        result.testClass('org.gradle.BrokenTest')
+            .assertTestCount(2, 2, 0)
+            .assertTestFailed('failure', equalTo(failureAssertionError('failed')))
+            .assertTestFailed('broken', equalTo('java.lang.IllegalStateException: html: <> cdata: ]]>'))
+        result.testClass('org.gradle.BrokenBeforeClass').assertTestFailed(beforeClassErrorTestName, equalTo(failureAssertionError('failed')))
+        result.testClass('org.gradle.BrokenAfterClass').assertTestFailed(afterClassErrorTestName, equalTo(failureAssertionError('failed')))
+        result.testClass('org.gradle.BrokenBefore').assertTestFailed('ok', equalTo(failureAssertionError('failed')))
+        result.testClass('org.gradle.BrokenAfter').assertTestFailed('ok', equalTo(failureAssertionError('failed')))
+        result.testClass('org.gradle.BrokenBeforeAndAfter').assertTestFailed('ok', brokenBeforeAndAfterMatchers)
+        result.testClass('org.gradle.BrokenConstructor').assertTestFailed('ok', equalTo(failureAssertionError('failed')))
+        result.testClass('org.gradle.BrokenException').assertTestFailed('broken', startsWith('Could not determine failure message for exception of type org.gradle.BrokenException$BrokenRuntimeException: java.lang.UnsupportedOperationException'))
+        result.testClass('org.gradle.CustomException').assertTestFailed('custom', startsWith('Exception with a custom toString implementation'))
+        result.testClass('org.gradle.Unloadable').assertTestFailed('ok', equalTo(failureAssertionError('failed')))
+        result.testClass('org.gradle.Unloadable').assertTestFailed('ok2', startsWith('java.lang.NoClassDefFoundError'))
+        result.testClass('org.gradle.UnserializableException').assertTestFailed('unserialized', equalTo('org.gradle.UnserializableException$UnserializableRuntimeException: whatever'))
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/23602")
+    def "handles unserializable exception thrown from test"() {
+        given:
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """
+
+        file('src/test/java/PoisonTest.java') << """
+            ${testFrameworkImports}
+
+            public class PoisonTest {
+                @Test public void passingTest() { }
+
+                @Test public void testWithUnserializableException() {
+                    if (true) {
+                        throw new UnserializableException();
+                    }
+                }
+
+                @Test public void normalFailingTest() {
+                    assert false;
+                }
+
+                private static class WriteReplacer implements java.io.Serializable {
+                    private Object readResolve() {
+                        return new RuntimeException();
+                    }
+                }
+
+                private static class UnserializableException extends RuntimeException {
+                    private Object writeReplace() {
+                        return new WriteReplacer();
+                    }
+                }
+            }
+        """
+
+        when:
+        fails("test")
+
+        then:
+        with(new DefaultTestExecutionResult(testDirectory).testClass("PoisonTest")) {
+            assertTestPassed("passingTest")
+            assertTestFailed("testWithUnserializableException", containsString("TestFailureSerializationException: An exception of type PoisonTest\$UnserializableException was thrown by the test, but Gradle was unable to recreate the exception in the build process"))
+            assertTestFailed("normalFailingTest", containsString("AssertionError"))
+        }
+    }
+
+    def "fails cleanly even if an exception is thrown that doesn't serialize cleanly"() {
+        given:
+        file('src/test/java/ExceptionTest.java') << """
+            ${testFrameworkImports}
+            import java.io.*;
+
+            public class ExceptionTest {
+
+                static class BadlyBehavedException extends Exception {
+                    BadlyBehavedException() {
+                        super("Broken writeObject()");
+                    }
+
+                    private void writeObject(ObjectOutputStream os) throws IOException {
+                        throw new IOException("Failed strangely");
+                    }
+                }
+
+                @Test
+                public void testThrow() throws Throwable {
+                    throw new BadlyBehavedException();
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        runAndFail "test"
+
+        then:
+        failureHasCause "There were failing tests"
+
+        and:
+        def results = new DefaultTestExecutionResult(file("."))
+        results.assertTestClassesExecuted("ExceptionTest")
+        results.testClass("ExceptionTest").assertTestFailed("testThrow", equalTo('ExceptionTest$BadlyBehavedException: Broken writeObject()'))
+    }
+
+    def "fails cleanly even if an exception is thrown that doesn't de-serialize cleanly"() {
+        given:
+
+        file('src/test/java/ExceptionTest.java') << """
+            ${testFrameworkImports}
+            import java.io.*;
+
+            public class ExceptionTest {
+                static class BadlyBehavedException extends Exception {
+                    BadlyBehavedException() {
+                        super("Broken readObject()");
+                    }
+
+                    private void readObject(ObjectInputStream os) throws IOException {
+                        throw new IOException("Failed strangely");
+                    }
+                }
+
+                @Test
+                public void testThrow() throws Throwable {
+                    throw new BadlyBehavedException();
+                }
+            }
+        """.stripIndent()
+        file('build.gradle') << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        // an exception was thrown so we should fail here
+        runAndFail "test"
+
+        then:
+        failureHasCause "There were failing tests"
+
+        and:
+        def results = new DefaultTestExecutionResult(file("."))
+        results.assertTestClassesExecuted("ExceptionTest")
+        results.testClass("ExceptionTest").assertTestFailed("testThrow", equalTo('ExceptionTest$BadlyBehavedException: Broken readObject()'))
+    }
+
+    @Requires([UnitTestPreconditions.Jdk14OrLater, IntegTestPreconditions.NotEmbeddedExecutor])
+    def "useful NPE messages are transported to the daemon"() {
+        buildFile << """
+            apply plugin:'java-library'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+
+            test {
+                ${configureTestFramework}
+                jvmArgs("-XX:+ShowCodeDetailsInExceptionMessages")
+            }
+        """.stripIndent()
+
+        file('src/test/java/UsefulNPETest.java') << """
+            ${testFrameworkImports}
+
+            public class UsefulNPETest {
+                @Test
+                public void testUsefulNPE() {
+                    Object o = null;
+                    o.toString();
+                }
+
+                @Test
+                public void testDeepUsefulNPE() {
+                    other(null);
+                }
+
+                @Test
+                public void testFailingGetMessage() {
+                    throw new NullPointerException() {
+                        public String getMessage() {
+                            throw new RuntimeException();
+                        }
+                    };
+                }
+
+                void other(Object param) {
+                    try {
+                       System.out.println(param.toString());
+                    } catch (NullPointerException ex) {
+                        throw new RuntimeException(ex);
+                    }
+                }
+
+            }
+        """.stripIndent()
+
+        when:
+        fails 'test'
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass("UsefulNPETest")
+            .testFailed("testUsefulNPE", equalTo('java.lang.NullPointerException: Cannot invoke "Object.toString()" because "o" is null'))
+        result.testClass("UsefulNPETest")
+            .testFailed("testDeepUsefulNPE", equalTo('java.lang.RuntimeException: java.lang.NullPointerException: Cannot invoke "Object.toString()" because "param" is null'))
+        result.testClass("UsefulNPETest")
+            .testFailed("testFailingGetMessage", equalTo('Could not determine failure message for exception of type UsefulNPETest$1: java.lang.RuntimeException'))
+    }
+
+    String failureAssertionError(String message) {
+        return "${assertionFailureClassName}: ${message}".toString()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestListenerIntegrationTest.groovy
new file mode 100644
index 0000000..c4a446a
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractJUnitTestListenerIntegrationTest.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 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.testing.junit
+
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+import static org.gradle.util.Matchers.containsLine
+import static org.gradle.util.Matchers.matchesRegexp
+
+abstract class AbstractJUnitTestListenerIntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+    abstract String getAssertionError()
+
+    def "can listen for test results"() {
+        given:
+        file('src/main/java/AppException.java') << """
+            public class AppException extends Exception { }
+        """.stripIndent()
+        file('src/test/java/SomeTest.java') << """
+            ${testFrameworkImports}
+
+            public class SomeTest {
+                @Test public void failing() { fail(\"message\"); }
+                @Test public void knownError() { throw new RuntimeException(\"message\"); }
+                @Test public void unknownError() throws AppException { throw new AppException(); }
+            }
+        """.stripIndent()
+        file('src/test/java/SomeOtherTest.java') << """
+            ${testFrameworkImports}
+
+            public class SomeOtherTest {
+                @Test public void pass() { }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+
+            def listener = new TestListenerImpl()
+            test {
+                ${configureTestFramework}
+                addTestListener(listener)
+                ignoreFailures = true
+            }
+
+            class TestListenerImpl implements TestListener {
+                void beforeSuite(TestDescriptor suite) { println "START [\$suite] [\$suite.name]" }
+                void afterSuite(TestDescriptor suite, TestResult result) { println "FINISH [\$suite] [\$suite.name] [\$result.resultType] [\$result.testCount]" }
+                void beforeTest(TestDescriptor test) { println "START [\$test] [\$test.name]" }
+                void afterTest(TestDescriptor test, TestResult result) { println "FINISH [\$test] [\$test.name] [\$result.resultType] [\$result.testCount] [\$result.exception]" }
+            }
+        """.stripIndent()
+
+        when:
+        def result = executer.withTasks("test").run()
+
+        then:
+        containsLine(result.getOutput(), "START [Gradle Test Run :test] [Gradle Test Run :test]")
+        containsLine(result.getOutput(), "FINISH [Gradle Test Run :test] [Gradle Test Run :test] [FAILURE] [4]")
+
+        containsLine(result.getOutput(), matchesRegexp("START \\[Gradle Test Executor \\d+\\] \\[Gradle Test Executor \\d+\\]"))
+        containsLine(result.getOutput(), matchesRegexp("FINISH \\[Gradle Test Executor \\d+\\] \\[Gradle Test Executor \\d+\\] \\[FAILURE\\] \\[4\\]"))
+
+        containsLine(result.getOutput(), "START [Test class SomeOtherTest] [SomeOtherTest]")
+        containsLine(result.getOutput(), "FINISH [Test class SomeOtherTest] [SomeOtherTest] [SUCCESS] [1]")
+        containsLine(result.getOutput(), "START [Test ${maybeParentheses('pass')}(SomeOtherTest)] [${maybeParentheses('pass')}]")
+        containsLine(result.getOutput(), "FINISH [Test ${maybeParentheses('pass')}(SomeOtherTest)] [${maybeParentheses('pass')}] [SUCCESS] [1] [null]")
+
+        containsLine(result.getOutput(), "START [Test class SomeTest] [SomeTest]")
+        containsLine(result.getOutput(), "FINISH [Test class SomeTest] [SomeTest] [FAILURE] [3]")
+        containsLine(result.getOutput(), "START [Test ${maybeParentheses('failing')}(SomeTest)] [${maybeParentheses('failing')}]")
+        containsLine(result.getOutput(), "FINISH [Test ${maybeParentheses('failing')}(SomeTest)] [${maybeParentheses('failing')}] [FAILURE] [1] [${assertionError}: message]")
+        containsLine(result.getOutput(), "START [Test ${maybeParentheses('knownError')}(SomeTest)] [${maybeParentheses('knownError')}]")
+        containsLine(result.getOutput(), "FINISH [Test ${maybeParentheses('knownError')}(SomeTest)] [${maybeParentheses('knownError')}] [FAILURE] [1] [java.lang.RuntimeException: message]")
+        containsLine(result.getOutput(), "START [Test ${maybeParentheses('unknownError')}(SomeTest)] [${maybeParentheses('unknownError')}]")
+        containsLine(result.getOutput(), "FINISH [Test ${maybeParentheses('unknownError')}(SomeTest)] [${maybeParentheses('unknownError')}] [FAILURE] [1] [AppException]")
+
+        when:
+        testDirectory.file('src/test/java/SomeOtherTest.java').delete()
+        result = executer.withTasks("test").run()
+
+        then:
+        result.assertNotOutput("SomeOtherTest")
+        containsLine(result.getOutput(), "START [Test class SomeTest] [SomeTest]")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractSpecs2IntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractSpecs2IntegrationTest.groovy
new file mode 100644
index 0000000..0084028
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/AbstractSpecs2IntegrationTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 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.testing.junit
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+abstract class AbstractSpecs2IntegrationTest extends AbstractTestingMultiVersionIntegrationTest {
+
+    def 'can run Specs2 tests'() {
+        given:
+        buildFile << """
+            plugins {
+                id("scala")
+            }
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                implementation 'org.scala-lang:scala-library:2.11.8'
+                testImplementation 'org.specs2:specs2_2.11:3.7'
+                testImplementation 'org.specs2:specs2-junit_2.11:4.7.0'
+                ${testFrameworkDependencies}
+            }
+        """
+        file('src/test/scala/BasicSpec.scala') << '''
+            import org.junit.runner.RunWith
+            import org.specs2.runner.JUnitRunner
+            import org.specs2.mutable.Specification
+
+            @RunWith(classOf[JUnitRunner])
+            class BasicSpec extends Specification {
+              "Basic Math" >> {
+                (1 + 1) mustEqual 2
+              }
+            }
+        '''
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClass("BasicSpec").assertTestCount(1, 0, 0)
+            .assertTestPassed('Basic Math')
+    }
+
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/BuildSrcSpockIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/BuildSrcSpockIntegrationTest.groovy
deleted file mode 100644
index c0fb5bf..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/BuildSrcSpockIntegrationTest.groovy
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright 2016 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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUPITER
-
-@TargetCoverage({ [JUPITER] })
-class BuildSrcSpockIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    def "can run spock tests with mock of class using gradleApi"() {
-        file("build.gradle") << """
-            plugins {
-                id("groovy")
-            }
-
-            ${mavenCentralRepository()}
-
-            dependencies {
-                implementation gradleApi()
-                implementation localGroovy()
-            }
-
-            testing {
-                suites {
-                    // Must explicitly use `named` to avoid being rewritten by JUnitPlatformTestRewriter.rewriteBuildFile
-                    named('test') {
-                        useSpock()
-                        dependencies {
-                            ${dependencyNotation.collect { "implementation '$it'" }.join('\n')}
-                            // Required to use Spock mocking
-                            runtimeOnly 'net.bytebuddy:byte-buddy:1.12.17'
-                        }
-                    }
-                }
-            }
-        """
-        file("src/main/groovy/MockIt.groovy") << """
-            class MockIt {
-                void call() {
-                }
-            }
-        """
-
-        file("src/main/groovy/Caller.groovy") << """
-            class Caller {
-                private MockIt callable
-
-                Caller(MockIt callable) {
-                    this.callable = callable
-                }
-
-                void call() {
-                   callable.call()
-                }
-            }
-        """
-        file("src/test/groovy/TestSpec.groovy") << """
-            import spock.lang.Specification
-
-            class TestSpec extends Specification {
-                def testMethod() {
-                    final callable = Mock(MockIt)
-                    def caller = new Caller(callable)
-                    when:
-                    caller.call()
-                    then:
-                    1 * callable.call()
-                    0 * _
-                }
-            }
-        """
-        expect:
-        succeeds("test")
-    }
-
-    private void writeSpockDependencies() {
-        file("build.gradle") << """
-            apply plugin: 'groovy'
-
-            ${mavenCentralRepository()}
-
-            testing {
-                suites {
-                    // Must explicitly use `named` to avoid being rewritten by JUnitPlatformTestRewriter.rewriteBuildFile
-                    named('test') {
-                        useSpock()
-                        dependencies {
-                            implementation localGroovy()
-                            ${dependencyNotation.collect { "implementation '$it'" }.join('\n')}
-                        }
-                    }
-                }
-            }
-        """
-    }
-
-    def 'can run spock with @Unroll'() {
-        given:
-        writeSpockDependencies()
-        file('src/test/groovy/UnrollTest.groovy') << '''
-            import spock.lang.Specification
-            import spock.lang.Unroll
-
-            class UnrollTest extends Specification {
-                @Unroll
-                def "can test #type"() {
-                    expect: type
-
-                    where:
-                    type << ['1', '2']
-                }
-            }
-        '''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClass("UnrollTest").assertTestCount(2, 0, 0)
-            .assertTestPassed('can test 1')
-            .assertTestPassed('can test 2')
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/4358')
-    def 'can run spock test with same method name in super class and base class'() {
-        given:
-        writeSpockDependencies()
-        file('src/test/groovy/Base.groovy') << '''
-            import spock.lang.Specification
-
-            abstract class Base extends Specification {
-                def ok() {
-                    expect: "success"
-                }
-            }
-
-            class Sub extends Base {
-                def ok() {
-                    expect: "success"
-                }
-            }
-        '''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClass("Sub").assertTestCount(2, 0, 0)
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnit3FilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnit3FilteringIntegrationTest.groovy
deleted file mode 100644
index 0a659cf..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnit3FilteringIntegrationTest.groovy
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2013 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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-
-import static org.gradle.testing.fixture.JUnitCoverage.FILTER_JUNIT3_TESTS
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@TargetCoverage({ FILTER_JUNIT3_TESTS + JUNIT_VINTAGE })
-class JUnit3FilteringIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    void "filters tests implemented using 3.x test cases"() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')} }
-        """
-
-        file("src/test/java/FooTest.java") << """
-            import junit.framework.*;
-
-            public class FooTest extends TestCase {
-                public void testPass() {}
-                public void testOk() {}
-            }
-        """
-
-        when:
-        succeeds("test", "--tests", "FooTest.testPass")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("FooTest")
-        result.testClass("FooTest").assertTestsExecuted("testPass")
-
-        when:
-        fails("test", "--tests", "FooTest.unknown")
-
-        then:
-        failure.assertHasCause("No tests found for given includes: [FooTest.unknown]")
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest.groovy
deleted file mode 100644
index 7deb3b2..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest.groovy
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2019 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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@TargetCoverage({ JUNIT_VINTAGE })
-class JUnitAbortedTestClassIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-
-    // Note: there're some behavior changes in 4.13:
-    // https://github.com/junit-team/junit4/issues/1066
-    // So this test was adjusted accordingly.
-    def supportsAssumptionsInRules() {
-        given:
-        executer.noExtraLogging()
-        buildFile << """
-dependencies {
-    ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')}
-}
-"""
-
-        when:
-        run('test')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.SkippingRuleTests')
-        result.testClass('org.gradle.SkippingRuleTests')
-            .assertTestCount(3, 0, 0)
-            .assertTestsExecuted('a', 'c')
-            .assertTestsSkipped('b')
-    }
-
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest.groovy
deleted file mode 100644
index 0337ae5..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest.groovy
+++ /dev/null
@@ -1,53 +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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.ASSUMPTIONS
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@TargetCoverage({ ASSUMPTIONS + JUNIT_VINTAGE })
-class JUnitAssumptionsIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-
-    def supportsAssumptions() {
-        executer.noExtraLogging()
-        buildFile << """
-dependencies {
-    ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')}
-}
-"""
-
-        when:
-        run('check')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.TestWithAssumptions')
-        result.testClass('org.gradle.TestWithAssumptions')
-                .assertTestCount(2, 0, 0)
-                .assertTestsExecuted('assumptionSucceeded')
-                .assertTestPassed('assumptionSucceeded')
-                .assertTestsSkipped('assumptionFailed')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec.groovy
deleted file mode 100644
index a2eb2516..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec.groovy
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2013 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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.CATEGORIES
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@TargetCoverage({ CATEGORIES + JUNIT_VINTAGE })
-class JUnitCategoriesCoverageIntegrationSpec extends JUnitMultiVersionIntegrationSpec {
-
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-
-    def setup() {
-        executer.noExtraLogging()
-        buildFile << "dependencies { ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')} }"
-    }
-
-    def canSpecifyIncludeAndExcludeCategories() {
-        when:
-        run('test')
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.CatATests', 'org.gradle.CatBTests', 'org.gradle.CatADTests', 'org.gradle.MixedTests')
-        result.testClass("org.gradle.CatATests").assertTestCount(4, 0, 0)
-        result.testClass("org.gradle.CatATests").assertTestsExecuted('catAOk1', 'catAOk2', 'catAOk3', 'catAOk4')
-        result.testClass("org.gradle.CatBTests").assertTestCount(4, 0, 0)
-        result.testClass("org.gradle.CatBTests").assertTestsExecuted('catBOk1', 'catBOk2', 'catBOk3', 'catBOk4')
-        result.testClass("org.gradle.CatADTests").assertTestCount(2, 0, 0)
-        result.testClass("org.gradle.CatADTests").assertTestsExecuted('catAOk1', 'catAOk2')
-        result.testClass("org.gradle.MixedTests").assertTestCount(3, 0, 0)
-        result.testClass("org.gradle.MixedTests").assertTestsExecuted('catAOk1', 'catBOk2')
-        result.testClass("org.gradle.MixedTests").assertTestsSkipped('someIgnoredTest')
-    }
-
-    def canSpecifyExcludesOnly() {
-        when:
-        run('test')
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.NoCatTests', 'org.gradle.SomeTests', 'org.gradle.SomeOtherCatTests')
-        result.testClass("org.gradle.SomeOtherCatTests").assertTestCount(2, 0, 0)
-        result.testClass("org.gradle.SomeOtherCatTests").assertTestsExecuted('someOtherOk1', 'someOtherOk2')
-        result.testClass("org.gradle.NoCatTests").assertTestCount(2, 0, 0)
-        result.testClass("org.gradle.NoCatTests").assertTestsExecuted('noCatOk1', 'noCatOk2')
-        result.testClass("org.gradle.SomeTests").assertTestCount(3, 0, 0)
-        result.testClass("org.gradle.SomeTests").assertTestsExecuted('noCatOk3', 'noCatOk4', 'someOtherCatOk2')
-    }
-
-    def canCombineCategoriesWithCustomRunner() {
-        when:
-        run('test')
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.SomeLocaleTests')
-        result.testClass("org.gradle.SomeLocaleTests").assertTestCount(3, 0, 0)
-        result.testClass("org.gradle.SomeLocaleTests").assertTestsExecuted('ok1 [de]', 'ok1 [en]', 'ok1 [fr]')
-    }
-
-    def canRunParameterizedTestsWithCategories() {
-        when:
-        run('test')
-
-        then:
-        def expectedTestClasses = ['org.gradle.NestedTestsWithCategories$TagOnMethodNoParam', 'org.gradle.NestedTestsWithCategories$TagOnMethod']
-        if (isVintage() || !(version in ['4.10', '4.11', '4.12'])) {
-            expectedTestClasses << 'org.gradle.NestedTestsWithCategories$TagOnClass'
-        }
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted(expectedTestClasses as String[])
-        expectedTestClasses.each {
-            result.testClass(it).assertTestCount(1, 0, 0)
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec.groovy
deleted file mode 100644
index 4f0744c..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec.groovy
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright 2013 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.testing.junit
-
-import org.gradle.integtests.fixtures.AbstractSampleIntegrationTest
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-import spock.lang.Issue
-
-import static org.hamcrest.CoreMatchers.startsWith
-
-class JUnitCategoriesIntegrationSpec extends AbstractSampleIntegrationTest {
-
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-
-    def 'reports unloadable #type'() {
-        given:
-        resources.maybeCopy("JUnitCategoriesIntegrationSpec/reportsUnloadableCategories")
-        buildFile << "test.useJUnit { ${type} 'org.gradle.CategoryA' }"
-
-        when:
-        fails("test")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.SomeTestClass')
-        result.testClass("org.gradle.SomeTestClass").assertTestCount(1, 1, 0)
-        result.testClass("org.gradle.SomeTestClass").assertTestFailed("initializationError", startsWith("org.gradle.api.InvalidUserDataException: Can't load category class [org.gradle.CategoryA]"))
-
-        where:
-        type << ['includeCategories', 'excludeCategories']
-    }
-
-    def testTaskFailsIfCategoriesNotSupported() {
-        when:
-        fails('test')
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass("org.gradle.SomeTest").assertTestFailed("initializationError", startsWith("org.gradle.api.GradleException: JUnit Categories defined but declared JUnit version does not support Categories."))
-    }
-
-    def supportsCategoriesAndNullTestClassDescription() {
-        when:
-        succeeds("test")
-
-        then:
-        executedAndNotSkipped(":test")
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        def testClass = result.testClass("Not a real class name")
-        testClass.assertTestCount(1, 0, 0)
-        testClass.assertTestPassed("someTest")
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/3189')
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
-    def canWorkWithPowerMock() {
-        given:
-        buildFile << """
-apply plugin: 'java'
-
-${mavenCentralRepository()}
-
-dependencies {
-    testImplementation "junit:junit:4.13"
-    testImplementation "org.powermock:powermock-api-mockito:1.6.5"
-    testImplementation "org.powermock:powermock-module-junit4:1.6.5"
-}
-
-test {
-    useJUnit { includeCategories 'FastTest'  }
-}
-"""
-        file('src/test/java/FastTest.java') << '''
-public interface FastTest {
-}
-'''
-        file('src/test/java/MyTest.java') << '''
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.powermock.modules.junit4.PowerMockRunner;
-@RunWith(PowerMockRunner.class)
-@Category(FastTest.class)
-public class MyTest {
-    @Test
-    public void testMyMethod() {
-        assertTrue("This is an error", false);
-    }
-}
-'''
-        when:
-        fails('test')
-
-        then:
-        outputContains('MyTest > testMyMethod FAILED')
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/4924')
-    def "re-executes test when options are changed in #suiteName"() {
-        given:
-        resources.maybeCopy("JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged")
-        buildFile << """
-        |testing {
-        |   suites {
-        |       $suiteDeclaration {
-        |           useJUnit()
-        |           targets {
-        |               all {
-        |                   testTask.configure {
-        |                       options {
-        |                           includeCategories 'org.gradle.CategoryA'
-        |                       }
-        |                   }
-        |               }
-        |           }
-        |       }
-        |   }
-        |}""".stripMargin()
-
-        when:
-        succeeds ":$task"
-
-        then:
-        executedAndNotSkipped ":$task"
-
-        when:
-        resources.maybeCopy("JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged")
-        buildFile << """
-        |testing {
-        |   suites {
-        |       $suiteDeclaration {
-        |           useJUnit()
-        |           targets {
-        |               all {
-        |                   testTask.configure {
-        |                       options {
-        |                           includeCategories 'org.gradle.CategoryB'
-        |                       }
-        |                   }
-        |               }
-        |           }
-        |       }
-        |   }
-        |}""".stripMargin()
-
-        and:
-        succeeds ":$task"
-
-        then:
-        executedAndNotSkipped ":$task"
-
-        where:
-        suiteName   | suiteDeclaration              | task
-        'test'      | 'test'                        | 'test'
-        'integTest' | 'integTest(JvmTestSuite)'     | 'integTest'
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/4924')
-    def "skips test on re-run when options are NOT changed"() {
-        given:
-        resources.maybeCopy("JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged")
-        buildFile << """
-        |testing {
-        |   suites {
-        |       test {
-        |           useJUnit()
-        |           targets {
-        |               all {
-        |                   testTask.configure {
-        |                       options {
-        |                           includeCategories 'org.gradle.CategoryA'
-        |                       }
-        |                   }
-        |               }
-        |           }
-        |       }
-        |   }
-        |}""".stripMargin()
-
-        when:
-        succeeds ':test'
-
-        then:
-        executedAndNotSkipped ':test'
-
-        when:
-        succeeds ':test'
-
-        then:
-        skipped ':test'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassDetectionIntegrationTest.groovy
deleted file mode 100644
index 2181b51..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassDetectionIntegrationTest.groovy
+++ /dev/null
@@ -1,88 +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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE })
-class JUnitClassDetectionIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')} }
-        """
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/18465")
-    def 'does not try to execute non-test class as test class'() {
-        given:
-        file('src/test/java/com/example/MyTest.java') << '''
-package com.example;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class MyTest {
-    @Test public void someTest() {
-        assertTrue(true);
-    }
-}
-'''
-        file('src/test/java/com/example/CacheSpec.java') << '''
-package com.example;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ForkJoinPool;
-
-@SuppressWarnings("ImmutableEnumChecker")
-@Target(METHOD) @Retention(RUNTIME)
-public @interface CacheSpec {
-
-  enum CacheExecutor {
-    DEFAULT {
-      @Override public Executor create() {
-        return ForkJoinPool.commonPool();
-      }
-    },
-    DIRECT {
-      @Override public Executor create() {
-        return Runnable::run;
-      }
-    };
-
-    public abstract Executor create();
-  }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory).testClass('com.example.MyTest').assertTestCount(1, 0, 0)
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassInJarDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassInJarDetectionIntegrationTest.groovy
deleted file mode 100644
index 826b550..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassInJarDetectionIntegrationTest.groovy
+++ /dev/null
@@ -1,165 +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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import spock.lang.Issue
-
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
-import java.util.zip.ZipOutputStream
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@Issue("https://github.com/gradle/gradle/issues/18486")
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE })
-class JUnitClassInJarDetectionIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')} }
-        """.stripIndent()
-    }
-
-    def jarName = "testFramework.jar"
-    def jarNameNew = "anotherTestFramework.jar"
-    def jar = new File(testDirectory, "build/libs/$jarName")
-    def jarNew = new File(testDirectory, "build/libs/$jarNameNew")
-
-    def 'support detecting classes whose package is not a zip entry'() {
-        given:
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies {
-                implementation "junit:junit:3.8"
-            }
-            jar {
-                manifest {
-                    attributes 'Manifest-Version': '1.0'
-                }
-                archiveBaseName = 'testFramework'
-            }
-        """
-
-        file("src/main/java/org/gradle/BasePlatformTestCase.java") << """
-            package org.gradle;
-            import junit.framework.TestCase;
-            public class BasePlatformTestCase extends TestCase { }
-        """
-
-        when:
-        succeeds('build')
-
-        then:
-        Set<String> entries = ["org/", "org/gradle/", "org/gradle/BasePlatformTestCase.class"]
-        assertJarContainsAllEntries(jar, entries)
-        buildFile << """
-            apply plugin: 'java'
-            repositories { mavenCentral() }
-            dependencies {
-                testImplementation files('build/libs/testFramework.jar')
-                testImplementation "junit:junit:3.8"
-            }
-        """
-        file('src/test/java/SomeTest.java') << """
-            package com.example;
-            import org.gradle.BasePlatformTestCase;
-            public class SomeTest extends BasePlatformTestCase {
-                public void testPass() { }
-            }
-        """
-
-        when:
-        succeeds("test", "--tests", "SomeTest")
-        then:
-        new DefaultTestExecutionResult(testDirectory).testClass('com.example.SomeTest').assertTestCount(1, 0, 0)
-        new DefaultTestExecutionResult(testDirectory).assertTestClassesExecuted('com.example.SomeTest')
-
-        when:
-        createNewJarWithoutPackageEntries(jar, jarNew)
-        String entry = "org/gradle/BasePlatformTestCase.class"
-        assertJarContainsOnlyOneEntry(jarNew, entry)
-        testDirectory.file("build.gradle").replace(jarName, jarNameNew)
-        succeeds("test", "--tests", "SomeTest")
-        then:
-        new DefaultTestExecutionResult(testDirectory).testClass('com.example.SomeTest').assertTestCount(1, 0, 0)
-        new DefaultTestExecutionResult(testDirectory).assertTestClassesExecuted('com.example.SomeTest')
-    }
-
-    private static void assertJarContainsAllEntries(final File jar, final Set<String> entries) {
-        ZipFile originalJar = new ZipFile(jar)
-
-        originalJar.withCloseable {
-            Enumeration<? extends ZipEntry> enumeration = originalJar.entries()
-            while (enumeration.hasMoreElements()) {
-                ZipEntry entry = enumeration.nextElement()
-                String name = entry.getName()
-                if (entries.contains(name)) {
-                    entries.remove(name)
-                }
-            }
-        }
-
-        assert entries.isEmpty()
-    }
-
-    private static void assertJarContainsOnlyOneEntry(final File jar, final String entry) {
-        ZipFile originalJar = new ZipFile(jar)
-
-        originalJar.withCloseable {
-            Enumeration<? extends ZipEntry> enumeration = originalJar.entries()
-            assert enumeration.hasMoreElements()
-            String name = enumeration.nextElement().name
-            assert name == entry
-            assert !enumeration.hasMoreElements()
-        }
-    }
-
-    private static void createNewJarWithoutPackageEntries(final File jar, final File jarNew) {
-        ZipFile originalJar = new ZipFile(jar)
-        originalJar.withCloseable {
-            Enumeration<? extends ZipEntry> entries = originalJar.entries()
-            while (entries.hasMoreElements()) {
-                ZipEntry entry = entries.nextElement()
-                if (entry.getName().contains("BasePlatformTestCase")) {
-                    ZipEntry newEntry = new ZipEntry(entry.getName())
-                    ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(jarNew))
-                    outputStream.withCloseable { output ->
-                        output.putNextEntry(newEntry)
-                        InputStream inputStream = originalJar.getInputStream(entry)
-                        inputStream.withCloseable { input ->
-                            byte[] buffer = new byte[512]
-                            while (input.available() > 0) {
-                                int read = input.read(buffer)
-                                if (read > 0) {
-                                    output.write(buffer, 0, read)
-                                }
-                            }
-                        }
-                    }
-                    return
-                }
-            }
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassLevelFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassLevelFilteringIntegrationTest.groovy
deleted file mode 100644
index 93935e3..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitClassLevelFilteringIntegrationTest.groovy
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2015 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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-import static org.gradle.testing.fixture.JUnitCoverage.LARGE_COVERAGE
-
-@TargetCoverage({ LARGE_COVERAGE + JUNIT_VINTAGE})
-class JUnitClassLevelFilteringIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')} }
-            test { use${testFramework}() }
-        """
-    }
-
-    def "runs all tests for class instead of method when runner is not filterable"() {
-        file("src/test/java/FooTest.java") << """
-            import org.junit.*;
-            import org.junit.runner.*;
-            @RunWith(SomeRunner.class)
-            public class FooTest {
-            }
-        """
-        file("src/test/java/SomeRunner.java") << """
-            import org.junit.*;
-            import org.junit.runner.*;
-            import org.junit.runner.notification.*;
-            public class SomeRunner extends Runner {
-                Class<?> c;
-
-                public SomeRunner(Class<?> c) {
-                    this.c = c;
-                }
-
-                public Description getDescription() {
-                    Description suite = Description.createSuiteDescription(c.getName());
-                    suite.addChild(Description.createTestDescription(c, "pass"));
-                    suite.addChild(Description.createTestDescription(c, "other"));
-                    return suite;
-                }
-
-                public void run(RunNotifier notifier) {
-                    for(Description d: getDescription().getChildren()) {
-                        notifier.fireTestStarted(d);
-                        notifier.fireTestFinished(d);
-                    }
-                }
-            }
-        """
-
-        when:
-        run("test", "--tests", "FooTest.pass")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("FooTest")
-        result.testClass("FooTest").assertTestsExecuted("other", "pass")
-
-        when:
-        fails("test", "--tests", "FooTest.ignored")
-
-        then:
-        failure.assertHasCause("No tests found for given includes: [FooTest.ignored]")
-
-        when:
-        fails("test", "--tests", "NotFooTest.pass")
-
-        then:
-        failure.assertHasCause("No tests found for given includes: [NotFooTest.pass]")
-    }
-
-    def "can filter tests from a superclass"() {
-        given:
-        file('src/test/java/SuperClass.java') << '''
-            import org.junit.Test;
-
-            public abstract class SuperClass {
-                @Test
-                public void superTest() {
-                }
-            }
-        '''
-        file('src/test/java/SubClass.java') << '''
-            import org.junit.Test;
-
-            public class SubClass extends SuperClass {
-                @Test
-                public void subTest() {
-                }
-            }
-        '''
-
-        when:
-        succeeds('test', '--tests', 'SubClass.superTest')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted('SubClass')
-            .testClass('SubClass')
-            .assertTestCount(1, 0, 0)
-            .assertTestPassed('superTest')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest.groovy
deleted file mode 100644
index 9ecb7d2..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest.groovy
+++ /dev/null
@@ -1,123 +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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
-import static org.hamcrest.CoreMatchers.equalTo
-
-// cannot make assumptions about order in which test methods of JUnit4Test get executed
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class JUnitConsoleLoggingIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-
-    def setup() {
-        executer.noExtraLogging()
-    }
-
-    def "defaultLifecycleLogging"() {
-        when:
-        fails("test")
-
-        then:
-        outputContains("""
-${classNamePrefix}JUnit4Test > badTest FAILED
-    java.lang.RuntimeException at JUnit4Test.groovy:44
-""")
-    }
-
-    def "customQuietLogging"() {
-        when:
-        executer.withStackTraceChecksDisabled()
-        args("-q")
-        fails("test")
-
-        then:
-        outputContains("""
-badTest FAILED
-    java.lang.RuntimeException: bad
-        at org.gradle.JUnit4Test.beBad(JUnit4Test.groovy:44)
-        at org.gradle.JUnit4Test.badTest(JUnit4Test.groovy:28)
-""")
-
-        outputContains("ignoredTest SKIPPED")
-
-        outputContains("${classNamePrefix}JUnit4Test FAILED")
-    }
-
-    def "standardOutputLogging"() {
-        when:
-        args("-q")
-        fails("test")
-
-        then:
-        outputContains("""
-${classNamePrefix}JUnit4StandardOutputTest > printTest STANDARD_OUT
-    line 1
-    line 2
-    line 3
-""")
-    }
-
-    def "test logging is included in XML results"() {
-        file("build.gradle") << """
-            apply plugin: 'java'
-                ${mavenCentralRepository()}
-                dependencies { testImplementation 'junit:junit:4.13' }
-        """
-
-        file("src/test/java/EncodingTest.java") << """
-import org.junit.*;
-
-public class EncodingTest {
-    @Test public void encodesCdata() {
-        System.out.println("< html allowed, cdata closing token ]]> encoded!");
-        System.out.print("no EOL, ");
-        System.out.println("non-asci char: ż");
-        System.out.println("xml entity: &amp;");
-        System.err.println("< html allowed, cdata closing token ]]> encoded!");
-    }
-    @Test public void encodesAttributeValues() {
-        throw new RuntimeException("html: <> cdata: ]]>");
-    }
-}
-"""
-        when:
-        fails("test")
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-                .testClass("EncodingTest")
-                .assertTestPassed("encodesCdata")
-                .assertTestFailed("encodesAttributeValues", equalTo('java.lang.RuntimeException: html: <> cdata: ]]>'))
-                .assertStdout(equalTo("""< html allowed, cdata closing token ]]> encoded!
-no EOL, non-asci char: ż
-xml entity: &amp;
-"""))
-                .assertStderr(equalTo("< html allowed, cdata closing token ]]> encoded!\n"))
-    }
-
-    private String getClassNamePrefix() {
-        isJUnitPlatform() ? '' : 'org.gradle.'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitEnclosedRunnerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitEnclosedRunnerIntegrationTest.groovy
deleted file mode 100644
index 8220f97..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitEnclosedRunnerIntegrationTest.groovy
+++ /dev/null
@@ -1,144 +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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE })
-class JUnitEnclosedRunnerIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')} }
-        """.stripIndent()
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/2319')
-    def 'can run tests in Enclosed runner'() {
-        given:
-        file('src/test/java/EnclosedTest.java') << '''
-import org.junit.*;
-import org.junit.experimental.runners.Enclosed;
-import org.junit.runner.RunWith;
-@RunWith( Enclosed.class )
-public class EnclosedTest {
-    public static class InnerClass {
-       @Test
-        public void aTest() {
-            Assert.assertEquals( "test", "test" );
-        }
-    }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClass('EnclosedTest$InnerClass').assertTestCount(1, 0, 0)
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/2320')
-    def 'can run @BeforeClass in Enclosed runner'() {
-        given:
-        file('src/test/java/EnclosedTest.java') << '''
-import org.junit.*;
-import org.junit.experimental.runners.Enclosed;
-import org.junit.runner.RunWith;
-@RunWith( Enclosed.class )
-public class EnclosedTest {
-    private static String someValue;
-
-    @BeforeClass
-    public static void setSomeValue() {
-        someValue = "test";
-    }
-
-    public static class InnerClass {
-        @Test
-        public void aTest() {
-            Assert.assertEquals( "test", someValue );
-        }
-    }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClass('EnclosedTest$InnerClass').assertTestCount(1, 0, 0)
-    }
-
-    @Issue('https://github.com/junit-team/junit4/issues/1354')
-    def 'can run tests in Enclosed runner wit Category'() {
-        given:
-        file('src/test/java/EnclosedTest.java') << '''
-import org.junit.*;
-import org.junit.experimental.categories.*;
-import org.junit.experimental.runners.Enclosed;
-import org.junit.runner.RunWith;
-
-@RunWith(Enclosed.class)
-public class EnclosedTest {
-  private static int outer;
-
-  @BeforeClass
-  public static void runBeforeEnclosedSuite() {
-      outer = 1;
-  }
-
-  @Category(Fast.class)
-  public static class InnerClass {
-    private int inner;
-
-    @Before
-    public void setUp() {
-       inner = 2;
-    }
-
-    @Test
-    public void test() {
-       Assert.assertTrue(outer==1 && inner==2);
-    }
-  }
-}
-'''
-        file('src/test/java/Fast.java') << 'public interface Fast {}'
-        buildFile << '''
-test {
-    useJUnit {
-        includeCategories 'Fast'
-    }
-}
-'''
-
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClass('EnclosedTest$InnerClass').assertTestCount(1, 0, 0)
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitFailFastIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitFailFastIntegrationTest.groovy
deleted file mode 100644
index 1c47967..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitFailFastIntegrationTest.groovy
+++ /dev/null
@@ -1,36 +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.testing.junit
-
-import org.gradle.testing.fixture.AbstractJvmFailFastIntegrationSpec
-
-class JUnitFailFastIntegrationTest extends AbstractJvmFailFastIntegrationSpec {
-    @Override
-    String testAnnotationClass() {
-        'org.junit.Test'
-    }
-
-    @Override
-    String testDependency() {
-        'junit:junit:4.13'
-    }
-
-    @Override
-    String testFrameworkConfiguration() {
-        ''
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitFilteringIntegrationTest.groovy
deleted file mode 100644
index 362fa81..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitFilteringIntegrationTest.groovy
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright 2013 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.testing.junit
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.AbstractTestFilteringIntegrationTest
-import org.junit.Assume
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.*
-import static org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec.*
-
-@TargetCoverage({ LARGE_COVERAGE + JUNIT_VINTAGE})
-class JUnitFilteringIntegrationTest extends AbstractTestFilteringIntegrationTest {
-
-    String imports = "org.junit.*"
-
-    @Override
-    String getFramework() {
-        return version.toString().startsWith('Vintage') ? "JUnitPlatform" : "JUnit"
-    }
-
-    @Override
-    String getDependencies() {
-        if (version.toString().startsWith('Vintage')) {
-            """
-                testCompileOnly 'junit:junit:4.13'
-                testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:${dependencyVersion}'
-            """
-        } else {
-            "testImplementation 'junit:junit:${dependencyVersion}'"
-        }
-    }
-
-    void theParameterizedFiles() {
-        file("src/test/java/ParameterizedFoo.java") << """import $imports;
-            import org.junit.runners.Parameterized;
-            import org.junit.runners.Parameterized.Parameters;
-            import org.junit.runner.RunWith;
-            import java.util.Arrays;
-            import java.util.Collection;
-
-            @RunWith(Parameterized.class)
-            public class ParameterizedFoo {
-                int index;
-                public ParameterizedFoo(int index){
-                    this.index = index;
-                }
-
-                @Parameters
-                public static Collection data() {
-                   return Arrays.asList(new Object[][] {
-                      { 2 },
-                      { 6 },
-                      { 19 },
-                      { 22 },
-                      { 23 }
-                   });
-                }
-                @Test public void pass() {}
-                @Test public void fail() {}
-            }
-        """
-    }
-
-    void theSuiteFiles() {
-        file("src/test/java/FooTest.java") << """
-            import org.junit.Test;
-            public class FooTest {
-                @Test
-                public void testFoo() { }
-            }
-        """
-        file("src/test/java/FooServerTest.java") << """
-            import org.junit.Test;
-            public class FooServerTest {
-                @Test
-                public void testFooServer() { }
-            }
-        """
-        file("src/test/java/BarTest.java") << """
-            import org.junit.Test;
-            public class BarTest {
-                @Test
-                public void testBar() { }
-            }
-        """
-        file("src/test/java/AllFooTests.java") << """
-            import org.junit.runner.RunWith;
-            import org.junit.runners.Suite;
-            import org.junit.runners.Suite.SuiteClasses;
-            @RunWith(Suite.class)
-            @SuiteClasses({FooTest.class, FooServerTest.class})
-            public class AllFooTests {
-            }
-        """
-    }
-
-    @Issue("GRADLE-3112")
-    def "can filter parameterized tests from the build file."() {
-        given:
-        // this addition to the build file ...
-        buildFile << """
-            test {
-              filter {
-                includeTestsMatching "*ParameterizedFoo.pass*"
-              }
-            }
-        """
-        // and ...
-        theParameterizedFiles()
-
-        when:
-        run("test")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("ParameterizedFoo")
-        result.testClass("ParameterizedFoo").assertTestsExecuted("pass[0]", "pass[1]", "pass[2]", "pass[3]", "pass[4]")
-    }
-
-    @Issue("GRADLE-3112")
-    def "can filter parameterized tests from the command-line"() {
-        given:
-        theParameterizedFiles()
-
-        when:
-        run("test", "--tests", "*ParameterizedFoo.pass*")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("ParameterizedFoo")
-        result.testClass("ParameterizedFoo").assertTestsExecuted("pass[0]", "pass[1]", "pass[2]", "pass[3]", "pass[4]")
-    }
-
-    @Issue("GRADLE-3112")
-    def "passing a suite argument to --tests runs all tests in the suite"() {
-        given:
-        theSuiteFiles()
-
-        when:
-        run("test", "--tests", "*AllFooTests")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-
-        result.assertTestClassesExecuted("FooTest", "FooServerTest")
-        result.testClass("FooTest").assertTestCount(1, 0, 0);
-        result.testClass("FooTest").assertTestsExecuted("testFoo")
-        result.testClass("FooServerTest").assertTestCount(1, 0, 0);
-        result.testClass("FooServerTest").assertTestsExecuted("testFooServer")
-    }
-
-    @Issue("GRADLE-3112")
-    def "can filter test Suites from build file."() {
-        given:
-        // this addition to the build files ...
-        buildFile << """
-            test {
-              filter {
-                includeTestsMatching "*AllFooTests"
-              }
-            }
-        """
-        // and ...
-        theSuiteFiles()
-
-        when:
-        run("test")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-
-        result.assertTestClassesExecuted("FooTest", "FooServerTest")
-        result.testClass("FooTest").assertTestCount(1, 0, 0);
-        result.testClass("FooTest").assertTestsExecuted("testFoo")
-        result.testClass("FooServerTest").assertTestCount(1, 0, 0);
-        result.testClass("FooServerTest").assertTestsExecuted("testFooServer")
-    }
-
-    def 'filter as many classes as possible before sending to worker process'() {
-        given:
-        // We can know which class is sent to TestClassProcessor via afterSuite() hook method
-        // because JUnitTestClassProcessor will emit a test suite event for each loaded class.
-        // However, JUnitPlatformTestClassProcessor won't emit such event unless the class is executed.
-        // That's why we run test with JUnit 4 only.
-        Assume.assumeFalse(framework == "JUnitPlatform")
-        file('src/test/java/org/gradle/FooTest.java') << """
-            package org.gradle;
-            import $imports;
-            public class FooTest {
-                @Test public void test() {}
-            }
-        """
-        file('src/test/java/com/gradle/FooTest.java') << """
-            package com.gradle;
-            import $imports;
-            public class FooTest {
-                @Test public void test() {}
-            }
-        """
-        file('src/test/java/org/gradle/BarTest.java') << """
-            package org.gradle;
-            import $imports;
-            public class BarTest {
-                @Test public void test() {}
-            }
-        """
-        buildFile << """
-            test {
-                filter {
-                    includeTestsMatching "$pattern"
-                }
-                afterSuite { descriptor, result ->
-                    println descriptor
-                }
-            }
-        """
-
-        when:
-        if (successful) {
-            succeeds('test')
-        } else {
-            fails('test')
-        }
-
-        then:
-        includedClasses.every { output.contains(it) }
-        excludedClasses.every { !output.contains(it) }
-
-        where:
-        pattern             | includedClasses                                                    | excludedClasses        | successful
-        'FooTest'           | ['org.gradle.FooTest', 'com.gradle.FooTest']                       | ['org.gradle.BarTest'] | true
-        'FooTest.anyMethod' | ['org.gradle.FooTest', 'com.gradle.FooTest']                       | ['org.gradle.BarTest'] | false
-        'org.gradle.*'      | ['org.gradle.FooTest', 'org.gradle.BarTest']                       | ['com.gradle.FooTest'] | true
-        '*FooTest'          | ['org.gradle.FooTest', 'com.gradle.FooTest', 'org.gradle.BarTest'] | []                     | true
-        'org*'              | ['org.gradle.FooTest', 'org.gradle.BarTest']                       | ['com.gradle.FooTest'] | true
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec.groovy
deleted file mode 100644
index 224a3ec..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec.groovy
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2013 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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.IGNORE_ON_CLASS
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@TargetCoverage({ IGNORE_ON_CLASS + JUNIT_VINTAGE })
-class JUnitIgnoreClassMultiVersionIntegrationSpec extends JUnitMultiVersionIntegrationSpec {
-
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-
-    def canHandleClassLevelIgnoredTests() {
-        executer.noExtraLogging()
-        buildFile << """
-            dependencies { ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')} }
-        """
-
-        when:
-        run('check')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.IgnoredTest', 'org.gradle.CustomIgnoredTest')
-        result.testClass('org.gradle.IgnoredTest').assertTestCount(1, 0, 0).assertTestsSkipped("testIgnored")
-        result.testClass('org.gradle.CustomIgnoredTest').assertTestCount(3, 0, 0).assertTestsSkipped("first test run", "second test run", "third test run")
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitIntegrationTest.groovy
deleted file mode 100755
index 2787b43..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitIntegrationTest.groovy
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * Copyright 2011 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.testing.junit
-
-import org.gradle.api.JavaVersion
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-import spock.lang.IgnoreIf
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
-import static org.gradle.util.Matchers.containsLine
-import static org.gradle.util.Matchers.matchesRegexp
-import static org.hamcrest.CoreMatchers.containsString
-import static org.hamcrest.CoreMatchers.equalTo
-import static org.hamcrest.CoreMatchers.not
-import static org.hamcrest.CoreMatchers.startsWith
-import static org.hamcrest.MatcherAssert.assertThat
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class JUnitIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    @Rule
-    final TestResources resources = new TestResources(testDirectoryProvider)
-
-    def setup() {
-        executer.noExtraLogging()
-    }
-
-    def executesTestsInCorrectEnvironment() {
-        given:
-        buildFile << """
-        test {
-            systemProperties.isJava9 = ${JavaVersion.current().isJava9Compatible()}
-        }
-        """.stripIndent()
-
-        when:
-        executer.withTasks('build').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.OkTest', 'org.gradle.OtherTest')
-        result.testClass('org.gradle.OkTest').assertTestPassed('ok')
-        result.testClass('org.gradle.OtherTest').assertTestPassed('ok')
-    }
-
-    def suitesOutputIsVisible() {
-        when:
-        ignoreWhenJupiter()
-        executer.withTasks('test').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.ASuite', 'org.gradle.OkTest', 'org.gradle.OtherTest')
-        result.testClass('org.gradle.ASuite').assertStdout(containsString('suite class loaded'))
-        result.testClass('org.gradle.ASuite').assertStdout(containsString('before suite class out'))
-        result.testClass('org.gradle.ASuite').assertStdout(containsString('non-asci char: ż'))
-        result.testClass('org.gradle.ASuite').assertStderr(containsString('before suite class err'))
-        result.testClass('org.gradle.ASuite').assertStdout(containsString('after suite class out'))
-        result.testClass('org.gradle.ASuite').assertStderr(containsString('after suite class err'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('This is test stderr'))
-        result.testClass('org.gradle.OkTest').assertStdout(containsString('sys out from another test method'))
-        result.testClass('org.gradle.OkTest').assertStderr(containsString('sys err from another test method'))
-        result.testClass('org.gradle.OtherTest').assertStdout(containsString('This is other stdout'))
-    }
-
-    def testClassesCanBeSharedByMultipleSuites() {
-        when:
-        ignoreWhenJupiter()
-        executer.withTasks('test').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.SomeTest')
-        result.testClass("org.gradle.SomeTest").assertTestCount(2, 0, 0)
-        result.testClass("org.gradle.SomeTest").assertTestsExecuted("ok", "ok")
-    }
-
-    def reportsAndBreaksBuildWhenTestFails() {
-        when:
-        executer.withTasks('build').runWithFailure().assertTestsFailed()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted(
-            'org.gradle.ClassWithBrokenRunner',
-            'org.gradle.CustomException',
-            'org.gradle.BrokenTest',
-            'org.gradle.BrokenBefore',
-            'org.gradle.BrokenAfter',
-            'org.gradle.BrokenBeforeClass',
-            'org.gradle.BrokenAfterClass',
-            'org.gradle.BrokenBeforeAndAfter',
-            'org.gradle.BrokenConstructor',
-            'org.gradle.BrokenException',
-            'org.gradle.Unloadable',
-            'org.gradle.UnserializableException')
-        result.testClass('org.gradle.ClassWithBrokenRunner').assertTestFailed('initializationError', equalTo('java.lang.UnsupportedOperationException: broken'))
-        result.testClass('org.gradle.BrokenTest')
-            .assertTestCount(2, 2, 0)
-            .assertTestFailed('failure', equalTo('java.lang.AssertionError: failed'))
-            .assertTestFailed('broken', equalTo('java.lang.IllegalStateException: html: <> cdata: ]]>'))
-        result.testClass('org.gradle.BrokenBeforeClass').assertTestFailed('classMethod', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenAfterClass').assertTestFailed('classMethod', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenBefore').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenAfter').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenBeforeAndAfter').assertTestFailed('ok', equalTo('java.lang.AssertionError: before failed'), equalTo('java.lang.AssertionError: after failed'))
-        result.testClass('org.gradle.BrokenConstructor').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.BrokenException').assertTestFailed('broken', startsWith('Could not determine failure message for exception of type org.gradle.BrokenException$BrokenRuntimeException: java.lang.UnsupportedOperationException'))
-        result.testClass('org.gradle.CustomException').assertTestFailed('custom', startsWith('Exception with a custom toString implementation'))
-        result.testClass('org.gradle.Unloadable').assertTestFailed('ok', equalTo('java.lang.AssertionError: failed'))
-        result.testClass('org.gradle.Unloadable').assertTestFailed('ok2', startsWith('java.lang.NoClassDefFoundError'))
-        result.testClass('org.gradle.UnserializableException').assertTestFailed('unserialized', equalTo('org.gradle.UnserializableException$UnserializableRuntimeException: whatever'))
-    }
-
-    def canRunSingleTests() {
-        when:
-        succeeds("test", "--tests=Ok2*")
-
-        then:
-        def testResult = new DefaultTestExecutionResult(testDirectory)
-        testResult.assertTestClassesExecuted('Ok2')
-
-        when:
-        succeeds("cleanTest", "test", "--tests=Ok*")
-
-        then:
-        testResult.assertTestClassesExecuted('Ok', 'Ok2')
-
-        when:
-        fails("test", "--tests=DoesNotMatchAClass*")
-
-        then:
-        result.assertHasCause('No tests found for given includes: [DoesNotMatchAClass*](--tests filter)')
-
-        when:
-        fails("test", "--tests=NotATest*")
-        then:
-        result.assertHasCause('No tests found for given includes: [NotATest*](--tests filter)')
-    }
-
-    def canUseTestSuperClassesFromAnotherProject() {
-        given:
-        testDirectory.file('settings.gradle').write("include 'a', 'b'")
-        testDirectory.file('b/build.gradle') << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { implementation 'junit:junit:4.13' }
-        """
-        testDirectory.file('b/src/main/java/org/gradle/AbstractTest.java') << '''
-            package org.gradle;
-            public abstract class AbstractTest {
-                @org.junit.Test public void ok() { }
-            }
-        '''
-        TestFile buildFile = testDirectory.file('a/build.gradle')
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation project(':b') }
-        """
-        testDirectory.file('a/src/test/java/org/gradle/SomeTest.java') << '''
-            package org.gradle;
-            public class SomeTest extends AbstractTest {
-            }
-        '''
-
-        when:
-        executer.withTasks('a:test').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory.file('a'))
-        result.assertTestClassesExecuted('org.gradle.SomeTest')
-        result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
-    }
-
-    def canExcludeSuperClassesFromExecution() {
-        given:
-        TestFile buildFile = testDirectory.file('build.gradle')
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:4.13' }
-            test { exclude '**/BaseTest.*' }
-        """
-        testDirectory.file('src/test/java/org/gradle/BaseTest.java') << '''
-            package org.gradle;
-            public class BaseTest {
-                @org.junit.Test public void ok() { }
-            }
-        '''
-        testDirectory.file('src/test/java/org/gradle/SomeTest.java') << '''
-            package org.gradle;
-            public class SomeTest extends BaseTest {
-            }
-        '''
-
-        when:
-        executer.withTasks('test').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.SomeTest')
-        result.testClass('org.gradle.SomeTest').assertTestPassed('ok')
-    }
-
-    def detectsTestClasses() {
-        when:
-        executer.withTasks('test').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.EmptyRunWithSubclass', 'org.gradle.TestsOnInner', 'org.gradle.TestsOnInner$SomeInner')
-        result.testClass('org.gradle.EmptyRunWithSubclass').assertTestsExecuted('ok')
-        result.testClass('org.gradle.EmptyRunWithSubclass').assertTestPassed('ok')
-        result.testClass('org.gradle.TestsOnInner').assertTestPassed('ok')
-        result.testClass('org.gradle.TestsOnInner$SomeInner').assertTestPassed('ok')
-    }
-
-    @Issue("https://issues.gradle.org//browse/GRADLE-3114")
-    def createsRunnerBeforeTests() {
-        when:
-        executer.withTasks('test').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.ExecutionOrderTest')
-        result.testClass('org.gradle.ExecutionOrderTest').assertTestPassed('classUnderTestIsLoadedOnlyByRunner')
-    }
-
-    def runsAllTestsInTheSameForkedJvm() {
-        given:
-        testDirectory.file('build.gradle').writelns(
-            "apply plugin: 'java'",
-            mavenCentralRepository(),
-            "dependencies { implementation 'junit:junit:4.13' }"
-        )
-        testDirectory.file('src/test/java/org/gradle/AbstractTest.java').writelns(
-            "package org.gradle;",
-            "public abstract class AbstractTest {",
-            "    @org.junit.Test public void ok() {",
-            "        long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();",
-            "        System.out.println(String.format(\"VM START TIME = %s\", time));",
-            "    }",
-            "}")
-        testDirectory.file('src/test/java/org/gradle/SomeTest.java').writelns(
-            "package org.gradle;",
-            "public class SomeTest extends AbstractTest {",
-            "}")
-        testDirectory.file('src/test/java/org/gradle/SomeTest2.java').writelns(
-            "package org.gradle;",
-            "public class SomeTest2 extends AbstractTest {",
-            "}")
-
-        when:
-        executer.withTasks('test').run()
-
-        then:
-        TestFile results1 = testDirectory.file('build/test-results/test/TEST-org.gradle.SomeTest.xml')
-        TestFile results2 = testDirectory.file('build/test-results/test/TEST-org.gradle.SomeTest2.xml')
-        results1.assertIsFile()
-        results2.assertIsFile()
-        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), equalTo(results2.linesThat(containsString('VM START TIME =')).get(0)))
-    }
-
-    def canSpecifyMaximumNumberOfTestClassesToExecuteInAForkedJvm() {
-        given:
-        testDirectory.file('build.gradle').writelns(
-            "apply plugin: 'java'",
-            mavenCentralRepository(),
-            "dependencies { implementation 'junit:junit:4.13' }",
-            "test.forkEvery = 1"
-        )
-        testDirectory.file('src/test/java/org/gradle/AbstractTest.java').writelns(
-            "package org.gradle;",
-            "public abstract class AbstractTest {",
-            "    @org.junit.Test public void ok() {",
-            "        long time = java.lang.management.ManagementFactory.getRuntimeMXBean().getStartTime();",
-            "        System.out.println(String.format(\"VM START TIME = %s\", time));",
-            "    }",
-            "}")
-        testDirectory.file('src/test/java/org/gradle/SomeTest.java').writelns(
-            "package org.gradle;",
-            "public class SomeTest extends AbstractTest {",
-            "}")
-        testDirectory.file('src/test/java/org/gradle/SomeTest2.java').writelns(
-            "package org.gradle;",
-            "public class SomeTest2 extends AbstractTest {",
-            "}")
-
-        when:
-        executer.withTasks('test').run()
-
-        then:
-        TestFile results1 = testDirectory.file('build/test-results/test/TEST-org.gradle.SomeTest.xml')
-        TestFile results2 = testDirectory.file('build/test-results/test/TEST-org.gradle.SomeTest2.xml')
-        results1.assertIsFile()
-        results2.assertIsFile()
-        assertThat(results1.linesThat(containsString('VM START TIME =')).get(0), not(equalTo(results2.linesThat(
-            containsString('VM START TIME =')).get(0))))
-    }
-
-    def canListenForTestResults() {
-        given:
-        testDirectory.file('src/main/java/AppException.java').writelns(
-            "public class AppException extends Exception { }"
-        )
-
-        testDirectory.file('src/test/java/SomeTest.java').writelns(
-            "public class SomeTest {",
-            "@org.junit.Test public void fail() { org.junit.Assert.fail(\"message\"); }",
-            "@org.junit.Test public void knownError() { throw new RuntimeException(\"message\"); }",
-            "@org.junit.Test public void unknownError() throws AppException { throw new AppException(); }",
-            "}"
-        )
-        testDirectory.file('src/test/java/SomeOtherTest.java').writelns(
-            "public class SomeOtherTest {",
-            "@org.junit.Test public void pass() { }",
-            "}"
-        )
-
-        testDirectory.file('build.gradle') << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:4.13' }
-            def listener = new TestListenerImpl()
-            test.addTestListener(listener)
-            test.ignoreFailures = true
-            class TestListenerImpl implements TestListener {
-                void beforeSuite(TestDescriptor suite) { println "START [\$suite] [\$suite.name]" }
-                void afterSuite(TestDescriptor suite, TestResult result) { println "FINISH [\$suite] [\$suite.name] [\$result.resultType] [\$result.testCount]" }
-                void beforeTest(TestDescriptor test) { println "START [\$test] [\$test.name]" }
-                void afterTest(TestDescriptor test, TestResult result) { println "FINISH [\$test] [\$test.name] [\$result.resultType] [\$result.testCount] [\$result.exception]" }
-            }
-        """
-
-        when:
-        def result = executer.withTasks("test").run()
-
-        then:
-        containsLine(result.getOutput(), "START [Gradle Test Run :test] [Gradle Test Run :test]")
-        containsLine(result.getOutput(), "FINISH [Gradle Test Run :test] [Gradle Test Run :test] [FAILURE] [4]")
-
-        containsLine(result.getOutput(), matchesRegexp("START \\[Gradle Test Executor \\d+\\] \\[Gradle Test Executor \\d+\\]"))
-        containsLine(result.getOutput(), matchesRegexp("FINISH \\[Gradle Test Executor \\d+\\] \\[Gradle Test Executor \\d+\\] \\[FAILURE\\] \\[4\\]"))
-
-        containsLine(result.getOutput(), "START [Test class SomeOtherTest] [SomeOtherTest]")
-        containsLine(result.getOutput(), "FINISH [Test class SomeOtherTest] [SomeOtherTest] [SUCCESS] [1]")
-        containsLine(result.getOutput(), "START [Test pass(SomeOtherTest)] [pass]")
-        containsLine(result.getOutput(), "FINISH [Test pass(SomeOtherTest)] [pass] [SUCCESS] [1] [null]")
-
-        containsLine(result.getOutput(), "START [Test class SomeTest] [SomeTest]")
-        containsLine(result.getOutput(), "FINISH [Test class SomeTest] [SomeTest] [FAILURE] [3]")
-        containsLine(result.getOutput(), "START [Test fail(SomeTest)] [fail]")
-        containsLine(result.getOutput(), "FINISH [Test fail(SomeTest)] [fail] [FAILURE] [1] [java.lang.AssertionError: message]")
-        containsLine(result.getOutput(), "START [Test knownError(SomeTest)] [knownError]")
-        containsLine(result.getOutput(), "FINISH [Test knownError(SomeTest)] [knownError] [FAILURE] [1] [java.lang.RuntimeException: message]")
-        containsLine(result.getOutput(), "START [Test unknownError(SomeTest)] [unknownError]")
-        containsLine(result.getOutput(), "FINISH [Test unknownError(SomeTest)] [unknownError] [FAILURE] [1] [AppException]")
-
-        when:
-        testDirectory.file('src/test/java/SomeOtherTest.java').delete()
-        result = executer.withTasks("test").run()
-
-        then:
-        result.assertNotOutput("SomeOtherTest")
-        containsLine(result.getOutput(), "START [Test class SomeTest] [SomeTest]")
-    }
-
-    def canListenForTestResultsWhenJUnit3IsUsed() {
-        given:
-        ignoreWhenJupiter()
-        testDirectory.file('src/test/java/SomeTest.java').writelns(
-            "public class SomeTest extends junit.framework.TestCase {",
-            "public void testPass() { }",
-            "public void testFail() { junit.framework.Assert.fail(\"message\"); }",
-            "public void testError() { throw new RuntimeException(\"message\"); }",
-            "}"
-        )
-
-        testDirectory.file('build.gradle') << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:3.8' }
-            def listener = new TestListenerImpl()
-            test.addTestListener(listener)
-            test.ignoreFailures = true
-            class TestListenerImpl implements TestListener {
-                void beforeSuite(TestDescriptor suite) { println "START [\$suite] [\$suite.name]" }
-                void afterSuite(TestDescriptor suite, TestResult result) { println "FINISH [\$suite] [\$suite.name]" }
-                void beforeTest(TestDescriptor test) { println "START [\$test] [\$test.name]" }
-                void afterTest(TestDescriptor test, TestResult result) { println "FINISH [\$test] [\$test.name] [\$result.exception]" }
-            }
-        """
-
-        when:
-        ExecutionResult result = executer.withTasks("test").run()
-
-        then:
-        assert containsLine(result.getOutput(), "START [Test class SomeTest] [SomeTest]")
-        assert containsLine(result.getOutput(), "FINISH [Test class SomeTest] [SomeTest]")
-        assert containsLine(result.getOutput(), "START [Test testPass(SomeTest)] [testPass]")
-        assert containsLine(result.getOutput(), "FINISH [Test testPass(SomeTest)] [testPass] [null]")
-        assert containsLine(result.getOutput(), "START [Test testFail(SomeTest)] [testFail]")
-        assert containsLine(result.getOutput(), "FINISH [Test testFail(SomeTest)] [testFail] [junit.framework.AssertionFailedError: message]")
-        assert containsLine(result.getOutput(), "START [Test testError(SomeTest)] [testError]")
-        assert containsLine(result.getOutput(), "FINISH [Test testError(SomeTest)] [testError] [java.lang.RuntimeException: message]")
-    }
-
-    @IgnoreIf({ GradleContextualExecuter.parallel })
-    def canHaveMultipleTestTaskInstances() {
-        when:
-        executer.withTasks('check').run()
-
-        then:
-        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
-        testResult.assertTestClassesExecuted('org.gradle.Test1')
-        testResult.testClass('org.gradle.Test1').assertTestPassed('ok')
-
-        def test2Result = new JUnitXmlTestExecutionResult(testDirectory, 'build/test-results/test2')
-        test2Result.assertTestClassesExecuted('org.gradle.Test2')
-        test2Result.testClass('org.gradle.Test2').assertTestPassed('ok')
-    }
-
-    def supportsJunit3Suites() {
-        when:
-        executer.withTasks('test').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.SomeTest1', 'org.gradle.SomeTest2', 'org.gradle.SomeSuite')
-        result.testClass("org.gradle.SomeTest1").assertTestCount(1, 0, 0)
-        result.testClass("org.gradle.SomeTest1").assertTestsExecuted("testOk1")
-        result.testClass("org.gradle.SomeTest2").assertTestCount(1, 0, 0)
-        result.testClass("org.gradle.SomeTest2").assertTestsExecuted("testOk2")
-        result.testClass("org.gradle.SomeSuite").assertTestCount(0, 0, 0)
-        result.testClass("org.gradle.SomeSuite").assertStdout(containsString("stdout in TestSetup#setup"))
-        result.testClass("org.gradle.SomeSuite").assertStdout(containsString("stdout in TestSetup#teardown"))
-        result.testClass("org.gradle.SomeSuite").assertStderr(containsString("stderr in TestSetup#setup"))
-        result.testClass("org.gradle.SomeSuite").assertStderr(containsString("stderr in TestSetup#teardown"))
-    }
-
-    def "tries to execute unparseable test classes"() {
-        given:
-        testDirectory.file('build/classes/java/test/com/example/Foo.class').text = "invalid class file"
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies {
-                ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')}
-            }
-        """
-
-        when:
-        fails('test', '-x', 'compileTestJava')
-
-        then:
-        failureCauseContains("There were failing tests")
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        if (isVintage() || isJupiter()) {
-            result.testClassStartsWith('Gradle Test Executor')
-                .assertTestCount(1, 1, 0)
-                .assertTestFailed("failed to execute tests", containsString("Could not execute test class 'com.example.Foo'"))
-        } else {
-            result.testClass('com.example.Foo')
-                .assertTestCount(1, 1, 0)
-                .assertTestFailed("initializationError", containsString('ClassFormatError'))
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest.groovy
deleted file mode 100644
index 0341d4e8..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest.groovy
+++ /dev/null
@@ -1,44 +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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-@Issue("GRADLE-1682")
-class JUnitJdkNavigationIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-
-    @Rule
-    final TestResources resources = new TestResources(testDirectoryProvider)
-
-    def shouldNotNavigateToJdkClasses() {
-        when:
-        succeeds('test')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass('org.gradle.Test1').assertTestPassed('shouldPass')
-    }
-
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitJnaIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitJnaIntegrationTest.groovy
deleted file mode 100644
index 1a2fd0a..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitJnaIntegrationTest.groovy
+++ /dev/null
@@ -1,44 +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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.*
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class JUnitJnaIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    @Rule
-    final TestResources resources = new TestResources(testDirectoryProvider)
-
-    @Requires(TestPrecondition.WINDOWS)
-    def canRunTestsUsingJna() {
-        when:
-        executer.withTasks('build').run()
-
-        then:
-        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('OkTest')
-        result.testClass('OkTest').assertTestPassed('ok')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingOutputCaptureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingOutputCaptureIntegrationTest.groovy
deleted file mode 100644
index 39bc25b..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitLoggingOutputCaptureIntegrationTest.groovy
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright 2014 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.testing.junit
-
-import org.gradle.integtests.fixtures.HtmlTestExecutionResult
-import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUPITER
-import static org.gradle.testing.fixture.JUnitCoverage.VINTAGE
-import static org.hamcrest.CoreMatchers.containsString
-import static org.hamcrest.CoreMatchers.is
-
-// https://github.com/junit-team/junit5/issues/1285
-@TargetCoverage({ JUNIT_4_LATEST + [JUPITER, VINTAGE] })
-class JUnitLoggingOutputCaptureIntegrationTest extends JUnitMultiVersionIntegrationSpec {
-    def setup() {
-        buildFile << """
-            apply plugin: "java"
-            ${mavenCentralRepository()}
-            dependencies {
-                ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')}
-            }
-            test {
-                reports.junitXml.outputPerTestCase = true
-                // JUnit 5's test name contains parentheses
-                onOutput { test, event -> print "\${test.toString().replace('()(', '(')} -> \$event.message" }
-            }
-        """
-    }
-
-    def "captures logging output events"() {
-        file("src/test/java/OkTest.java") << """
-public class OkTest {
-    static {
-        System.out.println("class loaded");
-    }
-
-    public OkTest() {
-        System.out.println("test constructed");
-    }
-
-    @org.junit.BeforeClass public static void init() {
-        System.out.println("before class out");
-        System.err.println("before class err");
-    }
-
-    @org.junit.AfterClass public static void end() {
-        System.out.println("after class out");
-        System.err.println("after class err");
-    }
-
-    @org.junit.Before
-    public void before() {
-        System.out.println("before out");
-        System.err.println("before err");
-    }
-
-    @org.junit.After
-    public void after() {
-        System.out.println("after out");
-        System.err.println("after err");
-    }
-
-    @org.junit.Test
-    public void ok() {
-        System.out.print("test out: \u03b1</html>");
-        System.out.println();
-        System.err.println("test err");
-    }
-
-    @org.junit.Test
-    public void anotherOk() {
-        System.out.println("ok out");
-        System.err.println("ok err");
-    }
-}
-"""
-        when:
-        succeeds "test"
-
-        then:
-        if (isJupiter()) {
-            outputContains """Test class OkTest -> class loaded
-Test class OkTest -> before class out
-Test class OkTest -> before class err
-Test class OkTest -> test constructed
-Test anotherOk(OkTest) -> before out
-Test anotherOk(OkTest) -> before err
-Test anotherOk(OkTest) -> ok out
-Test anotherOk(OkTest) -> ok err
-Test anotherOk(OkTest) -> after out
-Test anotherOk(OkTest) -> after err
-Test class OkTest -> test constructed
-Test ok(OkTest) -> before out
-Test ok(OkTest) -> before err
-Test ok(OkTest) -> test out: \u03b1</html>
-Test ok(OkTest) -> test err
-Test ok(OkTest) -> after out
-Test ok(OkTest) -> after err
-Test class OkTest -> after class out
-Test class OkTest -> after class err
-"""
-
-            // This test covers current behaviour, not necessarily desired behaviour
-
-            def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
-            def classResult = xmlReport.testClass("OkTest")
-            classResult.assertTestCaseStdout("ok", is("""before out
-test out: \u03b1</html>
-after out
-"""))
-            classResult.assertTestCaseStderr("ok", is("""before err
-test err
-after err
-"""))
-            classResult.assertStdout(is("""class loaded
-before class out
-test constructed
-test constructed
-after class out
-"""))
-            classResult.assertStderr(is("""before class err
-after class err
-"""))
-        } else {
-            // Behavior change of JUnit 4.13
-            outputContains """Test class OkTest -> class loaded
-Test class OkTest -> before class out
-Test class OkTest -> before class err
-Test anotherOk(OkTest) -> test constructed
-Test anotherOk(OkTest) -> before out
-Test anotherOk(OkTest) -> before err
-Test anotherOk(OkTest) -> ok out
-Test anotherOk(OkTest) -> ok err
-Test anotherOk(OkTest) -> after out
-Test anotherOk(OkTest) -> after err
-Test ok(OkTest) -> test constructed
-Test ok(OkTest) -> before out
-Test ok(OkTest) -> before err
-Test ok(OkTest) -> test out: \u03b1</html>
-Test ok(OkTest) -> test err
-Test ok(OkTest) -> after out
-Test ok(OkTest) -> after err
-Test class OkTest -> after class out
-Test class OkTest -> after class err
-"""
-
-            // This test covers current behaviour, not necessarily desired behaviour
-
-            def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
-            def classResult = xmlReport.testClass("OkTest")
-            classResult.assertTestCaseStdout("ok", is("""test constructed
-before out
-test out: \u03b1</html>
-after out
-"""))
-            classResult.assertTestCaseStderr("ok", is("""before err
-test err
-after err
-"""))
-            classResult.assertStdout(is("""class loaded
-before class out
-after class out
-"""))
-            classResult.assertStderr(is("""before class err
-after class err
-"""))
-        }
-
-        def htmlReport = new HtmlTestExecutionResult(testDirectory)
-        def classReport = htmlReport.testClass("OkTest")
-        classReport.assertStdout(is("""class loaded
-before class out
-test constructed
-before out
-ok out
-after out
-test constructed
-before out
-test out: \u03b1</html>
-after out
-after class out
-"""))
-        classReport.assertStderr(is("""before class err
-before err
-ok err
-after err
-before err
-test err
-after err
-after class err
-"""))
-    }
-
-    def "captures output from logging frameworks"() {
-        buildFile << """
-dependencies { testImplementation "org.slf4j:slf4j-simple:1.7.10", "org.slf4j:slf4j-api:1.7.10" }
-"""
-        file("src/test/java/FooTest.java") << """
-
-            public class FooTest {
-                private final static org.slf4j.Logger SLF4J = org.slf4j.LoggerFactory.getLogger(FooTest.class);
-                private final static java.util.logging.Logger JUL = java.util.logging.Logger.getLogger(FooTest.class.getName());
-
-                @org.junit.Test
-                public void foo() {
-                  SLF4J.info("slf4j info");
-                  JUL.info("jul info");
-                  JUL.warning("jul warning");
-                }
-            }
-        """
-
-        when:
-        succeeds("test")
-
-        then:
-        outputContains("Test foo(FooTest) -> [Test worker] INFO FooTest - slf4j info")
-        outputContains("Test foo(FooTest) -> ${java.util.logging.Level.INFO.getLocalizedName()}: jul info")
-        outputContains("Test foo(FooTest) -> ${java.util.logging.Level.WARNING.getLocalizedName()}: jul warning")
-
-        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
-        def classResult = testResult.testClass("FooTest")
-        classResult.assertTestCaseStderr("foo", containsString("[Test worker] INFO FooTest - slf4j info"))
-        classResult.assertTestCaseStderr("foo", containsString("${java.util.logging.Level.INFO.getLocalizedName()}: jul info"))
-        classResult.assertTestCaseStderr("foo", containsString("${java.util.logging.Level.WARNING.getLocalizedName()}: jul warning"))
-    }
-
-    def "test can generate output from multiple threads"() {
-        file("src/test/java/OkTest.java") << """
-import java.util.logging.Logger;
-import java.util.List;
-import java.util.ArrayList;
-
-public class OkTest {
-    @org.junit.Test
-    public void ok() throws Exception {
-        // logging from multiple threads
-        List<Thread> threads  = new ArrayList<Thread>();
-        for (int i = 0; i < 5; i++) {
-            Thread thread = new Thread("thread " + i) {
-                @Override
-                public void run() {
-                    System.out.print("stdout from "); // print a partial line
-                    System.err.println("stderr from " + getName());
-                    System.out.println(getName());
-                    Logger.getLogger("test-logger").info("info from " + getName());
-                }
-            };
-            thread.start();
-            threads.add(thread);
-        }
-        for(Thread thread: threads) {
-            thread.join();
-        }
-    }
-}
-"""
-
-        when:
-        succeeds("test")
-
-        then:
-        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
-        def classResult = testResult.testClass("OkTest")
-
-        5.times { n ->
-            outputContains("Test ok(OkTest) -> stdout from thread $n")
-            outputContains("Test ok(OkTest) -> stderr from thread $n")
-            outputContains("Test ok(OkTest) -> ${java.util.logging.Level.INFO.getLocalizedName()}: info from thread $n")
-
-            classResult.assertTestCaseStdout("ok", containsString("stdout from thread $n"))
-            classResult.assertTestCaseStderr("ok", containsString("stderr from thread $n"))
-            classResult.assertTestCaseStderr("ok", containsString("${java.util.logging.Level.INFO.getLocalizedName()}: info from thread $n"))
-        }
-    }
-
-    def "output does not require trailing end-of-line separator"() {
-        file("src/test/java/OkTest.java") << """
-public class OkTest {
-    @org.junit.Before
-    public void before() {
-        System.out.print("[before out]");
-        System.err.print("[before err]");
-    }
-
-    @org.junit.After
-    public void after() {
-        System.out.print("[after out]");
-        System.err.print("[after err]");
-    }
-
-    @org.junit.BeforeClass public static void init() {
-        System.out.print("[before class out]");
-        System.err.print("[before class err]");
-    }
-
-    @org.junit.AfterClass public static void end() {
-        System.out.print("[after class out]");
-        System.err.print("[after class err]");
-    }
-
-    @org.junit.Test
-    public void ok() {
-        System.out.print("[test out]");
-        System.err.print("[test err]");
-    }
-
-    @org.junit.Test
-    public void anotherOk() {
-        System.out.println();
-        System.err.println();
-        System.out.print("[ok out]");
-        System.err.print("[ok err]");
-    }
-}
-"""
-
-        when:
-        run "test"
-
-        then:
-        def testResult = new JUnitXmlTestExecutionResult(testDirectory)
-        def classResult = testResult.testClass("OkTest")
-        classResult.assertTestCaseStdout("ok", is("[before out][test out][after out]"))
-        classResult.assertTestCaseStderr("ok", is("[before err][test err][after err]"))
-        classResult.assertTestCaseStdout("anotherOk", is("[before out]\n[ok out][after out]"))
-        classResult.assertTestCaseStderr("anotherOk", is("[before err]\n[ok err][after err]"))
-        classResult.assertStdout(is("[before class out][after class out]"))
-        classResult.assertStderr(is("[before class err][after class err]"))
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitSmokeMultiVersionIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitSmokeMultiVersionIntegrationSpec.groovy
deleted file mode 100644
index 9a1be15..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitSmokeMultiVersionIntegrationSpec.groovy
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2012 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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.integtests.fixtures.TestResources
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.*
-import static org.hamcrest.CoreMatchers.containsString
-
-@TargetCoverage({ LARGE_COVERAGE + JUNIT_VINTAGE })
-class JUnitSmokeMultiVersionIntegrationSpec extends JUnitMultiVersionIntegrationSpec {
-
-    @Rule TestResources resources = new TestResources(temporaryFolder)
-
-    def canRunTestsUsingJUnit() {
-        given:
-        resources.maybeCopy('JUnitIntegrationTest/junit3Tests')
-        resources.maybeCopy('JUnitIntegrationTest/junit4Tests')
-
-        buildFile << """
-        apply plugin: 'java'
-        ${mavenCentralRepository()}
-        dependencies {
-            ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')}
-        }"""
-
-        when:
-        fails('test')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.Junit3Test', 'org.gradle.Junit4Test')
-        def junit3TestClass = result.testClass('org.gradle.Junit3Test')
-            .assertTestCount(2, 1, 0)
-            .assertTestFailed('a test that renames itself', containsString("epic"))
-        // The original test is never reported as finished.
-        // Thus, reporting it as skipped actually makes more sense.
-        if (isJUnitPlatform()) {
-            junit3TestClass.assertTestsSkipped('testRenamesItself')
-        } else {
-            junit3TestClass.assertTestPassed('testRenamesItself')
-        }
-        result.testClass('org.gradle.Junit4Test')
-                .assertTestCount(2, 0, 0)
-                .assertTestsExecuted('ok')
-                .assertTestPassed('ok')
-                .assertTestsSkipped('broken')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitTestFilteringSamplesIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitTestFilteringSamplesIntegrationTest.groovy
deleted file mode 100644
index c3ce6c0..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitTestFilteringSamplesIntegrationTest.groovy
+++ /dev/null
@@ -1,49 +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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.junit.Rule
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
-
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE_JUPITER })
-class JUnitTestFilteringSamplesIntegrationTest extends MultiVersionIntegrationSpec {
-
-    @Rule Sample sample = new Sample(temporaryFolder, 'testing/filtering/groovy')
-
-    def setup() {
-        executer.withRepositoryMirrors()
-    }
-
-    def "uses test filter"() {
-        when:
-        inDirectory(sample.dir)
-        run("test")
-
-        then:
-        def result = new DefaultTestExecutionResult(sample.dir)
-        result.assertTestClassesExecuted("SomeIntegTest", "SomeOtherTest")
-        result.testClass("SomeIntegTest").assertTestsExecuted("test1", "test2")
-        result.testClass("SomeOtherTest").assertTestsExecuted("quickUiCheck")
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitTestFrameworkIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitTestFrameworkIntegrationTest.groovy
deleted file mode 100644
index 924ec33..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/JUnitTestFrameworkIntegrationTest.groovy
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2017 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.testing.junit
-
-import org.gradle.testing.AbstractTestFrameworkIntegrationTest
-
-class JUnitTestFrameworkIntegrationTest extends AbstractTestFrameworkIntegrationTest {
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies { testImplementation 'junit:junit:4.13' }
-        """
-    }
-
-    @Override
-    void createPassingFailingTest() {
-        file('src/main/java/AppException.java').writelns(
-            "public class AppException extends Exception { }"
-        )
-
-        file('src/test/java/SomeTest.java') << """
-            public class SomeTest {
-                @org.junit.Test
-                public void ${failingTestCaseName}() {
-                    System.err.println("some error output");
-                    org.junit.Assert.fail(\"test failure message\");
-                }
-                @org.junit.Test
-                public void ${passingTestCaseName}() { }
-            }
-        """
-        file('src/test/java/SomeOtherTest.java') << """
-            public class SomeOtherTest {
-                @org.junit.Test
-                public void ${passingTestCaseName}() { }
-            }
-        """
-    }
-
-    @Override
-    void createEmptyProject() {
-        file("src/test/java/NotATest.java") << """
-            public class NotATest {}
-        """
-    }
-
-    @Override
-    void renameTests() {
-        def newTest = file("src/test/java/NewTest.java")
-        file('src/test/java/SomeOtherTest.java').renameTo(newTest)
-        newTest.text = newTest.text.replaceAll("SomeOtherTest", "NewTest")
-    }
-
-    @Override
-    String getTestTaskName() {
-        return "test"
-    }
-
-    @Override
-    String getPassingTestCaseName() {
-        return "pass"
-    }
-
-    @Override
-    String getFailingTestCaseName() {
-        return "fail"
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/RerunPreviousFailedTestIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/RerunPreviousFailedTestIntegrationTest.groovy
deleted file mode 100644
index 8434e0f..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/RerunPreviousFailedTestIntegrationTest.groovy
+++ /dev/null
@@ -1,170 +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.testing.junit
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.hamcrest.CoreMatchers
-
-class RerunPreviousFailedTestIntegrationTest extends AbstractIntegrationSpec {
-    private static final String INDEX_OF_TEST_TO_FAIL = "index.of.test.to.fail"
-    private static final List<Integer> TESTS = [1, 2, 3]
-    private static final List<String> TEST_CLASSES = TESTS.collect { "ConditionalFailingTest_${it}".toString() }
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-
-
-            dependencies {
-                testImplementation 'junit:junit:4.13'
-            }
-        """
-
-        TESTS.each {
-            file("src/test/java/ConditionalFailingTest_${it}.java") << """
-                import org.junit.Test;
-                public class ConditionalFailingTest_${it} {
-                    @Test
-                    public void failedTest() {
-                        System.out.println("Test index " + ${it});
-                        if("${it}".equals(System.getProperty("${INDEX_OF_TEST_TO_FAIL}"))) {
-                            throw new RuntimeException();
-                        }
-                    }
-                }
-            """.stripIndent()
-        }
-    }
-
-    def letTestFail(def index) {
-        buildFile << """
-        test {
-            systemProperty('${INDEX_OF_TEST_TO_FAIL}', '${index}')
-        }
-        """
-    }
-
-    def 'subsequent execution runs failed test first'() {
-        given:
-        letTestFail(indexOfTestToFail)
-
-        when:
-        fails('test', '--info')
-
-        then:
-        testFailed(indexOfTestToFail)
-
-        when:
-        fails('test', '--info')
-
-        then:
-        failedTestAreRerunFirst(indexOfTestToFail)
-
-        when:
-        letTestFail(0)
-        succeeds('test')
-
-        then:
-        allTestsSucceed()
-
-        where:
-        indexOfTestToFail << TESTS
-    }
-
-    void failedTestAreRerunFirst(int failedTestIndex) {
-        List<String> lines = output.readLines()
-        boolean findIt = false
-        for (String line in lines) {
-            if (line.contains("Test index ${failedTestIndex}")) {
-                findIt = true
-                break
-            } else if (line.contains("Test index")) {
-                assert false
-            }
-        }
-
-        assert findIt
-    }
-
-
-    def 'can delete previous failed test'() {
-        given:
-        letTestFail(indexOfTestToFail)
-
-        when:
-        fails('test')
-
-        then:
-        testFailed(indexOfTestToFail)
-
-        when:
-        file("src/test/java/ConditionalFailingTest_${indexOfTestToFail}.java").delete()
-        succeeds('test')
-
-        then:
-        remainTestsSucceed(indexOfTestToFail)
-
-        where:
-        indexOfTestToFail << TESTS
-    }
-
-    def 'can modify previous failed test'() {
-        given:
-        letTestFail(indexOfTestToFail)
-
-        when:
-        fails('test')
-
-        then:
-        testFailed(indexOfTestToFail)
-
-        when:
-        file("src/test/java/ConditionalFailingTest_${indexOfTestToFail}.java").text = """
-        public class ConditionalFailingTest_${indexOfTestToFail} {
-            public void failedTest() {
-            }
-        }
-        """
-        succeeds('test')
-
-        then:
-        remainTestsSucceed(indexOfTestToFail)
-
-        where:
-        indexOfTestToFail << TESTS
-    }
-
-    void testFailed(def indexOfTestToFail) {
-        new DefaultTestExecutionResult(testDirectory)
-            .testClass("ConditionalFailingTest_${indexOfTestToFail}").assertTestFailed('failedTest', CoreMatchers.anything())
-    }
-
-    void allTestsSucceed() {
-        new DefaultTestExecutionResult(testDirectory).assertTestClassesExecuted(TEST_CLASSES as String[])
-    }
-
-    void remainTestsSucceed(def indexOfTestToFail) {
-        def failedTest = "ConditionalFailingTest_${indexOfTestToFail}".toString()
-        def testClasses = TEST_CLASSES.clone()
-        testClasses.remove(failedTest)
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted(testClasses as String[])
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/Specs2IntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/Specs2IntegrationTest.groovy
deleted file mode 100644
index 86ce6c5..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/Specs2IntegrationTest.groovy
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2019 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.testing.junit
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec
-
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
-import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
-
-@TargetCoverage({ JUNIT_4_LATEST + JUNIT_VINTAGE })
-class Specs2IntegrationTest extends JUnitMultiVersionIntegrationSpec {
-
-    def 'can run Specs2 tests'() {
-        given:
-        buildFile << """
-            plugins {
-                id("scala")
-            }
-
-            ${mavenCentralRepository()}
-
-            dependencies {
-                implementation 'org.scala-lang:scala-library:2.11.8'
-                testImplementation 'org.specs2:specs2_2.11:3.7'
-                testImplementation 'org.specs2:specs2-junit_2.11:4.7.0'
-                ${dependencyNotation.collect { "testImplementation '$it'" }.join('\n')}
-            }
-        """
-        file('src/test/scala/BasicSpec.scala') << '''
-            import org.junit.runner.RunWith
-            import org.specs2.runner.JUnitRunner
-            import org.specs2.mutable.Specification
-
-            @RunWith(classOf[JUnitRunner])
-            class BasicSpec extends Specification {
-              "Basic Math" >> {
-                (1 + 1) mustEqual 2
-              }
-            }
-        '''
-
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClass("BasicSpec").assertTestCount(1, 0, 0)
-            .assertTestPassed('Basic Math')
-    }
-
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4CategoriesOrTagsCoverageIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4CategoriesOrTagsCoverageIntegrationTest.groovy
new file mode 100644
index 0000000..d1ff0f6
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4CategoriesOrTagsCoverageIntegrationTest.groovy
@@ -0,0 +1,484 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.testing.junit.AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec
+
+/**
+ * Base class for JUnit 4 category/tag coverage integration tests.  Provides JUnit4-specific tests and test sources for both JUnit4 and JUnit Vintage.
+ */
+abstract class AbstractJUnit4CategoriesOrTagsCoverageIntegrationTest extends AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec {
+    @Override
+    TestSourceGenerator getTestSourceGenerator() {
+        return new JUnit4TestSourceGenerator()
+    }
+
+    abstract boolean supportsCategoryOnNestedClass()
+
+    def "can specify both includes and excludes for categories"() {
+        given:
+        testSources.with {
+            testClass('CategoryACTests')
+                .withCategoryOrTag('CategoryA')
+                .withCategoryOrTag('CategoryC')
+                .with {
+                    testMethod('catACOk1').shouldPass()
+                    testMethod('catACOk2').shouldPass()
+                    testMethod('catACOk3').shouldPass()
+                    testMethod('catACOk4').shouldPass()
+                }
+            testClass('CategoryADTests')
+                .withCategoryOrTag('CategoryA')
+                .with {
+                    testMethod('catAOk1').shouldPass()
+                    testMethod('catAOk2').shouldPass()
+                    testMethod('catCOk3').withCategoryOrTag('CategoryC').shouldPass()
+                    testMethod('catDOk4').withCategoryOrTag('CategoryD').shouldPass()
+                }
+            testClass('CategoryATests')
+                .withCategoryOrTag('CategoryA')
+                .with {
+                    testMethod('catAOk1').shouldPass()
+                    testMethod('catAOk2').shouldPass()
+                    testMethod('catAOk3').shouldPass()
+                    testMethod('catAOk4').shouldPass()
+                }
+            testClass('CategoryBTests')
+                .withCategoryOrTag('CategoryB')
+                .with {
+                    testMethod('catBOk1').shouldPass()
+                    testMethod('catBOk2').shouldPass()
+                    testMethod('catBOk3').shouldPass()
+                    testMethod('catBOk4').shouldPass()
+                }
+            testClass('CategoryCBTests')
+                .withCategoryOrTag('CategoryC')
+                .with {
+                    testMethod('catCOk1').shouldPass()
+                    testMethod('catCOk2').shouldPass()
+                    testMethod('catAOk3').shouldPass()
+                    testMethod('catBOk4').withCategoryOrTag('CategoryB').shouldPass()
+                }
+            testClass('CategoryCTests')
+                .withCategoryOrTag('CategoryC')
+                .with {
+                    testMethod('catCOk1').shouldPass()
+                    testMethod('catCOk2').shouldPass()
+                    testMethod('catCOk3').shouldPass()
+                    testMethod('catCOk4').shouldPass()
+                }
+            testClass('CategoryDTests')
+                .withCategoryOrTag('CategoryD')
+                .with {
+                    testMethod('catDOk1').shouldPass()
+                    testMethod('catDOk2').shouldPass()
+                    testMethod('catDOk3').shouldPass()
+                    testMethod('catDOk4').shouldPass()
+                }
+            testClass('CategoryZTests')
+                .withCategoryOrTag('CategoryZ')
+                .with {
+                    testMethod('catZOk1').shouldPass()
+                    testMethod('catZOk2').shouldPass()
+                    testMethod('catZOk3').shouldPass()
+                    testMethod('catZOk4').shouldPass()
+                }
+            testClass('MixedTests')
+                .with {
+                    testMethod('catAOk1').withCategoryOrTag('CategoryA').shouldPass()
+                    testMethod('catBOk2').withCategoryOrTag('CategoryB').shouldPass()
+                    testMethod('ignoredWithCategoryA').withCategoryOrTag('CategoryA').ignoreOrDisable()
+                    testMethod('noCatOk4').shouldPass()
+                }
+            testClass('NoCategoryTests')
+                .with {
+                    testMethod('noCatOk1').shouldPass()
+                    testMethod('noCatOk2').shouldPass()
+                    testMethod('noCatOk3').shouldPass()
+                    testMethod('noCatOk4').shouldPass()
+                }
+            testCategory('CategoryA')
+            testCategory('CategoryB').extendsCategory('CategoryA')
+            testCategory('CategoryC')
+            testCategory('CategoryD').extendsCategory('CategoryC')
+            testCategory('CategoryZ')
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                ${configureTestFramework} {
+                    ${includeCategoryOrTag('CategoryA')}
+                    ${excludeCategoryOrTag('CategoryC')}
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('CategoryATests', 'CategoryBTests', 'CategoryADTests', 'MixedTests')
+        result.testClass("CategoryATests").assertTestCount(4, 0, 0)
+        result.testClass("CategoryATests").assertTestsExecuted('catAOk1', 'catAOk2', 'catAOk3', 'catAOk4')
+        result.testClass("CategoryBTests").assertTestCount(4, 0, 0)
+        result.testClass("CategoryBTests").assertTestsExecuted('catBOk1', 'catBOk2', 'catBOk3', 'catBOk4')
+        result.testClass("CategoryADTests").assertTestCount(2, 0, 0)
+        result.testClass("CategoryADTests").assertTestsExecuted('catAOk1', 'catAOk2')
+        result.testClass("MixedTests").assertTestCount(3, 0, 0)
+        result.testClass("MixedTests").assertTestsExecuted('catAOk1', 'catBOk2')
+        result.testClass("MixedTests").assertTestsSkipped('ignoredWithCategoryA')
+    }
+
+    def "can combine categories with custom runner"() {
+        given:
+        testSources.with {
+            testCategory('CategoryA')
+            sourceFile('LocaleHolder.java').withSource("""
+                import java.util.Locale;
+
+                class LocaleHolder {
+                    private static Locale locale;
+
+                    public static Locale set(Locale locale) {
+                        Locale old = LocaleHolder.locale;
+                        LocaleHolder.locale = locale;
+                        return old;
+                    }
+                    public static Locale get() {
+                        return locale;
+                    }
+                }
+            """)
+            sourceFile('Locales.java').withSource("""
+                import org.junit.runner.Runner;
+                import org.junit.runners.BlockJUnit4ClassRunner;
+                import org.junit.runners.Suite;
+                import org.junit.runners.model.FrameworkMethod;
+                import org.junit.runners.model.InitializationError;
+                import org.junit.runners.model.Statement;
+
+                import java.util.ArrayList;
+                import java.util.Arrays;
+                import java.util.List;
+                import java.util.Locale;
+
+                public class Locales extends Suite {
+                    private static final Iterable<Locale> localesToUse = Arrays.asList(Locale.FRENCH, Locale.GERMAN, Locale.ENGLISH);
+
+                    public Locales(Class<?> klass) throws InitializationError {
+                        super(klass, extractAndCreateRunners(klass));
+                    }
+
+                    private static List<Runner> extractAndCreateRunners(Class<?> klass) throws InitializationError {
+                        List<Runner> runners = new ArrayList<Runner>();
+                        for (Locale locale : localesToUse) {
+                            runners.add(new LocalesRunner(locale, klass));
+                        }
+                        return runners;
+                    }
+
+                    private static class LocalesRunner extends BlockJUnit4ClassRunner {
+                        private final Locale locale;
+
+                        LocalesRunner(Locale locale, Class<?> klass) throws InitializationError {
+                            super(klass);
+                            this.locale = locale;
+                        }
+
+                        @Override
+                        protected Statement methodBlock(final FrameworkMethod method) {
+                            return new Statement() {
+                                @Override
+                                public void evaluate() throws Throwable {
+                                    Locale oldLocale = LocaleHolder.set(locale);
+                                    try {
+                                        LocalesRunner.super.methodBlock(method).evaluate();
+                                    } finally {
+                                        LocaleHolder.set(oldLocale);
+                                    }
+                                }
+                            };
+                        }
+
+                        @Override// The name of the test class
+                        protected String getName() {
+                            return String.format("%s [%s]", super.getName(), locale);
+                        }
+
+                        @Override// The name of the test method
+                        protected String testName(final FrameworkMethod method) {
+                            return String.format("%s [%s]", method.getName(), locale);
+                        }
+                    }
+
+                }
+            """)
+            sourceFile("SomeLocaleTests.java").withSource("""
+                import org.junit.Test;
+                import org.junit.experimental.categories.Category;
+                import org.junit.runner.RunWith;
+
+                @RunWith(Locales.class)
+                public class SomeLocaleTests {
+                    @Test
+                    public void ok1() {
+                        System.out.println("Locale in use: " + LocaleHolder.get());
+                    }
+
+                    @Test
+                    @Category(CategoryA.class)
+                    public void ok2() {
+                        System.out.println("Locale in use: " + LocaleHolder.get());
+                    }
+                }
+            """)
+            sourceFile("SomeMoreLocaleTests.java").withSource("""
+                import org.junit.Test;
+                import org.junit.experimental.categories.Category;
+                import org.junit.runner.RunWith;
+
+                @RunWith(Locales.class)
+                @Category(CategoryA.class)
+                public class SomeMoreLocaleTests {
+                    @Test
+                    public void someMoreTest1() {
+                        System.out.println("Locale in use: " + LocaleHolder.get());
+                    }
+
+                    @Test
+                    public void someMoreTest2() {
+                        System.out.println("Locale in use: " + LocaleHolder.get());
+                    }
+                }
+            """)
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                ${configureTestFramework} {
+                    ${excludeCategoryOrTag('CategoryA')}
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('SomeLocaleTests')
+        result.testClass("SomeLocaleTests").assertTestCount(3, 0, 0)
+        result.testClass("SomeLocaleTests").assertTestsExecuted('ok1 [de]', 'ok1 [en]', 'ok1 [fr]')
+    }
+
+    def "can run parameterized tests with categories"() {
+        given:
+        testSources.with {
+            testCategory('SomeCategory')
+            sourceFile("NestedTestsWithCategories.java").withSource("""
+                import org.junit.Test;
+                import org.junit.experimental.categories.Category;
+                import org.junit.runner.RunWith;
+                import org.junit.runners.Parameterized;
+
+                import java.util.ArrayList;
+
+                import static org.junit.Assert.fail;
+
+                public class NestedTestsWithCategories {
+
+                    @Category(SomeCategory.class)
+                    @RunWith(Parameterized.class)
+                    public static class TagOnClass {
+                        @Parameterized.Parameters
+                        public static Iterable<Object[]> getParameters() {
+                            ArrayList<Object[]> parameters = new ArrayList<>();
+                            parameters.add(new Object[] { "tag on class" });
+                            return parameters;
+                        }
+
+                        private final String param;
+
+                        public TagOnClass(String param) {
+                            this.param = param;
+                        }
+
+                        @Test
+                        public void run() {
+                            System.err.println("executed " + param);
+                        }
+                    }
+
+                    @RunWith(Parameterized.class)
+                    public static class TagOnMethod {
+                        @Parameterized.Parameters
+                        public static Iterable<Object[]> getParameters() {
+                            ArrayList<Object[]> parameters = new ArrayList<>();
+                            parameters.add(new Object[] { "tag on method" });
+                            return parameters;
+                        }
+
+                        private final String param;
+
+                        public TagOnMethod(String param) {
+                            this.param = param;
+                        }
+
+                        @Test
+                        @Category(SomeCategory.class)
+                        public void run() {
+                            System.err.println("executed " + param);
+                        }
+
+                        @Test
+                        public void filteredOut() {
+                            throw new AssertionError("should be filtered out");
+                        }
+                    }
+
+                    public static class TagOnMethodNoParam {
+                        @Test
+                        @Category(SomeCategory.class)
+                        public void run() {
+                            System.err.println("executed tag on method (no param)");
+                        }
+
+                        @Test
+                        public void filteredOut() {
+                            throw new AssertionError("should be filtered out");
+                        }
+                    }
+
+                    @RunWith(Parameterized.class)
+                    public static class Untagged {
+                        @Parameterized.Parameters
+                        public static Iterable<Object[]> getParameters() {
+                            ArrayList<Object[]> parameters = new ArrayList<>();
+                            parameters.add(new Object[] { "untagged" });
+                            return parameters;
+                        }
+
+                        private final String param;
+
+                        public Untagged(String param) {
+                            this.param = param;
+                        }
+
+                        @Test
+                        public void run() {
+                            System.err.println("executed " + param);
+                        }
+                    }
+                }
+            """)
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                ${configureTestFramework} {
+                    ${includeCategoryOrTag('SomeCategory')}
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        def expectedTestClasses = ['NestedTestsWithCategories$TagOnMethodNoParam', 'NestedTestsWithCategories$TagOnMethod']
+        if (supportsCategoryOnNestedClass()) {
+            expectedTestClasses << 'NestedTestsWithCategories$TagOnClass'
+        }
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted(expectedTestClasses as String[])
+        expectedTestClasses.each {
+            result.testClass(it).assertTestCount(1, 0, 0)
+        }
+    }
+
+    private class JUnit4TestSourceGenerator implements AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestSourceGenerator {
+        @Override
+        void writeAllSources(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestSourceFixture fixture) {
+            fixture.testClasses.each { testClass ->
+                writeTestClass(testClass)
+            }
+            fixture.categories.each { category ->
+                writeCategoryClass(category)
+            }
+            fixture.sources.each { source ->
+                writeSourceFile(source)
+            }
+        }
+
+        private TestFile writeTestClass(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestClass testClass) {
+            String packagePath = testClass.packageName.replace('.', '/')
+            testDirectory.file("src/${testClass.sourceSet}/java/${packagePath}/${testClass.name}.java") << """
+                    ${testClass.packageName}
+
+                    import org.junit.Test;
+                    import org.junit.Ignore;
+                    import org.junit.experimental.categories.Category;
+
+                    ${categoryAnnotation(testClass.categoriesOrTags)}
+                    public class ${testClass.name} {
+                        ${testClass.methods.collect { generateTestMethod(it) }.join('\n')}
+                    }
+                """.stripIndent()
+        }
+
+        private TestFile writeCategoryClass(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.Category category) {
+            String packagePath = category.packageName.replace('.', '/')
+            testDirectory.file("src/${category.sourceSet}/java/${packagePath}/${category.name}.java") << """
+                    ${category.packageName}
+
+                    import org.junit.experimental.categories.Category;
+
+                    public interface ${category.name} ${!category.extendsCategories.isEmpty() ? "extends ${category.extendsCategories.join(', ')}" : ""} {
+                    }
+                """.stripIndent()
+        }
+
+        private TestFile writeSourceFile(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestSource testSource) {
+            testDirectory.file("src/test/java/${testSource.relativePath}") << testSource.source.stripIndent()
+        }
+
+        private String categoryAnnotation(List<String> categories) {
+            if (!categories.isEmpty()) {
+                return "@Category({${categories.collect { it + '.class' }.join(', ')}})"
+            } else {
+                return ""
+            }
+        }
+
+        private String generateTestMethod(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestMethod method) {
+            return """
+                @Test
+                ${method.isIgnoredOrDisabled ? "@Ignore" : ""}
+                ${categoryAnnotation(method.categoriesOrTags)}
+                public void ${method.name}() {
+                    ${method.shouldPass ? "assert true;" : "assert false;"}
+                }
+            """
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4ClassLevelFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4ClassLevelFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..618300c
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4ClassLevelFilteringIntegrationTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitClassLevelFilteringIntegrationTest
+
+abstract class AbstractJUnit4ClassLevelFilteringIntegrationTest extends AbstractJUnitClassLevelFilteringIntegrationTest {
+    def "runs all tests for class instead of method when runner is not filterable"() {
+        file("src/test/java/FooTest.java") << """
+            import org.junit.*;
+            import org.junit.runner.*;
+            @RunWith(SomeRunner.class)
+            public class FooTest {
+            }
+        """.stripIndent()
+        file("src/test/java/SomeRunner.java") << """
+            import org.junit.*;
+            import org.junit.runner.*;
+            import org.junit.runner.notification.*;
+            public class SomeRunner extends Runner {
+                Class<?> c;
+
+                public SomeRunner(Class<?> c) {
+                    this.c = c;
+                }
+
+                public Description getDescription() {
+                    Description suite = Description.createSuiteDescription(c.getName());
+                    suite.addChild(Description.createTestDescription(c, "pass"));
+                    suite.addChild(Description.createTestDescription(c, "other"));
+                    return suite;
+                }
+
+                public void run(RunNotifier notifier) {
+                    for(Description d: getDescription().getChildren()) {
+                        notifier.fireTestStarted(d);
+                        notifier.fireTestFinished(d);
+                    }
+                }
+            }
+        """.stripIndent()
+
+        when:
+        run("test", "--tests", "FooTest.pass")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("FooTest")
+        result.testClass("FooTest").assertTestsExecuted("other", "pass")
+
+        when:
+        fails("test", "--tests", "FooTest.ignored")
+
+        then:
+        failure.assertHasCause("No tests found for given includes: [FooTest.ignored]")
+
+        when:
+        fails("test", "--tests", "NotFooTest.pass")
+
+        then:
+        failure.assertHasCause("No tests found for given includes: [NotFooTest.pass]")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4FilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4FilteringIntegrationTest.groovy
new file mode 100644
index 0000000..851cc15
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4FilteringIntegrationTest.groovy
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.AbstractTestFilteringIntegrationTest
+import spock.lang.Issue
+
+abstract class AbstractJUnit4FilteringIntegrationTest extends AbstractTestFilteringIntegrationTest {
+
+    void theParameterizedFiles() {
+        file("src/test/java/ParameterizedFoo.java") << """
+            ${testFrameworkImports}
+            import org.junit.runners.Parameterized;
+            import org.junit.runners.Parameterized.Parameters;
+            import org.junit.runner.RunWith;
+            import java.util.Arrays;
+            import java.util.Collection;
+
+            @RunWith(Parameterized.class)
+            public class ParameterizedFoo {
+                int index;
+                public ParameterizedFoo(int index){
+                    this.index = index;
+                }
+
+                @Parameters
+                public static Collection data() {
+                   return Arrays.asList(new Object[][] {
+                      { 2 },
+                      { 6 },
+                      { 19 },
+                      { 22 },
+                      { 23 }
+                   });
+                }
+                @Test public void pass() {}
+                @Test public void fail() {}
+            }
+        """
+    }
+
+    void theSuiteFiles() {
+        file("src/test/java/FooTest.java") << """
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test
+                public void testFoo() { }
+            }
+        """
+        file("src/test/java/FooServerTest.java") << """
+            ${testFrameworkImports}
+            public class FooServerTest {
+                @Test
+                public void testFooServer() { }
+            }
+        """
+        file("src/test/java/BarTest.java") << """
+            ${testFrameworkImports}
+            public class BarTest {
+                @Test
+                public void testBar() { }
+            }
+        """
+        file("src/test/java/AllFooTests.java") << """
+            ${testFrameworkImports}
+            import org.junit.runners.Suite;
+            import org.junit.runners.Suite.SuiteClasses;
+            @RunWith(Suite.class)
+            @SuiteClasses({FooTest.class, FooServerTest.class})
+            public class AllFooTests {
+            }
+        """
+    }
+
+    @Issue("GRADLE-3112")
+    def "can filter parameterized tests from the build file."() {
+        given:
+        // this addition to the build file ...
+        buildFile << """
+            test {
+              filter {
+                includeTestsMatching "*ParameterizedFoo.pass*"
+              }
+            }
+        """
+        // and ...
+        theParameterizedFiles()
+
+        when:
+        run("test")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("ParameterizedFoo")
+        result.testClass("ParameterizedFoo").assertTestsExecuted("pass[0]", "pass[1]", "pass[2]", "pass[3]", "pass[4]")
+    }
+
+    @Issue("GRADLE-3112")
+    def "can filter parameterized tests from the command-line"() {
+        given:
+        theParameterizedFiles()
+
+        when:
+        run("test", "--tests", "*ParameterizedFoo.pass*")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("ParameterizedFoo")
+        result.testClass("ParameterizedFoo").assertTestsExecuted("pass[0]", "pass[1]", "pass[2]", "pass[3]", "pass[4]")
+    }
+
+    @Issue("GRADLE-3112")
+    def "passing a suite argument to --tests runs all tests in the suite"() {
+        given:
+        theSuiteFiles()
+
+        when:
+        run("test", "--tests", "*AllFooTests")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+
+        result.assertTestClassesExecuted("FooTest", "FooServerTest")
+        result.testClass("FooTest").assertTestCount(1, 0, 0);
+        result.testClass("FooTest").assertTestsExecuted("testFoo")
+        result.testClass("FooServerTest").assertTestCount(1, 0, 0);
+        result.testClass("FooServerTest").assertTestsExecuted("testFooServer")
+    }
+
+    @Issue("GRADLE-3112")
+    def "can filter test Suites from build file."() {
+        given:
+        // this addition to the build files ...
+        buildFile << """
+            test {
+              filter {
+                includeTestsMatching "*AllFooTests"
+              }
+            }
+        """
+        // and ...
+        theSuiteFiles()
+
+        when:
+        run("test")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+
+        result.assertTestClassesExecuted("FooTest", "FooServerTest")
+        result.testClass("FooTest").assertTestCount(1, 0, 0);
+        result.testClass("FooTest").assertTestsExecuted("testFoo")
+        result.testClass("FooServerTest").assertTestCount(1, 0, 0);
+        result.testClass("FooServerTest").assertTestsExecuted("testFooServer")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4IgnoreClassIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4IgnoreClassIntegrationTest.groovy
new file mode 100644
index 0000000..d5ec986
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4IgnoreClassIntegrationTest.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitIgnoreClassIntegrationTest
+
+abstract class AbstractJUnit4IgnoreClassIntegrationTest extends AbstractJUnitIgnoreClassIntegrationTest {
+    def "can handle class level ignored tests with custom runner"() {
+        given:
+        executer.noExtraLogging()
+        file ('src/test/java/org/gradle/CustomIgnoredTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+            import org.junit.runner.notification.Failure;
+            import org.junit.runner.notification.RunNotifier;
+
+            import java.util.ArrayList;
+            import java.util.List;
+
+            @Ignore
+            @RunWith(org.gradle.CustomIgnoredTest.TheRunner.class)
+            public class CustomIgnoredTest {
+                static int count = 0;
+
+                public boolean doSomething() {
+                    return true;
+                }
+
+                public static class TheRunner extends Runner {
+                    List descriptions = new ArrayList();
+                    private final Class<? extends org.gradle.CustomIgnoredTest> testClass;
+                    private final org.gradle.CustomIgnoredTest testContainingInstance;
+                    private Description testSuiteDescription;
+
+                    public TheRunner(Class<? extends org.gradle.CustomIgnoredTest> testClass) {
+                        this.testClass = testClass;
+                        testContainingInstance = reflectMeATestContainingInstance(testClass);
+                        testSuiteDescription = Description.createSuiteDescription("Custom Test with Suite ");
+                        testSuiteDescription.addChild(createTestDescription("first test run"));
+                        testSuiteDescription.addChild(createTestDescription("second test run"));
+                        testSuiteDescription.addChild(createTestDescription("third test run"));
+                    }
+
+                    @Override
+                    public Description getDescription() {
+                        return testSuiteDescription;
+                    }
+
+                    @Override
+                    public void run(RunNotifier notifier) {
+                        for (Description description : testSuiteDescription.getChildren()) {
+                            notifier.fireTestStarted(description);
+                            try {
+                                if (testContainingInstance.doSomething()) {
+                                    notifier.fireTestFinished(description);
+                                } else {
+                                    notifier.fireTestIgnored(description);
+                                }
+                            } catch (Exception e) {
+                                notifier.fireTestFailure(new Failure(description, e));
+                            }
+                        }
+                    }
+
+                    private org.gradle.CustomIgnoredTest reflectMeATestContainingInstance(Class<? extends org.gradle.CustomIgnoredTest> testClass) {
+                        try {
+                            return testClass.getConstructor().newInstance();
+                        } catch (Exception e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+
+                    private Description createTestDescription(String description) {
+                        return Description.createTestDescription(testClass, description);
+                    }
+                }
+            }
+        """
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """
+
+        when:
+        run('check')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.CustomIgnoredTest')
+        result.testClass('org.gradle.CustomIgnoredTest').assertTestCount(3, 0, 0).assertTestsSkipped("first test run", "second test run", "third test run")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4JUnitIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4JUnitIntegrationTest.groovy
new file mode 100644
index 0000000..d93699f
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4JUnitIntegrationTest.groovy
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitIntegrationTest
+import spock.lang.Issue
+
+abstract class AbstractJUnit4JUnitIntegrationTest extends AbstractJUnitIntegrationTest implements JUnit4CommonTestSources {
+    @Issue("https://issues.gradle.org//browse/GRADLE-3114")
+    def "creates runner before tests"() {
+        given:
+        file('src/test/java/org/gradle/CustomRunner.java') << """
+            package org.gradle;
+
+            import java.lang.reflect.Method;
+            import org.junit.runner.notification.RunNotifier;
+            import org.junit.runners.BlockJUnit4ClassRunner;
+            import org.junit.runners.model.FrameworkMethod;
+            import org.junit.runners.model.InitializationError;
+            import org.junit.runners.model.Statement;
+
+            public class CustomRunner extends BlockJUnit4ClassRunner {
+                public static boolean isClassUnderTestLoaded;
+                private final Class<?> bootstrappedTestClass;
+
+                public CustomRunner(Class<?> clazz) throws Exception {
+                    super(clazz);
+                    bootstrappedTestClass = clazz;
+                }
+
+                @Override
+                protected Statement methodBlock(final FrameworkMethod method) {
+                    return new Statement() {
+                        @Override
+                        public void evaluate() throws Throwable {
+
+                            if(isClassUnderTestLoaded){
+                                throw new RuntimeException("Test Class should not be loaded");
+                            }
+
+                            final HelperTestRunner helperTestRunner = new HelperTestRunner(bootstrappedTestClass);
+                            final Method bootstrappedMethod = bootstrappedTestClass.getMethod(method.getName());
+                            final Statement statement = helperTestRunner.methodBlock(new FrameworkMethod(bootstrappedMethod));
+                            statement.evaluate();
+                        }
+                    };
+                }
+
+                public class HelperTestRunner extends BlockJUnit4ClassRunner {
+                    public HelperTestRunner(Class<?> testClass) throws InitializationError {
+                        super(testClass);
+                    }
+
+                    @Override
+                    protected Object createTest() throws Exception {
+                        return super.createTest();
+                    }
+
+                    @Override
+                    public Statement classBlock(RunNotifier notifier) {
+                        return super.classBlock(notifier);
+                    }
+
+                    @Override
+                    public Statement methodBlock(FrameworkMethod method) {
+                        return super.methodBlock(method);
+                    }
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/ExecutionOrderTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            ${getRunOrExtendWithAnnotation('CustomRunner.class')}
+            public class ExecutionOrderTest {
+
+                static{
+                    CustomRunner.isClassUnderTestLoaded = true;
+                }
+
+                @Test
+                public void classUnderTestIsLoadedOnlyByRunner(){
+                    // The CustomRunner class will fail this test if this class is initialized before its
+                    // run method is triggered.
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test {
+                ${configureTestFramework}
+            }
+        """.stripIndent()
+
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.ExecutionOrderTest')
+        result.testClass('org.gradle.ExecutionOrderTest').assertTestPassed('classUnderTestIsLoadedOnlyByRunner')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4LoggingOutputCaptureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4LoggingOutputCaptureIntegrationTest.groovy
new file mode 100644
index 0000000..17904b4
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4LoggingOutputCaptureIntegrationTest.groovy
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.HtmlTestExecutionResult
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitLoggingOutputCaptureIntegrationTest
+
+import static org.hamcrest.CoreMatchers.is
+
+abstract class AbstractJUnit4LoggingOutputCaptureIntegrationTest extends AbstractJUnitLoggingOutputCaptureIntegrationTest {
+    def "captures logging output events"() {
+        file("src/test/java/OkTest.java") << """
+            ${testFrameworkImports}
+
+            public class OkTest {
+                static {
+                    System.out.println("class loaded");
+                }
+
+                public OkTest() {
+                    System.out.println("test constructed");
+                }
+
+                ${beforeClassAnnotation} public static void init() {
+                    System.out.println("before class out");
+                    System.err.println("before class err");
+                }
+
+                ${afterClassAnnotation} public static void end() {
+                    System.out.println("after class out");
+                    System.err.println("after class err");
+                }
+
+                ${beforeTestAnnotation}
+                public void before() {
+                    System.out.println("before out");
+                    System.err.println("before err");
+                }
+
+                ${afterTestAnnotation}
+                public void after() {
+                    System.out.println("after out");
+                    System.err.println("after err");
+                }
+
+                @Test
+                public void ok() {
+                    System.out.print("test out: \u03b1</html>");
+                    System.out.println();
+                    System.err.println("test err");
+                }
+
+                @Test
+                public void anotherOk() {
+                    System.out.println("ok out");
+                    System.err.println("ok err");
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds "test"
+
+        then:
+
+            // Behavior change of JUnit 4.13
+            outputContains(
+                "Test class OkTest -> class loaded\n" +
+                "Test class OkTest -> before class out\n" +
+                "Test class OkTest -> before class err\n" +
+                "Test anotherOk(OkTest) -> test constructed\n" +
+                "Test anotherOk(OkTest) -> before out\n" +
+                "Test anotherOk(OkTest) -> before err\n" +
+                "Test anotherOk(OkTest) -> ok out\n" +
+                "Test anotherOk(OkTest) -> ok err\n" +
+                "Test anotherOk(OkTest) -> after out\n" +
+                "Test anotherOk(OkTest) -> after err\n" +
+                "Test ok(OkTest) -> test constructed\n" +
+                "Test ok(OkTest) -> before out\n" +
+                "Test ok(OkTest) -> before err\n" +
+                "Test ok(OkTest) -> test out: \u03b1</html>\n" +
+                "Test ok(OkTest) -> test err\n" +
+                "Test ok(OkTest) -> after out\n" +
+                "Test ok(OkTest) -> after err\n" +
+                "Test class OkTest -> after class out\n" +
+                "Test class OkTest -> after class err\n"
+            )
+
+            // This test covers current behaviour, not necessarily desired behaviour
+
+            def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+            def classResult = xmlReport.testClass("OkTest")
+            classResult.assertTestCaseStdout("ok", is(
+                "test constructed\n" +
+                "before out\n" +
+                "test out: \u03b1</html>\n" +
+                "after out\n"
+            ))
+            classResult.assertTestCaseStderr("ok", is(
+                "before err\n" +
+                "test err\n" +
+                "after err\n"
+            ))
+            classResult.assertStdout(is(
+                "class loaded\n" +
+                "before class out\n" +
+                "after class out\n"
+            ))
+            classResult.assertStderr(is(
+                "before class err\n" +
+                "after class err\n"
+            ))
+
+
+        def htmlReport = new HtmlTestExecutionResult(testDirectory)
+        def classReport = htmlReport.testClass("OkTest")
+        classReport.assertStdout(is(
+            "class loaded\n" +
+            "before class out\n" +
+            "test constructed\n" +
+            "before out\n" +
+            "ok out\n" +
+            "after out\n" +
+            "test constructed\n" +
+            "before out\n" +
+            "test out: \u03b1</html>\n" +
+            "after out\n" +
+            "after class out\n"
+        ))
+        classReport.assertStderr(is(
+            "before class err\n" +
+            "before err\n" +
+            "ok err\n" +
+            "after err\n" +
+            "before err\n" +
+            "test err\n" +
+            "after err\n" +
+            "after class err\n"
+        ))
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4SuitesIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4SuitesIntegrationTest.groovy
new file mode 100644
index 0000000..c381253
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4SuitesIntegrationTest.groovy
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitSuitesIntegrationTest
+import org.junit.Assume
+
+import static org.hamcrest.CoreMatchers.containsString
+
+abstract class AbstractJUnit4SuitesIntegrationTest extends AbstractJUnitSuitesIntegrationTest {
+    abstract boolean supportsSuiteOutput()
+    abstract String getTestFrameworkJUnit3Dependencies()
+
+    @Override
+    String getTestFrameworkSuiteImports() {
+        return """
+            import org.junit.runner.RunWith;
+            import org.junit.runners.Suite;
+        """.stripIndent()
+    }
+
+    @Override
+    String getTestFrameworkSuiteAnnotations(String classes) {
+        return """
+            @RunWith(Suite.class)
+            @Suite.SuiteClasses({ ${classes} })
+        """.stripIndent()
+    }
+
+    @Override
+    String getTestFrameworkSuiteDependencies() {
+        return ""
+    }
+
+    def "suite output is visible"() {
+        Assume.assumeTrue(supportsSuiteOutput())
+
+        given:
+        file('src/test/java/org/gradle/ASuite.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+            import org.junit.runner.RunWith;
+            import org.junit.runners.Suite;
+
+            @RunWith(Suite.class)
+            @Suite.SuiteClasses({ OkTest.class, OtherTest.class })
+            public class ASuite {
+                static {
+                    System.out.println("suite class loaded");
+                }
+
+                ${beforeClassAnnotation} public static void init() {
+                    System.out.println("before suite class out");
+                    System.out.println("non-asci char: ż");
+                    System.err.println("before suite class err");
+                }
+
+                ${afterClassAnnotation} public static void end() {
+                    System.out.println("after suite class out");
+                    System.err.println("after suite class err");
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/OkTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class OkTest {
+
+                @Test
+                public void ok() throws Exception {
+                    System.err.println("This is test stderr");
+                }
+
+                @Test
+                public void anotherOk() {
+                    System.out.println("sys out from another test method");
+                    System.err.println("sys err from another test method");
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/OtherTest.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            public class OtherTest {
+
+                @Test
+                public void ok() throws Exception {
+                    System.out.println("This is other stdout");
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test {
+                ${configureTestFramework}
+                include '**/ASuite.class'
+                exclude '**/*Test.class'
+            }
+        """.stripIndent()
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.ASuite', 'org.gradle.OkTest', 'org.gradle.OtherTest')
+        result.testClass('org.gradle.ASuite').assertStdout(containsString('suite class loaded'))
+        result.testClass('org.gradle.ASuite').assertStdout(containsString('before suite class out'))
+        result.testClass('org.gradle.ASuite').assertStdout(containsString('non-asci char: ż'))
+        result.testClass('org.gradle.ASuite').assertStderr(containsString('before suite class err'))
+        result.testClass('org.gradle.ASuite').assertStdout(containsString('after suite class out'))
+        result.testClass('org.gradle.ASuite').assertStderr(containsString('after suite class err'))
+        result.testClass('org.gradle.OkTest').assertStderr(containsString('This is test stderr'))
+        result.testClass('org.gradle.OkTest').assertStdout(containsString('sys out from another test method'))
+        result.testClass('org.gradle.OkTest').assertStderr(containsString('sys err from another test method'))
+        result.testClass('org.gradle.OtherTest').assertStdout(containsString('This is other stdout'))
+    }
+
+    def "supports Junit3 suites"() {
+        given:
+        file('src/test/java/org/gradle/SomeSuite.java') << """
+            package org.gradle;
+
+            import junit.extensions.TestSetup;
+            import junit.framework.Test;
+            import junit.framework.TestCase;
+            import junit.framework.TestSuite;
+
+            public class SomeSuite extends TestCase {
+
+                public static Test suite() {
+                    final TestSuite suite = new TestSuite();
+                    suite.addTestSuite(SomeTest1.class);
+                    suite.addTestSuite(SomeTest2.class);
+                    TestSetup wrappedSuite = new junit.extensions.TestSetup(suite) {
+
+                        protected void setUp() {
+                            System.out.println("stdout in TestSetup#setup");
+                            System.err.println("stderr in TestSetup#setup");
+                        }
+
+                        protected void tearDown() {
+                            System.out.println("stdout in TestSetup#teardown");
+                            System.err.println("stderr in TestSetup#teardown");
+                        }
+                    };
+
+                    return wrappedSuite;
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeTest1.java') << """
+            package org.gradle;
+
+            import junit.framework.TestCase;
+
+            public class SomeTest1 extends TestCase {
+                public void testOk1(){
+                }
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/SomeTest2.java') << """
+            package org.gradle;
+
+            import junit.framework.TestCase;
+
+            public class SomeTest2 extends TestCase {
+                public void testOk2(){
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkJUnit3Dependencies}
+            }
+            test {
+                ${configureTestFramework}
+                include '**/*Suite.class'
+                exclude '**/*Test.class'
+            }
+        """.stripIndent()
+
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.SomeTest1', 'org.gradle.SomeTest2', 'org.gradle.SomeSuite')
+        result.testClass("org.gradle.SomeTest1").assertTestCount(1, 0, 0)
+        result.testClass("org.gradle.SomeTest1").assertTestsExecuted("testOk1")
+        result.testClass("org.gradle.SomeTest2").assertTestCount(1, 0, 0)
+        result.testClass("org.gradle.SomeTest2").assertTestsExecuted("testOk2")
+        result.testClass("org.gradle.SomeSuite").assertTestCount(0, 0, 0)
+        if (supportsSuiteOutput()) {
+            result.testClass("org.gradle.SomeSuite").assertStdout(containsString("stdout in TestSetup#setup"))
+            result.testClass("org.gradle.SomeSuite").assertStderr(containsString("stderr in TestSetup#setup"))
+            // JUnit3 suite teardown output does not seem to get captured with Vintage (even with 5.9.0)
+            // TODO need to investigate whether this is a bug in JUnit or in Gradle testing or what
+            //result.testClass("org.gradle.SomeSuite").assertStdout(containsString("stdout in TestSetup#teardown"))
+            //result.testClass("org.gradle.SomeSuite").assertStderr(containsString("stderr in TestSetup#teardown"))
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestClassDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..40210f7
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestClassDetectionIntegrationTest.groovy
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitTestClassDetectionIntegrationTest
+
+abstract class AbstractJUnit4TestClassDetectionIntegrationTest extends AbstractJUnitTestClassDetectionIntegrationTest {
+    // TODO: See if there is some way we can implement the custom runner class as a JUnit Jupiter extension
+    def "detects test classes"() {
+        given:
+        file('src/test/java/org/gradle/AbstractHasRunWith.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            ${getRunOrExtendWithAnnotation('CustomRunnerOrExtension.class')}
+            public abstract class AbstractHasRunWith {
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/EmptyRunWithSubclass.java') << """
+            package org.gradle;
+
+            public class EmptyRunWithSubclass extends AbstractHasRunWith {
+
+            }
+        """.stripIndent()
+        file('src/test/java/org/gradle/TestsOnInner.java') << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+            import junit.framework.TestCase;
+
+            public class TestsOnInner {
+                @Test public void ok() { }
+
+                public static class SomeInner {
+                    @Test public void ok() { }
+                }
+
+                public class NonStaticInner {
+                    @Test public void ok() { }
+                }
+
+                public class NonStaticInnerTestCase extends TestCase {
+                    public void testOk() { }
+                }
+            }
+        """.stripIndent()
+        writeCustomRunnerClass('CustomRunnerOrExtension')
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        when:
+        executer.withTasks('test').run()
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.EmptyRunWithSubclass', 'org.gradle.TestsOnInner', 'org.gradle.TestsOnInner$SomeInner')
+        result.testClass('org.gradle.EmptyRunWithSubclass').assertTestsExecuted('ok')
+        result.testClass('org.gradle.EmptyRunWithSubclass').assertTestPassed('ok')
+        result.testClass('org.gradle.TestsOnInner').assertTestPassed('ok')
+        result.testClass('org.gradle.TestsOnInner$SomeInner').assertTestPassed('ok')
+    }
+
+    void writeCustomRunnerClass(String className) {
+        file("src/test/java/org/gradle/${className}.java") << """
+            package org.gradle;
+
+            import org.junit.runner.*;
+            import org.junit.runner.notification.RunNotifier;
+
+            public class ${className} extends Runner {
+                private final Description description;
+
+                public ${className}(Class type) throws Exception {
+                    description = Description.createSuiteDescription(type);
+                    description.addChild(Description.createTestDescription(type, "ok"));
+                }
+
+                @Override
+                public Description getDescription() {
+                    return description;
+                }
+
+                @Override
+                public void run(RunNotifier notifier) {
+                    for (Description child : description.getChildren()) {
+                        notifier.fireTestStarted(child);
+                        notifier.fireTestFinished(child);
+                    }
+                }
+            }
+        """.stripIndent()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestEnvironmentIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestEnvironmentIntegrationTest.groovy
new file mode 100644
index 0000000..4722546f
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestEnvironmentIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.testing.AbstractTestEnvironmentIntegrationTest
+
+abstract class AbstractJUnit4TestEnvironmentIntegrationTest extends AbstractTestEnvironmentIntegrationTest implements JUnit4CommonTestSources {
+    @Override
+    String getModuleName() {
+        return "junit"
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestFailureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestFailureIntegrationTest.groovy
new file mode 100644
index 0000000..6fb3f93
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestFailureIntegrationTest.groovy
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.testing.junit.AbstractJUnitTestFailureIntegrationTest
+
+abstract class AbstractJUnit4TestFailureIntegrationTest extends AbstractJUnitTestFailureIntegrationTest {
+    @Override
+    void writeBrokenRunnerOrExtension(String className) {
+        file("src/test/java/org/gradle/${className}.java") << """
+            package org.gradle;
+
+            import org.junit.runner.Description;
+            import org.junit.runner.Runner;
+            import org.junit.runner.notification.RunNotifier;
+
+            public class ${className} extends Runner {
+                private final Class<?> type;
+
+                public BrokenRunnerOrExtension(Class<?> type) {
+                    this.type = type;
+                }
+
+                @Override
+                public Description getDescription() {
+                    return Description.createSuiteDescription(type);
+                }
+
+                @Override
+                public void run(RunNotifier notifier) {
+                    throw new UnsupportedOperationException("broken");
+                }
+            }
+        """.stripIndent()
+    }
+
+    @Override
+    void writeClassUsingBrokenRunnerOrExtension(String className, String runnerOrExtensionName) {
+        file("src/test/java/org/gradle/${className}.java") << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            @RunWith(${runnerOrExtensionName}.class)
+            public class ${className} {
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+    }
+
+    @Override
+    String getAssertionFailureClassName() {
+        return "java.lang.AssertionError"
+    }
+
+
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestListenerBuildOperationAdapterIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestListenerBuildOperationAdapterIntegrationTest.groovy
new file mode 100644
index 0000000..04099e5
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestListenerBuildOperationAdapterIntegrationTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.testing.AbstractTestListenerBuildOperationAdapterIntegrationTest
+
+/**
+ * Base class for JUnit 4 build operation adapter integration tests.  Provides JUnit4-specific tests and test sources for both JUnit4 and JUnit Vintage.
+ */
+abstract class AbstractJUnit4TestListenerBuildOperationAdapterIntegrationTest extends AbstractTestListenerBuildOperationAdapterIntegrationTest {
+    @Override
+    void writeTestSources() {
+        file('src/test/java/org/gradle/ASuite.java') << """
+            package org.gradle;
+            import org.junit.runner.RunWith;
+            import org.junit.runners.Suite;
+            @RunWith(Suite.class)
+            @Suite.SuiteClasses({OkTest.class, OtherTest.class })
+            public class ASuite {
+                static {
+                    System.out.println("suite class loaded");
+                }
+            }
+        """
+        file('src/test/java/org/gradle/OkTest.java') << """
+            package org.gradle;
+            import org.junit.Test;
+            public class OkTest {
+                @Test
+                public void ok() throws Exception {
+                    System.err.println("This is test stderr");
+                }
+
+                @Test
+                public void anotherOk() {
+                    System.out.println("sys out from another test method");
+                    System.err.println("sys err from another test method");
+                }
+            }
+        """
+        file('src/test/java/org/gradle/OtherTest.java') << """
+            package org.gradle;
+            import org.junit.Test;
+            public class OtherTest {
+                @Test
+                public void ok() throws Exception {
+                    System.out.println("This is other stdout");
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestListenerIntegrationTest.groovy
new file mode 100644
index 0000000..c2d053f
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestListenerIntegrationTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.executer.ExecutionResult
+import org.gradle.testing.junit.AbstractJUnitTestListenerIntegrationTest
+
+import static org.gradle.util.Matchers.containsLine
+
+abstract class AbstractJUnit4TestListenerIntegrationTest extends AbstractJUnitTestListenerIntegrationTest {
+    abstract String getTestFrameworkJUnit3Dependencies()
+
+    @Override
+    String getAssertionError() {
+        return "java.lang.AssertionError"
+    }
+
+    def "can listen for test results when JUnit3 is used"() {
+        given:
+        file('src/test/java/SomeTest.java') << """
+            public class SomeTest extends junit.framework.TestCase {
+                public void testPass() { }
+                public void testFail() { junit.framework.Assert.fail(\"message\"); }
+                public void testError() { throw new RuntimeException(\"message\"); }
+            }
+        """.stripIndent()
+
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkJUnit3Dependencies}
+            }
+            def listener = new TestListenerImpl()
+            test {
+                ${configureTestFramework}
+                addTestListener(listener)
+                ignoreFailures = true
+            }
+            class TestListenerImpl implements TestListener {
+                void beforeSuite(TestDescriptor suite) { println "START [\$suite] [\$suite.name]" }
+                void afterSuite(TestDescriptor suite, TestResult result) { println "FINISH [\$suite] [\$suite.name]" }
+                void beforeTest(TestDescriptor test) { println "START [\$test] [\$test.name]" }
+                void afterTest(TestDescriptor test, TestResult result) { println "FINISH [\$test] [\$test.name] [\$result.exception]" }
+            }
+        """.stripIndent()
+
+        when:
+        ExecutionResult result = executer.withTasks("test").run()
+
+        then:
+        assert containsLine(result.getOutput(), "START [Test class SomeTest] [SomeTest]")
+        assert containsLine(result.getOutput(), "FINISH [Test class SomeTest] [SomeTest]")
+        assert containsLine(result.getOutput(), "START [Test testPass(SomeTest)] [testPass]")
+        assert containsLine(result.getOutput(), "FINISH [Test testPass(SomeTest)] [testPass] [null]")
+        assert containsLine(result.getOutput(), "START [Test testFail(SomeTest)] [testFail]")
+        assert containsLine(result.getOutput(), "FINISH [Test testFail(SomeTest)] [testFail] [junit.framework.AssertionFailedError: message]")
+        assert containsLine(result.getOutput(), "START [Test testError(SomeTest)] [testError]")
+        assert containsLine(result.getOutput(), "FINISH [Test testError(SomeTest)] [testError] [java.lang.RuntimeException: message]")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestReportIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestReportIntegrationTest.groovy
new file mode 100644
index 0000000..e512c7b
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestReportIntegrationTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.testing.AbstractTestReportIntegrationTest
+
+import static org.hamcrest.CoreMatchers.is
+
+abstract class AbstractJUnit4TestReportIntegrationTest extends AbstractTestReportIntegrationTest implements JUnit4CommonTestSources {
+    def "outputs over lifecycle"() {
+        when:
+        buildScript """
+            $junitSetup
+            test.reports.junitXml.outputPerTestCase = true
+        """
+
+        file("src/test/java/OutputLifecycleTest.java") << """
+            ${testFrameworkImports}
+
+            public class OutputLifecycleTest {
+
+                public OutputLifecycleTest() {
+                    System.out.println("constructor out");
+                    System.err.println("constructor err");
+                }
+
+                ${beforeClassAnnotation}
+                public static void beforeClass() {
+                    System.out.println("beforeClass out");
+                    System.err.println("beforeClass err");
+                }
+
+                ${beforeTestAnnotation}
+                public void beforeTest() {
+                    System.out.println("beforeTest out");
+                    System.err.println("beforeTest err");
+                }
+
+                @Test public void m1() {
+                    System.out.println("m1 out");
+                    System.err.println("m1 err");
+                }
+
+                @Test public void m2() {
+                    System.out.println("m2 out");
+                    System.err.println("m2 err");
+                }
+
+                ${afterTestAnnotation}
+                public void afterTest() {
+                    System.out.println("afterTest out");
+                    System.err.println("afterTest err");
+                }
+
+                ${afterClassAnnotation}
+                public static void afterClass() {
+                    System.out.println("afterClass out");
+                    System.err.println("afterClass err");
+                }
+            }
+        """
+
+        succeeds "test"
+
+        then:
+        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+        def clazz = xmlReport.testClass("OutputLifecycleTest")
+        // Output behavior change in JUnit 4.13
+        clazz.assertTestCaseStderr("m1", is("constructor err\nbeforeTest err\nm1 err\nafterTest err\n"))
+        clazz.assertTestCaseStderr("m2", is("constructor err\nbeforeTest err\nm2 err\nafterTest err\n"))
+        clazz.assertTestCaseStdout("m1", is("constructor out\nbeforeTest out\nm1 out\nafterTest out\n"))
+        clazz.assertTestCaseStdout("m2", is("constructor out\nbeforeTest out\nm2 out\nafterTest out\n"))
+        clazz.assertStderr(is("beforeClass err\nafterClass err\n"))
+        clazz.assertStdout(is("beforeClass out\nafterClass out\n"))
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestTaskIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestTaskIntegrationTest.groovy
new file mode 100644
index 0000000..2b41fc8
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/AbstractJUnit4TestTaskIntegrationTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.testing.AbstractTestTaskIntegrationTest
+
+/**
+ * Base class for JUnit 4 test task integration tests.  Provides JUnit4-specific tests and test sources for both JUnit4 and JUnit Vintage.
+ */
+abstract class AbstractJUnit4TestTaskIntegrationTest extends AbstractTestTaskIntegrationTest implements JUnit4CommonTestSources {
+    @Override
+    String getStandaloneTestClass() {
+        return testClass('MyTest')
+    }
+
+    @Override
+    String testClass(String className) {
+        return """
+            import org.junit.*;
+            import org.junit.experimental.categories.Category;
+
+            public class $className {
+               @Test
+               @Category(Fast.class)
+               public void fastTest() {
+                  System.out.println(System.getProperty("java.version"));
+                  Assert.assertEquals(1,1);
+               }
+
+               @Test
+               @Category(Slow.class)
+               public void slowTest() {
+                  System.out.println(System.getProperty("java.version"));
+                  Assert.assertEquals(1,1);
+               }
+
+               interface Fast {}
+               interface Slow {}
+            }
+        """.stripIndent()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4AssumptionsIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4AssumptionsIntegrationTest.groovy
new file mode 100644
index 0000000..8cc9e22
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4AssumptionsIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitAssumptionsIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.getASSUMPTIONS
+
+@TargetCoverage({ ASSUMPTIONS })
+class JUnit4AssumptionsIntegrationTest extends AbstractJUnitAssumptionsIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CategoriesNotSupportedIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CategoriesNotSupportedIntegrationTest.groovy
new file mode 100644
index 0000000..b05038d
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CategoriesNotSupportedIntegrationTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.AbstractSampleIntegrationTest
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+
+import static org.hamcrest.CoreMatchers.startsWith
+
+class JUnit4CategoriesNotSupportedIntegrationTest extends AbstractSampleIntegrationTest {
+
+    def "test task fails if categories not supported"() {
+        given:
+        file('src/test/java/org/gradle/SomeTest.java') << """
+            package org.gradle;
+
+            import org.junit.Test;
+
+            public class SomeTest {
+                @Test
+                public void ok() {
+                }
+
+                public void helpermethod() {
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: "java"
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                testImplementation "junit:junit:4.4"
+            }
+
+            test {
+                useJUnit {
+                    includeCategories 'org.gradle.CategoryA'
+                    excludeCategories 'org.gradle.CategoryC'
+                }
+            }
+        """
+
+        when:
+        fails('test')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass("org.gradle.SomeTest").assertTestFailed("initializationError", startsWith("org.gradle.api.GradleException: JUnit Categories defined but declared JUnit version does not support Categories."))
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CategoriesOrTagsCoverageIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CategoriesOrTagsCoverageIntegrationTest.groovy
new file mode 100644
index 0000000..186fcbf
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CategoriesOrTagsCoverageIntegrationTest.groovy
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
+import org.gradle.util.internal.VersionNumber
+import org.junit.Assume
+import spock.lang.Issue
+
+import static org.gradle.testing.fixture.JUnitCoverage.CATEGORIES
+import static org.hamcrest.CoreMatchers.startsWith
+
+@TargetCoverage({ CATEGORIES })
+class JUnit4CategoriesOrTagsCoverageIntegrationTest extends AbstractJUnit4CategoriesOrTagsCoverageIntegrationTest implements JUnit4MultiVersionTest {
+    String singularCategoryOrTagName = "category"
+    String pluralCategoryOrTagName = "categories"
+
+    @Override
+    boolean supportsCategoryOnNestedClass() {
+        return !(version in ['4.10', '4.11', '4.12'])
+    }
+
+    def 'reports unloadable #type'() {
+        given:
+        testSources.with {
+            testClass('SomeTestClass').with {
+                testMethod('ok1')
+                testMethod('ok2')
+            }
+        }
+        testSourceGenerator.writeAllSources(testSources)
+        buildFile << """
+            apply plugin: "java"
+
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework} { ${type} 'org.gradle.CategoryA' }
+        """.stripIndent()
+
+        when:
+        fails("test")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('SomeTestClass')
+        result.testClass("SomeTestClass").assertTestCount(1, 1, 0)
+        result.testClass("SomeTestClass").assertTestFailed("initializationError", startsWith("org.gradle.api.InvalidUserDataException: Can't load category class [org.gradle.CategoryA]"))
+
+        where:
+        type << ['includeCategories', 'excludeCategories']
+    }
+
+    def "supports categories and null test class description"() {
+        // Our custom runner class won't work with JUnit < 4.11
+        Assume.assumeTrue(VersionNumber.parse(version) >= VersionNumber.parse('4.11'))
+
+        given:
+        file('src/test/java/CategoryA.java') << """
+            public interface CategoryA { }
+        """
+        file('src/test/java/CustomRunner.java') << """
+            import org.junit.runner.Description;
+            import org.junit.runners.BlockJUnit4ClassRunner;
+            import org.junit.runners.model.FrameworkMethod;
+            import org.junit.runners.model.InitializationError;
+
+            public class CustomRunner extends BlockJUnit4ClassRunner {
+
+                public CustomRunner(Class<?> klass) throws InitializationError {
+                    super(klass);
+                }
+
+                /**
+                 * Returns a test Description with a null TestClass.
+                 * @param method method under test
+                 * @return a Description
+                 */
+                @Override
+                protected Description describeChild(FrameworkMethod method) {
+                    return Description.createTestDescription("Not a real class name", testName(method), "someSerializable");
+                }
+            }
+        """
+        file('src/test/java/DescriptionWithNullClassTest.java') << """
+            ${testFrameworkImports}
+
+            ${getRunOrExtendWithAnnotation('CustomRunner.class')}
+            public class DescriptionWithNullClassTest {
+                @Test
+                public void someTest() {
+                }
+            }
+
+        """
+        buildFile << """
+            apply plugin: "java"
+
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework} { ${excludeCategoryOrTag('org.gradle.CategoryA')} }
+        """.stripIndent()
+
+        when:
+        succeeds("test")
+
+        then:
+        executedAndNotSkipped(":test")
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        def testClass = result.testClass("Not a real class name")
+        testClass.assertTestCount(1, 0, 0)
+        testClass.assertTestPassed("someTest")
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/3189')
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
+    def "can work with PowerMock"() {
+        given:
+        file('src/test/java/FastTest.java') << '''
+            public interface FastTest {
+            }
+        '''.stripIndent()
+        file('src/test/java/MyTest.java') << """
+            ${testFrameworkImports}
+            import org.junit.experimental.categories.Category;
+            import org.powermock.modules.junit4.PowerMockRunner;
+            @RunWith(PowerMockRunner.class)
+            @Category(FastTest.class)
+            public class MyTest {
+                @Test
+                public void testMyMethod() {
+                    assertTrue("This is an error", false);
+                }
+            }
+        """.stripIndent()
+        buildFile << """
+            apply plugin: 'java'
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                ${testFrameworkDependencies}
+                testImplementation "org.powermock:powermock-api-mockito:1.6.5"
+                testImplementation "org.powermock:powermock-module-junit4:1.6.5"
+            }
+
+            test {
+                ${configureTestFramework} { ${includeCategoryOrTag('FastTest')} }
+            }
+        """.stripIndent()
+
+        when:
+        fails('test')
+
+        then:
+        outputContains('MyTest > testMyMethod FAILED')
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/4924')
+    def "re-executes test when options are changed in #suiteName"() {
+        given:
+        testSources.with {
+            ['test', 'integTest'].each { sourceSet ->
+                testClass('SomeTestClass', sourceSet).with {
+                    testMethod('ok1').withCategoryOrTag('CategoryA')
+                    testMethod('ok2').withCategoryOrTag('CategoryB')
+                }
+                testCategory('CategoryA', sourceSet)
+                testCategory('CategoryB', sourceSet)
+            }
+        }
+        testSourceGenerator.writeAllSources(testSources)
+        buildFile << """
+            testing {
+               apply plugin: 'java'
+               ${mavenCentralRepository()}
+               suites {
+                   $suiteDeclaration {
+                       useJUnit()
+                       targets {
+                           all {
+                               testTask.configure {
+                                   options {
+                                       includeCategories 'CategoryA'
+                                   }
+                               }
+                           }
+                       }
+                   }
+               }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds ":$task"
+
+        then:
+        executedAndNotSkipped ":$task"
+
+        when:
+        buildFile.text = """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            testing {
+               suites {
+                   $suiteDeclaration {
+                       useJUnit()
+                       targets {
+                           all {
+                               testTask.configure {
+                                   options {
+                                       includeCategories 'CategoryB'
+                                   }
+                               }
+                           }
+                       }
+                   }
+               }
+            }
+        """.stripIndent()
+
+        and:
+        succeeds ":$task"
+
+        then:
+        executedAndNotSkipped ":$task"
+
+        where:
+        suiteName   | suiteDeclaration              | task
+        'test'      | 'test'                        | 'test'
+        'integTest' | 'integTest(JvmTestSuite)'     | 'integTest'
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/4924')
+    def "skips test on re-run when options are NOT changed"() {
+        given:
+        testSources.with {
+            testClass('SomeTestClass').with {
+                testMethod('ok1').withCategoryOrTag('CategoryA')
+                testMethod('ok2').withCategoryOrTag('CategoryB')
+            }
+            testCategory('CategoryA')
+            testCategory('CategoryB')
+        }
+        testSourceGenerator.writeAllSources(testSources)
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            testing {
+               suites {
+                   test {
+                       useJUnit()
+                       targets {
+                           all {
+                               testTask.configure {
+                                   options {
+                                       includeCategories 'CategoryA'
+                                   }
+                               }
+                           }
+                       }
+                   }
+               }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds ':test'
+
+        then:
+        executedAndNotSkipped ':test'
+
+        when:
+        succeeds ':test'
+
+        then:
+        skipped ':test'
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ClassDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..361ffeb
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ClassDetectionIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitClassDetectionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4ClassDetectionIntegrationTest extends AbstractJUnitClassDetectionIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ClassLevelFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ClassLevelFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..268c624
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ClassLevelFilteringIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.LARGE_COVERAGE
+
+@TargetCoverage({ LARGE_COVERAGE })
+class JUnit4ClassLevelFilteringIntegrationTest extends AbstractJUnit4ClassLevelFilteringIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CommonTestSources.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CommonTestSources.groovy
new file mode 100644
index 0000000..3d74a8e
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4CommonTestSources.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+import static org.gradle.util.internal.VersionNumber.*
+
+trait JUnit4CommonTestSources {
+    static class JUnit4TestSourceConfiguration implements AbstractTestingMultiVersionIntegrationTest.TestSourceConfiguration {
+        String version
+
+        JUnit4TestSourceConfiguration(String version) {
+            this.version = version
+        }
+
+        @Override
+        String getTestFrameworkImports() {
+            return """
+                import org.junit.*;
+                import org.junit.runner.*;
+
+                import static org.junit.Assert.*;
+                ${maybeImportAssumptions}
+            """.stripIndent()
+        }
+
+        private String getMaybeImportAssumptions() {
+            def thisVersion = parse(version)
+            // The Assume class was only introduced in JUnit 4.4
+            if (thisVersion >= parse('4.4')) {
+                return """
+                    import static org.junit.Assume.*;
+                """.stripIndent()
+            } else {
+                return ""
+            }
+        }
+
+        @Override
+        String getBeforeClassAnnotation() {
+            return "@BeforeClass"
+        }
+
+        @Override
+        String getAfterClassAnnotation() {
+            return "@AfterClass"
+        }
+
+        @Override
+        String getBeforeTestAnnotation() {
+            return "@Before"
+        }
+
+        @Override
+        String getAfterTestAnnotation() {
+            return "@After"
+        }
+
+        @Override
+        String getRunOrExtendWithAnnotation(String runOrExtendWithClasses) {
+            return "@RunWith(${runOrExtendWithClasses})"
+        }
+
+        @Override
+        String maybeParentheses(String methodName) {
+            return methodName
+        }
+
+        @Override
+        String getIgnoreOrDisabledAnnotation() {
+            return "@Ignore"
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ConsoleLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ConsoleLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..5f504c7
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4ConsoleLoggingIntegrationTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitConsoleLoggingIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4ConsoleLoggingIntegrationTest extends AbstractJUnitConsoleLoggingIntegrationTest implements JUnit4MultiVersionTest {
+    @Override
+    String getMaybePackagePrefix() {
+        return 'org.gradle.'
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4EnclosedRunnerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4EnclosedRunnerIntegrationTest.groovy
new file mode 100644
index 0000000..81061b7
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4EnclosedRunnerIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitEnclosedRunnerIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4EnclosedRunnerIntegrationTest extends AbstractJUnitEnclosedRunnerIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4FailFastIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4FailFastIntegrationTest.groovy
new file mode 100644
index 0000000..329e094
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4FailFastIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractJvmFailFastIntegrationSpec
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUNIT4_VERSION
+
+@TargetCoverage({ [LATEST_JUNIT4_VERSION] })
+class JUnit4FailFastIntegrationTest extends AbstractJvmFailFastIntegrationSpec implements JUnit4MultiVersionTest{
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4FilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4FilteringIntegrationTest.groovy
new file mode 100644
index 0000000..0f3707f
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4FilteringIntegrationTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.LARGE_COVERAGE
+
+@TargetCoverage({ LARGE_COVERAGE })
+class JUnit4FilteringIntegrationTest extends AbstractJUnit4FilteringIntegrationTest implements JUnit4MultiVersionTest {
+    def 'filter as many classes as possible before sending to worker process'() {
+        given:
+        // We can know which class is sent to TestClassProcessor via afterSuite() hook method
+        // because JUnitTestClassProcessor will emit a test suite event for each loaded class.
+        // However, JUnitPlatformTestClassProcessor won't emit such event unless the class is executed.
+        // That's why we run test with JUnit 4 only.
+        file('src/test/java/org/gradle/FooTest.java') << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test public void test() {}
+            }
+        """
+        file('src/test/java/com/gradle/FooTest.java') << """
+            package com.gradle;
+            ${testFrameworkImports}
+            public class FooTest {
+                @Test public void test() {}
+            }
+        """
+        file('src/test/java/org/gradle/BarTest.java') << """
+            package org.gradle;
+            ${testFrameworkImports}
+            public class BarTest {
+                @Test public void test() {}
+            }
+        """
+        buildFile << """
+            test {
+                filter {
+                    includeTestsMatching "$pattern"
+                }
+                afterSuite { descriptor, result ->
+                    println descriptor
+                }
+            }
+        """
+
+        when:
+        if (successful) {
+            succeeds('test')
+        } else {
+            fails('test')
+        }
+
+        then:
+        includedClasses.every { output.contains(it) }
+        excludedClasses.every { !output.contains(it) }
+
+        where:
+        pattern             | includedClasses                                                    | excludedClasses        | successful
+        'FooTest'           | ['org.gradle.FooTest', 'com.gradle.FooTest']                       | ['org.gradle.BarTest'] | true
+        'FooTest.anyMethod' | ['org.gradle.FooTest', 'com.gradle.FooTest']                       | ['org.gradle.BarTest'] | false
+        'org.gradle.*'      | ['org.gradle.FooTest', 'org.gradle.BarTest']                       | ['com.gradle.FooTest'] | true
+        '*FooTest'          | ['org.gradle.FooTest', 'com.gradle.FooTest', 'org.gradle.BarTest'] | []                     | true
+        'org*'              | ['org.gradle.FooTest', 'org.gradle.BarTest']                       | ['com.gradle.FooTest'] | true
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4IgnoreClassIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4IgnoreClassIntegrationTest.groovy
new file mode 100644
index 0000000..febfde3
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4IgnoreClassIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.IGNORE_ON_CLASS
+
+@TargetCoverage({ IGNORE_ON_CLASS })
+class JUnit4IgnoreClassIntegrationTest extends AbstractJUnit4IgnoreClassIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JUnit3FilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JUnit3FilteringIntegrationTest.groovy
new file mode 100644
index 0000000..6200f8c
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JUnit3FilteringIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnit3FilteringIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.FILTER_JUNIT3_TESTS
+
+@TargetCoverage({ FILTER_JUNIT3_TESTS })
+class JUnit4JUnit3FilteringIntegrationTest extends AbstractJUnit3FilteringIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JUnitIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JUnitIntegrationTest.groovy
new file mode 100644
index 0000000..bbcaccc
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JUnitIntegrationTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import spock.lang.Issue
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4JUnitIntegrationTest extends AbstractJUnit4JUnitIntegrationTest implements JUnit4MultiVersionTest {
+    @Issue("https://issues.gradle.org/browse/GRADLE-2313")
+    def "can clean test after extracting class file with junit"() {
+        when:
+        buildFile << """
+            apply plugin: "java"
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+        and:
+        file("src/test/java/SomeTest.java") << """
+            public class SomeTest extends org.junit.runner.Result {
+                @org.junit.Test
+                public void test() { }
+            }
+        """.stripIndent()
+        then:
+        succeeds "clean", "test"
+
+        and:
+        file("build/tmp/test").exists() // ensure we extracted classes
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JdkNavigationIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JdkNavigationIntegrationTest.groovy
new file mode 100644
index 0000000..6bdccc8
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JdkNavigationIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitJdkNavigationIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4JdkNavigationIntegrationTest extends AbstractJUnitJdkNavigationIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JnaIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JnaIntegrationTest.groovy
new file mode 100644
index 0000000..8282317
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JnaIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitJnaIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4JnaIntegrationTest extends AbstractJUnitJnaIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JunitTestFailureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JunitTestFailureIntegrationTest.groovy
new file mode 100644
index 0000000..29d13c3
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4JunitTestFailureIntegrationTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.hamcrest.Matcher
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+import static org.hamcrest.CoreMatchers.equalTo
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4JunitTestFailureIntegrationTest extends AbstractJUnit4TestFailureIntegrationTest implements JUnit4MultiVersionTest {
+    @Override
+    String getInitializationErrorTestName() {
+        return 'initializationError'
+    }
+
+    @Override
+    String getBeforeClassErrorTestName() {
+        return 'classMethod'
+    }
+
+    @Override
+    String getAfterClassErrorTestName() {
+        return 'classMethod'
+    }
+
+    @Override
+    Matcher<? super String>[] getBrokenBeforeAndAfterMatchers() {
+        return [equalTo(failureAssertionError('before failed')), equalTo(failureAssertionError('after failed'))]
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4LoggingOutputCaptureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4LoggingOutputCaptureIntegrationTest.groovy
new file mode 100644
index 0000000..3cc181e
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4LoggingOutputCaptureIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4LoggingOutputCaptureIntegrationTest extends AbstractJUnit4LoggingOutputCaptureIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4MultiVersionTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4MultiVersionTest.groovy
new file mode 100644
index 0000000..1ad77e4
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4MultiVersionTest.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import org.gradle.testing.fixture.JUnitCoverage
+
+trait JUnit4MultiVersionTest extends JUnit4CommonTestSources {
+    AbstractTestingMultiVersionIntegrationTest.BuildScriptConfiguration getBuildScriptConfiguration() {
+        return new JUnit4BuildScriptConfiguration()
+    }
+
+    AbstractTestingMultiVersionIntegrationTest.TestSourceConfiguration getTestSourceConfiguration() {
+        new JUnit4TestSourceConfiguration(AbstractTestingMultiVersionIntegrationTest.version as String)
+    }
+
+    static class JUnit4BuildScriptConfiguration implements AbstractTestingMultiVersionIntegrationTest.BuildScriptConfiguration {
+        String configureTestFramework = "useJUnit()"
+
+        @Override
+        String getTestFrameworkDependencies(String sourceSet) {
+            if (MultiVersionIntegrationSpec.version.startsWith("3")) {
+                // We support compiling classes with JUnit 3, but we require JUnit 4 at runtime (which is backwards compatible).
+                // The runtime dependency would not be necessary if using test suites, which would automatically inject the runtime
+                // dependency if not specified.
+                return """
+                    ${configurationFor(sourceSet, 'compileOnly')} 'junit:junit:${MultiVersionIntegrationSpec.version}'
+                    ${configurationFor(sourceSet, 'runtimeOnly')} 'junit:junit:${JUnitCoverage.JUNIT_4_LATEST}'
+                """
+            } else {
+                return """
+                    ${configurationFor(sourceSet, 'implementation')} 'junit:junit:${MultiVersionIntegrationSpec.version}'
+                """
+            }
+        }
+
+        @Override
+        String getIncludeCategoryOrTagConfigurationElement() {
+            return "includeCategories"
+        }
+
+        @Override
+        String getExcludeCategoryOrTagConfigurationElement() {
+            return "excludeCategories"
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4RerunPreviousFailedTestIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4RerunPreviousFailedTestIntegrationTest.groovy
new file mode 100644
index 0000000..28b2fe9
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4RerunPreviousFailedTestIntegrationTest.groovy
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.hamcrest.CoreMatchers
+
+class JUnit4RerunPreviousFailedTestIntegrationTest extends AbstractIntegrationSpec {
+    private static final String INDEX_OF_TEST_TO_FAIL = "index.of.test.to.fail"
+    private static final List<Integer> TESTS = [1, 2, 3]
+    private static final List<String> TEST_CLASSES = TESTS.collect { "ConditionalFailingTest_${it}".toString() }
+
+    def setup() {
+        buildFile << """
+            apply plugin: 'java'
+
+            ${mavenCentralRepository()}
+
+
+            dependencies {
+                testImplementation 'junit:junit:4.13'
+            }
+        """
+
+        TESTS.each {
+            file("src/test/java/ConditionalFailingTest_${it}.java") << """
+                import org.junit.Test;
+                public class ConditionalFailingTest_${it} {
+                    @Test
+                    public void failedTest() {
+                        System.out.println("Test index " + ${it});
+                        if("${it}".equals(System.getProperty("${INDEX_OF_TEST_TO_FAIL}"))) {
+                            throw new RuntimeException();
+                        }
+                    }
+                }
+            """.stripIndent()
+        }
+    }
+
+    def letTestFail(def index) {
+        buildFile << """
+        test {
+            systemProperty('${INDEX_OF_TEST_TO_FAIL}', '${index}')
+        }
+        """
+    }
+
+    def 'subsequent execution runs failed test first'() {
+        given:
+        letTestFail(indexOfTestToFail)
+
+        when:
+        fails('test', '--info')
+
+        then:
+        testFailed(indexOfTestToFail)
+
+        when:
+        fails('test', '--info')
+
+        then:
+        failedTestAreRerunFirst(indexOfTestToFail)
+
+        when:
+        letTestFail(0)
+        succeeds('test')
+
+        then:
+        allTestsSucceed()
+
+        where:
+        indexOfTestToFail << TESTS
+    }
+
+    void failedTestAreRerunFirst(int failedTestIndex) {
+        List<String> lines = output.readLines()
+        boolean findIt = false
+        for (String line in lines) {
+            if (line.contains("Test index ${failedTestIndex}")) {
+                findIt = true
+                break
+            } else if (line.contains("Test index")) {
+                assert false
+            }
+        }
+
+        assert findIt
+    }
+
+
+    def 'can delete previous failed test'() {
+        given:
+        letTestFail(indexOfTestToFail)
+
+        when:
+        fails('test')
+
+        then:
+        testFailed(indexOfTestToFail)
+
+        when:
+        file("src/test/java/ConditionalFailingTest_${indexOfTestToFail}.java").delete()
+        succeeds('test')
+
+        then:
+        remainTestsSucceed(indexOfTestToFail)
+
+        where:
+        indexOfTestToFail << TESTS
+    }
+
+    def 'can modify previous failed test'() {
+        given:
+        letTestFail(indexOfTestToFail)
+
+        when:
+        fails('test')
+
+        then:
+        testFailed(indexOfTestToFail)
+
+        when:
+        file("src/test/java/ConditionalFailingTest_${indexOfTestToFail}.java").text = """
+        public class ConditionalFailingTest_${indexOfTestToFail} {
+            public void failedTest() {
+            }
+        }
+        """
+        succeeds('test')
+
+        then:
+        remainTestsSucceed(indexOfTestToFail)
+
+        where:
+        indexOfTestToFail << TESTS
+    }
+
+    void testFailed(def indexOfTestToFail) {
+        new DefaultTestExecutionResult(testDirectory)
+            .testClass("ConditionalFailingTest_${indexOfTestToFail}").assertTestFailed('failedTest', CoreMatchers.anything())
+    }
+
+    void allTestsSucceed() {
+        new DefaultTestExecutionResult(testDirectory).assertTestClassesExecuted(TEST_CLASSES as String[])
+    }
+
+    void remainTestsSucceed(def indexOfTestToFail) {
+        def failedTest = "ConditionalFailingTest_${indexOfTestToFail}".toString()
+        def testClasses = TEST_CLASSES.clone()
+        testClasses.remove(failedTest)
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted(testClasses as String[])
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4SmokeMultiVersionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4SmokeMultiVersionIntegrationTest.groovy
new file mode 100644
index 0000000..4957484
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4SmokeMultiVersionIntegrationTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.testing.junit.AbstractJUnitSmokeMultiVersionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.LARGE_COVERAGE
+
+@TargetCoverage({ LARGE_COVERAGE })
+class JUnit4SmokeMultiVersionIntegrationTest extends AbstractJUnitSmokeMultiVersionIntegrationTest implements JUnit4MultiVersionTest {
+    @Override
+    void assertTestSkippedOrPassed(TestClassExecutionResult testClassResult, String testName) {
+        testClassResult.assertTestPassed(testName)
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4Specs2IntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4Specs2IntegrationTest.groovy
new file mode 100644
index 0000000..75bb3b5
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4Specs2IntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractSpecs2IntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4Specs2IntegrationTest extends AbstractSpecs2IntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4SuitesIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4SuitesIntegrationTest.groovy
new file mode 100644
index 0000000..ef9ad9b
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4SuitesIntegrationTest.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUNIT3_VERSION
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4SuitesIntegrationTest extends AbstractJUnit4SuitesIntegrationTest implements JUnit4MultiVersionTest {
+    @Override
+    boolean supportsSuiteOutput() {
+        return true
+    }
+
+    @Override
+    String getTestFrameworkJUnit3Dependencies() {
+        return """
+            testCompileOnly 'junit:junit:${LATEST_JUNIT3_VERSION}'
+            testRuntimeOnly 'junit:junit:${version}'
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestClassDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..592ca60
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestClassDetectionIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4TestClassDetectionIntegrationTest extends AbstractJUnit4TestClassDetectionIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestEnvironmentIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestEnvironmentIntegrationTest.groovy
new file mode 100644
index 0000000..bf1d687
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestEnvironmentIntegrationTest.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4TestEnvironmentIntegrationTest extends AbstractJUnit4TestEnvironmentIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestExecutionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..38bf2e9
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestExecutionIntegrationTest.groovy
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.integtests.fixtures.TestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitTestExecutionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+import static org.hamcrest.CoreMatchers.containsString
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4TestExecutionIntegrationTest extends AbstractJUnitTestExecutionIntegrationTest implements JUnit4MultiVersionTest {
+    @Override
+    String getJUnitVersionAssertion() {
+        return "assertEquals(\"${version}\", new org.junit.runner.JUnitCore().getVersion());"
+    }
+
+    @Override
+    TestClassExecutionResult assertFailedToExecute(TestExecutionResult testResult, String testClassName) {
+        return testResult.testClass(testClassName)
+            .assertTestFailed("initializationError", containsString('ClassFormatError'))
+    }
+
+    def "test thread name is reset after test execution"() {
+        when:
+        buildFile << """
+            apply plugin: "java"
+            ${mavenCentralRepository()}
+            dependencies {
+                ${testFrameworkDependencies}
+            }
+            test.${configureTestFramework}
+        """.stripIndent()
+
+        and:
+        file("src/test/java/SomeTest.java") << threadNameCheckTest("SomeTest")
+        file("src/test/java/AnotherTest.java") << threadNameCheckTest("AnotherTest")
+
+        then:
+        succeeds "clean", "test"
+
+        and:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass("SomeTest").assertTestPassed("checkThreadName")
+        result.testClass("AnotherTest").assertTestPassed("checkThreadName")
+    }
+
+    private String threadNameCheckTest(String className) {
+        return """
+            ${testFrameworkImports}
+
+            public class ${className} {
+                @Test
+                public void checkThreadName() {
+                    assertEquals("Test worker", Thread.currentThread().getName());
+                    Thread.currentThread().setName(getClass().getSimpleName());
+                }
+            }
+        """.stripIndent()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestFilteringSamplesIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestFilteringSamplesIntegrationTest.groovy
new file mode 100644
index 0000000..df1739b
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestFilteringSamplesIntegrationTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.fixture.JUnitCoverage
+import org.junit.Rule
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE_JUPITER
+
+
+@TargetCoverage({ JUnitCoverage.JUNIT_4_LATEST + JUnitCoverage.JUNIT_VINTAGE_JUPITER })
+class JUnit4TestFilteringSamplesIntegrationTest extends MultiVersionIntegrationSpec {
+
+    @Rule Sample sample = new Sample(temporaryFolder, 'testing/filtering/groovy')
+
+    def setup() {
+        executer.withRepositoryMirrors()
+    }
+
+    def "uses test filter"() {
+        when:
+        inDirectory(sample.dir)
+        run("test")
+
+        then:
+        def result = new DefaultTestExecutionResult(sample.dir)
+        result.assertTestClassesExecuted("SomeIntegTest", "SomeOtherTest")
+        result.testClass("SomeIntegTest").assertTestsExecuted("test1", "test2")
+        result.testClass("SomeOtherTest").assertTestsExecuted("quickUiCheck")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestFrameworkIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestFrameworkIntegrationTest.groovy
new file mode 100644
index 0000000..e9d2e52
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestFrameworkIntegrationTest.groovy
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.testing.AbstractTestFrameworkIntegrationTest
+
+class JUnit4TestFrameworkIntegrationTest extends AbstractTestFrameworkIntegrationTest {
+
+    def setup() {
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies { testImplementation 'junit:junit:4.13' }
+        """
+    }
+
+    @Override
+    void createPassingFailingTest() {
+        file('src/main/java/AppException.java').writelns(
+            "public class AppException extends Exception { }"
+        )
+
+        file('src/test/java/SomeTest.java') << """
+            public class SomeTest {
+                @org.junit.Test
+                public void ${failingTestCaseName}() {
+                    System.err.println("some error output");
+                    org.junit.Assert.fail(\"test failure message\");
+                }
+                @org.junit.Test
+                public void ${passingTestCaseName}() { }
+            }
+        """
+        file('src/test/java/SomeOtherTest.java') << """
+            public class SomeOtherTest {
+                @org.junit.Test
+                public void ${passingTestCaseName}() { }
+            }
+        """
+    }
+
+    @Override
+    void createEmptyProject() {
+        file("src/test/java/NotATest.java") << """
+            public class NotATest {}
+        """
+    }
+
+    @Override
+    void renameTests() {
+        def newTest = file("src/test/java/NewTest.java")
+        file('src/test/java/SomeOtherTest.java').renameTo(newTest)
+        newTest.text = newTest.text.replaceAll("SomeOtherTest", "NewTest")
+    }
+
+    @Override
+    String getTestTaskName() {
+        return "test"
+    }
+
+    @Override
+    String getPassingTestCaseName() {
+        return "pass"
+    }
+
+    @Override
+    String getFailingTestCaseName() {
+        return "fail"
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestListenerBuildOperationAdapterIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestListenerBuildOperationAdapterIntegrationTest.groovy
new file mode 100644
index 0000000..898dc31
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestListenerBuildOperationAdapterIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.getJUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4TestListenerBuildOperationAdapterIntegrationTest extends AbstractJUnit4TestListenerBuildOperationAdapterIntegrationTest implements JUnit4MultiVersionTest {
+    boolean emitsTestClassOperations = false
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestListenerIntegrationTest.groovy
new file mode 100644
index 0000000..3c20aa6
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestListenerIntegrationTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+import static org.gradle.testing.fixture.JUnitCoverage.getLATEST_JUNIT3_VERSION
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4TestListenerIntegrationTest extends AbstractJUnit4TestListenerIntegrationTest implements JUnit4MultiVersionTest {
+    @Override
+    String getTestFrameworkJUnit3Dependencies() {
+        return """
+            testCompileOnly 'junit:junit:${LATEST_JUNIT3_VERSION}'
+            testRuntimeOnly 'junit:junit:${version}'
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestOutputListenerTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestOutputListenerTest.groovy
new file mode 100644
index 0000000..8aa771b
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestOutputListenerTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestOutputListenerIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4TestOutputListenerTest extends AbstractTestOutputListenerIntegrationTest implements JUnit4MultiVersionTest{
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestProgressLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestProgressLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..ce293c8
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestProgressLoggingIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestProgressLoggingIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4TestProgressLoggingIntegrationTest extends AbstractTestProgressLoggingIntegrationTest implements JUnit4MultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestReportIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestReportIntegrationTest.groovy
new file mode 100644
index 0000000..8ff7113
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestReportIntegrationTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_4_LATEST
+
+
+@TargetCoverage({ JUNIT_4_LATEST })
+class JUnit4TestReportIntegrationTest extends AbstractJUnit4TestReportIntegrationTest implements JUnit4MultiVersionTest {
+
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestTaskIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestTaskIntegrationTest.groovy
new file mode 100644
index 0000000..7e3c6ed
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/JUnit4TestTaskIntegrationTest.groovy
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2023 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.testing.junit.junit4
+
+import org.gradle.integtests.fixtures.TargetCoverage
+
+import static org.gradle.testing.fixture.JUnitCoverage.CATEGORIES
+
+@TargetCoverage({ CATEGORIES })
+class JUnit4TestTaskIntegrationTest extends AbstractJUnit4TestTaskIntegrationTest implements JUnit4MultiVersionTest {
+    def "options can be set prior to setting same test framework for the default test task"() {
+        given:
+        file('src/test/java/MyTest.java') << standaloneTestClass
+        file("src/test/java/Slow.java") << """public interface Slow {}"""
+
+        settingsFile << "rootProject.name = 'Sample'"
+        buildFile << """apply plugin: 'java'
+            test {
+                options {
+                    excludeCategories = ["Slow"]
+                }
+                $configureTestFramework
+            }
+        """.stripIndent()
+
+        expect:
+        succeeds("test")
+    }
+
+    def "options can be set prior to setting same test framework for a custom test task"() {
+        given:
+        file('src/customTest/java/MyTest.java') << standaloneTestClass
+
+        settingsFile << "rootProject.name = 'Sample'"
+        buildFile << """
+            sourceSets {
+                customTest
+            }
+
+            dependencies {
+                customTestImplementation 'junit:junit:${version}'
+            }
+
+            tasks.create('customTest', Test) {
+                classpath = sourceSets.customTest.runtimeClasspath
+                testClassesDirs = sourceSets.customTest.output.classesDirs
+                options {
+                    excludeCategories = ["MyTest\\\$Slow"]
+                }
+                $configureTestFramework
+            }
+        """.stripIndent()
+
+        expect:
+        succeeds("customTest")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/package-info.java b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/package-info.java
new file mode 100644
index 0000000..822a314
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/junit4/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * This package contains tests that should be specific to JUnit 4.  Some classes are abstract
+ * and may be implemented by classes in this package as well as in the vintage package.
+ */
+package org.gradle.testing.junit.junit4;
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterAssumptionsIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterAssumptionsIntegrationTest.groovy
new file mode 100644
index 0000000..70f0dad
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterAssumptionsIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitAssumptionsIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterAssumptionsIntegrationTest extends AbstractJUnitAssumptionsIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterCategoriesOrTagsCoverageIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterCategoriesOrTagsCoverageIntegrationTest.groovy
new file mode 100644
index 0000000..5185d4b5
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterCategoriesOrTagsCoverageIntegrationTest.groovy
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.testing.junit.AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterCategoriesOrTagsCoverageIntegrationTest extends AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec implements JUnitJupiterMultiVersionTest {
+    String singularCategoryOrTagName = "tag"
+    String pluralCategoryOrTagName = "tags"
+
+    def "can specify include and exclude tags"() {
+        given:
+        testSources.with {
+            testClass('TagACTests')
+                .withCategoryOrTag('TagA')
+                .withCategoryOrTag('TagC')
+                .with {
+                    testMethod('tagACOk1').shouldPass()
+                    testMethod('tagACOk2').shouldPass()
+                    testMethod('tagACOk3').shouldPass()
+                    testMethod('tagACOk4').shouldPass()
+                }
+            testClass('TagADTests')
+                .withCategoryOrTag('TagA')
+                .with {
+                    testMethod('tagAOk1').shouldPass()
+                    testMethod('tagAOk2').shouldPass()
+                    testMethod('tagCOk3').withCategoryOrTag('TagC').shouldPass()
+                    testMethod('tagDOk4').withCategoryOrTag('TagD').shouldPass()
+                }
+            testClass('TagATests')
+                .withCategoryOrTag('TagA')
+                .with {
+                    testMethod('tagAOk1').shouldPass()
+                    testMethod('tagAOk2').shouldPass()
+                    testMethod('tagAOk3').shouldPass()
+                    testMethod('tagAOk4').shouldPass()
+                }
+            testClass('TagBTests')
+                .withCategoryOrTag('TagB')
+                .with {
+                    testMethod('tagBOk1').shouldPass()
+                    testMethod('tagBOk2').shouldPass()
+                    testMethod('tagBOk3').shouldPass()
+                    testMethod('tagBOk4').shouldPass()
+                }
+            testClass('TagCBTests')
+                .withCategoryOrTag('TagC')
+                .with {
+                    testMethod('tagCOk1').shouldPass()
+                    testMethod('tagCOk2').shouldPass()
+                    testMethod('tagAOk3').shouldPass()
+                    testMethod('tagBOk4').withCategoryOrTag('TagB').shouldPass()
+                }
+            testClass('TagCTests')
+                .withCategoryOrTag('TagC')
+                .with {
+                    testMethod('tagCOk1').shouldPass()
+                    testMethod('tagCOk2').shouldPass()
+                    testMethod('tagCOk3').shouldPass()
+                    testMethod('tagCOk4').shouldPass()
+                }
+            testClass('TagDTests')
+                .withCategoryOrTag('TagD')
+                .with {
+                    testMethod('tagDOk1').shouldPass()
+                    testMethod('tagDOk2').shouldPass()
+                    testMethod('tagDOk3').shouldPass()
+                    testMethod('tagDOk4').shouldPass()
+                }
+            testClass('TagZTests')
+                .withCategoryOrTag('TagZ')
+                .with {
+                    testMethod('tagZOk1').shouldPass()
+                    testMethod('tagZOk2').shouldPass()
+                    testMethod('tagZOk3').shouldPass()
+                    testMethod('tagZOk4').shouldPass()
+                }
+            testClass('MixedTests')
+                .with {
+                    testMethod('tagAOk1').withCategoryOrTag('TagA').shouldPass()
+                    testMethod('tagBOk2').withCategoryOrTag('TagB').shouldPass()
+                    testMethod('ignoredWithTagA').withCategoryOrTag('TagA').ignoreOrDisable()
+                    testMethod('noTagOk4').shouldPass()
+                }
+            testClass('NoTagTests')
+                .with {
+                    testMethod('noTagOk1').shouldPass()
+                    testMethod('noTagOk2').shouldPass()
+                    testMethod('noTagOk3').shouldPass()
+                    testMethod('noTagOk4').shouldPass()
+                }
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                useJUnitPlatform {
+                    includeTags 'TagA'
+                    excludeTags 'TagC'
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('TagATests', 'TagADTests', 'MixedTests')
+        result.testClass("TagATests").assertTestCount(4, 0, 0)
+        result.testClass("TagATests").assertTestsExecuted('tagAOk1', 'tagAOk2', 'tagAOk3', 'tagAOk4')
+        result.testClass("TagADTests").assertTestCount(3, 0, 0)
+        result.testClass("TagADTests").assertTestsExecuted('tagAOk1', 'tagAOk2', 'tagDOk4')
+        result.testClass("MixedTests").assertTestCount(2, 0, 0)
+        result.testClass("MixedTests").assertTestsExecuted('tagAOk1')
+        result.testClass("MixedTests").assertTestsSkipped('ignoredWithTagA')
+    }
+
+    def "can combine tags with custom extension"() {
+        given:
+        testSources.with {
+            sourceFile("Locales.java").withSource("""
+                import org.junit.jupiter.api.extension.Extension;
+                import org.junit.jupiter.api.extension.ExtensionContext;
+                import org.junit.jupiter.api.extension.ParameterContext;
+                import org.junit.jupiter.api.extension.ParameterResolver;
+                import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
+                import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
+
+                import java.util.ArrayList;
+                import java.util.Arrays;
+                import java.util.Collections;
+                import java.util.List;
+                import java.util.Locale;
+                import java.util.stream.Stream;
+
+                public class Locales implements TestTemplateInvocationContextProvider {
+                    @Override
+                    public boolean supportsTestTemplate(ExtensionContext context) {
+                        return true;
+                    }
+
+                    @Override
+                    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
+                        ExtensionContext context) {
+
+                        return Stream.of(invocationContext(Locale.FRENCH), invocationContext(Locale.GERMAN), invocationContext(Locale.ENGLISH));
+                    }
+
+                    private TestTemplateInvocationContext invocationContext(Locale locale) {
+                        return new TestTemplateInvocationContext() {
+                            @Override
+                            public String getDisplayName(int invocationIndex) {
+                                return locale.getDisplayName();
+                            }
+
+                            @Override
+                            public List<Extension> getAdditionalExtensions() {
+                                return Collections.singletonList(new ParameterResolver() {
+                                    @Override
+                                    public boolean supportsParameter(ParameterContext parameterContext,
+                                                                     ExtensionContext extensionContext) {
+                                        return parameterContext.getParameter().getType().equals(Locale.class);
+                                    }
+
+                                    @Override
+                                    public Object resolveParameter(ParameterContext parameterContext,
+                                                                   ExtensionContext extensionContext) {
+                                        return locale;
+                                    }
+                                });
+                            }
+                        };
+                    }
+                }
+            """)
+            sourceFile("SomeLocaleTests.java").withSource("""
+                import org.junit.jupiter.api.Tag;
+                import org.junit.jupiter.api.TestTemplate;
+                import org.junit.jupiter.api.extension.ExtendWith;
+
+                import java.util.Locale;
+
+                @ExtendWith(Locales.class)
+                public class SomeLocaleTests {
+                    @TestTemplate
+                    public void ok1(Locale locale) {
+                        System.out.println("Locale in use: " + locale);
+                    }
+
+                    @TestTemplate
+                    @Tag("TagA")
+                    public void ok2(Locale locale) {
+                        System.out.println("Locale in use: " + locale);
+                    }
+                }
+            """)
+            sourceFile("SomeMoreLocaleTests.java").withSource("""
+                import org.junit.jupiter.api.Tag;
+                import org.junit.jupiter.api.TestTemplate;
+                import org.junit.jupiter.api.extension.ExtendWith;
+
+                import java.util.Locale;
+
+                @ExtendWith(Locales.class)
+                @Tag("TagA")
+                public class SomeMoreLocaleTests {
+                    @TestTemplate
+                    public void someMoreTest1(Locale locale) {
+                        System.out.println("Locale in use: " + locale);
+                    }
+
+                    @TestTemplate
+                    public void someMoreTest2(Locale locale) {
+                        System.out.println("Locale in use: " + locale);
+                    }
+                }
+            """)
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            test {
+                useJUnitPlatform {
+                    excludeTags 'TagA'
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('SomeLocaleTests')
+        result.testClass("SomeLocaleTests").assertTestCount(3, 0, 0)
+        result.testClass("SomeLocaleTests").assertTestsExecuted(
+            result.testCase('ok1(Locale)[1]', 'French'),
+            result.testCase('ok1(Locale)[2]', 'German'),
+            result.testCase('ok1(Locale)[3]', 'English')
+        )
+    }
+
+    def "can run parameterized tests with tags"() {
+        given:
+        testSources.with {
+            sourceFile("NestedTestsWithTags.java").withSource("""
+                import org.junit.jupiter.api.Tag;
+                import org.junit.jupiter.api.Test;
+                import org.junit.jupiter.params.ParameterizedTest;
+                import org.junit.jupiter.params.provider.ValueSource;
+
+                public class NestedTestsWithTags {
+
+                    @Tag("SomeTag")
+                    public static class TagOnClass {
+                        @ParameterizedTest
+                        @ValueSource(strings = {"tag on class"})
+                        public void run(String param) {
+                            System.err.println("executed " + param);
+                        }
+                    }
+
+                    public static class TagOnMethod {
+                        @ParameterizedTest
+                        @ValueSource(strings = {"tag on method"})
+                        @Tag("SomeTag")
+                        public void run(String param) {
+                            System.err.println("executed " + param);
+                        }
+
+                        @Test
+                        public void filteredOut() {
+                            throw new AssertionError("should be filtered out");
+                        }
+                    }
+
+                    public static class TagOnMethodNoParam {
+                        @Test
+                        @Tag("SomeTag")
+                        public void run() {
+                            System.err.println("executed tag on method (no param)");
+                        }
+
+                        @Test
+                        public void filteredOut() {
+                            throw new AssertionError("should be filtered out");
+                        }
+                    }
+
+                    public static class Untagged {
+                        @ParameterizedTest
+                        @ValueSource(strings = {"untagged"})
+                        public void run(String param) {
+                            System.err.println("executed " + param);
+                        }
+                    }
+                }
+            """)
+        }
+        testSourceGenerator.writeAllSources(testSources)
+
+        buildFile << """
+            dependencies {
+                testImplementation("org.junit.jupiter:junit-jupiter-params")
+            }
+            test {
+                useJUnitPlatform {
+                    includeTags 'SomeTag'
+                }
+            }
+        """
+
+        when:
+        run('test')
+
+        then:
+        DefaultTestExecutionResult result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('NestedTestsWithTags$TagOnMethodNoParam', 'NestedTestsWithTags$TagOnMethod', 'NestedTestsWithTags$TagOnClass')
+        result.testClass('NestedTestsWithTags$TagOnMethodNoParam').assertTestCount(1, 0, 0)
+        result.testClass('NestedTestsWithTags$TagOnMethod').assertTestCount(1, 0, 0)
+        result.testClass('NestedTestsWithTags$TagOnClass').assertTestCount(1, 0, 0)
+    }
+
+    @Override
+    TestSourceGenerator getTestSourceGenerator() {
+        return new JUnit5TestSourceGenerator()
+    }
+
+    private class JUnit5TestSourceGenerator implements AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestSourceGenerator {
+        @Override
+        void writeAllSources(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestSourceFixture fixture) {
+            fixture.testClasses.each { testClass ->
+                writeTestClass(testClass)
+            }
+            fixture.sources.each { sourceFile ->
+                writeSourceFile(sourceFile)
+            }
+        }
+
+        private TestFile writeTestClass(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestClass testClass) {
+            String packagePath = testClass.packageName.replace('.', '/')
+            testDirectory.file("src/${testClass.sourceSet}/java/${packagePath}/${testClass.name}.java") << """
+                    ${testClass.packageName}
+
+                    import org.junit.jupiter.api.Test;
+                    import org.junit.jupiter.api.Tag;
+                    import org.junit.jupiter.api.Disabled;
+
+                    ${testClass.categoriesOrTags.collect { "@Tag(\"${it}\")" }.join('\n')}
+                    public class ${testClass.name} {
+                        ${testClass.methods.collect { generateTestMethod(it) }.join('\n')}
+                    }
+                """.stripIndent()
+        }
+
+        private TestFile writeSourceFile(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestSource testSource) {
+            testDirectory.file("src/test/java/${testSource.relativePath}") << testSource.source.stripIndent()
+        }
+
+        private String generateTestMethod(AbstractJUnitCategoriesOrTagsCoverageIntegrationSpec.TestMethod method) {
+            return """
+                @Test
+                ${method.isIgnoredOrDisabled ? "@Disabled" : ""}
+                ${method.categoriesOrTags.collect { "@Tag(\"${it}\")" }.join('\n')}
+                public void ${method.name}() {
+                    ${method.shouldPass ? "assert true;" : "assert false;"}
+                }
+            """
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterClassLevelFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterClassLevelFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..9a43086
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterClassLevelFilteringIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitClassLevelFilteringIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterClassLevelFilteringIntegrationTest extends AbstractJUnitClassLevelFilteringIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterConsoleLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterConsoleLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..1b70c48
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterConsoleLoggingIntegrationTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitConsoleLoggingIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterConsoleLoggingIntegrationTest extends AbstractJUnitConsoleLoggingIntegrationTest implements JUnitJupiterMultiVersionTest {
+    @Override
+    String getMaybePackagePrefix() {
+        return ''
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterFailFastIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterFailFastIntegrationTest.groovy
new file mode 100644
index 0000000..e8d6e05
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterFailFastIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractJvmFailFastIntegrationSpec
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
+
+@TargetCoverage({ [LATEST_JUPITER_VERSION] })
+class JUnitJupiterFailFastIntegrationTest extends AbstractJvmFailFastIntegrationSpec implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..2d93f9b
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterFilteringIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestFilteringIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterFilteringIntegrationTest extends AbstractTestFilteringIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterIgnoreClassIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterIgnoreClassIntegrationTest.groovy
new file mode 100644
index 0000000..8771ff7
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterIgnoreClassIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitIgnoreClassIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterIgnoreClassIntegrationTest extends AbstractJUnitIgnoreClassIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJUnitIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJUnitIntegrationTest.groovy
new file mode 100644
index 0000000..1ec5bd8
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJUnitIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterJUnitIntegrationTest extends AbstractJUnitIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJUnitTestFailureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJUnitTestFailureIntegrationTest.groovy
new file mode 100644
index 0000000..7bebd5b
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJUnitTestFailureIntegrationTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitTestFailureIntegrationTest
+import org.hamcrest.Matcher
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+import static org.hamcrest.CoreMatchers.equalTo
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterJUnitTestFailureIntegrationTest extends AbstractJUnitTestFailureIntegrationTest implements JUnitJupiterMultiVersionTest {
+    @Override
+    void writeBrokenRunnerOrExtension(String className) {
+        file("src/test/java/org/gradle/${className}.java") << """
+            package org.gradle;
+
+            import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
+            import org.junit.jupiter.api.extension.ExtensionContext;
+
+            public class BrokenRunnerOrExtension implements BeforeTestExecutionCallback {
+                @Override
+                public void beforeTestExecution(ExtensionContext context) throws Exception {
+                    throw new UnsupportedOperationException("broken");
+                }
+            }
+        """.stripIndent()
+    }
+
+    @Override
+    void writeClassUsingBrokenRunnerOrExtension(String className, String runnerOrExtensionName) {
+        file("src/test/java/org/gradle/${className}.java") << """
+            package org.gradle;
+
+            ${testFrameworkImports}
+
+            @ExtendWith(${runnerOrExtensionName}.class)
+            public class ${className} {
+                @Test
+                public void ok() {
+                }
+            }
+        """.stripIndent()
+    }
+
+    @Override
+    String getInitializationErrorTestName() {
+        return 'ok()'
+    }
+
+    @Override
+    String getAssertionFailureClassName() {
+        return 'org.opentest4j.AssertionFailedError'
+    }
+
+    @Override
+    String getBeforeClassErrorTestName() {
+        return 'initializationError'
+    }
+
+    @Override
+    String getAfterClassErrorTestName() {
+        return 'executionError'
+    }
+
+    @Override
+    Matcher<? super String>[] getBrokenBeforeAndAfterMatchers() {
+        return [equalTo(failureAssertionError('before failed'))]
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJdkNavigationIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJdkNavigationIntegrationTest.groovy
new file mode 100644
index 0000000..0f06e21
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJdkNavigationIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitJdkNavigationIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterJdkNavigationIntegrationTest extends AbstractJUnitJdkNavigationIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJnaIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJnaIntegrationTest.groovy
new file mode 100644
index 0000000..38d8db9
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterJnaIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitJnaIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterJnaIntegrationTest extends AbstractJUnitJnaIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterLoggingOutputCaptureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterLoggingOutputCaptureIntegrationTest.groovy
new file mode 100644
index 0000000..d0feaf1
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterLoggingOutputCaptureIntegrationTest.groovy
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.HtmlTestExecutionResult
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitLoggingOutputCaptureIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+import static org.hamcrest.CoreMatchers.containsString
+
+// https://github.com/junit-team/junit5/issues/1285
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterLoggingOutputCaptureIntegrationTest extends AbstractJUnitLoggingOutputCaptureIntegrationTest implements JUnitJupiterMultiVersionTest {
+    def "captures logging output events"() {
+        file("src/test/java/OkTest.java") << """
+            ${testFrameworkImports}
+
+            public class OkTest {
+                static {
+                    System.out.println("class loaded");
+                }
+
+                public OkTest() {
+                    System.out.println("test constructed");
+                }
+
+                ${beforeClassAnnotation} public static void init() {
+                    System.out.println("before class out");
+                    System.err.println("before class err");
+                }
+
+                ${afterClassAnnotation} public static void end() {
+                    System.out.println("after class out");
+                    System.err.println("after class err");
+                }
+
+                ${beforeTestAnnotation}
+                public void before() {
+                    System.out.println("before out");
+                    System.err.println("before err");
+                }
+
+                ${afterTestAnnotation}
+                public void after() {
+                    System.out.println("after out");
+                    System.err.println("after err");
+                }
+
+                @Test
+                public void ok() {
+                    System.out.print("test out: \u03b1</html>");
+                    System.out.println();
+                    System.err.println("test err");
+                }
+
+                @Test
+                public void anotherOk() {
+                    System.out.println("ok out");
+                    System.err.println("ok err");
+                }
+            }
+        """.stripIndent()
+
+        when:
+        succeeds "test"
+
+        then:
+
+        outputContains(
+            "Test class OkTest -> class loaded\n" +
+            "Test class OkTest -> before class out\n" +
+            "Test class OkTest -> before class err\n" +
+            "Test class OkTest -> test constructed\n" +
+            "Test anotherOk(OkTest) -> before out\n" +
+            "Test anotherOk(OkTest) -> before err\n" +
+            "Test anotherOk(OkTest) -> ok out\n" +
+            "Test anotherOk(OkTest) -> ok err\n" +
+            "Test anotherOk(OkTest) -> after out\n" +
+            "Test anotherOk(OkTest) -> after err\n" +
+            "Test class OkTest -> test constructed\n" +
+            "Test ok(OkTest) -> before out\n" +
+            "Test ok(OkTest) -> before err\n" +
+            "Test ok(OkTest) -> test out: \u03b1</html>\n" +
+            "Test ok(OkTest) -> test err\n" +
+            "Test ok(OkTest) -> after out\n" +
+            "Test ok(OkTest) -> after err\n" +
+            "Test class OkTest -> after class out\n" +
+            "Test class OkTest -> after class err\n"
+        )
+
+        // This test covers current behaviour, not necessarily desired behaviour
+
+        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+        def classResult = xmlReport.testClass("OkTest")
+        classResult.assertTestCaseStdout("ok", containsString(
+            "before out\n" +
+            "test out: \u03b1</html>\n" +
+            "after out\n"
+        ))
+        classResult.assertTestCaseStderr("ok", containsString(
+            "before err\n" +
+            "test err\n" +
+            "after err\n"
+        ))
+        classResult.assertStdout(containsString(
+            "class loaded\n" +
+            "before class out\n" +
+            "test constructed\n" +
+            "test constructed\n" +
+            "after class out\n"
+        ))
+        classResult.assertStderr(containsString(
+            "before class err\n" +
+            "after class err\n"
+        ))
+
+
+        def htmlReport = new HtmlTestExecutionResult(testDirectory)
+        def classReport = htmlReport.testClass("OkTest")
+        classReport.assertStdout(containsString(
+            "class loaded\n" +
+            "before class out\n" +
+            "test constructed\n" +
+            "before out\n" +
+            "ok out\n" +
+            "after out\n" +
+            "test constructed\n" +
+            "before out\n" +
+            "test out: \u03b1</html>\n" +
+            "after out\n" +
+            "after class out\n"
+        ))
+        classReport.assertStderr(containsString(
+            "before class err\n" +
+            "before err\n" +
+            "ok err\n" +
+            "after err\n" +
+            "before err\n" +
+            "test err\n" +
+            "after err\n" +
+            "after class err\n"
+        ))
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterMultiVersionTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterMultiVersionTest.groovy
new file mode 100644
index 0000000..2324285
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterMultiVersionTest.groovy
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+trait JUnitJupiterMultiVersionTest {
+    AbstractTestingMultiVersionIntegrationTest.BuildScriptConfiguration getBuildScriptConfiguration() {
+        return new JUnitJupiterBuildScriptConfiguration()
+    }
+
+    AbstractTestingMultiVersionIntegrationTest.TestSourceConfiguration getTestSourceConfiguration() {
+        return new JUnitJupiterTestSourceConfiguration()
+    }
+
+    static class JUnitJupiterBuildScriptConfiguration implements AbstractTestingMultiVersionIntegrationTest.BuildScriptConfiguration {
+        String configureTestFramework = "useJUnitPlatform()"
+
+        @Override
+        String getTestFrameworkDependencies(String sourceSet) {
+            return """
+                ${configurationFor(sourceSet, 'implementation')} 'org.junit.jupiter:junit-jupiter-api:${MultiVersionIntegrationSpec.version}'
+                ${configurationFor(sourceSet, 'runtimeOnly')} 'org.junit.jupiter:junit-jupiter-engine:${MultiVersionIntegrationSpec.version}'
+                ${configurationFor(sourceSet, 'runtimeOnly')} 'org.junit.platform:junit-platform-launcher'
+            """
+        }
+
+        @Override
+        String getIncludeCategoryOrTagConfigurationElement() {
+            return "includeTags"
+        }
+
+        @Override
+        String getExcludeCategoryOrTagConfigurationElement() {
+            return "excludeTags"
+        }
+    }
+
+    static class JUnitJupiterTestSourceConfiguration implements AbstractTestingMultiVersionIntegrationTest.TestSourceConfiguration {
+        @Override
+        String getTestFrameworkImports() {
+            return """
+                import org.junit.jupiter.api.*;
+                import org.junit.jupiter.api.extension.*;
+                import static org.junit.jupiter.api.Assertions.*;
+                import static org.junit.jupiter.api.Assumptions.*;
+            """.stripIndent()
+        }
+
+        @Override
+        String getBeforeClassAnnotation() {
+            return "@BeforeAll"
+        }
+
+        @Override
+        String getAfterClassAnnotation() {
+            return "@AfterAll"
+        }
+
+        @Override
+        String getBeforeTestAnnotation() {
+            return "@BeforeEach"
+        }
+
+        @Override
+        String getAfterTestAnnotation() {
+            return "@AfterEach"
+        }
+
+        @Override
+        String getRunOrExtendWithAnnotation(String runOrExtendWithClasses) {
+            return "@ExtendWith({ ${runOrExtendWithClasses} })"
+        }
+
+        @Override
+        String maybeParentheses(String methodName) {
+            return "${methodName}()"
+        }
+
+        @Override
+        String getIgnoreOrDisabledAnnotation() {
+            return "@Disabled"
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterProgressLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterProgressLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..fbf25ad
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterProgressLoggingIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestProgressLoggingIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterProgressLoggingIntegrationTest extends AbstractTestProgressLoggingIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterSpockIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterSpockIntegrationTest.groovy
new file mode 100644
index 0000000..ac2fa5f
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterSpockIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitSpockIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterSpockIntegrationTest extends AbstractJUnitSpockIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterSuitesIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterSuitesIntegrationTest.groovy
new file mode 100644
index 0000000..45a6835
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterSuitesIntegrationTest.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.fixture.JUnitCoverage
+import org.gradle.testing.junit.AbstractJUnitSuitesIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterSuitesIntegrationTest extends AbstractJUnitSuitesIntegrationTest implements JUnitJupiterMultiVersionTest {
+    @Override
+    String getTestFrameworkSuiteImports() {
+        return """
+            import org.junit.platform.suite.api.SelectClasses;
+            import org.junit.platform.suite.api.Suite;
+        """.stripIndent()
+    }
+
+    @Override
+    String getTestFrameworkSuiteAnnotations(String classes) {
+        return """
+            @SelectClasses({ ${classes} })
+            @Suite
+        """.stripIndent()
+    }
+
+    @Override
+    String getTestFrameworkSuiteDependencies() {
+        return """
+            testCompileOnly 'org.junit.platform:junit-platform-suite-api:${JUnitCoverage.LATEST_PLATFORM_VERSION}'
+            testRuntimeOnly 'org.junit.platform:junit-platform-suite-engine:${JUnitCoverage.LATEST_PLATFORM_VERSION}'
+        """.stripIndent()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestClassDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..0a45f87
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestClassDetectionIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitTestClassDetectionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterTestClassDetectionIntegrationTest extends AbstractJUnitTestClassDetectionIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestEnvironmentIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestEnvironmentIntegrationTest.groovy
new file mode 100644
index 0000000..54e0912
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestEnvironmentIntegrationTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestEnvironmentIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterTestEnvironmentIntegrationTest extends AbstractTestEnvironmentIntegrationTest implements JUnitJupiterMultiVersionTest {
+    @Override
+    String getModuleName() {
+        return "org.junit.jupiter.api"
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestExecutionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..ea0367c
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestExecutionIntegrationTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.integtests.fixtures.TestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitTestExecutionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+import static org.hamcrest.CoreMatchers.containsString
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterTestExecutionIntegrationTest extends AbstractJUnitTestExecutionIntegrationTest implements JUnitJupiterMultiVersionTest {
+    @Override
+    String getJUnitVersionAssertion() {
+        return "assertEquals(\"${version}\", org.junit.jupiter.api.Test.class.getPackage().getImplementationVersion());"
+    }
+
+    @Override
+    TestClassExecutionResult assertFailedToExecute(TestExecutionResult testResult, String testClassName) {
+        return testResult.testClassStartsWith('Gradle Test Executor')
+            .assertTestFailed("failed to execute tests", containsString("Could not execute test class '${testClassName}'"))
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestFrameworkIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestFrameworkIntegrationTest.groovy
new file mode 100644
index 0000000..702fd16
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestFrameworkIntegrationTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.testing.AbstractTestFrameworkIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
+
+class JUnitJupiterTestFrameworkIntegrationTest extends AbstractTestFrameworkIntegrationTest {
+
+    def setup() {
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'org.junit.jupiter:junit-jupiter:${LATEST_JUPITER_VERSION}'
+                testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+            }
+            test {
+                useJUnitPlatform()
+            }
+        """
+    }
+
+    @Override
+    void createPassingFailingTest() {
+        file('src/main/java/AppException.java').writelns(
+            "public class AppException extends Exception { }"
+        )
+
+        file('src/test/java/SomeTest.java') << """
+            public class SomeTest {
+                @org.junit.jupiter.api.Test
+                public void ${failingTestCaseName} {
+                    System.err.println("some error output");
+                    org.junit.jupiter.api.Assertions.fail(\"test failure message\");
+                }
+                @org.junit.jupiter.api.Test
+                public void ${passingTestCaseName} { }
+            }
+        """
+        file('src/test/java/SomeOtherTest.java') << """
+            public class SomeOtherTest {
+                @org.junit.jupiter.api.Test
+                public void ${passingTestCaseName} { }
+            }
+        """
+    }
+
+    @Override
+    void createEmptyProject() {
+        file("src/test/java/NotATest.java") << """
+            public class NotATest {}
+        """
+    }
+
+    @Override
+    void renameTests() {
+        def newTest = file("src/test/java/NewTest.java")
+        file('src/test/java/SomeOtherTest.java').renameTo(newTest)
+        newTest.text = newTest.text.replaceAll("SomeOtherTest", "NewTest")
+    }
+
+    @Override
+    String getTestTaskName() {
+        return "test"
+    }
+
+    @Override
+    String getPassingTestCaseName() {
+        return "pass()"
+    }
+
+    @Override
+    String getFailingTestCaseName() {
+        return "fail()"
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestListenerBuildOperationAdapterIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestListenerBuildOperationAdapterIntegrationTest.groovy
new file mode 100644
index 0000000..89ae2a4
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestListenerBuildOperationAdapterIntegrationTest.groovy
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.internal.operations.trace.BuildOperationRecord
+import org.gradle.testing.AbstractTestListenerBuildOperationAdapterIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_PLATFORM_VERSION
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterTestListenerBuildOperationAdapterIntegrationTest extends AbstractTestListenerBuildOperationAdapterIntegrationTest implements JUnitJupiterMultiVersionTest {
+    boolean emitsTestClassOperations = true
+
+    @Override
+    void checkForSuiteOperations(Iterator<BuildOperationRecord> iterator, String suiteName) {
+        super.checkForSuiteOperations(iterator, suiteName)
+        with(iterator.next()) {
+            details.testDescriptor.name == "JUnit Jupiter"
+            details.testDescriptor.composite == true
+        }
+    }
+
+    @Override
+    void writeTestSources() {
+        buildFile << """
+            dependencies {
+                testImplementation 'org.junit.platform:junit-platform-suite-api:${LATEST_PLATFORM_VERSION}'
+                testRuntimeOnly 'org.junit.platform:junit-platform-suite-engine:${LATEST_PLATFORM_VERSION}'
+            }
+        """
+        file('src/test/java/org/gradle/ASuite.java') << """
+            package org.gradle;
+            import org.junit.platform.suite.api.Suite;
+            import org.junit.platform.suite.api.SelectClasses;
+            @Suite
+            @SelectClasses({ org.gradle.OkTest.class, org.gradle.OtherTest.class })
+            public class ASuite {
+                static {
+                    System.out.println("suite class loaded");
+                }
+            }
+        """
+        file('src/test/java/org/gradle/OkTest.java') << """
+            package org.gradle;
+            import org.junit.jupiter.api.Test;
+            public class OkTest {
+                @Test
+                public void ok() throws Exception {
+                    System.err.println("This is test stderr");
+                }
+
+                @Test
+                public void anotherOk() {
+                    System.out.println("sys out from another test method");
+                    System.err.println("sys err from another test method");
+                }
+            }
+        """
+        file('src/test/java/org/gradle/OtherTest.java') << """
+            package org.gradle;
+            import org.junit.jupiter.api.Test;
+            public class OtherTest {
+                @Test
+                public void ok() throws Exception {
+                    System.out.println("This is other stdout");
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestListenerIntegrationTest.groovy
new file mode 100644
index 0000000..a1415c7
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestListenerIntegrationTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitTestListenerIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterTestListenerIntegrationTest extends AbstractJUnitTestListenerIntegrationTest implements JUnitJupiterMultiVersionTest {
+    @Override
+    String getAssertionError() {
+        return "org.opentest4j.AssertionFailedError"
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestOutputLIstenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestOutputLIstenerIntegrationTest.groovy
new file mode 100644
index 0000000..292bca2
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestOutputLIstenerIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestOutputListenerIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterTestOutputLIstenerIntegrationTest extends AbstractTestOutputListenerIntegrationTest implements JUnitJupiterMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestReportIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestReportIntegrationTest.groovy
new file mode 100644
index 0000000..0872194
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestReportIntegrationTest.groovy
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.HtmlTestExecutionResult
+import org.gradle.integtests.fixtures.JUnitXmlTestExecutionResult
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestReportIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+import static org.hamcrest.CoreMatchers.containsString
+import static org.hamcrest.CoreMatchers.is
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterTestReportIntegrationTest extends AbstractTestReportIntegrationTest implements JUnitJupiterMultiVersionTest {
+    def "outputs over lifecycle"() {
+        when:
+        buildScript """
+            $junitSetup
+            test.reports.junitXml.outputPerTestCase = true
+        """
+
+        file("src/test/java/OutputLifecycleTest.java") << """
+            ${testFrameworkImports}
+
+            public class OutputLifecycleTest {
+
+                public OutputLifecycleTest() {
+                    System.out.println("constructor out");
+                    System.err.println("constructor err");
+                }
+
+                ${beforeClassAnnotation}
+                public static void beforeClass() {
+                    System.out.println("beforeClass out");
+                    System.err.println("beforeClass err");
+                }
+
+                ${beforeTestAnnotation}
+                public void beforeTest() {
+                    System.out.println("beforeTest out");
+                    System.err.println("beforeTest err");
+                }
+
+                @Test public void m1() {
+                    System.out.println("m1 out");
+                    System.err.println("m1 err");
+                }
+
+                @Test public void m2() {
+                    System.out.println("m2 out");
+                    System.err.println("m2 err");
+                }
+
+                ${afterTestAnnotation}
+                public void afterTest() {
+                    System.out.println("afterTest out");
+                    System.err.println("afterTest err");
+                }
+
+                ${afterClassAnnotation}
+                public static void afterClass() {
+                    System.out.println("afterClass out");
+                    System.err.println("afterClass err");
+                }
+            }
+        """
+
+        succeeds "test"
+
+        then:
+        def xmlReport = new JUnitXmlTestExecutionResult(testDirectory)
+        def clazz = xmlReport.testClass("OutputLifecycleTest")
+        clazz.assertTestCaseStderr("m1", is("beforeTest err\nm1 err\nafterTest err\n"))
+        clazz.assertTestCaseStderr("m2", is("beforeTest err\nm2 err\nafterTest err\n"))
+        clazz.assertTestCaseStdout("m1", is("beforeTest out\nm1 out\nafterTest out\n"))
+        clazz.assertTestCaseStdout("m2", is("beforeTest out\nm2 out\nafterTest out\n"))
+        clazz.assertStderr(is("beforeClass err\nconstructor err\nconstructor err\nafterClass err\n"))
+        clazz.assertStdout(is("beforeClass out\nconstructor out\nconstructor out\nafterClass out\n"))
+    }
+
+    def "collects output for failing non-root suite descriptors"() {
+        given:
+        buildScript """
+            $junitSetup
+            dependencies {
+                testImplementation(platform('org.junit:junit-bom:$version'))
+                testImplementation('org.junit.platform:junit-platform-launcher')
+            }
+        """
+
+        and:
+        testClass "SomeTest"
+        file("src/test/java/ThrowingListener.java") << """
+            import org.junit.platform.launcher.*;
+            public class ThrowingListener implements TestExecutionListener {
+                @Override
+                public void testPlanExecutionStarted(TestPlan testPlan) {
+                    System.out.println("System.out from ThrowingListener");
+                    System.err.println("System.err from ThrowingListener");
+                    throw new OutOfMemoryError("not caught by JUnit Platform");
+                }
+            }
+        """
+        file("src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener") << "ThrowingListener"
+
+        when:
+        fails "test"
+
+        then:
+        new HtmlTestExecutionResult(testDirectory)
+            .testClassStartsWith("Gradle Test Executor")
+            .assertTestFailed("failed to execute tests", containsString("Could not complete execution"))
+            .assertStdout(containsString("System.out from ThrowingListener"))
+            .assertStderr(containsString("System.err from ThrowingListener"))
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestTaskIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestTaskIntegrationTest.groovy
new file mode 100644
index 0000000..a57f464
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/JUnitJupiterTestTaskIntegrationTest.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 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.testing.junit.jupiter
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestTaskIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_JUPITER
+
+@TargetCoverage({ JUNIT_JUPITER })
+class JUnitJupiterTestTaskIntegrationTest extends AbstractTestTaskIntegrationTest implements JUnitJupiterMultiVersionTest {
+    @Override
+    String getStandaloneTestClass() {
+        return testClass('MyTest')
+    }
+
+    @Override
+    String testClass(String className) {
+        return """
+            import org.junit.jupiter.api.*;
+
+            public class $className {
+               @Test
+               @Tag("MyTest\$Fast")
+               public void fastTest() {
+                  System.out.println(System.getProperty("java.version"));
+                  Assertions.assertEquals(1,1);
+               }
+
+               @Test
+               @Tag("MyTest\$Slow")
+               public void slowTest() {
+                  System.out.println(System.getProperty("java.version"));
+                  Assertions.assertEquals(1,1);
+               }
+            }
+        """.stripIndent()
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/package-info.java b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/package-info.java
new file mode 100644
index 0000000..0700806
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/jupiter/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * This package contains tests that should be specific to the JUnit Jupiter engine.
+ */
+package org.gradle.testing.junit.jupiter;
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/package-info.java b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/package-info.java
new file mode 100644
index 0000000..2bbeae3
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * This package contains tests that apply broadly across all JUnit platforms.
+ * Most test classes are abstract and are implemented by framework/engine specific classes
+ * in sub-packages.
+ */
+package org.gradle.testing.junit;
+
+
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformEnvironmentIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformEnvironmentIntegrationTest.groovy
new file mode 100644
index 0000000..dd7f00d
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformEnvironmentIntegrationTest.groovy
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_PLATFORM_VERSION
+import static org.hamcrest.CoreMatchers.containsString
+
+/**
+ * Tests the state of the application classpath in the forked test process to ensure the correct
+ * test framework dependencies are exposed to the user's test code. Additionally tests environmental
+ * state like system properties, and environment variables.
+ *
+ * <p>This test intentionally does not extend {@link JUnitPlatformIntegrationSpec} in order to have
+ * complete control over the configuration of the test setup</p>
+ */
+class JUnitPlatformEnvironmentIntegrationTest extends AbstractIntegrationSpec {
+
+    // The versions tested against here are intentionally different than the version of junit-platform-launcher
+    // that Gradle will load from the distribution. This way, we can use the version on the application classpath
+    // to determine whether the launcher was loaded from the distribution or from the test runtime classpath.
+    private static final JUNIT_JUPITER_VERSION = '5.6.3'
+    private static final JUNIT_PLATFORM_VERSION = '1.6.3'
+    private static final OPENTEST4J_VERSION = '1.2.0'
+    private static final API_GUARDIAN_VERSION = '1.1.0'
+
+    def setup() {
+        buildFile << """
+            plugins {
+                id 'java-library'
+            }
+
+            ${mavenCentralRepository()}
+
+            test {
+                useJUnitPlatform()
+
+                systemProperties.isJava9 = "\${JavaVersion.current().isJava9Compatible()}"
+                systemProperties.testSysProperty = 'value'
+                systemProperties.projectDir = projectDir
+                environment.TEST_ENV_VAR = 'value'
+                testLogging.showStandardStreams = true
+            }
+        """
+    }
+
+    def "should prompt user to add dependencies when they are not in test runtime classpath"() {
+        given:
+        buildFile << """
+            testing.suites.test.dependencies {
+                compileOnly 'org.junit.jupiter:junit-jupiter:${JUNIT_JUPITER_VERSION}'
+                runtimeOnly 'org.junit.platform:junit-platform-launcher:${LATEST_PLATFORM_VERSION}'
+            }
+        """
+        file('src/test/java/org/example/ExampleTest.java') << """
+            package org.example;
+            public class ExampleTest {
+                @org.junit.jupiter.api.Test
+                public void ok() { }
+            }
+        """
+
+        when:
+        fails('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClassStartsWith('Gradle Test Executor')
+            .assertExecutionFailedWithCause(containsString('consider adding an engine implementation JAR to the classpath'))
+    }
+
+    // When running embedded with test distribution, the remote distribution has a newer version of
+    // junit-platform-launcher which is not compatible with the junit jupiter jars we test against.
+    @IgnoreIf({ GradleContextualExecuter.embedded })
+    def "automatically loads framework dependencies from distribution"() {
+        given:
+        buildFile << """
+            testing.suites.test.dependencies {
+                implementation 'org.junit.jupiter:junit-jupiter:${JUNIT_JUPITER_VERSION}'
+            }
+        """
+
+        addClasspathTest("""
+            Set<String> jarSet = new HashSet<>(Arrays.asList(
+                "gradle-worker.jar",
+                "test",
+                "junit-jupiter-params-${JUNIT_JUPITER_VERSION}.jar",
+                "junit-jupiter-engine-${JUNIT_JUPITER_VERSION}.jar",
+                "junit-jupiter-api-${JUNIT_JUPITER_VERSION}.jar",
+                "junit-platform-engine-${JUNIT_PLATFORM_VERSION}.jar",
+                "junit-platform-commons-${JUNIT_PLATFORM_VERSION}.jar",
+                "junit-jupiter-${JUNIT_JUPITER_VERSION}.jar",
+                "opentest4j-${OPENTEST4J_VERSION}.jar",
+                "apiguardian-api-${API_GUARDIAN_VERSION}.jar"
+            ));
+            assertTrue(jars.containsAll(jarSet));
+            jars.removeAll(jarSet);
+
+            // The distribtuion-loaded jars can vary in version, since the GE test acceleration
+            // plugins inject their own versions of these libraries during integration tests.
+            // See: https://github.com/gradle/gradle/pull/21494
+            assertEquals(jars.size(), 3);
+            assertTrue(jars.removeIf(it -> it.startsWith("junit-platform-launcher-1.")));
+            assertTrue(jars.removeIf(it -> it.startsWith("junit-platform-commons-1.")));
+            assertTrue(jars.removeIf(it -> it.startsWith("junit-platform-engine-1.")));
+            assertEquals(jars.size(), 0);
+        """)
+
+        expect:
+        executer.expectDocumentedDeprecationWarning("The automatic loading of test framework implementation dependencies has been deprecated. This is scheduled to be removed in Gradle 9.0. Declare the desired test framework directly on the test suite or explicitly declare the test framework implementation dependencies on the test's runtime classpath. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_framework_implementation_dependencies")
+        succeeds "test"
+    }
+
+    def "does not load framework dependencies from distribution when they are on the classpath already"() {
+        given:
+        buildFile << """
+            testing.suites.test.dependencies {
+                implementation 'org.junit.jupiter:junit-jupiter:${JUNIT_JUPITER_VERSION}'
+
+                runtimeOnly 'org.junit.platform:junit-platform-launcher'
+            }
+        """
+
+        addClasspathTest("""
+            assertTrue(new HashSet<>(jars).equals(new HashSet<>(Arrays.asList(
+                "gradle-worker.jar",
+                "test",
+                "junit-jupiter-params-${JUNIT_JUPITER_VERSION}.jar",
+                "junit-jupiter-engine-${JUNIT_JUPITER_VERSION}.jar",
+                "junit-jupiter-api-${JUNIT_JUPITER_VERSION}.jar",
+                "junit-platform-launcher-${JUNIT_PLATFORM_VERSION}.jar",
+                "junit-platform-engine-${JUNIT_PLATFORM_VERSION}.jar",
+                "junit-platform-commons-${JUNIT_PLATFORM_VERSION}.jar",
+                "junit-jupiter-${JUNIT_JUPITER_VERSION}.jar",
+                "opentest4j-${OPENTEST4J_VERSION}.jar",
+                "apiguardian-api-${API_GUARDIAN_VERSION}.jar"
+            ))));
+        """)
+
+        expect:
+        succeeds "test"
+
+    }
+
+    def "does not load framework dependencies even if they are on the classpath but have nonstandard-named jars"() {
+        given:
+        buildFile << """
+            testing.suites.test.dependencies {
+                implementation 'org.junit.jupiter:junit-jupiter:${JUNIT_JUPITER_VERSION}'
+
+                runtimeOnly 'org.junit.platform:junit-platform-launcher'
+            }
+
+            task renameJUnitJars(type: Copy) {
+                from configurations.testRuntimeClasspath
+                into file('build/renamed-classpath')
+                rename { String fileName ->
+                    if (fileName.startsWith('junit')) {
+                        return fileName.replace('junit', 'renamed-junit')
+                    }
+                }
+            }
+
+            testing.suites.test.sources.runtimeClasspath =
+                testing.suites.test.sources.output.plus(
+                    renameJUnitJars.outputs.files.asFileTree.matching {
+                        include '**/*.jar'
+                    }
+                )
+        """
+
+        addClasspathTest("""
+            assertTrue(new HashSet<>(jars).equals(new HashSet<>(Arrays.asList(
+                "gradle-worker.jar",
+                "test",
+                "renamed-junit-jupiter-api-${JUNIT_JUPITER_VERSION}.jar",
+                "renamed-junit-platform-launcher-${JUNIT_PLATFORM_VERSION}.jar",
+                "renamed-junit-jupiter-engine-${JUNIT_JUPITER_VERSION}.jar",
+                "renamed-junit-platform-commons-${JUNIT_PLATFORM_VERSION}.jar",
+                "renamed-junit-jupiter-${JUNIT_JUPITER_VERSION}.jar",
+                "renamed-junit-platform-engine-${JUNIT_PLATFORM_VERSION}.jar",
+                "renamed-junit-jupiter-params-${JUNIT_JUPITER_VERSION}.jar",
+                "opentest4j-${OPENTEST4J_VERSION}.jar",
+                "apiguardian-api-${API_GUARDIAN_VERSION}.jar"
+            ))));
+        """)
+
+        expect:
+        succeeds "test"
+    }
+
+    def addClasspathTest(String testCode) {
+        file("src/test/java/org/example/ClasspathCheckingTest.java") << """
+            package org.example;
+
+            import java.io.File;
+            import java.net.URL;
+            import java.net.URLClassLoader;
+            import java.util.ArrayList;
+            import java.util.Arrays;
+            import java.util.HashSet;
+            import java.util.List;
+            import java.util.Set;
+            import java.util.regex.Pattern;
+            import java.util.stream.Collectors;
+
+            import static org.junit.jupiter.api.Assertions.assertEquals;
+            import static org.junit.jupiter.api.Assertions.assertTrue;
+
+            public class ClasspathCheckingTest {
+                @org.junit.jupiter.api.Test
+                public void checkEnvironment() {
+                    assert System.getProperty("projectDir").equals(System.getProperty("user.dir"));
+                    assert "value".equals(System.getProperty("testSysProperty"));
+                    assert "value".equals(System.getenv("TEST_ENV_VAR"));
+
+                    assert ClassLoader.getSystemClassLoader() == getClass().getClassLoader();
+                    assert getClass().getClassLoader() == Thread.currentThread().getContextClassLoader();
+
+                    boolean isJava9 = Boolean.parseBoolean(System.getProperty("isJava9"));
+
+                    List<String> classpath;
+                    if (isJava9) {
+                        classpath = Arrays.stream(System.getProperty("java.class.path").split(Pattern.quote(File.pathSeparator)))
+                            .map(path -> new File(path).getName())
+                            .collect(Collectors.toList());
+                    } else {
+                        classpath = Arrays.stream(((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs())
+                            .map(url -> new File(url.getPath()).getName())
+                            .collect(Collectors.toList());
+                    }
+
+                    try {
+                        // Any remaining jars should be verified by the individual test.
+                        List<String> jars = new ArrayList<>(classpath);
+                        ${testCode}
+                    } catch (AssertionError e) {
+                        System.err.println(e.getMessage() + "\\nActual Jars:\\n- " + String.join("\\n- ", classpath));
+                        throw e;
+                    }
+                }
+            }
+        """
+    }
+
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..7d49292
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformFilteringIntegrationTest.groovy
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import spock.lang.Issue
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_ARCHUNIT_VERSION
+
+class JUnitPlatformFilteringIntegrationTest extends JUnitPlatformIntegrationSpec {
+
+    def 'can filter nested tests'() {
+        given:
+        file('src/test/java/org/gradle/NestedTest.java') << '''
+            package org.gradle;
+            import static org.junit.jupiter.api.Assertions.*;
+
+            import java.util.EmptyStackException;
+            import java.util.Stack;
+
+            import org.junit.jupiter.api.*;
+
+            class NestedTest {
+                @Test
+                void outerTest() {
+                }
+
+                @Nested
+                class Inner {
+                    @Test
+                    void innerTest() {
+                    }
+                }
+            }
+        '''
+        buildFile << '''
+            test {
+                filter {
+                    includeTestsMatching "*innerTest*"
+                }
+            }
+        '''
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted('org.gradle.NestedTest$Inner')
+            .testClass('org.gradle.NestedTest$Inner').assertTestCount(1, 0, 0)
+            .assertTestPassed('innerTest()')
+    }
+
+    def 'can use nested class as test pattern'() {
+        given:
+        file('src/test/java/EnclosingClass.java') << '''
+            import org.junit.jupiter.api.Test;
+            import org.junit.jupiter.api.Nested;
+            import static org.junit.jupiter.api.Assertions.assertEquals;
+
+            class EnclosingClass {
+                @Nested
+                class NestedClass {
+                    @Test
+                    void nestedTest() {
+                    }
+                    @Test
+                    void anotherTest() {
+                    }
+                }
+                @Nested
+                class AnotherNestedClass {
+                    @Test
+                    void foo() {
+                    }
+                }
+                @Test
+                void foo() {
+                }
+            }
+        '''
+        when:
+        succeeds('test', '--tests', 'EnclosingClass$NestedClass.nestedTest')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted('EnclosingClass$NestedClass')
+            .testClass('EnclosingClass$NestedClass')
+            .assertTestCount(1, 0, 0)
+            .assertTestPassed('nestedTest')
+    }
+
+    def 'can filter tests from a superclass'() {
+        given:
+        file('src/test/java/SuperClass.java') << '''
+            import org.junit.jupiter.api.Test;
+
+            abstract class SuperClass {
+                @Test
+                void superTest() {
+                }
+            }
+        '''
+        file('src/test/java/SubClass.java') << '''
+            import org.junit.jupiter.api.Test;
+
+            class SubClass extends SuperClass {
+                @Test
+                void subTest() {
+                }
+            }
+        '''
+
+        when:
+        succeeds('test', '--tests', 'SubClass.superTest')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted('SubClass')
+            .testClass('SubClass')
+            .assertTestCount(1, 0, 0)
+            .assertTestPassed('superTest')
+    }
+
+    /**
+     * This test documents the status quo behavior of the test runner, where tests based on fields
+     * are not filtered by exclude patterns.  It might be desirable to change this behavior in the
+     * future to filter on field names directly; if this is done, this test should be replaced.
+     */
+    @Issue("https://github.com/gradle/gradle/issues/19352")
+    def 'does not exclude tests with a non-standard test source if filter matches nothing'() {
+        given:
+        buildFile << """
+            dependencies {
+                testImplementation 'com.tngtech.archunit:archunit-junit5:${LATEST_ARCHUNIT_VERSION}'
+            }
+
+            test {
+                filter {
+                    excludeTestsMatching "*notMatchingAnythingSoEverythingShouldBeRun"
+                }
+            }
+        """
+        file('src/test/java/DeclaresTestsAsFieldsNotMethodsTest.java') << '''
+            import com.tngtech.archunit.junit.AnalyzeClasses;
+            import com.tngtech.archunit.junit.ArchTest;
+            import com.tngtech.archunit.lang.ArchRule;
+
+            import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+
+            @AnalyzeClasses(packages = "example")
+            class DeclaresTestsAsFieldsNotMethodsTest {
+                // this will create a JUnit Platform TestDescriptor with neither a Class- nor a MethodSource
+                @ArchTest
+                static final ArchRule example = classes().should().bePublic();
+            }
+        '''
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted('DeclaresTestsAsFieldsNotMethodsTest')
+            .testClass('DeclaresTestsAsFieldsNotMethodsTest')
+            .assertTestCount(1, 0, 0)
+            .assertTestPassed('example')
+    }
+
+    /**
+     * This test documents the status quo behavior of the test runner, where tests based on fields
+     * are not filtered by exclude patterns.  It might be desirable to change this behavior in the
+     * future to filter on field names directly; if this is done, this test should be replaced.
+     */
+    @Issue("https://github.com/gradle/gradle/issues/19352")
+    def 'does not exclude tests with a non-standard test source if filter matches field name'() {
+        given:
+        buildFile << """
+            dependencies {
+                testImplementation 'com.tngtech.archunit:archunit-junit5:${LATEST_ARCHUNIT_VERSION}'
+            }
+
+            test {
+                filter {
+                    excludeTestsMatching "*example"
+                }
+            }
+        """
+        file('src/test/java/DeclaresTestsAsFieldsNotMethodsTest.java') << '''
+            import com.tngtech.archunit.junit.AnalyzeClasses;
+            import com.tngtech.archunit.junit.ArchTest;
+            import com.tngtech.archunit.lang.ArchRule;
+
+            import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+
+            @AnalyzeClasses(packages = "example")
+            class DeclaresTestsAsFieldsNotMethodsTest {
+                // this will create a JUnit Platform TestDescriptor with neither a Class- nor a MethodSource
+                @ArchTest
+                static final ArchRule example = classes().should().bePublic();
+            }
+        '''
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted('DeclaresTestsAsFieldsNotMethodsTest')
+            .testClass('DeclaresTestsAsFieldsNotMethodsTest')
+            .assertTestCount(1, 0, 0)
+            .assertTestPassed('example')
+    }
+
+    /**
+     * This test demonstrates the workaround for the inabilty to filter fields - we can
+     * filter based on containing class name.
+     */
+    @Issue("https://github.com/gradle/gradle/issues/19352")
+    def 'can filter tests with a non-standard test source using containing class name'() {
+        given:
+        buildFile << """
+            dependencies {
+                testImplementation 'com.tngtech.archunit:archunit-junit5:${LATEST_ARCHUNIT_VERSION}'
+            }
+
+            test {
+                filter {
+                    excludeTestsMatching "*DeclaresTestsAsFieldsNotMethodsTest"
+                }
+            }
+        """
+        file('src/test/java/DeclaresTestsAsFieldsNotMethodsTest.java') << '''
+            import com.tngtech.archunit.junit.AnalyzeClasses;
+            import com.tngtech.archunit.junit.ArchTest;
+            import com.tngtech.archunit.lang.ArchRule;
+
+            import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+
+            @AnalyzeClasses(packages = "example")
+            class DeclaresTestsAsFieldsNotMethodsTest {
+                // this will create a JUnit Platform TestDescriptor with neither a Class- nor a MethodSource
+                @ArchTest
+                static final ArchRule example = classes().should().bePublic();
+            }
+        '''
+
+        expect:
+        fails('test')
+        errorOutput.contains("No tests found for given includes")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformIntegrationSpec.groovy
new file mode 100644
index 0000000..14e8f2e
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformIntegrationSpec.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
+
+class JUnitPlatformIntegrationSpec extends AbstractIntegrationSpec {
+    def setup() {
+        executer.noExtraLogging()
+        buildScriptWithJupiterDependencies("""
+            test {
+                useJUnitPlatform()
+            }
+        """)
+    }
+
+    def buildScriptWithJupiterDependencies(script, String version = LATEST_JUPITER_VERSION) {
+        buildScript("""
+            apply plugin: 'java'
+
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'org.junit.jupiter:junit-jupiter:${version}'
+                testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+            }
+            $script
+        """)
+    }
+
+    void createSimpleJupiterTest() {
+        file('src/test/java/org/gradle/JUnitJupiterTest.java') << '''
+            package org.gradle;
+
+            import org.junit.jupiter.api.Test;
+
+            public class JUnitJupiterTest {
+                @Test
+                public void ok() { }
+            }
+            '''
+    }
+
+    void createSimpleJupiterTests() {
+        file('src/test/java/org/gradle/JUnitJupiterTest.java') << '''
+            package org.gradle;
+
+            import org.junit.jupiter.api.Tag;
+            import org.junit.jupiter.api.Test;
+
+            public class JUnitJupiterTest {
+                @Test
+                @Tag("good")
+                public void good() { }
+
+                @Test
+                @Tag("bad")
+                public void bad() { }
+            }
+            '''
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformIntegrationTest.groovy
new file mode 100644
index 0000000..6d58bfd
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformIntegrationTest.groovy
@@ -0,0 +1,464 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import org.gradle.api.internal.tasks.testing.junit.JUnitSupport
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import spock.lang.IgnoreIf
+import spock.lang.Issue
+import spock.lang.Timeout
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_PLATFORM_VERSION
+import static org.hamcrest.CoreMatchers.containsString
+
+class JUnitPlatformIntegrationTest extends JUnitPlatformIntegrationSpec {
+
+    def 'can work with junit-platform-runner'() {
+        given:
+        buildFile << """
+        dependencies {
+            testImplementation 'org.junit.platform:junit-platform-runner:1.0.3'
+        }
+        """
+        createSimpleJupiterTest()
+
+        expect:
+        succeeds('test')
+    }
+
+    def 'can handle class level ignored tests'() {
+        given:
+        file('src/test/java/org/gradle/IgnoredTest.java') << '''
+            package org.gradle;
+
+            import org.junit.jupiter.api.*;
+
+            @Disabled
+            public class IgnoredTest {
+                @Test
+                public void testIgnored1() {
+                    throw new RuntimeException();
+                }
+            }
+        '''
+
+        when:
+        run('check')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted('org.gradle.IgnoredTest')
+            .testClass('org.gradle.IgnoredTest').assertTestCount(1, 0, 0).assertTestsSkipped("testIgnored1()")
+    }
+
+    def 'can handle class-level error in #location method'() {
+        given:
+        file('src/test/java/org/gradle/ClassErrorTest.java') << """
+            package org.gradle;
+
+            import org.junit.jupiter.api.*;
+            import static org.junit.jupiter.api.Assertions.*;
+
+            public class ClassErrorTest {
+                @Test
+                public void ok() {
+                }
+
+                @BeforeAll
+                public static void before() {
+                    $beforeStatement;
+                }
+
+                @AfterAll
+                public static void after() {
+                    $afterStatement;
+                }
+            }
+        """
+
+        when:
+        fails('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecuted('org.gradle.ClassErrorTest')
+            .testClass('org.gradle.ClassErrorTest')
+            .assertTestCount(successCount + 1, 1, 0)
+            .assertTestFailed(failedTestName, containsString(location))
+
+        where:
+        location     | beforeStatement      | afterStatement      | successCount | failedTestName
+        '@BeforeAll' | 'fail("@BeforeAll")' | ''                  | 0            | "initializationError"
+        '@AfterAll'  | ''                   | 'fail("@AfterAll")' | 1            | "executionError"
+    }
+
+    def 'can handle class level assumption'() {
+        given:
+        file('src/test/java/org/gradle/ClassAssumeTest.java') << '''
+        package org.gradle;
+
+        import org.junit.jupiter.api.*;
+        import org.junit.jupiter.api.Assumptions;
+
+        public class ClassAssumeTest {
+            @Test
+            public void ok() {
+            }
+
+            @BeforeAll
+            public static void before() {
+                Assumptions.assumeTrue(false);
+            }
+        }
+        '''
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory).testClass('org.gradle.ClassAssumeTest').assertTestCount(1, 0, 0)
+    }
+
+    def 'can handle repeated tests'() {
+        given:
+        file('src/test/java/org/gradle/RepeatTest.java') << '''
+        package org.gradle;
+
+        import org.junit.jupiter.api.*;
+        import org.junit.jupiter.api.Assumptions;
+
+        public class RepeatTest {
+            @RepeatedTest(value = 3, name = "ok {currentRepetition}/{totalRepetitions}")
+            public void ok() {
+            }
+
+            @RepeatedTest(value = 3, name = "partialFail {currentRepetition}/{totalRepetitions}")
+            public void partialFail(RepetitionInfo repetitionInfo) {
+                if (repetitionInfo.getCurrentRepetition() == 2) {
+                    throw new RuntimeException();
+                }
+            }
+
+            @RepeatedTest(value = 3, name = "partialSkip {currentRepetition}/{totalRepetitions}")
+            public void partialSkip(RepetitionInfo repetitionInfo) {
+                if (repetitionInfo.getCurrentRepetition() == 2) {
+                    Assumptions.assumeTrue(false);
+                }
+            }
+        }
+        '''
+
+        when:
+        fails('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .assertTestClassesExecutedJudgementByHtml('org.gradle.RepeatTest')
+            .testClassByHtml('org.gradle.RepeatTest')
+            .assertTestCount(9, 1, 0)
+            .assertTestPassed('ok()[1]', 'ok 1/3')
+            .assertTestPassed('ok()[2]', 'ok 2/3')
+            .assertTestPassed('ok()[3]', 'ok 3/3')
+            .assertTestPassed('partialFail(RepetitionInfo)[1]', 'partialFail 1/3')
+            .assertTestFailed('partialFail(RepetitionInfo)[2]', 'partialFail 2/3', containsString('java.lang.RuntimeException'))
+            .assertTestPassed('partialFail(RepetitionInfo)[3]', 'partialFail 3/3')
+            .assertTestPassed('partialSkip(RepetitionInfo)[1]', 'partialSkip 1/3')
+            .assertTestSkipped('partialSkip(RepetitionInfo)[2]', 'partialSkip 2/3')
+            .assertTestPassed('partialSkip(RepetitionInfo)[3]', 'partialSkip 3/3')
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/4476')
+    def 'can handle test engine failure'() {
+        given:
+        createSimpleJupiterTest()
+        file('src/test/java/UninstantiableExtension.java') << '''
+import org.junit.jupiter.api.extension.*;
+public class UninstantiableExtension implements BeforeEachCallback {
+  private UninstantiableExtension(){}
+
+  @Override
+  public void beforeEach(final ExtensionContext context) throws Exception {
+  }
+}
+'''
+        file('src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension') << 'UninstantiableExtension'
+        buildFile << '''
+            test {
+                systemProperty('junit.jupiter.extensions.autodetection.enabled', 'true')
+            }
+        '''
+
+        when:
+        fails('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClass(JUnitSupport.UNKNOWN_CLASS)
+            .assertTestFailed('initializationError', containsString('UninstantiableExtension'))
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/4427')
+    def 'can run tests in static nested class'() {
+        given:
+        file('src/test/java/org/gradle/StaticInnerTest.java') << '''
+package org.gradle;
+import org.junit.jupiter.api.*;
+public class StaticInnerTest {
+    public static class Nested {
+        @Test
+        public void inside() {
+        }
+
+        public static class Nested2 {
+            @Test
+            public void inside() {
+            }
+        }
+    }
+
+    @Test
+    public void outside() {
+    }
+}
+'''
+        when:
+        succeeds('test')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted('org.gradle.StaticInnerTest', 'org.gradle.StaticInnerTest$Nested', 'org.gradle.StaticInnerTest$Nested$Nested2')
+        result.testClass('org.gradle.StaticInnerTest').assertTestCount(1, 0, 0)
+            .assertTestPassed('outside')
+        result.testClass('org.gradle.StaticInnerTest$Nested').assertTestCount(1, 0, 0)
+            .assertTestPassed('inside')
+        result.testClass('org.gradle.StaticInnerTest$Nested$Nested2').assertTestCount(1, 0, 0)
+            .assertTestPassed('inside')
+    }
+
+    @Issue('https://github.com/gradle/gradle/issues/4924')
+    def "re-executes test when #key is changed"() {
+        given:
+        buildScriptWithJupiterDependencies("""
+            test {
+                useJUnitPlatform {
+                    ${key} ${value}
+                }
+            }
+        """)
+        createSimpleJupiterTests()
+
+        when:
+        succeeds ':test'
+
+        then:
+        executedAndNotSkipped ':test'
+
+        when:
+        buildScriptWithJupiterDependencies("""
+            test {
+                useJUnitPlatform()
+            }
+        """)
+
+        and:
+        succeeds ':test'
+
+        then:
+        executedAndNotSkipped ':test'
+
+        where:
+        key              | value
+        'includeTags'    | '"good"'
+        'excludeTags'    | '"bad"'
+        'includeEngines' | '"junit-jupiter"'
+        'excludeEngines' | '"junit-vintage-engine"'
+    }
+
+    @Timeout(60)
+    @Issue('https://github.com/gradle/gradle/issues/6453')
+    def "can handle parallel test execution"() {
+        given:
+        def numTestClasses = 32
+        buildScriptWithJupiterDependencies("""
+            test {
+                useJUnitPlatform()
+                systemProperty('junit.jupiter.execution.parallel.enabled', 'true')
+                systemProperty('junit.jupiter.execution.parallel.config.strategy', 'fixed')
+                systemProperty('junit.jupiter.execution.parallel.config.fixed.parallelism', '$numTestClasses')
+            }
+        """)
+        file('src/test/java/org/gradle/Tests.java') << """
+            package org.gradle;
+
+            import java.util.concurrent.*;
+            import org.junit.jupiter.api.*;
+            import org.junit.jupiter.api.parallel.*;
+            import static org.junit.jupiter.api.Assertions.*;
+            import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;
+
+            @Execution(CONCURRENT)
+            class Sync {
+                static CountDownLatch LATCH = new CountDownLatch($numTestClasses);
+            }
+
+            ${(1..numTestClasses).collect { classNumber -> """
+                class Test$classNumber extends Sync {
+                    @Test
+                    public void test() throws Exception {
+                        LATCH.countDown();
+                        LATCH.await();
+                    }
+                }
+            """ }.join("") }
+        """
+
+        when:
+        succeeds(':test')
+
+        then:
+        with(new DefaultTestExecutionResult(testDirectory)) {
+            (1..numTestClasses).every { classNumber ->
+                testClass("org.gradle.Test$classNumber").assertTestCount(1, 0, 0)
+            }
+        }
+    }
+
+    @Issue("https://github.com/junit-team/junit5/issues/2028 and https://github.com/gradle/gradle/issues/12073")
+    def 'properly fails when engine fails during discovery #scenario'() {
+        given:
+        createSimpleJupiterTest()
+        buildFile << """
+            dependencies {
+                testImplementation 'org.junit.platform:junit-platform-engine:${LATEST_PLATFORM_VERSION}'
+            }
+        """
+        file('src/test/java/EngineFailingDiscovery.java') << '''
+            import org.junit.platform.engine.*;
+            public class EngineFailingDiscovery implements TestEngine {
+                @Override
+                public String getId() {
+                    return "EngineFailingDiscovery";
+                }
+
+                @Override
+                public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {
+                    throw new RuntimeException("oops");
+                }
+
+                @Override
+                public void execute(ExecutionRequest request) {
+                }
+            }
+        '''
+        file('src/test/resources/META-INF/services/org.junit.platform.engine.TestEngine') << 'EngineFailingDiscovery'
+
+        expect:
+        fails('test', *extraArgs)
+        failureCauseContains('There were failing tests.')
+
+        where:
+        scenario       | extraArgs
+        "w/o filters"  | []
+        "with filters" | ['--tests', 'JUnitJupiterTest']
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/23602")
+    def "handles unserializable exception thrown from test"() {
+        given:
+        file('src/test/java/PoisonTest.java') << """
+            import org.junit.jupiter.api.Test;
+
+            public class PoisonTest {
+                @Test
+                public void passingTest() { }
+
+                @Test
+                public void testWithUnserializableException() {
+                    throw new UnserializableException();
+                }
+
+                @Test
+                public void normalFailingTest() {
+                    assert false;
+                }
+
+                private static class WriteReplacer implements java.io.Serializable {
+                    private Object readResolve() {
+                        return new RuntimeException();
+                    }
+                }
+
+                private static class UnserializableException extends RuntimeException {
+                    private Object writeReplace() {
+                        return new WriteReplacer();
+                    }
+                }
+            }
+        """
+
+        when:
+        fails("test")
+
+        then:
+        with(new DefaultTestExecutionResult(testDirectory).testClass("PoisonTest")) {
+            assertTestPassed("passingTest")
+            assertTestFailed("testWithUnserializableException", containsString("TestFailureSerializationException: An exception of type PoisonTest\$UnserializableException was thrown by the test, but Gradle was unable to recreate the exception in the build process"))
+            assertTestFailed("normalFailingTest", containsString("AssertionError"))
+        }
+    }
+
+    // When running embedded with test distribution, the remote distribution has a newer version of
+    // junit-platform-launcher which is not compatible with the junit jupiter jars we test against.
+    @IgnoreIf({ GradleContextualExecuter.embedded })
+    // JUnitCoverage is quite limited and doesn't test older versions or the newest version.
+    // Future work is planned to improve junit test rewriting, and at the same time should verify
+    // greater ranges of junit platform testing. This is only reproducible with the newest version
+    // of junit, so test that version explicitly here.
+    @Issue("https://github.com/gradle/gradle/issues/24429")
+    def "works with parameterized tests for larger version range"() {
+        given:
+        buildScriptWithJupiterDependencies("""
+            test {
+                useJUnitPlatform()
+            }
+        """, version)
+
+        file("src/test/java/SomeTest.java") << """
+            import java.util.stream.Stream;
+            import org.junit.jupiter.params.ParameterizedTest;
+            import org.junit.jupiter.params.provider.Arguments;
+            import org.junit.jupiter.params.provider.MethodSource;
+
+            class SomeTest {
+                public static Stream<Arguments> args() {
+                    return Stream.of(Arguments.of("blah"));
+                }
+
+                @ParameterizedTest
+                @MethodSource("args")
+                void someTest(String value) { }
+            }
+        """
+
+        expect:
+        succeeds "test"
+
+        where:
+        version << ["5.9.2", "5.6.3"]
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformLauncherSessionListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformLauncherSessionListenerIntegrationTest.groovy
new file mode 100644
index 0000000..55a92e3
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformLauncherSessionListenerIntegrationTest.groovy
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import spock.lang.Issue
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_PLATFORM_VERSION
+
+/**
+ * Tests JUnitPlatform integrations with {@code LauncherSessionListener}.
+ */
+class JUnitPlatformLauncherSessionListenerIntegrationTest extends JUnitPlatformIntegrationSpec {
+
+    /**
+     * @see <a href=https://github.com/JetBrains/intellij-community/commit/d41841670c8a98c0464ef25ef490c79b5bafe8a9">The IntelliJ commit</a>
+     * which introduced a {@code LauncherSessionListener} onto the test classpath when using the {@code org.jetbrains.intellij} plugin.
+     */
+    @Issue("https://github.com/gradle/gradle/issues/22333")
+    def "LauncherSessionListeners are automatically loaded from the test classpath when listener does not provide junit platform launcher dependency"() {
+        settingsFile << "include 'other'"
+        file("other/build.gradle") << """
+            plugins {
+                id 'java'
+            }
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                compileOnly 'org.junit.platform:junit-platform-launcher:1.9.1'
+            }
+        """
+        file("other/src/main/java/com/example/MyLauncherSessionListener.java") << """
+            package com.example;
+            import org.junit.platform.launcher.LauncherSession;
+            import org.junit.platform.launcher.LauncherSessionListener;
+            public class MyLauncherSessionListener implements LauncherSessionListener {
+                public void launcherSessionOpened(LauncherSession session) {
+                    System.out.println("Session opened");
+                }
+                public void launcherSessionClosed(LauncherSession session) {
+                    System.out.println("Session closed");
+                }
+            }
+        """
+        file("other/src/main/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener") << """
+            com.example.MyLauncherSessionListener
+        """
+
+        buildFile.text = """
+            plugins {
+                id 'java'
+            }
+
+            ${mavenCentralRepository()}
+
+            dependencies {
+                testImplementation project(':other')
+                testCompileOnly 'org.junit.jupiter:junit-jupiter:5.9.1'
+                testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1'
+            }
+
+            test {
+                useJUnitPlatform()
+                testLogging.showStandardStreams = true
+            }
+        """
+        file("src/test/java/com/example/MyTest.java") << """
+            package com.example;
+
+            public class MyTest {
+                @org.junit.jupiter.api.Test
+                public void myTest() { }
+            }
+        """
+
+        when:
+        executer.expectDocumentedDeprecationWarning("The automatic loading of test framework implementation dependencies has been deprecated. This is scheduled to be removed in Gradle 9.0. Declare the desired test framework directly on the test suite or explicitly declare the test framework implementation dependencies on the test's runtime classpath. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_framework_implementation_dependencies")
+        succeeds "test"
+
+        then:
+        outputContains("Session opened")
+        outputContains("Session closed")
+
+        when:
+        succeeds "dependencies", "--configuration", "testRuntimeClasspath"
+
+        then:
+        // Sanity check in case future versions for some reason include a launcher
+        outputDoesNotContain("junit-platform-launcher")
+    }
+
+    def "creates LauncherSession before loading test classes"() {
+        given:
+        createSimpleJupiterTest()
+        buildFile << """
+            dependencies {
+                testImplementation 'org.junit.platform:junit-platform-launcher:${LATEST_PLATFORM_VERSION}'
+            }
+            test {
+                testLogging {
+                    showStandardStreams = true
+                }
+            }
+        """
+        file('src/test/java/NoisyLauncherSessionListener.java') << '''
+            import org.junit.platform.launcher.*;
+            public class NoisyLauncherSessionListener implements LauncherSessionListener {
+                @Override public void launcherSessionOpened(LauncherSession session) {
+                    System.out.println("launcherSessionOpened");
+                    Thread thread = Thread.currentThread();
+                    ClassLoader parent = thread.getContextClassLoader();
+                    ClassLoader replacement = new ClassLoader(parent) {
+                        @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+                            System.out.println("Loading class " + name);
+                            return super.loadClass(name, resolve);
+                        }
+                    };
+                    thread.setContextClassLoader(replacement);
+                }
+                @Override public void launcherSessionClosed(LauncherSession session) {
+                    System.out.println("launcherSessionClosed");
+                }
+            }
+        '''
+        file('src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener') << '''\
+            NoisyLauncherSessionListener
+        '''.stripIndent(true)
+
+        expect:
+        succeeds('test')
+        outputContains('launcherSessionOpened')
+        outputContains('Loading class org.gradle.JUnitJupiterTest')
+        outputContains('launcherSessionClosed')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..dcddc4a
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformLoggingIntegrationTest.groovy
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import spock.lang.Issue
+
+class JUnitPlatformLoggingIntegrationTest extends JUnitPlatformIntegrationSpec {
+
+    @Override
+    def setup() {
+        buildFile << """
+            test {
+                testLogging {
+                    events "passed", "skipped", "failed"
+                }
+            }
+        """
+    }
+
+    def "should log display names if present"() {
+        given:
+        file("src/test/java/pkg/TopLevelClass.java") << """
+            package pkg;
+            import org.junit.jupiter.api.DisplayName;
+            import org.junit.jupiter.api.Nested;
+            import org.junit.jupiter.api.Test;
+
+            @DisplayName("Class level display name")
+            public class TopLevelClass {
+
+                @Nested
+                @DisplayName("Nested class display name")
+                public class NestedClass {
+
+                    @Test
+                    @DisplayName("Nested test method display name")
+                    public void nestedTestMethod() {
+                    }
+                }
+
+                @Test
+                @DisplayName("Method display name")
+                public void testMethod() {
+                }
+            }
+         """
+
+        when:
+        run("test")
+
+        then:
+        outputContains("Class level display name > Method display name")
+        outputContains("Class level display name > Nested class display name > Nested test method display name")
+    }
+
+    def "should fall back to plain name if no display names present"() {
+        given:
+        file("src/test/java/pkg/TopLevelClass.java") << """
+            package pkg;
+
+            import org.junit.jupiter.api.DisplayName;
+            import org.junit.jupiter.api.Nested;
+            import org.junit.jupiter.api.Test;
+
+            public class TopLevelClass {
+
+                @Nested
+                public class NestedClass {
+
+                    @Test
+                    public void nestedTestMethod() {
+                    }
+                }
+
+                @Test
+                public void testMethod() {
+                }
+            }
+         """
+
+        when:
+        run("test")
+
+        then:
+        outputContains("TopLevelClass > testMethod()")
+        outputContains("TopLevelClass > NestedClass > nestedTestMethod()")
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/5975")
+    def "should log display names for dynamically created tests"() {
+        given:
+        file("src/test/java/org/gradle/JUnitJupiterDynamicTest.java") << """
+            package org.gradle;
+            import org.junit.jupiter.api.DynamicTest;
+            import org.junit.jupiter.api.TestFactory;
+            import java.util.stream.IntStream;
+            import java.util.stream.Stream;
+            import static org.junit.jupiter.api.Assertions.*;
+            import static org.junit.jupiter.api.DynamicTest.dynamicTest;
+            public class JUnitJupiterDynamicTest {
+                @TestFactory
+                Stream<DynamicTest> streamOfTests() {
+                    return IntStream.of(2, 4, 5)
+                        .mapToObj(v -> dynamicTest(v + " is even", () -> assertEquals(0, v % 2)));
+                }
+            }
+        """
+
+        when:
+        runAndFail("test")
+
+        then:
+        def parentEventPath = "JUnitJupiterDynamicTest > streamOfTests()"
+        outputContains("${parentEventPath} > 2 is even PASSED")
+        outputContains("${parentEventPath} > 4 is even PASSED")
+        outputContains("${parentEventPath} > 5 is even FAILED")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformOnJdk7IntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformOnJdk7IntegrationTest.groovy
new file mode 100644
index 0000000..09efa03
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformOnJdk7IntegrationTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
+import static org.junit.Assume.assumeNotNull
+
+class JUnitPlatformOnJdk7IntegrationTest extends AbstractIntegrationSpec {
+
+    def setup() {
+        def jdk7 = AvailableJavaHomes.getJdk7()
+        assumeNotNull(jdk7)
+        file("gradle.properties").writeProperties("org.gradle.java.installations.paths": jdk7.javaHome.canonicalPath)
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'org.junit.jupiter:junit-jupiter:${LATEST_JUPITER_VERSION}'
+            }
+
+            java {
+                disableAutoTargetJvm()
+                toolchain {
+                    languageVersion = JavaLanguageVersion.of(7)
+                }
+            }
+            test { useJUnitPlatform() }
+            """
+        file('src/test/java/org/gradle/JUnitJupiterTest.java') << '''
+            package org.gradle;
+
+            import org.junit.jupiter.api.Test;
+
+            public class JUnitJupiterTest {
+                @Test
+                public void ok() { }
+            }
+            '''
+    }
+
+    def 'can forbid user to run JUnit platform on Java 7'() {
+        when:
+        def failure = fails('test')
+
+        then:
+        failure.assertHasCause('Running tests with JUnit platform requires a Java 8+ toolchain.')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformParameterizedTestIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformParameterizedTestIntegrationTest.groovy
new file mode 100644
index 0000000..73f9bb4
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformParameterizedTestIntegrationTest.groovy
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.fixtures.server.http.BlockingHttpServer
+import org.gradle.testing.fixture.JUnitPlatformTestFixture
+import org.hamcrest.CoreMatchers
+import org.junit.Rule
+import spock.lang.Issue
+
+import static org.gradle.testing.fixture.JUnitCoverage.getLATEST_PLATFORM_VERSION
+
+
+class JUnitPlatformParameterizedTestIntegrationTest extends JUnitPlatformIntegrationSpec implements JUnitPlatformTestFixture {
+    @Override
+    TestFile getProjectDir() {
+        return testDirectory
+    }
+
+    @Rule BlockingHttpServer server = new BlockingHttpServer()
+
+    @Issue("https://github.com/gradle/gradle/issues/20081")
+    def "test report receives events for disabled parameterized test"() {
+        given:
+        testClass("PassingWithDisabledParameterizedTest").with {
+            testMethod('passingTest')
+            testMethod('disabledTest').disabled()
+            parameterizedMethod('disabledParameterizedTest').disabled()
+            parameterizedMethod('enabledParameterizedTest')
+        }
+        testClass("OnlyDisabledParameterizedTest").with {
+            parameterizedMethod('disabledParameterizedTest1').disabled()
+            parameterizedMethod('disabledParameterizedTest2').disabled()
+        }
+        writeTestClassFiles()
+
+        when:
+        run "test"
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("PassingWithDisabledParameterizedTest", "OnlyDisabledParameterizedTest")
+        result.testClassByHtml("PassingWithDisabledParameterizedTest")
+            .assertTestCount(5, 0, 0)
+            .assertTestsSkipped("disabledTest", "disabledParameterizedTest(String)")
+            .assertTestPassed("passingTest")
+            .assertTestPassed("enabledParameterizedTest(String)[1]", "[1] first")
+            .assertTestPassed("enabledParameterizedTest(String)[2]", "[2] second")
+        result.testClassByHtml("OnlyDisabledParameterizedTest")
+            .assertTestCount(2, 0, 0)
+            .assertTestsSkipped("disabledParameterizedTest1(String)", "disabledParameterizedTest2(String)")
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/20081")
+    def "test report receives events for disabled parameterized test within test suite"() {
+        given:
+        buildFile << """
+            dependencies {
+                testImplementation 'org.junit.platform:junit-platform-suite-engine:${LATEST_PLATFORM_VERSION}'
+            }
+        """
+
+        testSuite("TestSuite").with {
+            testClass("PassingWithDisabledParameterizedTest").with {
+                testMethod("passingTest")
+                testMethod("disabledTest").disabled()
+                parameterizedMethod("disabledParameterizedTest").disabled()
+                parameterizedMethod("enabledParameterizedTest")
+            }
+            testClass("OnlyDisabledParameterizedTest").with {
+                parameterizedMethod("disabledParameterizedTest1").disabled()
+                parameterizedMethod("disabledParameterizedTest2").disabled()
+            }
+        }
+
+        writeTestClassFiles()
+
+        when:
+        run("test", "--tests", "TestSuite")
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+
+        result.assertTestClassesExecuted("PassingWithDisabledParameterizedTest", "OnlyDisabledParameterizedTest")
+        result.testClassByHtml("PassingWithDisabledParameterizedTest")
+            .assertTestCount(5, 0, 0)
+            .assertTestsSkipped("disabledTest", "disabledParameterizedTest(String)")
+            .assertTestPassed("passingTest")
+            .assertTestPassed("enabledParameterizedTest(String)[1]", "[1] first")
+            .assertTestPassed("enabledParameterizedTest(String)[2]", "[2] second")
+        result.testClassByHtml("OnlyDisabledParameterizedTest")
+            .assertTestCount(2, 0, 0)
+            .assertTestsSkipped("disabledParameterizedTest1(String)", "disabledParameterizedTest2(String)")
+    }
+
+    @Issue("https://github.com/gradle/gradle/issues/20081")
+    def "test report receives events for skipped parameterized test when there is a failure and fail fast is used"() {
+        given:
+        server.start()
+        buildFile << """
+            test { maxParallelForks = 2 }
+        """
+        testClass("FailingTest")
+            .testMethod('failingTest')
+                .shouldFail()
+                .customContent(server.callFromBuild("failingTest"))
+        testClass("WithParameterizedTest")
+            .parameterizedMethod('enabledParameterizedTest')
+                .customContent(server.callFromBuild("enabledParameterizedTest"))
+        writeTestClassFiles()
+
+        def testExecution = server.expectConcurrentAndBlock('failingTest', 'enabledParameterizedTest')
+
+        when:
+        def gradle = executer.withTasks('test', '--fail-fast').start()
+
+        then:
+        testExecution.waitForAllPendingCalls()
+
+        when:
+        testExecution.release('failingTest')
+
+        then:
+        gradle.waitForFailure()
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecuted("FailingTest", "WithParameterizedTest")
+        result.testClassByHtml("FailingTest")
+            .assertTestFailed("failingTest", CoreMatchers.anything())
+        result.testClassByHtml("WithParameterizedTest")
+            .assertTestSkipped("enabledParameterizedTest(String)", "enabledParameterizedTest(String)")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformSampleIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformSampleIntegrationTest.groovy
new file mode 100644
index 0000000..6e10138
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformSampleIntegrationTest.groovy
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import org.gradle.integtests.fixtures.AbstractSampleIntegrationTest
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+import org.gradle.integtests.fixtures.Sample
+import org.gradle.integtests.fixtures.UsesSample
+import org.junit.Rule
+
+class JUnitPlatformSampleIntegrationTest extends AbstractSampleIntegrationTest {
+    @Rule
+    public final Sample sample = new Sample(testDirectoryProvider)
+
+    @UsesSample('testing/junitplatform-jupiter/groovy')
+    def 'jupiter sample test'() {
+        given:
+        super.sample sample
+
+        when:
+        succeeds 'test'
+
+        then:
+        new DefaultTestExecutionResult(sample.dir).testClassByHtml('org.gradle.junitplatform.JupiterTest').assertTestCount(5, 0, 0)
+            .assertTestPassed('ok')
+            .assertTestPassed('repeated()[1]', 'repetition 1 of 2')
+            .assertTestPassed('repeated()[2]', 'repetition 2 of 2')
+            .assertTestPassed('test1(TestInfo)', 'TEST 1')
+            .assertTestSkipped('disabled')
+    }
+
+    @UsesSample('testing/junitplatform-mix/groovy')
+    def 'mix JUnit3/4/5'() {
+        given:
+        super.sample sample
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(sample.dir)
+            .testClass('org.gradle.junitplatform.JUnit3Test').assertTestCount(1, 0, 0)
+        new DefaultTestExecutionResult(sample.dir)
+            .testClass('org.gradle.junitplatform.JUnit4Test').assertTestCount(1, 0, 0)
+        new DefaultTestExecutionResult(sample.dir)
+            .testClass('org.gradle.junitplatform.JupiterTest').assertTestCount(1, 0, 0)
+    }
+
+    @UsesSample('testing/junitplatform-engine/groovy')
+    def 'engine sample test'() {
+        given:
+        super.sample sample
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(sample.dir)
+            .assertTestClassesExecuted('org.gradle.junitplatform.JUnit4Test')
+            .testClass('org.gradle.junitplatform.JUnit4Test').assertTestCount(1, 0, 0)
+    }
+
+    @UsesSample('testing/junitplatform-tagging/groovy')
+    def 'tagging sample test'() {
+        given:
+        super.sample sample
+
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(sample.dir).testClass('org.gradle.junitplatform.TagTest').assertTestCount(1, 0, 0)
+            .assertTestPassed('fastTest()')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformUserGuideIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformUserGuideIntegrationTest.groovy
new file mode 100644
index 0000000..8884f15
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/JUnitPlatformUserGuideIntegrationTest.groovy
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2023 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.testing.junit.platform
+
+import org.gradle.integtests.fixtures.DefaultTestExecutionResult
+
+import static org.hamcrest.CoreMatchers.containsString
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
+
+/**
+ * These test cases are all from http://junit.org/junit5/docs/current/user-guide
+ */
+class JUnitPlatformUserGuideIntegrationTest extends JUnitPlatformIntegrationSpec {
+    def 'can display test case and test class in @DisplayName'() {
+        given:
+        file('src/test/java/org/gradle/DisplayNameDemo.java') << '''
+package org.gradle;
+import org.junit.jupiter.api.*;
+
+@DisplayName("A special test case")
+class DisplayNameDemo {
+
+    @Test
+    @DisplayName("Custom test name containing spaces")
+    void testWithDisplayNameContainingSpaces() {
+    }
+}
+'''
+        file('src/test/java/org/gradle/DisplayNameDemo2.java') << '''
+package org.gradle;
+import org.junit.jupiter.api.*;
+
+@DisplayName("A special test case2")
+class DisplayNameDemo2 {
+
+    @Test
+    @DisplayName("╯°□°)╯")
+    void testWithDisplayNameContainingSpecialCharacters() {
+    }
+}
+'''
+        when:
+        succeeds('test')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.assertTestClassesExecutedJudgementByHtml('org.gradle.DisplayNameDemo', 'org.gradle.DisplayNameDemo2')
+            .assertTestClassesExecutedJudgementByXml('A special test case', 'A special test case2')
+        result.testClassByHtml('org.gradle.DisplayNameDemo')
+            .assertDisplayName('A special test case')
+            .assertTestCount(1, 0, 0)
+            .assertTestPassed('testWithDisplayNameContainingSpaces', 'Custom test name containing spaces')
+        result.testClassByHtml('org.gradle.DisplayNameDemo2')
+            .assertDisplayName('A special test case2')
+            .assertTestCount(1, 0, 0)
+            .assertTestPassed('testWithDisplayNameContainingSpecialCharacters', '╯°□°)╯')
+    }
+
+    def 'can change test instance lifecycle with #method'() {
+        given:
+        if (jvmArg) {
+            buildFile << """
+test {
+    jvmArgs '${jvmArg}'
+}
+"""
+        }
+        file('src/test/java/org/gradle/LifecycleTest.java') << """
+package org.gradle;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+
+${annotation}
+public class LifecycleTest {
+    private static int counter = 0;
+
+    public LifecycleTest() {
+        if(counter == 0) {
+            counter += 1;
+        } else {
+            throw new IllegalStateException("I can only be instantiated once!");
+        }
+    }
+
+    @Test
+    public void test1() {
+        System.out.println(this);
+    }
+    @Test
+    public void test2() {
+        System.out.println(this);
+    }
+}
+"""
+        expect:
+        succeeds('test')
+
+        where:
+        method        | jvmArg                                                     | annotation
+        'JVM args'    | '-Djunit.jupiter.testinstance.lifecycle.default=per_class' | ''
+        'annotations' | ''                                                         | '@TestInstance(Lifecycle.PER_CLASS)'
+    }
+
+    def 'can perform nested tests with #maxParallelForks'() {
+        given:
+        buildFile << """
+test {
+    maxParallelForks = ${maxParallelForks}
+}
+"""
+        file('src/test/java/org/gradle/TestingAStackDemo.java') << '''
+package org.gradle;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.EmptyStackException;
+import java.util.Stack;
+
+import org.junit.jupiter.api.*;
+
+@DisplayName("A stack")
+class TestingAStackDemo {
+
+    Stack<Object> stack;
+
+    @Test
+    @DisplayName("is instantiated with new Stack()")
+    void isInstantiatedWithNew() {
+        new Stack<>();
+    }
+
+    @Nested
+    @DisplayName("when new")
+    class WhenNew {
+
+        @BeforeEach
+        void createNewStack() {
+            stack = new Stack<>();
+        }
+
+        @Test
+        @DisplayName("is empty")
+        void isEmpty() {
+            assertTrue(stack.isEmpty());
+        }
+
+        @Test
+        @DisplayName("throws EmptyStackException when popped")
+        void throwsExceptionWhenPopped() {
+            assertThrows(EmptyStackException.class, () -> stack.pop());
+        }
+
+        @Nested
+        @DisplayName("after pushing an element")
+        class AfterPushing {
+
+            String anElement = "an element";
+
+            @BeforeEach
+            void pushAnElement() {
+                stack.push(anElement);
+            }
+
+            @Test
+            @DisplayName("it is no longer empty")
+            void isNotEmpty() {
+                assertFalse(stack.isEmpty());
+            }
+        }
+    }
+}
+'''
+        when:
+        succeeds('test')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClassByHtml('org.gradle.TestingAStackDemo').assertTestCount(1, 0, 0)
+            .assertTestPassed('isInstantiatedWithNew', 'is instantiated with new Stack')
+        result.testClassByHtml('org.gradle.TestingAStackDemo$WhenNew').assertTestCount(2, 0, 0)
+            .assertTestPassed('isEmpty', 'is empty')
+            .assertTestPassed('throwsExceptionWhenPopped', 'throws EmptyStackException when popped')
+        result.testClassByHtml('org.gradle.TestingAStackDemo$WhenNew$AfterPushing').assertTestCount(1, 0, 0)
+            .assertTestPassed('isNotEmpty', 'it is no longer empty')
+
+        where:
+        maxParallelForks << [1, 3]
+    }
+
+    def 'can support dependency injection'() {
+        given:
+        file('src/test/java/org/gradle/TestInfoDemo.java') << '''
+package org.gradle;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.*;
+
+@DisplayName("TestInfo Demo")
+class TestInfoDemo {
+
+    TestInfoDemo(TestInfo testInfo) {
+        assertEquals("TestInfo Demo", testInfo.getDisplayName());
+    }
+
+    @BeforeEach
+    void init(TestInfo testInfo) {
+        String displayName = testInfo.getDisplayName();
+        assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
+    }
+
+    @Test
+    @DisplayName("TEST 1")
+    @Tag("my-tag")
+    void test1(TestInfo testInfo) {
+        assertEquals("TEST 1", testInfo.getDisplayName());
+        assertTrue(testInfo.getTags().contains("my-tag"));
+    }
+
+    @Test
+    void test2() {
+    }
+}
+'''
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClassByHtml('org.gradle.TestInfoDemo').assertTestCount(2, 0, 0)
+            .assertTestPassed('test2', 'test2')
+            .assertTestPassed('test1(TestInfo)', 'TEST 1')
+
+    }
+
+    def 'can use custom Extension'() {
+        given:
+        file('src/test/java/org/gradle/MyExtension.java') << '''
+package org.gradle;
+import org.junit.jupiter.api.extension.*;
+
+public class MyExtension implements TestInstancePostProcessor {
+        @Override
+        public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
+                System.out.println("Created!");
+        }
+}
+'''
+        file('src/test/java/org/gradle/ExtensionTest.java') << '''
+package org.gradle;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.*;
+
+@ExtendWith(MyExtension.class)
+public class ExtensionTest {
+    @Test public void test1() { }
+    @Test public void test2() { }
+}
+'''
+        when:
+        succeeds('test')
+
+        then:
+        def result = new DefaultTestExecutionResult(testDirectory)
+        result.testClass('org.gradle.ExtensionTest').assertTestCount(2, 0, 0)
+            .assertStdout(containsString('Created!'))
+    }
+
+    def 'can test interface default method'() {
+        file('src/test/java/org/gradle/TestInterfaceDynamicTestsDemo.java') << '''
+package org.gradle;
+
+import org.junit.jupiter.api.*;
+import java.util.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+interface TestInterfaceDynamicTestsDemo {
+    @TestFactory
+    default Collection<DynamicTest> dynamicTestsFromCollection() {
+        return Arrays.asList(
+            DynamicTest.dynamicTest("1st dynamic test in test interface", () -> assertTrue(true)),
+            DynamicTest.dynamicTest("2nd dynamic test in test interface", () -> assertEquals(4, 2 * 2))
+        );
+    }
+
+    @BeforeEach
+    default void beforeEach() {
+        System.out.println("Invoked!");
+    }
+}
+'''
+        file('src/test/java/org/gradle/Test.java') << '''
+package org.gradle;
+
+public class Test implements TestInterfaceDynamicTestsDemo {
+}
+'''
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClassByHtml('org.gradle.Test').assertTestCount(2, 0, 0)
+            .assertTestPassed('dynamicTestsFromCollection()[1]', '1st dynamic test in test interface')
+            .assertTestPassed('dynamicTestsFromCollection()[2]', '2nd dynamic test in test interface')
+            .assertStdout(containsString('Invoked!'))
+    }
+
+    def 'can support parameterized tests'() {
+        given:
+        buildFile << """
+dependencies {
+    testImplementation 'org.junit.jupiter:junit-jupiter-params:${LATEST_JUPITER_VERSION}'
+}
+"""
+        file('src/test/java/org/gradle/Test.java') << '''
+package org.gradle;
+
+import org.junit.jupiter.params.*;
+import org.junit.jupiter.params.provider.*;
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class Test {
+    @ParameterizedTest
+    @ValueSource(strings = { "a", "b", "c" })
+    void ok(String s) {
+        assertTrue(s.length() == 1);
+    }
+}
+'''
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClassByHtml('org.gradle.Test').assertTestCount(3, 0, 0)
+            .assertTestPassed('ok(String)[1]', '[1] a')
+            .assertTestPassed('ok(String)[2]', '[2] b')
+            .assertTestPassed('ok(String)[3]', '[3] c')
+    }
+
+    def 'can use test template'() {
+        given:
+        file('src/test/java/org/gradle/TestTemplateTest.java') << '''
+package org.gradle;
+
+import org.junit.jupiter.api.*;
+import java.util.stream.*;
+import java.util.*;
+import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.extension.*;
+
+public class TestTemplateTest {
+    @TestTemplate
+    @ExtendWith(MyTestTemplateInvocationContextProvider.class)
+    void testTemplate(String parameter) {
+        assertEquals(3, parameter.length());
+    }
+
+    private static class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
+        @Override
+        public boolean supportsTestTemplate(ExtensionContext context) {
+            return true;
+        }
+
+        @Override
+        public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
+            return Stream.of(invocationContext("foo"), invocationContext("bar"));
+        }
+
+        private TestTemplateInvocationContext invocationContext(String parameter) {
+            return new TestTemplateInvocationContext() {
+                @Override
+                public String getDisplayName(int invocationIndex) {
+                    return parameter;
+                }
+
+                @Override
+                public List<Extension> getAdditionalExtensions() {
+                    return Collections.singletonList(new ParameterResolver() {
+                        @Override
+                        public boolean supportsParameter(ParameterContext parameterContext,
+                                ExtensionContext extensionContext) {
+                            return parameterContext.getParameter().getType().equals(String.class);
+                        }
+
+                        @Override
+                        public Object resolveParameter(ParameterContext parameterContext,
+                                ExtensionContext extensionContext) {
+                            return parameter;
+                        }
+                    });
+                }
+            };
+        }
+    }
+}
+'''
+        when:
+        succeeds('test')
+
+        then:
+        new DefaultTestExecutionResult(testDirectory)
+            .testClassByHtml('org.gradle.TestTemplateTest').assertTestCount(2, 0, 0)
+            .assertTestPassed('testTemplate(String)[1]', 'foo')
+            .assertTestPassed('testTemplate(String)[2]', 'bar')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/package-info.java b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/package-info.java
new file mode 100644
index 0000000..65ecfd6
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/platform/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * This package contains tests that should be geared towards general functionality of the
+ * JUnit Platform and may or may not be specific to an engine.
+ */
+package org.gradle.testing.junit.platform;
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageAbortedTestClassIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageAbortedTestClassIntegrationTest.groovy
new file mode 100644
index 0000000..86b1388
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageAbortedTestClassIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitAbortedTestClassIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.getJUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageAbortedTestClassIntegrationTest extends AbstractJUnitAbortedTestClassIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageAssumptionsIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageAssumptionsIntegrationTest.groovy
new file mode 100644
index 0000000..3b11083
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageAssumptionsIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitAssumptionsIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageAssumptionsIntegrationTest extends AbstractJUnitAssumptionsIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageCategoriesOrTagsCoverageIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageCategoriesOrTagsCoverageIntegrationTest.groovy
new file mode 100644
index 0000000..113898a
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageCategoriesOrTagsCoverageIntegrationTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4CategoriesOrTagsCoverageIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageCategoriesOrTagsCoverageIntegrationTest extends AbstractJUnit4CategoriesOrTagsCoverageIntegrationTest implements JUnitVintageMultiVersionTest {
+    String singularCategoryOrTagName = "tag"
+    String pluralCategoryOrTagName = "tags"
+
+    @Override
+    boolean supportsCategoryOnNestedClass() {
+        return true
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageClassDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..11b869f
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageClassDetectionIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitClassDetectionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({  JUNIT_VINTAGE })
+class JUnitVintageClassDetectionIntegrationTest extends AbstractJUnitClassDetectionIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageClassLevelFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageClassLevelFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..fa449ae
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageClassLevelFilteringIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4ClassLevelFilteringIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageClassLevelFilteringIntegrationTest extends AbstractJUnit4ClassLevelFilteringIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageConsoleLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageConsoleLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..a03ee6f
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageConsoleLoggingIntegrationTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitConsoleLoggingIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageConsoleLoggingIntegrationTest extends AbstractJUnitConsoleLoggingIntegrationTest implements JUnitVintageMultiVersionTest {
+    @Override
+    String getMaybePackagePrefix() {
+        return ''
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageEnclosedRunnerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageEnclosedRunnerIntegrationTest.groovy
new file mode 100644
index 0000000..554fb40
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageEnclosedRunnerIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitEnclosedRunnerIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageEnclosedRunnerIntegrationTest extends AbstractJUnitEnclosedRunnerIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageFailFastIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageFailFastIntegrationTest.groovy
new file mode 100644
index 0000000..90e43b1
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageFailFastIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractJvmFailFastIntegrationSpec
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_VINTAGE_VERSION
+
+@TargetCoverage({ [LATEST_VINTAGE_VERSION] })
+class JUnitVintageFailFastIntegrationTest extends AbstractJvmFailFastIntegrationSpec implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageFilteringIntegrationTest.groovy
new file mode 100644
index 0000000..461c15f4
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageFilteringIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4FilteringIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE})
+class JUnitVintageFilteringIntegrationTest extends AbstractJUnit4FilteringIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageIgnoreClassIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageIgnoreClassIntegrationTest.groovy
new file mode 100644
index 0000000..5300548
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageIgnoreClassIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4IgnoreClassIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.getJUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageIgnoreClassIntegrationTest extends AbstractJUnit4IgnoreClassIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnit3FilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnit3FilteringIntegrationTest.groovy
new file mode 100644
index 0000000..ab8e510
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnit3FilteringIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnit3FilteringIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageJUnit3FilteringIntegrationTest extends AbstractJUnit3FilteringIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnitIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnitIntegrationTest.groovy
new file mode 100644
index 0000000..182f90e
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnitIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4JUnitIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageJUnitIntegrationTest extends AbstractJUnit4JUnitIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnitTestFailureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnitTestFailureIntegrationTest.groovy
new file mode 100644
index 0000000..c9fa88c
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJUnitTestFailureIntegrationTest.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4TestFailureIntegrationTest
+import org.hamcrest.Matcher
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+import static org.hamcrest.CoreMatchers.allOf
+import static org.hamcrest.CoreMatchers.containsString
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageJUnitTestFailureIntegrationTest extends AbstractJUnit4TestFailureIntegrationTest implements JUnitVintageMultiVersionTest {
+    @Override
+    String getInitializationErrorTestName() {
+        return 'failed to execute tests'
+    }
+
+    @Override
+    String getBeforeClassErrorTestName() {
+        return 'initializationError'
+    }
+
+    @Override
+    String getAfterClassErrorTestName() {
+        return 'executionError'
+    }
+
+    @Override
+    Matcher<? super String>[] getBrokenBeforeAndAfterMatchers() {
+        return [allOf(containsString(failureAssertionError('before failed')), containsString(failureAssertionError('after failed')))]
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJdkNavigationIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJdkNavigationIntegrationTest.groovy
new file mode 100644
index 0000000..e70e5c6
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJdkNavigationIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitJdkNavigationIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageJdkNavigationIntegrationTest extends AbstractJUnitJdkNavigationIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJnaIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJnaIntegrationTest.groovy
new file mode 100644
index 0000000..cc3caaa
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageJnaIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractJUnitJnaIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageJnaIntegrationTest extends AbstractJUnitJnaIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageLoggingOutputCaptureIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageLoggingOutputCaptureIntegrationTest.groovy
new file mode 100644
index 0000000..d3a2a9e
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageLoggingOutputCaptureIntegrationTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4LoggingOutputCaptureIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+// https://github.com/junit-team/junit5/issues/1285
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageLoggingOutputCaptureIntegrationTest extends AbstractJUnit4LoggingOutputCaptureIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageMultiVersionTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageMultiVersionTest.groovy
new file mode 100644
index 0000000..abe3580
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageMultiVersionTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+import org.gradle.testing.junit.junit4.JUnit4CommonTestSources
+
+import static org.gradle.testing.fixture.JUnitCoverage.*
+
+trait JUnitVintageMultiVersionTest extends JUnit4CommonTestSources {
+    AbstractTestingMultiVersionIntegrationTest.BuildScriptConfiguration getBuildScriptConfiguration() {
+        return new JUnitVintageBuildScriptConfiguration()
+    }
+
+    AbstractTestingMultiVersionIntegrationTest.TestSourceConfiguration getTestSourceConfiguration() {
+        new JUnit4TestSourceConfiguration(MultiVersionIntegrationSpec.version)
+    }
+
+    static class JUnitVintageBuildScriptConfiguration implements AbstractTestingMultiVersionIntegrationTest.BuildScriptConfiguration {
+        String configureTestFramework = "useJUnitPlatform()"
+
+        @Override
+        String getTestFrameworkDependencies(String sourceSet) {
+            return """
+                ${configurationFor(sourceSet, 'compileOnly')} 'junit:junit:${LATEST_JUNIT4_VERSION}'
+                ${configurationFor(sourceSet, 'runtimeOnly')} 'org.junit.vintage:junit-vintage-engine:${MultiVersionIntegrationSpec.version}'
+                ${configurationFor(sourceSet, 'runtimeOnly')} 'org.junit.platform:junit-platform-launcher'
+            """
+        }
+
+        @Override
+        String getIncludeCategoryOrTagConfigurationElement() {
+            return "includeTags"
+        }
+
+        @Override
+        String getExcludeCategoryOrTagConfigurationElement() {
+            return "excludeTags"
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSmokeMultiVersionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSmokeMultiVersionIntegrationTest.groovy
new file mode 100644
index 0000000..8e5f2c7
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSmokeMultiVersionIntegrationTest.groovy
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.testing.junit.AbstractJUnitSmokeMultiVersionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageSmokeMultiVersionIntegrationTest extends AbstractJUnitSmokeMultiVersionIntegrationTest implements JUnitVintageMultiVersionTest {
+    @Override
+    void assertTestSkippedOrPassed(TestClassExecutionResult testClassResult, String testName) {
+        testClassResult.assertTestSkipped(testName)
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSpecs2IntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSpecs2IntegrationTest.groovy
new file mode 100644
index 0000000..61d3574
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSpecs2IntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.AbstractSpecs2IntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageSpecs2IntegrationTest extends AbstractSpecs2IntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSuitesIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSuitesIntegrationTest.groovy
new file mode 100644
index 0000000..04fd350
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageSuitesIntegrationTest.groovy
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4SuitesIntegrationTest
+import org.gradle.util.internal.VersionNumber
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUNIT3_VERSION
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageSuitesIntegrationTest extends AbstractJUnit4SuitesIntegrationTest implements JUnitVintageMultiVersionTest {
+    @Override
+    boolean supportsSuiteOutput() {
+        // Suite output events are not correctly reported until version 5.9.0.  See https://github.com/junit-team/junit5/pull/2985.
+        return VersionNumber.parse(version) >= VersionNumber.parse("5.9.0")
+    }
+
+    @Override
+    String getTestFrameworkJUnit3Dependencies() {
+        return """
+            testCompileOnly 'junit:junit:${LATEST_JUNIT3_VERSION}'
+            testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:${version}'
+            testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestClassDetectionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestClassDetectionIntegrationTest.groovy
new file mode 100644
index 0000000..c2a3b11
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestClassDetectionIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4TestClassDetectionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestClassDetectionIntegrationTest extends AbstractJUnit4TestClassDetectionIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestEnvironmentIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestEnvironmentIntegrationTest.groovy
new file mode 100644
index 0000000..e108d02
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestEnvironmentIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4TestEnvironmentIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestEnvironmentIntegrationTest extends AbstractJUnit4TestEnvironmentIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestExecutionIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestExecutionIntegrationTest.groovy
new file mode 100644
index 0000000..719f19e
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestExecutionIntegrationTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.integtests.fixtures.TestClassExecutionResult
+import org.gradle.integtests.fixtures.TestExecutionResult
+import org.gradle.testing.junit.AbstractJUnitTestExecutionIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUNIT4_VERSION
+import static org.hamcrest.CoreMatchers.containsString
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestExecutionIntegrationTest extends AbstractJUnitTestExecutionIntegrationTest implements JUnitVintageMultiVersionTest {
+    @Override
+    String getJUnitVersionAssertion() {
+        return "assertEquals(\"${LATEST_JUNIT4_VERSION}\", new org.junit.runner.JUnitCore().getVersion());"
+    }
+
+    @Override
+    TestClassExecutionResult assertFailedToExecute(TestExecutionResult testResult, String testClassName) {
+        return testResult.testClassStartsWith('Gradle Test Executor')
+            .assertTestFailed("failed to execute tests", containsString("Could not execute test class '${testClassName}'"))
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestListenerBuildOperationAdapterIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestListenerBuildOperationAdapterIntegrationTest.groovy
new file mode 100644
index 0000000..bbebfdc
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestListenerBuildOperationAdapterIntegrationTest.groovy
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4TestListenerBuildOperationAdapterIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestListenerBuildOperationAdapterIntegrationTest extends AbstractJUnit4TestListenerBuildOperationAdapterIntegrationTest implements JUnitVintageMultiVersionTest {
+    boolean emitsTestClassOperations = true
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestListenerIntegrationTest.groovy
new file mode 100644
index 0000000..b38720c
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestListenerIntegrationTest.groovy
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4TestListenerIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+import static org.gradle.testing.fixture.JUnitCoverage.getLATEST_JUNIT3_VERSION
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestListenerIntegrationTest extends AbstractJUnit4TestListenerIntegrationTest implements JUnitVintageMultiVersionTest {
+    @Override
+    String getTestFrameworkJUnit3Dependencies() {
+        return """
+            testCompileOnly 'junit:junit:${LATEST_JUNIT3_VERSION}'
+            testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:${version}'
+            testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+        """
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestOutputListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestOutputListenerIntegrationTest.groovy
new file mode 100644
index 0000000..fd221ab
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestOutputListenerIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestOutputListenerIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestOutputListenerIntegrationTest extends AbstractTestOutputListenerIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestProgressLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestProgressLoggingIntegrationTest.groovy
new file mode 100644
index 0000000..36fd9b0
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestProgressLoggingIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractTestProgressLoggingIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestProgressLoggingIntegrationTest extends AbstractTestProgressLoggingIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestReportIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestReportIntegrationTest.groovy
new file mode 100644
index 0000000..55098ef
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestReportIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4TestReportIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.JUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestReportIntegrationTest extends AbstractJUnit4TestReportIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestTaskIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestTaskIntegrationTest.groovy
new file mode 100644
index 0000000..43abd32
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/JUnitVintageTestTaskIntegrationTest.groovy
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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.testing.junit.vintage
+
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.junit.junit4.AbstractJUnit4TestTaskIntegrationTest
+
+import static org.gradle.testing.fixture.JUnitCoverage.getJUNIT_VINTAGE
+
+@TargetCoverage({ JUNIT_VINTAGE })
+class JUnitVintageTestTaskIntegrationTest extends AbstractJUnit4TestTaskIntegrationTest implements JUnitVintageMultiVersionTest {
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/package-info.java b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/package-info.java
new file mode 100644
index 0000000..a09dae0
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junit/vintage/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 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.
+ */
+
+/**
+ * This package contains tests that should be specific to the JUnit Vintage engine.  Many classes
+ * may extend from abstract classes in the junit4 package.
+ */
+package org.gradle.testing.junit.vintage;
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformFailFastIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformFailFastIntegrationTest.groovy
deleted file mode 100644
index 6016b4f..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformFailFastIntegrationTest.groovy
+++ /dev/null
@@ -1,38 +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.testing.junitplatform
-
-import org.gradle.testing.fixture.AbstractJvmFailFastIntegrationSpec
-
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
-
-class JUnitPlatformFailFastIntegrationTest extends AbstractJvmFailFastIntegrationSpec {
-    @Override
-    String testAnnotationClass() {
-        'org.junit.jupiter.api.Test'
-    }
-
-    @Override
-    String testDependency() {
-        "org.junit.jupiter:junit-jupiter:$LATEST_JUPITER_VERSION"
-    }
-
-    @Override
-    String testFrameworkConfiguration() {
-        'test { useJUnitPlatform() }'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformFilteringIntegrationTest.groovy
deleted file mode 100644
index d3e6f3a..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformFilteringIntegrationTest.groovy
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright 2020 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.testing.junitplatform
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_ARCHUNIT_VERSION
-
-class JUnitPlatformFilteringIntegrationTest extends JUnitPlatformIntegrationSpec {
-
-    def 'can filter nested tests'() {
-        given:
-        file('src/test/java/org/gradle/NestedTest.java') << '''
-            package org.gradle;
-            import static org.junit.jupiter.api.Assertions.*;
-
-            import java.util.EmptyStackException;
-            import java.util.Stack;
-
-            import org.junit.jupiter.api.*;
-
-            class NestedTest {
-                @Test
-                void outerTest() {
-                }
-
-                @Nested
-                class Inner {
-                    @Test
-                    void innerTest() {
-                    }
-                }
-            }
-        '''
-        buildFile << '''
-            test {
-                filter {
-                    includeTestsMatching "*innerTest*"
-                }
-            }
-        '''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted('org.gradle.NestedTest$Inner')
-            .testClass('org.gradle.NestedTest$Inner').assertTestCount(1, 0, 0)
-            .assertTestPassed('innerTest()')
-    }
-
-    def 'can use nested class as test pattern'() {
-        given:
-        file('src/test/java/EnclosingClass.java') << '''
-            import org.junit.jupiter.api.Test;
-            import org.junit.jupiter.api.Nested;
-            import static org.junit.jupiter.api.Assertions.assertEquals;
-
-            class EnclosingClass {
-                @Nested
-                class NestedClass {
-                    @Test
-                    void nestedTest() {
-                    }
-                    @Test
-                    void anotherTest() {
-                    }
-                }
-                @Nested
-                class AnotherNestedClass {
-                    @Test
-                    void foo() {
-                    }
-                }
-                @Test
-                void foo() {
-                }
-            }
-        '''
-        when:
-        succeeds('test', '--tests', 'EnclosingClass$NestedClass.nestedTest')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted('EnclosingClass$NestedClass')
-            .testClass('EnclosingClass$NestedClass')
-            .assertTestCount(1, 0, 0)
-            .assertTestPassed('nestedTest')
-    }
-
-    def 'can filter tests from a superclass'() {
-        given:
-        file('src/test/java/SuperClass.java') << '''
-            import org.junit.jupiter.api.Test;
-
-            abstract class SuperClass {
-                @Test
-                void superTest() {
-                }
-            }
-        '''
-        file('src/test/java/SubClass.java') << '''
-            import org.junit.jupiter.api.Test;
-
-            class SubClass extends SuperClass {
-                @Test
-                void subTest() {
-                }
-            }
-        '''
-
-        when:
-        succeeds('test', '--tests', 'SubClass.superTest')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted('SubClass')
-            .testClass('SubClass')
-            .assertTestCount(1, 0, 0)
-            .assertTestPassed('superTest')
-    }
-
-    /**
-     * This test documents the status quo behavior of the test runner, where tests based on fields
-     * are not filtered by exclude patterns.  It might be desirable to change this behavior in the
-     * future to filter on field names directly; if this is done, this test should be replaced.
-     */
-    @Issue("https://github.com/gradle/gradle/issues/19352")
-    def 'does not exclude tests with a non-standard test source if filter matches nothing'() {
-        given:
-        buildFile << """
-            dependencies {
-                testImplementation 'com.tngtech.archunit:archunit-junit5:${LATEST_ARCHUNIT_VERSION}'
-            }
-
-            test {
-                filter {
-                    excludeTestsMatching "*notMatchingAnythingSoEverythingShouldBeRun"
-                }
-            }
-        """
-        file('src/test/java/DeclaresTestsAsFieldsNotMethodsTest.java') << '''
-            import com.tngtech.archunit.junit.AnalyzeClasses;
-            import com.tngtech.archunit.junit.ArchTest;
-            import com.tngtech.archunit.lang.ArchRule;
-
-            import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
-
-            @AnalyzeClasses(packages = "example")
-            class DeclaresTestsAsFieldsNotMethodsTest {
-                // this will create a JUnit Platform TestDescriptor with neither a Class- nor a MethodSource
-                @ArchTest
-                static final ArchRule example = classes().should().bePublic();
-            }
-        '''
-
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted('DeclaresTestsAsFieldsNotMethodsTest')
-            .testClass('DeclaresTestsAsFieldsNotMethodsTest')
-            .assertTestCount(1, 0, 0)
-            .assertTestPassed('example')
-    }
-
-    /**
-     * This test documents the status quo behavior of the test runner, where tests based on fields
-     * are not filtered by exclude patterns.  It might be desirable to change this behavior in the
-     * future to filter on field names directly; if this is done, this test should be replaced.
-     */
-    @Issue("https://github.com/gradle/gradle/issues/19352")
-    def 'does not exclude tests with a non-standard test source if filter matches field name'() {
-        given:
-        buildFile << """
-            dependencies {
-                testImplementation 'com.tngtech.archunit:archunit-junit5:${LATEST_ARCHUNIT_VERSION}'
-            }
-
-            test {
-                filter {
-                    excludeTestsMatching "*example"
-                }
-            }
-        """
-        file('src/test/java/DeclaresTestsAsFieldsNotMethodsTest.java') << '''
-            import com.tngtech.archunit.junit.AnalyzeClasses;
-            import com.tngtech.archunit.junit.ArchTest;
-            import com.tngtech.archunit.lang.ArchRule;
-
-            import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
-
-            @AnalyzeClasses(packages = "example")
-            class DeclaresTestsAsFieldsNotMethodsTest {
-                // this will create a JUnit Platform TestDescriptor with neither a Class- nor a MethodSource
-                @ArchTest
-                static final ArchRule example = classes().should().bePublic();
-            }
-        '''
-
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted('DeclaresTestsAsFieldsNotMethodsTest')
-            .testClass('DeclaresTestsAsFieldsNotMethodsTest')
-            .assertTestCount(1, 0, 0)
-            .assertTestPassed('example')
-    }
-
-    /**
-     * This test demonstrates the workaround for the inabilty to filter fields - we can
-     * filter based on containing class name.
-     */
-    @Issue("https://github.com/gradle/gradle/issues/19352")
-    def 'can filter tests with a non-standard test source using containing class name'() {
-        given:
-        buildFile << """
-            dependencies {
-                testImplementation 'com.tngtech.archunit:archunit-junit5:${LATEST_ARCHUNIT_VERSION}'
-            }
-
-            test {
-                filter {
-                    excludeTestsMatching "*DeclaresTestsAsFieldsNotMethodsTest"
-                }
-            }
-        """
-        file('src/test/java/DeclaresTestsAsFieldsNotMethodsTest.java') << '''
-            import com.tngtech.archunit.junit.AnalyzeClasses;
-            import com.tngtech.archunit.junit.ArchTest;
-            import com.tngtech.archunit.lang.ArchRule;
-
-            import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
-
-            @AnalyzeClasses(packages = "example")
-            class DeclaresTestsAsFieldsNotMethodsTest {
-                // this will create a JUnit Platform TestDescriptor with neither a Class- nor a MethodSource
-                @ArchTest
-                static final ArchRule example = classes().should().bePublic();
-            }
-        '''
-
-        expect:
-        fails('test')
-        errorOutput.contains("No tests found for given includes")
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformIntegrationSpec.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformIntegrationSpec.groovy
deleted file mode 100644
index b0033b7..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformIntegrationSpec.groovy
+++ /dev/null
@@ -1,57 +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.testing.junitplatform
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
-
-class JUnitPlatformIntegrationSpec extends AbstractIntegrationSpec {
-    def setup() {
-        executer.noExtraLogging()
-        buildScriptWithJupiterDependencies("""
-            test {
-                useJUnitPlatform()
-            }
-        """)
-    }
-
-    def buildScriptWithJupiterDependencies(script) {
-        buildScript("""
-            apply plugin: 'java'
-
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:${LATEST_JUPITER_VERSION}'
-            }
-            $script
-        """)
-    }
-
-    void createSimpleJupiterTest() {
-        file('src/test/java/org/gradle/JUnitJupiterTest.java') << '''
-            package org.gradle;
-
-            import org.junit.jupiter.api.Test;
-
-            public class JUnitJupiterTest {
-                @Test
-                public void ok() { }
-            }
-            '''
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformIntegrationTest.groovy
deleted file mode 100644
index 9bb19e8..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformIntegrationTest.groovy
+++ /dev/null
@@ -1,400 +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.testing.junitplatform
-
-import org.gradle.api.internal.tasks.testing.junit.JUnitSupport
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import spock.lang.Issue
-import spock.lang.Timeout
-
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_PLATFORM_VERSION
-import static org.hamcrest.CoreMatchers.containsString
-
-class JUnitPlatformIntegrationTest extends JUnitPlatformIntegrationSpec {
-
-    def 'can work with junit-platform-runner'() {
-        given:
-        buildFile << """
-        dependencies {
-            testImplementation 'org.junit.platform:junit-platform-runner:1.0.3'
-        }
-        """
-        createSimpleJupiterTest()
-
-        expect:
-        succeeds('test')
-    }
-
-    def 'should prompt user to add dependencies when they are not in test runtime classpath'() {
-        given:
-        buildFile.text = """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies {
-                testCompileOnly 'org.junit.jupiter:junit-jupiter:${LATEST_JUPITER_VERSION}'
-            }
-
-            test { useJUnitPlatform() }
-            """
-        createSimpleJupiterTest()
-
-        when:
-        fails('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClassStartsWith('Gradle Test Executor')
-            .assertExecutionFailedWithCause(containsString('consider adding an engine implementation JAR to the classpath'))
-    }
-
-    def 'can handle class level ignored tests'() {
-        given:
-        file('src/test/java/org/gradle/IgnoredTest.java') << '''
-            package org.gradle;
-
-            import org.junit.jupiter.api.*;
-
-            @Disabled
-            public class IgnoredTest {
-                @Test
-                public void testIgnored1() {
-                    throw new RuntimeException();
-                }
-            }
-        '''
-
-        when:
-        run('check')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted('org.gradle.IgnoredTest')
-            .testClass('org.gradle.IgnoredTest').assertTestCount(1, 0, 0).assertTestsSkipped("testIgnored1()")
-    }
-
-    def 'can handle class-level error in #location method'() {
-        given:
-        file('src/test/java/org/gradle/ClassErrorTest.java') << """
-            package org.gradle;
-
-            import org.junit.jupiter.api.*;
-            import static org.junit.jupiter.api.Assertions.*;
-
-            public class ClassErrorTest {
-                @Test
-                public void ok() {
-                }
-
-                @BeforeAll
-                public static void before() {
-                    $beforeStatement;
-                }
-
-                @AfterAll
-                public static void after() {
-                    $afterStatement;
-                }
-            }
-        """
-
-        when:
-        fails('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecuted('org.gradle.ClassErrorTest')
-            .testClass('org.gradle.ClassErrorTest')
-            .assertTestCount(successCount + 1, 1, 0)
-            .assertTestFailed(failedTestName, containsString(location))
-
-        where:
-        location     | beforeStatement      | afterStatement      | successCount | failedTestName
-        '@BeforeAll' | 'fail("@BeforeAll")' | ''                  | 0            | "initializationError"
-        '@AfterAll'  | ''                   | 'fail("@AfterAll")' | 1            | "executionError"
-    }
-
-    def 'can handle class level assumption'() {
-        given:
-        file('src/test/java/org/gradle/ClassAssumeTest.java') << '''
-        package org.gradle;
-
-        import org.junit.jupiter.api.*;
-        import org.junit.jupiter.api.Assumptions;
-
-        public class ClassAssumeTest {
-            @Test
-            public void ok() {
-            }
-
-            @BeforeAll
-            public static void before() {
-                Assumptions.assumeTrue(false);
-            }
-        }
-        '''
-
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory).testClass('org.gradle.ClassAssumeTest').assertTestCount(1, 0, 0)
-    }
-
-    def 'can handle repeated tests'() {
-        given:
-        file('src/test/java/org/gradle/RepeatTest.java') << '''
-        package org.gradle;
-
-        import org.junit.jupiter.api.*;
-        import org.junit.jupiter.api.Assumptions;
-
-        public class RepeatTest {
-            @RepeatedTest(value = 3, name = "ok {currentRepetition}/{totalRepetitions}")
-            public void ok() {
-            }
-
-            @RepeatedTest(value = 3, name = "partialFail {currentRepetition}/{totalRepetitions}")
-            public void partialFail(RepetitionInfo repetitionInfo) {
-                if (repetitionInfo.getCurrentRepetition() == 2) {
-                    throw new RuntimeException();
-                }
-            }
-
-            @RepeatedTest(value = 3, name = "partialSkip {currentRepetition}/{totalRepetitions}")
-            public void partialSkip(RepetitionInfo repetitionInfo) {
-                if (repetitionInfo.getCurrentRepetition() == 2) {
-                    Assumptions.assumeTrue(false);
-                }
-            }
-        }
-        '''
-
-        when:
-        fails('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .assertTestClassesExecutedJudgementByHtml('org.gradle.RepeatTest')
-            .testClassByHtml('org.gradle.RepeatTest')
-            .assertTestCount(9, 1, 0)
-            .assertTestPassed('ok()[1]', 'ok 1/3')
-            .assertTestPassed('ok()[2]', 'ok 2/3')
-            .assertTestPassed('ok()[3]', 'ok 3/3')
-            .assertTestPassed('partialFail(RepetitionInfo)[1]', 'partialFail 1/3')
-            .assertTestFailed('partialFail(RepetitionInfo)[2]', 'partialFail 2/3', containsString('java.lang.RuntimeException'))
-            .assertTestPassed('partialFail(RepetitionInfo)[3]', 'partialFail 3/3')
-            .assertTestPassed('partialSkip(RepetitionInfo)[1]', 'partialSkip 1/3')
-            .assertTestSkipped('partialSkip(RepetitionInfo)[2]', 'partialSkip 2/3')
-            .assertTestPassed('partialSkip(RepetitionInfo)[3]', 'partialSkip 3/3')
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/4476')
-    def 'can handle test engine failure'() {
-        given:
-        createSimpleJupiterTest()
-        file('src/test/java/UninstantiableExtension.java') << '''
-import org.junit.jupiter.api.extension.*;
-public class UninstantiableExtension implements BeforeEachCallback {
-  private UninstantiableExtension(){}
-
-  @Override
-  public void beforeEach(final ExtensionContext context) throws Exception {
-  }
-}
-'''
-        file('src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension') << 'UninstantiableExtension'
-        buildFile << '''
-            test {
-                systemProperty('junit.jupiter.extensions.autodetection.enabled', 'true')
-            }
-        '''
-
-        when:
-        fails('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClass(JUnitSupport.UNKNOWN_CLASS)
-            .assertTestFailed('initializationError', containsString('UninstantiableExtension'))
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/4427')
-    def 'can run tests in static nested class'() {
-        given:
-        file('src/test/java/org/gradle/StaticInnerTest.java') << '''
-package org.gradle;
-import org.junit.jupiter.api.*;
-public class StaticInnerTest {
-    public static class Nested {
-        @Test
-        public void inside() {
-        }
-
-        public static class Nested2 {
-            @Test
-            public void inside() {
-            }
-        }
-    }
-
-    @Test
-    public void outside() {
-    }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted('org.gradle.StaticInnerTest', 'org.gradle.StaticInnerTest$Nested', 'org.gradle.StaticInnerTest$Nested$Nested2')
-        result.testClass('org.gradle.StaticInnerTest').assertTestCount(1, 0, 0)
-            .assertTestPassed('outside')
-        result.testClass('org.gradle.StaticInnerTest$Nested').assertTestCount(1, 0, 0)
-            .assertTestPassed('inside')
-        result.testClass('org.gradle.StaticInnerTest$Nested$Nested2').assertTestCount(1, 0, 0)
-            .assertTestPassed('inside')
-    }
-
-    @Issue('https://github.com/gradle/gradle/issues/4924')
-    def "re-executes test when #key is changed"() {
-        given:
-        buildScriptWithJupiterDependencies("""
-            test {
-                useJUnitPlatform {
-                    ${key} ${value}
-                }
-            }
-        """)
-        createSimpleJupiterTest()
-
-        when:
-        succeeds ':test'
-
-        then:
-        executedAndNotSkipped ':test'
-
-        when:
-        buildScriptWithJupiterDependencies("""
-            test {
-                useJUnitPlatform()
-            }
-        """)
-
-        and:
-        succeeds ':test'
-
-        then:
-        executedAndNotSkipped ':test'
-
-        where:
-        key              | value
-        'includeTags'    | '"ok"'
-        'excludeTags'    | '"ok"'
-        'includeEngines' | '"junit-jupiter"'
-        'excludeEngines' | '"junit-jupiter"'
-    }
-
-    @Timeout(60)
-    @Issue('https://github.com/gradle/gradle/issues/6453')
-    def "can handle parallel test execution"() {
-        given:
-        def numTestClasses = 32
-        buildScriptWithJupiterDependencies("""
-            test {
-                useJUnitPlatform()
-                systemProperty('junit.jupiter.execution.parallel.enabled', 'true')
-                systemProperty('junit.jupiter.execution.parallel.config.strategy', 'fixed')
-                systemProperty('junit.jupiter.execution.parallel.config.fixed.parallelism', '$numTestClasses')
-            }
-        """)
-        file('src/test/java/org/gradle/Tests.java') << """
-            package org.gradle;
-
-            import java.util.concurrent.*;
-            import org.junit.jupiter.api.*;
-            import org.junit.jupiter.api.parallel.*;
-            import static org.junit.jupiter.api.Assertions.*;
-            import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;
-
-            @Execution(CONCURRENT)
-            class Sync {
-                static CountDownLatch LATCH = new CountDownLatch($numTestClasses);
-            }
-
-            ${(1..numTestClasses).collect { classNumber -> """
-                class Test$classNumber extends Sync {
-                    @Test
-                    public void test() throws Exception {
-                        LATCH.countDown();
-                        LATCH.await();
-                    }
-                }
-            """ }.join("") }
-        """
-
-        when:
-        succeeds(':test')
-
-        then:
-        with(new DefaultTestExecutionResult(testDirectory)) {
-            (1..numTestClasses).every { classNumber ->
-                testClass("org.gradle.Test$classNumber").assertTestCount(1, 0, 0)
-            }
-        }
-    }
-
-    @Issue("https://github.com/junit-team/junit5/issues/2028 and https://github.com/gradle/gradle/issues/12073")
-    def 'properly fails when engine fails during discovery #scenario'() {
-        given:
-        createSimpleJupiterTest()
-        buildFile << """
-            dependencies {
-                testImplementation 'org.junit.platform:junit-platform-engine:${LATEST_PLATFORM_VERSION}'
-            }
-        """
-        file('src/test/java/EngineFailingDiscovery.java') << '''
-            import org.junit.platform.engine.*;
-            public class EngineFailingDiscovery implements TestEngine {
-                @Override
-                public String getId() {
-                    return "EngineFailingDiscovery";
-                }
-
-                @Override
-                public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {
-                    throw new RuntimeException("oops");
-                }
-
-                @Override
-                public void execute(ExecutionRequest request) {
-                }
-            }
-        '''
-        file('src/test/resources/META-INF/services/org.junit.platform.engine.TestEngine') << 'EngineFailingDiscovery'
-
-        expect:
-        fails('test', *extraArgs)
-        failureCauseContains('There were failing tests.')
-
-        where:
-        scenario       | extraArgs
-        "w/o filters"  | []
-        "with filters" | ['--tests', 'JUnitJupiterTest']
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformLauncherSessionListenerIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformLauncherSessionListenerIntegrationTest.groovy
deleted file mode 100644
index 375c16a..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformLauncherSessionListenerIntegrationTest.groovy
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 2022 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.testing.junitplatform
-
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_PLATFORM_VERSION
-
-/**
- * Tests JUnitPlatform integrations with {@code LauncherSessionListener}.
- */
-class JUnitPlatformLauncherSessionListenerIntegrationTest extends JUnitPlatformIntegrationSpec {
-
-    /**
-     * @see <a href=https://github.com/JetBrains/intellij-community/commit/d41841670c8a98c0464ef25ef490c79b5bafe8a9">The IntelliJ commit</a>
-     * which introduced a {@code LauncherSessionListener} onto the test classpath when using the {@code org.jetbrains.intellij} plugin.
-     */
-    @Issue("https://github.com/gradle/gradle/issues/22333")
-    def "LauncherSessionListeners are automatically loaded from the test classpath when listener does not provide junit platform launcher dependency"() {
-        settingsFile << "include 'other'"
-        file("other/build.gradle") << """
-            plugins {
-                id 'java'
-            }
-
-            ${mavenCentralRepository()}
-
-            dependencies {
-                compileOnly 'org.junit.platform:junit-platform-launcher:1.9.1'
-            }
-        """
-        file("other/src/main/java/com/example/MyLauncherSessionListener.java") << """
-            package com.example;
-            import org.junit.platform.launcher.LauncherSession;
-            import org.junit.platform.launcher.LauncherSessionListener;
-            public class MyLauncherSessionListener implements LauncherSessionListener {
-                public void launcherSessionOpened(LauncherSession session) {
-                    System.out.println("Session opened");
-                }
-                public void launcherSessionClosed(LauncherSession session) {
-                    System.out.println("Session closed");
-                }
-            }
-        """
-        file("other/src/main/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener") << """
-            com.example.MyLauncherSessionListener
-        """
-
-        buildFile.text = """
-            plugins {
-                id 'java'
-            }
-
-            ${mavenCentralRepository()}
-
-            dependencies {
-                testImplementation project(':other')
-                testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1'
-            }
-
-            test {
-                useJUnitPlatform()
-                testLogging.showStandardStreams = true
-            }
-        """
-        file("src/test/java/com/example/MyTest.java") << "package com.example; public class MyTest {} "
-
-        when:
-        succeeds "test"
-
-        then:
-        outputContains("Session opened")
-        outputContains("Session closed")
-
-        when:
-        succeeds "dependencies", "--configuration", "testRuntimeClasspath"
-
-        then:
-        // Sanity check in case future versions for some reason include a launcher
-        outputDoesNotContain("junit-platform-launcher")
-    }
-
-    def "creates LauncherSession before loading test classes"() {
-        given:
-        createSimpleJupiterTest()
-        buildFile << """
-            dependencies {
-                testImplementation 'org.junit.platform:junit-platform-launcher:${LATEST_PLATFORM_VERSION}'
-            }
-            test {
-                testLogging {
-                    showStandardStreams = true
-                }
-            }
-        """
-        file('src/test/java/NoisyLauncherSessionListener.java') << '''
-            import org.junit.platform.launcher.*;
-            public class NoisyLauncherSessionListener implements LauncherSessionListener {
-                @Override public void launcherSessionOpened(LauncherSession session) {
-                    System.out.println("launcherSessionOpened");
-                    Thread thread = Thread.currentThread();
-                    ClassLoader parent = thread.getContextClassLoader();
-                    ClassLoader replacement = new ClassLoader(parent) {
-                        @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
-                            System.out.println("Loading class " + name);
-                            return super.loadClass(name, resolve);
-                        }
-                    };
-                    thread.setContextClassLoader(replacement);
-                }
-                @Override public void launcherSessionClosed(LauncherSession session) {
-                    System.out.println("launcherSessionClosed");
-                }
-            }
-        '''
-        file('src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener') << '''\
-            NoisyLauncherSessionListener
-        '''.stripIndent(true)
-
-        expect:
-        succeeds('test')
-        outputContains('launcherSessionOpened')
-        outputContains('Loading class org.gradle.JUnitJupiterTest')
-        outputContains('launcherSessionClosed')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformLoggingIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformLoggingIntegrationTest.groovy
deleted file mode 100644
index b88bccf..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformLoggingIntegrationTest.groovy
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2019 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.testing.junitplatform
-
-import spock.lang.Issue
-
-class JUnitPlatformLoggingIntegrationTest extends JUnitPlatformIntegrationSpec {
-
-    @Override
-    def setup() {
-        buildFile << """
-            test {
-                testLogging {
-                    events "passed", "skipped", "failed"
-                }
-            }
-        """
-    }
-
-    def "should log display names if present"() {
-        given:
-        file("src/test/java/pkg/TopLevelClass.java") << """
-            package pkg;
-            import org.junit.jupiter.api.DisplayName;
-            import org.junit.jupiter.api.Nested;
-            import org.junit.jupiter.api.Test;
-
-            @DisplayName("Class level display name")
-            public class TopLevelClass {
-
-                @Nested
-                @DisplayName("Nested class display name")
-                public class NestedClass {
-
-                    @Test
-                    @DisplayName("Nested test method display name")
-                    public void nestedTestMethod() {
-                    }
-                }
-
-                @Test
-                @DisplayName("Method display name")
-                public void testMethod() {
-                }
-            }
-         """
-
-        when:
-        run("test")
-
-        then:
-        outputContains("Class level display name > Method display name")
-        outputContains("Class level display name > Nested class display name > Nested test method display name")
-    }
-
-    def "should fall back to plain name if no display names present"() {
-        given:
-        file("src/test/java/pkg/TopLevelClass.java") << """
-            package pkg;
-
-            import org.junit.jupiter.api.DisplayName;
-            import org.junit.jupiter.api.Nested;
-            import org.junit.jupiter.api.Test;
-
-            public class TopLevelClass {
-
-                @Nested
-                public class NestedClass {
-
-                    @Test
-                    public void nestedTestMethod() {
-                    }
-                }
-
-                @Test
-                public void testMethod() {
-                }
-            }
-         """
-
-        when:
-        run("test")
-
-        then:
-        outputContains("TopLevelClass > testMethod()")
-        outputContains("TopLevelClass > NestedClass > nestedTestMethod()")
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/5975")
-    def "should log display names for dynamically created tests"() {
-        given:
-        file("src/test/java/org/gradle/JUnitJupiterDynamicTest.java") << """
-            package org.gradle;
-            import org.junit.jupiter.api.DynamicTest;
-            import org.junit.jupiter.api.TestFactory;
-            import java.util.stream.IntStream;
-            import java.util.stream.Stream;
-            import static org.junit.jupiter.api.Assertions.*;
-            import static org.junit.jupiter.api.DynamicTest.dynamicTest;
-            public class JUnitJupiterDynamicTest {
-                @TestFactory
-                Stream<DynamicTest> streamOfTests() {
-                    return IntStream.of(2, 4, 5)
-                        .mapToObj(v -> dynamicTest(v + " is even", () -> assertEquals(0, v % 2)));
-                }
-            }
-        """
-
-        when:
-        runAndFail("test")
-
-        then:
-        def parentEventPath = "JUnitJupiterDynamicTest > streamOfTests()"
-        outputContains("${parentEventPath} > 2 is even PASSED")
-        outputContains("${parentEventPath} > 4 is even PASSED")
-        outputContains("${parentEventPath} > 5 is even FAILED")
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformOnJdk7IntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformOnJdk7IntegrationTest.groovy
deleted file mode 100644
index 4a3499a..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformOnJdk7IntegrationTest.groovy
+++ /dev/null
@@ -1,65 +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.testing.junitplatform
-
-import org.gradle.integtests.fixtures.AbstractIntegrationSpec
-import org.gradle.integtests.fixtures.AvailableJavaHomes
-
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
-import static org.junit.Assume.assumeNotNull
-
-class JUnitPlatformOnJdk7IntegrationTest extends AbstractIntegrationSpec {
-
-    def setup() {
-        def jdk7 = AvailableJavaHomes.getJdk7()
-        assumeNotNull(jdk7)
-        file("gradle.properties").writeProperties("org.gradle.java.installations.paths": jdk7.javaHome.canonicalPath)
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:${LATEST_JUPITER_VERSION}'
-            }
-
-            java {
-                disableAutoTargetJvm()
-                toolchain {
-                    languageVersion = JavaLanguageVersion.of(7)
-                }
-            }
-            test { useJUnitPlatform() }
-            """
-        file('src/test/java/org/gradle/JUnitJupiterTest.java') << '''
-            package org.gradle;
-
-            import org.junit.jupiter.api.Test;
-
-            public class JUnitJupiterTest {
-                @Test
-                public void ok() { }
-            }
-            '''
-    }
-
-    def 'can forbid user to run JUnit platform on Java 7'() {
-        when:
-        def failure = fails('test')
-
-        then:
-        failure.assertHasCause('Running tests with JUnit platform requires a Java 8+ toolchain.')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformParameterizedTestIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformParameterizedTestIntegrationTest.groovy
deleted file mode 100644
index f5aa03c..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformParameterizedTestIntegrationTest.groovy
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright 2023 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.testing.junitplatform
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.test.fixtures.file.TestFile
-import org.gradle.test.fixtures.server.http.BlockingHttpServer
-import org.gradle.testing.fixture.JUnitPlatformTestFixture
-import org.hamcrest.CoreMatchers
-import org.junit.Rule
-import spock.lang.Issue
-
-import static org.gradle.testing.fixture.JUnitCoverage.getLATEST_PLATFORM_VERSION
-
-
-class JUnitPlatformParameterizedTestIntegrationTest extends JUnitPlatformIntegrationSpec implements JUnitPlatformTestFixture {
-    @Override
-    TestFile getProjectDir() {
-        return testDirectory
-    }
-
-    @Rule BlockingHttpServer server = new BlockingHttpServer()
-
-    @Issue("https://github.com/gradle/gradle/issues/20081")
-    def "test report receives events for disabled parameterized test"() {
-        given:
-        testClass("PassingWithDisabledParameterizedTest").with {
-            testMethod('passingTest')
-            testMethod('disabledTest').disabled()
-            parameterizedMethod('disabledParameterizedTest').disabled()
-            parameterizedMethod('enabledParameterizedTest')
-        }
-        testClass("OnlyDisabledParameterizedTest").with {
-            parameterizedMethod('disabledParameterizedTest1').disabled()
-            parameterizedMethod('disabledParameterizedTest2').disabled()
-        }
-        writeTestClassFiles()
-
-        when:
-        run "test"
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("PassingWithDisabledParameterizedTest", "OnlyDisabledParameterizedTest")
-        result.testClassByHtml("PassingWithDisabledParameterizedTest")
-            .assertTestCount(5, 0, 0)
-            .assertTestsSkipped("disabledTest", "disabledParameterizedTest(String)")
-            .assertTestPassed("passingTest")
-            .assertTestPassed("enabledParameterizedTest(String)[1]", "[1] first")
-            .assertTestPassed("enabledParameterizedTest(String)[2]", "[2] second")
-        result.testClassByHtml("OnlyDisabledParameterizedTest")
-            .assertTestCount(2, 0, 0)
-            .assertTestsSkipped("disabledParameterizedTest1(String)", "disabledParameterizedTest2(String)")
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/20081")
-    def "test report receives events for disabled parameterized test within test suite"() {
-        given:
-        buildFile << """
-            dependencies {
-                testImplementation 'org.junit.platform:junit-platform-suite-engine:${LATEST_PLATFORM_VERSION}'
-            }
-        """
-
-        testSuite("TestSuite").with {
-            testClass("PassingWithDisabledParameterizedTest").with {
-                testMethod("passingTest")
-                testMethod("disabledTest").disabled()
-                parameterizedMethod("disabledParameterizedTest").disabled()
-                parameterizedMethod("enabledParameterizedTest")
-            }
-            testClass("OnlyDisabledParameterizedTest").with {
-                parameterizedMethod("disabledParameterizedTest1").disabled()
-                parameterizedMethod("disabledParameterizedTest2").disabled()
-            }
-        }
-
-        writeTestClassFiles()
-
-        when:
-        run("test", "--tests", "TestSuite")
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-
-        result.assertTestClassesExecuted("PassingWithDisabledParameterizedTest", "OnlyDisabledParameterizedTest")
-        result.testClassByHtml("PassingWithDisabledParameterizedTest")
-            .assertTestCount(5, 0, 0)
-            .assertTestsSkipped("disabledTest", "disabledParameterizedTest(String)")
-            .assertTestPassed("passingTest")
-            .assertTestPassed("enabledParameterizedTest(String)[1]", "[1] first")
-            .assertTestPassed("enabledParameterizedTest(String)[2]", "[2] second")
-        result.testClassByHtml("OnlyDisabledParameterizedTest")
-            .assertTestCount(2, 0, 0)
-            .assertTestsSkipped("disabledParameterizedTest1(String)", "disabledParameterizedTest2(String)")
-    }
-
-    @Issue("https://github.com/gradle/gradle/issues/20081")
-    def "test report receives events for skipped parameterized test when there is a failure and fail fast is used"() {
-        given:
-        server.start()
-        buildFile << """
-            test { maxParallelForks = 2 }
-        """
-        testClass("FailingTest")
-            .testMethod('failingTest')
-                .shouldFail()
-                .customContent(server.callFromBuild("failingTest"))
-        testClass("WithParameterizedTest")
-            .parameterizedMethod('enabledParameterizedTest')
-                .customContent(server.callFromBuild("enabledParameterizedTest"))
-        writeTestClassFiles()
-
-        def testExecution = server.expectConcurrentAndBlock('failingTest', 'enabledParameterizedTest')
-
-        when:
-        def gradle = executer.withTasks('test', '--fail-fast').start()
-
-        then:
-        testExecution.waitForAllPendingCalls()
-
-        when:
-        testExecution.release('failingTest')
-
-        then:
-        gradle.waitForFailure()
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecuted("FailingTest", "WithParameterizedTest")
-        result.testClassByHtml("FailingTest")
-            .assertTestFailed("failingTest", CoreMatchers.anything())
-        result.testClassByHtml("WithParameterizedTest")
-            .assertTestSkipped("enabledParameterizedTest(String)", "enabledParameterizedTest(String)")
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformSampleIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformSampleIntegrationTest.groovy
deleted file mode 100644
index 767b410..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformSampleIntegrationTest.groovy
+++ /dev/null
@@ -1,89 +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.testing.junitplatform
-
-import org.gradle.integtests.fixtures.AbstractSampleIntegrationTest
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.integtests.fixtures.Sample
-import org.gradle.integtests.fixtures.UsesSample
-import org.junit.Rule
-
-class JUnitPlatformSampleIntegrationTest extends AbstractSampleIntegrationTest {
-    @Rule
-    public final Sample sample = new Sample(testDirectoryProvider)
-
-    @UsesSample('testing/junitplatform-jupiter/groovy')
-    def 'jupiter sample test'() {
-        given:
-        super.sample sample
-
-        when:
-        succeeds 'test'
-
-        then:
-        new DefaultTestExecutionResult(sample.dir).testClassByHtml('org.gradle.junitplatform.JupiterTest').assertTestCount(5, 0, 0)
-            .assertTestPassed('ok')
-            .assertTestPassed('repeated()[1]', 'repetition 1 of 2')
-            .assertTestPassed('repeated()[2]', 'repetition 2 of 2')
-            .assertTestPassed('test1(TestInfo)', 'TEST 1')
-            .assertTestSkipped('disabled')
-    }
-
-    @UsesSample('testing/junitplatform-mix/groovy')
-    def 'mix JUnit3/4/5'() {
-        given:
-        super.sample sample
-
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(sample.dir)
-            .testClass('org.gradle.junitplatform.JUnit3Test').assertTestCount(1, 0, 0)
-        new DefaultTestExecutionResult(sample.dir)
-            .testClass('org.gradle.junitplatform.JUnit4Test').assertTestCount(1, 0, 0)
-        new DefaultTestExecutionResult(sample.dir)
-            .testClass('org.gradle.junitplatform.JupiterTest').assertTestCount(1, 0, 0)
-    }
-
-    @UsesSample('testing/junitplatform-engine/groovy')
-    def 'engine sample test'() {
-        given:
-        super.sample sample
-
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(sample.dir)
-            .assertTestClassesExecuted('org.gradle.junitplatform.JUnit4Test')
-            .testClass('org.gradle.junitplatform.JUnit4Test').assertTestCount(1, 0, 0)
-    }
-
-    @UsesSample('testing/junitplatform-tagging/groovy')
-    def 'tagging sample test'() {
-        given:
-        super.sample sample
-
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(sample.dir).testClass('org.gradle.junitplatform.TagTest').assertTestCount(1, 0, 0)
-            .assertTestPassed('fastTest()')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformTestFrameworkIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformTestFrameworkIntegrationTest.groovy
deleted file mode 100644
index 8ca938a..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformTestFrameworkIntegrationTest.groovy
+++ /dev/null
@@ -1,91 +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.testing.junitplatform
-
-import org.gradle.testing.AbstractTestFrameworkIntegrationTest
-
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
-
-class JUnitPlatformTestFrameworkIntegrationTest extends AbstractTestFrameworkIntegrationTest {
-
-    def setup() {
-        buildFile << """
-            apply plugin: 'java'
-            ${mavenCentralRepository()}
-            dependencies {
-                testImplementation 'org.junit.jupiter:junit-jupiter:${LATEST_JUPITER_VERSION}'
-            }
-            test {
-                useJUnitPlatform()
-            }
-        """
-    }
-
-    @Override
-    void createPassingFailingTest() {
-        file('src/main/java/AppException.java').writelns(
-            "public class AppException extends Exception { }"
-        )
-
-        file('src/test/java/SomeTest.java') << """
-            public class SomeTest {
-                @org.junit.jupiter.api.Test
-                public void ${failingTestCaseName} {
-                    System.err.println("some error output");
-                    org.junit.jupiter.api.Assertions.fail(\"test failure message\");
-                }
-                @org.junit.jupiter.api.Test
-                public void ${passingTestCaseName} { }
-            }
-        """
-        file('src/test/java/SomeOtherTest.java') << """
-            public class SomeOtherTest {
-                @org.junit.jupiter.api.Test
-                public void ${passingTestCaseName} { }
-            }
-        """
-    }
-
-    @Override
-    void createEmptyProject() {
-        file("src/test/java/NotATest.java") << """
-            public class NotATest {}
-        """
-    }
-
-    @Override
-    void renameTests() {
-        def newTest = file("src/test/java/NewTest.java")
-        file('src/test/java/SomeOtherTest.java').renameTo(newTest)
-        newTest.text = newTest.text.replaceAll("SomeOtherTest", "NewTest")
-    }
-
-    @Override
-    String getTestTaskName() {
-        return "test"
-    }
-
-    @Override
-    String getPassingTestCaseName() {
-        return "pass()"
-    }
-
-    @Override
-    String getFailingTestCaseName() {
-        return "fail()"
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformUserGuideIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformUserGuideIntegrationTest.groovy
deleted file mode 100644
index 468bace..0000000
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/junitplatform/JUnitPlatformUserGuideIntegrationTest.groovy
+++ /dev/null
@@ -1,424 +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.testing.junitplatform
-
-import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-
-import static org.hamcrest.CoreMatchers.containsString
-import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
-
-/**
- * These test cases are all from http://junit.org/junit5/docs/current/user-guide
- */
-class JUnitPlatformUserGuideIntegrationTest extends JUnitPlatformIntegrationSpec {
-    def 'can display test case and test class in @DisplayName'() {
-        given:
-        file('src/test/java/org/gradle/DisplayNameDemo.java') << '''
-package org.gradle;
-import org.junit.jupiter.api.*;
-
-@DisplayName("A special test case")
-class DisplayNameDemo {
-
-    @Test
-    @DisplayName("Custom test name containing spaces")
-    void testWithDisplayNameContainingSpaces() {
-    }
-}
-'''
-        file('src/test/java/org/gradle/DisplayNameDemo2.java') << '''
-package org.gradle;
-import org.junit.jupiter.api.*;
-
-@DisplayName("A special test case2")
-class DisplayNameDemo2 {
-
-    @Test
-    @DisplayName("╯°□°)╯")
-    void testWithDisplayNameContainingSpecialCharacters() {
-    }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.assertTestClassesExecutedJudgementByHtml('org.gradle.DisplayNameDemo', 'org.gradle.DisplayNameDemo2')
-            .assertTestClassesExecutedJudgementByXml('A special test case', 'A special test case2')
-        result.testClassByHtml('org.gradle.DisplayNameDemo')
-            .assertDisplayName('A special test case')
-            .assertTestCount(1, 0, 0)
-            .assertTestPassed('testWithDisplayNameContainingSpaces', 'Custom test name containing spaces')
-        result.testClassByHtml('org.gradle.DisplayNameDemo2')
-            .assertDisplayName('A special test case2')
-            .assertTestCount(1, 0, 0)
-            .assertTestPassed('testWithDisplayNameContainingSpecialCharacters', '╯°□°)╯')
-    }
-
-    def 'can change test instance lifecycle with #method'() {
-        given:
-        if (jvmArg) {
-            buildFile << """
-test {
-    jvmArgs '${jvmArg}'
-}
-"""
-        }
-        file('src/test/java/org/gradle/LifecycleTest.java') << """
-package org.gradle;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.api.TestInstance.Lifecycle;
-
-${annotation}
-public class LifecycleTest {
-    private static int counter = 0;
-
-    public LifecycleTest() {
-        if(counter == 0) {
-            counter += 1;
-        } else {
-            throw new IllegalStateException("I can only be instantiated once!");
-        }
-    }
-
-    @Test
-    public void test1() {
-        System.out.println(this);
-    }
-    @Test
-    public void test2() {
-        System.out.println(this);
-    }
-}
-"""
-        expect:
-        succeeds('test')
-
-        where:
-        method        | jvmArg                                                     | annotation
-        'JVM args'    | '-Djunit.jupiter.testinstance.lifecycle.default=per_class' | ''
-        'annotations' | ''                                                         | '@TestInstance(Lifecycle.PER_CLASS)'
-    }
-
-    def 'can perform nested tests with #maxParallelForks'() {
-        given:
-        buildFile << """
-test {
-    maxParallelForks = ${maxParallelForks}
-}
-"""
-        file('src/test/java/org/gradle/TestingAStackDemo.java') << '''
-package org.gradle;
-import static org.junit.jupiter.api.Assertions.*;
-
-import java.util.EmptyStackException;
-import java.util.Stack;
-
-import org.junit.jupiter.api.*;
-
-@DisplayName("A stack")
-class TestingAStackDemo {
-
-    Stack<Object> stack;
-
-    @Test
-    @DisplayName("is instantiated with new Stack()")
-    void isInstantiatedWithNew() {
-        new Stack<>();
-    }
-
-    @Nested
-    @DisplayName("when new")
-    class WhenNew {
-
-        @BeforeEach
-        void createNewStack() {
-            stack = new Stack<>();
-        }
-
-        @Test
-        @DisplayName("is empty")
-        void isEmpty() {
-            assertTrue(stack.isEmpty());
-        }
-
-        @Test
-        @DisplayName("throws EmptyStackException when popped")
-        void throwsExceptionWhenPopped() {
-            assertThrows(EmptyStackException.class, () -> stack.pop());
-        }
-
-        @Nested
-        @DisplayName("after pushing an element")
-        class AfterPushing {
-
-            String anElement = "an element";
-
-            @BeforeEach
-            void pushAnElement() {
-                stack.push(anElement);
-            }
-
-            @Test
-            @DisplayName("it is no longer empty")
-            void isNotEmpty() {
-                assertFalse(stack.isEmpty());
-            }
-        }
-    }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClassByHtml('org.gradle.TestingAStackDemo').assertTestCount(1, 0, 0)
-            .assertTestPassed('isInstantiatedWithNew', 'is instantiated with new Stack')
-        result.testClassByHtml('org.gradle.TestingAStackDemo$WhenNew').assertTestCount(2, 0, 0)
-            .assertTestPassed('isEmpty', 'is empty')
-            .assertTestPassed('throwsExceptionWhenPopped', 'throws EmptyStackException when popped')
-        result.testClassByHtml('org.gradle.TestingAStackDemo$WhenNew$AfterPushing').assertTestCount(1, 0, 0)
-            .assertTestPassed('isNotEmpty', 'it is no longer empty')
-
-        where:
-        maxParallelForks << [1, 3]
-    }
-
-    def 'can support dependency injection'() {
-        given:
-        file('src/test/java/org/gradle/TestInfoDemo.java') << '''
-package org.gradle;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.junit.jupiter.api.*;
-
-@DisplayName("TestInfo Demo")
-class TestInfoDemo {
-
-    TestInfoDemo(TestInfo testInfo) {
-        assertEquals("TestInfo Demo", testInfo.getDisplayName());
-    }
-
-    @BeforeEach
-    void init(TestInfo testInfo) {
-        String displayName = testInfo.getDisplayName();
-        assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
-    }
-
-    @Test
-    @DisplayName("TEST 1")
-    @Tag("my-tag")
-    void test1(TestInfo testInfo) {
-        assertEquals("TEST 1", testInfo.getDisplayName());
-        assertTrue(testInfo.getTags().contains("my-tag"));
-    }
-
-    @Test
-    void test2() {
-    }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClassByHtml('org.gradle.TestInfoDemo').assertTestCount(2, 0, 0)
-            .assertTestPassed('test2', 'test2')
-            .assertTestPassed('test1(TestInfo)', 'TEST 1')
-
-    }
-
-    def 'can use custom Extension'() {
-        given:
-        file('src/test/java/org/gradle/MyExtension.java') << '''
-package org.gradle;
-import org.junit.jupiter.api.extension.*;
-
-public class MyExtension implements TestInstancePostProcessor {
-        @Override
-        public void postProcessTestInstance(Object testInstance, ExtensionContext context) {
-                System.out.println("Created!");
-        }
-}
-'''
-        file('src/test/java/org/gradle/ExtensionTest.java') << '''
-package org.gradle;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.*;
-
-@ExtendWith(MyExtension.class)
-public class ExtensionTest {
-    @Test public void test1() { }
-    @Test public void test2() { }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        def result = new DefaultTestExecutionResult(testDirectory)
-        result.testClass('org.gradle.ExtensionTest').assertTestCount(2, 0, 0)
-            .assertStdout(containsString('Created!'))
-    }
-
-    def 'can test interface default method'() {
-        file('src/test/java/org/gradle/TestInterfaceDynamicTestsDemo.java') << '''
-package org.gradle;
-
-import org.junit.jupiter.api.*;
-import java.util.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-interface TestInterfaceDynamicTestsDemo {
-    @TestFactory
-    default Collection<DynamicTest> dynamicTestsFromCollection() {
-        return Arrays.asList(
-            DynamicTest.dynamicTest("1st dynamic test in test interface", () -> assertTrue(true)),
-            DynamicTest.dynamicTest("2nd dynamic test in test interface", () -> assertEquals(4, 2 * 2))
-        );
-    }
-
-    @BeforeEach
-    default void beforeEach() {
-        System.out.println("Invoked!");
-    }
-}
-'''
-        file('src/test/java/org/gradle/Test.java') << '''
-package org.gradle;
-
-public class Test implements TestInterfaceDynamicTestsDemo {
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClassByHtml('org.gradle.Test').assertTestCount(2, 0, 0)
-            .assertTestPassed('dynamicTestsFromCollection()[1]', '1st dynamic test in test interface')
-            .assertTestPassed('dynamicTestsFromCollection()[2]', '2nd dynamic test in test interface')
-            .assertStdout(containsString('Invoked!'))
-    }
-
-    def 'can support parameterized tests'() {
-        given:
-        buildFile << """
-dependencies {
-    testImplementation 'org.junit.jupiter:junit-jupiter-params:${LATEST_JUPITER_VERSION}'
-}
-"""
-        file('src/test/java/org/gradle/Test.java') << '''
-package org.gradle;
-
-import org.junit.jupiter.params.*;
-import org.junit.jupiter.params.provider.*;
-import org.junit.jupiter.api.*;
-import static org.junit.jupiter.api.Assertions.*;
-
-public class Test {
-    @ParameterizedTest
-    @ValueSource(strings = { "a", "b", "c" })
-    void ok(String s) {
-        assertTrue(s.length() == 1);
-    }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClassByHtml('org.gradle.Test').assertTestCount(3, 0, 0)
-            .assertTestPassed('ok(String)[1]', '[1] a')
-            .assertTestPassed('ok(String)[2]', '[2] b')
-            .assertTestPassed('ok(String)[3]', '[3] c')
-    }
-
-    def 'can use test template'() {
-        given:
-        file('src/test/java/org/gradle/TestTemplateTest.java') << '''
-package org.gradle;
-
-import org.junit.jupiter.api.*;
-import java.util.stream.*;
-import java.util.*;
-import static org.junit.jupiter.api.Assertions.*;
-import org.junit.jupiter.api.extension.*;
-
-public class TestTemplateTest {
-    @TestTemplate
-    @ExtendWith(MyTestTemplateInvocationContextProvider.class)
-    void testTemplate(String parameter) {
-        assertEquals(3, parameter.length());
-    }
-
-    private static class MyTestTemplateInvocationContextProvider implements TestTemplateInvocationContextProvider {
-        @Override
-        public boolean supportsTestTemplate(ExtensionContext context) {
-            return true;
-        }
-
-        @Override
-        public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
-            return Stream.of(invocationContext("foo"), invocationContext("bar"));
-        }
-
-        private TestTemplateInvocationContext invocationContext(String parameter) {
-            return new TestTemplateInvocationContext() {
-                @Override
-                public String getDisplayName(int invocationIndex) {
-                    return parameter;
-                }
-
-                @Override
-                public List<Extension> getAdditionalExtensions() {
-                    return Collections.singletonList(new ParameterResolver() {
-                        @Override
-                        public boolean supportsParameter(ParameterContext parameterContext,
-                                ExtensionContext extensionContext) {
-                            return parameterContext.getParameter().getType().equals(String.class);
-                        }
-
-                        @Override
-                        public Object resolveParameter(ParameterContext parameterContext,
-                                ExtensionContext extensionContext) {
-                            return parameter;
-                        }
-                    });
-                }
-            };
-        }
-    }
-}
-'''
-        when:
-        succeeds('test')
-
-        then:
-        new DefaultTestExecutionResult(testDirectory)
-            .testClassByHtml('org.gradle.TestTemplateTest').assertTestCount(2, 0, 0)
-            .assertTestPassed('testTemplate(String)[1]', 'foo')
-            .assertTestPassed('testTemplate(String)[2]', 'bar')
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFailFastIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFailFastIntegrationTest.groovy
index 12ba28b..8187801 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFailFastIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFailFastIntegrationTest.groovy
@@ -17,28 +17,13 @@
 package org.gradle.testing.testng
 
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.testing.fixture.AbstractJvmFailFastIntegrationSpec
+import org.gradle.integtests.fixtures.TargetCoverage
+import org.gradle.testing.AbstractJvmFailFastIntegrationSpec
+import org.gradle.testing.fixture.TestNGCoverage
 import org.hamcrest.CoreMatchers
 
-class TestNGFailFastIntegrationTest extends AbstractJvmFailFastIntegrationSpec {
-    @Override
-    String testAnnotationClass() {
-        'org.testng.annotations.Test'
-    }
-
-    @Override
-    String testDependency() {
-        'org.testng:testng:6.9.13.6'
-    }
-
-    @Override
-    String testFrameworkConfiguration() {
-        """
-            tasks.withType(Test) {
-                useTestNG()
-            }
-        """
-    }
+@TargetCoverage({ [TestNGCoverage.NEWEST] })
+class TestNGFailFastIntegrationTest extends AbstractJvmFailFastIntegrationSpec implements TestNGMultiVersionTest {
 
     def "parallel #parallel execution with #threadCount threads, #maxWorkers workers fails fast"() {
         given:
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFailOnNoTestIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFailOnNoTestIntegrationTest.groovy
new file mode 100644
index 0000000..73ccf50
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFailOnNoTestIntegrationTest.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 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.testing.testng
+
+import static org.gradle.testing.fixture.JUnitCoverage.LATEST_JUPITER_VERSION
+import static org.gradle.testing.fixture.TestNGCoverage.NEWEST
+
+class TestNGFailOnNoTestIntegrationTest extends TestNGTestFrameworkIntegrationTest {
+
+    static final String LATEST_TESTNG_VERSION = NEWEST
+
+    def "test source and test task use same test framework"() {
+        given:
+        buildFile << """
+            apply plugin:'java-library'
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'org.testng:testng:$LATEST_TESTNG_VERSION'
+            }
+            test {
+                useTestNG()
+            }
+        """
+
+        file("src/test/java/NotATest.java") << """
+            // missing @org.testng.annotations.Test
+            public class NotATest {}
+        """
+
+        executer.expectDocumentedDeprecationWarning("No test executed. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. There are test sources present but no test was executed. Please check your test configuration. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_fail_on_no_test_executed")
+
+        expect:
+        succeeds('test')
+    }
+
+    def "test source and test task use different test frameworks"() {
+        given:
+        buildFile << """
+            apply plugin:'java-library'
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'org.junit.jupiter:junit-jupiter:${LATEST_JUPITER_VERSION}'
+                testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+            }
+            test {
+                useJUnitPlatform()
+            }
+        """
+
+        createPassingFailingTest()
+
+        executer.expectDocumentedDeprecationWarning("No test executed. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. There are test sources present but no test was executed. Please check your test configuration. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_fail_on_no_test_executed")
+
+        expect:
+        succeeds('test')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFilteringIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFilteringIntegrationTest.groovy
index 62bc4e5..18696b7 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFilteringIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGFilteringIntegrationTest.groovy
@@ -19,32 +19,22 @@
 
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
 import org.gradle.integtests.fixtures.TargetCoverage
-import org.gradle.testing.fixture.AbstractTestFilteringIntegrationTest
+import org.gradle.testing.AbstractTestFilteringIntegrationTest
 import org.gradle.testing.fixture.TestNGCoverage
 import spock.lang.Issue
 
-import static org.gradle.testing.fixture.JUnitMultiVersionIntegrationSpec.*
-
 @TargetCoverage({ TestNGCoverage.SUPPORTED_BY_JDK })
-class TestNGFilteringIntegrationTest extends AbstractTestFilteringIntegrationTest {
-
-    String imports = "org.testng.annotations.*"
-    String framework = "TestNG"
-
-    @Override
-    String getDependencies() {
-        return "testImplementation 'org.testng:testng:${dependencyVersion}'"
-    }
+class TestNGFilteringIntegrationTest extends AbstractTestFilteringIntegrationTest implements TestNGMultiVersionTest {
 
     void theUsualFiles() {
         buildFile << """
             apply plugin: 'java'
             ${mavenCentralRepository()}
             dependencies {
-                testImplementation 'org.testng:testng:$version'
+                ${testFrameworkDependencies}
             }
             test {
-              useTestNG {
+              ${configureTestFramework} {
                 suiteXmlBuilder().suite(name: 'AwesomeSuite') {
                     test (name: 'AwesomeTest') {
                         classes([:]) {
@@ -57,20 +47,20 @@
             }
         """
 
-        file("src/test/java/FooTest.java") << """import $imports;
-
+        file("src/test/java/FooTest.java") << """
+            ${testFrameworkImports}
             public class FooTest {
                 @Test public void pass() {}
             }
         """
-        file("src/test/java/BarTest.java") << """import $imports;
-
+        file("src/test/java/BarTest.java") << """
+            ${testFrameworkImports}
             public class BarTest {
                 @Test public void pass() {}
             }
         """
-        file("src/test/java/BazTest.java") << """import $imports;
-
+        file("src/test/java/BazTest.java") << """
+            ${testFrameworkImports}
             public class BazTest {
                 @Test public void pass() {}
             }
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGIntegrationTest.groovy
index 3ae873b..9155cae 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGIntegrationTest.groovy
@@ -312,6 +312,75 @@
             .testClass('TestNG7878').assertTestCount(4, 0, 0)
     }
 
+    @Issue("https://github.com/gradle/gradle/issues/23602")
+    def "handles unserializable exception thrown from test"() {
+        given:
+        file('src/test/java/PoisonTest.java') << """
+            import org.testng.annotations.Test;
+
+            public class PoisonTest {
+                @Test public void passingTest() { }
+
+                @Test public void testWithUnserializableException() {
+                    if (true) {
+                        throw new UnserializableException();
+                    }
+                }
+
+                @Test public void normalFailingTest() {
+                    assert false;
+                }
+
+                private static class WriteReplacer implements java.io.Serializable {
+                    private Object readResolve() {
+                        return new RuntimeException();
+                    }
+                }
+
+                private static class UnserializableException extends RuntimeException {
+                    private Object writeReplace() {
+                        return new WriteReplacer();
+                    }
+                }
+            }
+        """
+
+        when:
+        fails("test")
+
+        then:
+        with(new DefaultTestExecutionResult(testDirectory).testClass("PoisonTest")) {
+            assertTestPassed("passingTest")
+            assertTestFailed("testWithUnserializableException", containsString("TestFailureSerializationException: An exception of type PoisonTest\$UnserializableException was thrown by the test, but Gradle was unable to recreate the exception in the build process"))
+            assertTestFailed("normalFailingTest", containsString("AssertionError"))
+        }
+    }
+
+    @Issue("https://issues.gradle.org/browse/GRADLE-2313")
+    def "can clean test after extracting class file"() {
+        when:
+        buildFile << """
+            apply plugin: "java"
+            ${mavenCentralRepository()}
+            dependencies {
+                testImplementation 'org.testng:testng:6.3.1'
+            }
+            test.useTestNG()
+        """
+        and:
+        file("src/test/java/SomeTest.java") << """
+            public class SomeTest extends org.testng.Converter {
+                @org.testng.annotations.Test
+                public void test() {}
+            }
+        """
+        then:
+        succeeds "clean", "test"
+
+        and:
+        file("build/tmp/test").exists() // ensure we extracted classes
+    }
+
     private static String testListener() {
         return '''
             def listener = new TestListenerImpl()
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGMultiVersionTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGMultiVersionTest.groovy
new file mode 100644
index 0000000..adc1248
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGMultiVersionTest.groovy
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2023 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.testing.testng
+
+import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec
+import org.gradle.testing.fixture.AbstractTestingMultiVersionIntegrationTest
+
+trait TestNGMultiVersionTest {
+    AbstractTestingMultiVersionIntegrationTest.BuildScriptConfiguration getBuildScriptConfiguration() {
+        return new TestNGBuildSourceConfiguration()
+    }
+
+    AbstractTestingMultiVersionIntegrationTest.TestSourceConfiguration getTestSourceConfiguration() {
+        return new TestNGTestSourceConfiguration()
+    }
+
+    static class TestNGBuildSourceConfiguration implements AbstractTestingMultiVersionIntegrationTest.BuildScriptConfiguration {
+        @Override
+        String getTestFrameworkDependencies(String sourceSet) {
+            return """
+                ${configurationFor(sourceSet, 'implementation')} 'org.testng:testng:${MultiVersionIntegrationSpec.version}'
+            """.stripIndent()
+        }
+
+        @Override
+        String getConfigureTestFramework() {
+            return "useTestNG()"
+        }
+
+        @Override
+        String getIncludeCategoryOrTagConfigurationElement() {
+            // TODO implement this if needed
+            throw new UnsupportedOperationException()
+        }
+
+        @Override
+        String getExcludeCategoryOrTagConfigurationElement() {
+            // TODO implement this if needed
+            throw new UnsupportedOperationException()
+        }
+    }
+
+    static class TestNGTestSourceConfiguration implements AbstractTestingMultiVersionIntegrationTest.TestSourceConfiguration {
+        @Override
+        String getTestFrameworkImports() {
+            return """
+                    import org.testng.annotations.*;
+               """.stripIndent()
+        }
+
+        @Override
+        String getBeforeClassAnnotation() {
+            return "@BeforeClass"
+        }
+
+        @Override
+        String getAfterClassAnnotation() {
+            return "@AfterClass"
+        }
+
+        @Override
+        String getBeforeTestAnnotation() {
+            return "@BeforeTest"
+        }
+
+        @Override
+        String getAfterTestAnnotation() {
+            return "@AfterTest"
+        }
+
+        @Override
+        String getIgnoreOrDisabledAnnotation() {
+            return "@Ignore"
+        }
+
+        @Override
+        String getRunOrExtendWithAnnotation(String runOrExtendWithClasses) {
+            // TODO implement this if needed
+            throw new UnsupportedOperationException()
+        }
+
+        @Override
+        String maybeParentheses(String methodName) {
+            return methodName
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGTestFrameworkIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGTestFrameworkIntegrationTest.groovy
index a6c36d5..6e7ed9b 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGTestFrameworkIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGTestFrameworkIntegrationTest.groovy
@@ -88,6 +88,9 @@
         """
 
         when:
+        executer.expectDocumentedDeprecationWarning("No test executed. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. There are test sources present but no test was executed. Please check your test configuration. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_fail_on_no_test_executed")
         succeeds "test"
 
         then:
@@ -108,6 +111,9 @@
         """
 
         when:
+        executer.expectDocumentedDeprecationWarning("No test executed. This behavior has been deprecated. " +
+            "This will fail with an error in Gradle 9.0. There are test sources present but no test was executed. Please check your test configuration. " +
+            "Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_fail_on_no_test_executed")
         succeeds "test"
 
         then:
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGTestOutputListenerTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGTestOutputListenerTest.groovy
new file mode 100644
index 0000000..6abe58c
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGTestOutputListenerTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 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.testing.testng
+
+class TestNGTestOutputListenerTest extends AbstractTestNGVersionIntegrationTest {
+    def "shows standard stream also for testNG"() {
+        given:
+        def test = file("src/test/java/SomeTest.java")
+        test << """
+            import org.testng.*;
+            import org.testng.annotations.*;
+
+            public class SomeTest {
+                @Test public void foo() {
+                    System.out.println("output from foo");
+                    System.err.println("error from foo");
+                }
+            }
+        """.stripIndent()
+
+        buildFile << """
+            apply plugin: 'java'
+            ${mavenCentralRepository()}
+            dependencies { testImplementation 'org.testng:testng:6.3.1' }
+
+            test {
+                useTestNG()
+                testLogging.showStandardStreams = true
+            }
+        """.stripIndent()
+
+        when: "run with quiet"
+        executer.withArguments("-q")
+        succeeds('test')
+
+        then:
+        outputDoesNotContain('output from foo')
+
+        when: "run with lifecycle"
+        executer.noExtraLogging()
+        succeeds('cleanTest', 'test')
+
+        then:
+        outputContains('output from foo')
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGUpToDateCheckIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGUpToDateCheckIntegrationTest.groovy
index be64a95..9b434e8 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGUpToDateCheckIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testng/TestNGUpToDateCheckIntegrationTest.groovy
@@ -26,7 +26,7 @@
         executer.noExtraLogging()
         file('src/test/java/SomeTest.java') << '''
             public class SomeTest {
-                @org.testng.annotations.Test
+                @org.testng.annotations.Test(groups = {"group to include"})
                 public void pass() {}
             }
         '''.stripIndent()
@@ -173,8 +173,8 @@
 
         where:
         property              | modification
-        'excludeGroups'       | '= ["some group"]'
-        'includeGroups'       | '= ["some group"]'
+        'excludeGroups'       | '= ["group to exclude"]'
+        'includeGroups'       | '= ["group to include"]'
         'outputDirectory'     | '= file("$buildDir/my-out")'
         'suiteXmlFiles'       | '= [file("suite.xml")]'
         'suiteXmlBuilder()'   | '''
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testsuites/TestSuitesIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testsuites/TestSuitesIntegrationTest.groovy
index a769d1d..8f68f42 100644
--- a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testsuites/TestSuitesIntegrationTest.groovy
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testsuites/TestSuitesIntegrationTest.groovy
@@ -384,6 +384,17 @@
     }
 
     def "task configuration overrules test suite configuration"() {
+        file('src/integTest/java/FooTest.java') << """
+            import org.junit.Test;
+
+            public class FooTest {
+                @Test
+                public void test() {
+                    System.out.println("Hello from FooTest");
+                }
+            }
+        """.stripIndent()
+
         buildFile << """
             plugins {
                 id 'java'
@@ -394,28 +405,44 @@
             testing {
                 suites {
                     integTest(JvmTestSuite) {
-                        // uses junit jupiter by default
+                        // uses junit jupiter by default, but we'll change it to junit4 on the task
+                        dependencies {
+                            implementation 'junit:junit:4.13.2'
+                        }
                         targets {
                             all {
                                 testTask.configure {
                                     useJUnit()
+                                    doFirst {
+                                        assert testFramework instanceof ${JUnitTestFramework.canonicalName}
+                                    }
                                 }
                             }
                         }
                     }
                 }
             }
-
-            // force integTest to be configured
-            tasks.named("integTest").get()
         """
 
         expect:
-        fails("help")
-        failure.assertHasCause("The value for task ':integTest' property 'testFrameworkProperty' cannot be changed any further.")
+        succeeds("integTest")
+
+        and:
+        result.assertTaskExecuted(":integTest")
     }
 
     def "task configuration overrules test suite configuration with test suite set test framework"() {
+        file("src/integTest/java/FooTest.java") << """
+            import org.junit.jupiter.api.Test;
+
+            public class FooTest {
+                @Test
+                public void test() {
+                    System.out.println("Hello from FooTest");
+                }
+            }
+        """.stripIndent()
+
         buildFile << """
             plugins {
                 id 'java'
@@ -426,25 +453,32 @@
             testing {
                 suites {
                     integTest(JvmTestSuite) {
+                        // set it to junit in the suite, but then we'll change it to junit platform on the task
                         useJUnit()
+                        dependencies {
+                            implementation 'org.junit.jupiter:junit-jupiter:5.7.1'
+                            runtimeOnly 'org.junit.platform:junit-platform-launcher'
+                        }
                         targets {
                             all {
                                 testTask.configure {
                                     useJUnitPlatform()
+                                    doFirst {
+                                        assert testFramework instanceof ${JUnitPlatformTestFramework.canonicalName}
+                                    }
                                 }
                             }
                         }
                     }
                 }
             }
-
-            // force integTest to be configured
-            tasks.named("integTest").get()
         """
 
         expect:
-        fails("help")
-        failure.assertHasCause("The value for task ':integTest' property 'testFrameworkProperty' cannot be changed any further.")
+        succeeds("integTest")
+
+        and:
+        result.assertTaskExecuted(":integTest")
     }
 
     @Issue("https://github.com/gradle/gradle/issues/18622")
@@ -482,8 +516,8 @@
         '''
 
         when:
-        executer.expectDocumentedDeprecationWarning("Configuring the test classpath by default for standalone Test tasks has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_default_classpath")
-        executer.expectDocumentedDeprecationWarning("Configuring the test classes dirs by default for standalone Test tasks has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_default_classpath")
+        executer.expectDocumentedDeprecationWarning("Relying on the convention for Test.testClassesDirs in custom Test tasks has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_default_classpath")
+        executer.expectDocumentedDeprecationWarning("Relying on the convention for Test.classpath in custom Test tasks has been deprecated. This is scheduled to be removed in Gradle 9.0. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#test_task_default_classpath")
         succeeds("mytest")
 
         then:
@@ -537,35 +571,6 @@
         succeeds("assertSameFrameworkInstance")
     }
 
-    def "multiple getTestingFramework() calls on a test suite return same instance even when calling useJUnit"() {
-        given:
-        buildFile << """
-            plugins {
-                id 'java'
-            }
-
-            def first = testing.suites.test.getTestSuiteTestingFramework()
-
-            testing {
-                suites {
-                    test {
-                        useJUnit()
-                    }
-                }
-            }
-
-            def second = testing.suites.test.getTestSuiteTestingFramework()
-
-            tasks.register('assertSameFrameworkInstance') {
-                doLast {
-                    assert first.get() === second.get()
-                }
-            }""".stripIndent()
-
-        expect:
-        succeeds("assertSameFrameworkInstance")
-    }
-
     def "the default test suite does NOT use JUnit 4 by default"() {
         given: "a build which uses the default test suite and doesn't specify a testing framework"
         file("build.gradle") << """
diff --git a/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testsuites/TestSuitesMultiTargetIntegrationTest.groovy b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testsuites/TestSuitesMultiTargetIntegrationTest.groovy
new file mode 100644
index 0000000..4571cd2
--- /dev/null
+++ b/subprojects/testing-jvm/src/integTest/groovy/org/gradle/testing/testsuites/TestSuitesMultiTargetIntegrationTest.groovy
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2023 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.testing.testsuites
+
+import org.gradle.api.internal.tasks.testing.junit.JUnitTestFramework
+import org.gradle.api.plugins.jvm.internal.DefaultJvmTestSuite
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.integtests.fixtures.jvm.JavaToolchainFixture
+import org.gradle.internal.jvm.Jvm
+
+import static org.junit.Assume.assumeNotNull
+
+class TestSuitesMultiTargetIntegrationTest extends AbstractIntegrationSpec implements JavaToolchainFixture {
+    Jvm otherJvm
+
+    def setup() {
+        otherJvm = AvailableJavaHomes.differentVersion
+        assumeNotNull(otherJvm)
+    }
+
+    private def setupBasicTestingProject(Iterable<String> extraPlugins = []) {
+        file("src/test/java/MultiTargetTest.java") << """
+            import org.junit.*;
+
+            public class MultiTargetTest {
+               @Test
+               public void test() {
+                  System.out.println("Tests running with " + System.getProperty("java.home"));
+                  Assert.assertEquals(1, 1);
+               }
+            }
+        """
+        buildFile << """
+            plugins {
+                id 'java-library'
+                ${extraPlugins.collect { "id '$it'" }.join('\n')}
+            }
+
+            java {
+                sourceCompatibility = JavaVersion.VERSION_1_8
+                targetCompatibility = JavaVersion.VERSION_1_8
+            }
+
+            ${mavenCentralRepository()}
+        """
+    }
+
+    def "multiple targets can be used"() {
+        setupBasicTestingProject()
+        buildFile << """
+            testing {
+                suites {
+                    test {
+                        useJUnit()
+                        targets {
+                            all {
+                                testTask.configure {
+                                    doLast {
+                                        assert testFramework instanceof ${JUnitTestFramework.canonicalName}
+                                        // .collect() is intentional for a better error message on failure
+                                        // The 6 elements are: junit, hamcrest, test classes and resources, main classes and resources
+                                        assert classpath.collect().size() == 6
+                                        assert classpath.any { it.name == "junit-${DefaultJvmTestSuite.TestingFramework.JUNIT4.getDefaultVersion()}.jar" }
+                                    }
+                                }
+                            }
+                            testOtherJdk {
+                                testTask.configure {
+                                    javaLauncher = javaToolchains.launcherFor {
+                                        languageVersion = JavaLanguageVersion.of(${otherJvm.javaVersion.majorVersion})
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        withInstallations(Jvm.current(), otherJvm).succeeds("check")
+
+        then:
+        result.assertTaskExecuted(":test")
+        result.assertTaskExecuted(":testOtherJdk")
+    }
+
+    // currently not supported, namespacing issues
+    def "targets in two different test suites may not share names"() {
+        setupBasicTestingProject()
+        buildFile << """
+            testing {
+                suites {
+                    test {
+                        useJUnit()
+                    }
+                    similarToTest(JvmTestSuite) {
+                        useJUnit()
+                        // Add a target with overlapping name to the default `test` target
+                        targets {
+                            test {}
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        withInstallations(Jvm.current(), otherJvm).fails("check")
+
+        then:
+        failure.assertThatCause(containsNormalizedString("Cannot add task 'test' as a task with that name already exists."))
+    }
+
+    // currently not supported, variants are ambiguous without further information
+    def "reports of multiple targets cannot be aggregated"() {
+        setupBasicTestingProject(['test-report-aggregation'])
+        buildFile << """
+            testing {
+                suites {
+                    test {
+                        useJUnit()
+                        targets {
+                            testOtherJdk {
+                                testTask.configure {
+                                    javaLauncher = javaToolchains.launcherFor {
+                                        languageVersion = JavaLanguageVersion.of(${otherJvm.javaVersion.majorVersion})
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        """
+
+        when:
+        withInstallations(Jvm.current(), otherJvm).fails("testAggregateTestReport")
+
+        then:
+        failure.assertThatCause(containsNormalizedString("However we cannot choose between the following variants of project"))
+    }
+
+    def "reports of multiple targets can be aggregated if variant information is specified"() {
+        setupBasicTestingProject(['test-report-aggregation'])
+        buildFile << """
+            testing {
+                suites {
+                    test {
+                        useJUnit()
+                        targets {
+                            testOtherJdk {
+                                testTask.configure {
+                                    javaLauncher = javaToolchains.launcherFor {
+                                        languageVersion = JavaLanguageVersion.of(${otherJvm.javaVersion.majorVersion})
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            configurations {
+                testResultsElementsForTest.attributes {
+                    attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, ${Jvm.current().javaVersion.majorVersion})
+                }
+                testResultsElementsForTestOtherJdk.attributes {
+                    attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, ${otherJvm.javaVersion.majorVersion})
+                }
+            }
+        """
+
+        when:
+        withInstallations(Jvm.current(), otherJvm).succeeds("testAggregateTestReport")
+
+        then:
+        result.assertTaskExecuted(":testAggregateTestReport")
+    }
+}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/doesNotRunStaleTests/src/test/java/Broken.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/doesNotRunStaleTests/src/test/java/Broken.java
index 6ab9964..868dabf 100644
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/doesNotRunStaleTests/src/test/java/Broken.java
+++ b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/doesNotRunStaleTests/src/test/java/Broken.java
@@ -5,4 +5,4 @@ public class Broken {
     public void broken() {
         throw new RuntimeException("broken");
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewMainClass.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewMainClass.java
index 981627b..09d61f4 100644
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewMainClass.java
+++ b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewMainClass.java
@@ -3,4 +3,4 @@
     public String toString() {
         return "new main class";
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewOk.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewOk.java
index 8d98813..9afeafd 100644
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewOk.java
+++ b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/NewOk.java
@@ -2,4 +2,4 @@
     @org.junit.Test
     public void someTest() {
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/src/main/java/MainClass.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/src/main/java/MainClass.java
index 47dc26b..b64e611 100644
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/src/main/java/MainClass.java
+++ b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/executesTestsWhenSourceChanges/src/main/java/MainClass.java
@@ -1,2 +1,2 @@
 class MainClass {
-}
\ No newline at end of file
+}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/shared/src/test/java/Ok.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/shared/src/test/java/Ok.java
index f6fe7c4..9eef52e 100644
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/shared/src/test/java/Ok.java
+++ b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/IncrementalTestIntegrationTest/shared/src/test/java/Ok.java
@@ -4,4 +4,4 @@ public class Ok {
     @Test
     public void ok() {
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/build.gradle
deleted file mode 100644
index 552879f..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/build.gradle
+++ /dev/null
@@ -1,31 +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.
- */
-
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    implementation 'org.slf4j:slf4j-api:1.7.25'
-    testImplementation 'junit:junit:4.13'
-    testRuntimeOnly 'org.slf4j:slf4j-simple:1.7.25'
-}
-
-test {
-    systemProperties 'java.system.class.loader':'org.gradle.MySystemClassLoader'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java
deleted file mode 100644
index 1636d8a..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.gradle;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-
-public class MySystemClassLoader extends URLClassLoader {
-    public MySystemClassLoader(ClassLoader parent) {
-        super(new URL[0], parent);
-        // Should be constructed with the default system ClassLoader as root
-        if (!getClass().getClassLoader().equals(parent)) {
-            throw new AssertionError();
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/src/test/java/org/gradle/TestUsingSlf4j.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/src/test/java/org/gradle/TestUsingSlf4j.java
deleted file mode 100644
index 00432ac..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithCustomSystemClassLoader/src/test/java/org/gradle/TestUsingSlf4j.java
+++ /dev/null
@@ -1,36 +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;
-
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static junit.framework.Assert.*;
-
-public class TestUsingSlf4j {
-    @Test
-    public void mySystemClassLoaderIsUsed() throws ClassNotFoundException {
-        assertTrue(ClassLoader.getSystemClassLoader() instanceof MySystemClassLoader);
-        assertEquals(getClass().getClassLoader(), ClassLoader.getSystemClassLoader().getParent());
-
-        Logger logger = LoggerFactory.getLogger(TestUsingSlf4j.class);
-        logger.info("INFO via slf4j");
-        logger.warn("WARN via slf4j");
-        logger.error("ERROR via slf4j");
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/build.gradle
deleted file mode 100644
index f88f837..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/build.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
-plugins {
-    id 'java-library'
-}
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    implementation 'org.slf4j:slf4j-api:1.7.30'
-    implementation 'org.slf4j:slf4j-simple:1.7.30'
-
-    testImplementation 'junit:junit:4.13'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/settings.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/settings.gradle
deleted file mode 100644
index 24d738d..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-rootProject.name = 'modular'
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/src/test/java/module-info.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/src/test/java/module-info.java
deleted file mode 100644
index 7522f27..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/src/test/java/module-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-module org.gradle.example {
-    exports org.gradle.example;
-    requires junit;
-    requires org.slf4j;
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/src/test/java/org/gradle/example/TestUsingSlf4j.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/src/test/java/org/gradle/example/TestUsingSlf4j.java
deleted file mode 100644
index b2e7475..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsReferencingSlf4jWithModularJava/src/test/java/org/gradle/example/TestUsingSlf4j.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.gradle.example;
-
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class TestUsingSlf4j {
-    @Test public void testModular() {
-        Logger logger = LoggerFactory.getLogger(org.gradle.example.TestUsingSlf4j.class);
-        logger.info("INFO via slf4j");
-        logger.warn("WARN via slf4j");
-        logger.error("ERROR via slf4j");
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/build.gradle
deleted file mode 100644
index 6fd0b7b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/build.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-apply plugin: "java"
-
-java {
-    toolchain {
-        languageVersion = JavaLanguageVersion.of(11)
-    }
-}
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation "junit:junit:4.13"
-}
-
-test {
-    systemProperties 'java.security.manager': 'org.gradle.MySecurityManager'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/src/test/java/org/gradle/JUnitTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/src/test/java/org/gradle/JUnitTest.java
deleted file mode 100644
index fd6644b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/src/test/java/org/gradle/JUnitTest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-import static junit.framework.Assert.*;
-import static org.junit.Assert.assertEquals;
-
-public class JUnitTest {
-    @Test
-    public void mySecurityManagerIsUsed() throws ClassNotFoundException {
-        assertTrue(System.getSecurityManager() instanceof MySecurityManager);
-        assertEquals(ClassLoader.getSystemClassLoader(), MySecurityManager.class.getClassLoader());
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/src/test/java/org/gradle/MySecurityManager.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/src/test/java/org/gradle/MySecurityManager.java
deleted file mode 100644
index 7507c21..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSecurityManager/src/test/java/org/gradle/MySecurityManager.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.gradle;
-
-import java.security.Permission;
-
-import static org.junit.Assert.assertEquals;
-
-public class MySecurityManager extends SecurityManager {
-    public MySecurityManager() {
-        assertEquals(getClass().getName(), System.getProperty("java.security.manager"));
-    }
-
-    @Override
-    public void checkPermission(Permission permission) {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/build.gradle
deleted file mode 100644
index 4c35482..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/build.gradle
+++ /dev/null
@@ -1,13 +0,0 @@
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation "junit:junit:4.13"
-}
-
-test {
-    systemProperties 'java.system.class.loader':'org.gradle.MySystemClassLoader'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/JUnitTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/JUnitTest.java
deleted file mode 100644
index 85dea42..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/JUnitTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-import static junit.framework.Assert.*;
-
-public class JUnitTest {
-    @Test
-    public void mySystemClassLoaderIsUsed() throws ClassNotFoundException {
-        assertTrue(ClassLoader.getSystemClassLoader() instanceof MySystemClassLoader);
-        assertEquals(getClass().getClassLoader(), ClassLoader.getSystemClassLoader().getParent());
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java
deleted file mode 100644
index 1636d8a..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoader/src/test/java/org/gradle/MySystemClassLoader.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.gradle;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-
-public class MySystemClassLoader extends URLClassLoader {
-    public MySystemClassLoader(ClassLoader parent) {
-        super(new URL[0], parent);
-        // Should be constructed with the default system ClassLoader as root
-        if (!getClass().getClassLoader().equals(parent)) {
-            throw new AssertionError();
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/build.gradle
deleted file mode 100644
index 9d83f6a..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/build.gradle
+++ /dev/null
@@ -1,21 +0,0 @@
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    implementation "junit:junit:4.13"
-}
-
-jar {
-    manifest {
-        attributes 'Premain-Class': 'org.gradle.MyAgent'
-    }
-}
-
-test {
-    dependsOn jar
-    systemProperties 'java.system.class.loader':'org.gradle.MySystemClassLoader'
-    jvmArgs "-javaagent:${jar.archiveFile.asFile.get()}"
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MyAgent.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MyAgent.java
deleted file mode 100644
index bbc4d13..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MyAgent.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.gradle;
-
-import java.lang.instrument.Instrumentation;
-
-import static junit.framework.Assert.assertTrue;
-
-public class MyAgent {
-    public static void premain(String args, Instrumentation instrumentation) {
-        System.setProperty("using.custom.agent", "true");
-
-        // This agent should be loaded via the custom system ClassLoader
-        assertTrue(ClassLoader.getSystemClassLoader() instanceof MySystemClassLoader);
-        assertTrue(MyAgent.class.getClassLoader() == ClassLoader.getSystemClassLoader());
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MySystemClassLoader.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MySystemClassLoader.java
deleted file mode 100644
index 8e01e43..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/main/java/org/gradle/MySystemClassLoader.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.gradle;
-
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLClassLoader;
-
-/**
- * A custom ClassLoader that redefines the custom agent and the tests, to verify that they are
- * loaded via the custom system ClassLoader.
- */
-public class MySystemClassLoader extends URLClassLoader {
-    public MySystemClassLoader(ClassLoader parent) throws URISyntaxException, ClassNotFoundException {
-        super(new URL[0], parent);
-        // Should be constructed with the default system ClassLoader as root
-        if (!getClass().getClassLoader().equals(parent)) {
-            throw new AssertionError();
-        }
-        addClasspathFor("org.gradle.MyAgent");
-        addClasspathFor("org.gradle.JUnitTest");
-    }
-
-    private void addClasspathFor(String name) throws ClassNotFoundException {
-        URL codebase = getParent().loadClass(name).getProtectionDomain().getCodeSource().getLocation();
-        addURL(codebase);
-    }
-
-    @Override
-    public Class<?> loadClass(String className) throws ClassNotFoundException {
-        if (className.equals(getClass().getName())) {
-            return getClass();
-        }
-        try {
-            return findClass(className);
-        } catch (ClassNotFoundException e) {
-            return super.loadClass(className);
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/test/java/org/gradle/JUnitTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/test/java/org/gradle/JUnitTest.java
deleted file mode 100644
index 9699dc8..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/TestEnvironmentIntegrationTest/canRunTestsWithCustomSystemClassLoaderAndJavaAgent/src/test/java/org/gradle/JUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-public class JUnitTest {
-    @Test
-    public void mySystemClassLoaderIsUsed() throws ClassNotFoundException {
-        assertEquals("true", System.getProperty("using.custom.agent"));
-
-        // This test class should be loaded via the custom system ClassLoader
-        assertTrue(ClassLoader.getSystemClassLoader() instanceof MySystemClassLoader);
-        assertTrue(getClass().getClassLoader() == ClassLoader.getSystemClassLoader());
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest/supportsAssumptionsInRules/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest/supportsAssumptionsInRules/build.gradle
deleted file mode 100644
index 1c9cc4d..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest/supportsAssumptionsInRules/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-apply plugin: 'java'
-repositories { mavenCentral() }
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest/supportsAssumptionsInRules/src/test/java/org/gradle/SkippingRuleTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest/supportsAssumptionsInRules/src/test/java/org/gradle/SkippingRuleTests.java
deleted file mode 100644
index cb76679..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAbortedTestClassIntegrationTest/supportsAssumptionsInRules/src/test/java/org/gradle/SkippingRuleTests.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019 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;
-
-import org.junit.FixMethodOrder;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.MethodRule;
-
-import static org.junit.Assume.assumeFalse;
-import static org.junit.runners.MethodSorters.NAME_ASCENDING;
-
-@FixMethodOrder(NAME_ASCENDING)
-public class SkippingRuleTests {
-
-    @Rule
-    public MethodRule misbehavingSkippingRule = (statement, method, target) -> {
-        assumeFalse(method.getName().equals("b"));
-        return statement;
-    };
-
-    @Test
-    public void a() {
-    }
-
-    @Test
-    public void b() {
-    }
-
-    @Test
-    public void c() {
-    }
-
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest/supportsAssumptions/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest/supportsAssumptions/build.gradle
deleted file mode 100644
index 1c9cc4d..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest/supportsAssumptions/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-apply plugin: 'java'
-repositories { mavenCentral() }
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest/supportsAssumptions/src/test/java/org/gradle/TestWithAssumptions.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest/supportsAssumptions/src/test/java/org/gradle/TestWithAssumptions.java
deleted file mode 100644
index 7fd3fc1..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitAssumptionsIntegrationTest/supportsAssumptions/src/test/java/org/gradle/TestWithAssumptions.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Assume;
-import org.junit.Test;
-
-public class TestWithAssumptions {
-    @Test
-    public void assumptionFailed() {
-        Assume.assumeTrue(false);
-    }
-
-    @Test
-    public void assumptionSucceeded() {
-        Assume.assumeTrue(true);
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/build.gradle
deleted file mode 100644
index 6b5b248..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/build.gradle
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-test {
-    useJUnit {
-        excludeCategories 'org.gradle.CategoryA'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/CategoryA.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/CategoryA.java
deleted file mode 100644
index 932dcf1..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/CategoryA.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public interface CategoryA {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/LocaleHolder.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/LocaleHolder.java
deleted file mode 100644
index b42b587..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/LocaleHolder.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import java.util.Locale;
-
-class LocaleHolder {
-    private static Locale locale;
-
-    public static Locale set(Locale locale) {
-        Locale old = LocaleHolder.locale;
-        LocaleHolder.locale = locale;
-        return old;
-    }
-    public static Locale get() {
-        return locale;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/Locales.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/Locales.java
deleted file mode 100644
index aedac80..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/Locales.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.runner.Runner;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.Suite;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.Statement;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-
-public class Locales extends Suite {
-    private static final Iterable<Locale> localesToUse = Arrays.asList(Locale.FRENCH, Locale.GERMAN, Locale.ENGLISH);
-
-    public Locales(Class<?> klass) throws InitializationError {
-        super(klass, extractAndCreateRunners(klass));
-    }
-
-    private static List<Runner> extractAndCreateRunners(Class<?> klass) throws InitializationError {
-        List<Runner> runners = new ArrayList<Runner>();
-        for (Locale locale : localesToUse) {
-            runners.add(new LocalesRunner(locale, klass));
-        }
-        return runners;
-    }
-
-    private static class LocalesRunner extends BlockJUnit4ClassRunner {
-        private final Locale locale;
-
-        LocalesRunner(Locale locale, Class<?> klass) throws InitializationError {
-            super(klass);
-            this.locale = locale;
-        }
-
-        @Override
-        protected Statement methodBlock(final FrameworkMethod method) {
-            return new Statement() {
-                @Override
-                public void evaluate() throws Throwable {
-                    Locale oldLocale = LocaleHolder.set(locale);
-                    try {
-                        LocalesRunner.super.methodBlock(method).evaluate();
-                    } finally {
-                        LocaleHolder.set(oldLocale);
-                    }
-                }
-            };
-        }
-
-        @Override// The name of the test class
-        protected String getName() {
-            return String.format("%s [%s]", super.getName(), locale);
-        }
-
-        @Override// The name of the test method
-        protected String testName(final FrameworkMethod method) {
-            return String.format("%s [%s]", method.getName(), locale);
-        }
-    }
-
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/SomeLocaleTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/SomeLocaleTests.java
deleted file mode 100644
index 97b8122..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/SomeLocaleTests.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-
-@RunWith(org.gradle.Locales.class)
-public class SomeLocaleTests {
-    @Test
-    public void ok1() {
-        System.out.println("Locale in use: " + LocaleHolder.get());
-    }
-
-    @Test
-    @Category(org.gradle.CategoryA.class)
-    public void ok2() {
-        System.out.println("Locale in use: " + LocaleHolder.get());
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/SomeMoreLocalTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/SomeMoreLocalTests.java
deleted file mode 100644
index e18620f..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canCombineCategoriesWithCustomRunner/src/test/java/org/gradle/SomeMoreLocalTests.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-
-@RunWith(org.gradle.Locales.class)
-@Category(org.gradle.CategoryA.class)
-public class SomeMoreLocalTests {
-    @Test
-    public void someMoreTest1() {
-        System.out.println("Locale in use: " + LocaleHolder.get());
-    }
-
-    @Test
-    public void someMoreTest2() {
-        System.out.println("Locale in use: " + LocaleHolder.get());
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/build.gradle
deleted file mode 100644
index 987aa0b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-test {
-    useJUnit {
-        includeCategories 'org.gradle.SomeCategory'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/src/test/java/org/gradle/NestedTestsWithCategories.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/src/test/java/org/gradle/NestedTestsWithCategories.java
deleted file mode 100644
index c633a47..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/src/test/java/org/gradle/NestedTestsWithCategories.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.ArrayList;
-
-import static org.junit.Assert.fail;
-
-public class NestedTestsWithCategories {
-
-    @Category(SomeCategory.class)
-    @RunWith(Parameterized.class)
-    public static class TagOnClass {
-        @Parameterized.Parameters
-        public static Iterable<Object[]> getParameters() {
-            ArrayList<Object[]> parameters = new ArrayList<>();
-            parameters.add(new Object[] { "tag on class" });
-            return parameters;
-        }
-
-        private final String param;
-
-        public TagOnClass(String param) {
-            this.param = param;
-        }
-
-        @Test
-        public void run() {
-            System.err.println("executed " + param);
-        }
-    }
-
-    @RunWith(Parameterized.class)
-    public static class TagOnMethod {
-        @Parameterized.Parameters
-        public static Iterable<Object[]> getParameters() {
-            ArrayList<Object[]> parameters = new ArrayList<>();
-            parameters.add(new Object[] { "tag on method" });
-            return parameters;
-        }
-
-        private final String param;
-
-        public TagOnMethod(String param) {
-            this.param = param;
-        }
-
-        @Test
-        @Category(SomeCategory.class)
-        public void run() {
-            System.err.println("executed " + param);
-        }
-
-        @Test
-        public void filteredOut() {
-            throw new AssertionError("should be filtered out");
-        }
-    }
-
-    public static class TagOnMethodNoParam {
-        @Test
-        @Category(SomeCategory.class)
-        public void run() {
-            System.err.println("executed tag on method (no param)");
-        }
-
-        @Test
-        public void filteredOut() {
-            throw new AssertionError("should be filtered out");
-        }
-    }
-
-    @RunWith(Parameterized.class)
-    public static class Untagged {
-        @Parameterized.Parameters
-        public static Iterable<Object[]> getParameters() {
-            ArrayList<Object[]> parameters = new ArrayList<>();
-            parameters.add(new Object[] { "untagged" });
-            return parameters;
-        }
-
-        private final String param;
-
-        public Untagged(String param) {
-            this.param = param;
-        }
-
-        @Test
-        public void run() {
-            System.err.println("executed " + param);
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/src/test/java/org/gradle/SomeCategory.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/src/test/java/org/gradle/SomeCategory.java
deleted file mode 100644
index 036456a..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canRunParameterizedTestsWithCategories/src/test/java/org/gradle/SomeCategory.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.gradle;
-
-public interface SomeCategory {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/build.gradle
deleted file mode 100644
index 72cfbd5..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/build.gradle
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-test {
-    useJUnit {
-        excludeCategories 'org.gradle.CategoryA'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/CatATests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/CatATests.java
deleted file mode 100644
index 3abce2e..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/CatATests.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-
-@Category(org.gradle.CategoryA.class)
-public class CatATests {
-
-    @Test
-    public void catAOk1() {
-    }
-
-    @Test
-    public void catAOk2() {
-    }
-
-    @Test
-    public void catAOk3() {
-    }
-
-    @Test
-    public void catAOk4() {
-    }
-}
-
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/CategoryA.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/CategoryA.java
deleted file mode 100644
index fe86403..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/CategoryA.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public class CategoryA {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/NoCatTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/NoCatTests.java
deleted file mode 100644
index 31fe49e..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/NoCatTests.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-
-public class NoCatTests {
-
-    @Test
-    public void noCatOk1() {
-    }
-
-    @Test
-    public void noCatOk2() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeOtherCat.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeOtherCat.java
deleted file mode 100644
index f694f60..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeOtherCat.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public interface SomeOtherCat {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeOtherCatTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeOtherCatTests.java
deleted file mode 100644
index 68266a5..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeOtherCatTests.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Category(org.gradle.SomeOtherCat.class)
-public class SomeOtherCatTests {
-
-    @Test
-    public void someOtherOk1() {
-    }
-
-    @Test
-    public void someOtherOk2() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeTests.java
deleted file mode 100644
index dd69759..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyExcludesOnly/src/test/java/org/gradle/SomeTests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-public class SomeTests {
-    @Category(org.gradle.CategoryA.class)
-    @Test
-    public void catAOk1() {
-    }
-
-    @Category(org.gradle.SomeOtherCat.class)
-    @Test
-    public void someOtherCatOk2() {
-    }
-
-    @Test
-    public void noCatOk3() {
-    }
-
-    @Test
-    public void noCatOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/build.gradle
deleted file mode 100644
index 34071d2..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/build.gradle
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-test {
-    useJUnit {
-        includeCategories 'org.gradle.CategoryA'
-        excludeCategories 'org.gradle.CategoryC'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatACTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatACTests.java
deleted file mode 100644
index c026a15..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatACTests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Category({org.gradle.CategoryA.class, org.gradle.CategoryC.class})
-public class CatACTests {
-
-    @Test
-    public void catABOk1() {
-    }
-
-    @Test
-    public void catABOk2() {
-    }
-
-    @Test
-    public void catABOk3() {
-    }
-
-    @Test
-    public void catABOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatADTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatADTests.java
deleted file mode 100644
index 6650d3b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatADTests.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-
-@Category(org.gradle.CategoryA.class)
-public class CatADTests {
-
-    @Test
-    public void catAOk1() {
-    }
-
-    @Test
-    public void catAOk2() {
-    }
-
-    @Test
-    @Category(org.gradle.CategoryC.class)
-    public void catCOk3() {
-    }
-
-    @Test
-    @Category(org.gradle.CategoryD.class)
-    public void catDOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatATests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatATests.java
deleted file mode 100644
index a8966b4..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatATests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-
-@Category(org.gradle.CategoryA.class)
-public class CatATests {
-
-    @Test
-    public void catAOk1() {
-    }
-
-    @Test
-    public void catAOk2() {
-    }
-
-    @Test
-    public void catAOk3() {
-    }
-
-    @Test
-    public void catAOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatBTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatBTests.java
deleted file mode 100644
index a6dccd4..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatBTests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Category(org.gradle.CategoryB.class)
-public class CatBTests {
-
-    @Test
-    public void catBOk1() {
-    }
-
-    @Test
-    public void catBOk2() {
-    }
-
-    @Test
-    public void catBOk3() {
-    }
-
-    @Test
-    public void catBOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatCBTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatCBTests.java
deleted file mode 100644
index 88617d4..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatCBTests.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-
-@Category(org.gradle.CategoryC.class)
-public class CatCBTests {
-
-    @Test
-    public void catCOk1() {
-    }
-
-    @Test
-    public void catCOk2() {
-    }
-
-    @Test
-    @Category(org.gradle.CategoryA.class)
-    public void catAOk3() {
-    }
-
-    @Test
-    @Category(org.gradle.CategoryB.class)
-    public void catBOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatCTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatCTests.java
deleted file mode 100644
index 6ff66fe..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatCTests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Category(CategoryC.class)
-public class CatCTests {
-
-    @Test
-    public void catCOk1() {
-    }
-
-    @Test
-    public void catCOk2() {
-    }
-
-    @Test
-    public void catCOk3() {
-    }
-
-    @Test
-    public void catCOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatDTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatDTests.java
deleted file mode 100644
index f755acc..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatDTests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Category(CategoryD.class)
-public class CatDTests {
-
-    @Test
-    public void catDOk1() {
-    }
-
-    @Test
-    public void catDOk2() {
-    }
-
-    @Test
-    public void catDOk3() {
-    }
-
-    @Test
-    public void catDOk4() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatZTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatZTests.java
deleted file mode 100644
index c95e596..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CatZTests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Category(CategoryZ.class)
-public class CatZTests {
-
-    @Test
-    public void catZOk1() {
-    }
-
-    @Test
-    public void catZOk2() {
-    }
-
-    @Test
-    public void catZOk3() {
-    }
-
-    @Test
-    public void catZOk4() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryA.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryA.java
deleted file mode 100644
index 87277ec..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryA.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public interface CategoryA{
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryB.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryB.java
deleted file mode 100644
index 355b5f8..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryB.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public interface CategoryB extends org.gradle.CategoryA{
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryC.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryC.java
deleted file mode 100644
index ccee989..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryC.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public interface CategoryC{
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryD.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryD.java
deleted file mode 100644
index c8f7024..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryD.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public interface CategoryD extends org.gradle.CategoryC{
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryZ.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryZ.java
deleted file mode 100644
index a866cbc..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/CategoryZ.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public interface CategoryZ {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/MixedTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/MixedTests.java
deleted file mode 100644
index fefb774..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/MixedTests.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-
-public class MixedTests {
-
-    @Category(org.gradle.CategoryA.class)
-    @Test
-    public void catAOk1() {
-    }
-
-    @Category(org.gradle.CategoryB.class)
-    @Test
-    public void catBOk2() {
-    }
-
-    @Category(org.gradle.CategoryA.class)
-    @Ignore
-    @Test
-    public void someIgnoredTest() {
-    }
-
-    @Test
-    public void noCatOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/NoCatTests.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/NoCatTests.java
deleted file mode 100644
index a96e2c8..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesCoverageIntegrationSpec/canSpecifyIncludeAndExcludeCategories/src/test/java/org/gradle/NoCatTests.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.Test;
-
-public class NoCatTests {
-
-    @Test
-    public void noCatOk1() {
-    }
-
-    @Test
-    public void noCatOk2() {
-    }
-
-    @Test
-    public void noCatOk3() {
-    }
-
-    @Test
-    public void noCatOk4() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/build.gradle
deleted file mode 100644
index 77acb15..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/build.gradle
+++ /dev/null
@@ -1,21 +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.
- */
-
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/CategoryA.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/CategoryA.java
deleted file mode 100644
index 8329eae..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/CategoryA.java
+++ /dev/null
@@ -1,20 +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;
-
-public interface CategoryA {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/CategoryB.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/CategoryB.java
deleted file mode 100644
index 4c09973..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/CategoryB.java
+++ /dev/null
@@ -1,20 +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;
-
-public interface CategoryB {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/SomeTestClass.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/SomeTestClass.java
deleted file mode 100644
index c60f959..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/integTest/java/org/gradle/SomeTestClass.java
+++ /dev/null
@@ -1,29 +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;
-
-import org.junit.Test;
-
-public class SomeTestClass {
-    @Test
-    public void ok1() {
-    }
-
-    @Test
-    public void ok2() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/CategoryA.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/CategoryA.java
deleted file mode 100644
index 8329eae..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/CategoryA.java
+++ /dev/null
@@ -1,20 +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;
-
-public interface CategoryA {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/CategoryB.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/CategoryB.java
deleted file mode 100644
index 4c09973..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/CategoryB.java
+++ /dev/null
@@ -1,20 +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;
-
-public interface CategoryB {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/SomeTestClass.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/SomeTestClass.java
deleted file mode 100644
index c60f959..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reExecutesWhenPropertyIsChanged/src/test/java/org/gradle/SomeTestClass.java
+++ /dev/null
@@ -1,29 +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;
-
-import org.junit.Test;
-
-public class SomeTestClass {
-    @Test
-    public void ok1() {
-    }
-
-    @Test
-    public void ok2() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/build.gradle
deleted file mode 100644
index 4b87618..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/build.gradle
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-
-
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation 'junit:junit:4.13'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/src/test/java/org/gradle/SomeTestClass.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/src/test/java/org/gradle/SomeTestClass.java
deleted file mode 100644
index 28a0c2b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/reportsUnloadableCategories/src/test/java/org/gradle/SomeTestClass.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-public class SomeTestClass {
-    @Test
-    public void ok1() {
-    }
-
-    @Test
-    public void ok2() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/build.gradle
deleted file mode 100644
index e9ba460..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/build.gradle
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2017 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.
- */
-
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation "junit:junit:4.13"
-}
-
-test {
-    useJUnit {
-        excludeCategories 'CategoryA'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/CategoryA.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/CategoryA.java
deleted file mode 100644
index ad12f47..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/CategoryA.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2017 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.
- */
-
-public interface CategoryA {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/CustomRunner.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/CustomRunner.java
deleted file mode 100644
index 6cc4cd0..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/CustomRunner.java
+++ /dev/null
@@ -1,21 +0,0 @@
-import org.junit.runner.Description;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-
-public class CustomRunner extends BlockJUnit4ClassRunner {
-
-    public CustomRunner(Class<?> klass) throws InitializationError {
-        super(klass);
-    }
-
-    /**
-     * Returns a test Description with a null TestClass.
-     * @param method method under test
-     * @return a Description
-     */
-    @Override
-    protected Description describeChild(FrameworkMethod method) {
-        return Description.createTestDescription("Not a real class name", testName(method), "someSerializable");
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/DescriptionWithNullClassTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/DescriptionWithNullClassTest.java
deleted file mode 100644
index 800aeb5..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/supportsCategoriesAndNullTestClassDescription/src/test/java/DescriptionWithNullClassTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2017 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.
- */
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(CustomRunner.class)
-public class DescriptionWithNullClassTest {
-
-    @Test
-    public void someTest() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/testTaskFailsIfCategoriesNotSupported/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/testTaskFailsIfCategoriesNotSupported/build.gradle
deleted file mode 100644
index 7a27a77..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/testTaskFailsIfCategoriesNotSupported/build.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation "junit:junit:4.4"
-}
-
-test {
-    useJUnit {
-        includeCategories 'org.gradle.CategoryA'
-        excludeCategories 'org.gradle.CategoryC'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/testTaskFailsIfCategoriesNotSupported/src/test/java/org/gradle/SomeTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/testTaskFailsIfCategoriesNotSupported/src/test/java/org/gradle/SomeTest.java
deleted file mode 100644
index 0c8904e..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitCategoriesIntegrationSpec/testTaskFailsIfCategoriesNotSupported/src/test/java/org/gradle/SomeTest.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-public class SomeTest {
-    @Test
-    public void ok() {
-    }
-
-    public void helpermethod() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/build.gradle
deleted file mode 100644
index 558bc18..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/build.gradle
+++ /dev/null
@@ -1,23 +0,0 @@
-apply plugin: "groovy"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    implementation "org.codehaus.groovy:groovy:2.4.15"
-    testImplementation "junit:junit:4.13"
-}
-
-test {
-    testLogging {
-        quiet {
-            events "skipped", "failed"
-            minGranularity 2
-            maxGranularity -1
-            displayGranularity 3
-            exceptionFormat "full"
-            stackTraceFilters "truncate", "groovy"
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/src/test/groovy/org/gradle/JUnit4Test.groovy b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/src/test/groovy/org/gradle/JUnit4Test.groovy
deleted file mode 100644
index e34e121..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/shared/src/test/groovy/org/gradle/JUnit4Test.groovy
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2012 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
-
-import org.junit.Ignore
-import org.junit.Test
-
-class JUnit4Test {
-    @Test
-    void goodTest() {}
-
-    @Test
-    void badTest() {
-        beBad()
-    }
-
-    @Test
-    void printTest() {
-        println "line 1\nline 2"
-        println "line 3"
-    }
-
-    @Test
-    @Ignore
-    void ignoredTest() {
-        throw new RuntimeException("ignored")
-    }
-
-    private beBad() {
-        throw new RuntimeException("bad")
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/build.gradle
deleted file mode 100644
index 70ab35e..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/build.gradle
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2012 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.
- */
-
-apply plugin: "groovy"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    implementation "org.codehaus.groovy:groovy:2.4.15"
-    testImplementation "junit:junit:4.13"
-}
-
-test {
-    testLogging {
-        quiet {
-            events "standardOut", "standardError"
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/src/test/groovy/org/gradle/JUnit4StandardOutputTest.groovy b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/src/test/groovy/org/gradle/JUnit4StandardOutputTest.groovy
deleted file mode 100644
index 0cb930e..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitConsoleLoggingIntegrationTest/standardOutputLogging/src/test/groovy/org/gradle/JUnit4StandardOutputTest.groovy
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2012 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
-
-import org.junit.Ignore
-import org.junit.Test
-
-class JUnit4StandardOutputTest {
-    @Test
-    void goodTest() {}
-
-    @Test
-    void badTest() {
-        beBad()
-    }
-
-    @Test
-    void printTest() {
-        println "line 1\nline 2"
-        println "line 3"
-    }
-
-    @Test
-    @Ignore
-    void ignoredTest() {
-        throw new RuntimeException("ignored")
-    }
-
-    private beBad() {
-        throw new RuntimeException("bad")
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/build.gradle
deleted file mode 100644
index f3986de..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/build.gradle
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-
-
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/src/test/java/org/gradle/CustomIgnoredTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/src/test/java/org/gradle/CustomIgnoredTest.java
deleted file mode 100644
index eb87e9f..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/src/test/java/org/gradle/CustomIgnoredTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.gradle;
-
-import org.junit.Ignore;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runner.Runner;
-import org.junit.runner.notification.Failure;
-import org.junit.runner.notification.RunNotifier;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Ignore
-@RunWith(org.gradle.CustomIgnoredTest.TheRunner.class)
-public class CustomIgnoredTest {
-    static int count = 0;
-
-    public boolean doSomething() {
-        return true;
-    }
-
-    public static class TheRunner extends Runner {
-        List descriptions = new ArrayList();
-        private final Class<? extends org.gradle.CustomIgnoredTest> testClass;
-        private final org.gradle.CustomIgnoredTest testContainingInstance;
-        private Description testSuiteDescription;
-
-        public TheRunner(Class<? extends org.gradle.CustomIgnoredTest> testClass) {
-            this.testClass = testClass;
-            testContainingInstance = reflectMeATestContainingInstance(testClass);
-            testSuiteDescription = Description.createSuiteDescription("Custom Test with Suite ");
-            testSuiteDescription.addChild(createTestDescription("first test run"));
-            testSuiteDescription.addChild(createTestDescription("second test run"));
-            testSuiteDescription.addChild(createTestDescription("third test run"));
-        }
-
-        @Override
-        public Description getDescription() {
-            return testSuiteDescription;
-        }
-
-        @Override
-        public void run(RunNotifier notifier) {
-            for (Description description : testSuiteDescription.getChildren()) {
-                notifier.fireTestStarted(description);
-                try {
-                    if (testContainingInstance.doSomething()) {
-                        notifier.fireTestFinished(description);
-                    } else {
-                        notifier.fireTestIgnored(description);
-                    }
-                } catch (Exception e) {
-                    notifier.fireTestFailure(new Failure(description, e));
-                }
-            }
-        }
-
-        private org.gradle.CustomIgnoredTest reflectMeATestContainingInstance(Class<? extends org.gradle.CustomIgnoredTest> testClass) {
-            try {
-                return testClass.getConstructor().newInstance();
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        private Description createTestDescription(String description) {
-            return Description.createTestDescription(testClass, description);
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/src/test/java/org/gradle/IgnoredTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/src/test/java/org/gradle/IgnoredTest.java
deleted file mode 100644
index ec02a29..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIgnoreClassMultiVersionIntegrationSpec/canHandleClassLevelIgnoredTests/src/test/java/org/gradle/IgnoredTest.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.gradle;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-@Ignore
-public class IgnoredTest {
-    @Test
-    public void testIgnored() {
-        throw new RuntimeException();
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
deleted file mode 100644
index bcd420e..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/build.gradle
+++ /dev/null
@@ -1,23 +0,0 @@
-apply plugin: 'java'
-
-sourceSets {
-    test2
-}
-
-task test2(type: Test) {
-    classpath = sourceSets.test2.runtimeClasspath
-    testClassesDirs = sourceSets.test2.output.classesDirs
-}
-
-check {
-    dependsOn test2
-}
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation 'junit:junit:4.13'
-    test2Implementation 'junit:junit:4.13'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java
deleted file mode 100644
index e0deefbb..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test/java/org/gradle/Test1.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-public class Test1 {
-    @Test
-    public void ok() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test2/java/org/gradle/Test2.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test2/java/org/gradle/Test2.java
deleted file mode 100644
index 32b1274..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canHaveMultipleTestTaskInstances/src/test2/java/org/gradle/Test2.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-public class Test2 {
-    @Test
-    public void ok() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/build.gradle
deleted file mode 100644
index 1032aaf..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/build.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation 'junit:junit:4.13'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/NotATest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/NotATest.java
deleted file mode 100644
index 2e10657..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/NotATest.java
+++ /dev/null
@@ -1,2 +0,0 @@
-public class NotATest {
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok.java
deleted file mode 100644
index f6fe7c4..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok.java
+++ /dev/null
@@ -1,7 +0,0 @@
-import org.junit.Test;
-
-public class Ok {
-    @Test
-    public void ok() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok2.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok2.java
deleted file mode 100644
index c47e685c..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunSingleTests/src/test/java/Ok2.java
+++ /dev/null
@@ -1,7 +0,0 @@
-import org.junit.Test;
-
-public class Ok2 {
-    @Test
-    public void ok() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunTestsUsingJUnit3/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunTestsUsingJUnit3/build.gradle
deleted file mode 100644
index 6ccb947..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/canRunTestsUsingJUnit3/build.gradle
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation "junit:junit:3.8"
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/build.gradle
deleted file mode 100644
index 6a112e7..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/build.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-dependencies {
-    implementation 'junit:junit:4.13'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/CustomRunner.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/CustomRunner.java
deleted file mode 100644
index 9892c46..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/CustomRunner.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.gradle;
-
-import java.lang.reflect.Method;
-import org.junit.runner.notification.RunNotifier;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.Statement;
-
-public class CustomRunner extends BlockJUnit4ClassRunner {
-    public static boolean isClassUnderTestLoaded;
-    private final Class<?> bootstrappedTestClass;
-
-    public CustomRunner(Class<?> clazz) throws Exception {
-        super(clazz);
-        bootstrappedTestClass = clazz;
-    }
-
-    @Override
-    protected Statement methodBlock(final FrameworkMethod method) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-
-                if(isClassUnderTestLoaded){
-                    throw new RuntimeException("Test Class should not be loaded");
-                }
-
-                final HelperTestRunner helperTestRunner = new HelperTestRunner(bootstrappedTestClass);
-                final Method bootstrappedMethod = bootstrappedTestClass.getMethod(method.getName());
-                final Statement statement = helperTestRunner.methodBlock(new FrameworkMethod(bootstrappedMethod));
-                statement.evaluate();
-            }
-        };
-    }
-
-    public class HelperTestRunner extends BlockJUnit4ClassRunner {
-        public HelperTestRunner(Class<?> testClass) throws InitializationError {
-            super(testClass);
-        }
-
-        @Override
-        protected Object createTest() throws Exception {
-            return super.createTest();
-        }
-
-        @Override
-        public Statement classBlock(RunNotifier notifier) {
-            return super.classBlock(notifier);
-        }
-
-        @Override
-        public Statement methodBlock(FrameworkMethod method) {
-            return super.methodBlock(method);
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/ExecutionOrderTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/ExecutionOrderTest.java
deleted file mode 100644
index b8f5dbf..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/createsRunnerBeforeTests/src/test/java/org/gradle/ExecutionOrderTest.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.gradle;
-
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-
-@RunWith(CustomRunner.class)
-public class ExecutionOrderTest {
-
-    static{
-        CustomRunner.isClassUnderTestLoaded = true;
-    }
-
-	@Test
-	public void classUnderTestIsLoadedOnlyByRunner(){
-		// The CustomRunner class will fail this test if this class is initialized before its 
-		// run method is triggered. 
-	}
-	
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/build.gradle
deleted file mode 100644
index 6a112e7..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/build.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-dependencies {
-    implementation 'junit:junit:4.13'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/AbstractHasRunWith.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/AbstractHasRunWith.java
deleted file mode 100644
index 206f20a..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/AbstractHasRunWith.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.gradle;
-
-import org.junit.runner.RunWith;
-
-@RunWith(CustomRunner.class)
-public abstract class AbstractHasRunWith {
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/CustomRunner.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/CustomRunner.java
deleted file mode 100644
index 5ee597c..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/CustomRunner.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.gradle;
-
-import org.junit.runner.*;
-import org.junit.runner.notification.RunNotifier;
-
-public class CustomRunner extends Runner {
-    private final Description description;
-
-    public CustomRunner(Class type) throws Exception {
-        description = Description.createSuiteDescription(type);
-        description.addChild(Description.createTestDescription(type, "ok"));
-    }
-
-    @Override
-    public Description getDescription() {
-        return description;
-    }
-
-    @Override
-    public void run(RunNotifier notifier) {
-        for (Description child : description.getChildren()) {
-            notifier.fireTestStarted(child);
-            notifier.fireTestFinished(child);
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/EmptyRunWithSubclass.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/EmptyRunWithSubclass.java
deleted file mode 100644
index 6455710..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/EmptyRunWithSubclass.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.gradle;
-
-public class EmptyRunWithSubclass extends AbstractHasRunWith {
-
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/TestsOnInner.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/TestsOnInner.java
deleted file mode 100644
index 3d7bafa..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/detectsTestClasses/src/test/java/org/gradle/TestsOnInner.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.gradle;
-
-import junit.framework.TestCase;
-import org.junit.Test;
-
-public class TestsOnInner {
-    @Test public void ok() { }
-
-    public static class SomeInner {
-        @Test public void ok() { }
-    }
-
-    public class NonStaticInner {
-        @Test public void ok() { }
-    }
-
-    public class NonStaticInnerTestCase extends TestCase {
-        public void testOk() { }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
deleted file mode 100644
index 89c6a1f..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/build.gradle
+++ /dev/null
@@ -1,20 +0,0 @@
-apply plugin: 'java'
-repositories { mavenCentral() }
-dependencies { testImplementation 'junit:junit:4.13', 'ant:ant:1.6.1', 'ant:ant-launcher:1.6.1' }
-test {
-    systemProperties.testSysProperty = 'value'
-    systemProperties.projectDir = projectDir
-    jvmArgumentProviders.add(new TestClassPathProvider(testClasspath: sourceSets.test.runtimeClasspath))
-    environment.TEST_ENV_VAR = 'value'
-}
-
-class TestClassPathProvider implements CommandLineArgumentProvider {
-    @Classpath
-    FileCollection testClasspath
-
-    @Override
-    List<String> asArguments() {
-        FileCollection filteredTestClasspath = testClasspath.filter { f -> f.exists() || ("*".equals(f.getName()) && f.getParentFile() != null && f.getParentFile().exists()) }
-        ["-DtestRuntimeClasspath=${filteredTestClasspath.asPath}".toString()]
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
deleted file mode 100644
index 3c67e67..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OkTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package org.gradle;
-
-import java.io.File;
-import java.io.PrintStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-import static org.junit.Assert.*;
-
-public class OkTest {
-    @org.junit.Test
-    public void ok() throws Exception {
-        // check versions of dependencies
-        assertEquals("4.13", new org.junit.runner.JUnitCore().getVersion());
-        assertTrue(org.apache.tools.ant.Main.getAntVersion().contains("1.6.1"));
-
-        // check working dir
-        assertEquals(System.getProperty("projectDir"), System.getProperty("user.dir"));
-
-        // check sys properties
-        assertEquals("value", System.getProperty("testSysProperty"));
-
-        // check env vars
-        assertEquals("value", System.getenv("TEST_ENV_VAR"));
-
-        // check classloader and classpath
-        assertSame(ClassLoader.getSystemClassLoader(), getClass().getClassLoader());
-        assertSame(getClass().getClassLoader(), Thread.currentThread().getContextClassLoader());
-        boolean isJava9 = Boolean.parseBoolean(System.getProperty("isJava9"));
-        String[] splitTestRuntimeClasspath = splitClasspath(System.getProperty("testRuntimeClasspath"));
-        if (isJava9) {
-            String[] splitCliClasspath = splitClasspath(System.getProperty("java.class.path"));
-
-            // The worker jar is first on the classpath.
-            String workerJar = splitCliClasspath[0];
-            assertTrue(workerJar + " is the worker jar", workerJar.contains("gradle-worker.jar"));
-
-            // In between, we expect the test runtime classpath.
-            String[] filteredCliClasspath = Arrays.copyOfRange(splitCliClasspath, 1, splitCliClasspath.length - 1);
-            assertEquals(splitTestRuntimeClasspath, filteredCliClasspath);
-
-            // We inject our own junit jar from the distribution on the end of the classpath.
-            String distributionJUnitJar = splitCliClasspath[splitCliClasspath.length - 1];
-            assertTrue(distributionJUnitJar + " is the injected junit jar", distributionJUnitJar.endsWith("junit-4.13.2.jar"));
-        } else {
-            List<URL> systemClasspath = Arrays.asList(((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs());
-
-            // The worker jar is first on the classpath.
-            String workerJar = systemClasspath.get(0).getPath();
-            assertTrue(workerJar + " is the worker jar", workerJar.endsWith("gradle-worker.jar"));
-
-            // In between, we expect the test runtime classpath.
-            List<URL> filteredSystemClasspath = systemClasspath.subList(1, systemClasspath.size() - 1);
-            List<URL> testRuntimeClasspath = getTestRuntimeClasspath(splitTestRuntimeClasspath);
-            assertEquals(testRuntimeClasspath, filteredSystemClasspath);
-
-            // We inject our own junit jar from the distribution on the end of the classpath.
-            String distributionJUnitJar = systemClasspath.get(systemClasspath.size() - 1).getPath();
-            assertTrue(distributionJUnitJar + " is the injected junit jar", distributionJUnitJar.endsWith("junit-4.13.2.jar"));
-        }
-
-        // check Gradle and impl classes not visible
-        try {
-            getClass().getClassLoader().loadClass("org.gradle.api.Project");
-            fail();
-        } catch (ClassNotFoundException e) {
-        }
-        try {
-            getClass().getClassLoader().loadClass("org.slf4j.Logger");
-            fail();
-        } catch (ClassNotFoundException e) {
-        }
-
-        // check other environmental stuff
-        assertEquals("Test worker", Thread.currentThread().getName());
-        assertNull(System.console());
-
-        final PrintStream out = System.out;
-        // logging from a shutdown hook
-        Runtime.getRuntime().addShutdownHook(new Thread() {
-            @Override
-            public void run() {
-                out.println("stdout from a shutdown hook.");
-                Logger.getLogger("test-logger").info("info from a shutdown hook.");
-            }
-        });
-    }
-
-    public List<URL> getTestRuntimeClasspath(String[] splitTestRuntimeClasspath) throws MalformedURLException {
-        List<URL> urls = new ArrayList<URL>();
-        for (String path : splitTestRuntimeClasspath) {
-            urls.add(new File(path).toURI().toURL());
-        }
-        return urls;
-    }
-
-    private String[] splitClasspath(String classpath) {
-        return classpath.split(Pattern.quote(File.pathSeparator));
-    }
-
-    @org.junit.Test
-    public void anotherOk() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java
deleted file mode 100644
index 52c9af4..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/executesTestsInCorrectEnvironment/src/test/java/org/gradle/OtherTest.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.gradle;
-
-public class OtherTest {
-    @org.junit.Test
-    public void ok() throws Exception {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit3Tests/src/test/java/org/gradle/Junit3Test.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit3Tests/src/test/java/org/gradle/Junit3Test.java
deleted file mode 100644
index a621efc..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit3Tests/src/test/java/org/gradle/Junit3Test.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.gradle;
-
-import junit.framework.TestCase;
-
-public class Junit3Test extends TestCase {
-    public void testRenamesItself() {
-        setName("a test that renames itself");
-        fail("epic");
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/Junit4Test.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/Junit4Test.java
deleted file mode 100644
index 9d0ba47..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/Junit4Test.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.gradle;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class Junit4Test {
-    @Test
-    public void ok() {
-    }
-
-    @Test
-    @Ignore
-    public void broken() {
-        throw new RuntimeException();
-    }
-
-    public void helpermethod() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/NoTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/NoTest.java
deleted file mode 100644
index 32dea4b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/junit4Tests/src/test/java/org/gradle/NoTest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2013 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.testing.junit.JUnitIntegrationTest.junit4Tests.src.test.java.org.gradle;
-
-public class NoTest {
-    public void notATest() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
deleted file mode 100644
index 1e6d230..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-apply plugin: 'java'
-repositories {
-    mavenCentral()
-}
-dependencies {
-    testImplementation 'junit:junit:4.13'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfter.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfter.java
deleted file mode 100644
index 0e0c901..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfter.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.gradle;
-
-import org.junit.After;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class BrokenAfter {
-    @After
-    public void broken() {
-        fail("failed");
-    }
-
-    @Test
-    public void ok() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfterClass.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfterClass.java
deleted file mode 100644
index 8452a92..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenAfterClass.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.gradle;
-
-import org.junit.AfterClass;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class BrokenAfterClass {
-    @AfterClass
-    public static void broken() {
-        fail("failed");
-    }
-
-    @Test
-    public void ok() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBefore.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBefore.java
deleted file mode 100644
index dccf416..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBefore.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.gradle;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class BrokenBefore {
-    @Before
-    public void broken() {
-        fail("failed");
-    }
-
-    @Test
-    public void ok() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeAndAfter.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeAndAfter.java
deleted file mode 100644
index c8931c1..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeAndAfter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.gradle;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class BrokenBeforeAndAfter {
-    @Before
-    public void brokenBefore() {
-        fail("before failed");
-    }
-
-    @After
-    public void brokenAfter() {
-        fail("after failed");
-    }
-
-    @Test
-    public void ok() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeClass.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeClass.java
deleted file mode 100644
index b69ef80..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenBeforeClass.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.gradle;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class BrokenBeforeClass {
-    @BeforeClass
-    public static void broken() {
-        fail("failed");
-    }
-
-    @Test
-    public void ok() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenConstructor.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenConstructor.java
deleted file mode 100644
index 4e86b14..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenConstructor.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class BrokenConstructor {
-    public BrokenConstructor() {
-        fail("failed");
-    }
-
-    @Test
-    public void ok() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenException.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenException.java
deleted file mode 100644
index 969897d..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-public class BrokenException {
-    @Test
-    public void broken() {
-        // Exception's toString() throws an exception
-        throw new BrokenRuntimeException();
-    }
-
-    private static class BrokenRuntimeException extends RuntimeException {
-        @Override
-        public String toString() {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenRunner.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenRunner.java
deleted file mode 100644
index 4abefe8..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenRunner.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.gradle;
-
-import org.junit.runner.Description;
-import org.junit.runner.Runner;
-import org.junit.runner.notification.RunNotifier;
-
-public class BrokenRunner extends Runner {
-    private final Class<?> type;
-
-    public BrokenRunner(Class<?> type) {
-        this.type = type;
-    }
-
-    @Override
-    public Description getDescription() {
-        return Description.createSuiteDescription(type);
-    }
-
-    @Override
-    public void run(RunNotifier notifier) {
-        throw new UnsupportedOperationException("broken");
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenTest.java
deleted file mode 100644
index 76a09ac..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/BrokenTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class BrokenTest {
-    @Test
-    public void failure() {
-        fail("failed");
-    }
-
-    @Test
-    public void broken() {
-        throw new IllegalStateException("html: <> cdata: ]]>");
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/ClassWithBrokenRunner.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/ClassWithBrokenRunner.java
deleted file mode 100644
index 8984a63..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/ClassWithBrokenRunner.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.gradle;
-
-import org.junit.runner.RunWith;
-
-@RunWith(BrokenRunner.class)
-public class ClassWithBrokenRunner {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/CustomException.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/CustomException.java
deleted file mode 100644
index 001f6d5..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/CustomException.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-public class CustomException {
-    @Test
-    public void custom() {
-        throw new CustomException.CustomRuntimeException();
-    }
-
-    private static class CustomRuntimeException extends RuntimeException {
-        @Override
-        public String toString() {
-            return "Exception with a custom toString implementation";
-        }
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java
deleted file mode 100644
index 92753b3..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/Unloadable.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class Unloadable {
-    static {
-        fail("failed");
-    }
-
-    @Test
-    public void ok() {
-    }
-
-    @Test
-    public void ok2() {
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/UnserializableException.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/UnserializableException.java
deleted file mode 100644
index 51ef669..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/reportsAndBreaksBuildWhenTestFails/src/test/java/org/gradle/UnserializableException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-
-public class UnserializableException {
-
-    @Test
-    public void unserialized() {
-        throw new UnserializableRuntimeException("whatever", null);
-    }
-
-    static class UnserializableRuntimeException extends RuntimeException {
-        UnserializableRuntimeException(String message, Throwable cause) {
-            super(message, cause);
-        }
-
-        private void writeObject(ObjectOutputStream outstr) throws IOException {
-            outstr.writeObject(new Object());
-        }
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/build.gradle
deleted file mode 100644
index 914ea64..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-apply plugin: 'java'
-repositories { mavenCentral() }
-dependencies { testImplementation 'junit:junit:4.13'}
-test {
-    include '**/ASuite.class'
-    exclude '**/*Test.class'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/ASuite.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/ASuite.java
deleted file mode 100644
index 33c8e9b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/ASuite.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2011 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;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({OkTest.class, OtherTest.class })
-public class ASuite {
-    static {
-        System.out.println("suite class loaded");
-    }
-
-    @org.junit.BeforeClass public static void init() {
-        System.out.println("before suite class out");
-        System.out.println("non-asci char: ż");
-        System.err.println("before suite class err");
-    }
-
-    @org.junit.AfterClass public static void end() {
-        System.out.println("after suite class out");
-        System.err.println("after suite class err");
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/OkTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/OkTest.java
deleted file mode 100644
index 54c874a..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/OkTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2011 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;
-
-public class OkTest {
-
-    @org.junit.Test
-    public void ok() throws Exception {
-        System.err.println("This is test stderr");
-    }
-
-    @org.junit.Test
-    public void anotherOk() {
-        System.out.println("sys out from another test method");
-        System.err.println("sys err from another test method");
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/OtherTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/OtherTest.java
deleted file mode 100644
index 75891c2..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/suitesOutputIsVisible/src/test/java/org/gradle/OtherTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2011 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;
-
-public class OtherTest {
-
-    @org.junit.Test
-    public void ok() throws Exception {
-        System.out.println("This is other stdout");
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/build.gradle
deleted file mode 100644
index ebf0682..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/build.gradle
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-apply plugin: 'java'
-repositories { mavenCentral() }
-dependencies {
-    testImplementation "junit:junit:3.8"
-}
-
-test {
-    include '**/*Suite.class'
-    exclude '**/*Test.class'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeSuite.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeSuite.java
deleted file mode 100644
index 7953c04..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeSuite.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import junit.extensions.TestSetup;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-public class SomeSuite extends TestCase {
-
-    public static Test suite() {
-        final TestSuite suite = new TestSuite();
-        suite.addTestSuite(SomeTest1.class);
-        suite.addTestSuite(SomeTest2.class);
-        TestSetup wrappedSuite = new junit.extensions.TestSetup(suite) {
-
-            protected void setUp() {
-                System.out.println("stdout in TestSetup#setup");
-                System.err.println("stderr in TestSetup#setup");
-            }
-
-            protected void tearDown() {
-                System.out.println("stdout in TestSetup#teardown");
-                System.err.println("stderr in TestSetup#teardown");
-            }
-        };
-
-        return wrappedSuite;
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeTest1.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeTest1.java
deleted file mode 100644
index 42de49f..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeTest1.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import junit.framework.TestCase;
-
-public class SomeTest1 extends TestCase {
-    public void testOk1(){
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeTest2.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeTest2.java
deleted file mode 100644
index 4e5105b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsJunit3Suites/src/test/java/org/gradle/SomeTest2.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import junit.framework.TestCase;
-
-public class SomeTest2 extends TestCase{
-    public void testOk2(){
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/build.gradle
deleted file mode 100644
index 371a478..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/build.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-apply plugin: "java"
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation "junit:junit:4.13"
-}
-
-test {
-    useJUnit {
-        includeCategories 'org.gradle.CategoryA'
-        excludeCategories 'org.gradle.CategoryC'
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryA.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryA.java
deleted file mode 100644
index d396068..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryA.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.gradle;
-
-public interface CategoryA {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryB.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryB.java
deleted file mode 100644
index 00d73b6..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryB.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.gradle;
-
-public interface CategoryB extends CategoryA{
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryC.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryC.java
deleted file mode 100644
index e70c77f..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/CategoryC.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.gradle;
-
-public interface CategoryC {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/SomeTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/SomeTest.java
deleted file mode 100644
index 5e0ba2b..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/supportsTestCategories/src/test/java/org/gradle/SomeTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-@Category(CategoryA.class)
-public class SomeTest {
-
-    @Test
-    public void testOk1() {
-    }
-
-    @Test
-    @Category(CategoryC.class)
-    public void testOk2() {
-    }
-
-    @Test
-    @Category(CategoryB.class)
-    public void testOk3() {
-    }
-
-    @Test
-    @Category({CategoryB.class, CategoryC.class})
-    public void testOk4() {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/build.gradle
deleted file mode 100644
index 3def9b9..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-apply plugin: 'java'
-repositories { mavenCentral() }
-dependencies { testImplementation 'junit:junit:4.13'}
-test {
-    include '**/*Suite.class'
-    exclude '**/*Test.class'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeOtherTestSuite.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeOtherTestSuite.java
deleted file mode 100644
index 98bbbdc..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeOtherTestSuite.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2013 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;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({SomeTest.class})
-public class SomeOtherTestSuite {
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeTest.java
deleted file mode 100644
index c349393..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2013 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;
-
-public class SomeTest {
-    @org.junit.Test
-    public void ok() throws Exception {
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeTestSuite.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeTestSuite.java
deleted file mode 100644
index 30610ec..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitIntegrationTest/testClassesCanBeSharedByMultipleSuites/src/test/java/org/gradle/SomeTestSuite.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2013 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;
-import  org.junit.runner.RunWith;
-import  org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({SomeTest.class})
-public class SomeTestSuite {
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/build.gradle
deleted file mode 100644
index 78e4893..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation 'junit:junit:4.13'
-    testImplementation 'org.apache.felix:org.osgi.foundation:1.2.0'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/src/test/java/org/gradle/AbstractTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/src/test/java/org/gradle/AbstractTest.java
deleted file mode 100644
index 108f457..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/src/test/java/org/gradle/AbstractTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.gradle;
-
-import org.junit.Before;
-
-public abstract class AbstractTest {
-
-    protected int value = 0;
-
-    @Before
-    public void before() {
-        value = 1;
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/src/test/java/org/gradle/Test1.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/src/test/java/org/gradle/Test1.java
deleted file mode 100644
index 439c523..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJdkNavigationIntegrationTest/shouldNotNavigateToJdkClasses/src/test/java/org/gradle/Test1.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.gradle;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class Test1 extends AbstractTest {
-
-    @Test
-    public void shouldPass() {
-        assertEquals(1, value);
-    }
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/build.gradle b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/build.gradle
deleted file mode 100644
index f1d4390..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-apply plugin: 'java'
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    testImplementation 'junit:junit:4.13'
-    testImplementation 'net.java.dev.jna:jna-platform:4.1.0'
-}
diff --git a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/src/test/java/OkTest.java b/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/src/test/java/OkTest.java
deleted file mode 100644
index 70ed647..0000000
--- a/subprojects/testing-jvm/src/integTest/resources/org/gradle/testing/junit/JUnitJnaIntegrationTest/canRunTestsUsingJna/src/test/java/OkTest.java
+++ /dev/null
@@ -1,9 +0,0 @@
-import org.junit.Test;
-import com.sun.jna.platform.win32.Shell32;
-
-public class OkTest {
-    @Test
-    public void ok() {
-        assert Shell32.INSTANCE != null;
-    }
-}
\ No newline at end of file
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/TestFramework.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/TestFramework.java
index e699edf..ee6ed97 100644
--- a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/TestFramework.java
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/TestFramework.java
@@ -26,6 +26,7 @@
 import org.gradle.process.internal.worker.WorkerProcessBuilder;
 
 import java.io.Closeable;
+import java.util.Collections;
 import java.util.List;
 
 @UsedByScanPlugin("test-retry")
@@ -67,28 +68,59 @@ public interface TestFramework extends Closeable {
     Action<WorkerProcessBuilder> getWorkerConfigurationAction();
 
     /**
-     * Returns a list of jars the test worker requires on the classpath.
+     * Returns a list of distribution modules that the test worker requires on the application classpath.
      * These dependencies are loaded from the Gradle distribution.
      *
+     * Application classes specified by {@link WorkerProcessBuilder#sharedPackages} are
+     * also included in the implementation classpath.
+     *
      * @see #getUseDistributionDependencies()
      */
     @Internal
-    List<String> getTestWorkerApplicationClasses();
+    default List<TestFrameworkDistributionModule> getWorkerApplicationClasspathModules() {
+        return Collections.emptyList();
+    }
 
     /**
-     * Returns a list of modules the test worker requires on the modulepath if it runs as a module.
+     * Returns a list of distribution modules that the test worker requires on the application modulepath if it runs as a module.
+     * These dependencies are loaded from the Gradle distribution.
+     *
+     * Application classes specified by {@link WorkerProcessBuilder#sharedPackages} are
+     * also included in the implementation classpath.
+     *
+     * @see #getUseDistributionDependencies()
+     */
+    @Internal
+    default List<TestFrameworkDistributionModule> getWorkerApplicationModulepathModules() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns a list of distribution modules that the test worker requires on implementation the classpath.
      * These dependencies are loaded from the Gradle distribution.
      *
      * @see #getUseDistributionDependencies()
      */
     @Internal
-    List<String> getTestWorkerApplicationModules();
+    default List<TestFrameworkDistributionModule> getWorkerImplementationClasspathModules() {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Returns a list of distribution modules that the test worker requires on the implementation modulepath if it runs as a module.
+     * These dependencies are loaded from the Gradle distribution.
+     *
+     * @see #getUseDistributionDependencies()
+     */
+    @Internal
+    default List<TestFrameworkDistributionModule> getWorkerImplementationModulepathModules() {
+        return Collections.emptyList();
+    }
 
     /**
      * Whether the legacy behavior of loading test framework dependencies from the Gradle distribution
-     * is enabled. If true, jars and modules as specified by {@link #getTestWorkerApplicationClasses()}
-     * and {@link #getTestWorkerApplicationModules()} respectively are loaded from the Gradle distribution
-     * and placed on the test worker application classpath and/or modulepath.
+     * is enabled. If true, jars specified by this framework are loaded from the Gradle distribution
+     * and placed on the test worker implementation/application classpath/modulepath.
      * <p>
      * This functionality is legacy and will eventually be deprecated and removed. Test framework dependencies
      * should be managed externally from the Gradle distribution, as is done by test suites.
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/TestFrameworkDistributionModule.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/TestFrameworkDistributionModule.java
new file mode 100644
index 0000000..b5cb894
--- /dev/null
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/TestFrameworkDistributionModule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.testing;
+
+import java.util.regex.Pattern;
+
+/**
+ * Represents a module loaded from the gradle distribution, including metadata detailing the name of the JAR
+ * which implements the module and an example class name, which if loaded, signals the existence of the module.
+ *
+ * <p>The complexity here is necessary to determine if test framework dependencies are already present on the
+ * application classpath, and thus do not need to be loaded from the Gradle distribution. The behavior of loading
+ * test framework dependencies from the Gradle distribution is deprecated and will be removed in 9.0</p>
+ */
+public class TestFrameworkDistributionModule {
+
+    private final String moduleName;
+    private final Pattern jarFilePattern;
+    private final String exampleClassName;
+
+    public TestFrameworkDistributionModule(String moduleName, Pattern jarFilePattern, String exampleClassName) {
+        this.moduleName = moduleName;
+        this.jarFilePattern = jarFilePattern;
+        this.exampleClassName = exampleClassName;
+    }
+
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    public Pattern getJarFilePattern() {
+        return jarFilePattern;
+    }
+
+    public String getExampleClassName() {
+        return exampleClassName;
+    }
+}
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
index 8d7eb68..df373c8 100644
--- a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/detection/DefaultTestExecuter.java
@@ -16,8 +16,6 @@
 
 package org.gradle.api.internal.tasks.testing.detection;
 
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
 import org.gradle.api.file.FileTree;
 import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.api.internal.classpath.ModuleRegistry;
@@ -34,6 +32,7 @@
 import org.gradle.api.internal.tasks.testing.processors.RunPreviousFailedFirstTestClassProcessor;
 import org.gradle.api.internal.tasks.testing.processors.TestMainAction;
 import org.gradle.api.internal.tasks.testing.worker.ForkingTestClassProcessor;
+import org.gradle.api.internal.tasks.testing.worker.ForkedTestClasspath;
 import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.internal.Factory;
@@ -44,7 +43,6 @@
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * The default test class scanner factory.
@@ -55,7 +53,7 @@ public class DefaultTestExecuter implements TestExecuter<JvmTestExecutionSpec> {
 
     private final WorkerProcessFactory workerFactory;
     private final ActorFactory actorFactory;
-    private final ModuleRegistry moduleRegistry;
+    private final ForkedTestClasspathFactory testClasspathFactory;
     private final WorkerLeaseService workerLeaseService;
     private final int maxWorkerCount;
     private final Clock clock;
@@ -70,7 +68,7 @@ public DefaultTestExecuter(
     ) {
         this.workerFactory = workerFactory;
         this.actorFactory = actorFactory;
-        this.moduleRegistry = moduleRegistry;
+        this.testClasspathFactory = new ForkedTestClasspathFactory(moduleRegistry);
         this.workerLeaseService = workerLeaseService;
         this.maxWorkerCount = maxWorkerCount;
         this.clock = clock;
@@ -83,39 +81,16 @@ public void execute(final JvmTestExecutionSpec testExecutionSpec, TestResultProc
         final TestFramework testFramework = testExecutionSpec.getTestFramework();
         final WorkerTestClassProcessorFactory testInstanceFactory = testFramework.getProcessorFactory();
 
-        // TODO: Loading jars from the Gradle distribution can lead confusion in regards
-        // to which test framework dependencies actually end up on the classpath, and can
-        // lead to multiple different versions on the classpath at once.
-        // Once test suites are de-incubated, we should deprecate this distribution-loading
-        // behavior entirely and rely on the tests to always provide their implementation
-        // dependencies.
-
-        final List<File> classpath;
-        final List<File> modulePath;
-        if (testFramework.getUseDistributionDependencies()) {
-            if (testExecutionSpec.getTestIsModule()) {
-                classpath = pathWithAdditionalJars(testExecutionSpec.getClasspath(), testFramework.getTestWorkerApplicationClasses());
-                modulePath = pathWithAdditionalJars(testExecutionSpec.getModulePath(), testFramework.getTestWorkerApplicationModules());
-            } else {
-                // For non-module tests, add all additional distribution jars to the classpath.
-                List<String> additionalClasspath = ImmutableList.<String>builder()
-                    .addAll(testFramework.getTestWorkerApplicationClasses())
-                    .addAll(testFramework.getTestWorkerApplicationModules())
-                    .build();
-
-                classpath = pathWithAdditionalJars(testExecutionSpec.getClasspath(), additionalClasspath);
-                modulePath = ImmutableList.copyOf(testExecutionSpec.getModulePath());
-            }
-        } else {
-            classpath = ImmutableList.copyOf(testExecutionSpec.getClasspath());
-            modulePath = ImmutableList.copyOf(testExecutionSpec.getModulePath());
-        }
+        ForkedTestClasspath classpath = testClasspathFactory.create(
+            testExecutionSpec.getClasspath(), testExecutionSpec.getModulePath(),
+            testFramework, testExecutionSpec.getTestIsModule()
+        );
 
         final Factory<TestClassProcessor> forkingProcessorFactory = new Factory<TestClassProcessor>() {
             @Override
             public TestClassProcessor create() {
                 return new ForkingTestClassProcessor(workerLeaseService, workerFactory, testInstanceFactory, testExecutionSpec.getJavaForkOptions(),
-                    classpath, modulePath, testFramework.getWorkerConfigurationAction(), moduleRegistry, documentationRegistry);
+                    classpath, testFramework.getWorkerConfigurationAction(), documentationRegistry);
             }
         };
         final Factory<TestClassProcessor> reforkingProcessorFactory = new Factory<TestClassProcessor>() {
@@ -135,7 +110,7 @@ public TestClassProcessor create() {
         if (testExecutionSpec.isScanForTestClasses() && testFramework.getDetector() != null) {
             TestFrameworkDetector testFrameworkDetector = testFramework.getDetector();
             testFrameworkDetector.setTestClasses(new ArrayList<File>(testExecutionSpec.getTestClassesDirs().getFiles()));
-            testFrameworkDetector.setTestClasspath(classpath);
+            testFrameworkDetector.setTestClasspath(classpath.getApplicationClasspath());
             detector = new DefaultTestClassScanner(testClassFiles, testFrameworkDetector, processor);
         } else {
             detector = new DefaultTestClassScanner(testClassFiles, null, processor);
@@ -159,34 +134,4 @@ private int getMaxParallelForks(JvmTestExecutionSpec testExecutionSpec) {
         }
         return maxParallelForks;
     }
-
-    /**
-     * Create a classpath or modulePath, as a list of files, given both the files provided by the test spec and a list of
-     * modules to load from the Gradle distribution.
-     *
-     * @param testFiles A set of jars, as given from a {@link JvmTestExecutionSpec}'s classpath or modulePath.
-     * @param additionalModules The names of any additional modules to load from the Gradle distribution.
-     *
-     * @return A set of files representing the constructed classpath or modulePath.
-     */
-    private List<File> pathWithAdditionalJars(Iterable<? extends File> testFiles, List<String> additionalModules) {
-        ImmutableList.Builder<File> outputFiles = ImmutableList.<File>builder().addAll(testFiles);
-
-        if (LOGGER.isDebugEnabled() && !additionalModules.isEmpty()) {
-            LOGGER.debug("Loaded additional modules from the Gradle distribution: " + Joiner.on(",").join(additionalModules));
-        }
-
-        for (String module : additionalModules) {
-            outputFiles.addAll(moduleRegistry.getExternalModule(module).getImplementationClasspath().getAsFiles());
-
-            // TODO: The user is relying on dependencies from the Gradle distribution. Emit a deprecation warning.
-            // We may want to wait for test-suites to be de-incubated here. If users are using the `test.useJUnitPlatform`
-            // syntax, they will need to list their framework dependency manually, but if they are using the
-            // `testing.suites.test.useJUnitFramework` syntax, they do not need to explicitly list their dependencies.
-            // We don't want to push users to add their dependencies explicitly if test suites will remove that
-            // requirement in the future.
-        }
-
-        return outputFiles.build();
-    }
 }
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/detection/ForkedTestClasspathFactory.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/detection/ForkedTestClasspathFactory.java
new file mode 100644
index 0000000..bdd7184
--- /dev/null
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/detection/ForkedTestClasspathFactory.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.testing.detection;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import org.gradle.api.internal.classpath.ModuleRegistry;
+import org.gradle.api.internal.tasks.testing.JvmTestExecutionSpec;
+import org.gradle.api.internal.tasks.testing.TestFramework;
+import org.gradle.api.internal.tasks.testing.TestFrameworkDistributionModule;
+import org.gradle.api.internal.tasks.testing.worker.ForkedTestClasspath;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.internal.UncheckedException;
+import org.gradle.internal.classpath.ClassPath;
+import org.gradle.internal.classpath.DefaultClassPath;
+import org.gradle.internal.deprecation.DeprecationLogger;
+import org.gradle.util.internal.CollectionUtils;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Constructs the application and implementation classpaths for a test process,
+ * while also optionally loading test framework implementation dependencies from
+ * the distribution.
+ *
+ * @see ForkedTestClasspath
+ */
+public class ForkedTestClasspathFactory {
+
+    private static final Logger LOGGER = Logging.getLogger(ForkedTestClasspathFactory.class);
+
+    private final ModuleRegistry moduleRegistry;
+    private final ClassDetectorFactory classDetectorFactory;
+
+    public ForkedTestClasspathFactory(ModuleRegistry moduleRegistry) {
+        this(moduleRegistry, ClassLoadingClassDetector::new);
+    }
+
+    @VisibleForTesting
+    public ForkedTestClasspathFactory(ModuleRegistry moduleRegistry, ClassDetectorFactory classDetectorFactory) {
+        this.moduleRegistry = moduleRegistry;
+        this.classDetectorFactory = classDetectorFactory;
+    }
+
+    public ForkedTestClasspath create(
+        Iterable<? extends File> classpath,
+        Iterable<? extends File> modulepath,
+        TestFramework testFramework,
+        boolean isModule
+    ) {
+        if (!testFramework.getUseDistributionDependencies()) {
+            return new ForkedTestClasspath(
+                ImmutableList.copyOf(classpath), ImmutableList.copyOf(modulepath),
+                withImplementation(ImmutableList.of()), ImmutableList.of()
+            );
+        }
+
+        AdditionalClasspath unfiltered = new AdditionalClasspath(
+            testFramework.getWorkerApplicationClasspathModules(),
+            testFramework.getWorkerApplicationModulepathModules(),
+            testFramework.getWorkerImplementationClasspathModules(),
+            testFramework.getWorkerImplementationModulepathModules()
+        );
+
+        AdditionalClasspath filtered = filterAdditionalClasspath(classpath, modulepath, unfiltered);
+
+        // The test's runtimeClasspath already includes the test framework's implementation modules.
+        // No need to load anything extra ourselves.
+        if (filtered.isEmpty()) {
+            return new ForkedTestClasspath(
+                ImmutableList.copyOf(classpath), ImmutableList.copyOf(modulepath),
+                withImplementation(ImmutableList.of()), ImmutableList.of()
+            );
+        }
+
+        // There are some framework implementation dependencies which are not present on the testRuntimeClasspath.
+        // It is not sufficient to just load the missing modules, since some classes which are referenced
+        // by the loaded modules may exist in the versions of the present dependencies from the test classpath.
+        // For example, junit-platform-launcher 1.7+ depends on ClassNamePatternFilterUtils from junit-platform-commons 1.7+.
+        // If the test classpath contains junit-platform-commons 1.6, then the test process will fail to load the required
+        // class.
+        //
+        // So, even if some framework dependencies are already present on the test classpath, we still need to load
+        // the entire set of framework implementation dependencies from the distribution in order to ensure that all implementation
+        // dependencies on the classpath share a version. This can _still_ lead to duplicates on the classpath, but it is at least
+        // avoidable if the user adds junit-platform-launcher to their test runtime classpath, which they should be doing, since
+        // distribution loading is deprecated in Gradle 8.2.
+        return getClasspathWithAdditionalModules(classpath, modulepath, unfiltered, isModule);
+    }
+
+    /**
+     * Creates a classpath for the forked process which injects the additional modules from
+     * {@code additional} into the classpath provided by {@code classpath} and {@code modulepath}.
+     */
+    private ForkedTestClasspath getClasspathWithAdditionalModules(
+        Iterable<? extends File> classpath,
+        Iterable<? extends File> modulepath,
+        AdditionalClasspath additional,
+        boolean isModule
+    ) {
+        DeprecationLogger.deprecateIndirectUsage("The automatic loading of test framework implementation dependencies")
+            .withAdvice("Declare the desired test framework directly on the test suite or explicitly declare the test framework implementation dependencies on the test's runtime classpath.")
+            .willBeRemovedInGradle9()
+            .withUpgradeGuideSection(8, "test_framework_implementation_dependencies")
+            .nagUser();
+
+        if (isModule) {
+            return new ForkedTestClasspath(
+                pathWithAdditionalModules(classpath, additional.applicationClasspath),
+                pathWithAdditionalModules(modulepath, additional.applicationModulepath),
+                withImplementation(loadDistributionUrls(additional.implementationClasspath)),
+                loadDistributionUrls(additional.implementationModulepath)
+            );
+        } else {
+            // For non-module tests, add all additional distribution modules to the classpath.
+            List<TestFrameworkDistributionModule> additionalApplicationClasspath = ImmutableList.<TestFrameworkDistributionModule>builder()
+                .addAll(additional.applicationClasspath)
+                .addAll(additional.applicationModulepath)
+                .build();
+            List<TestFrameworkDistributionModule> additionalImplementationClasspath = ImmutableList.<TestFrameworkDistributionModule>builder()
+                .addAll(additional.implementationClasspath)
+                .addAll(additional.implementationModulepath)
+                .build();
+
+            return new ForkedTestClasspath(
+                pathWithAdditionalModules(classpath, additionalApplicationClasspath),
+                ImmutableList.copyOf(modulepath),
+                withImplementation(loadDistributionUrls(additionalImplementationClasspath)),
+                ImmutableList.of()
+            );
+        }
+    }
+
+    /**
+     * Constructs the implementation classpath required by the Gradle testing infrastructure
+     * while also mixing-in the additional implementation classpath jars required by the
+     */
+    private ImmutableList<URL> withImplementation(List<URL> additionalImplementationClasspath) {
+        return ImmutableList.copyOf(CollectionUtils.flattenCollections(URL.class,
+            moduleRegistry.getModule("gradle-core-api").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-worker-processes").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-core").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-logging").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-logging-api").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-messaging").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-files").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-file-temp").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-hashing").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-base-services").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-enterprise-logging").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-enterprise-workers").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-cli").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-native").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-testing-base").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-testing-jvm-infrastructure").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-testing-junit-platform").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-process-services").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getModule("gradle-build-operations").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getExternalModule("slf4j-api").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getExternalModule("jul-to-slf4j").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getExternalModule("native-platform").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getExternalModule("kryo").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getExternalModule("commons-lang").getImplementationClasspath().getAsURLs(),
+            moduleRegistry.getExternalModule("javax.inject").getImplementationClasspath().getAsURLs(),
+            additionalImplementationClasspath
+        ));
+    }
+
+    /**
+     * Create a classpath or modulepath, as a list of files, given both the files provided by the test spec and a list of
+     * modules to load from the Gradle distribution.
+     *
+     * @param testFiles A set of jars, as given from a {@link JvmTestExecutionSpec}'s classpath or modulePath.
+     * @param additionalModules The names of any additional modules to load from the Gradle distribution.
+     *
+     * @return A set of files representing the constructed classpath or modulePath.
+     */
+    private ImmutableList<File> pathWithAdditionalModules(Iterable<? extends File> testFiles, List<TestFrameworkDistributionModule> additionalModules) {
+        return ImmutableList.<File>builder()
+            .addAll(testFiles)
+            .addAll(loadDistributionFiles(additionalModules))
+            .build();
+    }
+
+    private ImmutableList<File> loadDistributionFiles(List<TestFrameworkDistributionModule> moduleNames) {
+        return loadFromDistribution(moduleNames, ClassPath::getAsFiles);
+    }
+
+    private ImmutableList<URL> loadDistributionUrls(List<TestFrameworkDistributionModule> moduleNames) {
+        return loadFromDistribution(moduleNames, ClassPath::getAsURLs);
+    }
+
+    private <T> ImmutableList<T> loadFromDistribution(List<TestFrameworkDistributionModule> moduleNames, Function<ClassPath, List<T>> extractor) {
+        ImmutableList.Builder<T> outputFiles = ImmutableList.builder();
+
+        if (LOGGER.isDebugEnabled() && !moduleNames.isEmpty()) {
+            LOGGER.debug("Loaded additional modules from the Gradle distribution: " + Joiner.on(",").join(moduleNames));
+        }
+
+        for (TestFrameworkDistributionModule module : moduleNames) {
+            ClassPath cp = moduleRegistry.getExternalModule(module.getModuleName()).getImplementationClasspath();
+            outputFiles.addAll(extractor.apply(cp));
+        }
+
+        return outputFiles.build();
+    }
+
+    private static class AdditionalClasspath {
+
+        public final List<TestFrameworkDistributionModule> applicationClasspath;
+        public final List<TestFrameworkDistributionModule> applicationModulepath;
+        public final List<TestFrameworkDistributionModule> implementationClasspath;
+        public final List<TestFrameworkDistributionModule> implementationModulepath;
+
+        public AdditionalClasspath(
+            List<TestFrameworkDistributionModule> applicationClasspath,
+            List<TestFrameworkDistributionModule> applicationModulepath,
+            List<TestFrameworkDistributionModule> implementationClasspath,
+            List<TestFrameworkDistributionModule> implementationModulepath
+        ) {
+            this.applicationClasspath = applicationClasspath;
+            this.applicationModulepath = applicationModulepath;
+            this.implementationClasspath = implementationClasspath;
+            this.implementationModulepath = implementationModulepath;
+        }
+
+        public boolean isEmpty() {
+            return applicationClasspath.isEmpty() && applicationModulepath.isEmpty() &&
+                implementationClasspath.isEmpty() && implementationModulepath.isEmpty();
+        }
+    }
+
+    /**
+     * Filters the provided {@code unfiltered} additional classpath to only contain modules which are not already
+     * present in {@code classpath} and {@code modulepath}. This operates in a two-step process. First, it attempts
+     * to parse the names the jars in the provided classpath to quickly determine if the additional modules already
+     * exist. This is brittle and prone to errors, but much faster than the second step. If this step fails to filter
+     * all additional modules, the second step creates a {@link ClassLoader} based on the provided classpath and
+     * modulepath and attempts to discover the modules which are already present.
+     */
+    private AdditionalClasspath filterAdditionalClasspath(Iterable<? extends File> classpath, Iterable<? extends File> modulepath, AdditionalClasspath unfiltered) {
+        AdditionalClasspath fastFiltered = filterFast(classpath, modulepath, unfiltered);
+
+        if (fastFiltered.isEmpty()) {
+            return fastFiltered;
+        }
+
+        return filterSlow(classpath, modulepath, fastFiltered);
+    }
+
+    /**
+     * Filters additional modules based on jar file names.
+     */
+    private AdditionalClasspath filterFast(Iterable<? extends File> classpath, Iterable<? extends File> modulepath, AdditionalClasspath unfiltered) {
+        AdditionalClasspath mutable = new AdditionalClasspath(
+            new ArrayList<>(unfiltered.applicationClasspath),
+            new ArrayList<>(unfiltered.applicationModulepath),
+            new ArrayList<>(unfiltered.implementationClasspath),
+            new ArrayList<>(unfiltered.implementationModulepath)
+        );
+
+        // Filter additional modules which are provided by the classpath
+        Iterator<? extends File> it = classpath.iterator();
+        while (it.hasNext() && !mutable.isEmpty()) {
+            String name = it.next().getName();
+            mutable.applicationClasspath.removeIf(module -> module.getJarFilePattern().matcher(name).matches());
+            mutable.applicationModulepath.removeIf(module -> module.getJarFilePattern().matcher(name).matches());
+            mutable.implementationClasspath.removeIf(module -> module.getJarFilePattern().matcher(name).matches());
+            mutable.implementationModulepath.removeIf(module -> module.getJarFilePattern().matcher(name).matches());
+        }
+
+        // Filter additional modules which are provided by the modulepath
+        it = modulepath.iterator();
+        while (it.hasNext() && !mutable.isEmpty()) {
+            String name = it.next().getName();
+            mutable.applicationClasspath.removeIf(module -> module.getJarFilePattern().matcher(name).matches());
+            mutable.applicationModulepath.removeIf(module -> module.getJarFilePattern().matcher(name).matches());
+            mutable.implementationClasspath.removeIf(module -> module.getJarFilePattern().matcher(name).matches());
+            mutable.implementationModulepath.removeIf(module -> module.getJarFilePattern().matcher(name).matches());
+        }
+
+        return mutable;
+    }
+
+    /**
+     * Filters additional modules by constructing a {@link ClassLoader} and attempting to load classes from the additional modules.
+     */
+    private AdditionalClasspath filterSlow(Iterable<? extends File> classpath, Iterable<? extends File> modulepath, AdditionalClasspath unfiltered) {
+        try (ClassDetector classDetector = classDetectorFactory.create(classpath, modulepath)) {
+            return new AdditionalClasspath(
+                classDetector.withoutDetectedModules(unfiltered.applicationClasspath),
+                classDetector.withoutDetectedModules(unfiltered.applicationModulepath),
+                classDetector.withoutDetectedModules(unfiltered.implementationClasspath),
+                classDetector.withoutDetectedModules(unfiltered.implementationModulepath)
+            );
+        } catch (IOException e) {
+            throw UncheckedException.throwAsUncheckedException(e);
+        }
+    }
+
+    public interface ClassDetector extends Closeable {
+        boolean hasClass(String className);
+
+        default List<TestFrameworkDistributionModule> withoutDetectedModules(List<TestFrameworkDistributionModule> modules) {
+            ImmutableList.Builder<TestFrameworkDistributionModule> builder = ImmutableList.builder();
+            for (TestFrameworkDistributionModule module : modules) {
+                if (!hasClass(module.getExampleClassName())) {
+                    builder.add(module);
+                }
+            }
+            return builder.build();
+        }
+    }
+
+    public interface ClassDetectorFactory {
+        ClassDetector create(Iterable<? extends File> classpath, Iterable<? extends File> modulepath);
+    }
+
+    public static class ClassLoadingClassDetector implements ClassDetector {
+        private final URLClassLoader classLoader;
+
+        public ClassLoadingClassDetector(Iterable<? extends File> classpath, Iterable<? extends File> modulepath) {
+            ClassPath cp = DefaultClassPath.of(Iterables.concat(classpath, modulepath));
+            classLoader = new URLClassLoader(cp.getAsURLArray());
+        }
+
+        @Override
+        public boolean hasClass(String className) {
+            // Load the class resource instead of calling `loadClass` in order to
+            // avoid parsing the entire class file and any referenced classes.
+            String path = className.replace('.', '/').concat(".class");
+            return classLoader.findResource(path) != null;
+        }
+
+        @Override
+        public void close() throws IOException {
+            classLoader.close();
+        }
+    }
+}
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
index 0494d17..b91d223 100644
--- a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/junit/JUnitTestFramework.java
@@ -16,11 +16,15 @@
 
 package org.gradle.api.internal.tasks.testing.junit;
 
+import com.google.common.collect.Sets;
 import org.gradle.api.Action;
 import org.gradle.api.internal.tasks.testing.TestFramework;
+import org.gradle.api.internal.tasks.testing.TestFrameworkDistributionModule;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
 import org.gradle.api.internal.tasks.testing.detection.ClassFileExtractionManager;
 import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
 import org.gradle.api.tasks.testing.Test;
 import org.gradle.api.tasks.testing.TestFilter;
 import org.gradle.api.tasks.testing.junit.JUnitOptions;
@@ -32,9 +36,21 @@
 import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 @UsedByScanPlugin("test-retry")
 public class JUnitTestFramework implements TestFramework {
+    private static final Logger LOGGER = Logging.getLogger(JUnitTestFramework.class);
+
+    private static final List<TestFrameworkDistributionModule> DISTRIBUTION_MODULES =
+        Collections.singletonList(new TestFrameworkDistributionModule(
+            "junit",
+            Pattern.compile("junit-4.*\\.jar"),
+            "org.junit.runner.Runner"
+        ));
+
     private JUnitOptions options;
     private JUnitDetector detector;
     private final DefaultTestFilter filter;
@@ -69,6 +85,7 @@ public TestFramework copyWithFilters(TestFilter newTestFilters) {
 
     @Override
     public WorkerTestClassProcessorFactory getProcessorFactory() {
+        validateOptions();
         return new JUnitTestClassProcessorFactory(new JUnitSpec(
             filter.toSpec(), options.getIncludeCategories(), options.getExcludeCategories()));
     }
@@ -83,13 +100,8 @@ public Action<WorkerProcessBuilder> getWorkerConfigurationAction() {
     }
 
     @Override
-    public List<String> getTestWorkerApplicationClasses() {
-        return Collections.singletonList("junit");
-    }
-
-    @Override
-    public List<String> getTestWorkerApplicationModules() {
-        return Collections.emptyList();
+    public List<TestFrameworkDistributionModule> getWorkerImplementationClasspathModules() {
+        return DISTRIBUTION_MODULES;
     }
 
     @Override
@@ -118,4 +130,20 @@ public void close() throws IOException {
         detector = null;
     }
 
+    private void validateOptions() {
+        Set<String> intersection = Sets.newHashSet(options.getIncludeCategories());
+        intersection.retainAll(options.getExcludeCategories());
+        if (!intersection.isEmpty()) {
+            if (intersection.size() == 1) {
+                LOGGER.warn("The category '" + intersection.iterator().next() + "' is both included and excluded.  " +
+                    "This will result in the category being excluded, which may not be what was intended.  " +
+                    "Please either include or exclude the category but not both.");
+            } else {
+                String allCategories = intersection.stream().sorted().map(s -> "'" + s + "'").collect(Collectors.joining(", "));
+                LOGGER.warn("The categories " + allCategories + " are both included and excluded.  " +
+                    "This will result in the categories being excluded, which may not be what was intended. " +
+                    "Please either include or exclude the categories but not both.");
+            }
+        }
+    }
 }
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/junitplatform/JUnitPlatformTestFramework.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/junitplatform/JUnitPlatformTestFramework.java
index 7161b2d..90dba3a 100644
--- a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/junitplatform/JUnitPlatformTestFramework.java
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/junitplatform/JUnitPlatformTestFramework.java
@@ -17,12 +17,16 @@
 package org.gradle.api.internal.tasks.testing.junitplatform;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 import org.gradle.api.Action;
 import org.gradle.api.JavaVersion;
 import org.gradle.api.internal.tasks.testing.TestFramework;
+import org.gradle.api.internal.tasks.testing.TestFrameworkDistributionModule;
 import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory;
 import org.gradle.api.internal.tasks.testing.detection.TestFrameworkDetector;
 import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
 import org.gradle.api.tasks.testing.TestFilter;
 import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions;
 import org.gradle.internal.jvm.UnsupportedJavaRuntimeException;
@@ -30,11 +34,34 @@
 import org.gradle.process.internal.worker.WorkerProcessBuilder;
 
 import java.io.IOException;
-import java.util.Collections;
 import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 @UsedByScanPlugin("test-retry")
 public class JUnitPlatformTestFramework implements TestFramework {
+    private static final Logger LOGGER = Logging.getLogger(JUnitPlatformTestFramework.class);
+
+    private static final List<TestFrameworkDistributionModule> DISTRIBUTION_MODULES =
+        ImmutableList.of(
+            new TestFrameworkDistributionModule(
+                "junit-platform-engine",
+                Pattern.compile("junit-platform-engine-1.*\\.jar"),
+                "org.junit.platform.engine.DiscoverySelector"
+            ),
+            new TestFrameworkDistributionModule(
+                "junit-platform-launcher",
+                Pattern.compile("junit-platform-launcher-1.*\\.jar"),
+                "org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder"
+            ),
+            new TestFrameworkDistributionModule(
+                "junit-platform-commons",
+                Pattern.compile("junit-platform-commons-1.*\\.jar"),
+                "org.junit.platform.commons.util.ReflectionUtils"
+            )
+        );
+
     private final JUnitPlatformOptions options;
     private final DefaultTestFilter filter;
     private final boolean useImplementationDependencies;
@@ -67,6 +94,7 @@ public WorkerTestClassProcessorFactory getProcessorFactory() {
         if (!JavaVersion.current().isJava8Compatible()) {
             throw new UnsupportedJavaRuntimeException("Running JUnit Platform requires Java 8+, please configure your test java executable with Java 8 or higher.");
         }
+        validateOptions();
         return new JUnitPlatformTestClassProcessorFactory(new JUnitPlatformSpec(
             filter.toSpec(), options.getIncludeEngines(), options.getExcludeEngines(),
             options.getIncludeTags(), options.getExcludeTags()
@@ -79,13 +107,8 @@ public Action<WorkerProcessBuilder> getWorkerConfigurationAction() {
     }
 
     @Override
-    public List<String> getTestWorkerApplicationClasses() {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public List<String> getTestWorkerApplicationModules() {
-        return ImmutableList.of("junit-platform-engine", "junit-platform-launcher", "junit-platform-commons");
+    public List<TestFrameworkDistributionModule> getWorkerApplicationModulepathModules() {
+        return DISTRIBUTION_MODULES;
     }
 
     @Override
@@ -108,4 +131,20 @@ public void close() throws IOException {
         // this test framework doesn't hold any state
     }
 
+    private void validateOptions() {
+        Set<String> intersection = Sets.newHashSet(options.getIncludeTags());
+        intersection.retainAll(options.getExcludeTags());
+        if (!intersection.isEmpty()) {
+            if (intersection.size() == 1) {
+                LOGGER.warn("The tag '" + intersection.iterator().next() + "' is both included and excluded.  " +
+                    "This will result in the tag being excluded, which may not be what was intended.  " +
+                    "Please either include or exclude the tag but not both.");
+            } else {
+                String allTags = intersection.stream().sorted().map(s -> "'" + s + "'").collect(Collectors.joining(", "));
+                LOGGER.warn("The tags " + allTags + " are both included and excluded.  " +
+                    "This will result in the tags being excluded, which may not be what was intended.  " +
+                    "Please either include or exclude the tags but not both.");
+            }
+        }
+    }
 }
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/testng/TestNGTestFramework.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/testng/TestNGTestFramework.java
index bf7e8d5..2c88284 100644
--- a/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/testng/TestNGTestFramework.java
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/testng/TestNGTestFramework.java
@@ -33,7 +33,6 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Callable;
 
@@ -112,16 +111,6 @@ public Action<WorkerProcessBuilder> getWorkerConfigurationAction() {
     }
 
     @Override
-    public List<String> getTestWorkerApplicationClasses() {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public List<String> getTestWorkerApplicationModules() {
-        return Collections.emptyList();
-    }
-
-    @Override
     public boolean getUseDistributionDependencies() {
         // We have no (default) implementation dependencies (see above).
         // The user must add their TestNG dependency to the test's runtimeClasspath themselves
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/Test.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/Test.java
index d9d16a37..1e1317f 100644
--- a/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/Test.java
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/Test.java
@@ -30,7 +30,6 @@
 import org.gradle.api.file.FileTreeElement;
 import org.gradle.api.internal.DocumentationRegistry;
 import org.gradle.api.internal.classpath.ModuleRegistry;
-import org.gradle.api.internal.provider.DefaultProperty;
 import org.gradle.api.internal.tasks.testing.JvmTestExecutionSpec;
 import org.gradle.api.internal.tasks.testing.TestExecutableUtils;
 import org.gradle.api.internal.tasks.testing.TestExecuter;
@@ -197,7 +196,6 @@ public Object call() {
         javaLauncher = objectFactory.property(JavaLauncher.class).convention(createJavaLauncherConvention());
         javaLauncher.finalizeValueOnRead();
         testFramework = objectFactory.property(TestFramework.class).convention(new JUnitTestFramework(this, (DefaultTestFilter) getFilter(), true));
-        testFramework.finalizeValueOnRead();
     }
 
     private Provider<JavaLauncher> createJavaLauncherConvention() {
@@ -1057,16 +1055,20 @@ public void useTestNG(Action<? super TestNGOptions> testFrameworkConfigure) {
         applyOptions(TestNGOptions.class, testFrameworkConfigure);
     }
 
+    /**
+     * Set the framework, only if it is being changed to a new value.
+     *
+     * If we are setting a framework to its existing value, no-op so as not to overwrite existing options here.
+     * We need to allow this especially for the default test task, so that existing builds that configure options and
+     * then call useJunit() don't clear out their options.
+     *
+     * @param testFramework
+     */
     void useTestFramework(TestFramework testFramework) {
-        if (((DefaultProperty<?>) this.testFramework).isFinalized()) {
-            Class<?> currentFramework = this.testFramework.get().getClass();
-            Class<?> newFramework = testFramework.getClass();
-            if (currentFramework == newFramework) {
-                // We are setting a finalized framework to its existing value, no-op so as not to trigger a failure here.
-                // We need to allow this especially for the default test task, so that existing builds that configure options and
-                // then call useJunit() afterwards don't fail
-                return;
-            }
+        Class<?> currentFramework = this.testFramework.get().getClass();
+        Class<?> newFramework = testFramework.getClass();
+        if (currentFramework == newFramework) {
+            return;
         }
 
         this.testFramework.set(testFramework);
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/junit/package-info.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/junit/package-info.java
index 9dad92a..54fdd3d 100644
--- a/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/junit/package-info.java
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/junit/package-info.java
@@ -17,4 +17,4 @@
 /**
  * JUnit specific testing classes.
  */
-package org.gradle.api.tasks.testing.junit;
\ No newline at end of file
+package org.gradle.api.tasks.testing.junit;
diff --git a/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/testng/package-info.java b/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/testng/package-info.java
index 97d61bb..f1fa698 100644
--- a/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/testng/package-info.java
+++ b/subprojects/testing-jvm/src/main/java/org/gradle/api/tasks/testing/testng/package-info.java
@@ -17,4 +17,4 @@
 /**
  * TestNG specific testing classes.
  */
-package org.gradle.api.tasks.testing.testng;
\ No newline at end of file
+package org.gradle.api.tasks.testing.testng;
diff --git a/subprojects/testing-jvm/src/test/groovy/org/gradle/api/internal/tasks/testing/detection/ForkedTestClasspathFactoryTest.groovy b/subprojects/testing-jvm/src/test/groovy/org/gradle/api/internal/tasks/testing/detection/ForkedTestClasspathFactoryTest.groovy
new file mode 100644
index 0000000..ceaeac9
--- /dev/null
+++ b/subprojects/testing-jvm/src/test/groovy/org/gradle/api/internal/tasks/testing/detection/ForkedTestClasspathFactoryTest.groovy
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2023 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.api.internal.tasks.testing.detection
+
+
+import org.gradle.api.internal.classpath.Module
+import org.gradle.api.internal.classpath.ModuleRegistry
+import org.gradle.api.internal.tasks.testing.TestFramework
+import org.gradle.api.internal.tasks.testing.TestFrameworkDistributionModule
+import org.gradle.internal.classpath.ClassPath
+import spock.lang.Specification
+
+import java.util.regex.Pattern
+
+/**
+ * Tests {@link ForkedTestClasspathFactory}.
+ */
+class ForkedTestClasspathFactoryTest extends Specification {
+
+    // The number of internal and external implementation jars loaded from the distribution regardless of framework.
+    private static final int NUM_INTERNAL_JARS = 19
+    private static final int NUM_EXTERNAL_JARS = 6
+
+    ModuleRegistry moduleRegistry = Mock(ModuleRegistry) {
+        getModule(_) >> { module(it[0], false) }
+        getExternalModule(_) >> { module(it[0], true) }
+    }
+
+    def runtimeClasses = Spy(TestClassDetector)
+    def classDetectorFactory = Mock(ForkedTestClasspathFactory.ClassDetectorFactory) {
+        create(_, _) >> { runtimeClasses }
+    }
+    ForkedTestClasspathFactory underTest = new ForkedTestClasspathFactory(moduleRegistry, classDetectorFactory)
+
+    def "creates a limited implementation classpath"() {
+        when:
+        def framework = newFramework(false, [], [], [], [])
+        def classpath = underTest.create([new File("cls.jar")], [new File("mod.jar")], framework, false)
+
+        then:
+        classpath.applicationClasspath == [new File("cls.jar")]
+        classpath.applicationModulepath == [new File("mod.jar")]
+        classpath.implementationClasspath.size() == 25
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-internal.jar") }.size() == NUM_INTERNAL_JARS
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-external.jar") }.size() == NUM_EXTERNAL_JARS
+        classpath.implementationModulepath.isEmpty()
+
+        0 * classDetectorFactory._
+    }
+
+    def "adds framework dependencies to classpath when test is not module"() {
+        when:
+        def framework = newFramework(true, ["app-cls"], ["app-mod"], ["impl-cls"], ["impl-mod"])
+        def classpath = underTest.create([new File("cls.jar")], [new File("mod.jar")], framework, false)
+
+        then:
+        classpath.applicationClasspath == [new File("cls.jar"), new File("app-cls-external.jar"), new File("app-mod-external.jar")]
+        classpath.applicationModulepath == [new File("mod.jar")]
+        classpath.implementationClasspath.size() == NUM_INTERNAL_JARS + NUM_EXTERNAL_JARS + 2
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-internal.jar") }.size() == NUM_INTERNAL_JARS
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-external.jar") }.size() == NUM_EXTERNAL_JARS + 2
+        classpath.implementationClasspath.takeRight(2) == [new URL("file://impl-cls-external.jar"), new URL("file://impl-mod-external.jar")]
+        classpath.implementationModulepath.isEmpty()
+    }
+
+    def "adds framework dependencies to classpath and modulepath when test is module"() {
+        when:
+        def framework = newFramework(true, ["app-cls"], ["app-mod"], ["impl-cls"], ["impl-mod"])
+        def classpath = underTest.create([new File("cls.jar")], [new File("mod.jar")], framework, true)
+
+        then:
+        classpath.applicationClasspath == [new File("cls.jar"), new File("app-cls-external.jar")]
+        classpath.applicationModulepath == [new File("mod.jar"), new File("app-mod-external.jar")]
+        classpath.implementationClasspath.size() == NUM_INTERNAL_JARS + NUM_EXTERNAL_JARS + 1
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-internal.jar") }.size() == NUM_INTERNAL_JARS
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-external.jar") }.size() == NUM_EXTERNAL_JARS + 1
+        classpath.implementationClasspath.last() == new URL("file://impl-cls-external.jar")
+        classpath.implementationModulepath == [new URL("file://impl-mod-external.jar")]
+    }
+
+    def "does not load framework dependencies from distribution if they are on the test runtime classpath already with matching jar names"() {
+        when:
+        def framework = newFramework(true, ["app-cls"], ["app-mod"], ["impl-cls"], ["impl-mod"])
+        def classpath = underTest.create([
+            new File("app-cls-1.0.jar"), new File("app-mod-1.0.jar"), new File("impl-cls-1.0.jar"), new File("impl-mod-1.0.jar")
+        ], [], framework, true)
+
+        then:
+        classpath.applicationClasspath == [new File("app-cls-1.0.jar"), new File("app-mod-1.0.jar"), new File("impl-cls-1.0.jar"), new File("impl-mod-1.0.jar")]
+        classpath.applicationModulepath.isEmpty()
+        classpath.implementationClasspath.size() == NUM_INTERNAL_JARS + NUM_EXTERNAL_JARS
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-internal.jar") }.size() == NUM_INTERNAL_JARS
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-external.jar") }.size() == NUM_EXTERNAL_JARS
+        classpath.implementationModulepath.isEmpty()
+
+        0 * classDetectorFactory._
+    }
+
+    def "does not load framework dependencies from distribution if they are on the test runtime modulepath already with matching jar names"() {
+        when:
+        def framework = newFramework(true, ["app-cls"], ["app-mod"], ["impl-cls"], ["impl-mod"])
+        def classpath = underTest.create([], [
+            new File("app-cls-1.0.jar"), new File("app-mod-1.0.jar"), new File("impl-cls-1.0.jar"), new File("impl-mod-1.0.jar")
+        ], framework, true)
+
+        then:
+        classpath.applicationClasspath.isEmpty()
+        classpath.applicationModulepath == [new File("app-cls-1.0.jar"), new File("app-mod-1.0.jar"), new File("impl-cls-1.0.jar"), new File("impl-mod-1.0.jar")]
+        classpath.implementationClasspath.size() == NUM_INTERNAL_JARS + NUM_EXTERNAL_JARS
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-internal.jar") }.size() == NUM_INTERNAL_JARS
+        classpath.implementationClasspath.findAll { it.toString().endsWith("-external.jar") }.size() == NUM_EXTERNAL_JARS
+        classpath.implementationModulepath.isEmpty()
+
+        0 * classDetectorFactory._
+    }
+
+    def "loads all framework dependencies from distribution even if some are already available with matching jar names"() {
+        when:
+        def cpFiles = cp.collect { new File("$it-1.0.jar") }
+        def mpFiles = mp.collect { new File("$it-1.0.jar") }
+
+        def framework = newFramework(true, ["a", "b"], ["c", "d"], ["e", "f"], ["g", "h"])
+        def classpath = underTest.create(cpFiles, mpFiles, framework, true)
+
+        then:
+        if (loadsAll) {
+            assert classpath.applicationClasspath.takeRight(2) == ["a", "b"].collect { new File("$it-external.jar") }
+            assert classpath.applicationModulepath.takeRight(2) == ["c", "d"].collect { new File("$it-external.jar") }
+            assert classpath.implementationClasspath.takeRight(2) == ["e", "f"].collect { new URL("file://$it-external.jar") }
+            assert classpath.implementationModulepath == ["g", "h"].collect { new URL("file://$it-external.jar") }
+        } else {
+            assert classpath.applicationClasspath == cpFiles
+            assert classpath.applicationModulepath == mpFiles
+            assert classpath.implementationClasspath.size() == 25
+            assert classpath.implementationModulepath.isEmpty()
+        }
+
+        where:
+        cp                                       | mp                                       | loadsAll
+        []                                       | []                                       | true
+        ["test"]                                 | []                                       | true
+        ["m", "q"]                               | ["l", "t"]                               | true
+        ["a"]                                    | []                                       | true
+        []                                       | ["a"]                                    | true
+        ["c"]                                    | []                                       | true
+        []                                       | ["c"]                                    | true
+        ["e"]                                    | []                                       | true
+        []                                       | ["e"]                                    | true
+        ["g"]                                    | []                                       | true
+        []                                       | ["g"]                                    | true
+        ["test", "a"]                            | []                                       | true
+        []                                       | ["test", "a"]                            | true
+        ["a", "b", "c", "d"]                     | ["e", "f", "g", "h"]                     | false
+        ["a", "b", "c", "d", "e", "f", "g", "h"] | []                                       | false
+        []                                       | ["a", "b", "c", "d", "e", "f", "g", "h"] | false
+        ["a", "c", "e", "g"]                     | ["b", "d", "f", "h"]                     | false
+        ["b", "d", "f", "h"]                     | ["a", "c", "e", "g"]                     | false
+    }
+
+    def "can detect class names from classloader if jar names not found"() {
+        given:
+        classes.forEach(it -> runtimeClasses.add("org.$it"))
+
+        when:
+        def cpFiles = cp.collect { new File("$it-1.0.jar") }
+        def mpFiles = mp.collect { new File("$it-1.0.jar") }
+
+        def framework = newFramework(true, ["a", "b"], ["c", "d"], ["e", "f"], ["g", "h"])
+        def classpath = underTest.create(cpFiles, mpFiles, framework, true)
+
+        then:
+        (["a", "b", "c", "d", "e", "f", "g", "h"] - (cp + mp)).forEach {
+            1 * runtimeClasses.hasClass("org.$it")
+        }
+        0 * runtimeClasses.hasClass(_)
+
+        then:
+        if (loadsAll) {
+            assert classpath.applicationClasspath.takeRight(2) == ["a", "b"].collect { new File("$it-external.jar") }
+            assert classpath.applicationModulepath.takeRight(2) == ["c", "d"].collect { new File("$it-external.jar") }
+            assert classpath.implementationClasspath.takeRight(2) == ["e", "f"].collect { new URL("file://$it-external.jar") }
+            assert classpath.implementationModulepath == ["g", "h"].collect { new URL("file://$it-external.jar") }
+        } else {
+            assert classpath.applicationClasspath == cpFiles
+            assert classpath.applicationModulepath == mpFiles
+            assert classpath.implementationClasspath.size() == 25
+            assert classpath.implementationModulepath.isEmpty()
+        }
+
+        where:
+        cp                   | mp                   | classes                                   | loadsAll
+        []                   | []                   | []                                        | true
+        []                   | []                   | ["a"]                                     | true
+        []                   | []                   | ["b"]                                     | true
+        []                   | []                   | ["c"]                                     | true
+        []                   | []                   | ["d"]                                     | true
+        []                   | []                   | ["e"]                                     | true
+        []                   | []                   | ["f"]                                     | true
+        []                   | []                   | ["g"]                                     | true
+        []                   | []                   | ["h"]                                     | true
+        ["b", "c", "f", "g"] | []                   | ["b", "c", "f", "g"]                      | true
+        []                   | ["b", "c", "f", "g"] | ["b", "c", "f", "g"]                      | true
+        ["a", "d", "e", "h"] | []                   | ["b", "c", "f", "g"]                      | false
+        []                   | ["a", "d", "e", "h"] | ["b", "c", "f", "g"]                      | false
+        []                   | []                   | ["a", "b", "c", "d", "e", "f", "g", "h"]  | false
+    }
+
+    def module(String module, boolean external) {
+        String extra = external ? "external" : "internal"
+        return Mock(Module) {
+            getImplementationClasspath() >> {
+                Mock(ClassPath) {
+                    getAsURLs() >> { [new URL("file://${module}-${extra}.jar")] }
+                    getAsFiles() >> { [new File("${module}-${extra}.jar")] }
+                }
+            }
+        }
+    }
+
+    TestFramework newFramework(
+        boolean useDependencies,
+        List<String> appClasses,
+        List<String> appModules,
+        List<String> implClasses,
+        List<String> implModules
+    ) {
+        def asDistModule = { String name ->
+            new TestFrameworkDistributionModule(name, Pattern.compile("$name-1.*\\.jar"), "org.$name")
+        }
+        return Mock(TestFramework) {
+            getUseDistributionDependencies() >> useDependencies
+            getWorkerApplicationClasspathModules() >> appClasses.collect { asDistModule(it) }
+            getWorkerApplicationModulepathModules() >> appModules.collect { asDistModule(it) }
+            getWorkerImplementationClasspathModules() >> implClasses.collect { asDistModule(it) }
+            getWorkerImplementationModulepathModules() >> implModules.collect { asDistModule(it) }
+        }
+    }
+
+    static class TestClassDetector implements ForkedTestClasspathFactory.ClassDetector {
+
+        Set<String> testClasses = []
+
+        void add(String className) {
+            testClasses.add(className)
+        }
+
+        @Override
+        boolean hasClass(String className) {
+            return testClasses.contains(className)
+        }
+
+        @Override
+        void close() throws IOException {}
+    }
+}
diff --git a/subprojects/testing-jvm/src/testFixtures/groovy/org/gradle/testing/fixture/JUnitCoverage.groovy b/subprojects/testing-jvm/src/testFixtures/groovy/org/gradle/testing/fixture/JUnitCoverage.groovy
index a7da249..6e54117 100644
--- a/subprojects/testing-jvm/src/testFixtures/groovy/org/gradle/testing/fixture/JUnitCoverage.groovy
+++ b/subprojects/testing-jvm/src/testFixtures/groovy/org/gradle/testing/fixture/JUnitCoverage.groovy
@@ -21,20 +21,20 @@
  * VINTAGE is JUnit Vintage engine which supports JUnit 4 tests on top of JUnit Platform, i.e. org.junit.vintage:junit-vintage-engine:5.7.1
  */
 class JUnitCoverage {
-    final static String NEWEST = '4.13'
-    final static String LATEST_JUNIT5_VERSION = '5.8.2'
+    final static String LATEST_JUNIT3_VERSION = '3.8.2'
+    final static String LATEST_JUNIT4_VERSION = '4.13.2'
+    final static String LATEST_JUNIT5_VERSION = '5.9.0'
     final static String LATEST_JUPITER_VERSION = LATEST_JUNIT5_VERSION
     final static String LATEST_VINTAGE_VERSION = LATEST_JUNIT5_VERSION
     final static String LATEST_PLATFORM_VERSION = '1.8.2'
     final static String LATEST_ARCHUNIT_VERSION = '0.22.0'
-    final static String JUPITER = 'Jupiter:' + LATEST_JUPITER_VERSION
-    final static String VINTAGE = 'Vintage:' + LATEST_VINTAGE_VERSION
-    final static List<String> LARGE_COVERAGE = ['4.0', '4.4', '4.8.2', NEWEST]
-    final static List<String> IGNORE_ON_CLASS = ['4.4', '4.8.2', NEWEST]
-    final static List<String> ASSUMPTIONS = ['4.5', NEWEST]
-    final static List<String> CATEGORIES = ['4.8', NEWEST]
-    final static List<String> FILTER_JUNIT3_TESTS = ['3.8.1', '4.6', NEWEST]
-    final static List<String> JUNIT_4_LATEST = [NEWEST]
-    final static List<String> JUNIT_VINTAGE = [VINTAGE]
-    final static List<String> JUNIT_VINTAGE_JUPITER = [VINTAGE, JUPITER]
+    final static List<String> LARGE_COVERAGE = ['4.0', '4.4', '4.8.2', LATEST_JUNIT4_VERSION]
+    final static List<String> IGNORE_ON_CLASS = ['4.4', '4.8.2', LATEST_JUNIT4_VERSION]
+    final static List<String> ASSUMPTIONS = ['4.5', LATEST_JUNIT4_VERSION]
+    final static List<String> CATEGORIES = ['4.8', LATEST_JUNIT4_VERSION]
+    final static List<String> FILTER_JUNIT3_TESTS = [LATEST_JUNIT3_VERSION, '4.6', LATEST_JUNIT4_VERSION]
+    final static List<String> JUNIT_4_LATEST = [LATEST_JUNIT4_VERSION]
+    final static List<String> JUNIT_VINTAGE = [LATEST_VINTAGE_VERSION]
+    final static List<String> JUNIT_JUPITER = [LATEST_JUPITER_VERSION]
+    final static List<String> JUNIT_VINTAGE_JUPITER = [LATEST_VINTAGE_VERSION, LATEST_JUPITER_VERSION]
 }
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitDependentComponentsIntegrationSpec.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitDependentComponentsIntegrationSpec.groovy
index d0cc30e..13f6cb0 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitDependentComponentsIntegrationSpec.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitDependentComponentsIntegrationSpec.groovy
@@ -22,11 +22,11 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 
-@Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+@Requires(UnitTestPreconditions.CanInstallExecutable)
 @RequiresInstalledToolChain(ToolChainRequirement.SUPPORTS_32)
 class CUnitDependentComponentsIntegrationSpec extends AbstractInstalledToolChainIntegrationSpec {
 
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitIntegrationTest.groovy
index 4e6ba0d..8803fed 100755
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitIntegrationTest.groovy
@@ -24,12 +24,12 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.fixtures.app.CHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import spock.lang.Issue
 
-@Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+@Requires(UnitTestPreconditions.CanInstallExecutable)
 @RequiresInstalledToolChain(ToolChainRequirement.SUPPORTS_32)
 class CUnitIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
 
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitSamplesIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitSamplesIntegrationTest.groovy
index 7c77db0..873cc2a 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitSamplesIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/cunit/CUnitSamplesIntegrationTest.groovy
@@ -17,20 +17,20 @@
 
 package org.gradle.nativeplatform.test.cunit
 
-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
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
 
 import static org.junit.Assume.assumeTrue
 
-@Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+@Requires(UnitTestPreconditions.CanInstallExecutable)
 @RequiresInstalledToolChain(ToolChainRequirement.SUPPORTS_32)
 class CUnitSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     @Rule public final Sample cunit = sample(temporaryFolder, 'cunit')
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestDependentComponentsIntegrationSpec.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestDependentComponentsIntegrationSpec.groovy
index 0b4450e..2767e76 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestDependentComponentsIntegrationSpec.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestDependentComponentsIntegrationSpec.groovy
@@ -22,11 +22,11 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 
-@Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+@Requires(UnitTestPreconditions.CanInstallExecutable)
 @RequiresInstalledToolChain(ToolChainRequirement.SUPPORTS_32)
 class GoogleTestDependentComponentsIntegrationSpec extends AbstractInstalledToolChainIntegrationSpec {
 
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestIntegrationTest.groovy
index 74a42ae..8a5783d 100755
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestIntegrationTest.groovy
@@ -23,14 +23,14 @@
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.nativeplatform.fixtures.app.CppHelloWorldApp
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.internal.TextUtil
 import spock.lang.Issue
 
 import static org.gradle.util.internal.TextUtil.normaliseLineSeparators
 
-@Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+@Requires(UnitTestPreconditions.CanInstallExecutable)
 @RequiresInstalledToolChain(ToolChainRequirement.SUPPORTS_32)
 class GoogleTestIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
 
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestSamplesIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestSamplesIntegrationTest.groovy
index b30e770..68bcdd3 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestSamplesIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/googletest/GoogleTestSamplesIntegrationTest.groovy
@@ -17,19 +17,20 @@
 
 package org.gradle.nativeplatform.test.googletest
 
-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
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
 import org.gradle.test.fixtures.file.TestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Rule
-import static org.junit.Assume.*
 
-@Requires(TestPrecondition.CAN_INSTALL_EXECUTABLE)
+import static org.junit.Assume.assumeTrue
+
+@Requires(UnitTestPreconditions.CanInstallExecutable)
 @RequiresInstalledToolChain(ToolChainRequirement.SUPPORTS_32)
 class GoogleTestSamplesIntegrationTest extends AbstractInstalledToolChainIntegrationSpec {
     @Rule public final Sample googleTest = sample(temporaryFolder, 'google-test')
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/AbstractSwiftXCTestComponentIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/AbstractSwiftXCTestComponentIntegrationTest.groovy
index d9603c3..bfd3913 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/AbstractSwiftXCTestComponentIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/AbstractSwiftXCTestComponentIntegrationTest.groovy
@@ -25,8 +25,8 @@
 import org.gradle.nativeplatform.fixtures.app.Swift4XCTest
 import org.gradle.nativeplatform.fixtures.app.Swift5XCTest
 import org.gradle.nativeplatform.fixtures.app.XCTestSourceElement
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.junit.Assume
 
 abstract class AbstractSwiftXCTestComponentIntegrationTest extends AbstractSwiftComponentIntegrationTest implements SwiftTaskNames {
@@ -51,7 +51,7 @@
         outputContains("'${componentName}' component in project ':' does not target this operating system.")
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     @ToBeFixedForConfigurationCache
     def "does not compile and link LinuxMain.swift on macOS"() {
         given:
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/SwiftXCTestIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/SwiftXCTestIntegrationTest.groovy
index f3a4b64..5c06f71 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/SwiftXCTestIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/SwiftXCTestIntegrationTest.groovy
@@ -35,6 +35,7 @@
 import org.gradle.nativeplatform.fixtures.app.SwiftLibWithXCTest
 import org.gradle.nativeplatform.fixtures.app.SwiftSingleFileApp
 import org.gradle.nativeplatform.fixtures.app.SwiftSingleFileLibWithSingleXCTestSuite
+import org.gradle.nativeplatform.fixtures.app.SwiftXCTestWithDepAndCustomXCTestSuite
 import org.gradle.nativeplatform.fixtures.app.XCTestCaseElement
 import org.gradle.nativeplatform.fixtures.app.XCTestSourceElement
 import org.gradle.nativeplatform.fixtures.app.XCTestSourceFileElement
@@ -429,7 +430,7 @@
     }
 
     @ToBeFixedForConfigurationCache
-    def 'can build xctest bundle which transitively dependencies on other Swift libraries'() {
+    def 'can build xctest bundle which transitively depends on other Swift libraries'() {
         given:
         def app = new SwiftAppWithLibraries()
         settingsFile << """
@@ -456,16 +457,9 @@
         app.greeter.writeToProject(file('hello'))
         app.logger.writeToProject(file('log'))
 
-        file('src/test/swift/MainTest.swift') << """
-            import XCTest
-            import App
+        def test = new SwiftXCTestWithDepAndCustomXCTestSuite('bundle', 'Main','XCTAssert(main() == 0)', ['App'] as String[], [] as String[])
+        test.writeToProject(testDirectory)
 
-            public class MainTest : XCTestCase {
-                public func testMain() {
-                    XCTAssert(main() == 0)
-                }
-            }
-        """
         when:
         succeeds 'test'
 
@@ -516,16 +510,9 @@
         app.greeter.writeToProject(file('Sources/Hello'))
         app.logger.writeToProject(file('Sources/Log'))
 
-        file('Tests/AppTests/UtilTest.swift') << """
-            import XCTest
-            import App
+        def test = new SwiftXCTestWithDepAndCustomXCTestSuite('testing', 'Main', 'XCTAssert(main() == 0)', ['App'] as String[], [] as String[])
+        test.writeToProject(testDirectory)
 
-            public class MainTest : XCTestCase {
-                public func testMain() {
-                    XCTAssert(main() == 0)
-                }
-            }
-        """
         when:
         succeeds 'test'
 
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/XCTestTestFrameworkIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/XCTestTestFrameworkIntegrationTest.groovy
index ab95f71..84e08c7 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/XCTestTestFrameworkIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/XCTestTestFrameworkIntegrationTest.groovy
@@ -22,13 +22,13 @@
 import org.gradle.nativeplatform.fixtures.app.XCTestCaseElement
 import org.gradle.nativeplatform.fixtures.app.XCTestSourceElement
 import org.gradle.nativeplatform.fixtures.app.XCTestSourceFileElement
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testing.AbstractTestFrameworkIntegrationTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
 import static org.junit.Assume.assumeTrue
 
-@Requires(TestPrecondition.NOT_MAC_OS_X_M1)
+@Requires(UnitTestPreconditions.NotMacOsM1)
 class XCTestTestFrameworkIntegrationTest extends AbstractTestFrameworkIntegrationTest {
     def setup() {
         def toolChain = AvailableToolChains.getToolChain(ToolChainRequirement.SWIFTC)
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/XCTestWithApplicationDependenciesIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/XCTestWithApplicationDependenciesIntegrationTest.groovy
index 68cb46f..82a0c0a 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/XCTestWithApplicationDependenciesIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/XCTestWithApplicationDependenciesIntegrationTest.groovy
@@ -20,6 +20,7 @@
 import org.gradle.language.swift.SwiftTaskNames
 import org.gradle.nativeplatform.fixtures.RequiresInstalledToolChain
 import org.gradle.nativeplatform.fixtures.ToolChainRequirement
+import org.gradle.nativeplatform.fixtures.app.SwiftXCTestWithDepAndCustomXCTestSuite
 
 @RequiresInstalledToolChain(ToolChainRequirement.SWIFTC)
 class XCTestWithApplicationDependenciesIntegrationTest extends AbstractNativeUnitTestComponentDependenciesIntegrationTest implements SwiftTaskNames {
@@ -34,19 +35,14 @@
 """
         file("src/main/swift/App.swift") << """
             import Lib
-            
+
             class App {
                 var util = Util()
             }
 """
-        file("src/test/swift/Test.swift") << """
-            @testable import Root
-            import XCTest
-                        
-            class Test {
-                var app = App()
-            }
-"""
+        def testSource = new SwiftXCTestWithDepAndCustomXCTestSuite("app", "App", "XCTAssertNotNil(App().util)", [] as String[], ["Root"] as String[])
+        testSource.writeToProject(testDirectory)
+
         file("lib/src/main/swift/Util.swift") << """
             public class Util {
                 public init() { }
diff --git a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/plugins/XCTestPluginOnUnsupportedPlatformIntegrationTest.groovy b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/plugins/XCTestPluginOnUnsupportedPlatformIntegrationTest.groovy
index cb79d50..4889eec 100644
--- a/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/plugins/XCTestPluginOnUnsupportedPlatformIntegrationTest.groovy
+++ b/subprojects/testing-native/src/integTest/groovy/org/gradle/nativeplatform/test/xctest/plugins/XCTestPluginOnUnsupportedPlatformIntegrationTest.groovy
@@ -19,10 +19,10 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.nativeplatform.fixtures.HostPlatform
 import org.gradle.nativeplatform.fixtures.app.SwiftAppWithXCTest
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 
-@Requires(TestPrecondition.WINDOWS)
+@Requires(UnitTestPreconditions.Windows)
 class XCTestPluginOnUnsupportedPlatformIntegrationTest extends AbstractIntegrationSpec implements HostPlatform {
     def setup() {
         buildFile << "apply plugin: 'xctest'"
diff --git a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cpp/internal/DefaultCppTestExecutable.java b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cpp/internal/DefaultCppTestExecutable.java
index 623d16d..58b3550 100644
--- a/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cpp/internal/DefaultCppTestExecutable.java
+++ b/subprojects/testing-native/src/main/java/org/gradle/nativeplatform/test/cpp/internal/DefaultCppTestExecutable.java
@@ -18,12 +18,12 @@
 
 import org.gradle.api.Task;
 import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ConfigurationContainer;
 import org.gradle.api.file.ConfigurableFileCollection;
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.RegularFile;
 import org.gradle.api.file.RegularFileProperty;
+import org.gradle.api.internal.artifacts.configurations.RoleBasedConfigurationContainerInternal;
 import org.gradle.api.model.ObjectFactory;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.Provider;
@@ -56,7 +56,7 @@ public class DefaultCppTestExecutable extends DefaultCppBinary implements CppTes
     private final RegularFileProperty debuggerExecutableFile;
 
     @Inject
-    public DefaultCppTestExecutable(Names names, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, Configuration implementation, Provider<CppComponent> testedComponent, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity, ConfigurationContainer configurations, ObjectFactory objects) {
+    public DefaultCppTestExecutable(Names names, Provider<String> baseName, FileCollection sourceFiles, FileCollection componentHeaderDirs, Configuration implementation, Provider<CppComponent> testedComponent, CppPlatform targetPlatform, NativeToolChainInternal toolChain, PlatformToolProvider platformToolProvider, NativeVariantIdentity identity, RoleBasedConfigurationContainerInternal configurations, ObjectFactory objects) {
         super(names, objects, baseName, sourceFiles, componentHeaderDirs, configurations, implementation, targetPlatform, toolChain, platformToolProvider, identity);
         this.testedComponent = testedComponent;
         this.executableFile = objects.fileProperty();
diff --git a/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/xctest/plugins/XCTestConventionPluginTest.groovy b/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/xctest/plugins/XCTestConventionPluginTest.groovy
index cccb0e0..66aaba5 100644
--- a/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/xctest/plugins/XCTestConventionPluginTest.groovy
+++ b/subprojects/testing-native/src/test/groovy/org/gradle/nativeplatform/test/xctest/plugins/XCTestConventionPluginTest.groovy
@@ -29,9 +29,9 @@
 import org.gradle.nativeplatform.test.xctest.tasks.InstallXCTestBundle
 import org.gradle.nativeplatform.test.xctest.tasks.XCTest
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.ProjectBuilder
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.gradle.util.UsesNativeServices
 import org.junit.Rule
 import spock.lang.Specification
@@ -87,7 +87,7 @@
         project.xctest.testedComponent.orNull == project.application
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     def "registers a test bundle for the test suite on macOS"() {
         when:
         project.pluginManager.apply(XCTestConventionPlugin)
@@ -107,7 +107,7 @@
         project.xctest.testBinary.get() == binaries.first()
     }
 
-    @Requires(TestPrecondition.NOT_MAC_OS_X)
+    @Requires(UnitTestPreconditions.NotMacOs)
     def "registers a test executable for the test suite"() {
         when:
         project.pluginManager.apply(XCTestConventionPlugin)
@@ -127,7 +127,7 @@
         project.xctest.testBinary.get() == binaries.first()
     }
 
-    @Requires(TestPrecondition.MAC_OS_X)
+    @Requires(UnitTestPreconditions.MacOs)
     def "adds compile, link and install tasks on macOS"() {
         given:
         def src = projectDir.file("src/test/swift/test.swift").createFile()
@@ -159,7 +159,7 @@
         test.workingDirectory.get().asFile == projectDir.file("build/install/test")
     }
 
-    @Requires(TestPrecondition.NOT_MAC_OS_X)
+    @Requires(UnitTestPreconditions.NotMacOs)
     def "adds compile, link and install tasks"() {
         given:
         def src = projectDir.file("src/test/swift/test.swift").createFile()
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TaskOperationMapper.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TaskOperationMapper.java
index 8c9eaf6..b7aea34 100644
--- a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TaskOperationMapper.java
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TaskOperationMapper.java
@@ -110,7 +110,7 @@ public InternalOperationStartedProgressEvent createStartedEvent(DefaultTaskDescr
     @Override
     public InternalOperationFinishedProgressEvent createFinishedEvent(DefaultTaskDescriptor descriptor, ExecuteTaskBuildOperationDetails details, OperationFinishEvent finishEvent) {
         TaskInternal task = details.getTask();
-        AbstractTaskResult taskResult = operationResultPostProcessor.process(toTaskResult(task, finishEvent), descriptor.getId());
+        AbstractTaskResult taskResult = operationResultPostProcessor.process(toTaskResult(task, finishEvent), task);
         return new DefaultTaskFinishedProgressEvent(finishEvent.getEndTime(), descriptor, taskResult);
     }
 
@@ -157,9 +157,9 @@ public void finished(BuildOperationDescriptor buildOperation, OperationFinishEve
             }
         }
 
-        public AbstractTaskResult process(AbstractTaskResult taskResult, OperationIdentifier taskBuildOperationId) {
+        public AbstractTaskResult process(AbstractTaskResult taskResult, TaskInternal taskInternal) {
             for (OperationResultPostProcessor factory : processors) {
-                taskResult = factory.process(taskResult, taskBuildOperationId);
+                taskResult = factory.process(taskResult, taskInternal);
             }
             return taskResult;
         }
diff --git a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TransformOperationMapper.java b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TransformOperationMapper.java
index 628bb14..53e58ad 100644
--- a/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TransformOperationMapper.java
+++ b/subprojects/tooling-api-builders/src/main/java/org/gradle/tooling/internal/provider/runner/TransformOperationMapper.java
@@ -16,8 +16,8 @@
 
 package org.gradle.tooling.internal.provider.runner;
 
-import org.gradle.api.internal.artifacts.transform.ExecuteScheduledTransformationStepBuildOperationDetails;
-import org.gradle.api.internal.artifacts.transform.TransformationNode;
+import org.gradle.api.internal.artifacts.transform.ExecutePlannedTransformStepBuildOperationDetails;
+import org.gradle.api.internal.artifacts.transform.TransformStepNode;
 import org.gradle.execution.plan.Node;
 import org.gradle.internal.build.event.BuildEventSubscriptions;
 import org.gradle.internal.build.event.types.DefaultOperationFinishedProgressEvent;
@@ -44,8 +44,8 @@
  *
  * @since 5.1
  */
-class TransformOperationMapper implements BuildOperationMapper<ExecuteScheduledTransformationStepBuildOperationDetails, DefaultTransformDescriptor>, OperationDependencyLookup {
-    private final Map<TransformationNode, DefaultTransformDescriptor> descriptors = new ConcurrentHashMap<>();
+class TransformOperationMapper implements BuildOperationMapper<ExecutePlannedTransformStepBuildOperationDetails, DefaultTransformDescriptor>, OperationDependencyLookup {
+    private final Map<TransformStepNode, DefaultTransformDescriptor> descriptors = new ConcurrentHashMap<>();
     private final OperationDependenciesResolver operationDependenciesResolver;
 
     TransformOperationMapper(OperationDependenciesResolver operationDependenciesResolver) {
@@ -58,37 +58,37 @@ public boolean isEnabled(BuildEventSubscriptions subscriptions) {
     }
 
     @Override
-    public Class<ExecuteScheduledTransformationStepBuildOperationDetails> getDetailsType() {
-        return ExecuteScheduledTransformationStepBuildOperationDetails.class;
+    public Class<ExecutePlannedTransformStepBuildOperationDetails> getDetailsType() {
+        return ExecutePlannedTransformStepBuildOperationDetails.class;
     }
 
     @Override
     public InternalOperationDescriptor lookupExistingOperationDescriptor(Node node) {
-        if (node instanceof TransformationNode) {
+        if (node instanceof TransformStepNode) {
             return descriptors.get(node);
         }
         return null;
     }
 
     @Override
-    public DefaultTransformDescriptor createDescriptor(ExecuteScheduledTransformationStepBuildOperationDetails details, BuildOperationDescriptor buildOperation, @Nullable OperationIdentifier parent) {
+    public DefaultTransformDescriptor createDescriptor(ExecutePlannedTransformStepBuildOperationDetails details, BuildOperationDescriptor buildOperation, @Nullable OperationIdentifier parent) {
         OperationIdentifier id = buildOperation.getId();
         String displayName = buildOperation.getDisplayName();
         String transformerName = details.getTransformerName();
         String subjectName = details.getSubjectName();
-        Set<InternalOperationDescriptor> dependencies = operationDependenciesResolver.resolveDependencies(details.getTransformationNode());
+        Set<InternalOperationDescriptor> dependencies = operationDependenciesResolver.resolveDependencies(details.getTransformStepNode());
         DefaultTransformDescriptor descriptor = new DefaultTransformDescriptor(id, displayName, parent, transformerName, subjectName, dependencies);
-        descriptors.put(details.getTransformationNode(), descriptor);
+        descriptors.put(details.getTransformStepNode(), descriptor);
         return descriptor;
     }
 
     @Override
-    public InternalOperationStartedProgressEvent createStartedEvent(DefaultTransformDescriptor descriptor, ExecuteScheduledTransformationStepBuildOperationDetails executeScheduledTransformationStepBuildOperationDetails, OperationStartEvent startEvent) {
+    public InternalOperationStartedProgressEvent createStartedEvent(DefaultTransformDescriptor descriptor, ExecutePlannedTransformStepBuildOperationDetails details, OperationStartEvent startEvent) {
         return new DefaultOperationStartedProgressEvent(startEvent.getStartTime(), descriptor);
     }
 
     @Override
-    public InternalOperationFinishedProgressEvent createFinishedEvent(DefaultTransformDescriptor descriptor, ExecuteScheduledTransformationStepBuildOperationDetails executeScheduledTransformationStepBuildOperationDetails, OperationFinishEvent finishEvent) {
+    public InternalOperationFinishedProgressEvent createFinishedEvent(DefaultTransformDescriptor descriptor, ExecutePlannedTransformStepBuildOperationDetails details, OperationFinishEvent finishEvent) {
         return new DefaultOperationFinishedProgressEvent(finishEvent.getEndTime(), descriptor, toOperationResult(finishEvent));
     }
 }
diff --git a/subprojects/tooling-api/build.gradle.kts b/subprojects/tooling-api/build.gradle.kts
index 173718f..432552b 100644
--- a/subprojects/tooling-api/build.gradle.kts
+++ b/subprojects/tooling-api/build.gradle.kts
@@ -40,6 +40,7 @@
     testFixturesImplementation(project(":model-core"))
     testFixturesImplementation(project(":base-services"))
     testFixturesImplementation(project(":base-services-groovy"))
+    testFixturesImplementation(project(":internal-testing"))
     testFixturesImplementation(project(":internal-integ-testing"))
     testFixturesImplementation(libs.commonsIo)
     testFixturesImplementation(libs.slf4jApi)
@@ -87,4 +88,3 @@
 testFilesCleanup.reportOnly = true
 
 apply(from = "buildship.gradle")
-
diff --git a/subprojects/tooling-api/readme.md b/subprojects/tooling-api/readme.md
new file mode 100644
index 0000000..1ebaf50
--- /dev/null
+++ b/subprojects/tooling-api/readme.md
@@ -0,0 +1,24 @@
+# Cross Version Test
+
+## Pitfalls
+
+###  Executing Gradle Versions that don't support your current JVM
+
+This will result in e.g. 
+
+    execution failed for task :tooling-api:gradle3.5.1CrossVersionTest'.
+    > No tests found for given includes: [org.gradle.integtests.tooling.r81.LogLevelConfigCrossVersionSpec](--tests filter)`
+
+You can fix it by adding `-PtestJavaVersion=8` to the build.
+
+This is due to the fact that Gradle 4.6 and older don't support Java 11 and newer, which is checked before the test filter is applied.
+`org.gradle.integtests.fixtures.executer.DefaultGradleDistribution.worksWith(org.gradle.internal.jvm.Jvm)` is the method that checks this.
+This is used by `AbstractCompatibilityTestInterceptor` to filter out incompatible Gradle Versions.
+Also all TestPrecondition annotations are checked in an interceptor (e.g. TestPrecondition.NOT_MAC_OS_X). 
+The preconditions will also end up in the same message if it filters all tests.
+
+### Executing with Kotlin scripts on Gradle Version prior 5.0.
+    
+This will result in an invocation as if no script is present and you'll be puzzled why nothing you wrote in the script is happening.
+The reason is that Kotlin DSL was introduced in Gradle 5.0. Older Gradle versions simply ignore Kotlin scripts.
+The solution is to use a `groovy` script since it works on all Gradle Versions. 
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/ToolingApiUnsupportedBuildJvmCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/ToolingApiUnsupportedBuildJvmCrossVersionSpec.groovy
index 4cbf6ea..fbf06b9 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/ToolingApiUnsupportedBuildJvmCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/ToolingApiUnsupportedBuildJvmCrossVersionSpec.groovy
@@ -21,12 +21,13 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.r18.BrokenAction
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.gradle.tooling.GradleConnectionException
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.model.GradleProject
-import org.gradle.util.Requires
 
-@Requires(adhoc = { AvailableJavaHomes.getJdks("1.5", "1.6", "1.7") })
+@Requires(IntegTestPreconditions.UnsupportedJavaHomeAvailable)
 @TargetGradleVersion("current")
 class ToolingApiUnsupportedBuildJvmCrossVersionSpec extends ToolingApiSpecification {
     def setup() {
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/fixture/CrossVersionToolingApiSpecificationRetryTest.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/fixture/CrossVersionToolingApiSpecificationRetryTest.groovy
index 2126455..3d15382 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/fixture/CrossVersionToolingApiSpecificationRetryTest.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/fixture/CrossVersionToolingApiSpecificationRetryTest.groovy
@@ -19,12 +19,12 @@
 import org.gradle.api.GradleException
 import org.gradle.integtests.fixtures.executer.UnexpectedBuildFailure
 import org.gradle.test.fixtures.file.LeaksFileHandles
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.GradleConnectionException
-import org.gradle.util.Requires
 
-import static org.gradle.integtests.fixtures.RetryConditions.runsOnWindowsAndJava7or8
-
-@LeaksFileHandles //With older 2.x Gradle versions -> Unable to delete file: native-platform.dll
+//With older 2.x Gradle versions -> Unable to delete file: native-platform.dll
+@LeaksFileHandles
 class CrossVersionToolingApiSpecificationRetryTest extends ToolingApiSpecification {
 
     def setup() {
@@ -71,7 +71,11 @@
         ioe.cause?.message == "Timeout waiting to connect to the Gradle daemon.\n more infos"
     }
 
-    @Requires(adhoc = { runsOnWindowsAndJava7or8() })
+    @Requires([
+        UnitTestPreconditions.Windows,
+        UnitTestPreconditions.Jdk7OrLater,
+        UnitTestPreconditions.Jdk8OrEarlier
+    ])
     def "retries if expected exception occurs"() {
         given:
         iteration++
@@ -86,7 +90,7 @@
         true
     }
 
-    @Requires(adhoc = { !runsOnWindowsAndJava7or8() })
+    @Requires(UnitTestPreconditions.NotWindowsJavaBefore9)
     def "does not retry on non-windows and non-java7 environments"() {
         given:
         iteration++
@@ -100,7 +104,11 @@
         ioe.cause?.message == "An existing connection was forcibly closed by the remote host"
     }
 
-    @Requires(adhoc = { runsOnWindowsAndJava7or8() })
+    @Requires([
+        UnitTestPreconditions.Windows,
+        UnitTestPreconditions.Jdk7OrLater,
+        UnitTestPreconditions.Jdk8OrEarlier
+    ])
     def "should fail for unexpected cause on client side"() {
         given:
         iteration++
@@ -114,7 +122,11 @@
         ioe.cause?.message == "A different cause"
     }
 
-    @Requires(adhoc = { runsOnWindowsAndJava7or8() })
+    @Requires([
+        UnitTestPreconditions.Windows,
+        UnitTestPreconditions.Jdk7OrLater,
+        UnitTestPreconditions.Jdk8OrEarlier
+    ])
     def "should fail for unexpected cause on daemon side"() {
         given:
         iteration++
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildExecutionCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildExecutionCrossVersionSpec.groovy
index c2756cb..4bea130 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildExecutionCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiBuildExecutionCrossVersionSpec.groovy
@@ -54,7 +54,6 @@
         withConnection { connection ->
             def build = connection.newBuild()
             build.forTasks('jar')
-            collectOutputs(build)
             build.run()
         }
 
@@ -68,9 +67,9 @@
         withConnection { connection ->
             GradleProject project = connection.getModel(GradleProject.class)
             Task clean = project.tasks.find { it.name == 'clean' }
-            def build = connection.newBuild()
-            build.forTasks(clean)
-            build.run()
+            connection.newBuild()
+                .forTasks(clean)
+                .run()
         }
 
         then:
@@ -95,10 +94,8 @@
         file('build.gradle') << 'broken'
 
         when:
-        withConnection { connection ->
-            def build = connection.newBuild()
-            collectOutputs(build)
-            return build.forTasks('jar').run()
+        withConnection {
+            it.newBuild().forTasks('jar').run()
         }
 
         then:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiModelCrossVersionSpec.groovy
index ba57733..9165838 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/m5/ToolingApiModelCrossVersionSpec.groovy
@@ -40,10 +40,8 @@
         file('build.gradle') << 'broken'
 
         when:
-        withConnection { connection ->
-            def builder = connection.model(GradleProject.class)
-            collectOutputs(builder)
-            return builder.get()
+        withConnection {
+            it.model(GradleProject.class).get()
         }
 
         then:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r12rc1/BuildModelCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r12rc1/BuildModelCrossVersionSpec.groovy
index 492c3ac..3b06f22 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r12rc1/BuildModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r12rc1/BuildModelCrossVersionSpec.groovy
@@ -56,7 +56,6 @@
         withConnection { ProjectConnection connection ->
             def builder = connection.model(HierarchicalEclipseProject.class)
             action(builder)
-            collectOutputs(builder)
             builder.get()
         }
 
@@ -84,7 +83,6 @@
         withConnection { ProjectConnection connection ->
             def builder = connection.model(HierarchicalEclipseProject.class)
             action(builder)
-            collectOutputs(builder)
             builder.get()
         }
 
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r16/UnknownCustomModelFeedbackCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r16/UnknownCustomModelFeedbackCrossVersionSpec.groovy
index e2850c0..8a3c951 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r16/UnknownCustomModelFeedbackCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r16/UnknownCustomModelFeedbackCrossVersionSpec.groovy
@@ -27,9 +27,7 @@
     def "fails gracefully when unknown model requested"() {
         when:
         withConnection {
-            def builder = it.model(CustomModel.class)
-            collectOutputs(builder)
-            builder.get()
+            it.model(CustomModel.class).get()
         }
 
         then:
@@ -45,8 +43,7 @@
     def "fails gracefully when unknown model requested for version that does not log failure"() {
         when:
         withConnection {
-            def builder = it.model(CustomModel.class)
-            builder.get()
+            it.model(CustomModel.class).get()
         }
 
         then:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r18/BuildActionCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r18/BuildActionCrossVersionSpec.groovy
index eeefcc5..7171698 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r18/BuildActionCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r18/BuildActionCrossVersionSpec.groovy
@@ -68,9 +68,7 @@
     def "client receives the exception thrown by the build action"() {
         when:
         withConnection {
-            def action = it.action(new BrokenAction())
-            collectOutputs(action)
-            action.run()
+            it.action(new BrokenAction()).run()
         }
 
         then:
@@ -101,9 +99,7 @@
     def "client receives the exception thrown when action requests unknown model"() {
         when:
         withConnection {
-            def action = it.action(new FetchUnknownModel())
-            collectOutputs(action)
-            action.run()
+            it.action(new FetchUnknownModel()).run()
         }
 
         then:
@@ -120,8 +116,7 @@
     def "client receives the exception thrown when action requests unknown model for version that does not log failure"() {
         when:
         withConnection {
-            def action = it.action(new FetchUnknownModel())
-            action.run()
+            it.action(new FetchUnknownModel()).run()
         }
 
         then:
@@ -137,9 +132,7 @@
 
         when:
         withConnection {
-            def action = it.action(new ActionShouldNotBeCalled())
-            collectOutputs(action)
-            action.run()
+            it.action(new ActionShouldNotBeCalled()).run()
         }
 
         then:
@@ -160,10 +153,9 @@
 
         when:
         withConnection {
-            def action = it.action(new ActionQueriesModelThatRequiresOnlySettingsEvaluation())
-            action.withArguments("-I${initScript}")
-            collectOutputs(action)
-            action.run()
+            it.action(new ActionQueriesModelThatRequiresOnlySettingsEvaluation())
+                .withArguments("-I${initScript}")
+                .run()
         }
 
         then:
@@ -186,9 +178,8 @@
 
         when:
         withConnection {
-            def action = it.action(new ActionQueriesModelThatRequiresOnlySettingsEvaluation())
-            collectOutputs(action)
-            action.run()
+            it.action(new ActionQueriesModelThatRequiresOnlySettingsEvaluation())
+                .run()
         }
 
         then:
@@ -208,9 +199,8 @@
 
         when:
         withConnection {
-            def action = it.action(new ActionQueriesModelThatRequiresConfigurationPhase())
-            collectOutputs(action)
-            action.run()
+            it.action(new ActionQueriesModelThatRequiresConfigurationPhase())
+                .run()
         }
 
         then:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r213/ModelsWithGradleProjectCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r213/ModelsWithGradleProjectCrossVersionSpec.groovy
index 537ef19..754c1b3 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r213/ModelsWithGradleProjectCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r213/ModelsWithGradleProjectCrossVersionSpec.groovy
@@ -22,8 +22,6 @@
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.tooling.GradleConnector
-import org.gradle.tooling.internal.consumer.DefaultGradleConnector
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.HasGradleProject
 import org.gradle.tooling.model.eclipse.EclipseProject
@@ -180,15 +178,15 @@
     }
 
     private GradleProject getGradleProjectWithProjectConnection(TestFile rootDir, Class modelType = GradleProject, boolean searchUpwards = true) {
-        GradleConnector connector = connector()
+        def connector = connector()
         connector.forProjectDirectory(rootDir.absoluteFile)
-        ((DefaultGradleConnector) connector).searchUpwards(searchUpwards)
+        connector.searchUpwards(searchUpwards)
         def model = withConnection(connector) { it.getModel(modelType) }
         return toGradleProject(model)
     }
 
     private <T> T getModelWithProjectConnection(TestFile rootDir, Class<T> modelType) {
-        GradleConnector connector = connector()
+        def connector = connector()
         connector.forProjectDirectory(rootDir.absoluteFile)
         return withConnection(connector) { it.getModel(modelType) }
     }
@@ -204,9 +202,9 @@
     }
 
     private getGradleProjectsWithProjectConnectionUsingBuildModel(TestFile rootDir, Class modelType = GradleProject, boolean searchUpwards = true) {
-        GradleConnector connector = connector()
+        def connector = connector()
         connector.forProjectDirectory(rootDir.absoluteFile)
-        ((DefaultGradleConnector) connector).searchUpwards(searchUpwards)
+        connector.searchUpwards(searchUpwards)
         def buildModel = withConnection(connector) { it.getModel(modelType) }
         return toGradleProjects(buildModel)
     }
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r213/ModelsWithGradleProjectIdentifierCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r213/ModelsWithGradleProjectIdentifierCrossVersionSpec.groovy
index 84bfd64..4d9100e 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r213/ModelsWithGradleProjectIdentifierCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r213/ModelsWithGradleProjectIdentifierCrossVersionSpec.groovy
@@ -23,8 +23,6 @@
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.tooling.GradleConnector
-import org.gradle.tooling.internal.consumer.DefaultGradleConnector
 import org.gradle.tooling.model.GradleProject
 import org.gradle.tooling.model.ProjectIdentifier
 import org.gradle.tooling.model.gradle.BuildInvocations
@@ -82,17 +80,17 @@
     }
 
     private getModelWithProjectConnection(TestFile rootDir, Class modelType = GradleProject, boolean searchUpwards = true) {
-        GradleConnector connector = connector()
+        def connector = connector()
         connector.forProjectDirectory(rootDir)
-        ((DefaultGradleConnector) connector).searchUpwards(searchUpwards)
+        connector.searchUpwards(searchUpwards)
         return withConnection(connector) { it.getModel(modelType) }
     }
 
     private getModelsWithProjectConnection(TestFile rootDir, Class modelType = GradleProject, boolean searchUpwards = true) {
         FetchProjectModelsBuildAction buildAction = new FetchProjectModelsBuildAction(modelType)
-        GradleConnector connector = connector()
+        def connector = connector()
         connector.forProjectDirectory(rootDir)
-        ((DefaultGradleConnector) connector).searchUpwards(searchUpwards)
+        connector.searchUpwards(searchUpwards)
         withConnection(connector) { connection ->
             connection.action(buildAction).run()
         }
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r22/CancellationCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r22/CancellationCrossVersionSpec.groovy
index 7ee557a..e5278b4 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r22/CancellationCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r22/CancellationCrossVersionSpec.groovy
@@ -17,8 +17,8 @@
 package org.gradle.integtests.tooling.r22
 
 import org.gradle.integtests.tooling.CancellationSpec
-import org.gradle.integtests.tooling.fixture.TestResultHandler
 import org.gradle.integtests.tooling.fixture.ActionQueriesModelThatRequiresConfigurationPhase
+import org.gradle.integtests.tooling.fixture.TestResultHandler
 import org.gradle.tooling.BuildCancelledException
 import org.gradle.tooling.GradleConnector
 import org.gradle.tooling.ProjectConnection
@@ -38,7 +38,6 @@
             def build = connection.newBuild()
             build.forTasks(':sub:broken')
             build.withCancellationToken(cancel.token())
-            collectOutputs(build)
             build.run(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
@@ -63,7 +62,6 @@
             def build = connection.newBuild()
             build.forTasks(':sub:broken')
             build.withCancellationToken(cancel.token())
-            collectOutputs(build)
             build.run(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
@@ -90,7 +88,6 @@
         withConnection { ProjectConnection connection ->
             def model = connection.model(GradleProject)
             model.withCancellationToken(cancel.token())
-            collectOutputs(model)
             model.get(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
@@ -116,7 +113,6 @@
         withConnection { ProjectConnection connection ->
             def action = connection.action(new ActionQueriesModelThatRequiresConfigurationPhase())
             action.withCancellationToken(cancel.token())
-            collectOutputs(action)
             action.run(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
@@ -140,7 +136,6 @@
         withConnection { ProjectConnection connection ->
             def action = connection.action(new ActionQueriesModelThatRequiresConfigurationPhase())
             action.withCancellationToken(cancel.token())
-            collectOutputs(action)
             action.run(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
@@ -179,7 +174,6 @@
             def build = connection.newBuild()
             build.forTasks('notExecuted')
             build.withCancellationToken(cancel.token())
-            collectOutputs(build)
             build.run(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
@@ -209,7 +203,6 @@
             def build = connection.newBuild()
             build.forTasks('hang')
             build.withCancellationToken(cancel.token())
-            collectOutputs(build)
             build.run(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
@@ -240,7 +233,6 @@
             def build = connection.newBuild()
             build.forTasks('hang')
             build.withCancellationToken(cancel.token())
-            collectOutputs(build)
             build.run(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r23/ModelBuilderCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r23/ModelBuilderCrossVersionSpec.groovy
index b35638c..028f1ac 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r23/ModelBuilderCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r23/ModelBuilderCrossVersionSpec.groovy
@@ -17,10 +17,7 @@
 
 package org.gradle.integtests.tooling.r23
 
-
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.tooling.ModelBuilder
-import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.model.build.BuildEnvironment
 
 class ModelBuilderCrossVersionSpec extends ToolingApiSpecification {
@@ -32,11 +29,10 @@
         """
 
         when:
-        BuildEnvironment model = toolingApi.withConnection { ProjectConnection connection ->
-            ModelBuilder<BuildEnvironment> modelBuilder = connection.model(BuildEnvironment.class)
-            modelBuilder.forTasks(new String[0])
-            collectOutputs(modelBuilder)
-            modelBuilder.get()
+        BuildEnvironment model = toolingApi.withConnection {
+            it.model(BuildEnvironment)
+                .forTasks(new String[0])
+                .get()
         }
 
         then:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/BuildProgressCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/BuildProgressCrossVersionSpec.groovy
index 5a33b4b..0deafef 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/BuildProgressCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/BuildProgressCrossVersionSpec.groovy
@@ -77,7 +77,6 @@
                 }, EnumSet.of(OperationType.GENERIC)).addProgressListener({ ProgressEvent event ->
                     resultsOfLastListener.add(event)
                 }, EnumSet.of(OperationType.GENERIC))
-                collectOutputs(build)
                 build.run()
         }
 
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/TaskProgressCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/TaskProgressCrossVersionSpec.groovy
index 1e73146..5efaf60 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/TaskProgressCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/TaskProgressCrossVersionSpec.groovy
@@ -86,7 +86,6 @@
                 }, EnumSet.of(OperationType.TASK)).addProgressListener({ ProgressEvent event ->
                     resultsOfLastListener << (event as TaskProgressEvent)
                 }, EnumSet.of(OperationType.TASK))
-                collectOutputs(build)
                 build.run()
         }
 
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/TestProgressCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/TestProgressCrossVersionSpec.groovy
index 76e28b2..8b87bcf 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/TestProgressCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r25/TestProgressCrossVersionSpec.groovy
@@ -17,10 +17,13 @@
 
 package org.gradle.integtests.tooling.r25
 
+import org.gradle.api.JavaVersion
 import org.gradle.integtests.tooling.fixture.ProgressEvents
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.WithOldConfigurationsSupport
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.ListenerFailedException
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.events.OperationType
@@ -33,8 +36,6 @@
 import org.gradle.tooling.events.test.TestSkippedResult
 import org.gradle.tooling.model.gradle.BuildInvocations
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
 class TestProgressCrossVersionSpec extends ToolingApiSpecification implements WithOldConfigurationsSupport {
     def "receive test progress events when requesting a model"() {
@@ -98,7 +99,6 @@
                         resultsOfLastListener << (event as TestProgressEvent)
                     }
                 }, EnumSet.of(OperationType.TEST))
-                collectOutputs(build)
                 build.run()
         }
 
@@ -362,7 +362,7 @@
         events.tests.tail().every { it.descriptor.parent != null }
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "test progress event ids are unique across multiple test tasks, even when run in parallel"() {
         given:
         projectDir.createFile('settings.gradle') << """
@@ -482,7 +482,7 @@
     def goodCode() {
         buildFile << """
             apply plugin: 'java'
-            sourceCompatibility = 1.7
+            ${javaSourceCompatibility(JavaVersion.VERSION_1_7)}
             ${mavenCentralRepository()}
             dependencies { ${testImplementationConfiguration} 'junit:junit:4.13' }
             compileTestJava.options.fork = true  // forked as 'Gradle Test Executor 1'
@@ -497,4 +497,12 @@
             }
         """
     }
+
+    private String javaSourceCompatibility(JavaVersion javaVersion) {
+        if (targetVersion >= GradleVersion.version("5.0")) {
+            return "java.sourceCompatibility = JavaVersion.${javaVersion.name()}"
+        } else {
+            return "sourceCompatibility = '${javaVersion.toString()}'"
+        }
+    }
 }
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r26/TestLauncherCancellationCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r26/TestLauncherCancellationCrossVersionSpec.groovy
index ffb6e7b..0788ec5 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r26/TestLauncherCancellationCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r26/TestLauncherCancellationCrossVersionSpec.groovy
@@ -35,7 +35,6 @@
             def build = connection.newTestLauncher()
             build.withJvmTestClasses("Broken")
             build.withCancellationToken(cancel.token())
-            collectOutputs(build)
             build.run(resultHandler)
             sync.waitForAllPendingCalls(resultHandler)
             cancel.cancel()
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r26/TestLauncherCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r26/TestLauncherCrossVersionSpec.groovy
index 666c8c7..c14eaed 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r26/TestLauncherCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r26/TestLauncherCrossVersionSpec.groovy
@@ -20,10 +20,11 @@
 import groovy.transform.stc.SimpleType
 import org.gradle.api.GradleException
 import org.gradle.integtests.tooling.TestLauncherSpec
-import org.gradle.integtests.tooling.fixture.ContinuousBuildToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ProgressEvents
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.TestResultHandler
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.gradle.tooling.BuildCancelledException
 import org.gradle.tooling.BuildException
 import org.gradle.tooling.ListenerFailedException
@@ -36,7 +37,6 @@
 import org.gradle.tooling.events.test.TestOperationDescriptor
 import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException
 import org.gradle.util.GradleVersion
-import spock.lang.Requires
 import spock.lang.Timeout
 
 @Timeout(120)
@@ -150,7 +150,14 @@
         assertTaskNotExecuted(":test")
     }
 
-    @Requires({ ContinuousBuildToolingApiSpecification.canUseContinuousBuildViaToolingApi() })
+    @Requires(
+        value = IntegTestPreconditions.NotEmbeddedExecutor,
+        reason = """
+            We have problems loading the file system watching library when starting a Gradle build via the tooling API in debug (= embedded) mode.
+            The problem there is that Gradle then tries to load the native library in two different classloaders in the same JDK, which isn't allowed.
+            We could try to fix this problems, though this is only a problem for testing.
+        """
+    )
     @TargetGradleVersion(">=3.0")
     def "can run and cancel test execution in continuous mode"() {
         given:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r32/BuildActionCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r32/BuildActionCrossVersionSpec.groovy
index 977d55a..b7aff13 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r32/BuildActionCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r32/BuildActionCrossVersionSpec.groovy
@@ -43,16 +43,14 @@
         assert action2 != Action2
 
         expect:
-        def l1 = withConnection { c ->
-            def builder = c.action(action1.getConstructor().newInstance())
-            collectOutputs(builder)
-            return builder.run()
+        def l1 = withConnection {
+            it.action(action1.getConstructor().newInstance())
+                .run()
         }
         l1 == ["not broken 1"]
-        def l2 = withConnection { c ->
-            def builder = c.action(action2.getConstructor().newInstance())
-            collectOutputs(builder)
-            return builder.run()
+        def l2 = withConnection {
+            it.action(action2.getConstructor().newInstance())
+                .run()
         }
         l2 == ["not broken 2"]
     }
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r33/IncompatibilityCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r33/IncompatibilityCrossVersionSpec.groovy
index 6b9e1a0..40a3423 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r33/IncompatibilityCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r33/IncompatibilityCrossVersionSpec.groovy
@@ -22,11 +22,14 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Ignore
 
-@Requires(TestPrecondition.JDK8_OR_EARLIER) // tests against old Gradle version that can only work with Java versions up tp 8
+@Requires(
+    value = UnitTestPreconditions.Jdk8OrEarlier,
+    reason = "tests against old Gradle version that can only work with Java versions up to 8"
+)
 @ToolingApiVersion("current")
 class IncompatibilityCrossVersionSpec extends ToolingApiSpecification {
     def buildPluginWith(String gradleVersion) {
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r35/BuildEnvironmentCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r35/BuildEnvironmentCrossVersionSpec.groovy
index 9556263..1e6b61f 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r35/BuildEnvironmentCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r35/BuildEnvironmentCrossVersionSpec.groovy
@@ -19,17 +19,17 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.UnsupportedVersionException
 import org.gradle.tooling.model.build.BuildEnvironment
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
 class BuildEnvironmentCrossVersionSpec extends ToolingApiSpecification {
 
     @ToolingApiVersion(">=3.5")
     @TargetGradleVersion(">=3.5")
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     def "old versions can mutate environment on JDK < 9"() {
         given:
         toolingApi.requireDaemons() //cannot be run in embedded mode
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r35/RunTasksBeforeRunActionCrossVersion.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r35/RunTasksBeforeRunActionCrossVersion.groovy
index db4dc5e..5159af3 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r35/RunTasksBeforeRunActionCrossVersion.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r35/RunTasksBeforeRunActionCrossVersion.groovy
@@ -49,7 +49,10 @@
         when:
         def stdOut = new ByteArrayOutputStream()
         withConnection {
-            connection -> connection.action(new SimpleAction()).forTasks("hello", "bye").setStandardOutput(stdOut).run()
+            it.action(new SimpleAction())
+                .forTasks("hello", "bye")
+                .setStandardOutput(stdOut)
+                .run()
         }
 
         then:
@@ -80,10 +83,8 @@
         """
 
         when:
-        withConnection { connection ->
-            def builder = connection.action(new SimpleAction())
-            collectOutputs(builder)
-            builder.run()
+        withConnection {
+            it.action(new SimpleAction()).run()
         }
 
         then:
@@ -97,18 +98,18 @@
         "build logic injects tasks into start param"       | "gradle.startParameter.taskNames = ['broken']"
     }
 
-    @ToolingApiVersion('>4.7') // older versions do not run any tasks
+    // older versions do not run any tasks
+    @ToolingApiVersion('>4.7')
     @TargetGradleVersion('>=4.7')
     def "empty array of task names means run help task"() {
         file('build.gradle') << """
         """
 
         when:
-        withConnection { connection ->
-            def builder = connection.action(new SimpleAction())
-            builder.forTasks()
-            collectOutputs(builder)
-            builder.run()
+        withConnection {
+            it.action(new SimpleAction())
+                .forTasks()
+                .run()
         }
 
         then:
@@ -116,26 +117,8 @@
         result.assertTasksExecuted(":help")
     }
 
-    @ToolingApiVersion('>4.7') // older versions do not run any tasks
-    @TargetGradleVersion('>=4.7')
-    def "empty list of task names means run help task"() {
-        file('build.gradle') << """
-        """
-
-        when:
-        withConnection { connection ->
-            def builder = connection.action(new SimpleAction())
-            builder.forTasks([])
-            collectOutputs(builder)
-            builder.run()
-        }
-
-        then:
-        assertHasBuildSuccessfulLogging()
-        result.assertTasksExecuted(":help")
-    }
-
-    @ToolingApiVersion('>4.7') // older versions do not run any tasks
+    // older versions do not run any tasks
+    @ToolingApiVersion('>4.7')
     @TargetGradleVersion('>=4.7')
     def "empty array of task names means run default tasks when they are defined"() {
         file('build.gradle') << """
@@ -145,11 +128,10 @@
         """
 
         when:
-        withConnection { connection ->
-            def builder = connection.action(new SimpleAction())
-            builder.forTasks()
-            collectOutputs(builder)
-            builder.run()
+        withConnection {
+            it.action(new SimpleAction())
+                .forTasks()
+                .run()
         }
 
         then:
@@ -157,7 +139,27 @@
         result.assertTasksExecuted(":thing")
     }
 
-    @ToolingApiVersion('>4.7') // older versions do not run any tasks
+    // older versions do not run any tasks
+    @ToolingApiVersion('>4.7')
+    @TargetGradleVersion('>=4.7')
+    def "empty list of task names means run help task"() {
+        file('build.gradle') << """
+        """
+
+        when:
+        withConnection {
+            it.action(new SimpleAction())
+                .forTasks([])
+                .run()
+        }
+
+        then:
+        assertHasBuildSuccessfulLogging()
+        result.assertTasksExecuted(":help")
+    }
+
+    // older versions do not run any tasks
+    @ToolingApiVersion('>4.7')
     @TargetGradleVersion('>=4.7')
     def "empty list of task names means run default tasks when they are defined"() {
         file('build.gradle') << """
@@ -167,11 +169,10 @@
         """
 
         when:
-        withConnection { connection ->
-            def builder = connection.action(new SimpleAction())
-            builder.forTasks([])
-            collectOutputs(builder)
-            builder.run()
+        withConnection {
+            it.action(new SimpleAction())
+                .forTasks([])
+                .run()
         }
 
         then:
@@ -179,7 +180,8 @@
         result.assertTasksExecuted(":thing")
     }
 
-    @ToolingApiVersion('>4.7') // older versions do not run any tasks
+    // older versions do not run any tasks
+    @ToolingApiVersion('>4.7')
     @TargetGradleVersion('>=4.7')
     def "empty array of task names means run tasks injected by build logic"() {
         file('build.gradle') << """
@@ -189,11 +191,10 @@
         """
 
         when:
-        withConnection { connection ->
-            def builder = connection.action(new SimpleAction())
-            builder.forTasks()
-            collectOutputs(builder)
-            builder.run()
+        withConnection {
+            it.action(new SimpleAction())
+                .forTasks()
+                .run()
         }
 
         then:
@@ -201,7 +202,8 @@
         result.assertTasksExecuted(":thing")
     }
 
-    @ToolingApiVersion('>4.7') // older versions do not run any tasks
+    // older versions do not run any tasks
+    @ToolingApiVersion('>4.7')
     @TargetGradleVersion('>=4.7')
     def "empty list of task names means run tasks injected by build logic"() {
         file('build.gradle') << """
@@ -211,11 +213,10 @@
         """
 
         when:
-        withConnection { connection ->
-            def builder = connection.action(new SimpleAction())
-            builder.forTasks([])
-            collectOutputs(builder)
-            builder.run()
+        withConnection {
+            it.action(new SimpleAction())
+                .forTasks([])
+                .run()
         }
 
         then:
@@ -227,7 +228,9 @@
     def "BuildExecuter.forTasks() should fail when it is not supported by target"() {
         when:
         withConnection {
-            connection -> connection.action(new SimpleAction()).forTasks("hello").run()
+            it.action(new SimpleAction())
+                .forTasks("hello")
+                .run()
         }
 
         then:
@@ -242,7 +245,9 @@
 
         when:
         withConnection {
-            connection -> connection.action(new SimpleAction()).forTasks("hello").run(handler)
+            it.action(new SimpleAction())
+                .forTasks("hello")
+                .run(handler)
         }
 
         then:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/ArtifactDownloadProgressCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/ArtifactDownloadProgressCrossVersionSpec.groovy
index cc4ed9a..231f88d 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/ArtifactDownloadProgressCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/ArtifactDownloadProgressCrossVersionSpec.groovy
@@ -19,12 +19,15 @@
 import org.gradle.integtests.tooling.fixture.AbstractHttpCrossVersionSpec
 import org.gradle.integtests.tooling.fixture.ProgressEvents
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
-import org.gradle.test.fixtures.Flaky
 import org.gradle.tooling.ProjectConnection
 import org.gradle.util.GradleVersion
+import spock.lang.Timeout
+
+import java.util.concurrent.TimeUnit
 
 class ArtifactDownloadProgressCrossVersionSpec extends AbstractHttpCrossVersionSpec {
-    @Flaky(because = "https://github.com/gradle/gradle-private/issues/3638")
+
+    @Timeout(value = 10, unit = TimeUnit.MINUTES)
     @TargetGradleVersion(">=5.7")
     def "generates events for downloading artifacts"() {
         given:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/PluginApplicationBuildProgressCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/PluginApplicationBuildProgressCrossVersionSpec.groovy
index 64571f5..5af2eea 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/PluginApplicationBuildProgressCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/PluginApplicationBuildProgressCrossVersionSpec.groovy
@@ -500,11 +500,10 @@
         """
 
         when:
-        withConnection { connection ->
-            def operation = connection.newBuild()
+        withConnection {
+            it.newBuild()
                 .addProgressListener(events)
-            collectOutputs(operation)
-            operation.run()
+                .run()
         }
 
         then:
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/ProjectConfigurationChildrenProgressCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/ProjectConfigurationChildrenProgressCrossVersionSpec.groovy
index b69c1f9..d8bbeb3 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/ProjectConfigurationChildrenProgressCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r40/ProjectConfigurationChildrenProgressCrossVersionSpec.groovy
@@ -20,12 +20,14 @@
 import org.gradle.integtests.tooling.fixture.ProgressEvents
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.TextUtil
-import org.gradle.test.fixtures.Flaky
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.gradle.test.fixtures.server.http.MavenHttpRepository
 import org.gradle.test.fixtures.server.http.RepositoryHttpServer
 import org.gradle.tooling.ProjectConnection
 import org.gradle.util.GradleVersion
+import spock.lang.Timeout
+
+import java.util.concurrent.TimeUnit
 
 @IntegrationTestTimeout(300)
 @TargetGradleVersion('>=4.0')
@@ -224,7 +226,7 @@
         }
     }
 
-    @Flaky(because = "https://github.com/gradle/gradle-private/issues/3638")
+    @Timeout(value = 10, unit = TimeUnit.MINUTES)
     def "generates events for downloading artifacts"() {
         given:
         toolingApi.requireIsolatedUserHome()
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r42/BuildProgressTaskActionsCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r42/BuildProgressTaskActionsCrossVersionSpec.groovy
index 0421995..3a48cbe 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r42/BuildProgressTaskActionsCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r42/BuildProgressTaskActionsCrossVersionSpec.groovy
@@ -57,7 +57,7 @@
     def "snapshot task inputs action has an informative name"() {
         given:
         buildFile << "task custom { doLast {} }"
-        file("gradle.properties") << "org.gradle.caching=true"
+        propertiesFile << "org.gradle.caching=true"
 
         when:
         runCustomTask()
@@ -80,7 +80,7 @@
                 }
             }
         """
-        file("gradle.properties") << "org.gradle.caching=true"
+        propertiesFile << "org.gradle.caching=true"
 
         when:
         runCustomTask()
@@ -127,6 +127,7 @@
         task.descendant('Execute doLast {} action for :custom')
     }
 
+    @TargetGradleVersion(">=4.10.3")
     def "task actions defined in doFirst and doLast blocks of Kotlin build scripts have informative names"() {
         given:
         buildFileKts << """
@@ -163,6 +164,7 @@
         task.descendant('Execute One last thing... for :custom')
     }
 
+    @TargetGradleVersion(">=4.10.3")
     def "task actions defined in doFirst and doLast blocks of Kotlin build scripts can be named"() {
         given:
         buildFileKts << """
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r44/JavaVersionCrossVersionTest.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r44/JavaVersionCrossVersionTest.groovy
index 4f21140..f8e2d3a 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r44/JavaVersionCrossVersionTest.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r44/JavaVersionCrossVersionTest.groovy
@@ -20,10 +20,10 @@
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.ProjectConnection
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
-import spock.lang.IgnoreIf
 import spock.lang.Issue
 
 @Issue('https://github.com/gradle/gradle/issues/3317')
@@ -33,8 +33,7 @@
         projectDir.file("gradle.properties").writeProperties("org.gradle.java.home": AvailableJavaHomes.jdk8.javaHome.absolutePath)
     }
 
-    @Requires(TestPrecondition.JDK9_OR_LATER)
-    @IgnoreIf({ AvailableJavaHomes.jdk8 == null })
+    @Requires([UnitTestPreconditions.Jdk9OrLater, IntegTestPreconditions.Java8HomeAvailable])
     def "smoke test for JavaVersion scheme patch"() {
         configureJava8()
         def output = new ByteArrayOutputStream()
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r47/BuildProgressCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r47/BuildProgressCrossVersionSpec.groovy
index 5a8bddd..34ded0a 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r47/BuildProgressCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r47/BuildProgressCrossVersionSpec.groovy
@@ -19,12 +19,14 @@
 import org.gradle.integtests.tooling.fixture.ProgressEvents
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
-import org.gradle.test.fixtures.Flaky
 import org.gradle.test.fixtures.maven.MavenFileRepository
 import org.gradle.test.fixtures.server.http.MavenHttpRepository
 import org.gradle.test.fixtures.server.http.RepositoryHttpServer
 import org.gradle.tooling.ProjectConnection
 import org.gradle.util.GradleVersion
+import spock.lang.Timeout
+
+import java.util.concurrent.TimeUnit
 
 @TargetGradleVersion(">=4.7")
 class BuildProgressCrossVersionSpec extends ToolingApiSpecification {
@@ -40,7 +42,7 @@
         server.after()
     }
 
-    @Flaky(because = "https://github.com/gradle/gradle-private/issues/3638")
+    @Timeout(value = 10, unit = TimeUnit.MINUTES)
     def "generates download events during maven publish"() {
         given:
         toolingApi.requireIsolatedUserHome()
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r51/ProjectConfigurationProgressEventCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r51/ProjectConfigurationProgressEventCrossVersionSpec.groovy
index bc95768..8201eaf 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r51/ProjectConfigurationProgressEventCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r51/ProjectConfigurationProgressEventCrossVersionSpec.groovy
@@ -21,7 +21,6 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
-import org.gradle.test.fixtures.Flaky
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
 import org.gradle.tooling.BuildException
@@ -32,8 +31,10 @@
 import org.gradle.tooling.events.configuration.ProjectConfigurationOperationResult
 import org.gradle.util.GradleVersion
 import org.junit.Rule
+import spock.lang.Timeout
 
 import java.time.Duration
+import java.util.concurrent.TimeUnit
 
 import static org.gradle.integtests.tooling.fixture.TextUtil.escapeString
 
@@ -262,7 +263,7 @@
         }
     }
 
-    @Flaky(because = "https://github.com/gradle/gradle-private/issues/3638")
+    @Timeout(value = 10, unit = TimeUnit.MINUTES)
     def "reports plugin configuration results for remote script plugins"() {
         given:
         toolingApi.requireIsolatedUserHome() // So that the script is not cached
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r65/ToolingApiShutdownCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r65/ToolingApiShutdownCrossVersionSpec.groovy
index d22a363..12dc40e 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r65/ToolingApiShutdownCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r65/ToolingApiShutdownCrossVersionSpec.groovy
@@ -57,7 +57,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection = connector.connect() // using withConnection would call close after the closure
 
         def build = connection.newBuild()
@@ -89,7 +89,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection = connector.connect() // using withConnection would call close after the closure
 
         def build = connection.newBuild()
@@ -122,7 +122,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection = connector.connect()
 
         def query = connection.model(EclipseProject)
@@ -151,7 +151,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection1 = connector.connect()
         ProjectConnection connection2 = connector.connect()
 
@@ -187,7 +187,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection = connector.connect()
 
         def build = connection.newBuild()
@@ -203,7 +203,7 @@
 
     def "disconnect before build starts"() {
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         connector.connect()
         connector.disconnect()
 
@@ -225,7 +225,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection = connector.connect()
 
         def cancellation = GradleConnector.newCancellationTokenSource()
@@ -255,7 +255,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection = connector.connect()
 
         def cancellation = GradleConnector.newCancellationTokenSource()
@@ -285,7 +285,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection = connector.connect()
 
         def build = connection.newBuild()
@@ -311,7 +311,7 @@
 
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         withConnection(connector) { connection ->
             def build = connection.newBuild()
             build.forTasks('myTask').run()
@@ -334,7 +334,7 @@
         def resultHandler = new TestResultHandler()
 
         when:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         withConnection(connector) { connection ->
             def build = connection.newBuild()
             build.forTasks('hang').run(resultHandler)
@@ -349,7 +349,7 @@
 
     def "cannot run build operations on project connection after disconnect"() {
         setup:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         ProjectConnection connection = connector.connect()
         connection.getModel(GradleProject)
         connector.disconnect()
@@ -364,7 +364,7 @@
 
     def "cannot create new project connection after disconnect"() {
         setup:
-        GradleConnector connector = toolingApi.connector()
+        def connector = toolingApi.connector()
         withConnection(connector) { connection ->
             connection.getModel(EclipseProject)
         }
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r72/JavaVersionCrossVersionTest.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r72/JavaVersionCrossVersionTest.groovy
index f616301..ab2ea1f 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r72/JavaVersionCrossVersionTest.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r72/JavaVersionCrossVersionTest.groovy
@@ -20,10 +20,10 @@
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.GradleConnectionException
 import org.gradle.tooling.ProjectConnection
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 import spock.util.Exceptions
@@ -42,7 +42,7 @@
         """
     }
 
-    @Requires(TestPrecondition.JDK11_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk11OrLater)
     @IgnoreIf({ AvailableJavaHomes.jdk8 == null })
     def "can deserialize failures with post-jigsaw client and pre-jigsaw daemon"() {
         projectDir.file("gradle.properties").writeProperties("org.gradle.java.home": AvailableJavaHomes.jdk8.javaHome.absolutePath)
@@ -62,7 +62,7 @@
         }
     }
 
-    @Requires(TestPrecondition.JDK8_OR_EARLIER)
+    @Requires(UnitTestPreconditions.Jdk8OrEarlier)
     @IgnoreIf({ AvailableJavaHomes.jdk11 == null })
     def "can deserialize failures with pre-jigsaw client and post-jigsaw daemon"() {
         projectDir.file("gradle.properties").writeProperties("org.gradle.java.home": AvailableJavaHomes.jdk11.javaHome.absolutePath)
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r73/DependencyArtifactDownloadProgressEventCrossVersionTest.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r73/DependencyArtifactDownloadProgressEventCrossVersionTest.groovy
index d35f8fe..8689902 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r73/DependencyArtifactDownloadProgressEventCrossVersionTest.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r73/DependencyArtifactDownloadProgressEventCrossVersionTest.groovy
@@ -20,15 +20,18 @@
 import org.gradle.integtests.tooling.fixture.ProgressEvents
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
-import org.gradle.test.fixtures.Flaky
 import org.gradle.tooling.BuildException
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.events.OperationType
+import spock.lang.Timeout
+
+import java.util.concurrent.TimeUnit
 
 @ToolingApiVersion(">=7.3")
 @TargetGradleVersion(">=7.3")
+@Timeout(value = 10, unit = TimeUnit.MINUTES)
 class DependencyArtifactDownloadProgressEventCrossVersionTest extends AbstractHttpCrossVersionSpec {
-    @Flaky(because = "https://github.com/gradle/gradle-private/issues/3638")
+
     def "generates typed events for downloads during dependency resolution"() {
         def modules = setupBuildWithArtifactDownloadDuringConfiguration()
         modules.useLargeJars()
@@ -105,7 +108,6 @@
         events.operation("Download ${projectF2.artifact.uri}").assertIsDownload(projectF2.artifact)
     }
 
-    @Flaky(because = "https://github.com/gradle/gradle-private/issues/3638")
     def "generates typed events for failed downloads during dependency resolution"() {
         def modules = setupBuildWithFailedArtifactDownloadDuringTaskExecution()
 
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r76/TestFailureProgressEventCrossVersionTest.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r76/TestFailureProgressEventCrossVersionTest.groovy
index bfa3fc3..1bee05f 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r76/TestFailureProgressEventCrossVersionTest.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r76/TestFailureProgressEventCrossVersionTest.groovy
@@ -51,7 +51,8 @@
             ${mavenCentralRepository()}
 
             dependencies {
-                testImplementation 'junit:junit:3.8.1'
+                testCompileOnly 'junit:junit:3.8.1'
+                testRuntimeOnly 'junit:junit:4.13.2'
             }
         """
         file("src/test/java/FooTest.java") << """
@@ -76,7 +77,7 @@
         frameworkFailures.size() == 0
         failures.size() == assertionFailures.size() + frameworkFailures.size()
 
-        assertionFailures[0].message == "String are not equal: expected:<foo> but was:<bar>"
+        assertionFailures[0].message == "String are not equal: expected:<[foo]> but was:<[bar]>"
         assertionFailures[0].description.length() > 100
         assertionFailures[0].description.contains('junit.framework.ComparisonFailure')
         assertionFailures[0].causes.empty
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r76/TestLauncherTaskExecutionCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r76/TestLauncherTaskExecutionCrossVersionSpec.groovy
index b41c8ce..43a4f82 100644
--- a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r76/TestLauncherTaskExecutionCrossVersionSpec.groovy
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r76/TestLauncherTaskExecutionCrossVersionSpec.groovy
@@ -20,7 +20,6 @@
 import org.gradle.integtests.tooling.fixture.TargetGradleVersion
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
-import org.gradle.tooling.GradleConnector
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.TestLauncher
 
@@ -236,7 +235,7 @@
         tasksExecutedInOrder(':test', ':setupTest')
     }
 
-    private def launchTestWithTestFilter(GradleConnector connector, @DelegatesTo(TestLauncher) @ClosureParams(value = SimpleType, options = ['org.gradle.tooling.TestLauncher']) Closure testLauncherSpec) {
+    private def launchTestWithTestFilter(connector, @DelegatesTo(TestLauncher) @ClosureParams(value = SimpleType, options = ['org.gradle.tooling.TestLauncher']) Closure testLauncherSpec) {
         withConnection(connector, connectionConfiguration(testLauncherSpec))
     }
 
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ActionRunsNestedActions.java b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ActionRunsNestedActions.java
new file mode 100644
index 0000000..feefbd3
--- /dev/null
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ActionRunsNestedActions.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 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.integtests.tooling.r81;
+
+import org.gradle.tooling.BuildAction;
+import org.gradle.tooling.BuildController;
+import org.gradle.tooling.model.gradle.BasicGradleProject;
+import org.gradle.tooling.model.gradle.GradleBuild;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ActionRunsNestedActions implements BuildAction<Models> {
+    @Override
+    public Models execute(BuildController controller) {
+        GradleBuild buildModel = controller.getBuildModel();
+        List<GetProjectModel> projectActions = new ArrayList<GetProjectModel>();
+        for (BasicGradleProject project : buildModel.getProjects()) {
+            projectActions.add(new GetProjectModel(project));
+        }
+        List<ToolchainModel> results = controller.run(projectActions);
+        return new Models(controller.getCanQueryProjectModelInParallel(ToolchainModel.class), results);
+    }
+
+    static class GetProjectModel implements BuildAction<ToolchainModel> {
+        private final BasicGradleProject project;
+
+        public GetProjectModel(BasicGradleProject project) {
+            this.project = project;
+        }
+
+        @Override
+        public ToolchainModel execute(BuildController controller) {
+            return controller.getModel(project, ToolchainModel.class);
+        }
+    }
+}
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/LogLevelConfigCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/LogLevelConfigCrossVersionSpec.groovy
new file mode 100644
index 0000000..cbaa95c
--- /dev/null
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/LogLevelConfigCrossVersionSpec.groovy
@@ -0,0 +1,66 @@
+/*
+ * 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.integtests.tooling.r81
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+
+import static org.gradle.integtests.tooling.fixture.ToolingApiTestCommon.LOG_LEVEL_TEST_SCRIPT
+import static org.gradle.integtests.tooling.fixture.ToolingApiTestCommon.runLogScript
+import static org.gradle.integtests.tooling.fixture.ToolingApiTestCommon.validateLogs
+
+class LogLevelConfigCrossVersionSpec extends ToolingApiSpecification {
+
+    def setup() {
+        propertiesFile << "org.gradle.logging.level=quiet"
+        buildFile << LOG_LEVEL_TEST_SCRIPT
+    }
+
+    @ToolingApiVersion('>=8.1')
+    @TargetGradleVersion('>=3.0 <=8.0')
+    def "tooling api uses log level set in arguments over gradle properties TAPI >= 8.1"() {
+        when:
+        def stdOut = runLogScript(toolingApi, arguments)
+
+        then:
+        validateLogs(stdOut, expectedLevel)
+
+        where:
+        expectedLevel  | arguments
+        LogLevel.LIFECYCLE | []
+        LogLevel.INFO  | ["--info"]
+        LogLevel.LIFECYCLE  | ["-Dorg.gradle.logging.level=info"]
+    }
+
+    @ToolingApiVersion(">=7.0 <8.1")
+    @TargetGradleVersion('>=8.1')
+    def "tooling api uses log level set in arguments over gradle properties TAPI < 8.1"() {
+        when:
+        def stdOut = runLogScript(toolingApi, arguments)
+
+        then:
+        validateLogs(stdOut, expectedLevel)
+
+        where:
+        expectedLevel  | arguments
+        LogLevel.QUIET | []
+        LogLevel.INFO  | ["--info"]
+        LogLevel.INFO  | ["-Dorg.gradle.logging.level=info"]
+    }
+}
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/Models.java b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/Models.java
new file mode 100644
index 0000000..c30a8f1
--- /dev/null
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/Models.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 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.integtests.tooling.r81;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class Models implements Serializable {
+    private final boolean runsInParallel;
+    private final List<ToolchainModel> projects;
+
+    public Models(boolean runsInParallel, Collection<ToolchainModel> projects) {
+        this.runsInParallel = runsInParallel;
+        this.projects = new ArrayList<ToolchainModel>(projects);
+    }
+
+    public boolean isMayRunInParallel() {
+        return runsInParallel;
+    }
+
+    public List<ToolchainModel> getProjects() {
+        return projects;
+    }
+}
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ToolchainModel.java b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ToolchainModel.java
new file mode 100644
index 0000000..d371eb1
--- /dev/null
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ToolchainModel.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.integtests.tooling.r81;
+
+public interface ToolchainModel {
+
+    String getPath();
+
+    int getJavaVersion();
+}
diff --git a/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ToolchainsParallelActionExecutionCrossVersionSpec.groovy b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ToolchainsParallelActionExecutionCrossVersionSpec.groovy
new file mode 100644
index 0000000..437d4eb
--- /dev/null
+++ b/subprojects/tooling-api/src/crossVersionTest/groovy/org/gradle/integtests/tooling/r81/ToolchainsParallelActionExecutionCrossVersionSpec.groovy
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2023 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.integtests.tooling.r81
+
+import org.gradle.integtests.tooling.fixture.TargetGradleVersion
+import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
+import org.gradle.integtests.tooling.fixture.ToolingApiVersion
+import org.gradle.tooling.BuildActionFailureException
+
+@ToolingApiVersion(">=8.1")
+@org.gradle.test.fixtures.Flaky(because='https://github.com/gradle/gradle-private/issues/3829')
+class ToolchainsParallelActionExecutionCrossVersionSpec extends ToolingApiSpecification {
+
+    def setup() {
+        buildFile << """
+            import javax.inject.Inject
+            import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
+            import org.gradle.tooling.provider.model.ToolingModelBuilder
+
+            class ToolchainPlugin implements Plugin<Project> {
+                ToolingModelBuilderRegistry registry
+
+                @Inject
+                ToolchainPlugin(ToolingModelBuilderRegistry registry) {
+                    this.registry = registry
+                }
+
+                void apply(Project project) {
+                    registry.register(new ToolchainBuilder())
+                }
+            }
+
+            class ToolchainModel implements Serializable {
+                String path
+                Integer javaVersion
+            }
+
+            class ToolchainBuilder implements ToolingModelBuilder {
+                boolean canBuild(String modelName) {
+                    return modelName == "org.gradle.integtests.tooling.r81.ToolchainModel"
+                }
+
+                Object buildAll(String modelName, Project project) {
+                    // Access toolchain related information
+                    def compileJavaVersion = project.tasks.compileJava.javaCompiler.get().metadata.languageVersion.asInt()
+                    return new ToolchainModel(path: project.path, javaVersion: compileJavaVersion);
+                }
+            }
+        """
+    }
+
+    @TargetGradleVersion(">=8.1")
+    def "nested actions that query a project model which leverages toolchain information do not cause Property evaluation to be in unexpected state"() {
+        given:
+        setupBuildWithToolchainsResolution()
+
+        when:
+        withConnection {
+            def action = action(new ActionRunsNestedActions())
+            action.standardOutput = System.out
+            action.standardError = System.err
+            action.addArguments("--parallel")
+            action.run()
+        }
+
+        then:
+        def e = thrown(BuildActionFailureException)
+        def root = rootCause(e)
+        root.message.startsWith('No locally installed toolchains match')
+    }
+
+    def rootCause(Exception e) {
+        def ex = e
+        while (ex.cause != null) {
+            ex = ex.cause
+        }
+        ex
+    }
+
+    def setupBuildWithToolchainsResolution() {
+        settingsFile << """
+            import java.util.Optional;
+            import org.gradle.platform.BuildPlatform;
+
+            public abstract class FakeResolver implements JavaToolchainResolver {
+                @Override
+                public Optional<JavaToolchainDownload> resolve(JavaToolchainRequest request) {
+                    return Optional.empty();
+                }
+            }
+            public abstract class FakeProvider implements Plugin<Settings> {
+                @Inject
+                protected abstract JavaToolchainResolverRegistry getToolchainResolverRegistry();
+
+                void apply(Settings settings) {
+                    settings.getPlugins().apply("jvm-toolchain-management");
+
+                    JavaToolchainResolverRegistry registry = getToolchainResolverRegistry();
+                    registry.register(FakeResolver.class);
+                }
+            }
+
+            apply plugin: FakeProvider
+
+            toolchainManagement {
+                jvm {
+                    javaRepositories {
+                        repository('useless') {
+                            resolverClass = FakeResolver
+                        }
+                    }
+                }
+            }
+
+            rootProject.name = 'root'
+            include 'a', 'b'
+        """
+        buildFile << """
+            allprojects {
+                apply plugin: ToolchainPlugin
+                apply plugin: 'java'
+
+                java {
+                    toolchain {
+                        // Using a toolchain that triggers auto-provisioning is needed
+                        languageVersion = JavaLanguageVersion.of('99')
+                    }
+                }
+            }
+        """
+    }
+}
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ConcurrentToolingApiIntegrationSpec.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ConcurrentToolingApiIntegrationSpec.groovy
index e23df3e..7dc8b7c 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ConcurrentToolingApiIntegrationSpec.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ConcurrentToolingApiIntegrationSpec.groovy
@@ -28,7 +28,6 @@
 import org.gradle.internal.jvm.Jvm
 import org.gradle.internal.logging.progress.ProgressLoggerFactory
 import org.gradle.test.fixtures.ConcurrentTestUtil
-import org.gradle.tooling.GradleConnector
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.internal.consumer.ConnectionParameters
 import org.gradle.tooling.internal.consumer.Distribution
@@ -274,11 +273,11 @@
         concurrent.finished()
     }
 
-    static void distributionProgressMessage(GradleConnector connector, String message) {
+    static void distributionProgressMessage(connector, String message) {
         connector.distribution = new ConfigurableDistribution(delegate: connector.distribution, operation: { it.description = message} )
     }
 
-    static void distributionOperation(GradleConnector connector, Closure operation) {
+    static void distributionOperation(connector, Closure operation) {
         connector.distribution = new ConfigurableDistribution(delegate: connector.distribution, operation: operation )
     }
 
@@ -374,7 +373,7 @@
     }
 
     def withConnectionInDir(String dir, Closure cl) {
-        GradleConnector connector = toolingApi.connector(file(dir))
+        def connector = toolingApi.connector(file(dir))
         toolingApi.withConnection(connector, cl)
     }
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClasspathIntegrationTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClasspathIntegrationTest.groovy
index 14f123e..8b7a029 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClasspathIntegrationTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClasspathIntegrationTest.groovy
@@ -34,6 +34,6 @@
         resolve.classpath.any {it.name ==~ /slf4j-api-.*\.jar/}
         // If this suddenly fails without an obvious reason, you likely have added some code
         // that references types that were previously eliminated from gradle-tooling-api.jar.
-        resolve.classpath.find { it.name ==~ /gradle-tooling-api.*\.jar/ }.size() < 2_600_000
+        resolve.classpath.find { it.name ==~ /gradle-tooling-api.*\.jar/ }.size() < 2_700_000
     }
 }
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClientJdkCompatibilityTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClientJdkCompatibilityTest.groovy
index 26bebf0..d550909 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClientJdkCompatibilityTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiClientJdkCompatibilityTest.groovy
@@ -19,9 +19,13 @@
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.test.fixtures.Flaky
+import org.gradle.util.GradleVersion
 import org.junit.Assume
 
+import static org.gradle.tooling.internal.consumer.DefaultGradleConnector.MINIMUM_SUPPORTED_GRADLE_VERSION
+
 abstract class ToolingApiClientJdkCompatibilityTest extends AbstractIntegrationSpec {
+
     def setup() {
         System.out.println("TAPI client is using Java " + clientJdkVersion)
 
@@ -214,7 +218,8 @@
     def "tapi client can launch task with Gradle and Java combination"(JavaVersion gradleDaemonJdkVersion, String gradleVersion) {
         setup:
         def gradleDaemonJdk = AvailableJavaHomes.getJdk(gradleDaemonJdkVersion)
-        Assume.assumeTrue(gradleDaemonJdk!=null)
+        Assume.assumeTrue(gradleDaemonJdk != null)
+        sortOutNotSupportedNotWorkingCombinations(gradleVersion)
 
         when:
         succeeds("runTask",
@@ -228,7 +233,7 @@
 
         where:
         gradleDaemonJdkVersion  | gradleVersion
-        JavaVersion.VERSION_1_7 | "2.14.1"
+        JavaVersion.VERSION_1_7 | MINIMUM_SUPPORTED_GRADLE_VERSION.version
         JavaVersion.VERSION_1_7 | "4.6"    // last version with reported regression
         JavaVersion.VERSION_1_7 | "4.10.3" // last Gradle version that can run on Java 1.7
 
@@ -239,11 +244,16 @@
         JavaVersion.VERSION_1_8 | "6.9.2"
     }
 
+    private sortOutNotSupportedNotWorkingCombinations(String gradleVersion) {
+        Assume.assumeFalse(clientJdkVersion.majorVersion.toInteger() >= 16 && GradleVersion.version(gradleVersion) <= GradleVersion.version("4.0"))
+    }
+
     @Flaky
     def "tapi client can run build action with Gradle and Java combination"(JavaVersion gradleDaemonJdkVersion, String gradleVersion) {
         setup:
         def gradleDaemonJdk = AvailableJavaHomes.getJdk(gradleDaemonJdkVersion)
-        Assume.assumeTrue(gradleDaemonJdk!=null)
+        Assume.assumeTrue(gradleDaemonJdk != null)
+        sortOutNotSupportedNotWorkingCombinations(gradleVersion)
 
         when:
         succeeds("buildAction",
@@ -257,7 +267,7 @@
 
         where:
         gradleDaemonJdkVersion  | gradleVersion
-        JavaVersion.VERSION_1_7 | "2.14.1"
+        JavaVersion.VERSION_1_7 | MINIMUM_SUPPORTED_GRADLE_VERSION.version
         JavaVersion.VERSION_1_7 | "4.6"    // last version with reported regression
         JavaVersion.VERSION_1_7 | "4.10.3" // last Gradle version that can run on Java 1.7
 
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
index a6394b6..1f4f515 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiIntegrationTest.groovy
@@ -15,6 +15,7 @@
  */
 package org.gradle.integtests.tooling
 
+import org.gradle.api.logging.LogLevel
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.RepoScriptBlockUtil
 import org.gradle.integtests.fixtures.executer.GradleDistribution
@@ -26,7 +27,6 @@
 import org.gradle.internal.time.CountdownTimer
 import org.gradle.internal.time.Time
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.tooling.GradleConnector
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.model.GradleProject
 import org.gradle.util.GradleVersion
@@ -37,6 +37,10 @@
 import java.time.ZonedDateTime
 import java.time.format.DateTimeFormatter
 
+import static org.gradle.integtests.tooling.fixture.ToolingApiTestCommon.LOG_LEVEL_TEST_SCRIPT
+import static org.gradle.integtests.tooling.fixture.ToolingApiTestCommon.runLogScript
+import static org.gradle.integtests.tooling.fixture.ToolingApiTestCommon.validateLogs
+
 class ToolingApiIntegrationTest extends AbstractIntegrationSpec {
 
     final ToolingApi toolingApi = new ToolingApi(distribution, temporaryFolder)
@@ -52,8 +56,14 @@
         settingsFile.touch()
     }
 
+    void setupLoggingTest() {
+        propertiesFile << "org.gradle.logging.level=quiet"
+        buildFile << LOG_LEVEL_TEST_SCRIPT
+    }
+
+
     def "tooling api uses to the current version of gradle when none has been specified"() {
-        projectDir.file('build.gradle') << "assert gradle.gradleVersion == '${GradleVersion.current().version}'"
+        buildFile << "assert gradle.gradleVersion == '${GradleVersion.current().version}'"
 
         when:
         GradleProject model = toolingApi.withConnection { connection -> connection.getModel(GradleProject.class) }
@@ -63,7 +73,7 @@
     }
 
     def "tooling api output reports 'CONFIGURE SUCCESSFUL' for model requests"() {
-        projectDir.file('build.gradle') << "assert gradle.gradleVersion == '${GradleVersion.current().version}'"
+        buildFile << "assert gradle.gradleVersion == '${GradleVersion.current().version}'"
 
         when:
         def stdOut = new ByteArrayOutputStream()
@@ -98,11 +108,33 @@
         stdOut.toString().contains("CONFIGURE SUCCESSFUL")
     }
 
+    def "tooling api uses log level set in arguments over gradle.properties"() {
+        given:
+        setupLoggingTest()
+
+        when:
+        def stdOut = runLogScript(toolingApi, arguments)
+        then:
+        validateLogs(stdOut, expectedLevel)
+
+        where:
+        expectedLevel  | arguments
+        LogLevel.QUIET | []
+        LogLevel.INFO  | ["--info"]
+        LogLevel.INFO  | ["-Dorg.gradle.logging.level=info"]
+    }
+
     def "tooling api uses the wrapper properties to determine which version to use"() {
-        projectDir.file('build.gradle').text = """
-wrapper { distributionUrl = '${otherVersion.binDistribution.toURI()}' }
-task check { doLast { assert gradle.gradleVersion == '${otherVersion.version.version}' } }
-"""
+        buildFile << """
+        wrapper {
+            distributionUrl = '${otherVersion.binDistribution.toURI()}'
+        }
+        task check {
+            doLast {
+                assert gradle.gradleVersion == '${otherVersion.version.version}'
+            }
+        }"""
+        otherVersion.binDistribution.makeReadable()
         executer.withTasks('wrapper').run()
 
         when:
@@ -116,14 +148,15 @@
     }
 
     def "tooling api searches up from the project directory to find the wrapper properties"() {
-        projectDir.file('settings.gradle') << "include 'child'"
-        projectDir.file('build.gradle') << """
-wrapper { distributionUrl = '${otherVersion.binDistribution.toURI()}' }
-allprojects {
-    task check { doLast { assert gradle.gradleVersion == '${otherVersion.version.version}' } }
-}
-"""
+        settingsFile << "include 'child'"
+        buildFile << """
+        wrapper { distributionUrl = '${otherVersion.binDistribution.toURI()}' }
+        allprojects {
+            task check { doLast { assert gradle.gradleVersion == '${otherVersion.version.version}' } }
+        }
+        """
         projectDir.file('child').createDir()
+        otherVersion.binDistribution.makeReadable()
         executer.withTasks('wrapper').run()
 
         when:
@@ -139,7 +172,8 @@
     }
 
     def "can specify a gradle installation to use"() {
-        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version.version}'"
+
+        buildFile << "assert gradle.gradleVersion == '${otherVersion.version.version}'"
 
         when:
         toolingApi.withConnector { connector ->
@@ -152,7 +186,7 @@
     }
 
     def "can specify a gradle distribution to use"() {
-        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version.version}'"
+        buildFile << "assert gradle.gradleVersion == '${otherVersion.version.version}'"
 
         when:
         toolingApi.withConnector { connector ->
@@ -165,11 +199,10 @@
     }
 
     def "can specify a gradle version to use"() {
-        projectDir.file('build.gradle').text = "assert gradle.gradleVersion == '${otherVersion.version.version}'"
+        buildFile << "assert gradle.gradleVersion == '${otherVersion.version.version}'"
 
         when:
-        toolingApi.withConnector { GradleConnector connector ->
-            connector.useGradleVersion(otherVersion.version.version)
+        toolingApi.withConnector { it.useGradleVersion(otherVersion.version.version)
         }
         GradleProject model = toolingApi.withConnection { connection -> connection.getModel(GradleProject.class) }
 
@@ -180,7 +213,6 @@
     @Issue("GRADLE-2419")
     def "tooling API does not hold JVM open"() {
         given:
-        def buildFile = projectDir.file("build.gradle")
         def startTimeoutMs = 90000
         def stateChangeTimeoutMs = 15000
         def stopTimeoutMs = 10000
diff --git a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiRemoteIntegrationTest.groovy b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiRemoteIntegrationTest.groovy
index 5552dda..d1c7132 100644
--- a/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiRemoteIntegrationTest.groovy
+++ b/subprojects/tooling-api/src/integTest/groovy/org/gradle/integtests/tooling/ToolingApiRemoteIntegrationTest.groovy
@@ -23,16 +23,14 @@
 import org.gradle.test.fixtures.ConcurrentTestUtil
 import org.gradle.test.fixtures.file.LeaksFileHandles
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.BuildCancelledException
 import org.gradle.tooling.BuildLauncher
 import org.gradle.tooling.GradleConnectionException
-import org.gradle.tooling.GradleConnector
-import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.events.OperationType
 import org.gradle.tooling.internal.consumer.DefaultCancellationTokenSource
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Issue
 
@@ -59,8 +57,8 @@
         server.expect(server.get("/custom-dist.zip").expectUserAgent(matchesNameAndVersion("Gradle Tooling API", GradleVersion.current().getVersion())).sendFile(distribution.binDistribution))
 
         and:
-        toolingApi.withConnector { GradleConnector connector ->
-            connector.useDistribution(URI.create("http://localhost:${server.port}/custom-dist.zip"))
+        toolingApi.withConnector {
+            it.useDistribution(URI.create("http://localhost:${server.port}/custom-dist.zip"))
         }
 
         when:
@@ -84,14 +82,13 @@
 
         and:
         def distUri = URI.create("http://localhost:${server.port}/custom-dist.zip")
-        toolingApi.withConnector { GradleConnector connector ->
-            connector.useDistribution(distUri)
+        toolingApi.withConnector {
+            it.useDistribution(distUri)
         }
 
         when:
-        toolingApi.withConnection { connection ->
-            BuildLauncher launcher = connection.newBuild().forTasks("help")
-            launcher.run()
+        toolingApi.withConnection {
+             it.newBuild().forTasks("help").run()
         }
 
         then:
@@ -110,15 +107,12 @@
         """
 
         and:
-        toolingApi.withConnector { GradleConnector connector ->
-            connector.useBuildDistribution()
+        toolingApi.withConnector {
+            it.useBuildDistribution()
         }
 
         when:
-        toolingApi.withConnection { connection ->
-            BuildLauncher launcher = connection.newBuild().forTasks("help")
-            launcher.run()
-        }
+        toolingApi.withConnection { it.newBuild().forTasks("help").run() }
 
         then:
         file("wrapper/dists/custom-dist").assertIsDir().listFiles().size() == 1
@@ -131,14 +125,14 @@
 
         and:
         def distUri = URI.create("http://localhost:${server.port}/custom-dist.zip")
-        toolingApi.withConnector { GradleConnector connector ->
-            connector.useDistribution(distUri)
+        toolingApi.withConnector {
+            it.useDistribution(distUri)
         }
 
         when:
         def events = ProgressEvents.create()
-        toolingApi.withConnection { ProjectConnection connection ->
-            connection.newBuild()
+        toolingApi.withConnection {
+            it.newBuild()
                 .forTasks("help")
                 .addProgressListener(events)
                 .run()
@@ -174,14 +168,14 @@
 
         and:
         def distUri = URI.create("http://localhost:${server.port}/custom-dist.zip")
-        toolingApi.withConnector { GradleConnector connector ->
-            connector.useDistribution(distUri)
+        toolingApi.withConnector {
+            it.useDistribution(distUri)
         }
 
         when:
         def events = ProgressEvents.create()
-        toolingApi.withConnection { ProjectConnection connection ->
-            connection.newBuild()
+        toolingApi.withConnection {
+            it.newBuild()
                 .forTasks("help")
                 .addProgressListener(events)
                 .run()
@@ -207,14 +201,14 @@
 
         and:
         def distUri = URI.create("http://localhost:${server.port}/custom-dist.zip")
-        toolingApi.withConnector { GradleConnector connector ->
-            connector.useDistribution(distUri)
+        toolingApi.withConnector {
+            it.useDistribution(distUri)
         }
 
         when:
         def events = ProgressEvents.create()
-        toolingApi.withConnection { ProjectConnection connection ->
-            connection.newBuild()
+        toolingApi.withConnection {
+            it.newBuild()
                 .forTasks("help")
                 .addProgressListener(events, EnumSet.complementOf(EnumSet.of(OperationType.FILE_DOWNLOAD)))
                 .run()
@@ -241,16 +235,16 @@
         server.expect(downloadHandle)
 
         and:
-        toolingApi.withConnector { GradleConnector connector ->
-            connector.useDistribution(distUri)
-            connector.useGradleUserHomeDir(userHomeDir)
+        toolingApi.withConnector {
+            it.useDistribution(distUri)
+            it.useGradleUserHomeDir(userHomeDir)
         }
 
         when:
         def events = ProgressEvents.create()
         def handler = new TestResultHandler()
-        toolingApi.withConnection { ProjectConnection connection ->
-            connection.newBuild()
+        toolingApi.withConnection {
+            it.newBuild()
                 .forTasks("help")
                 .withCancellationToken(tokenSource.token())
                 .addProgressListener(events)
@@ -287,7 +281,7 @@
     }
 
     @Issue('https://github.com/gradle/gradle/issues/15405')
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     // cannot delete files when daemon is running
     def "calling disconnect on existing connection does not re-trigger wrapper download"() {
         setup:
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
index 55bab79..6075fdd 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/GradleConnector.java
@@ -61,7 +61,7 @@
  *
  * <p>The Tooling API is both forwards and backwards compatible with other versions of Gradle. It supports execution of Gradle builds that use older or newer versions of Gradle.  Each release of Gradle contains a new release of the Tooling API as well.</p>
  *
- * <p>The Tooling API supports running builds using Gradle version 2.6 and up.</p>
+ * <p>The Tooling API supports running builds using Gradle version 3.0 and up.</p>
  *
  * <p>You should note that not all features of the Tooling API are available for all versions of Gradle. Refer to the documentation for each class and method for more details.</p>
  *
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProjectConnection.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProjectConnection.java
index 8a7856e..397c20c 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProjectConnection.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/ProjectConnection.java
@@ -96,7 +96,7 @@ public interface ProjectConnection extends Closeable {
     /**
      * Creates a test launcher which can be used to execute tests.
      *
-     * <p>Requires Gradle 2.6 or later.</p>
+     * <p>Requires Gradle 3.0 or later.</p>
      *
      * @return The launcher.
      * @since 2.6
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/UnknownModelException.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/UnknownModelException.java
index e6857b3..ea34df0 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/UnknownModelException.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/UnknownModelException.java
@@ -32,4 +32,4 @@ public UnknownModelException(String message) {
     public UnknownModelException(String message, Throwable cause) {
         super(message, cause);
     }
-}
\ No newline at end of file
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/exceptions/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/exceptions/package-info.java
index f1a5acf..0994c71 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/exceptions/package-info.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/exceptions/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Exceptions thrown when using the tooling API.
  */
-package org.gradle.tooling.exceptions;
\ No newline at end of file
+package org.gradle.tooling.exceptions;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java
index 131b9a3..9173a4e 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/internal/consumer/DefaultGradleConnector.java
@@ -29,6 +29,7 @@
 import java.util.concurrent.TimeUnit;
 
 public class DefaultGradleConnector extends GradleConnector implements ProjectConnectionCloseListener {
+    public static final GradleVersion MINIMUM_SUPPORTED_GRADLE_VERSION = GradleVersion.version("3.0");
     private static final Logger LOGGER = LoggerFactory.getLogger(GradleConnector.class);
     private final ConnectionFactory connectionFactory;
     private final DistributionFactory distributionFactory;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java
index ed59d43..26528a2 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/Task.java
@@ -15,6 +15,8 @@
  */
 package org.gradle.tooling.model;
 
+import org.gradle.api.Incubating;
+
 import javax.annotation.Nullable;
 
 /**
@@ -34,6 +36,16 @@ public interface Task extends Launchable {
     String getPath();
 
     /**
+     * Returns the path of this task within the build tree. This is a unique name for this task within the composite build.
+     *
+     * @return The path of this task in the composite build.
+     * @throws org.gradle.tooling.model.UnsupportedMethodException When the target Gradle version does not support this method.
+     * @since 8.2
+     */
+    @Incubating
+    String getBuildTreePath();
+
+    /**
      * Returns the name of this task. Note that the name is not necessarily a unique identifier for the task.
      *
      * @return The name of this task.
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/build/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/build/package-info.java
index 1f4f3df..c2e2b7a 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/build/package-info.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/build/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Tooling models for the build environment, which includes information such as Gradle or Java versions.
  */
-package org.gradle.tooling.model.build;
\ No newline at end of file
+package org.gradle.tooling.model.build;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/cpp/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/cpp/package-info.java
index 812460d..1b0ceaa 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/cpp/package-info.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/cpp/package-info.java
@@ -19,4 +19,4 @@
  *
  * <p>To fetch the C++ model for a build, use the {@link org.gradle.tooling.model.cpp.CppProject} type for each Gradle project.</p>
  */
-package org.gradle.tooling.model.cpp;
\ No newline at end of file
+package org.gradle.tooling.model.cpp;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/package-info.java
index 2aaec75..302b433 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/package-info.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/eclipse/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Eclipse-centric tooling models.
  */
-package org.gradle.tooling.model.eclipse;
\ No newline at end of file
+package org.gradle.tooling.model.eclipse;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BasicGradleProject.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BasicGradleProject.java
index 9eb08a5..1b278eb 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BasicGradleProject.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/gradle/BasicGradleProject.java
@@ -16,6 +16,7 @@
 
 package org.gradle.tooling.model.gradle;
 
+import org.gradle.api.Incubating;
 import org.gradle.tooling.model.DomainObjectSet;
 import org.gradle.tooling.model.Model;
 import org.gradle.tooling.model.ProjectIdentifier;
@@ -73,4 +74,14 @@ public interface BasicGradleProject extends Model, ProjectModel {
      * @return The child projects of this project, or the empty set if there are no child projects.
      */
     DomainObjectSet<? extends BasicGradleProject> getChildren();
+
+    /**
+     * Returns a path to the project for the full build tree
+     *
+     * @return a path to the project for the full build tree
+     * @throws org.gradle.tooling.model.UnsupportedMethodException When the target Gradle version does not support this method.
+     * @since 8.2
+     */
+    @Incubating
+    String getBuildTreePath();
 }
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaLanguageLevel.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaLanguageLevel.java
index 4a7b798..aa48f43 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaLanguageLevel.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/IdeaLanguageLevel.java
@@ -28,4 +28,4 @@ public interface IdeaLanguageLevel {
      */
     String getLevel();
 
-}
\ No newline at end of file
+}
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/package-info.java
index 1911501..0e47c0a 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/package-info.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/idea/package-info.java
@@ -17,4 +17,4 @@
 /**
  * IntelliJ IDEA centric tooling models.
  */
-package org.gradle.tooling.model.idea;
\ No newline at end of file
+package org.gradle.tooling.model.idea;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/java/InstalledJdk.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/java/InstalledJdk.java
index e5063c9..9ff92dd 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/java/InstalledJdk.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/java/InstalledJdk.java
@@ -26,7 +26,7 @@
  * @since 2.11
  */
 public interface InstalledJdk {
-    /***
+    /**
      * The version of the Java installation.
      *
      * @return The Java version. Never returns {@code null}.
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/java/JavaRuntime.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/java/JavaRuntime.java
index 1b674e1..7f03c69 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/java/JavaRuntime.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/java/JavaRuntime.java
@@ -26,7 +26,7 @@
  * @since 2.11
  */
 public interface JavaRuntime {
-    /***
+    /**
      * The Java version of the Java runtime installation.
      *
      * @return The Java version. Never returns {@code null}.
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/package-info.java
index 0ee0633..b1bbd33 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/package-info.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/model/package-info.java
@@ -17,4 +17,4 @@
 /**
  * The general-purpose tooling model types, provided by the tooling API.
  */
-package org.gradle.tooling.model;
\ No newline at end of file
+package org.gradle.tooling.model;
diff --git a/subprojects/tooling-api/src/main/java/org/gradle/tooling/package-info.java b/subprojects/tooling-api/src/main/java/org/gradle/tooling/package-info.java
index ceaf8b7..f0624e3 100644
--- a/subprojects/tooling-api/src/main/java/org/gradle/tooling/package-info.java
+++ b/subprojects/tooling-api/src/main/java/org/gradle/tooling/package-info.java
@@ -17,4 +17,4 @@
 /**
  * The main interfaces and classes of the Gradle tooling API.
  */
-package org.gradle.tooling;
\ No newline at end of file
+package org.gradle.tooling;
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ContinuousBuildToolingApiSpecification.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ContinuousBuildToolingApiSpecification.groovy
index 37dada3..799f660 100644
--- a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ContinuousBuildToolingApiSpecification.groovy
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ContinuousBuildToolingApiSpecification.groovy
@@ -20,18 +20,18 @@
 import groovy.transform.stc.SimpleType
 import org.gradle.integtests.fixtures.executer.ExecutionFailure
 import org.gradle.integtests.fixtures.executer.ExecutionResult
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.OutputScrapingExecutionResult
 import org.gradle.integtests.fixtures.executer.UnexpectedBuildFailure
 import org.gradle.internal.os.OperatingSystem
 import org.gradle.test.fixtures.ConcurrentTestUtil
 import org.gradle.test.fixtures.file.TestFile
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
 import org.gradle.tooling.BuildLauncher
 import org.gradle.tooling.CancellationToken
 import org.gradle.tooling.ProjectConnection
 import org.gradle.util.GradleVersion
 import org.hamcrest.Matcher
-import org.junit.Assume
 import org.junit.Rule
 import spock.lang.Retry
 import spock.lang.Timeout
@@ -43,6 +43,8 @@
 
 @Timeout(180)
 @Retry(condition = { onContinuousBuildTimeout(instance, failure) }, mode = SETUP_FEATURE_CLEANUP, count = 2)
+// Can't use the FS watching, embedded executor and TAPI together
+@Requires(IntegTestPreconditions.NotEmbeddedExecutor)
 abstract class ContinuousBuildToolingApiSpecification extends ToolingApiSpecification {
 
     public static final String BUILD_CANCELLED = "Build cancelled."
@@ -66,13 +68,6 @@
         return usesNativeWatchers(targetVersion)
     }
 
-    // We have problems loading the file system watching library when starting a Gradle build via the tooling API in debug (= embedded) mode.
-    // The problem there is that Gradle then tries to load the native library in two different classloaders in the same JDK, which isn't allowed.
-    // We could try to fix this problems, though this is only a problem for testing.
-    static boolean canUseContinuousBuildViaToolingApi() {
-        return  !GradleContextualExecuter.embedded
-    }
-
     private static final boolean OS_IS_WINDOWS = OperatingSystem.current().isWindows()
 
     ExecutionResult result
@@ -89,7 +84,6 @@
 
 
     def setup() {
-        Assume.assumeTrue("Unsupported for the embedded runner", canUseContinuousBuildViaToolingApi())
         buildFile.text = "apply plugin: 'java'\n"
         sourceDir = file("src/main/java").createDir()
     }
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApi.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApi.groovy
index 28636a9..a3bb721 100644
--- a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApi.groovy
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApi.groovy
@@ -15,16 +15,20 @@
  */
 package org.gradle.integtests.tooling.fixture
 
+import org.apache.commons.io.output.TeeOutputStream
 import org.gradle.integtests.fixtures.daemon.DaemonLogsAnalyzer
 import org.gradle.integtests.fixtures.daemon.DaemonsFixture
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.GradleDistribution
 import org.gradle.integtests.fixtures.executer.GradleExecuter
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.integtests.fixtures.executer.ResultAssertion
 import org.gradle.internal.service.DefaultServiceRegistry
 import org.gradle.test.fixtures.file.TestDirectoryProvider
 import org.gradle.test.fixtures.file.TestFile
 import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.LongRunningOperation
+import org.gradle.tooling.ModelBuilder
 import org.gradle.tooling.ProjectConnection
 import org.gradle.tooling.internal.consumer.ConnectorServices
 import org.gradle.tooling.internal.consumer.DefaultGradleConnector
@@ -53,8 +57,12 @@
 
     private final List<Closure> connectorConfigurers = []
     boolean verboseLogging = LOGGER.debugEnabled
+    private final OutputStream stdout
+    private final OutputStream stderr
 
-    ToolingApi(GradleDistribution dist, TestDirectoryProvider testWorkDirProvider) {
+    ToolingApi(GradleDistribution dist, TestDirectoryProvider testWorkDirProvider, OutputStream stdout = System.out, OutputStream stderr = System.err) {
+        this.stderr = stderr
+        this.stdout = stdout
         this.dist = dist
         this.useSeparateDaemonBaseDir = DefaultGradleConnector.metaClass.respondsTo(null, "daemonBaseDir")
         this.gradleUserHomeDir = context.gradleUserHomeDir
@@ -127,19 +135,46 @@
         return DaemonLogsAnalyzer.newAnalyzer(getDaemonBaseDir(), dist.version.version)
     }
 
-    void withConnector(Closure cl) {
+    void withConnector(@DelegatesTo(GradleConnector) Closure cl) {
         connectorConfigurers << cl
     }
 
-    def <T> T withConnection(Closure<T> cl) {
-        GradleConnector connector = connector()
+    def <T> T withConnection(@DelegatesTo(ProjectConnection) Closure<T> cl) {
+        def connector = connector()
         withConnection(connector, cl)
     }
 
-    def <T> T withConnection(GradleConnector connector, Closure<T> cl) {
+    def <T> T withConnection(connector, @DelegatesTo(ProjectConnection) Closure<T> cl) {
         return withConnectionRaw(connector, cl)
     }
 
+    def <T> T loadToolingLeanModel(Class<T> modelClass, @DelegatesTo(ModelBuilder<T>) Closure configurator = {}) {
+        withConnection {
+            def builder = it.model(modelClass)
+            builder.tap(configurator)
+            collectOutputs(builder)
+            builder.get()
+        }
+    }
+
+    def <T> T loadValidatedToolingModel(Class<T> modelClass, @DelegatesTo(ModelBuilder<T>) Closure configurator = {}) {
+        def result = loadToolingLeanModel(modelClass, configurator)
+        validateOutput()
+        result
+    }
+
+    void collectOutputs(LongRunningOperation op) {
+        op.setStandardOutput(new TeeOutputStream(stdout, System.out))
+        op.setStandardError(new TeeOutputStream(stderr, System.err))
+    }
+
+    def validateOutput() {
+        def assertion = new ResultAssertion(0, [], false, true, true)
+        assertion.validate(stdout.toString(), "stdout")
+        assertion.validate(stderr.toString(), "stderr")
+        true
+    }
+
     private validate(Throwable throwable) {
         if (dist.version != GradleVersion.current()) {
             return
@@ -159,33 +194,27 @@
         assert throwableStack.endsWith(currentThreadStackStr)
     }
 
-    private <T> T withConnectionRaw(GradleConnector connector, Closure<T> cl) {
-        ProjectConnection connection = connector.connect()
-        try {
+    private <T> T withConnectionRaw(connector, @DelegatesTo(ProjectConnection) Closure<T> cl) {
+        try (def connection = connector.connect()) {
             return connection.with(cl)
         } catch (Throwable t) {
             validate(t)
             throw t
-        } finally {
-            connection.close()
         }
     }
 
-    GradleConnector connector(projectDir = testWorkDirProvider.testDirectory) {
-        DefaultGradleConnector connector
-        if (isolatedToolingClient != null) {
-            connector = isolatedToolingClient.getFactory(DefaultGradleConnector).create()
-        } else {
-            connector = GradleConnector.newConnector() as DefaultGradleConnector
-        }
+    ToolingApiConnector connector(projectDir = testWorkDirProvider.testDirectory) {
+        DefaultGradleConnector connector = createConnector()
 
         connector.forProjectDirectory(projectDir)
+
         if (embedded) {
             connector.useClasspathDistribution()
         } else {
             connector.useInstallation(dist.gradleHomeDir.absoluteFile)
         }
         connector.embedded(embedded)
+
         if (GradleVersion.version(dist.getVersion().version) < GradleVersion.version("6.0")) {
             connector.searchUpwards(false)
         } else {
@@ -193,7 +222,7 @@
             if (projectDir.name != "buildSrc") {
                 def settingsFile = projectDir.file('settings.gradle')
                 def settingsFileKts = projectDir.file('settings.gradle.kts')
-                assert (settingsFile.exists() || settingsFileKts.exists()) : "the build must have a settings file"
+                assert (settingsFile.exists() || settingsFileKts.exists()): "the build must have a settings file"
             }
         }
         if (useSeparateDaemonBaseDir) {
@@ -208,11 +237,9 @@
             // When using an isolated user home, first initialise the Gradle instance using the default user home dir
             // This sets some static state that uses files from the user home dir, such as DLLs
             connector.useGradleUserHomeDir(new File(context.gradleUserHomeDir.path))
-            def connection = connector.connect()
-            try {
+
+            try (def connection = connector.connect()) {
                 connection.getModel(BuildEnvironment.class)
-            } finally {
-                connection.close()
             }
         }
 
@@ -222,16 +249,21 @@
         connectorConfigurers.each {
             connector.with(it)
         }
-        return connector
+
+        return new ToolingApiConnector(connector, stdout, stderr)
+    }
+
+    private createConnector() {
+        if (isolatedToolingClient != null) {
+            return isolatedToolingClient.getFactory(DefaultGradleConnector).create()
+        }
+        return GradleConnector.newConnector() as DefaultGradleConnector
     }
 
     private void isolateFromGradleOwnBuild(DefaultGradleConnector connector) {
         // override the `user.dir` property in order to isolate tests from the Gradle directory
-        def connection = connector.connect()
-        try {
+        try (def connection = connector.connect()) {
             connection.action(new SetWorkingDirectoryAction(testWorkDirProvider.testDirectory.absolutePath))
-        } finally {
-            connection.close()
         }
     }
 
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiClassLoaderProvider.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiClassLoaderProvider.groovy
index 5eff04d..f412075 100644
--- a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiClassLoaderProvider.groovy
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiClassLoaderProvider.groovy
@@ -26,19 +26,18 @@
 import org.gradle.internal.classloader.MultiParentClassLoader
 import org.gradle.internal.classloader.VisitableURLClassLoader
 import org.gradle.internal.os.OperatingSystem
-import org.gradle.util.internal.RedirectStdOutAndErr
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.precondition.TestPrecondition
 import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestPrecondition
+import org.gradle.util.internal.RedirectStdOutAndErr
 
 class ToolingApiClassLoaderProvider {
     private static final Map<String, ClassLoader> TEST_CLASS_LOADERS = [:]
     private static final GradleDistribution CURRENT_GRADLE = new UnderDevelopmentGradleDistribution(IntegrationTestBuildContext.INSTANCE)
 
     static ClassLoader getToolingApiClassLoader(ToolingApiDistribution toolingApi, Class<?> target) {
-        List<File> testClassPath = []
-        testClassPath << ClasspathUtil.getClasspathForClass(ToolingApiSpecification)
-        testClassPath << ClasspathUtil.getClasspathForClass(target)
+        def testClassPath = [ToolingApiSpecification, target]
+            .collect { ClasspathUtil.getClasspathForClass(it) }
 
         testClassPath.addAll(collectAdditionalClasspath(toolingApi, target))
 
@@ -55,7 +54,7 @@
     }
 
     private static ClassLoader getTestClassLoader(ToolingApiDistribution toolingApi, List<File> testClasspath) {
-        synchronized(ToolingApiClassLoaderProvider) {
+        synchronized (ToolingApiClassLoaderProvider) {
             def classLoader = TEST_CLASS_LOADERS.get(toolingApi.version.version)
             if (!classLoader) {
                 classLoader = createTestClassLoader(toolingApi, testClasspath)
@@ -82,11 +81,13 @@
         sharedSpec.allowPackage('org.gradle.play.integtest.fixtures')
         sharedSpec.allowPackage('org.gradle.plugins.ide.fixtures')
         sharedSpec.allowPackage('org.gradle.test.fixtures')
+        sharedSpec.allowPackage('org.gradle.test.preconditions')
         sharedSpec.allowPackage('org.gradle.nativeplatform.fixtures')
         sharedSpec.allowPackage('org.gradle.language.fixtures')
         sharedSpec.allowPackage('org.gradle.workers.fixtures')
         sharedSpec.allowPackage('org.gradle.launcher.daemon.testing')
         sharedSpec.allowPackage('org.gradle.tooling')
+        sharedSpec.allowPackage('org.gradle.kotlin.dsl.tooling.builders')
         sharedSpec.allowClass(OperatingSystem)
         sharedSpec.allowClass(Requires)
         sharedSpec.allowClass(TestPrecondition)
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiConnection.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiConnection.groovy
new file mode 100644
index 0000000..1278ee0
--- /dev/null
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiConnection.groovy
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2023 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.integtests.tooling.fixture
+
+import org.apache.commons.io.output.TeeOutputStream
+import org.gradle.tooling.BuildAction
+import org.gradle.tooling.BuildActionExecuter
+import org.gradle.tooling.BuildLauncher
+import org.gradle.tooling.ConfigurableLauncher
+import org.gradle.tooling.ModelBuilder
+import org.gradle.tooling.ProjectConnection
+import org.gradle.tooling.TestLauncher
+
+trait ToolingApiConfigurableLauncher<T extends ConfigurableLauncher<T>> {
+    T configurableLauncher
+    OutputStream stdout
+    OutputStream stderr
+
+    def initTrait(T configurableLauncher, OutputStream stdout, OutputStream stderr) {
+        this.configurableLauncher = configurableLauncher
+        configurableLauncher.standardOutput = stdout
+        configurableLauncher.standardError = stderr
+        this.stdout = stdout
+        this.stderr = stderr
+    }
+
+    T setStandardOutput(OutputStream outputStream) {
+        configurableLauncher.standardOutput = new TeeOutputStream(outputStream, stdout)
+        return configurableLauncher
+    }
+
+    T setStandardError(OutputStream outputStream) {
+        configurableLauncher.standardError = new TeeOutputStream(outputStream, stderr)
+        return configurableLauncher
+    }
+}
+
+class ToolingApiBuildLauncher implements BuildLauncher, ToolingApiConfigurableLauncher<BuildLauncher> {
+    @Delegate
+    private final BuildLauncher buildLauncher
+
+    ToolingApiBuildLauncher(BuildLauncher buildLauncher, OutputStream stdout, OutputStream stderr) {
+        initTrait(buildLauncher, stdout, stderr)
+        this.buildLauncher = buildLauncher
+    }
+}
+
+class ToolingApiTestLauncher implements TestLauncher, ToolingApiConfigurableLauncher<TestLauncher> {
+    @Delegate
+    private final TestLauncher testLauncher
+
+    ToolingApiTestLauncher(TestLauncher testLauncher, OutputStream stdout, OutputStream stderr) {
+        initTrait(testLauncher, stdout, stderr)
+        this.testLauncher = testLauncher
+    }
+}
+
+class ToolingApiBuildActionExecuter<T> implements BuildActionExecuter<T>, ToolingApiConfigurableLauncher<BuildActionExecuter<T>> {
+    @Delegate
+    private final BuildActionExecuter<T> buildActionExecutor
+
+    ToolingApiBuildActionExecuter(BuildActionExecuter<T> buildActionExecuter, OutputStream stdout, OutputStream stderr) {
+        initTrait(buildActionExecuter, stdout, stderr)
+        this.buildActionExecutor = buildActionExecuter
+    }
+}
+
+class ToolingApiModelBuilder<T> implements ModelBuilder<T>, ToolingApiConfigurableLauncher<ModelBuilder<T>> {
+    @Delegate
+    private final ModelBuilder<T> modelBuilder
+
+    ToolingApiModelBuilder(ModelBuilder<T> modelBuilder, OutputStream stdout, OutputStream stderr) {
+        initTrait(modelBuilder, stdout, stderr)
+        this.modelBuilder = modelBuilder
+    }
+}
+
+/**
+ * This trait is used to add the missing methods to the ToolingApiConnection class without actually deriving from ProjectConnection.
+ * While still allowing the ToolingApiConnection to be used as a ProjectConnection.
+ * This avoids loading the ProjectConnection class from the test code and postbones loading to the tooling api magic.
+ */
+
+trait ProjectConnectionTrait implements ProjectConnection {
+}
+
+class ToolingApiConnection {
+    private final Object projectConnection
+    private final OutputStream stderr
+    private final OutputStream stdout
+
+    ToolingApiConnection(Object projectConnection, OutputStream stdout, OutputStream stderr) {
+        this.stdout = stdout
+        this.stderr = stderr
+        this.projectConnection = projectConnection
+        this.withTraits(ProjectConnectionTrait)
+    }
+
+    def methodMissing(String name, args) {
+        projectConnection."$name"(*args)
+    }
+
+    BuildActionExecuter.Builder action() {
+        projectConnection.action()
+    }
+
+    BuildLauncher newBuild() {
+        new ToolingApiBuildLauncher(projectConnection.newBuild(), stdout, stderr)
+    }
+
+    TestLauncher newTestLauncher() {
+        new ToolingApiTestLauncher(projectConnection.newTestLauncher(), stdout, stderr)
+    }
+
+    <T> ModelBuilder<T> model(Class<T> modelType) {
+        new ToolingApiModelBuilder(projectConnection.model(modelType), stdout, stderr)
+    }
+
+    <T> BuildActionExecuter<T> action(BuildAction<T> buildAction) {
+        new ToolingApiBuildActionExecuter(projectConnection.action(buildAction), stdout, stderr)
+    }
+}
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiConnector.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiConnector.groovy
new file mode 100644
index 0000000..9d24a5b
--- /dev/null
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiConnector.groovy
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.integtests.tooling.fixture
+
+import org.gradle.tooling.GradleConnector
+import org.gradle.tooling.ProjectConnection
+
+class ToolingApiConnector {
+    GradleConnector connector
+    private final OutputStream stdout
+    private final OutputStream stderr
+
+    ToolingApiConnector(GradleConnector connector, OutputStream stdout, OutputStream stderr) {
+        this.stderr = stderr
+        this.stdout = stdout
+        this.connector = connector
+    }
+
+    def connect() {
+        new ToolingApiConnection(connector.connect(), stdout, stderr) as ProjectConnection
+    }
+
+    def searchUpwards(boolean searchUpwards) {
+        connector.searchUpwards(searchUpwards)
+        this
+    }
+
+    def methodMissing(String name, args) {
+        connector."$name"(*args)
+    }
+
+    def propertyMissing(String name, value) {
+        connector."$name" = value
+    }
+
+    def propertyMissing(String name) {
+        connector."$name"
+    }
+
+    def forProjectDirectory(File projectDir) {
+        connector.forProjectDirectory(projectDir)
+        connector
+    }
+
+    def disconnect() {
+        connector.disconnect()
+    }
+}
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiSpecification.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiSpecification.groovy
index 2ce42ea..a5f146d 100644
--- a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiSpecification.groovy
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiSpecification.groovy
@@ -18,10 +18,10 @@
 
 import groovy.transform.stc.ClosureParams
 import groovy.transform.stc.SimpleType
-import org.apache.commons.io.output.TeeOutputStream
 import org.gradle.integtests.fixtures.RepoScriptBlockUtil
 import org.gradle.integtests.fixtures.build.BuildTestFile
 import org.gradle.integtests.fixtures.build.BuildTestFixture
+import org.gradle.integtests.fixtures.build.TestProjectInitiation
 import org.gradle.integtests.fixtures.daemon.DaemonsFixture
 import org.gradle.integtests.fixtures.executer.ExecutionFailure
 import org.gradle.integtests.fixtures.executer.ExecutionResult
@@ -29,6 +29,7 @@
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
 import org.gradle.integtests.fixtures.executer.OutputScrapingExecutionFailure
 import org.gradle.integtests.fixtures.executer.OutputScrapingExecutionResult
+import org.gradle.integtests.fixtures.executer.ResultAssertion
 import org.gradle.integtests.fixtures.executer.UnderDevelopmentGradleDistribution
 import org.gradle.test.fixtures.file.CleanupTestDirectory
 import org.gradle.test.fixtures.file.TestDistributionDirectoryProvider
@@ -36,7 +37,7 @@
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
 import org.gradle.tooling.GradleConnectionException
 import org.gradle.tooling.GradleConnector
-import org.gradle.tooling.LongRunningOperation
+import org.gradle.tooling.ModelBuilder
 import org.gradle.tooling.ProjectConnection
 import org.gradle.util.GradleVersion
 import org.gradle.util.SetSystemProperties
@@ -61,10 +62,11 @@
  */
 @ToolingApiTest
 @CleanupTestDirectory
-@ToolingApiVersion('>=7.0') // The lowest tested version should be the first release of the previous major.
+// The lowest tested version should be the first release of the previous major.
+@ToolingApiVersion('>=7.0')
 @TargetGradleVersion('>=3.0')
 @Retry(condition = { onIssueWithReleasedGradleVersion(instance, failure) }, mode = SETUP_FEATURE_CLEANUP, count = 2)
-abstract class ToolingApiSpecification extends Specification {
+abstract class ToolingApiSpecification extends Specification implements TestProjectInitiation {
     /**
      * See https://github.com/gradle/gradle-private/issues/3216
      * To avoid flakiness when reusing daemons between CLI and TAPI
@@ -84,7 +86,8 @@
     private GradleDistribution targetGradleDistribution
 
     TestDistributionDirectoryProvider temporaryDistributionFolder = new TestDistributionDirectoryProvider(getClass())
-    final ToolingApi toolingApi = new ToolingApi(null, temporaryFolder)
+    @Delegate
+    final ToolingApi toolingApi = new ToolingApi(null, temporaryFolder, stdout, stderr)
 
     @Rule
     public RuleChain cleanupRule = RuleChain.outerRule(temporaryFolder).around(temporaryDistributionFolder).around(toolingApi)
@@ -101,7 +104,7 @@
     }
 
     GradleDistribution getTargetDist() {
-        if (targetGradleDistribution == null)  {
+        if (targetGradleDistribution == null) {
             throw new IllegalStateException("targetDist is not yet set by the testing framework")
         }
         return targetGradleDistribution
@@ -122,16 +125,23 @@
         temporaryFolder.testDirectory
     }
 
-    TestFile getBuildFile() {
-        file("build.gradle")
-    }
-
+    @Override
     TestFile getBuildFileKts() {
-        file("build.gradle.kts")
+        validateKotlinCompatibility()
+        TestProjectInitiation.super.getBuildFileKts()
     }
 
-    TestFile getSettingsFile() {
-        file("settings.gradle")
+    @Override
+    TestFile getSettingsFileKts() {
+        validateKotlinCompatibility()
+        TestProjectInitiation.super.getSettingsFileKts()
+    }
+
+
+    private validateKotlinCompatibility() {
+        if (targetGradleDistribution && !targetGradleDistribution.supportsKotlinScript) {
+            throw new RuntimeException("The current Gradle target version ($targetGradleDistribution.version) does not support execution of Kotlin build scripts.")
+        }
     }
 
     TestFile file(Object... path) {
@@ -167,7 +177,7 @@
         }
     }
 
-    def <T> T withConnection(GradleConnector connector, @DelegatesTo(ProjectConnection) @ClosureParams(value = SimpleType, options = ["org.gradle.tooling.ProjectConnection"]) Closure<T> cl) {
+    def <T> T withConnection(connector, @DelegatesTo(ProjectConnection) @ClosureParams(value = SimpleType, options = ["org.gradle.tooling.ProjectConnection"]) Closure<T> cl) {
         try {
             return toolingApi.withConnection(connector, cl)
         } catch (GradleConnectionException e) {
@@ -176,7 +186,7 @@
         }
     }
 
-    GradleConnector connector() {
+    ToolingApiConnector connector() {
         toolingApi.connector()
     }
 
@@ -207,11 +217,6 @@
         }
     }
 
-    void collectOutputs(LongRunningOperation op) {
-        op.setStandardOutput(new TeeOutputStream(stdout, System.out))
-        op.setStandardError(new TeeOutputStream(stderr, System.err))
-    }
-
     /**
      * Returns the set of implicit task names expected for any project for the target Gradle version.
      */
@@ -319,13 +324,16 @@
     }
 
     private void assertHasNoDeprecationWarnings() {
-        if (targetVersion < GradleVersion.version("6.9")) {
-            // Older versions have deprecations
-            return
+        if (shouldCheckForDeprecationWarnings()) {
+            assert !stdout.toString()
+                .replace("[deprecated]", "IGNORE") // don't check deprecated command-line argument
+                .containsIgnoreCase("deprecated")
         }
-        assert !stdout.toString()
-            .replace("[deprecated]", "IGNORE") // deprecated command-line argument
-            .containsIgnoreCase("deprecated")
+    }
+
+    def shouldCheckForDeprecationWarnings() {
+        // Older versions have deprecations
+        GradleVersion.version("6.9") < targetVersion
     }
 
     ExecutionResult getResult() {
@@ -336,13 +344,17 @@
         return OutputScrapingExecutionFailure.from(stdout.toString(), stderr.toString())
     }
 
-    def <T> T loadToolingModel(Class<T> modelClass) {
-        def result = withConnection { connection ->
-            def builder = connection.model(modelClass)
-            collectOutputs(builder)
-            builder.get()
-        }
+    def validateOutput() {
+        def assertion = new ResultAssertion(0, [], false, shouldCheckForDeprecationWarnings(), true)
+        assertion.validate(stdout.toString(), "stdout")
+        assertion.validate(stderr.toString(), "stderr")
+        true
+    }
+
+    def <T> T loadToolingModel(Class<T> modelClass, @DelegatesTo(ModelBuilder<T>) Closure cl = {}) {
+        def result = loadToolingLeanModel(modelClass, cl)
         assertHasConfigureSuccessfulLogging()
+        validateOutput()
         return result
     }
 
diff --git a/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiTestCommon.groovy b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiTestCommon.groovy
new file mode 100644
index 0000000..49b6a4d
--- /dev/null
+++ b/subprojects/tooling-api/src/testFixtures/groovy/org/gradle/integtests/tooling/fixture/ToolingApiTestCommon.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2023 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.integtests.tooling.fixture
+
+import org.gradle.api.logging.LogLevel
+import org.gradle.tooling.ProjectConnection
+
+class ToolingApiTestCommon {
+
+    public static final String LOG_LEVEL_TEST_SCRIPT = LogLevel.values()
+        .collect { """logger.${it.name().toLowerCase()}("Hello $it\\n")""" }
+        .join("\n") +
+        """
+        if(project.hasProperty("org.gradle.logging.level")){
+            System.out.println("\\nCurrent log level property value (org.gradle.logging.level): "  + project.property("org.gradle.logging.level"))
+        }
+        else {
+            System.out.println("\\\\nNo property org.gradle.logging.level set")
+        }
+"""
+
+    static getOutputPattern(LogLevel logLevel) {
+        /[\s\S]*Hello $logLevel[\s\S]*/
+    }
+
+    static validateLogs(Object stdOut, LogLevel expectedLevel) {
+        def output = stdOut.toString()
+        LogLevel.values().findAll { it < expectedLevel }.collect {
+            getOutputPattern(it)
+        }.every { !output.matches(it) }
+            &&
+            LogLevel.values().findAll { it >= expectedLevel }.collect {
+                getOutputPattern(it)
+            }.every { output.matches(it) }
+
+    }
+
+    static runLogScript(ToolingApi tapi, List<String> arguments) {
+        def stdOut = new ByteArrayOutputStream()
+        tapi.withConnection { ProjectConnection connection ->
+            connection.newBuild()
+                .withArguments(arguments)
+                .setStandardOutput(stdOut)
+                .setStandardError(stdOut)
+                .run()
+        }
+        stdOut
+    }
+}
diff --git a/subprojects/tooling-native/src/crossVersionTest/groovy/org/gradle/language/cpp/tooling/r410/CppModelCrossVersionSpec.groovy b/subprojects/tooling-native/src/crossVersionTest/groovy/org/gradle/language/cpp/tooling/r410/CppModelCrossVersionSpec.groovy
index 8932e44..4e46f68 100644
--- a/subprojects/tooling-native/src/crossVersionTest/groovy/org/gradle/language/cpp/tooling/r410/CppModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-native/src/crossVersionTest/groovy/org/gradle/language/cpp/tooling/r410/CppModelCrossVersionSpec.groovy
@@ -20,6 +20,8 @@
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.model.cpp.CppApplication
 import org.gradle.tooling.model.cpp.CppExecutable
 import org.gradle.tooling.model.cpp.CppLibrary
@@ -27,12 +29,10 @@
 import org.gradle.tooling.model.cpp.CppSharedLibrary
 import org.gradle.tooling.model.cpp.CppStaticLibrary
 import org.gradle.tooling.model.cpp.CppTestSuite
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
 @ToolingApiVersion(">=4.10")
 @TargetGradleVersion(">=4.10")
-@Requires(TestPrecondition.NOT_MAC_OS_X_M1) // TODO KM how to limit non-backwards compatible checks when aarch64 is not available on Gradle 7.5 and prior?
+@Requires(UnitTestPreconditions.NotMacOsM1) // TODO KM how to limit non-backwards compatible checks when aarch64 is not available on Gradle 7.5 and prior?
 class CppModelCrossVersionSpec extends ToolingApiSpecification {
     def toolchain = AvailableToolChains.defaultToolChain
 
diff --git a/subprojects/tooling-native/src/crossVersionTest/groovy/org/gradle/language/cpp/tooling/r52/CppModelCrossVersionSpec.groovy b/subprojects/tooling-native/src/crossVersionTest/groovy/org/gradle/language/cpp/tooling/r52/CppModelCrossVersionSpec.groovy
index 77a4c89..68c9c32 100644
--- a/subprojects/tooling-native/src/crossVersionTest/groovy/org/gradle/language/cpp/tooling/r52/CppModelCrossVersionSpec.groovy
+++ b/subprojects/tooling-native/src/crossVersionTest/groovy/org/gradle/language/cpp/tooling/r52/CppModelCrossVersionSpec.groovy
@@ -20,17 +20,17 @@
 import org.gradle.integtests.tooling.fixture.ToolingApiSpecification
 import org.gradle.integtests.tooling.fixture.ToolingApiVersion
 import org.gradle.nativeplatform.fixtures.AvailableToolChains
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.tooling.model.cpp.CppApplication
 import org.gradle.tooling.model.cpp.CppExecutable
 import org.gradle.tooling.model.cpp.CppLibrary
 import org.gradle.tooling.model.cpp.CppProject
 import org.gradle.tooling.model.cpp.CppSharedLibrary
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
 
 @ToolingApiVersion(">=5.2")
 @TargetGradleVersion(">=5.2")
-@Requires(TestPrecondition.NOT_MAC_OS_X_M1) // TODO KM how to limit non-backwards compatible checks when aarch64 is not available on Gradle 7.5 and prior?
+@Requires(UnitTestPreconditions.NotMacOsM1) // TODO KM how to limit non-backwards compatible checks when aarch64 is not available on Gradle 7.5 and prior?
 class CppModelCrossVersionSpec extends ToolingApiSpecification {
     def toolchain = AvailableToolChains.defaultToolChain
 
diff --git a/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/NestedSourceDependencyIdentityIntegrationTest.groovy b/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/NestedSourceDependencyIdentityIntegrationTest.groovy
index 266800c..fba3f16 100644
--- a/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/NestedSourceDependencyIdentityIntegrationTest.groovy
+++ b/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/NestedSourceDependencyIdentityIntegrationTest.groovy
@@ -157,18 +157,22 @@
             classes.doLast {
                 def components = configurations.runtimeClasspath.incoming.resolutionResult.allComponents.id
                 assert components.size() == 4
+                assert components[0].build.buildPath == ':'
                 assert components[0].build.name == ':'
                 assert components[0].build.currentBuild
                 assert components[0].projectPath == ':'
                 assert components[0].projectName == 'buildA'
+                assert components[1].build.buildPath == ':buildB'
                 assert components[1].build.name == 'buildB'
                 assert !components[1].build.currentBuild
                 assert components[1].projectPath == ':'
                 assert components[1].projectName == 'buildB'
+                assert components[2].build.buildPath == ':${buildName}'
                 assert components[2].build.name == '${buildName}'
                 assert !components[2].build.currentBuild
                 assert components[2].projectPath == ':'
-                assert components[2].projectName == '${buildName}'
+                assert components[2].projectName == '${dependencyName}'
+                assert components[3].build.buildPath == ':${buildName}'
                 assert components[3].build.name == '${buildName}'
                 assert !components[3].build.currentBuild
                 assert components[3].projectPath == ':a'
@@ -179,12 +183,18 @@
                 assert selectors[0].displayName == 'org.test:buildB:1.2'
                 assert selectors[1].displayName == 'org.test:${dependencyName}:1.2'
                 assert selectors[2].displayName == 'project :${buildName}:a'
-                // TODO = should be $buildName
-                assert selectors[2].buildName == 'buildC'
+                assert selectors[2].buildPath == ':${buildName}'
+                assert selectors[2].buildName == '${buildName}'
                 assert selectors[2].projectPath == ':a'
             }
         """
 
+        4.times {
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.isCurrentBuild() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+        }
+        executer.expectDocumentedDeprecationWarning("The ProjectComponentSelector.getBuildName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+
         expect:
         succeeds(":assemble")
 
diff --git a/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/SourceDependencyIdentityIntegrationTest.groovy b/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/SourceDependencyIdentityIntegrationTest.groovy
index 9baab1e..d0fa6df 100644
--- a/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/SourceDependencyIdentityIntegrationTest.groovy
+++ b/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/SourceDependencyIdentityIntegrationTest.groovy
@@ -130,14 +130,17 @@
             classes.doLast {
                 def components = configurations.runtimeClasspath.incoming.resolutionResult.allComponents.id
                 assert components.size() == 3
+                assert components[0].build.buildPath == ':'
                 assert components[0].build.name == ':'
                 assert components[0].build.currentBuild
                 assert components[0].projectPath == ':'
                 assert components[0].projectName == 'buildA'
+                assert components[1].build.buildPath == ':${buildName}'
                 assert components[1].build.name == '${buildName}'
                 assert !components[1].build.currentBuild
                 assert components[1].projectPath == ':'
-                assert components[1].projectName == '${buildName}'
+                assert components[1].projectName == '${dependencyName}'
+                assert components[2].build.buildPath == ':${buildName}'
                 assert components[2].build.name == '${buildName}'
                 assert !components[2].build.currentBuild
                 assert components[2].projectPath == ':a'
@@ -147,12 +150,18 @@
                 assert selectors.size() == 2
                 assert selectors[0].displayName == 'org.test:${dependencyName}:1.2'
                 assert selectors[1].displayName == 'project :${buildName}:a'
-                // TODO - should be buildB
-                assert selectors[1].buildName == 'buildB'
+                assert selectors[1].buildPath == ':${buildName}'
+                assert selectors[1].buildName == '${buildName}'
                 assert selectors[1].projectPath == ':a'
             }
         """
 
+        3.times {
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.getName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+            executer.expectDocumentedDeprecationWarning("The BuildIdentifier.isCurrentBuild() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+        }
+        executer.expectDocumentedDeprecationWarning("The ProjectComponentSelector.getBuildName() method has been deprecated. This is scheduled to be removed in Gradle 9.0. Use getBuildPath() to get a unique identifier for the build. Consult the upgrading guide for further information: https://docs.gradle.org/current/userguide/upgrading_version_8.html#build_identifier_name_and_current_deprecation")
+
         expect:
         succeeds(":assemble")
 
diff --git a/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/SourceDependencyIncludedBuildIntegrationTest.groovy b/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/SourceDependencyIncludedBuildIntegrationTest.groovy
index 0bdd01e..9f79b35 100644
--- a/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/SourceDependencyIncludedBuildIntegrationTest.groovy
+++ b/subprojects/version-control/src/integTest/groovy/org/gradle/vcs/internal/SourceDependencyIncludedBuildIntegrationTest.groovy
@@ -56,6 +56,6 @@
         then:
         failure.assertHasDescription("Could not determine the dependencies of task ':compileJava'.")
         failure.assertHasCause("Could not resolve all dependencies for configuration ':compileClasspath'.")
-        failure.assertHasCause("Cannot include build 'child' in build 'buildB'. This is not supported yet.")
+        failure.assertHasCause("Cannot include build 'child' in build ':buildB'. This is not supported yet.")
     }
 }
diff --git a/subprojects/version-control/src/main/java/org/gradle/vcs/internal/resolver/VcsDependencyResolver.java b/subprojects/version-control/src/main/java/org/gradle/vcs/internal/resolver/VcsDependencyResolver.java
index 1721c44..47c4d40 100644
--- a/subprojects/version-control/src/main/java/org/gradle/vcs/internal/resolver/VcsDependencyResolver.java
+++ b/subprojects/version-control/src/main/java/org/gradle/vcs/internal/resolver/VcsDependencyResolver.java
@@ -34,10 +34,9 @@
 import org.gradle.internal.build.PublicBuildPath;
 import org.gradle.internal.component.local.model.LocalComponentGraphResolveState;
 import org.gradle.internal.component.model.ComponentArtifactMetadata;
+import org.gradle.internal.component.model.ComponentArtifactResolveMetadata;
 import org.gradle.internal.component.model.ComponentOverrideMetadata;
-import org.gradle.internal.component.model.ComponentResolveMetadata;
 import org.gradle.internal.component.model.DependencyMetadata;
-import org.gradle.internal.component.model.ModuleSources;
 import org.gradle.internal.resolve.ModuleVersionNotFoundException;
 import org.gradle.internal.resolve.ModuleVersionResolveException;
 import org.gradle.internal.resolve.resolver.ArtifactResolver;
@@ -175,10 +174,10 @@ public ArtifactResolver getArtifactResolver() {
     }
 
     @Override
-    public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
+    public void resolveArtifactsWithType(ComponentArtifactResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) {
     }
 
     @Override
-    public void resolveArtifact(ModuleVersionIdentifier ownerId, ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactResolveResult result) {
+    public void resolveArtifact(ComponentArtifactResolveMetadata component, ComponentArtifactMetadata artifact, BuildableArtifactResolveResult result) {
     }
 }
diff --git a/subprojects/version-control/src/test/groovy/org/gradle/vcs/git/internal/GitVersionControlSystemSpec.groovy b/subprojects/version-control/src/test/groovy/org/gradle/vcs/git/internal/GitVersionControlSystemSpec.groovy
index e93a9a5..4a85758 100644
--- a/subprojects/version-control/src/test/groovy/org/gradle/vcs/git/internal/GitVersionControlSystemSpec.groovy
+++ b/subprojects/version-control/src/test/groovy/org/gradle/vcs/git/internal/GitVersionControlSystemSpec.groovy
@@ -20,8 +20,8 @@
 import org.eclipse.jgit.revwalk.RevCommit
 import org.gradle.api.GradleException
 import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.vcs.fixtures.GitFileRepository
 import org.gradle.vcs.git.GitVersionControlSpec
 import org.gradle.vcs.internal.VersionRef
@@ -115,7 +115,7 @@
     }
 
     // commit() method seems to leak files
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def 'reset a cloned repository with local commits'() {
         given:
         def target = tmpDir.file('versionDir')
diff --git a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/JdkIllegalReflectionTestWorkerIntegrationTest.groovy b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/JdkIllegalReflectionTestWorkerIntegrationTest.groovy
index 46affc8..6a477b3 100644
--- a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/JdkIllegalReflectionTestWorkerIntegrationTest.groovy
+++ b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/JdkIllegalReflectionTestWorkerIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
 import org.gradle.integtests.fixtures.DefaultTestExecutionResult
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.Issue
 
 import static org.hamcrest.CoreMatchers.containsString
@@ -71,7 +71,7 @@
         """
     }
 
-    @Requires(TestPrecondition.JDK16_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk16OrLater)
     @Issue("https://github.com/gradle/gradle/issues/19771")
     def "both tests and production code fail when application uses illegal reflection"() {
         when:
@@ -88,7 +88,7 @@
         results.testClass("example.MainTest").assertTestFailed("runTest", containsString('module java.base does not open java.lang to unnamed module'))
     }
 
-    @Requires(TestPrecondition.JDK16_OR_LATER)
+    @Requires(UnitTestPreconditions.Jdk16OrLater)
     @Issue("https://github.com/gradle/gradle/issues/19771")
     def "can add --add-opens flag in test to permit reflection"() {
         given:
diff --git a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonIntegrationTest.groovy b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonIntegrationTest.groovy
index 4f7da0c..9f6dd55 100644
--- a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonIntegrationTest.groovy
+++ b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonIntegrationTest.groovy
@@ -20,7 +20,8 @@
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.timeout.IntegrationTestTimeout
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.TestPrecondition
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.workers.fixtures.OptionsVerifier
 import org.gradle.workers.fixtures.WorkerExecutorFixture
 import org.junit.Assume
@@ -30,7 +31,7 @@
 
 @IntegrationTestTimeout(180)
 class WorkerDaemonIntegrationTest extends AbstractWorkerExecutorIntegrationTest {
-    boolean isOracleJDK = TestPrecondition.JDK_ORACLE.fulfilled && (Jvm.current().jre != null)
+    boolean isOracleJDK = TestPrecondition.doSatisfies(UnitTestPreconditions.JdkOracle) && (Jvm.current().jre != null)
 
     WorkerExecutorFixture.WorkActionClass workActionThatPrintsWorkingDirectory
 
diff --git a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonLifecycleTest.groovy b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonLifecycleTest.groovy
index bb878af..a4711f1 100644
--- a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonLifecycleTest.groovy
+++ b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonLifecycleTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.ProcessFixture
 import org.gradle.integtests.fixtures.timeout.IntegrationTestTimeout
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.workers.fixtures.WorkerExecutorFixture
 
 @IntegrationTestTimeout(180)
@@ -173,7 +173,7 @@
         sinceSnapshot().contains("Stopped 2 worker daemon(s).")
     }
 
-    @Requires(TestPrecondition.UNIX)
+    @Requires(UnitTestPreconditions.Unix)
     def "worker daemons exit after the build is complete"() {
         fixture.withWorkActionClassInBuildScript()
         buildFile << """
diff --git a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonLoggingIntegrationTest.groovy b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonLoggingIntegrationTest.groovy
index 1d39651..b69eb65 100644
--- a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonLoggingIntegrationTest.groovy
+++ b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerDaemonLoggingIntegrationTest.groovy
@@ -19,7 +19,6 @@
 import org.gradle.integtests.fixtures.BuildOperationsFixture
 import org.gradle.test.fixtures.ConcurrentTestUtil
 import org.gradle.test.fixtures.server.http.BlockingHttpServer
-import org.gradle.util.internal.TextUtil
 import org.junit.Rule
 import spock.lang.Issue
 
@@ -69,23 +68,14 @@
 
     def "log messages are still delivered to the build process after a worker action runs"() {
         def lastOutput = ""
-        def startFile = file("start")
-        def stopFile = file("stop")
 
+        given:
         server.start()
 
         workActionThatProducesLotsOfOutput.action += """
             new Thread({
-                while (true) {
-                    sleep 1000
-                    println "checking..."
-                    if (new File("${TextUtil.normaliseFileSeparators(startFile.absolutePath)}").exists()) {
-                        println "beep..."
-                    }
-                    if (new File("${TextUtil.normaliseFileSeparators(stopFile.absolutePath)}").exists()) {
-                        break
-                    }
-                }
+                ${server.callFromBuild("beep")}
+                println "beep..."
             }).start()
         """
         workActionThatProducesLotsOfOutput.writeToBuildFile()
@@ -100,17 +90,14 @@
         """
 
         when:
-        def handler = server.expectAndBlock("block")
+        def handler = server.expectConcurrentAndBlock("block", "beep")
         def gradle = executer.withTasks("block").start()
 
         then:
         handler.waitForAllPendingCalls()
+        handler.release("beep")
 
         when:
-        lastOutput = gradle.standardOutput
-        startFile.createFile()
-
-        then:
         ConcurrentTestUtil.poll {
             def newOutput = gradle.standardOutput - lastOutput
             lastOutput = gradle.standardOutput
@@ -118,7 +105,6 @@
         }
 
         then:
-        stopFile.createFile()
         handler.releaseAll()
 
         then:
diff --git a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerExecutorErrorHandlingIntegrationTest.groovy b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerExecutorErrorHandlingIntegrationTest.groovy
index 40351e2..c6065cf 100644
--- a/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerExecutorErrorHandlingIntegrationTest.groovy
+++ b/subprojects/workers/src/integTest/groovy/org/gradle/workers/internal/WorkerExecutorErrorHandlingIntegrationTest.groovy
@@ -19,8 +19,8 @@
 import org.gradle.integtests.fixtures.ToBeFixedForConfigurationCache
 import org.gradle.integtests.fixtures.timeout.IntegrationTestTimeout
 import org.gradle.internal.jvm.Jvm
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.workers.fixtures.WorkerExecutorFixture
 
 import static org.gradle.workers.fixtures.WorkerExecutorFixture.ISOLATION_MODES
@@ -259,7 +259,7 @@
         isolationMode << ISOLATION_MODES
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "produces a sensible error when worker fails before logging is initialized"() {
         fixture.withWorkActionClassInBuildScript()
 
diff --git a/subprojects/workers/src/main/java/org/gradle/workers/internal/DefaultWorkerExecutor.java b/subprojects/workers/src/main/java/org/gradle/workers/internal/DefaultWorkerExecutor.java
index cb5f181..b958666 100644
--- a/subprojects/workers/src/main/java/org/gradle/workers/internal/DefaultWorkerExecutor.java
+++ b/subprojects/workers/src/main/java/org/gradle/workers/internal/DefaultWorkerExecutor.java
@@ -25,6 +25,7 @@
 import org.gradle.internal.classpath.DefaultClassPath;
 import org.gradle.internal.exceptions.Contextual;
 import org.gradle.internal.exceptions.DefaultMultiCauseException;
+import org.gradle.internal.exceptions.NonGradleCauseExceptionsHolder;
 import org.gradle.internal.isolated.IsolationScheme;
 import org.gradle.internal.operations.BuildOperationExecutor;
 import org.gradle.internal.operations.BuildOperationRef;
@@ -245,20 +246,14 @@ WorkerRequirement getWorkerRequirement(Class<?> executionClass, WorkerSpec confi
     }
 
     private Class<?>[] getParamClasses(Class<?> actionClass, WorkParameters parameters) {
-        Object[] params = new Object[]{parameters};
-
-        List<Class<?>> classes = Lists.newArrayList();
-        classes.add(actionClass);
-        for (Object param : params) {
-            if (param != null) {
-                classes.add(param.getClass());
-            }
+        if (parameters != null) {
+            return new Class<?>[]{actionClass, parameters.getClass()};
         }
-        return classes.toArray(new Class<?>[0]);
+        return new Class<?>[]{actionClass};
     }
 
     @Contextual
-    private static class WorkExecutionException extends RuntimeException {
+    private static class WorkExecutionException extends RuntimeException implements NonGradleCauseExceptionsHolder {
         WorkExecutionException(String description, Throwable cause) {
             super(toMessage(description), cause);
         }
@@ -266,6 +261,11 @@ private static class WorkExecutionException extends RuntimeException {
         private static String toMessage(String description) {
             return "A failure occurred while executing " + description;
         }
+
+        @Override
+        public boolean hasCause(Class<?> type) {
+            return type.isInstance(getCause());
+        }
     }
 
     private static class WorkItemExecution extends AbstractConditionalExecution<DefaultWorkResult> implements AsyncWorkCompletion {
diff --git a/subprojects/workers/src/main/java/org/gradle/workers/internal/WorkerDaemonServer.java b/subprojects/workers/src/main/java/org/gradle/workers/internal/WorkerDaemonServer.java
index 8487cf2..f0fdb54 100644
--- a/subprojects/workers/src/main/java/org/gradle/workers/internal/WorkerDaemonServer.java
+++ b/subprojects/workers/src/main/java/org/gradle/workers/internal/WorkerDaemonServer.java
@@ -122,7 +122,7 @@ public String toString() {
 
     private static class WorkerDaemonServices extends WorkerSharedUserHomeScopeServices {
 
-        // TODO: configuration-cache - deprecate workers access to ProviderFactory?
+        // TODO:configuration-cache - deprecate workers access to ProviderFactory?
         ProviderFactory createProviderFactory() {
             return new DefaultProviderFactory();
         }
diff --git a/subprojects/workers/src/test/groovy/org/gradle/process/internal/worker/child/BootstrapSecurityManagerTest.groovy b/subprojects/workers/src/test/groovy/org/gradle/process/internal/worker/child/BootstrapSecurityManagerTest.groovy
index a3def28..59ac555 100644
--- a/subprojects/workers/src/test/groovy/org/gradle/process/internal/worker/child/BootstrapSecurityManagerTest.groovy
+++ b/subprojects/workers/src/test/groovy/org/gradle/process/internal/worker/child/BootstrapSecurityManagerTest.groovy
@@ -18,9 +18,9 @@
 
 import org.gradle.integtests.fixtures.RedirectStdIn
 import org.gradle.internal.stream.EncodedStream
-import org.gradle.util.Requires
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Specification
 
@@ -28,7 +28,7 @@
 import java.security.Permission
 
 @RedirectStdIn
-@Requires(TestPrecondition.JDK8_OR_EARLIER)
+@Requires(UnitTestPreconditions.Jdk8OrEarlier)
 class BootstrapSecurityManagerTest extends Specification {
     @Rule SetSystemProperties systemProperties
 
diff --git a/subprojects/wrapper-shared/build.gradle.kts b/subprojects/wrapper-shared/build.gradle.kts
index b953746..b9abf9b 100644
--- a/subprojects/wrapper-shared/build.gradle.kts
+++ b/subprojects/wrapper-shared/build.gradle.kts
@@ -14,6 +14,7 @@
     testImplementation(libs.commonsCompress)
 
     integTestImplementation(project(":dependency-management"))
+    integTestImplementation(project(":logging"))
 
     integTestDistributionRuntimeOnly(project(":distributions-full"))
 }
diff --git a/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/Download.java b/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/Download.java
index f2e44c3..1786a7e 100644
--- a/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/Download.java
+++ b/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/Download.java
@@ -24,6 +24,7 @@
 import java.io.OutputStream;
 import java.lang.reflect.Method;
 import java.net.Authenticator;
+import java.net.HttpURLConnection;
 import java.net.PasswordAuthentication;
 import java.net.SocketTimeoutException;
 import java.net.URI;
@@ -84,6 +85,25 @@ private void configureProxyAuthentication() {
         }
     }
 
+    public void sendHeadRequest(URI uri) throws Exception {
+        URL safeUrl = safeUri(uri).toURL();
+        int responseCode = -1;
+        try {
+            HttpURLConnection conn = (HttpURLConnection)safeUrl.openConnection();
+            conn.setRequestMethod("HEAD");
+            addBasicAuthentication(uri, conn);
+            conn.setRequestProperty("User-Agent", calculateUserAgent());
+            conn.setConnectTimeout(networkTimeout);
+            conn.connect();
+            responseCode = conn.getResponseCode();
+            if (responseCode != 200) {
+                throw new RuntimeException("HEAD request to " + safeUrl + " failed: response code (" + responseCode + ")");
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("HEAD request to " + safeUrl + " failed: response code (" + responseCode + "), timeout (" + networkTimeout + "ms)", e);
+        }
+    }
+
     public void download(URI address, File destination) throws Exception {
         destination.getParentFile().mkdirs();
         downloadInternal(address, destination);
diff --git a/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/WrapperConfiguration.java b/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/WrapperConfiguration.java
index fe9cdbe..e48fc7b 100644
--- a/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/WrapperConfiguration.java
+++ b/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/WrapperConfiguration.java
@@ -25,6 +25,7 @@ public class WrapperConfiguration {
     private String zipBase = PathAssembler.GRADLE_USER_HOME_STRING;
     private String zipPath = Install.DEFAULT_DISTRIBUTION_PATH;
     private int networkTimeout = Download.DEFAULT_NETWORK_TIMEOUT_MILLISECONDS;
+    private boolean validateDistributionUrl = true;
 
     public URI getDistribution() {
         return distribution;
@@ -81,4 +82,12 @@ public int getNetworkTimeout() {
     public void setNetworkTimeout(int networkTimeout) {
         this.networkTimeout = networkTimeout;
     }
+
+    public boolean getValidateDistributionUrl() {
+        return validateDistributionUrl;
+    }
+
+    public void setValidateDistributionUrl(boolean validateDistributionUrl) {
+        this.validateDistributionUrl = validateDistributionUrl;
+    }
 }
diff --git a/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/WrapperExecutor.java b/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/WrapperExecutor.java
index 117e871..f59d21e 100644
--- a/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/WrapperExecutor.java
+++ b/subprojects/wrapper-shared/src/main/java/org/gradle/wrapper/WrapperExecutor.java
@@ -31,6 +31,7 @@ public class WrapperExecutor {
     public static final String ZIP_STORE_BASE_PROPERTY = "zipStoreBase";
     public static final String ZIP_STORE_PATH_PROPERTY = "zipStorePath";
     public static final String NETWORK_TIMEOUT_PROPERTY = "networkTimeout";
+    public static final String VALIDATE_DISTRIBUTION_URL = "validateDistributionUrl";
     private final Properties properties;
     private final File propertiesFile;
     private final WrapperConfiguration config = new WrapperConfiguration();
@@ -59,6 +60,7 @@ public static WrapperExecutor forWrapperPropertiesFile(File propertiesFile) {
                 config.setZipBase(getProperty(ZIP_STORE_BASE_PROPERTY, config.getZipBase()));
                 config.setZipPath(getProperty(ZIP_STORE_PATH_PROPERTY, config.getZipPath()));
                 config.setNetworkTimeout(getProperty(NETWORK_TIMEOUT_PROPERTY, config.getNetworkTimeout()));
+                config.setValidateDistributionUrl(getProperty(VALIDATE_DISTRIBUTION_URL, config.getValidateDistributionUrl()));
             } catch (Exception e) {
                 throw new RuntimeException(String.format("Could not load wrapper properties from '%s'.", propertiesFile), e);
             }
@@ -122,6 +124,10 @@ private int getProperty(String propertyName, int defaultValue) {
         return Integer.parseInt(getProperty(propertyName, String.valueOf(defaultValue)));
     }
 
+    private boolean getProperty(String propertyName, boolean defaultValue) {
+        return Boolean.parseBoolean(getProperty(propertyName, String.valueOf(defaultValue)));
+    }
+
     private String getProperty(String propertyName, String defaultValue, boolean required) {
         String value = properties.getProperty(propertyName);
         if (value != null) {
diff --git a/subprojects/wrapper-shared/src/test/groovy/org/gradle/wrapper/GradleUserHomeLookupTest.groovy b/subprojects/wrapper-shared/src/test/groovy/org/gradle/wrapper/GradleUserHomeLookupTest.groovy
index 625f68b..1076eed 100644
--- a/subprojects/wrapper-shared/src/test/groovy/org/gradle/wrapper/GradleUserHomeLookupTest.groovy
+++ b/subprojects/wrapper-shared/src/test/groovy/org/gradle/wrapper/GradleUserHomeLookupTest.groovy
@@ -17,10 +17,10 @@
 package org.gradle.wrapper
 
 import org.gradle.internal.nativeintegration.ProcessEnvironment
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.testfixtures.internal.NativeServicesTestFixture
-import org.gradle.util.Requires
 import org.gradle.util.SetSystemProperties
-import org.gradle.util.TestPrecondition
 import org.junit.Rule
 import spock.lang.Issue
 import spock.lang.Specification
@@ -32,7 +32,7 @@
     @Rule
     SetSystemProperties setSystemProperties = new SetSystemProperties()
 
-    @Requires(TestPrecondition.NOT_EC2_AGENT)
+    @Requires(UnitTestPreconditions.NotEC2Agent)
     @Issue('https://github.com/gradle/gradle-private/issues/2876')
     def "returns default Gradle user home if environment variable or system property isn't defined"() {
         expect:
diff --git a/subprojects/wrapper-shared/src/test/groovy/org/gradle/wrapper/WrapperExecutorTest.groovy b/subprojects/wrapper-shared/src/test/groovy/org/gradle/wrapper/WrapperExecutorTest.groovy
index 7fdcbce..36ac7b8 100644
--- a/subprojects/wrapper-shared/src/test/groovy/org/gradle/wrapper/WrapperExecutorTest.groovy
+++ b/subprojects/wrapper-shared/src/test/groovy/org/gradle/wrapper/WrapperExecutorTest.groovy
@@ -40,6 +40,7 @@
         properties.zipStorePath = 'testZipPath'
         properties.distributionSha256Sum = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
         properties.networkTimeout = '11000'
+        properties.validateDistributionUrl = 'true'
         propertiesFile.parentFile.mkdirs()
         propertiesFile.withOutputStream { properties.store(it, 'header') }
     }
@@ -56,6 +57,7 @@
         wrapper.configuration.zipPath == 'testZipPath'
         wrapper.configuration.distributionSha256Sum == 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
         wrapper.configuration.networkTimeout == 11000
+        wrapper.configuration.validateDistributionUrl
     }
 
     def "loads wrapper meta data from specified project directory"() {
@@ -70,6 +72,7 @@
         wrapper.configuration.zipPath == 'testZipPath'
         wrapper.configuration.distributionSha256Sum == 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
         wrapper.configuration.networkTimeout == 11000
+        wrapper.configuration.validateDistributionUrl
     }
 
     def "uses default meta data when properties file does not exist in project directory"() {
@@ -84,6 +87,7 @@
         wrapper.configuration.zipPath == Install.DEFAULT_DISTRIBUTION_PATH
         wrapper.configuration.distributionSha256Sum == null
         wrapper.configuration.networkTimeout == Download.DEFAULT_NETWORK_TIMEOUT_MILLISECONDS
+        wrapper.configuration.validateDistributionUrl
     }
 
     def "properties file need contain only the distribution URL"() {
@@ -102,6 +106,7 @@
         wrapper.configuration.zipBase == PathAssembler.GRADLE_USER_HOME_STRING
         wrapper.configuration.zipPath == Install.DEFAULT_DISTRIBUTION_PATH
         wrapper.configuration.networkTimeout == Download.DEFAULT_NETWORK_TIMEOUT_MILLISECONDS
+        wrapper.configuration.validateDistributionUrl
     }
 
     def "execute installs distribution and launches application"() {
diff --git a/subprojects/wrapper/src/crossVersionTest/groovy/org/gradle/integtests/wrapper/WrapperCrossVersionIntegrationTest.groovy b/subprojects/wrapper/src/crossVersionTest/groovy/org/gradle/integtests/wrapper/WrapperCrossVersionIntegrationTest.groovy
index a6ef7a4..3d9d6bc 100644
--- a/subprojects/wrapper/src/crossVersionTest/groovy/org/gradle/integtests/wrapper/WrapperCrossVersionIntegrationTest.groovy
+++ b/subprojects/wrapper/src/crossVersionTest/groovy/org/gradle/integtests/wrapper/WrapperCrossVersionIntegrationTest.groovy
@@ -18,14 +18,14 @@
 import org.gradle.integtests.fixtures.AvailableJavaHomes
 import org.gradle.integtests.fixtures.CrossVersionIntegrationSpec
 import org.gradle.integtests.fixtures.daemon.DaemonLogsAnalyzer
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.GradleDistribution
 import org.gradle.integtests.fixtures.executer.GradleExecuter
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
 import org.junit.Assume
-import spock.lang.IgnoreIf
 
 @SuppressWarnings("IntegrationTestFixtures")
 class WrapperCrossVersionIntegrationTest extends CrossVersionIntegrationSpec {
@@ -44,7 +44,10 @@
         cleanupDaemons(executer, current)
     }
 
-    @IgnoreIf({ GradleContextualExecuter.embedded }) // wrapperExecuter requires a real distribution
+    @Requires(value = [
+        IntegTestPreconditions.NotEmbeddedExecutor,
+        UnitTestPreconditions.NotWindowsJavaBefore11
+    ], reason = "wrapperExecuter requires a real distribution, https://github.com/gradle/gradle-private/issues/3758")
     void canUseWrapperFromCurrentVersionToRunPreviousVersion() {
         when:
         GradleExecuter executer = prepareWrapperExecuter(current, previous).withWarningMode(null)
@@ -56,7 +59,7 @@
         cleanupDaemons(executer, previous)
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdks("1.6", "1.7") })
+    @Requires(IntegTestPreconditions.UnsupportedJavaHomeAvailable)
     def 'provides reasonable failure message when attempting to run current Version with previous wrapper under java #jdk.javaVersion'() {
         when:
         GradleExecuter executor = prepareWrapperExecuter(previous, current).withJavaHome(jdk.javaHome)
@@ -69,7 +72,7 @@
         jdk << AvailableJavaHomes.getJdks("1.6", "1.7")
     }
 
-    @Requires(adhoc = { AvailableJavaHomes.getJdks("1.6", "1.7") })
+    @Requires(IntegTestPreconditions.UnsupportedJavaHomeAvailable)
     def 'provides reasonable failure message when attempting to run with previous wrapper and the build is configured to use Java #jdk.javaVersion'() {
         when:
         GradleExecuter executor = prepareWrapperExecuter(previous, current)
@@ -146,4 +149,3 @@
         new DaemonLogsAnalyzer(executer.daemonBaseDir, executionVersion.version.version).killAll()
     }
 }
-
diff --git a/subprojects/wrapper/src/crossVersionTest/groovy/org/gradle/integtests/wrapper/WrapperPropertiesLoaderCrossVersionTest.groovy b/subprojects/wrapper/src/crossVersionTest/groovy/org/gradle/integtests/wrapper/WrapperPropertiesLoaderCrossVersionTest.groovy
index b6296cd..08d994e 100644
--- a/subprojects/wrapper/src/crossVersionTest/groovy/org/gradle/integtests/wrapper/WrapperPropertiesLoaderCrossVersionTest.groovy
+++ b/subprojects/wrapper/src/crossVersionTest/groovy/org/gradle/integtests/wrapper/WrapperPropertiesLoaderCrossVersionTest.groovy
@@ -20,6 +20,8 @@
 import org.gradle.integtests.fixtures.daemon.DaemonLogsAnalyzer
 import org.gradle.integtests.fixtures.executer.GradleDistribution
 import org.gradle.integtests.fixtures.executer.GradleExecuter
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import org.gradle.util.GradleVersion
 import spock.lang.Issue
 
@@ -30,6 +32,10 @@
 class WrapperPropertiesLoaderCrossVersionTest extends CrossVersionIntegrationSpec {
 
     @Issue('https://github.com/gradle/gradle/issues/11173')
+    @Requires(
+        value = UnitTestPreconditions.NotWindowsJavaBefore11,
+        reason = 'https://github.com/gradle/gradle-private/issues/3758'
+    )
     void "System properties defined in gradle.properties are available in buildSrc and in included builds"() {
         given:
         GradleDistribution wrapperVersion = previous
@@ -106,4 +112,3 @@
         new DaemonLogsAnalyzer(executer.daemonBaseDir, executionVersion.version.version).killAll()
     }
 }
-
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/AbstractWrapperIntegrationSpec.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/AbstractWrapperIntegrationSpec.groovy
index bc6bfe1..849c373 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/AbstractWrapperIntegrationSpec.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/AbstractWrapperIntegrationSpec.groovy
@@ -37,6 +37,14 @@
         executer.withArguments("wrapper", "--gradle-distribution-url", distributionUri.toString()).run()
     }
 
+    void prepareWrapper(URI distributionUri = distribution.binDistribution.toURI(), String trustStore, String trustStorePassword) {
+        def executer = new InProcessGradleExecuter(distribution, temporaryFolder)
+        executer.withArguments("wrapper", "--gradle-distribution-url", distributionUri.toString(),
+            "-Djavax.net.ssl.trustStore=$trustStore",
+            "-Djavax.net.ssl.trustStorePassword=$trustStorePassword")
+            .run()
+    }
+
     GradleExecuter getWrapperExecuter() {
         executer.requireOwnGradleUserHomeDir()
         executer.requireIsolatedDaemons()
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperBadArchiveTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperBadArchiveTest.groovy
index e75b285..102bbb8 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperBadArchiveTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperBadArchiveTest.groovy
@@ -42,6 +42,7 @@
 
     def "wrapper gets bad archive on 1 attempt"() {
         given:
+        server.expect(server.head(GRADLE_BIN_ZIP))
         server.expect(server.get(GRADLE_BIN_ZIP).sendFile(badArchive))
         server.expect(server.get(GRADLE_BIN_HASH).missing())
         server.expect(server.get(GRADLE_BIN_ZIP).sendFile(distribution.binDistribution))
@@ -62,6 +63,7 @@
 
     def "wrapper gets bad archive on 2 attempts"() {
         given:
+        server.expect(server.head(GRADLE_BIN_ZIP))
         server.expect(server.get(GRADLE_BIN_ZIP).sendFile(badArchive))
         server.expect(server.get(GRADLE_BIN_HASH).missing())
         server.expect(server.get(GRADLE_BIN_ZIP).sendFile(badArchive))
@@ -83,6 +85,7 @@
 
     def "wrapper gets bad archive on 3 attempts"() {
         given:
+        server.expect(server.head(GRADLE_BIN_ZIP))
         server.expect(server.get(GRADLE_BIN_ZIP).sendFile(badArchive))
         server.expect(server.get(GRADLE_BIN_HASH).missing())
         server.expect(server.get(GRADLE_BIN_ZIP).sendFile(badArchive))
@@ -102,6 +105,7 @@
 
     def "wrapper gets bad archive on 1 attempt and good hash"() {
         given:
+        server.expect(server.head(GRADLE_BIN_ZIP))
         server.expect(server.get(GRADLE_BIN_ZIP).sendFile(badArchive))
         server.expect(server.get(GRADLE_BIN_HASH).send(getDistributionHash(distribution)))
         server.expect(server.get(GRADLE_BIN_ZIP).sendFile(distribution.binDistribution))
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperChecksumVerificationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperChecksumVerificationTest.groovy
index e34f103..1a9e4b9 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperChecksumVerificationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperChecksumVerificationTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.integtests
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.integtests.fixtures.executer.GradleDistribution
 import org.gradle.integtests.fixtures.executer.IntegrationTestBuildContext
@@ -23,14 +24,13 @@
 import org.gradle.wrapper.WrapperExecutor
 import org.junit.Rule
 import spock.lang.IgnoreIf
-import spock.lang.Issue
 import spock.lang.Shared
 
 import static org.gradle.internal.hash.Hashing.sha256
 
-@Issue('https://github.com/gradle/gradle-private/issues/1537')
 // wrapperExecuter requires a real distribution
 @IgnoreIf({ GradleContextualExecuter.embedded })
+@LocalOnly(because = "https://github.com/gradle/gradle-private/issues/3799")
 class WrapperChecksumVerificationTest extends AbstractWrapperIntegrationSpec {
 
     private static final String WRAPPER_PROPERTIES_PATH = 'gradle/wrapper/gradle-wrapper.properties'
@@ -40,13 +40,17 @@
     @Rule
     BlockingHttpServer server = new BlockingHttpServer()
 
-    def setup() {
+    def configureServer(boolean expectHead) {
+        if (expectHead) {
+            server.expect(server.head("/gradle-bin.zip"))
+        }
         server.expect(server.get("/gradle-bin.zip").sendFile(distribution.binDistribution))
         server.start()
     }
 
     def "wrapper execution fails when using bad checksum"() {
         given:
+        configureServer(true)
         prepareWrapper(new URI(gradleBin))
 
         and:
@@ -83,6 +87,7 @@
 
     def "wrapper successfully verifies good checksum"() {
         given:
+        configureServer(true)
         prepareWrapper(new URI(gradleBin))
 
         and:
@@ -96,6 +101,7 @@
 
     def "wrapper requires checksum configuration if a checksum is present in gradle-wrapper.properties"() {
         given:
+        configureServer(true)
         prepareWrapper(new URI(gradleBin))
 
         and:
@@ -115,6 +121,7 @@
 
     def "wrapper uses new checksum if it was provided as an option"() {
         given:
+        configureServer(true)
         prepareWrapper(new URI(gradleBin))
 
         and:
@@ -136,6 +143,7 @@
 
     def "wrapper preserves new checksum if it was provided in properties"() {
         given:
+        configureServer(false)
         def releasedDistribution = IntegrationTestBuildContext.INSTANCE.distribution("7.5")
         prepareWrapper(releasedDistribution.binDistribution.toURI())
 
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperConcurrentDownloadTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperConcurrentDownloadTest.groovy
index dedea2e..6043c39 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperConcurrentDownloadTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperConcurrentDownloadTest.groovy
@@ -26,6 +26,7 @@
     @Rule BlockingHttpServer server = new BlockingHttpServer()
 
     def setup() {
+        server.expect(server.head("/gradle-bin.zip"))
         server.expect(server.get("/gradle-bin.zip").sendFile(distribution.binDistribution))
         server.start()
     }
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperGenerationIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperGenerationIntegrationTest.groovy
index e71f7ef..08dbaa5 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperGenerationIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperGenerationIntegrationTest.groovy
@@ -17,10 +17,15 @@
 package org.gradle.integtests
 
 import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
+import org.gradle.integtests.fixtures.executer.UnexpectedBuildFailure
 import org.gradle.internal.hash.HashCode
 import org.gradle.internal.hash.Hashing
 import org.gradle.test.fixtures.ConcurrentTestUtil
+import org.gradle.test.fixtures.server.http.HttpServer
 import org.gradle.util.internal.TextUtil
+import org.junit.Rule
+import spock.lang.IgnoreIf
 
 import java.util.jar.Attributes
 import java.util.jar.Manifest
@@ -34,7 +39,7 @@
         """
 
         when:
-        run "wrapper"
+        run "wrapper", "--no-validate-url"
 
         then:
         file("gradlew").text.split(TextUtil.unixLineSeparator).length > 1
@@ -50,16 +55,16 @@
         """
 
         when:
-        run "wrapper"
+        run "wrapper", "--no-validate-url"
 
         then:
         // wrapper needs to be small. Let's check it's smaller than some arbitrary 'small' limit
-        file("gradle/wrapper/gradle-wrapper.jar").length() < 61 * 1024
+        file("gradle/wrapper/gradle-wrapper.jar").length() < 62 * 1024
     }
 
     def "generated wrapper scripts for given version from command-line"() {
         when:
-        run "wrapper", "--gradle-version", "2.2.1"
+        run "wrapper", "--gradle-version", "2.2.1", "--no-validate-url"
 
         then:
         file("gradle/wrapper/gradle-wrapper.properties").text.contains("distributionUrl=https\\://services.gradle.org/distributions/gradle-2.2.1-bin.zip")
@@ -71,7 +76,7 @@
         executer.inDirectory(file("second")).withTasks("wrapper").run()
 
         then: "the checksum should be constant (unless there are code changes)"
-        Hashing.sha256().hashFile(file("first/gradle/wrapper/gradle-wrapper.jar")) == HashCode.fromString("ed2c26eba7cfb93cc2b7785d05e534f07b5b48b5e7fc941921cd098628abca58")
+        Hashing.sha256().hashFile(file("first/gradle/wrapper/gradle-wrapper.jar")) == HashCode.fromString("55e949185c26ba3ddcd2c6a4217d043bfa0ce3cc002bbbb52b709a181a513e81")
 
         and:
         file("first/gradle/wrapper/gradle-wrapper.jar").md5Hash == file("second/gradle/wrapper/gradle-wrapper.jar").md5Hash
@@ -83,7 +88,7 @@
     def "generated wrapper does not change unnecessarily"() {
         def wrapperJar = file("gradle/wrapper/gradle-wrapper.jar")
         def wrapperProperties = file("gradle/wrapper/gradle-wrapper.properties")
-        run "wrapper", "--gradle-version", "2.2.1"
+        run "wrapper", "--gradle-version", "2.2.1", "--no-validate-url"
         def testFile = file("modtime").touch()
         def originalTime = testFile.lastModified()
         when:
@@ -92,7 +97,7 @@
             testFile.touch()
             assert (testFile.lastModified() - originalTime) >= 2000L
         }
-        run "wrapper", "--gradle-version", "2.2.1", "--rerun-tasks"
+        run "wrapper", "--gradle-version", "2.2.1", "--rerun-tasks", "--no-validate-url"
 
         then:
         result.assertTasksExecuted(":wrapper")
@@ -102,7 +107,7 @@
 
     def "generated wrapper scripts for valid distribution types from command-line"() {
         when:
-        run "wrapper", "--gradle-version", "2.13", "--distribution-type", distributionType
+        run "wrapper", "--gradle-version", "2.13", "--distribution-type", distributionType, "--no-validate-url"
 
         then:
         file("gradle/wrapper/gradle-wrapper.properties").text.contains("distributionUrl=https\\://services.gradle.org/distributions/gradle-2.13-${distributionType}.zip")
@@ -113,7 +118,7 @@
 
     def "no generated wrapper scripts for invalid distribution type from command-line"() {
         when:
-        fails "wrapper", "--gradle-version", "2.13", "--distribution-type", "invalid-distribution-type"
+        fails "wrapper", "--gradle-version", "2.13", "--distribution-type", "invalid-distribution-type", "--no-validate-url"
 
         then:
         failure.assertHasCause("Cannot convert string value 'invalid-distribution-type' to an enum value of type 'org.gradle.api.tasks.wrapper.Wrapper\$DistributionType' (valid case insensitive values: BIN, ALL)")
@@ -121,7 +126,7 @@
 
     def "generated wrapper scripts for given distribution URL from command-line"() {
         when:
-        run "wrapper", "--gradle-distribution-url", "http://localhost:8080/gradlew/dist"
+        run "wrapper", "--gradle-distribution-url", "http://localhost:8080/gradlew/dist", "--no-validate-url"
 
         then:
         file("gradle/wrapper/gradle-wrapper.properties").text.contains("distributionUrl=http\\://localhost\\:8080/gradlew/dist")
@@ -158,4 +163,102 @@
             getValue(Attributes.Name.IMPLEMENTATION_TITLE) == 'Gradle Wrapper'
         }
     }
+
+    @Rule
+    HttpServer httpServer = new HttpServer()
+
+    @IgnoreIf({ GradleContextualExecuter.embedded })
+    def "wrapper task fails if http distribution url from command-line is invalid"() {
+        given:
+        def path = "/distributions/8.0-RC-5"
+        httpServer.start()
+        httpServer.expectHeadMissing(path)
+        def url = "${httpServer.uri}" + path
+
+        when:
+        run "wrapper", "--gradle-distribution-url", url
+
+        then:
+        Throwable throwable = thrown(UnexpectedBuildFailure.class)
+        assert throwable.message.contains("Test of distribution url ${url} failed. Please check the values set with --gradle-distribution-url and --gradle-version.")
+        file("gradle/wrapper/gradle-wrapper.properties").assertDoesNotExist()
+    }
+
+    def "wrapper task succeeds if http distribution url from command-line is valid"() {
+        given:
+        def path = "/distributions/8.0-rc-5"
+        def file = file(path) << "some content"
+        httpServer.start()
+        httpServer.expectHead(path, file)
+        def url = "${httpServer.uri}" + path
+
+        when:
+        run "wrapper", "--gradle-distribution-url", url
+
+        then:
+        succeeds()
+    }
+
+    @IgnoreIf({ GradleContextualExecuter.embedded })
+    def "wrapper task fails if file distribution url from command-line is invalid"() {
+        given:
+        def target = file("/distributions/8.0-rc-5")
+        def url = target.toURI().toString()
+        target.delete()
+        target.assertDoesNotExist()
+
+        when:
+        run "wrapper", "--gradle-distribution-url", url
+
+        then:
+        Throwable throwable = thrown(UnexpectedBuildFailure.class)
+        assert throwable.message.contains("Test of distribution url ${url} failed. Please check the values set with --gradle-distribution-url and --gradle-version.")
+        file("gradle/wrapper/gradle-wrapper.properties").assertDoesNotExist()
+    }
+
+    def "wrapper task succeeds if file distribution url from command-line is valid"() {
+        given:
+        def target = file("/distributions/8.0-rc-5") << "some content"
+        def url = target.toURI().toString()
+
+        when:
+        run "wrapper", "--gradle-distribution-url", url
+
+        then:
+        succeeds()
+    }
+
+    def "wrapper task with distribution url from command-line respects --offline"() {
+        httpServer.start()
+        def path = "/distributions/8.0-RC-5"
+        def url = "${httpServer.uri}" + path
+        when:
+        run("wrapper", "--gradle-distribution-url", "${url}", "--offline")
+
+        then:
+        succeeds()
+    }
+
+    def "wrapper task with distribution url from command-line respects --no-validate-url"() {
+        httpServer.start()
+        def path = "/distributions/8.0-RC-5"
+        def url = "${httpServer.uri}" + path
+        when:
+        run("wrapper", "--gradle-distribution-url", "${url}", "--no-validate-url")
+
+        then:
+        succeeds()
+    }
+
+    def "wrapper task with distribution url from command-line respects --validate-url"() {
+        given:
+        def target = file("/distributions/8.0-rc-5") << "some content"
+        def url = target.toURI().toString()
+
+        when:
+        run( "wrapper", "--gradle-distribution-url", url, "--validate-url")
+
+        then:
+        succeeds()
+    }
 }
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperHttpIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperHttpIntegrationTest.groovy
index 4b4edd7..116616b 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperHttpIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperHttpIntegrationTest.groovy
@@ -67,6 +67,7 @@
     @Issue('https://github.com/gradle/gradle-private/issues/1537')
     def "downloads wrapper from http server and caches"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(getDefaultBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
@@ -86,6 +87,7 @@
     @Issue('https://github.com/gradle/gradle-private/issues/1537')
     def "recovers from failed download"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(getDefaultBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").broken())
 
@@ -109,6 +111,7 @@
     @Issue('https://github.com/gradle/gradle-private/issues/3032')
     def "fails with reasonable message when download times out"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(getDefaultBaseUrl())
         server.expectAndBlock(server.get("/$TEST_DISTRIBUTION_URL"))
 
@@ -124,6 +127,7 @@
     @Issue('https://github.com/gradle/gradle-private/issues/3032')
     def "does not leak credentials when download times out"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper("http://username:password@localhost:${server.port}")
         server.expectAndBlock(server.get("/$TEST_DISTRIBUTION_URL"))
 
@@ -140,6 +144,7 @@
 
     def "reads timeout from wrapper properties"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(getDefaultBaseUrl())
         server.expectAndBlock(server.get("/$TEST_DISTRIBUTION_URL"))
 
@@ -157,14 +162,17 @@
     def "downloads wrapper via proxy"() {
         given:
         proxyServer.start()
+
+        and:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(server.uri.toString())
+        server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
+
         file("gradle.properties") << """
     systemProp.http.proxyHost=localhost
     systemProp.http.proxyPort=${proxyServer.port}
     systemProp.http.nonProxyHosts=
 """
-        server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
-
         when:
         def result = wrapperExecuter.withTasks('hello').run()
 
@@ -180,8 +188,10 @@
         proxyServer.start('my_user', 'my_password')
 
         and:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(server.uri.toString())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
+
         file("gradle.properties") << """
     systemProp.http.proxyHost=localhost
     systemProp.http.proxyPort=${proxyServer.port}
@@ -189,7 +199,6 @@
     systemProp.http.proxyUser=my_user
     systemProp.http.proxyPassword=my_password
 """
-
         when:
         def result = wrapperExecuter.withTasks('hello').run()
 
@@ -202,8 +211,9 @@
 
     def "downloads wrapper from basic authenticated server and caches"() {
         given:
-        prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.withBasicAuthentication("jdoe", "changeit")
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
+        prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
         when:
@@ -227,8 +237,9 @@
         '''.stripIndent()
 
         and:
-        prepareWrapper(getDefaultBaseUrl())
         server.withBasicAuthentication("jdoe", "changeit")
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
+        prepareWrapper(getDefaultBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
         when:
@@ -240,8 +251,9 @@
 
     def "warns about using basic authentication over insecure connection"() {
         given:
-        prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.withBasicAuthentication("jdoe", "changeit")
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
+        prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
         when:
@@ -253,8 +265,9 @@
 
     def "does not leak basic authentication credentials in output"() {
         given:
-        prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.withBasicAuthentication("jdoe", "changeit")
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
+        prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
         when:
@@ -266,6 +279,7 @@
 
     def "does not leak basic authentication credentials in exception messages"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").broken())
 
@@ -291,8 +305,9 @@
         )
 
         and:
-        prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.withBasicAuthentication("jdoe", "changeit")
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
+        prepareWrapper(getDefaultAuthenticatedBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
         when:
@@ -300,8 +315,7 @@
 
         then:
         outputContains('hello')
-        
         and:
-        proxyServer.requestCount == 1
+        proxyServer.requestCount == 2
     }
 }
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperHttpsIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperHttpsIntegrationTest.groovy
index c692fb6..ca9262b 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperHttpsIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperHttpsIntegrationTest.groovy
@@ -16,6 +16,7 @@
 
 package org.gradle.integtests
 
+import com.gradle.enterprise.testing.annotations.LocalOnly
 import org.gradle.integtests.fixtures.TestResources
 import org.gradle.integtests.fixtures.executer.ExecutionResult
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
@@ -34,6 +35,7 @@
 
 // wrapperExecuter requires a real distribution
 @IgnoreIf({ GradleContextualExecuter.embedded })
+@LocalOnly(because = "https://github.com/gradle/gradle-private/issues/3799")
 class WrapperHttpsIntegrationTest extends AbstractWrapperIntegrationSpec {
     private static final String DEFAULT_USER = "jdoe"
     private static final String DEFAULT_PASSWORD = "changeit"
@@ -71,11 +73,12 @@
     }
 
     private prepareWrapper(String baseUrl) {
-        prepareWrapper(new URI("${baseUrl}/$TEST_DISTRIBUTION_URL"))
+        prepareWrapper(new URI("${baseUrl}/$TEST_DISTRIBUTION_URL"), keyStore.keyStore.path, keyStore.keyStorePassword)
     }
 
     def "does not warn about using basic authentication over secure connection"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(getAuthenticatedBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL")
             .expectUserAgent(matchesNameAndVersion("gradlew", Download.UNKNOWN_VERSION))
@@ -91,7 +94,6 @@
     def "downloads wrapper via proxy"() {
         given:
         proxyServer.start()
-        prepareWrapper(getAuthenticatedBaseUrl())
 
         // Note that the HTTPS protocol handler uses the same nonProxyHosts property as the HTTP protocol.
         file("gradle.properties") << """
@@ -99,6 +101,8 @@
     systemProp.https.proxyPort=${proxyServer.port}
     systemProp.http.nonProxyHosts=
 """
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
+        prepareWrapper(getAuthenticatedBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
         when:
@@ -108,7 +112,7 @@
         assertThat(result.output, containsString('hello'))
 
         and:
-        proxyServer.requestCount == 1
+        proxyServer.requestCount == 2
     }
 
     @Issue('https://github.com/gradle/gradle/issues/5052')
@@ -117,6 +121,7 @@
         proxyServer.start('my_user', 'my_password')
 
         and:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         prepareWrapper(getAuthenticatedBaseUrl())
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
@@ -140,47 +145,55 @@
         proxyServer.requestCount == 1
     }
 
-    private String getAuthenticatedBaseUrl() {
-        "https://$DEFAULT_USER:$DEFAULT_PASSWORD@localhost:${server.port}"
-    }
-
     def "validate properties file content for latest"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         def baseUrl = getAuthenticatedBaseUrl()
         prepareWrapper(baseUrl)
         server.expect(server.get("/$TEST_DISTRIBUTION_URL")
             .sendFile(distribution.binDistribution))
         server.expect(server.get("/versions/current").send("""{ "version" : "7.6" }"""))
+        server.expect(server.head("/distributions/gradle-7.6-bin.zip"))
 
         when:
         result = runWithVersion(baseUrl, "latest")
 
         then:
-        validateDistributionUrl("7.6")
+        validateDistributionUrl("7.6", getEscapedAuthenticatedBaseUrl())
     }
 
     def "validate properties file content for any version"() {
         given:
+        server.expect(server.head("/$TEST_DISTRIBUTION_URL"))
         def baseUrl = getAuthenticatedBaseUrl()
         prepareWrapper(baseUrl)
         server.expect(server.get("/$TEST_DISTRIBUTION_URL").sendFile(distribution.binDistribution))
 
-
         def version = "7.6"
+        server.expect(server.head("/distributions/gradle-$version-bin.zip"))
+
         when:
         runWithVersion(baseUrl, version)
 
         then:
-        validateDistributionUrl(version)
+        validateDistributionUrl(version, getEscapedAuthenticatedBaseUrl())
     }
 
-    private boolean validateDistributionUrl(String version) {
+    private String getAuthenticatedBaseUrl() {
+        "https://$DEFAULT_USER:$DEFAULT_PASSWORD@localhost:${server.port}"
+    }
+
+    private String getEscapedAuthenticatedBaseUrl() {
+        "https\\://$DEFAULT_USER\\:$DEFAULT_PASSWORD@localhost\\:${server.port}"
+    }
+
+    private boolean validateDistributionUrl(String version, String escapedBaseUrl) {
         file("gradle/wrapper/gradle-wrapper.properties")
-            .text.contains("distributionUrl=https\\://services.gradle.org/distributions/gradle-$version-bin.zip")
+            .text.contains("distributionUrl=$escapedBaseUrl/distributions/gradle-$version-bin.zip")
     }
 
     private ExecutionResult runWithVersion(String baseUrl, String version) {
-        result = wrapperExecuter.withCommandLineGradleOpts("-Dorg.gradle.internal.services.version.url.override=$baseUrl",
+        result = wrapperExecuter.withCommandLineGradleOpts("-Dorg.gradle.internal.services.base.url=$baseUrl",
             "-Djavax.net.ssl.trustStore=$keyStore.keyStore.path",
             "-Djavax.net.ssl.trustStorePassword=$keyStore.keyStorePassword")
             .withArguments("wrapper", "--gradle-version", version)
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperIntegrationTest.groovy
index 8155164..c10a49d 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperIntegrationTest.groovy
@@ -16,11 +16,14 @@
 package org.gradle.integtests
 
 import groovy.io.FileType
+import org.gradle.integtests.fixtures.executer.AbstractGradleExecuter
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import spock.lang.IgnoreIf
 
 @IgnoreIf({ GradleContextualExecuter.embedded }) // wrapperExecuter requires a real distribution
 class WrapperIntegrationTest extends AbstractWrapperIntegrationSpec {
+    @IgnoreIf({ AbstractGradleExecuter.agentInstrumentationEnabled })
+    // TODO(mlopatkin) fails because of race between daemon shutdown and deleting the file, as --no-daemon still spawns the single-use daemon
     def "can recover from a broken distribution"() {
         buildFile << "task hello"
         prepareWrapper()
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperLoggingIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperLoggingIntegrationTest.groovy
index 32bebab..34d1b18 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperLoggingIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperLoggingIntegrationTest.groovy
@@ -18,8 +18,8 @@
 
 import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
 import org.gradle.test.fixtures.file.TestFile
-import org.gradle.util.Requires
-import org.gradle.util.TestPrecondition
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.UnitTestPreconditions
 import spock.lang.IgnoreIf
 import spock.lang.Issue
 
@@ -80,7 +80,7 @@
         outputContains("Welcome to Gradle $wrapperExecuter.distribution.version.version!")
     }
 
-    @Requires(TestPrecondition.NOT_WINDOWS)
+    @Requires(UnitTestPreconditions.NotWindows)
     def "wrapper logs and continues when there is a problem setting permissions"() {
         given: "malformed distribution"
         // Repackage distribution with bin/gradle removed so permissions cannot be set
diff --git a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperSupportedBuildJvmIntegrationTest.groovy b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperSupportedBuildJvmIntegrationTest.groovy
index 2ca9259..5e488a7 100644
--- a/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperSupportedBuildJvmIntegrationTest.groovy
+++ b/subprojects/wrapper/src/integTest/groovy/org/gradle/integtests/WrapperSupportedBuildJvmIntegrationTest.groovy
@@ -15,14 +15,15 @@
  */
 
 package org.gradle.integtests
-import org.gradle.integtests.fixtures.AvailableJavaHomes
-import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
-import org.gradle.util.GradleVersion
-import org.gradle.util.Requires
-import spock.lang.IgnoreIf
 
-@Requires(adhoc = { AvailableJavaHomes.getJdks("1.6", "1.7") })
-@IgnoreIf({ GradleContextualExecuter.embedded }) // wrapperExecuter requires a real distribution
+import org.gradle.integtests.fixtures.AvailableJavaHomes
+import org.gradle.test.precondition.Requires
+import org.gradle.test.preconditions.IntegTestPreconditions
+import org.gradle.util.GradleVersion
+
+@Requires(
+    value = [IntegTestPreconditions.UnsupportedJavaHomeAvailable, IntegTestPreconditions.NotEmbeddedExecutor],
+    reason = "wrapperExecuter requires a real distribution")
 class WrapperSupportedBuildJvmIntegrationTest extends AbstractWrapperIntegrationSpec {
     def "provides reasonable failure message when attempting to run under java #jdk.javaVersion"() {
         given:
diff --git a/version.txt b/version.txt
index b8eb026..223a939 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-8.1
+8.3
\ No newline at end of file